https://mooseframework.inl.gov
Classes | Functions
MooseMeshElementConversionUtils Namespace Reference

Classes

struct  BCTupleKeyComp
 

Functions

void hexElemSplitter (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &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 (ReplicatedMesh &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
 
void transitionLayerGenerator (ReplicatedMesh &mesh, const std::vector< BoundaryName > &boundary_names, const unsigned int conversion_element_layer_number, const bool external_boundaries_checking)
 
void assignConvertedElementsSubdomainNameSuffix (ReplicatedMesh &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 ( ReplicatedMesh &  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 ( ReplicatedMesh 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 1226 of file MooseMeshElementConversionUtils.C.

1232 {
1233  for (const auto & subdomain_id : original_subdomain_ids)
1234  {
1235  if (MooseMeshUtils::hasSubdomainID(mesh, subdomain_id + sid_shift_base))
1236  {
1237  const SubdomainName new_name =
1238  (mesh.subdomain_name(subdomain_id).empty() ? std::to_string(subdomain_id)
1239  : mesh.subdomain_name(subdomain_id)) +
1240  '_' + tet_suffix;
1241  if (MooseMeshUtils::hasSubdomainName(mesh, new_name))
1242  throw MooseException(
1243  "This suffix for converted TET4 elements results in a subdomain name, " + new_name +
1244  ", that already exists in the mesh. Please choose a different suffix.");
1245  mesh.subdomain_name(subdomain_id + sid_shift_base) = new_name;
1246  }
1247  if (MooseMeshUtils::hasSubdomainID(mesh, subdomain_id + 2 * sid_shift_base))
1248  {
1249  const SubdomainName new_name =
1250  (mesh.subdomain_name(subdomain_id).empty() ? std::to_string(subdomain_id)
1251  : mesh.subdomain_name(subdomain_id)) +
1252  '_' + pyramid_suffix;
1253  if (MooseMeshUtils::hasSubdomainName(mesh, new_name))
1254  throw MooseException(
1255  "This suffix for converted PYRAMID5 elements results in a subdomain name, " + new_name +
1256  ", that already exists in the mesh. Please choose a different suffix.");
1257  mesh.subdomain_name(subdomain_id + 2 * sid_shift_base) = new_name;
1258  }
1259  }
1260 }
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 ( ReplicatedMesh &  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 ( ReplicatedMesh &  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 ( ReplicatedMesh 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  std::vector<dof_id_type> converted_elems_ids_to_retain;
592  // Build boundary information of the mesh
593  BoundaryInfo & boundary_info = mesh.get_boundary_info();
594  const auto bdry_side_list = boundary_info.build_side_list();
595  for (const auto & elem_to_process : elems_to_process)
596  {
597  switch (mesh.elem_ptr(elem_to_process.first)->type())
598  {
599  case ElemType::HEX8:
600  hexElemSplitter(mesh,
601  bdry_side_list,
602  elem_to_process.first,
603  elem_to_process.second ? converted_elems_ids_to_track
604  : converted_elems_ids_to_retain);
605  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
606  break;
607  case ElemType::PYRAMID5:
608  pyramidElemSplitter(mesh,
609  bdry_side_list,
610  elem_to_process.first,
611  elem_to_process.second ? converted_elems_ids_to_track
612  : converted_elems_ids_to_retain);
613  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
614  break;
615  case ElemType::PRISM6:
616  prismElemSplitter(mesh,
617  bdry_side_list,
618  elem_to_process.first,
619  elem_to_process.second ? converted_elems_ids_to_track
620  : converted_elems_ids_to_retain);
621  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
622  break;
623  case ElemType::TET4:
624  if (elem_to_process.second)
625  converted_elems_ids_to_track.push_back(elem_to_process.first);
626  else
627  converted_elems_ids_to_retain.push_back(elem_to_process.first);
628  break;
629  default:
630  mooseError("Unexpected element type.");
631  }
632  }
633 
634  if (delete_block_to_remove)
635  {
636  for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
637  elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
638  elem_it++)
639  mesh.delete_elem(*elem_it);
640 
641  mesh.contract();
643  }
644 }
void pyramidElemSplitter(ReplicatedMesh &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 mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:333
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
virtual void delete_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
void prismElemSplitter(ReplicatedMesh &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 contract()=0
subdomain_id_type subdomain_id() const
void hexElemSplitter(ReplicatedMesh &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 ElemType type() const=0

◆ convert3DMeshToAllTet4() [4/4]

void MooseMeshElementConversionUtils::convert3DMeshToAllTet4 ( ReplicatedMesh mesh)

Definition at line 647 of file MooseMeshElementConversionUtils.C.

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

◆ convertElem() [1/2]

void MooseMeshElementConversionUtils::convertElem ( ReplicatedMesh &  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 ( ReplicatedMesh 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 688 of file MooseMeshElementConversionUtils.C.

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

Referenced by convertElem().

725 {
726  // We add a node at the centroid of the HEX8 element
727  // With this node, the HEX8 can be converted into 6 PYRAMID5 elements
728  // For the PYRAMID5 element at the 'side_indices', they can further be converted into 2 TET4
729  // elements
730  const Point elem_cent = mesh.elem_ptr(elem_id)->true_centroid();
731  auto new_node = mesh.add_point(elem_cent);
732  for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
733  {
734  if (std::find(side_indices.begin(), side_indices.end(), i_side) != side_indices.end())
736  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
737  else
739  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
740  }
741 }
virtual Point true_centroid() const
void createUnitTet4FromHex8(ReplicatedMesh &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)
void createUnitPyramid5FromHex8(ReplicatedMesh &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)

◆ convertPrism6Elem() [1/2]

void MooseMeshElementConversionUtils::convertPrism6Elem ( ReplicatedMesh &  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 ( ReplicatedMesh 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 826 of file MooseMeshElementConversionUtils.C.

Referenced by convertElem().

831 {
832  // We add a node at the centroid of the PRISM6 element
833  // With this node, the PRISM6 can be converted into 3 PYRAMID5 elements and 2 TET4 elements
834  // For the PYRAMID5 element, it can further be converted into 2 TET3 elements
835  const Point elem_cent = mesh.elem_ptr(elem_id)->true_centroid();
836  auto new_node = mesh.add_point(elem_cent);
837  for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
838  {
839  if (i_side % 4 == 0 ||
840  std::find(side_indices.begin(), side_indices.end(), i_side) != side_indices.end())
842  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
843  else
845  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
846  }
847 }
virtual Point true_centroid() const
void createUnitPyramid5FromPrism6(ReplicatedMesh &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(ReplicatedMesh &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 ( ReplicatedMesh &  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 ( ReplicatedMesh 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 933 of file MooseMeshElementConversionUtils.C.

Referenced by convertElem().

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

Referenced by convertHex8Elem(), and createUnitPyramid5FromPrism6().

750 {
751  auto new_elem = std::make_unique<Pyramid5>();
752  new_elem->set_node(0, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(3));
753  new_elem->set_node(1, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(2));
754  new_elem->set_node(2, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(1));
755  new_elem->set_node(3, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(0));
756  new_elem->set_node(4, const_cast<Node *>(new_node));
757  new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base * 2;
758  auto new_elem_ptr = mesh.add_elem(std::move(new_elem));
759  retainEEID(mesh, elem_id, new_elem_ptr);
760  for (const auto & bid : side_info)
761  mesh.get_boundary_info().add_side(new_elem_ptr, 4, bid);
762 }
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
void retainEEID(ReplicatedMesh &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 ( ReplicatedMesh &  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 ( ReplicatedMesh 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 920 of file MooseMeshElementConversionUtils.C.

Referenced by convertPrism6Elem().

926 {
927  // Same as Hex8
929  mesh, elem_id, side_index, new_node, side_info, subdomain_id_shift_base);
930 }
void createUnitPyramid5FromHex8(ReplicatedMesh &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 ( ReplicatedMesh &  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 ( ReplicatedMesh 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 765 of file MooseMeshElementConversionUtils.C.

Referenced by convertHex8Elem().

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

Referenced by convertPrism6Elem().

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

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

675 {
676  elem_side_list.resize(n_elem_sides);
677  const auto selected_bdry_side_list =
678  std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
679  for (auto selected_bdry_side = selected_bdry_side_list.first;
680  selected_bdry_side != selected_bdry_side_list.second;
681  ++selected_bdry_side)
682  {
683  elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
684  }
685 }

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

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

675 {
676  elem_side_list.resize(n_elem_sides);
677  const auto selected_bdry_side_list =
678  std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
679  for (auto selected_bdry_side = selected_bdry_side_list.first;
680  selected_bdry_side != selected_bdry_side_list.second;
681  ++selected_bdry_side)
682  {
683  elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
684  }
685 }

◆ hexElemSplitter() [1/2]

void MooseMeshElementConversionUtils::hexElemSplitter ( ReplicatedMesh 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 ( ReplicatedMesh &  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:333

◆ 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:333

◆ 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:333

◆ 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:333

◆ prismElemSplitter() [1/2]

void MooseMeshElementConversionUtils::prismElemSplitter ( ReplicatedMesh &  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 ( ReplicatedMesh 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 ( ReplicatedMesh &  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 ( ReplicatedMesh 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 ( ReplicatedMesh &  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 ( ReplicatedMesh mesh,
const dof_id_type elem_id,
Elem new_elem_ptr 
)

Definition at line 998 of file MooseMeshElementConversionUtils.C.

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

999 {
1000  const unsigned int n_eeid = mesh.n_elem_integers();
1001  for (const auto & i : make_range(n_eeid))
1002  new_elem_ptr->set_extra_integer(i, mesh.elem_ptr(elem_id)->get_extra_integer(i));
1003 }
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 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:333

◆ 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 ( ReplicatedMesh &  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().

◆ transitionLayerGenerator() [2/2]

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

Definition at line 1006 of file MooseMeshElementConversionUtils.C.

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