LCOV - code coverage report
Current view: top level - src/utils - MooseMeshElementConversionUtils.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 317 350 90.6 %
Date: 2025-08-08 20:01:16 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        7247 : 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        7247 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
      36             :   // Create a list of sidesets involving the element to be split
      37        7247 :   std::vector<std::vector<boundary_id_type>> elem_side_list;
      38        7247 :   elem_side_list.resize(6);
      39        7247 :   elementBoundaryInfoCollector(bdry_side_list, elem_id, 6, elem_side_list);
      40             : 
      41        7247 :   const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
      42        7247 :   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        7247 :   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        7247 :   std::vector<std::vector<unsigned int>> opt_option;
      48        7247 :   std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
      49        7247 :                                               mesh.elem_ptr(elem_id)->node_ptr(1),
      50        7247 :                                               mesh.elem_ptr(elem_id)->node_ptr(2),
      51        7247 :                                               mesh.elem_ptr(elem_id)->node_ptr(3),
      52        7247 :                                               mesh.elem_ptr(elem_id)->node_ptr(4),
      53        7247 :                                               mesh.elem_ptr(elem_id)->node_ptr(5),
      54        7247 :                                               mesh.elem_ptr(elem_id)->node_ptr(6),
      55       50729 :                                               mesh.elem_ptr(elem_id)->node_ptr(7)};
      56        7247 :   std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
      57             : 
      58        7247 :   std::vector<std::vector<const Node *>> optimized_node_list;
      59        7247 :   hexNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
      60             : 
      61        7247 :   std::vector<Elem *> elems_Tet4;
      62       50729 :   for (const auto i : index_range(optimized_node_list))
      63             :   {
      64       43482 :     auto new_elem = std::make_unique<Tet4>();
      65       43482 :     new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
      66       43482 :     new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
      67       43482 :     new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
      68       43482 :     new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
      69       43482 :     new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
      70       43482 :     elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
      71       43482 :     converted_elems_ids.push_back(elems_Tet4.back()->id());
      72             : 
      73      217410 :     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      173928 :       if (rotated_tet_face_indices[i][j] < 6)
      79             :       {
      80       97056 :         for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
      81       10092 :           boundary_info.add_side(elems_Tet4.back(), j, side_info);
      82             :       }
      83             :     }
      84       43482 :   }
      85             : 
      86             :   // Retain element extra integers
      87       50729 :   for (unsigned int i = 0; i < 6; i++)
      88       43482 :     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        7247 : }
      93             : 
      94             : void
      95         783 : 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         783 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     102             :   // Create a list of sidesets involving the element to be split
     103         783 :   std::vector<std::vector<boundary_id_type>> elem_side_list;
     104         783 :   elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
     105             : 
     106         783 :   const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
     107         783 :   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         783 :   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         783 :   std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
     114         783 :                                               mesh.elem_ptr(elem_id)->node_ptr(1),
     115         783 :                                               mesh.elem_ptr(elem_id)->node_ptr(2),
     116         783 :                                               mesh.elem_ptr(elem_id)->node_ptr(3),
     117         783 :                                               mesh.elem_ptr(elem_id)->node_ptr(4),
     118        3915 :                                               mesh.elem_ptr(elem_id)->node_ptr(5)};
     119         783 :   std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
     120         783 :   std::vector<std::vector<const Node *>> optimized_node_list;
     121         783 :   prismNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
     122             : 
     123         783 :   std::vector<Elem *> elems_Tet4;
     124        3132 :   for (const auto i : index_range(optimized_node_list))
     125             :   {
     126        2349 :     auto new_elem = std::make_unique<Tet4>();
     127        2349 :     new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
     128        2349 :     new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
     129        2349 :     new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
     130        2349 :     new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
     131        2349 :     new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     132        2349 :     elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
     133        2349 :     converted_elems_ids.push_back(elems_Tet4.back()->id());
     134             : 
     135       11745 :     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        9396 :       if (rotated_tet_face_indices[i][j] < 5)
     141             :       {
     142        7452 :         for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
     143        1188 :           boundary_info.add_side(elems_Tet4.back(), j, side_info);
     144             :       }
     145             :     }
     146        2349 :   }
     147             : 
     148             :   // Retain element extra integers
     149        3132 :   for (unsigned int i = 0; i < 3; i++)
     150        2349 :     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         783 : }
     155             : 
     156             : void
     157         576 : 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         576 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     164             :   // Create a list of sidesets involving the element to be split
     165         576 :   std::vector<std::vector<boundary_id_type>> elem_side_list;
     166         576 :   elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
     167             : 
     168         576 :   const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
     169         576 :   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         576 :   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         576 :   std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
     175         576 :                                               mesh.elem_ptr(elem_id)->node_ptr(1),
     176         576 :                                               mesh.elem_ptr(elem_id)->node_ptr(2),
     177         576 :                                               mesh.elem_ptr(elem_id)->node_ptr(3),
     178        2304 :                                               mesh.elem_ptr(elem_id)->node_ptr(4)};
     179         576 :   std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
     180         576 :   std::vector<std::vector<const Node *>> optimized_node_list;
     181         576 :   pyramidNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
     182             : 
     183         576 :   std::vector<Elem *> elems_Tet4;
     184        1728 :   for (const auto i : index_range(optimized_node_list))
     185             :   {
     186        1152 :     auto new_elem = std::make_unique<Tet4>();
     187        1152 :     new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
     188        1152 :     new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
     189        1152 :     new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
     190        1152 :     new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
     191        1152 :     new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     192        1152 :     elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
     193        1152 :     converted_elems_ids.push_back(elems_Tet4.back()->id());
     194             : 
     195        5760 :     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        4608 :       if (rotated_tet_face_indices[i][j] < 5)
     201             :       {
     202        4284 :         for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
     203         828 :           boundary_info.add_side(elems_Tet4.back(), j, side_info);
     204             :       }
     205             :     }
     206        1152 :   }
     207             : 
     208             :   // Retain element extra integers
     209        1728 :   for (unsigned int i = 0; i < 2; i++)
     210        1152 :     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         576 : }
     215             : 
     216             : std::vector<unsigned int>
     217        7247 : neighborNodeIndicesHEX8(unsigned int min_id_index)
     218             : {
     219             :   const std::vector<std::vector<unsigned int>> preset_indices = {
     220       65223 :       {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        7247 :   if (min_id_index > 7)
     222           0 :     mooseError("The input node index is out of range.");
     223             :   else
     224       14494 :     return preset_indices[min_id_index];
     225       21741 : }
     226             : 
     227             : void
     228        7247 : 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        7247 :   std::vector<dof_id_type> node_ids(8);
     234       65223 :   for (unsigned int i = 0; i < 8; i++)
     235       57976 :     node_ids[i] = hex_nodes[i]->id();
     236             : 
     237        7247 :   const unsigned int min_node_id_index = std::distance(
     238        7247 :       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        7247 :   const auto neighbor_node_indices = neighborNodeIndicesHEX8(min_node_id_index);
     243             : 
     244        7247 :   const auto neighbor_node_ids = {node_ids[neighbor_node_indices[0]],
     245        7247 :                                   node_ids[neighbor_node_indices[1]],
     246       14494 :                                   node_ids[neighbor_node_indices[2]]};
     247             :   const unsigned int sec_min_pos =
     248        7247 :       std::distance(std::begin(neighbor_node_ids),
     249        7247 :                     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        7247 :   std::vector<unsigned int> face_rotation;
     256        7247 :   std::vector<unsigned int> rotated_indices;
     257        7247 :   nodeRotationHEX8(min_node_id_index, sec_min_pos, face_rotation, rotated_indices);
     258        7247 :   std::vector<const Node *> rotated_hex_nodes;
     259       65223 :   for (unsigned int i = 0; i < 8; i++)
     260       57976 :     rotated_hex_nodes.push_back(hex_nodes[rotated_indices[i]]);
     261             : 
     262             :   // Find the selection of each face's cutting direction
     263        7247 :   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        7247 :   std::vector<std::vector<unsigned int>> tet_face_indices;
     268        7247 :   const auto tet_nodes_set = tetNodesForHex(diagonal_directions, tet_face_indices);
     269       50729 :   for (const auto & tet_face_index : tet_face_indices)
     270             :   {
     271       43482 :     rotated_tet_face_indices.push_back(std::vector<unsigned int>());
     272      217410 :     for (const auto & face_index : tet_face_index)
     273             :     {
     274      173928 :       if (face_index < 6)
     275       86964 :         rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
     276             :       else
     277       86964 :         rotated_tet_face_indices.back().push_back(6);
     278             :     }
     279             :   }
     280             : 
     281       50729 :   for (const auto & tet_nodes : tet_nodes_set)
     282             :   {
     283       43482 :     tet_nodes_list.push_back(std::vector<const Node *>());
     284      217410 :     for (const auto & tet_node : tet_nodes)
     285      173928 :       tet_nodes_list.back().push_back(rotated_hex_nodes[tet_node]);
     286             :   }
     287        7247 : }
     288             : 
     289             : std::vector<bool>
     290        7247 : 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       50729 :       {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        7247 :   std::vector<bool> diagonal_directions;
     296       50729 :   for (const auto & face_index : face_indices)
     297             :   {
     298       43482 :     std::vector<const Node *> quad_nodes = {hex_nodes[face_index[0]],
     299       43482 :                                             hex_nodes[face_index[1]],
     300       43482 :                                             hex_nodes[face_index[2]],
     301      130446 :                                             hex_nodes[face_index[3]]};
     302       43482 :     diagonal_directions.push_back(quadFaceDiagonalDirection(quad_nodes));
     303       43482 :   }
     304       14494 :   return diagonal_directions;
     305       21741 : }
     306             : 
     307             : bool
     308       55695 : quadFaceDiagonalDirection(const std::vector<const Node *> & quad_nodes)
     309             : {
     310             :   const std::vector<dof_id_type> node_ids = {
     311       55695 :       quad_nodes[0]->id(), quad_nodes[1]->id(), quad_nodes[2]->id(), quad_nodes[3]->id()};
     312       55695 :   const unsigned int min_id_index = std::distance(
     313       55695 :       std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
     314       55695 :   if (min_id_index == 0 || min_id_index == 2)
     315       36616 :     return true;
     316             :   else
     317       19079 :     return false;
     318       55695 : }
     319             : 
     320             : std::vector<std::vector<unsigned int>>
     321        7247 : 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       57976 :                                                           {true, false, true, false, false, false}};
     331             : 
     332        7247 :   const unsigned int input_index = std::distance(
     333             :       std::begin(possible_inputs),
     334        7247 :       std::find(std::begin(possible_inputs), std::end(possible_inputs), diagonal_directions));
     335             : 
     336        7247 :   switch (input_index)
     337             :   {
     338         338 :     case 0:
     339        2366 :       tet_face_indices = {
     340        2366 :           {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        2366 :       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        6909 :     case 2:
     347       48363 :       tet_face_indices = {
     348       48363 :           {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       48363 :       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       36235 : }
     369             : 
     370             : void
     371        7247 : 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      239151 :       {{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      239151 :       {{5, 3, 2, 1, 4, 0}, {4, 5, 1, 0, 3, 2}, {3, 4, 0, 2, 5, 1}}};
     399             : 
     400        7247 :   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        7247 :     face_rotation = preset_face_indices[min_id_index][sec_min_pos];
     406        7247 :     node_rotation = preset_indices[min_id_index][sec_min_pos];
     407             :   }
     408      376844 : }
     409             : 
     410             : void
     411       12213 : 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       85491 :                                                                  {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       85491 :                                                                       {4, 2, 1, 3, 0}};
     428             : 
     429       12213 :   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       12213 :     face_rotation = preset_face_indices[min_id_index];
     435       12213 :     node_rotation = preset_indices[min_id_index];
     436             :   }
     437       48852 : }
     438             : 
     439             : void
     440       12213 : 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       12213 :   std::vector<dof_id_type> node_ids(6);
     446       85491 :   for (unsigned int i = 0; i < 6; i++)
     447       73278 :     node_ids[i] = prism_nodes[i]->id();
     448             : 
     449       12213 :   const unsigned int min_node_id_index = std::distance(
     450       12213 :       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       12213 :   std::vector<unsigned int> face_rotation;
     456       12213 :   std::vector<unsigned int> rotated_indices;
     457       12213 :   nodeRotationPRISM6(min_node_id_index, face_rotation, rotated_indices);
     458       12213 :   std::vector<const Node *> rotated_prism_nodes;
     459       85491 :   for (unsigned int i = 0; i < 6; i++)
     460       73278 :     rotated_prism_nodes.push_back(prism_nodes[rotated_indices[i]]);
     461             : 
     462       12213 :   std::vector<const Node *> key_quad_nodes = {rotated_prism_nodes[1],
     463       12213 :                                               rotated_prism_nodes[2],
     464       12213 :                                               rotated_prism_nodes[5],
     465       12213 :                                               rotated_prism_nodes[4]};
     466             : 
     467             :   // Find the selection of each face's cutting direction
     468       12213 :   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       12213 :   std::vector<std::vector<unsigned int>> tet_face_indices;
     473       12213 :   const auto tet_nodes_set = tetNodesForPrism(diagonal_direction, tet_face_indices);
     474       48852 :   for (const auto & tet_face_index : tet_face_indices)
     475             :   {
     476       36639 :     rotated_tet_face_indices.push_back(std::vector<unsigned int>());
     477      183195 :     for (const auto & face_index : tet_face_index)
     478             :     {
     479      146556 :       if (face_index < 5)
     480       97704 :         rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
     481             :       else
     482       48852 :         rotated_tet_face_indices.back().push_back(5);
     483             :     }
     484             :   }
     485             : 
     486       48852 :   for (const auto & tet_nodes : tet_nodes_set)
     487             :   {
     488       36639 :     tet_nodes_list.push_back(std::vector<const Node *>());
     489      183195 :     for (const auto & tet_node : tet_nodes)
     490      146556 :       tet_nodes_list.back().push_back(rotated_prism_nodes[tet_node]);
     491             :   }
     492       12213 : }
     493             : 
     494             : std::vector<std::vector<unsigned int>>
     495       12213 : tetNodesForPrism(const bool diagonal_direction,
     496             :                  std::vector<std::vector<unsigned int>> & tet_face_indices)
     497             : {
     498             : 
     499       12213 :   if (diagonal_direction)
     500             :   {
     501       29160 :     tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 5}, {2, 5, 3, 0}};
     502       29160 :     return {{3, 5, 4, 0}, {1, 4, 5, 0}, {1, 5, 2, 0}};
     503             :   }
     504             :   else
     505             :   {
     506       19692 :     tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 0}, {2, 5, 5, 3}};
     507       19692 :     return {{3, 5, 4, 0}, {1, 4, 2, 0}, {2, 4, 5, 0}};
     508             :   }
     509       36639 : }
     510             : 
     511             : void
     512         810 : 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        4050 :       {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        4050 :       {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
     521             : 
     522         810 :   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         810 :     face_rotation = preset_face_indices[min_id_index];
     528         810 :     node_rotation = preset_indices[min_id_index];
     529             :   }
     530        3240 : }
     531             : 
     532             : void
     533         810 : 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         810 :   std::vector<dof_id_type> node_ids(4);
     539        4050 :   for (unsigned int i = 0; i < 4; i++)
     540        3240 :     node_ids[i] = pyramid_nodes[i]->id();
     541             : 
     542         810 :   const unsigned int min_node_id_index = std::distance(
     543         810 :       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         810 :   std::vector<unsigned int> face_rotation;
     549         810 :   std::vector<unsigned int> rotated_indices;
     550         810 :   nodeRotationPYRAMID5(min_node_id_index, face_rotation, rotated_indices);
     551         810 :   std::vector<const Node *> rotated_pyramid_nodes;
     552        4860 :   for (unsigned int i = 0; i < 5; i++)
     553        4050 :     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        2430 :   const std::vector<std::vector<unsigned int>> tet_nodes_set = {{0, 1, 2, 4}, {0, 2, 3, 4}};
     557        2430 :   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        2430 :   for (const auto & tet_face_index : tet_face_indices)
     562             :   {
     563        1620 :     rotated_tet_face_indices.push_back(std::vector<unsigned int>());
     564        8100 :     for (const auto & face_index : tet_face_index)
     565             :     {
     566        6480 :       if (face_index < 5)
     567        4860 :         rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
     568             :       else
     569        1620 :         rotated_tet_face_indices.back().push_back(5);
     570             :     }
     571             :   }
     572             : 
     573        2430 :   for (const auto & tet_nodes : tet_nodes_set)
     574             :   {
     575        1620 :     tet_nodes_list.push_back(std::vector<const Node *>());
     576        8100 :     for (const auto & tet_node : tet_nodes)
     577        6480 :       tet_nodes_list.back().push_back(rotated_pyramid_nodes[tet_node]);
     578             :   }
     579        3240 : }
     580             : 
     581             : void
     582         160 : 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         160 :   std::vector<dof_id_type> converted_elems_ids_to_retain;
     589             :   // Build boundary information of the mesh
     590         160 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     591         160 :   const auto bdry_side_list = boundary_info.build_side_list();
     592       10206 :   for (const auto & elem_to_process : elems_to_process)
     593             :   {
     594       10046 :     switch (mesh.elem_ptr(elem_to_process.first)->type())
     595             :     {
     596        7247 :       case ElemType::HEX8:
     597        7247 :         hexElemSplitter(mesh,
     598             :                         bdry_side_list,
     599        7247 :                         elem_to_process.first,
     600        7247 :                         elem_to_process.second ? converted_elems_ids_to_track
     601             :                                                : converted_elems_ids_to_retain);
     602        7247 :         mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
     603        7247 :         break;
     604         576 :       case ElemType::PYRAMID5:
     605         576 :         pyramidElemSplitter(mesh,
     606             :                             bdry_side_list,
     607         576 :                             elem_to_process.first,
     608         576 :                             elem_to_process.second ? converted_elems_ids_to_track
     609             :                                                    : converted_elems_ids_to_retain);
     610         576 :         mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
     611         576 :         break;
     612         783 :       case ElemType::PRISM6:
     613         783 :         prismElemSplitter(mesh,
     614             :                           bdry_side_list,
     615         783 :                           elem_to_process.first,
     616         783 :                           elem_to_process.second ? converted_elems_ids_to_track
     617             :                                                  : converted_elems_ids_to_retain);
     618         783 :         mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
     619         783 :         break;
     620        1440 :       case ElemType::TET4:
     621        1440 :         if (elem_to_process.second)
     622         576 :           converted_elems_ids_to_track.push_back(elem_to_process.first);
     623             :         else
     624         864 :           converted_elems_ids_to_retain.push_back(elem_to_process.first);
     625        1440 :         break;
     626           0 :       default:
     627           0 :         mooseError("Unexpected element type.");
     628             :     }
     629             :   }
     630             : 
     631         160 :   if (delete_block_to_remove)
     632             :   {
     633        1494 :     for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
     634        1494 :          elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
     635             :          elem_it++)
     636        1494 :       mesh.delete_elem(*elem_it);
     637             : 
     638          88 :     mesh.contract();
     639          88 :     mesh.prepare_for_use();
     640             :   }
     641         160 : }
     642             : 
     643             : void
     644          92 : convert3DMeshToAllTet4(ReplicatedMesh & mesh)
     645             : {
     646             :   // Subdomain ID for new utility blocks must be new
     647          92 :   std::set<subdomain_id_type> subdomain_ids_set;
     648          92 :   mesh.subdomain_ids(subdomain_ids_set);
     649          92 :   const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin();
     650          92 :   const subdomain_id_type block_id_to_remove = max_subdomain_id + 1;
     651          92 :   std::vector<std::pair<dof_id_type, bool>> original_elems;
     652             : 
     653        2362 :   for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
     654             :        elem_it++)
     655             :   {
     656        2274 :     if ((*elem_it)->default_order() != Order::FIRST)
     657           4 :       mooseError("Only first order elements are supported for cutting.");
     658        2270 :     original_elems.push_back(std::make_pair((*elem_it)->id(), false));
     659          88 :   }
     660             : 
     661          88 :   std::vector<dof_id_type> converted_elems_ids_to_track;
     662             : 
     663          88 :   convert3DMeshToAllTet4(
     664             :       mesh, original_elems, converted_elems_ids_to_track, block_id_to_remove, true);
     665          88 : }
     666             : 
     667             : void
     668       37586 : 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       37586 :   elem_side_list.resize(n_elem_sides);
     674             :   const auto selected_bdry_side_list =
     675       37586 :       std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
     676       37586 :   for (auto selected_bdry_side = selected_bdry_side_list.first;
     677       50111 :        selected_bdry_side != selected_bdry_side_list.second;
     678       12525 :        ++selected_bdry_side)
     679             :   {
     680       12525 :     elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
     681             :   }
     682       37586 : }
     683             : }

Generated by: LCOV version 1.14