LCOV - code coverage report
Current view: top level - src/utils - MooseMeshElementConversionUtils.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 317 350 90.6 %
Date: 2025-07-17 01:28:37 Functions: 17 17 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : // MOOSE includes
      11             : #include "MooseMeshElementConversionUtils.h"
      12             : #include "MooseError.h"
      13             : 
      14             : #include "libmesh/elem.h"
      15             : #include "libmesh/enum_order.h"
      16             : #include "libmesh/boundary_info.h"
      17             : #include "libmesh/mesh_base.h"
      18             : #include "libmesh/parallel.h"
      19             : #include "libmesh/parallel_algebra.h"
      20             : #include "libmesh/utility.h"
      21             : #include "libmesh/cell_tet4.h"
      22             : #include "libmesh/face_tri3.h"
      23             : 
      24             : using namespace libMesh;
      25             : 
      26             : namespace MooseMeshElementConversionUtils
      27             : {
      28             : void
      29        6456 : hexElemSplitter(ReplicatedMesh & mesh,
      30             :                 const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
      31             :                 const dof_id_type elem_id,
      32             :                 std::vector<dof_id_type> & converted_elems_ids)
      33             : {
      34             :   // Build boundary information of the mesh
      35        6456 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
      36             :   // Create a list of sidesets involving the element to be split
      37        6456 :   std::vector<std::vector<boundary_id_type>> elem_side_list;
      38        6456 :   elem_side_list.resize(6);
      39        6456 :   elementBoundaryInfoCollector(bdry_side_list, elem_id, 6, elem_side_list);
      40             : 
      41        6456 :   const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
      42        6456 :   std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
      43             :   // Record all the element extra integers of the original quad element
      44        6456 :   for (unsigned int j = 0; j < n_elem_extra_ids; j++)
      45           0 :     exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
      46             : 
      47        6456 :   std::vector<std::vector<unsigned int>> opt_option;
      48        6456 :   std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
      49        6456 :                                               mesh.elem_ptr(elem_id)->node_ptr(1),
      50        6456 :                                               mesh.elem_ptr(elem_id)->node_ptr(2),
      51        6456 :                                               mesh.elem_ptr(elem_id)->node_ptr(3),
      52        6456 :                                               mesh.elem_ptr(elem_id)->node_ptr(4),
      53        6456 :                                               mesh.elem_ptr(elem_id)->node_ptr(5),
      54        6456 :                                               mesh.elem_ptr(elem_id)->node_ptr(6),
      55       45192 :                                               mesh.elem_ptr(elem_id)->node_ptr(7)};
      56        6456 :   std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
      57             : 
      58        6456 :   std::vector<std::vector<const Node *>> optimized_node_list;
      59        6456 :   hexNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
      60             : 
      61        6456 :   std::vector<Elem *> elems_Tet4;
      62       45192 :   for (const auto i : index_range(optimized_node_list))
      63             :   {
      64       38736 :     auto new_elem = std::make_unique<Tet4>();
      65       38736 :     new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
      66       38736 :     new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
      67       38736 :     new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
      68       38736 :     new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
      69       38736 :     new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
      70       38736 :     elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
      71       38736 :     converted_elems_ids.push_back(elems_Tet4.back()->id());
      72             : 
      73      193680 :     for (unsigned int j = 0; j < 4; j++)
      74             :     {
      75             :       // A hex element has 6 faces indexed from 0 to 5
      76             :       // a <6 value indicates that the face of the tet element corresponds to the face of the
      77             :       // original hex; a =6 value means the face of the tet is an interior face of the hex
      78      154944 :       if (rotated_tet_face_indices[i][j] < 6)
      79             :       {
      80       86528 :         for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
      81        9056 :           boundary_info.add_side(elems_Tet4.back(), j, side_info);
      82             :       }
      83             :     }
      84       38736 :   }
      85             : 
      86             :   // Retain element extra integers
      87       45192 :   for (unsigned int i = 0; i < 6; i++)
      88       38736 :     for (unsigned int j = 0; j < n_elem_extra_ids; j++)
      89             :     {
      90           0 :       elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
      91             :     }
      92        6456 : }
      93             : 
      94             : void
      95         696 : prismElemSplitter(ReplicatedMesh & mesh,
      96             :                   const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
      97             :                   const dof_id_type elem_id,
      98             :                   std::vector<dof_id_type> & converted_elems_ids)
      99             : {
     100             :   // Build boundary information of the mesh
     101         696 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     102             :   // Create a list of sidesets involving the element to be split
     103         696 :   std::vector<std::vector<boundary_id_type>> elem_side_list;
     104         696 :   elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
     105             : 
     106         696 :   const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
     107         696 :   std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
     108             : 
     109             :   // Record all the element extra integers of the original quad element
     110         696 :   for (unsigned int j = 0; j < n_elem_extra_ids; j++)
     111           0 :     exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
     112             : 
     113         696 :   std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
     114         696 :                                               mesh.elem_ptr(elem_id)->node_ptr(1),
     115         696 :                                               mesh.elem_ptr(elem_id)->node_ptr(2),
     116         696 :                                               mesh.elem_ptr(elem_id)->node_ptr(3),
     117         696 :                                               mesh.elem_ptr(elem_id)->node_ptr(4),
     118        3480 :                                               mesh.elem_ptr(elem_id)->node_ptr(5)};
     119         696 :   std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
     120         696 :   std::vector<std::vector<const Node *>> optimized_node_list;
     121         696 :   prismNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
     122             : 
     123         696 :   std::vector<Elem *> elems_Tet4;
     124        2784 :   for (const auto i : index_range(optimized_node_list))
     125             :   {
     126        2088 :     auto new_elem = std::make_unique<Tet4>();
     127        2088 :     new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
     128        2088 :     new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
     129        2088 :     new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
     130        2088 :     new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
     131        2088 :     new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     132        2088 :     elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
     133        2088 :     converted_elems_ids.push_back(elems_Tet4.back()->id());
     134             : 
     135       10440 :     for (unsigned int j = 0; j < 4; j++)
     136             :     {
     137             :       // A prism element has 5 faces indexed from 0 to 4
     138             :       // a <4 value indicates that the face of the tet element corresponds to the face of the
     139             :       // original prism; a =5 value means the face of the tet is an interior face of the prism
     140        8352 :       if (rotated_tet_face_indices[i][j] < 5)
     141             :       {
     142        6624 :         for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
     143        1056 :           boundary_info.add_side(elems_Tet4.back(), j, side_info);
     144             :       }
     145             :     }
     146        2088 :   }
     147             : 
     148             :   // Retain element extra integers
     149        2784 :   for (unsigned int i = 0; i < 3; i++)
     150        2088 :     for (unsigned int j = 0; j < n_elem_extra_ids; j++)
     151             :     {
     152           0 :       elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
     153             :     }
     154         696 : }
     155             : 
     156             : void
     157         512 : pyramidElemSplitter(ReplicatedMesh & mesh,
     158             :                     const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
     159             :                     const dof_id_type elem_id,
     160             :                     std::vector<dof_id_type> & converted_elems_ids)
     161             : {
     162             :   // Build boundary information of the mesh
     163         512 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     164             :   // Create a list of sidesets involving the element to be split
     165         512 :   std::vector<std::vector<boundary_id_type>> elem_side_list;
     166         512 :   elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
     167             : 
     168         512 :   const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
     169         512 :   std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
     170             :   // Record all the element extra integers of the original quad element
     171         512 :   for (unsigned int j = 0; j < n_elem_extra_ids; j++)
     172           0 :     exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
     173             : 
     174         512 :   std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
     175         512 :                                               mesh.elem_ptr(elem_id)->node_ptr(1),
     176         512 :                                               mesh.elem_ptr(elem_id)->node_ptr(2),
     177         512 :                                               mesh.elem_ptr(elem_id)->node_ptr(3),
     178        2048 :                                               mesh.elem_ptr(elem_id)->node_ptr(4)};
     179         512 :   std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
     180         512 :   std::vector<std::vector<const Node *>> optimized_node_list;
     181         512 :   pyramidNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
     182             : 
     183         512 :   std::vector<Elem *> elems_Tet4;
     184        1536 :   for (const auto i : index_range(optimized_node_list))
     185             :   {
     186        1024 :     auto new_elem = std::make_unique<Tet4>();
     187        1024 :     new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
     188        1024 :     new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
     189        1024 :     new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
     190        1024 :     new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
     191        1024 :     new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     192        1024 :     elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
     193        1024 :     converted_elems_ids.push_back(elems_Tet4.back()->id());
     194             : 
     195        5120 :     for (unsigned int j = 0; j < 4; j++)
     196             :     {
     197             :       // A pyramid element has 5 faces indexed from 0 to 4
     198             :       // a <4 value indicates that the face of the tet element corresponds to the face of the
     199             :       // original pyramid; a =5 value means the face of the tet is an interior face of the pyramid
     200        4096 :       if (rotated_tet_face_indices[i][j] < 5)
     201             :       {
     202        3808 :         for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
     203         736 :           boundary_info.add_side(elems_Tet4.back(), j, side_info);
     204             :       }
     205             :     }
     206        1024 :   }
     207             : 
     208             :   // Retain element extra integers
     209        1536 :   for (unsigned int i = 0; i < 2; i++)
     210        1024 :     for (unsigned int j = 0; j < n_elem_extra_ids; j++)
     211             :     {
     212           0 :       elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
     213             :     }
     214         512 : }
     215             : 
     216             : std::vector<unsigned int>
     217        6456 : neighborNodeIndicesHEX8(unsigned int min_id_index)
     218             : {
     219             :   const std::vector<std::vector<unsigned int>> preset_indices = {
     220       58104 :       {1, 3, 4}, {0, 2, 5}, {3, 1, 6}, {2, 0, 7}, {5, 7, 0}, {4, 6, 1}, {7, 5, 2}, {6, 4, 3}};
     221        6456 :   if (min_id_index > 7)
     222           0 :     mooseError("The input node index is out of range.");
     223             :   else
     224       12912 :     return preset_indices[min_id_index];
     225       19368 : }
     226             : 
     227             : void
     228        6456 : hexNodesToTetNodesDeterminer(std::vector<const Node *> & hex_nodes,
     229             :                              std::vector<std::vector<unsigned int>> & rotated_tet_face_indices,
     230             :                              std::vector<std::vector<const Node *>> & tet_nodes_list)
     231             : {
     232             :   // Find the node with the minimum id
     233        6456 :   std::vector<dof_id_type> node_ids(8);
     234       58104 :   for (unsigned int i = 0; i < 8; i++)
     235       51648 :     node_ids[i] = hex_nodes[i]->id();
     236             : 
     237        6456 :   const unsigned int min_node_id_index = std::distance(
     238        6456 :       std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
     239             :   // Get the index of the three neighbor nodes of the minimum node
     240             :   // The order is consistent with the description in nodeRotationHEX8()
     241             :   // Then determine the index of the second minimum node
     242        6456 :   const auto neighbor_node_indices = neighborNodeIndicesHEX8(min_node_id_index);
     243             : 
     244        6456 :   const auto neighbor_node_ids = {node_ids[neighbor_node_indices[0]],
     245        6456 :                                   node_ids[neighbor_node_indices[1]],
     246       12912 :                                   node_ids[neighbor_node_indices[2]]};
     247             :   const unsigned int sec_min_pos =
     248        6456 :       std::distance(std::begin(neighbor_node_ids),
     249        6456 :                     std::min_element(std::begin(neighbor_node_ids), std::end(neighbor_node_ids)));
     250             : 
     251             :   // Rotate the node and face indices based on the identified minimum and second minimum nodes
     252             :   // After the rotation, we guarantee that the minimum node is the first node (Node 0)
     253             :   // And the second node (Node 1) has the minium global id among the three neighbor nodes of Node 0
     254             :   // This makes the splitting process simpler
     255        6456 :   std::vector<unsigned int> face_rotation;
     256        6456 :   std::vector<unsigned int> rotated_indices;
     257        6456 :   nodeRotationHEX8(min_node_id_index, sec_min_pos, face_rotation, rotated_indices);
     258        6456 :   std::vector<const Node *> rotated_hex_nodes;
     259       58104 :   for (unsigned int i = 0; i < 8; i++)
     260       51648 :     rotated_hex_nodes.push_back(hex_nodes[rotated_indices[i]]);
     261             : 
     262             :   // Find the selection of each face's cutting direction
     263        6456 :   const auto diagonal_directions = quadFaceDiagonalDirectionsHex(rotated_hex_nodes);
     264             : 
     265             :   // Based on the determined splitting directions of all the faces, determine the nodes of each
     266             :   // resulting TET4 elements after the splitting.
     267        6456 :   std::vector<std::vector<unsigned int>> tet_face_indices;
     268        6456 :   const auto tet_nodes_set = tetNodesForHex(diagonal_directions, tet_face_indices);
     269       45192 :   for (const auto & tet_face_index : tet_face_indices)
     270             :   {
     271       38736 :     rotated_tet_face_indices.push_back(std::vector<unsigned int>());
     272      193680 :     for (const auto & face_index : tet_face_index)
     273             :     {
     274      154944 :       if (face_index < 6)
     275       77472 :         rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
     276             :       else
     277       77472 :         rotated_tet_face_indices.back().push_back(6);
     278             :     }
     279             :   }
     280             : 
     281       45192 :   for (const auto & tet_nodes : tet_nodes_set)
     282             :   {
     283       38736 :     tet_nodes_list.push_back(std::vector<const Node *>());
     284      193680 :     for (const auto & tet_node : tet_nodes)
     285      154944 :       tet_nodes_list.back().push_back(rotated_hex_nodes[tet_node]);
     286             :   }
     287        6456 : }
     288             : 
     289             : std::vector<bool>
     290        6456 : quadFaceDiagonalDirectionsHex(const std::vector<const Node *> & hex_nodes)
     291             : {
     292             :   // Bottom/Top; Front/Back; Right/Left
     293             :   const std::vector<std::vector<unsigned int>> face_indices = {
     294       45192 :       {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 5, 4}, {2, 3, 7, 6}, {1, 2, 6, 5}, {3, 0, 4, 7}};
     295        6456 :   std::vector<bool> diagonal_directions;
     296       45192 :   for (const auto & face_index : face_indices)
     297             :   {
     298       38736 :     std::vector<const Node *> quad_nodes = {hex_nodes[face_index[0]],
     299       38736 :                                             hex_nodes[face_index[1]],
     300       38736 :                                             hex_nodes[face_index[2]],
     301      116208 :                                             hex_nodes[face_index[3]]};
     302       38736 :     diagonal_directions.push_back(quadFaceDiagonalDirection(quad_nodes));
     303       38736 :   }
     304       12912 :   return diagonal_directions;
     305       19368 : }
     306             : 
     307             : bool
     308       49592 : quadFaceDiagonalDirection(const std::vector<const Node *> & quad_nodes)
     309             : {
     310             :   const std::vector<dof_id_type> node_ids = {
     311       49592 :       quad_nodes[0]->id(), quad_nodes[1]->id(), quad_nodes[2]->id(), quad_nodes[3]->id()};
     312       49592 :   const unsigned int min_id_index = std::distance(
     313       49592 :       std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
     314       49592 :   if (min_id_index == 0 || min_id_index == 2)
     315       32608 :     return true;
     316             :   else
     317       16984 :     return false;
     318       49592 : }
     319             : 
     320             : std::vector<std::vector<unsigned int>>
     321        6456 : tetNodesForHex(const std::vector<bool> diagonal_directions,
     322             :                std::vector<std::vector<unsigned int>> & tet_face_indices)
     323             : {
     324             :   const std::vector<std::vector<bool>> possible_inputs = {{true, true, true, true, true, false},
     325             :                                                           {true, true, true, true, false, false},
     326             :                                                           {true, true, true, false, true, false},
     327             :                                                           {true, false, true, true, true, false},
     328             :                                                           {true, false, true, true, false, false},
     329             :                                                           {true, false, true, false, true, false},
     330       51648 :                                                           {true, false, true, false, false, false}};
     331             : 
     332        6456 :   const unsigned int input_index = std::distance(
     333             :       std::begin(possible_inputs),
     334        6456 :       std::find(std::begin(possible_inputs), std::end(possible_inputs), diagonal_directions));
     335             : 
     336        6456 :   switch (input_index)
     337             :   {
     338         304 :     case 0:
     339        2128 :       tet_face_indices = {
     340        2128 :           {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
     341        2128 :       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}};
     342           0 :     case 1:
     343           0 :       tet_face_indices = {
     344           0 :           {0, 1, 2, 6}, {6, 6, 2, 6}, {6, 6, 5, 1}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
     345           0 :       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}};
     346        6152 :     case 2:
     347       43064 :       tet_face_indices = {
     348       43064 :           {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {4, 6, 5, 6}, {4, 6, 3, 6}, {0, 6, 3, 6}};
     349       43064 :       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}};
     350           0 :     case 3:
     351           0 :       tet_face_indices = {
     352           0 :           {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {4, 0, 3, 6}, {6, 6, 3, 6}, {6, 6, 2, 0}};
     353           0 :       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}};
     354           0 :     case 4:
     355           0 :       tet_face_indices = {{0, 1, 2, 6}, {0, 6, 3, 4}, {5, 4, 6, 1}, {5, 6, 3, 2}, {6, 6, 6, 6}};
     356           0 :       return {{0, 1, 2, 5}, {0, 2, 3, 7}, {4, 7, 5, 0}, {5, 7, 6, 2}, {0, 2, 7, 5}};
     357           0 :     case 5:
     358           0 :       tet_face_indices = {
     359           0 :           {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {2, 6, 6, 0}, {3, 6, 6, 0}, {3, 6, 6, 4}};
     360           0 :       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}};
     361           0 :     case 6:
     362           0 :       tet_face_indices = {
     363           0 :           {1, 4, 5, 6}, {6, 6, 5, 6}, {6, 6, 3, 4}, {1, 6, 2, 0}, {6, 6, 2, 6}, {6, 0, 3, 6}};
     364           0 :       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}};
     365           0 :     default:
     366           0 :       mooseError("Unexpected input.");
     367             :   }
     368       32280 : }
     369             : 
     370             : void
     371        6456 : nodeRotationHEX8(const unsigned int min_id_index,
     372             :                  const unsigned int sec_min_pos,
     373             :                  std::vector<unsigned int> & face_rotation,
     374             :                  std::vector<unsigned int> & node_rotation)
     375             : {
     376             :   // Assuming the original hex element is a cube, the vectors formed by nodes 0-1, 0-3, and 0-4 are
     377             :   // overlapped with the x, y, and z axes, respectively. sec_min_pos = 0 means the second minimum
     378             :   // node is in the x direction, sec_min_pos = 1 means the second minimum node is in the y
     379             :   // direction, and sec_min_pos = 2 means the second minimum node is in the z direction.
     380             :   const std::vector<std::vector<std::vector<unsigned int>>> preset_indices = {
     381             :       {{0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 7, 4, 1, 2, 6, 5}, {0, 4, 5, 1, 3, 7, 6, 2}},
     382             :       {{1, 0, 4, 5, 2, 3, 7, 6}, {1, 2, 3, 0, 5, 6, 7, 4}, {1, 5, 6, 2, 0, 4, 7, 3}},
     383             :       {{2, 3, 0, 1, 6, 7, 4, 5}, {2, 1, 5, 6, 3, 0, 4, 7}, {2, 6, 7, 3, 1, 5, 4, 0}},
     384             :       {{3, 2, 6, 7, 0, 1, 5, 4}, {3, 0, 1, 2, 7, 4, 5, 6}, {3, 7, 4, 0, 2, 6, 5, 1}},
     385             :       {{4, 5, 1, 0, 7, 6, 2, 3}, {4, 7, 6, 5, 0, 3, 2, 1}, {4, 0, 3, 7, 5, 1, 2, 6}},
     386             :       {{5, 4, 7, 6, 1, 0, 3, 2}, {5, 6, 2, 1, 4, 7, 3, 0}, {5, 1, 0, 4, 6, 2, 3, 7}},
     387             :       {{6, 7, 3, 2, 5, 4, 0, 1}, {6, 5, 4, 7, 2, 1, 0, 3}, {6, 2, 1, 5, 7, 3, 0, 4}},
     388      213048 :       {{7, 6, 5, 4, 3, 2, 1, 0}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 3, 2, 6, 4, 0, 1, 5}}};
     389             : 
     390             :   const std::vector<std::vector<std::vector<unsigned int>>> preset_face_indices = {
     391             :       {{0, 1, 2, 3, 4, 5}, {4, 0, 3, 5, 1, 2}, {1, 4, 5, 2, 0, 3}},
     392             :       {{1, 0, 4, 5, 2, 3}, {0, 2, 3, 4, 1, 5}, {2, 1, 5, 3, 0, 4}},
     393             :       {{0, 3, 4, 1, 2, 5}, {2, 0, 1, 5, 3, 4}, {3, 2, 5, 4, 0, 1}},
     394             :       {{3, 0, 2, 5, 4, 1}, {0, 4, 1, 2, 3, 5}, {4, 3, 5, 1, 0, 2}},
     395             :       {{1, 5, 2, 0, 4, 3}, {5, 4, 3, 2, 1, 0}, {4, 1, 0, 3, 5, 2}},
     396             :       {{5, 1, 4, 3, 2, 0}, {2, 5, 3, 0, 1, 4}, {1, 2, 0, 4, 5, 3}},
     397             :       {{3, 5, 4, 0, 2, 1}, {5, 2, 1, 4, 3, 0}, {2, 3, 0, 1, 5, 4}},
     398      213048 :       {{5, 3, 2, 1, 4, 0}, {4, 5, 1, 0, 3, 2}, {3, 4, 0, 2, 5, 1}}};
     399             : 
     400        6456 :   if (min_id_index > 7 || sec_min_pos > 2)
     401           0 :     mooseError("The input node index is out of range.");
     402             :   else
     403             :   {
     404             :     // index: new face index; value: old face index
     405        6456 :     face_rotation = preset_face_indices[min_id_index][sec_min_pos];
     406        6456 :     node_rotation = preset_indices[min_id_index][sec_min_pos];
     407             :   }
     408      335712 : }
     409             : 
     410             : void
     411       10856 : nodeRotationPRISM6(unsigned int min_id_index,
     412             :                    std::vector<unsigned int> & face_rotation,
     413             :                    std::vector<unsigned int> & node_rotation)
     414             : {
     415             :   const std::vector<std::vector<unsigned int>> preset_indices = {{0, 1, 2, 3, 4, 5},
     416             :                                                                  {1, 2, 0, 4, 5, 3},
     417             :                                                                  {2, 0, 1, 5, 3, 4},
     418             :                                                                  {3, 5, 4, 0, 2, 1},
     419             :                                                                  {4, 3, 5, 1, 0, 2},
     420       75992 :                                                                  {5, 4, 3, 2, 1, 0}};
     421             : 
     422             :   const std::vector<std::vector<unsigned int>> preset_face_indices = {{0, 1, 2, 3, 4},
     423             :                                                                       {0, 2, 3, 1, 4},
     424             :                                                                       {0, 3, 1, 2, 4},
     425             :                                                                       {4, 3, 2, 1, 0},
     426             :                                                                       {4, 1, 3, 2, 0},
     427       75992 :                                                                       {4, 2, 1, 3, 0}};
     428             : 
     429       10856 :   if (min_id_index > 5)
     430           0 :     mooseError("The input node index is out of range.");
     431             :   else
     432             :   {
     433             :     // index: new face index; value: old face index
     434       10856 :     face_rotation = preset_face_indices[min_id_index];
     435       10856 :     node_rotation = preset_indices[min_id_index];
     436             :   }
     437       43424 : }
     438             : 
     439             : void
     440       10856 : prismNodesToTetNodesDeterminer(std::vector<const Node *> & prism_nodes,
     441             :                                std::vector<std::vector<unsigned int>> & rotated_tet_face_indices,
     442             :                                std::vector<std::vector<const Node *>> & tet_nodes_list)
     443             : {
     444             :   // Find the node with the minimum id
     445       10856 :   std::vector<dof_id_type> node_ids(6);
     446       75992 :   for (unsigned int i = 0; i < 6; i++)
     447       65136 :     node_ids[i] = prism_nodes[i]->id();
     448             : 
     449       10856 :   const unsigned int min_node_id_index = std::distance(
     450       10856 :       std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
     451             : 
     452             :   // Rotate the node and face indices based on the identified minimum node
     453             :   // After the rotation, we guarantee that the minimum node is the first node (Node 0)
     454             :   // This makes the splitting process simpler
     455       10856 :   std::vector<unsigned int> face_rotation;
     456       10856 :   std::vector<unsigned int> rotated_indices;
     457       10856 :   nodeRotationPRISM6(min_node_id_index, face_rotation, rotated_indices);
     458       10856 :   std::vector<const Node *> rotated_prism_nodes;
     459       75992 :   for (unsigned int i = 0; i < 6; i++)
     460       65136 :     rotated_prism_nodes.push_back(prism_nodes[rotated_indices[i]]);
     461             : 
     462       10856 :   std::vector<const Node *> key_quad_nodes = {rotated_prism_nodes[1],
     463       10856 :                                               rotated_prism_nodes[2],
     464       10856 :                                               rotated_prism_nodes[5],
     465       10856 :                                               rotated_prism_nodes[4]};
     466             : 
     467             :   // Find the selection of each face's cutting direction
     468       10856 :   const bool diagonal_direction = quadFaceDiagonalDirection(key_quad_nodes);
     469             : 
     470             :   // Based on the determined splitting directions of all the faces, determine the nodes of each
     471             :   // resulting TET4 elements after the splitting.
     472       10856 :   std::vector<std::vector<unsigned int>> tet_face_indices;
     473       10856 :   const auto tet_nodes_set = tetNodesForPrism(diagonal_direction, tet_face_indices);
     474       43424 :   for (const auto & tet_face_index : tet_face_indices)
     475             :   {
     476       32568 :     rotated_tet_face_indices.push_back(std::vector<unsigned int>());
     477      162840 :     for (const auto & face_index : tet_face_index)
     478             :     {
     479      130272 :       if (face_index < 5)
     480       86848 :         rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
     481             :       else
     482       43424 :         rotated_tet_face_indices.back().push_back(5);
     483             :     }
     484             :   }
     485             : 
     486       43424 :   for (const auto & tet_nodes : tet_nodes_set)
     487             :   {
     488       32568 :     tet_nodes_list.push_back(std::vector<const Node *>());
     489      162840 :     for (const auto & tet_node : tet_nodes)
     490      130272 :       tet_nodes_list.back().push_back(rotated_prism_nodes[tet_node]);
     491             :   }
     492       10856 : }
     493             : 
     494             : std::vector<std::vector<unsigned int>>
     495       10856 : tetNodesForPrism(const bool diagonal_direction,
     496             :                  std::vector<std::vector<unsigned int>> & tet_face_indices)
     497             : {
     498             : 
     499       10856 :   if (diagonal_direction)
     500             :   {
     501       25920 :     tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 5}, {2, 5, 3, 0}};
     502       25920 :     return {{3, 5, 4, 0}, {1, 4, 5, 0}, {1, 5, 2, 0}};
     503             :   }
     504             :   else
     505             :   {
     506       17504 :     tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 0}, {2, 5, 5, 3}};
     507       17504 :     return {{3, 5, 4, 0}, {1, 4, 2, 0}, {2, 4, 5, 0}};
     508             :   }
     509       32568 : }
     510             : 
     511             : void
     512         720 : nodeRotationPYRAMID5(unsigned int min_id_index,
     513             :                      std::vector<unsigned int> & face_rotation,
     514             :                      std::vector<unsigned int> & node_rotation)
     515             : {
     516             :   const std::vector<std::vector<unsigned int>> preset_indices = {
     517        3600 :       {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
     518             : 
     519             :   const std::vector<std::vector<unsigned int>> preset_face_indices = {
     520        3600 :       {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
     521             : 
     522         720 :   if (min_id_index > 3)
     523           0 :     mooseError("The input node index is out of range.");
     524             :   else
     525             :   {
     526             :     // index: new face index; value: old face index
     527         720 :     face_rotation = preset_face_indices[min_id_index];
     528         720 :     node_rotation = preset_indices[min_id_index];
     529             :   }
     530        2880 : }
     531             : 
     532             : void
     533         720 : pyramidNodesToTetNodesDeterminer(std::vector<const Node *> & pyramid_nodes,
     534             :                                  std::vector<std::vector<unsigned int>> & rotated_tet_face_indices,
     535             :                                  std::vector<std::vector<const Node *>> & tet_nodes_list)
     536             : {
     537             :   // Find the node with the minimum id, ignoring the top node
     538         720 :   std::vector<dof_id_type> node_ids(4);
     539        3600 :   for (unsigned int i = 0; i < 4; i++)
     540        2880 :     node_ids[i] = pyramid_nodes[i]->id();
     541             : 
     542         720 :   const unsigned int min_node_id_index = std::distance(
     543         720 :       std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
     544             : 
     545             :   // Rotate the node and face indices based on the identified minimum nodes
     546             :   // After the rotation, we guarantee that the minimum node is the first node (Node 0)
     547             :   // This makes the splitting process simpler
     548         720 :   std::vector<unsigned int> face_rotation;
     549         720 :   std::vector<unsigned int> rotated_indices;
     550         720 :   nodeRotationPYRAMID5(min_node_id_index, face_rotation, rotated_indices);
     551         720 :   std::vector<const Node *> rotated_pyramid_nodes;
     552        4320 :   for (unsigned int i = 0; i < 5; i++)
     553        3600 :     rotated_pyramid_nodes.push_back(pyramid_nodes[rotated_indices[i]]);
     554             : 
     555             :   // There is only one quad face in a pyramid element, so the splitting selection is binary
     556        2160 :   const std::vector<std::vector<unsigned int>> tet_nodes_set = {{0, 1, 2, 4}, {0, 2, 3, 4}};
     557        2160 :   const std::vector<std::vector<unsigned int>> tet_face_indices = {{4, 0, 1, 5}, {4, 5, 2, 3}};
     558             : 
     559             :   // Based on the determined splitting direction, determine the nodes of each resulting TET4
     560             :   // elements after the splitting.
     561        2160 :   for (const auto & tet_face_index : tet_face_indices)
     562             :   {
     563        1440 :     rotated_tet_face_indices.push_back(std::vector<unsigned int>());
     564        7200 :     for (const auto & face_index : tet_face_index)
     565             :     {
     566        5760 :       if (face_index < 5)
     567        4320 :         rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
     568             :       else
     569        1440 :         rotated_tet_face_indices.back().push_back(5);
     570             :     }
     571             :   }
     572             : 
     573        2160 :   for (const auto & tet_nodes : tet_nodes_set)
     574             :   {
     575        1440 :     tet_nodes_list.push_back(std::vector<const Node *>());
     576        7200 :     for (const auto & tet_node : tet_nodes)
     577        5760 :       tet_nodes_list.back().push_back(rotated_pyramid_nodes[tet_node]);
     578             :   }
     579        2880 : }
     580             : 
     581             : void
     582         144 : convert3DMeshToAllTet4(ReplicatedMesh & mesh,
     583             :                        const std::vector<std::pair<dof_id_type, bool>> & elems_to_process,
     584             :                        std::vector<dof_id_type> & converted_elems_ids_to_track,
     585             :                        const subdomain_id_type block_id_to_remove,
     586             :                        const bool delete_block_to_remove)
     587             : {
     588         144 :   std::vector<dof_id_type> converted_elems_ids_to_retain;
     589             :   // Build boundary information of the mesh
     590         144 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     591         144 :   const auto bdry_side_list = boundary_info.build_side_list();
     592        9088 :   for (const auto & elem_to_process : elems_to_process)
     593             :   {
     594        8944 :     switch (mesh.elem_ptr(elem_to_process.first)->type())
     595             :     {
     596        6456 :       case ElemType::HEX8:
     597        6456 :         hexElemSplitter(mesh,
     598             :                         bdry_side_list,
     599        6456 :                         elem_to_process.first,
     600        6456 :                         elem_to_process.second ? converted_elems_ids_to_track
     601             :                                                : converted_elems_ids_to_retain);
     602        6456 :         mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
     603        6456 :         break;
     604         512 :       case ElemType::PYRAMID5:
     605         512 :         pyramidElemSplitter(mesh,
     606             :                             bdry_side_list,
     607         512 :                             elem_to_process.first,
     608         512 :                             elem_to_process.second ? converted_elems_ids_to_track
     609             :                                                    : converted_elems_ids_to_retain);
     610         512 :         mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
     611         512 :         break;
     612         696 :       case ElemType::PRISM6:
     613         696 :         prismElemSplitter(mesh,
     614             :                           bdry_side_list,
     615         696 :                           elem_to_process.first,
     616         696 :                           elem_to_process.second ? converted_elems_ids_to_track
     617             :                                                  : converted_elems_ids_to_retain);
     618         696 :         mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
     619         696 :         break;
     620        1280 :       case ElemType::TET4:
     621        1280 :         if (elem_to_process.second)
     622         512 :           converted_elems_ids_to_track.push_back(elem_to_process.first);
     623             :         else
     624         768 :           converted_elems_ids_to_retain.push_back(elem_to_process.first);
     625        1280 :         break;
     626           0 :       default:
     627           0 :         mooseError("Unexpected element type.");
     628             :     }
     629             :   }
     630             : 
     631         144 :   if (delete_block_to_remove)
     632             :   {
     633        1344 :     for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
     634        1344 :          elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
     635             :          elem_it++)
     636        1344 :       mesh.delete_elem(*elem_it);
     637             : 
     638          80 :     mesh.contract();
     639          80 :     mesh.prepare_for_use();
     640             :   }
     641         144 : }
     642             : 
     643             : void
     644          84 : convert3DMeshToAllTet4(ReplicatedMesh & mesh)
     645             : {
     646             :   // Subdomain ID for new utility blocks must be new
     647          84 :   std::set<subdomain_id_type> subdomain_ids_set;
     648          84 :   mesh.subdomain_ids(subdomain_ids_set);
     649          84 :   const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin();
     650          84 :   const subdomain_id_type block_id_to_remove = max_subdomain_id + 1;
     651          84 :   std::vector<std::pair<dof_id_type, bool>> original_elems;
     652             : 
     653        2116 :   for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
     654             :        elem_it++)
     655             :   {
     656        2036 :     if ((*elem_it)->default_order() != Order::FIRST)
     657           4 :       mooseError("Only first order elements are supported for cutting.");
     658        2032 :     original_elems.push_back(std::make_pair((*elem_it)->id(), false));
     659          80 :   }
     660             : 
     661          80 :   std::vector<dof_id_type> converted_elems_ids_to_track;
     662             : 
     663          80 :   convert3DMeshToAllTet4(
     664             :       mesh, original_elems, converted_elems_ids_to_track, block_id_to_remove, true);
     665          80 : }
     666             : 
     667             : void
     668       33424 : elementBoundaryInfoCollector(const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
     669             :                              const dof_id_type elem_id,
     670             :                              const unsigned short n_elem_sides,
     671             :                              std::vector<std::vector<boundary_id_type>> & elem_side_list)
     672             : {
     673       33424 :   elem_side_list.resize(n_elem_sides);
     674             :   const auto selected_bdry_side_list =
     675       33424 :       std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
     676       33424 :   for (auto selected_bdry_side = selected_bdry_side_list.first;
     677       44600 :        selected_bdry_side != selected_bdry_side_list.second;
     678       11176 :        ++selected_bdry_side)
     679             :   {
     680       11176 :     elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
     681             :   }
     682       33424 : }
     683             : }

Generated by: LCOV version 1.14