LCOV - code coverage report
Current view: top level - src/meshgenerators - CutMeshByLevelSetGeneratorBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 271 275 98.5 %
Date: 2025-07-17 01:28:37 Functions: 8 8 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             : #include "CutMeshByLevelSetGeneratorBase.h"
      11             : #include "MooseMeshElementConversionUtils.h"
      12             : #include "MooseMeshUtils.h"
      13             : 
      14             : #include "libmesh/elem.h"
      15             : #include "libmesh/boundary_info.h"
      16             : #include "libmesh/mesh_base.h"
      17             : #include "libmesh/parallel.h"
      18             : #include "libmesh/parallel_algebra.h"
      19             : #include "libmesh/cell_tet4.h"
      20             : 
      21             : // C++ includes
      22             : #include <cmath>
      23             : 
      24             : InputParameters
      25       28690 : CutMeshByLevelSetGeneratorBase::validParams()
      26             : {
      27       28690 :   InputParameters params = MeshGenerator::validParams();
      28       28690 :   params += FunctionParserUtils<false>::validParams();
      29             : 
      30       28690 :   params.addRequiredParam<MeshGeneratorName>("input", "The input mesh that needs to be trimmed.");
      31             : 
      32       28690 :   params.addParam<boundary_id_type>("cut_face_id",
      33             :                                     "The boundary id of the face generated by the cut. An "
      34             :                                     "id will be automatically assigned if not provided.");
      35       86070 :   params.addParam<BoundaryName>(
      36       57380 :       "cut_face_name", BoundaryName(), "The boundary name of the face generated by the cut.");
      37             : 
      38       28690 :   params.addClassDescription(
      39             :       "This CutMeshByLevelSetGeneratorBase object is designed to be the base class of mesh "
      40             :       "generator that cuts a 3D mesh based on an analytic level set function. The level set "
      41             :       "function could be provided explicitly or indirectly.");
      42             : 
      43       28690 :   return params;
      44           0 : }
      45             : 
      46          80 : CutMeshByLevelSetGeneratorBase::CutMeshByLevelSetGeneratorBase(const InputParameters & parameters)
      47             :   : MeshGenerator(parameters),
      48             :     FunctionParserUtils<false>(parameters),
      49          80 :     _input_name(getParam<MeshGeneratorName>("input")),
      50          80 :     _cut_face_name(getParam<BoundaryName>("cut_face_name")),
      51         160 :     _input(getMeshByName(_input_name))
      52             : {
      53          80 :   _cut_face_id = isParamValid("cut_face_id") ? getParam<boundary_id_type>("cut_face_id") : -1;
      54          80 : }
      55             : 
      56             : std::unique_ptr<MeshBase>
      57          80 : CutMeshByLevelSetGeneratorBase::generate()
      58             : {
      59          80 :   auto replicated_mesh_ptr = dynamic_cast<ReplicatedMesh *>(_input.get());
      60          80 :   if (!replicated_mesh_ptr)
      61           4 :     paramError("input", "Input is not a replicated mesh, which is required");
      62         148 :   if (*(replicated_mesh_ptr->elem_dimensions().begin()) != 3 ||
      63         148 :       *(replicated_mesh_ptr->elem_dimensions().rbegin()) != 3)
      64           4 :     paramError(
      65             :         "input",
      66             :         "Only 3D meshes are supported. Use XYMeshLineCutter for 2D meshes if applicable. Mixed "
      67             :         "dimensional meshes are not supported at the moment.");
      68             : 
      69          72 :   ReplicatedMesh & mesh = *replicated_mesh_ptr;
      70             : 
      71          72 :   if (!_cut_face_name.empty())
      72             :   {
      73          44 :     if (MooseMeshUtils::hasBoundaryName(mesh, _cut_face_name))
      74             :     {
      75             :       const boundary_id_type exist_cut_face_id =
      76          12 :           MooseMeshUtils::getBoundaryID(_cut_face_name, mesh);
      77          12 :       if (_cut_face_id != -1 && _cut_face_id != exist_cut_face_id)
      78           4 :         paramError("cut_face_id",
      79             :                    "The cut face boundary name and id are both provided, but they are inconsistent "
      80             :                    "with an existing boundary name which has a different id.");
      81             :       else
      82           8 :         _cut_face_id = exist_cut_face_id;
      83             :     }
      84             :     else
      85             :     {
      86          32 :       if (_cut_face_id == -1)
      87           8 :         _cut_face_id = MooseMeshUtils::getNextFreeBoundaryID(mesh);
      88          32 :       mesh.get_boundary_info().sideset_name(_cut_face_id) = _cut_face_name;
      89             :     }
      90             :   }
      91          28 :   else if (_cut_face_id == -1)
      92             :   {
      93          20 :     _cut_face_id = MooseMeshUtils::getNextFreeBoundaryID(mesh);
      94             :   }
      95             : 
      96             :   // Subdomain ID for new utility blocks must be new
      97          68 :   std::set<subdomain_id_type> subdomain_ids_set;
      98          68 :   mesh.subdomain_ids(subdomain_ids_set);
      99          68 :   const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin();
     100          68 :   const subdomain_id_type block_id_to_remove = max_subdomain_id + 1;
     101             : 
     102             :   // For the boolean value in the pair, true means the element is crossed by the cutting plane
     103             :   // false means the element is on the remaining side
     104          68 :   std::vector<std::pair<dof_id_type, bool>> cross_and_remained_elems_pre_convert;
     105             : 
     106       11332 :   for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
     107             :        elem_it++)
     108             :   {
     109       11268 :     const unsigned int & n_vertices = (*elem_it)->n_vertices();
     110       11268 :     unsigned short elem_vertices_counter(0);
     111       93284 :     for (unsigned int i = 0; i < n_vertices; i++)
     112             :     {
     113             :       // We define elem_vertices_counter in this way so that those elements with one face on the
     114             :       // plane are also processed to have the cut face boundary assigned.
     115       82016 :       if (pointLevelSetRelation((*(*elem_it)->node_ptr(i))) !=
     116             :           PointLevelSetRelationIndex::level_set_in_side)
     117       50948 :         elem_vertices_counter++;
     118             :     }
     119       11268 :     if (elem_vertices_counter == n_vertices)
     120        4352 :       (*elem_it)->subdomain_id() = block_id_to_remove;
     121             :     else
     122             :     {
     123             :       // Check if any elements to be processed are not first order
     124        6916 :       if ((*elem_it)->default_order() != Order::FIRST)
     125           4 :         mooseError("Only first order elements are supported for cutting.");
     126        6912 :       cross_and_remained_elems_pre_convert.push_back(
     127       13824 :           std::make_pair((*elem_it)->id(), elem_vertices_counter > 0));
     128             :     }
     129          64 :   }
     130             : 
     131          64 :   std::vector<dof_id_type> converted_elems_ids_to_cut;
     132             :   // Then convert these non-TET4 elements into TET4 elements
     133          64 :   MooseMeshElementConversionUtils::convert3DMeshToAllTet4(mesh,
     134             :                                                           cross_and_remained_elems_pre_convert,
     135             :                                                           converted_elems_ids_to_cut,
     136             :                                                           block_id_to_remove,
     137             :                                                           false);
     138             : 
     139          64 :   std::vector<const Node *> new_on_plane_nodes;
     140             :   // We build the sideset information now, as we only need the information of the elements before
     141             :   // cutting
     142          64 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     143          64 :   const auto bdry_side_list = boundary_info.build_side_list();
     144             :   // Cut the TET4 Elements
     145       25824 :   for (const auto & converted_elems_id_to_cut : converted_elems_ids_to_cut)
     146             :   {
     147       25760 :     tet4ElemCutter(
     148             :         mesh, bdry_side_list, converted_elems_id_to_cut, block_id_to_remove, new_on_plane_nodes);
     149             :   }
     150             : 
     151             :   // Delete the block to remove
     152       33600 :   for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
     153       33600 :        elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
     154             :        elem_it++)
     155       33600 :     mesh.delete_elem(*elem_it);
     156             : 
     157          64 :   mesh.contract();
     158          64 :   mesh.set_isnt_prepared();
     159         128 :   return std::move(_input);
     160          64 : }
     161             : 
     162             : CutMeshByLevelSetGeneratorBase::PointLevelSetRelationIndex
     163      185056 : CutMeshByLevelSetGeneratorBase::pointLevelSetRelation(const Point & point)
     164             : {
     165      185056 :   const Real level_set_value = levelSetEvaluator(point);
     166      185056 :   if (MooseUtils::absoluteFuzzyLessThan(level_set_value, 0.0))
     167       75804 :     return PointLevelSetRelationIndex::level_set_in_side;
     168      109252 :   else if (MooseUtils::absoluteFuzzyGreaterThan(level_set_value, 0.0))
     169      105312 :     return PointLevelSetRelationIndex::level_set_out_side;
     170             :   else
     171        3940 :     return PointLevelSetRelationIndex::on_level_set;
     172             : }
     173             : 
     174             : Point
     175       60280 : CutMeshByLevelSetGeneratorBase::pointPairLevelSetInterception(const Point & point1,
     176             :                                                               const Point & point2)
     177             : {
     178       60280 :   Real dist1 = levelSetEvaluator(point1);
     179       60280 :   Real dist2 = levelSetEvaluator(point2);
     180             : 
     181       60280 :   if (MooseUtils::absoluteFuzzyEqual(dist1, 0.0) || MooseUtils::absoluteFuzzyEqual(dist2, 0.0))
     182           0 :     mooseError("At least one of the two points are on the plane.");
     183       60280 :   if (MooseUtils::absoluteFuzzyGreaterThan(dist1 * dist2, 0.0))
     184           0 :     mooseError("The two points are on the same side of the plane.");
     185             : 
     186       60280 :   Point p1(point1);
     187       60280 :   Point p2(point2);
     188       60280 :   Real dist = abs(dist1) + abs(dist2);
     189       60280 :   Point mid_point;
     190             : 
     191             :   // Bisection method to find midpoint
     192     2324288 :   while (MooseUtils::absoluteFuzzyGreaterThan(dist, 0.0))
     193             :   {
     194     2264008 :     mid_point = 0.5 * (p1 + p2);
     195     2264008 :     const Real dist_mid = levelSetEvaluator(mid_point);
     196             :     // We do not need Fuzzy here as it will be covered by the while loop
     197     2264008 :     if (dist_mid == 0.0)
     198        1984 :       dist = 0.0;
     199     2262024 :     else if (dist_mid * dist1 < 0.0)
     200             :     {
     201     1121248 :       p2 = mid_point;
     202     1121248 :       dist2 = levelSetEvaluator(p2);
     203     1121248 :       dist = abs(dist1) + abs(dist2);
     204             :     }
     205             :     else
     206             :     {
     207     1140776 :       p1 = mid_point;
     208     1140776 :       dist1 = levelSetEvaluator(p1);
     209     1140776 :       dist = abs(dist1) + abs(dist2);
     210             :     }
     211             :   }
     212      120560 :   return mid_point;
     213             : }
     214             : 
     215             : const Node *
     216       60280 : CutMeshByLevelSetGeneratorBase::nonDuplicateNodeCreator(
     217             :     ReplicatedMesh & mesh,
     218             :     std::vector<const Node *> & new_on_plane_nodes,
     219             :     const Point & new_point) const
     220             : {
     221    28502976 :   for (const auto & new_on_plane_node : new_on_plane_nodes)
     222             :   {
     223    28490664 :     if (MooseUtils::absoluteFuzzyEqual((*new_on_plane_node - new_point).norm(), 0.0))
     224       47968 :       return new_on_plane_node;
     225             :   }
     226       12312 :   new_on_plane_nodes.push_back(mesh.add_point(new_point));
     227       12312 :   return new_on_plane_nodes.back();
     228             : }
     229             : 
     230             : void
     231       25760 : CutMeshByLevelSetGeneratorBase::tet4ElemCutter(
     232             :     ReplicatedMesh & mesh,
     233             :     const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
     234             :     const dof_id_type elem_id,
     235             :     const subdomain_id_type & block_id_to_remove,
     236             :     std::vector<const Node *> & new_on_plane_nodes)
     237             : {
     238             :   // Retrieve boundary information for the mesh
     239       25760 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     240             :   // Create a list of sidesets involving the element to be split
     241             :   // It might be complex to assign the boundary id to the new elements
     242             :   // In TET4, none of the four faces have the same normal vector
     243             :   // So we are using the normal vector to identify the faces that
     244             :   // need to be assigned the same boundary id
     245       25760 :   std::vector<Point> elem_side_normal_list;
     246       25760 :   elem_side_normal_list.resize(4);
     247      128800 :   for (const auto i : make_range(4))
     248             :   {
     249      103040 :     auto elem_side = mesh.elem_ptr(elem_id)->side_ptr(i);
     250      206080 :     elem_side_normal_list[i] = (*elem_side->node_ptr(1) - *elem_side->node_ptr(0))
     251      103040 :                                    .cross(*elem_side->node_ptr(2) - *elem_side->node_ptr(1))
     252      103040 :                                    .unit();
     253      103040 :   }
     254             : 
     255       25760 :   std::vector<std::vector<boundary_id_type>> elem_side_list;
     256       25760 :   MooseMeshElementConversionUtils::elementBoundaryInfoCollector(
     257             :       bdry_side_list, elem_id, 4, elem_side_list);
     258             : 
     259       25760 :   std::vector<PointLevelSetRelationIndex> node_plane_relation(4);
     260       25760 :   std::vector<const Node *> tet4_nodes(4);
     261       25760 :   std::vector<const Node *> tet4_nodes_on_plane;
     262       25760 :   std::vector<const Node *> tet4_nodes_outside_plane;
     263       25760 :   std::vector<const Node *> tet4_nodes_inside_plane;
     264             :   // Sort tetrahedral nodes based on their positioning wrt the plane
     265      128800 :   for (unsigned int i = 0; i < 4; i++)
     266             :   {
     267      103040 :     tet4_nodes[i] = mesh.elem_ptr(elem_id)->node_ptr(i);
     268      103040 :     node_plane_relation[i] = pointLevelSetRelation(*tet4_nodes[i]);
     269      103040 :     if (node_plane_relation[i] == PointLevelSetRelationIndex::on_level_set)
     270        1952 :       tet4_nodes_on_plane.push_back(tet4_nodes[i]);
     271      101088 :     else if (node_plane_relation[i] == PointLevelSetRelationIndex::level_set_out_side)
     272       56352 :       tet4_nodes_outside_plane.push_back(tet4_nodes[i]);
     273             :     else
     274       44736 :       tet4_nodes_inside_plane.push_back(tet4_nodes[i]);
     275             :   }
     276       25760 :   std::vector<Elem *> elems_tet4;
     277       25760 :   bool new_elements_created(false);
     278             :   // No cutting needed if no nodes are outside the plane
     279       25760 :   if (tet4_nodes_outside_plane.size() == 0)
     280             :   {
     281        2976 :     if (tet4_nodes_on_plane.size() == 3)
     282             :     {
     283             :       // Record the element for future cross section boundary assignment
     284          64 :       elems_tet4.push_back(mesh.elem_ptr(elem_id));
     285             :     }
     286             :   }
     287             :   // Remove the element if all the nodes are outside the plane
     288       22784 :   else if (tet4_nodes_inside_plane.size() == 0)
     289             :   {
     290        4160 :     mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
     291        4160 :     if (tet4_nodes_on_plane.size() == 3)
     292             :     {
     293             :       // I think the neighboring element will be handled,
     294             :       // So we do not need to assign the cross section boundary here
     295             :     }
     296             :   }
     297             :   // As we have nodes on both sides, six different scenarios are possible
     298             :   else
     299             :   {
     300       18624 :     new_elements_created = true;
     301       18624 :     if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 3)
     302             :     {
     303        7800 :       std::vector<const Node *> new_plane_nodes;
     304             :       // A smaller TET4 element is created, this solution is unique
     305       31200 :       for (const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
     306             :       {
     307       23400 :         new_plane_nodes.push_back(nonDuplicateNodeCreator(
     308             :             mesh,
     309             :             new_on_plane_nodes,
     310       46800 :             pointPairLevelSetInterception(*tet4_node_outside_plane, *tet4_nodes_inside_plane[0])));
     311             :       }
     312        7800 :       auto new_elem_tet4 = std::make_unique<Tet4>();
     313        7800 :       new_elem_tet4->set_node(0, const_cast<Node *>(tet4_nodes_inside_plane[0]));
     314        7800 :       new_elem_tet4->set_node(1, const_cast<Node *>(new_plane_nodes[0]));
     315        7800 :       new_elem_tet4->set_node(2, const_cast<Node *>(new_plane_nodes[1]));
     316        7800 :       new_elem_tet4->set_node(3, const_cast<Node *>(new_plane_nodes[2]));
     317        7800 :       new_elem_tet4->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     318        7800 :       elems_tet4.push_back(mesh.add_elem(std::move(new_elem_tet4)));
     319        7800 :     }
     320       10824 :     else if (tet4_nodes_inside_plane.size() == 2 && tet4_nodes_outside_plane.size() == 2)
     321             :     {
     322        5328 :       std::vector<const Node *> new_plane_nodes;
     323             :       // 3 smaller TET3 elements are created
     324       15984 :       for (const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
     325             :       {
     326       31968 :         for (const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
     327             :         {
     328       21312 :           new_plane_nodes.push_back(nonDuplicateNodeCreator(
     329             :               mesh,
     330             :               new_on_plane_nodes,
     331       42624 :               pointPairLevelSetInterception(*tet4_node_outside_plane, *tet4_node_inside_plane)));
     332             :         }
     333             :       }
     334        5328 :       std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[1],
     335        5328 :                                                    new_plane_nodes[3],
     336        5328 :                                                    new_plane_nodes[1],
     337        5328 :                                                    tet4_nodes_inside_plane[0],
     338        5328 :                                                    new_plane_nodes[2],
     339        5328 :                                                    new_plane_nodes[0]};
     340        5328 :       std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
     341        5328 :       std::vector<std::vector<const Node *>> optimized_node_list;
     342        5328 :       MooseMeshElementConversionUtils::prismNodesToTetNodesDeterminer(
     343             :           new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
     344             : 
     345       21312 :       for (unsigned int i = 0; i < optimized_node_list.size(); i++)
     346             :       {
     347       15984 :         auto new_elem_tet4 = std::make_unique<Tet4>();
     348       15984 :         new_elem_tet4->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
     349       15984 :         new_elem_tet4->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
     350       15984 :         new_elem_tet4->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
     351       15984 :         new_elem_tet4->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
     352       15984 :         new_elem_tet4->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     353       15984 :         elems_tet4.push_back(mesh.add_elem(std::move(new_elem_tet4)));
     354       15984 :       }
     355        5328 :     }
     356        5496 :     else if (tet4_nodes_inside_plane.size() == 3 && tet4_nodes_outside_plane.size() == 1)
     357             :     {
     358        4832 :       std::vector<const Node *> new_plane_nodes;
     359             :       // 3 smaller Tet4 elements are created
     360       19328 :       for (const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
     361             :       {
     362       14496 :         new_plane_nodes.push_back(nonDuplicateNodeCreator(
     363             :             mesh,
     364             :             new_on_plane_nodes,
     365       28992 :             pointPairLevelSetInterception(*tet4_node_inside_plane, *tet4_nodes_outside_plane[0])));
     366             :       }
     367        4832 :       std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[0],
     368        4832 :                                                    tet4_nodes_inside_plane[1],
     369        4832 :                                                    tet4_nodes_inside_plane[2],
     370        4832 :                                                    new_plane_nodes[0],
     371        4832 :                                                    new_plane_nodes[1],
     372        4832 :                                                    new_plane_nodes[2]};
     373        4832 :       std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
     374        4832 :       std::vector<std::vector<const Node *>> optimized_node_list;
     375        4832 :       MooseMeshElementConversionUtils::prismNodesToTetNodesDeterminer(
     376             :           new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
     377             : 
     378       19328 :       for (unsigned int i = 0; i < optimized_node_list.size(); i++)
     379             :       {
     380       14496 :         auto new_elem_tet4 = std::make_unique<Tet4>();
     381       14496 :         new_elem_tet4->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
     382       14496 :         new_elem_tet4->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
     383       14496 :         new_elem_tet4->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
     384       14496 :         new_elem_tet4->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
     385       14496 :         new_elem_tet4->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     386       14496 :         elems_tet4.push_back(mesh.add_elem(std::move(new_elem_tet4)));
     387       14496 :       }
     388        4832 :     }
     389         664 :     else if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 1)
     390             :     {
     391         256 :       auto new_plane_node = nonDuplicateNodeCreator(
     392             :           mesh,
     393             :           new_on_plane_nodes,
     394         256 :           pointPairLevelSetInterception(*tet4_nodes_inside_plane[0], *tet4_nodes_outside_plane[0]));
     395             :       // A smaller Tet4 is created, this solution is unique
     396         256 :       auto new_elem_tet4 = std::make_unique<Tet4>();
     397         256 :       new_elem_tet4->set_node(0, const_cast<Node *>(new_plane_node));
     398         256 :       new_elem_tet4->set_node(1, const_cast<Node *>(tet4_nodes_on_plane[0]));
     399         256 :       new_elem_tet4->set_node(2, const_cast<Node *>(tet4_nodes_on_plane[1]));
     400         256 :       new_elem_tet4->set_node(3, const_cast<Node *>(tet4_nodes_inside_plane[0]));
     401         256 :       new_elem_tet4->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     402         256 :       elems_tet4.push_back(mesh.add_elem(std::move(new_elem_tet4)));
     403         256 :     }
     404         408 :     else if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 2)
     405             :     {
     406         200 :       std::vector<const Node *> new_plane_nodes;
     407             :       // A smaller Tet4 element is created, this solution is unique
     408         600 :       for (const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
     409             :       {
     410         400 :         new_plane_nodes.push_back(nonDuplicateNodeCreator(
     411             :             mesh,
     412             :             new_on_plane_nodes,
     413         800 :             pointPairLevelSetInterception(*tet4_node_outside_plane, *tet4_nodes_inside_plane[0])));
     414             :       }
     415         200 :       auto new_elem_tet4 = std::make_unique<Tet4>();
     416         200 :       new_elem_tet4->set_node(0, const_cast<Node *>(new_plane_nodes[0]));
     417         200 :       new_elem_tet4->set_node(1, const_cast<Node *>(new_plane_nodes[1]));
     418         200 :       new_elem_tet4->set_node(2, const_cast<Node *>(tet4_nodes_on_plane[0]));
     419         200 :       new_elem_tet4->set_node(3, const_cast<Node *>(tet4_nodes_inside_plane[0]));
     420         200 :       new_elem_tet4->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     421         200 :       elems_tet4.push_back(mesh.add_elem(std::move(new_elem_tet4)));
     422         200 :     }
     423         208 :     else if (tet4_nodes_inside_plane.size() == 2 && tet4_nodes_outside_plane.size() == 1)
     424             :     {
     425         208 :       std::vector<const Node *> new_plane_nodes;
     426             :       // 2 smaller TET4 elements are created
     427         624 :       for (const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
     428             :       {
     429         416 :         new_plane_nodes.push_back(nonDuplicateNodeCreator(
     430             :             mesh,
     431             :             new_on_plane_nodes,
     432         832 :             pointPairLevelSetInterception(*tet4_node_inside_plane, *tet4_nodes_outside_plane[0])));
     433             :       }
     434         208 :       std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[0],
     435         208 :                                                    tet4_nodes_inside_plane[1],
     436         208 :                                                    new_plane_nodes[1],
     437         208 :                                                    new_plane_nodes[0],
     438         208 :                                                    tet4_nodes_on_plane[0]};
     439         208 :       std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
     440         208 :       std::vector<std::vector<const Node *>> optimized_node_list;
     441         208 :       MooseMeshElementConversionUtils::pyramidNodesToTetNodesDeterminer(
     442             :           new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
     443             : 
     444         624 :       for (unsigned int i = 0; i < optimized_node_list.size(); i++)
     445             :       {
     446         416 :         auto new_elem_tet4 = std::make_unique<Tet4>();
     447         416 :         new_elem_tet4->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
     448         416 :         new_elem_tet4->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
     449         416 :         new_elem_tet4->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
     450         416 :         new_elem_tet4->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
     451         416 :         new_elem_tet4->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
     452         416 :         elems_tet4.push_back(mesh.add_elem(std::move(new_elem_tet4)));
     453         416 :       }
     454         208 :     }
     455             :     else
     456           0 :       mooseError("Unexpected scenario.");
     457             : 
     458       18624 :     mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
     459             :   }
     460             : 
     461       64976 :   for (auto & elem_tet4 : elems_tet4)
     462             :   {
     463       39216 :     if (new_elements_created)
     464             :     {
     465       39152 :       if (elem_tet4->volume() < 0.0)
     466             :       {
     467       16928 :         Node * temp = elem_tet4->node_ptr(0);
     468       16928 :         elem_tet4->set_node(0, elem_tet4->node_ptr(1));
     469       16928 :         elem_tet4->set_node(1, temp);
     470             :       }
     471             :     }
     472             :     // Find the boundary id of the new element
     473      196080 :     for (unsigned int i = 0; i < 4; i++)
     474             :     {
     475      156864 :       const Point & side_pt_0 = *elem_tet4->side_ptr(i)->node_ptr(0);
     476      156864 :       const Point & side_pt_1 = *elem_tet4->side_ptr(i)->node_ptr(1);
     477      156864 :       const Point & side_pt_2 = *elem_tet4->side_ptr(i)->node_ptr(2);
     478             : 
     479      156864 :       const Point side_normal = (side_pt_1 - side_pt_0).cross(side_pt_2 - side_pt_1).unit();
     480      784320 :       for (unsigned int j = 0; j < 4; j++)
     481             :       {
     482      627456 :         if (new_elements_created)
     483             :         {
     484      626432 :           if (MooseUtils::absoluteFuzzyEqual(side_normal * elem_side_normal_list[j], 1.0))
     485             :           {
     486       92864 :             for (const auto & side_info : elem_side_list[j])
     487             :             {
     488        1264 :               boundary_info.add_side(elem_tet4, i, side_info);
     489             :             }
     490             :           }
     491             :         }
     492             :       }
     493      313728 :       if (MooseUtils::absoluteFuzzyEqual(levelSetEvaluator(side_pt_0), 0.0) &&
     494      246696 :           MooseUtils::absoluteFuzzyEqual(levelSetEvaluator(side_pt_1), 0.0) &&
     495      246696 :           MooseUtils::absoluteFuzzyEqual(levelSetEvaluator(side_pt_2), 0.0))
     496             :       {
     497             : 
     498       24016 :         boundary_info.add_side(elem_tet4, i, _cut_face_id);
     499             :       }
     500             :     }
     501             :   }
     502       25760 : }
     503             : 
     504             : Real
     505     5193760 : CutMeshByLevelSetGeneratorBase::levelSetEvaluator(const Point & point)
     506             : {
     507     5193760 :   _func_params[0] = point(0);
     508     5193760 :   _func_params[1] = point(1);
     509     5193760 :   _func_params[2] = point(2);
     510     5193760 :   return evaluate(_func_level_set);
     511             : }

Generated by: LCOV version 1.14