LCOV - code coverage report
Current view: top level - src/userobjects - CrackMeshCut3DUserObject.C (source / functions) Hit Total Coverage
Test: idaholab/moose xfem: #31405 (292dce) with base fef103 Lines: 513 606 84.7 %
Date: 2025-09-04 07:58:55 Functions: 26 31 83.9 %
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 "CrackMeshCut3DUserObject.h"
      11             : 
      12             : #include "XFEMFuncs.h"
      13             : #include "MooseError.h"
      14             : #include "libmesh/string_to_enum.h"
      15             : #include "MooseMesh.h"
      16             : #include "MooseEnum.h"
      17             : #include "libmesh/face_tri3.h"
      18             : #include "libmesh/edge_edge2.h"
      19             : #include "libmesh/serial_mesh.h"
      20             : #include "libmesh/plane.h"
      21             : #include "libmesh/mesh_tools.h"
      22             : #include "Function.h"
      23             : 
      24             : registerMooseObject("XFEMApp", CrackMeshCut3DUserObject);
      25             : 
      26             : InputParameters
      27          24 : CrackMeshCut3DUserObject::validParams()
      28             : {
      29          24 :   InputParameters params = GeometricCutUserObject::validParams();
      30          48 :   params.addRequiredParam<MeshFileName>(
      31             :       "mesh_file",
      32             :       "Mesh file for the XFEM geometric cut; currently only the xda type is supported");
      33          48 :   MooseEnum growthDirection("MAX_HOOP_STRESS FUNCTION", "FUNCTION");
      34          48 :   params.addParam<MooseEnum>(
      35             :       "growth_dir_method", growthDirection, "choose from FUNCTION, MAX_HOOP_STRESS");
      36          48 :   MooseEnum growthRate("FATIGUE FUNCTION", "FUNCTION");
      37          48 :   params.addParam<MooseEnum>("growth_rate_method", growthRate, "choose from FUNCTION, FATIGUE");
      38          48 :   params.addParam<FunctionName>("growth_direction_x",
      39             :                                 "Function defining x-component of crack growth direction");
      40          48 :   params.addParam<FunctionName>("growth_direction_y",
      41             :                                 "Function defining y-component of crack growth direction");
      42          48 :   params.addParam<FunctionName>("growth_direction_z",
      43             :                                 "Function defining z-component of crack growth direction");
      44             : 
      45          48 :   params.addParam<FunctionName>("growth_rate", "Function defining crack growth rate");
      46          48 :   params.addParam<Real>(
      47          48 :       "size_control", 0, "Criterion for refining elements while growing the crack");
      48          48 :   params.addParam<unsigned int>("n_step_growth", 0, "Number of steps for crack growth");
      49          48 :   params.addParam<std::vector<dof_id_type>>("crack_front_nodes",
      50             :                                             "Set of nodes to define crack front");
      51          24 :   params.addClassDescription("Creates a UserObject for a mesh cutter in 3D problems");
      52          24 :   return params;
      53          24 : }
      54             : 
      55             : // This code does not allow predefined crack growth as a function of time
      56             : // all inital cracks are defined at t_start = t_end = 0
      57          12 : CrackMeshCut3DUserObject::CrackMeshCut3DUserObject(const InputParameters & parameters)
      58             :   : GeometricCutUserObject(parameters, true),
      59          12 :     _mesh(_subproblem.mesh()),
      60          24 :     _growth_dir_method(getParam<MooseEnum>("growth_dir_method").getEnum<GrowthDirectionEnum>()),
      61          24 :     _growth_rate_method(getParam<MooseEnum>("growth_rate_method").getEnum<GrowthRateEnum>()),
      62          12 :     _n_step_growth(getParam<unsigned int>("n_step_growth")),
      63          12 :     _is_mesh_modified(false),
      64          20 :     _func_x(parameters.isParamValid("growth_direction_x") ? &getFunction("growth_direction_x")
      65             :                                                           : nullptr),
      66          20 :     _func_y(parameters.isParamValid("growth_direction_y") ? &getFunction("growth_direction_y")
      67             :                                                           : nullptr),
      68          20 :     _func_z(parameters.isParamValid("growth_direction_z") ? &getFunction("growth_direction_z")
      69             :                                                           : nullptr),
      70          48 :     _func_v(parameters.isParamValid("growth_rate") ? &getFunction("growth_rate") : nullptr)
      71             : {
      72          12 :   _grow = (_n_step_growth == 0 ? 0 : 1);
      73             : 
      74          12 :   if (_grow)
      75             :   {
      76          24 :     if (!isParamValid("size_control"))
      77           0 :       mooseError("Crack growth needs size control");
      78             : 
      79          24 :     _size_control = getParam<Real>("size_control");
      80             : 
      81          12 :     if (_growth_dir_method == GrowthDirectionEnum::FUNCTION &&
      82           8 :         (_func_x == nullptr || _func_y == nullptr || _func_z == nullptr))
      83           0 :       mooseError("function is not specified for the function method that defines growth direction");
      84             : 
      85          12 :     if (_growth_dir_method == GrowthDirectionEnum::FUNCTION && _func_v == nullptr)
      86           0 :       mooseError("function is not specified for the function method that defines growth rate");
      87             : 
      88          12 :     if (_growth_dir_method == GrowthDirectionEnum::FUNCTION && _func_v == nullptr)
      89           0 :       mooseError("function with a variable is not specified for the fatigue method that defines "
      90             :                  "growth rate");
      91             : 
      92          24 :     if (isParamValid("crack_front_nodes"))
      93             :     {
      94          24 :       _tracked_crack_front_points = getParam<std::vector<dof_id_type>>("crack_front_nodes");
      95           8 :       _num_crack_front_points = _tracked_crack_front_points.size();
      96           8 :       _cfd = true;
      97             :     }
      98             :     else
      99           4 :       _cfd = false;
     100             :   }
     101             : 
     102          12 :   if ((_growth_dir_method == GrowthDirectionEnum::MAX_HOOP_STRESS ||
     103           8 :        _growth_rate_method == GrowthRateEnum::FATIGUE) &&
     104           8 :       !_cfd)
     105           0 :     mooseError("'crack_front_nodes' is not specified to use crack growth criteria!");
     106             : 
     107             :   // only the xda type is currently supported
     108          12 :   MeshFileName xfem_cut_mesh_file = getParam<MeshFileName>("mesh_file");
     109          12 :   _cut_mesh = std::make_unique<ReplicatedMesh>(_communicator);
     110          12 :   _cut_mesh->read(xfem_cut_mesh_file);
     111             : 
     112             :   // test element type; only tri3 elements are allowed
     113         168 :   for (const auto & cut_elem : _cut_mesh->element_ptr_range())
     114             :   {
     115          72 :     if (cut_elem->n_nodes() != _cut_elem_nnode)
     116           0 :       mooseError("The input cut mesh should include tri elements only!");
     117          72 :     if (cut_elem->dim() != _cut_elem_dim)
     118           0 :       mooseError("The input cut mesh should have 2D elements only!");
     119          12 :   }
     120          12 : }
     121             : 
     122             : void
     123          12 : CrackMeshCut3DUserObject::initialSetup()
     124             : {
     125          12 :   if (_cfd)
     126             :   {
     127           8 :     _crack_front_definition =
     128           8 :         &_fe_problem.getUserObject<CrackFrontDefinition>("crackFrontDefinition");
     129           8 :     _crack_front_points = _tracked_crack_front_points;
     130             :   }
     131             : 
     132          12 :   if (_grow)
     133             :   {
     134          12 :     findBoundaryNodes();
     135          12 :     findBoundaryEdges();
     136          12 :     sortBoundaryNodes();
     137             :   }
     138             : 
     139          12 :   if (_growth_rate_method == GrowthRateEnum::FATIGUE)
     140             :   {
     141           4 :     _dn.clear();
     142           4 :     _n.clear();
     143             :   }
     144          12 : }
     145             : 
     146             : void
     147          59 : CrackMeshCut3DUserObject::initialize()
     148             : {
     149          59 :   _is_mesh_modified = false;
     150             : 
     151          59 :   if (_grow)
     152             :   {
     153          59 :     if (_t_step == 1)
     154          12 :       _last_step_initialized = 1;
     155             : 
     156          59 :     _stop = 0;
     157             : 
     158          59 :     if (_t_step > 1 && _t_step != _last_step_initialized)
     159             :     {
     160          32 :       _last_step_initialized = _t_step;
     161             : 
     162          64 :       for (unsigned int i = 0; i < _n_step_growth; ++i)
     163             :       {
     164          32 :         if (_stop != 1)
     165             :         {
     166          32 :           findActiveBoundaryNodes();
     167          32 :           findActiveBoundaryDirection();
     168          32 :           _is_mesh_modified = true;
     169          32 :           growFront();
     170          32 :           sortFrontNodes();
     171          32 :           if (_inactive_boundary_pos.size() != 0)
     172          32 :             findFrontIntersection();
     173          32 :           refineFront();
     174          32 :           triangulation();
     175          32 :           joinBoundary();
     176             :         }
     177             :       }
     178             :     }
     179             :   }
     180          59 :   if (_cfd)
     181          42 :     _crack_front_definition->isCutterModified(_is_mesh_modified);
     182          59 : }
     183             : 
     184             : bool
     185           0 : CrackMeshCut3DUserObject::cutElementByGeometry(const Elem * /*elem*/,
     186             :                                                std::vector<Xfem::CutEdge> & /*cut_edges*/,
     187             :                                                std::vector<Xfem::CutNode> & /*cut_nodes*/) const
     188             : {
     189           0 :   mooseError("invalid method for 3D mesh cutting");
     190             :   return false;
     191             : }
     192             : 
     193             : bool
     194        2406 : CrackMeshCut3DUserObject::cutElementByGeometry(const Elem * elem,
     195             :                                                std::vector<Xfem::CutFace> & cut_faces) const
     196             : // With the crack defined by a planar mesh, this method cuts a solid element by all elements in the
     197             : // planar mesh
     198             : // TODO: Time evolving cuts not yet supported in 3D (hence the lack of use of the time variable)
     199             : {
     200             :   bool elem_cut = false;
     201             : 
     202        2406 :   if (elem->dim() != _elem_dim)
     203           0 :     mooseError("The structural mesh to be cut by a surface mesh must be 3D!");
     204             : 
     205       16842 :   for (unsigned int i = 0; i < elem->n_sides(); ++i)
     206             :   {
     207             :     // This returns the lowest-order type of side.
     208       14436 :     std::unique_ptr<const Elem> curr_side = elem->side_ptr(i);
     209       14436 :     if (curr_side->dim() != 2)
     210           0 :       mooseError("In cutElementByGeometry dimension of side must be 2, but it is ",
     211           0 :                  curr_side->dim());
     212       14436 :     unsigned int n_edges = curr_side->n_sides();
     213             : 
     214             :     std::vector<unsigned int> cut_edges;
     215             :     std::vector<Real> cut_pos;
     216             : 
     217       72180 :     for (unsigned int j = 0; j < n_edges; j++)
     218             :     {
     219             :       // This returns the lowest-order type of side.
     220       57744 :       std::unique_ptr<const Elem> curr_edge = curr_side->side_ptr(j);
     221       57744 :       if (curr_edge->type() != EDGE2)
     222           0 :         mooseError("In cutElementByGeometry face edge must be EDGE2, but type is: ",
     223           0 :                    libMesh::Utility::enum_to_string(curr_edge->type()),
     224             :                    " base element type is: ",
     225           0 :                    libMesh::Utility::enum_to_string(elem->type()));
     226             :       const Node * node1 = curr_edge->node_ptr(0);
     227             :       const Node * node2 = curr_edge->node_ptr(1);
     228             : 
     229     1565280 :       for (const auto & cut_elem : _cut_mesh->element_ptr_range())
     230             :       {
     231             :         std::vector<Point> vertices;
     232             : 
     233     2899584 :         for (auto & node : cut_elem->node_ref_range())
     234             :         {
     235     2174688 :           Point & this_point = node;
     236     2174688 :           vertices.push_back(this_point);
     237             :         }
     238             : 
     239             :         Point intersection;
     240      724896 :         if (intersectWithEdge(*node1, *node2, vertices, intersection))
     241             :         {
     242        3336 :           cut_edges.push_back(j);
     243        3336 :           cut_pos.emplace_back(getRelativePosition(*node1, *node2, intersection));
     244             :         }
     245      782640 :       }
     246       57744 :     }
     247             : 
     248             :     // if two edges of an element are cut, it is considered as an element being cut
     249       14436 :     if (cut_edges.size() == 2)
     250             :     {
     251             :       elem_cut = true;
     252             :       Xfem::CutFace mycut;
     253        1578 :       mycut._face_id = i;
     254        1578 :       mycut._face_edge.push_back(cut_edges[0]);
     255        1578 :       mycut._face_edge.push_back(cut_edges[1]);
     256        1578 :       mycut._position.push_back(cut_pos[0]);
     257        1578 :       mycut._position.push_back(cut_pos[1]);
     258        1578 :       cut_faces.push_back(mycut);
     259             :     }
     260       14436 :   }
     261        2406 :   return elem_cut;
     262             : }
     263             : 
     264             : bool
     265           0 : CrackMeshCut3DUserObject::cutFragmentByGeometry(std::vector<std::vector<Point>> & /*frag_edges*/,
     266             :                                                 std::vector<Xfem::CutEdge> & /*cut_edges*/) const
     267             : {
     268           0 :   mooseError("invalid method for 3D mesh cutting");
     269             :   return false;
     270             : }
     271             : 
     272             : bool
     273           0 : CrackMeshCut3DUserObject::cutFragmentByGeometry(std::vector<std::vector<Point>> & /*frag_faces*/,
     274             :                                                 std::vector<Xfem::CutFace> & /*cut_faces*/) const
     275             : {
     276             :   // TODO: Need this for branching in 3D
     277           0 :   mooseError("cutFragmentByGeometry not yet implemented for 3D mesh cutting");
     278             :   return false;
     279             : }
     280             : 
     281             : bool
     282      724896 : CrackMeshCut3DUserObject::intersectWithEdge(const Point & p1,
     283             :                                             const Point & p2,
     284             :                                             const std::vector<Point> & vertices,
     285             :                                             Point & pint) const
     286             : {
     287             :   bool has_intersection = false;
     288             : 
     289      724896 :   Plane elem_plane(vertices[0], vertices[1], vertices[2]);
     290      724896 :   Point point = vertices[0];
     291      724896 :   Point normal = elem_plane.unit_normal(point);
     292             : 
     293      724896 :   std::array<Real, 3> plane_point = {{point(0), point(1), point(2)}};
     294      724896 :   std::array<Real, 3> planenormal = {{normal(0), normal(1), normal(2)}};
     295      724896 :   std::array<Real, 3> edge_point1 = {{p1(0), p1(1), p1(2)}};
     296      724896 :   std::array<Real, 3> edge_point2 = {{p2(0), p2(1), p2(2)}};
     297      724896 :   std::array<Real, 3> cut_point = {{0.0, 0.0, 0.0}};
     298             : 
     299      724896 :   if (Xfem::plane_normal_line_exp_int_3d(
     300             :           &plane_point[0], &planenormal[0], &edge_point1[0], &edge_point2[0], &cut_point[0]) == 1)
     301             :   {
     302      328752 :     Point temp_p(cut_point[0], cut_point[1], cut_point[2]);
     303      328752 :     if (isInsideCutPlane(vertices, temp_p) && isInsideEdge(p1, p2, temp_p))
     304             :     {
     305        3336 :       pint = temp_p;
     306             :       has_intersection = true;
     307             :     }
     308             :   }
     309      724896 :   return has_intersection;
     310      724896 : }
     311             : 
     312             : bool
     313        6176 : CrackMeshCut3DUserObject::findIntersection(const Point & p1,
     314             :                                            const Point & p2,
     315             :                                            const std::vector<Point> & vertices,
     316             :                                            Point & pint) const
     317             : {
     318             :   bool has_intersection = false;
     319             : 
     320        6176 :   Plane elem_plane(vertices[0], vertices[1], vertices[2]);
     321        6176 :   Point point = vertices[0];
     322        6176 :   Point normal = elem_plane.unit_normal(point);
     323             : 
     324        6176 :   std::array<Real, 3> plane_point = {{point(0), point(1), point(2)}};
     325        6176 :   std::array<Real, 3> planenormal = {{normal(0), normal(1), normal(2)}};
     326        6176 :   std::array<Real, 3> p_begin = {{p1(0), p1(1), p1(2)}};
     327        6176 :   std::array<Real, 3> p_end = {{p2(0), p2(1), p2(2)}};
     328        6176 :   std::array<Real, 3> cut_point = {{0.0, 0.0, 0.0}};
     329             : 
     330        6176 :   if (Xfem::plane_normal_line_exp_int_3d(
     331             :           &plane_point[0], &planenormal[0], &p_begin[0], &p_end[0], &cut_point[0]) == 1)
     332             :   {
     333        5024 :     Point p(cut_point[0], cut_point[1], cut_point[2]);
     334        5024 :     Real dotp = ((p - p1) * (p2 - p1)) / ((p2 - p1) * (p2 - p1));
     335        5024 :     if (isInsideCutPlane(vertices, p) && dotp > 1)
     336             :     {
     337          64 :       pint = p;
     338             :       has_intersection = true;
     339             :     }
     340             :   }
     341        6176 :   return has_intersection;
     342        6176 : }
     343             : 
     344             : bool
     345       11688 : CrackMeshCut3DUserObject::isInsideEdge(const Point & p1, const Point & p2, const Point & p) const
     346             : {
     347             :   Real dotp1 = (p1 - p) * (p2 - p1);
     348             :   Real dotp2 = (p2 - p) * (p2 - p1);
     349       11688 :   return (dotp1 * dotp2 <= 0.0);
     350             : }
     351             : 
     352             : Real
     353        3336 : CrackMeshCut3DUserObject::getRelativePosition(const Point & p1,
     354             :                                               const Point & p2,
     355             :                                               const Point & p) const
     356             : {
     357        3336 :   Real full_len = (p2 - p1).norm();
     358        3336 :   Real len_p1_p = (p - p1).norm();
     359        3336 :   return len_p1_p / full_len;
     360             : }
     361             : 
     362             : bool
     363      333776 : CrackMeshCut3DUserObject::isInsideCutPlane(const std::vector<Point> & vertices,
     364             :                                            const Point & p) const
     365             : {
     366      333776 :   unsigned int n_node = vertices.size();
     367             : 
     368      333776 :   Plane elem_plane(vertices[0], vertices[1], vertices[2]);
     369      333776 :   Point normal = elem_plane.unit_normal(vertices[0]);
     370             : 
     371             :   bool inside = false;
     372             :   unsigned int counter = 0;
     373             : 
     374     1340128 :   for (unsigned int i = 0; i < n_node; ++i)
     375             :   {
     376     1006352 :     unsigned int iplus1 = (i < n_node - 1 ? i + 1 : 0);
     377     1006352 :     Point middle2p = p - 0.5 * (vertices[i] + vertices[iplus1]);
     378             :     const Point side_tang = vertices[iplus1] - vertices[i];
     379     1006352 :     Point side_norm = side_tang.cross(normal);
     380     1006352 :     Xfem::normalizePoint(middle2p);
     381     1006352 :     Xfem::normalizePoint(side_norm);
     382     1006352 :     if (middle2p * side_norm <= 0.0)
     383      589980 :       counter += 1;
     384             :   }
     385      333776 :   if (counter == n_node)
     386             :     inside = true;
     387      333776 :   return inside;
     388      333776 : }
     389             : 
     390             : void
     391          12 : CrackMeshCut3DUserObject::findBoundaryNodes()
     392             : {
     393          12 :   auto boundary_node_ids = MeshTools::find_boundary_nodes(*_cut_mesh);
     394         108 :   for (auto it = boundary_node_ids.cbegin(); it != boundary_node_ids.cend(); it++)
     395             :   {
     396          96 :     dof_id_type id = *it;
     397             :     std::vector<dof_id_type> neighbors;
     398          96 :     _boundary_map[id] = neighbors;
     399          96 :   }
     400          12 : }
     401             : 
     402             : void
     403          12 : CrackMeshCut3DUserObject::findBoundaryEdges()
     404             : {
     405             :   _boundary_edges.clear();
     406             : 
     407             :   std::vector<dof_id_type> corner_elem_id;
     408             :   unsigned int counter = 0;
     409             : 
     410          12 :   std::vector<dof_id_type> node_id(_cut_elem_nnode);
     411          12 :   std::vector<bool> is_node_on_boundary(_cut_elem_nnode);
     412             : 
     413          96 :   for (const auto & cut_elem : _cut_mesh->element_ptr_range())
     414             :   {
     415         288 :     for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
     416             :     {
     417         216 :       node_id[i] = cut_elem->node_ptr(i)->id();
     418         216 :       is_node_on_boundary[i] = (_boundary_map.find(node_id[i]) != _boundary_map.end());
     419             :     }
     420             : 
     421          72 :     if (is_node_on_boundary[0] && is_node_on_boundary[1] && is_node_on_boundary[2])
     422             :     {
     423             :       // this is an element at the corner; all nodes are on the boundary but not all edges are on
     424             :       // the boundary
     425          72 :       corner_elem_id.push_back(counter);
     426             :     }
     427             :     else
     428             :     {
     429             :       // for other elements, find and store boundary edges
     430           0 :       for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
     431             :       {
     432             :         // if both nodes on an edge are on the boundary, it is a boundary edge.
     433           0 :         if (is_node_on_boundary[i] && is_node_on_boundary[(i + 1 <= 2) ? i + 1 : 0])
     434             :         {
     435           0 :           dof_id_type node1 = node_id[i];
     436           0 :           dof_id_type node2 = node_id[(i + 1 <= 2) ? i + 1 : 0];
     437           0 :           if (node1 > node2)
     438             :             std::swap(node1, node2);
     439             : 
     440             :           Xfem::CutEdge ce;
     441             : 
     442           0 :           if (node1 > node2)
     443             :             std::swap(node1, node2);
     444           0 :           ce._id1 = node1;
     445           0 :           ce._id2 = node2;
     446             : 
     447           0 :           _boundary_edges.insert(ce);
     448             :         }
     449             :       }
     450             :     }
     451          72 :     ++counter;
     452          12 :   }
     453             : 
     454             :   // loop over edges in corner elements
     455             :   // if an edge is shared by two elements, it is not an boundary edge (is_edge_inside = 1)
     456          84 :   for (unsigned int i = 0; i < corner_elem_id.size(); ++i)
     457             :   {
     458          72 :     auto elem_it = _cut_mesh->elements_begin();
     459             : 
     460         252 :     for (dof_id_type j = 0; j < corner_elem_id[i]; ++j)
     461         180 :       ++elem_it;
     462          72 :     Elem * cut_elem = *elem_it;
     463             : 
     464         288 :     for (unsigned int j = 0; j < _cut_elem_nnode; ++j)
     465             :     {
     466             :       bool is_edge_inside = 0;
     467             : 
     468             :       dof_id_type node1 = cut_elem->node_ptr(j)->id();
     469         216 :       dof_id_type node2 = cut_elem->node_ptr((j + 1 <= 2) ? j + 1 : 0)->id();
     470         216 :       if (node1 > node2)
     471             :         std::swap(node1, node2);
     472             : 
     473             :       unsigned int counter = 0;
     474        1332 :       for (const auto & cut_elem2 : _cut_mesh->element_ptr_range())
     475             :       {
     476        1020 :         if (counter != corner_elem_id[i])
     477             :         {
     478        3228 :           for (unsigned int k = 0; k < _cut_elem_nnode; ++k)
     479             :           {
     480        2484 :             dof_id_type node3 = cut_elem2->node_ptr(k)->id();
     481        2484 :             dof_id_type node4 = cut_elem2->node_ptr((k + 1 <= 2) ? k + 1 : 0)->id();
     482        2484 :             if (node3 > node4)
     483             :               std::swap(node3, node4);
     484             : 
     485        2484 :             if (node1 == node3 && node2 == node4)
     486             :             {
     487             :               is_edge_inside = 1;
     488         120 :               goto endloop;
     489             :             }
     490             :           }
     491             :         }
     492         900 :         ++counter;
     493         216 :       }
     494             :     endloop:
     495             :       if (is_edge_inside == 0)
     496             :       {
     497             :         // store boundary edges
     498             :         Xfem::CutEdge ce;
     499             : 
     500          96 :         if (node1 > node2)
     501             :           std::swap(node1, node2);
     502          96 :         ce._id1 = node1;
     503          96 :         ce._id2 = node2;
     504             : 
     505          96 :         _boundary_edges.insert(ce);
     506             :       }
     507             :       else
     508             :       {
     509             :         // this is not a boundary edge; remove it from existing edge list
     510         684 :         for (auto it = _boundary_edges.begin(); it != _boundary_edges.end();)
     511             :         {
     512         564 :           if ((*it)._id1 == node1 && (*it)._id2 == node2)
     513           0 :             it = _boundary_edges.erase(it);
     514             :           else
     515             :             ++it;
     516             :         }
     517             :       }
     518             :     }
     519             :   }
     520          12 : }
     521             : 
     522             : void
     523          12 : CrackMeshCut3DUserObject::sortBoundaryNodes()
     524             : {
     525          12 :   _boundary.clear();
     526             : 
     527         108 :   for (auto it = _boundary_edges.begin(); it != _boundary_edges.end(); ++it)
     528             :   {
     529          96 :     dof_id_type node1 = (*it)._id1;
     530          96 :     dof_id_type node2 = (*it)._id2;
     531             : 
     532             :     mooseAssert(_boundary_map.find(node1) != _boundary_map.end(),
     533             :                 "_boundary_map does not have this key");
     534             :     mooseAssert(_boundary_map.find(node2) != _boundary_map.end(),
     535             :                 "_boundary_map does not have this key");
     536             : 
     537          96 :     _boundary_map.find(node1)->second.push_back(node2);
     538          96 :     _boundary_map.find(node2)->second.push_back(node1);
     539             :   }
     540             : 
     541             :   auto it = _boundary_map.begin();
     542         108 :   while (it != _boundary_map.end())
     543             :   {
     544          96 :     if (it->second.size() != 2)
     545           0 :       mooseError(
     546             :           "Boundary nodes in the cutter mesh must have exactly two neighbors; this one has: ",
     547           0 :           it->second.size());
     548             :     ++it;
     549             :   }
     550             : 
     551             :   auto it2 = _boundary_edges.begin();
     552          12 :   dof_id_type node1 = (*it2)._id1;
     553          12 :   dof_id_type node2 = (*it2)._id2;
     554          12 :   _boundary.push_back(node1);
     555          12 :   _boundary.push_back(node2);
     556             : 
     557          96 :   for (unsigned int i = 0; i < _boundary_edges.size() - 1; ++i)
     558             :   {
     559             :     mooseAssert(_boundary_map.find(node2) != _boundary_map.end(),
     560             :                 "_boundary_map does not have this key");
     561             : 
     562          84 :     dof_id_type node3 = _boundary_map.find(node2)->second[0];
     563          84 :     dof_id_type node4 = _boundary_map.find(node2)->second[1];
     564             : 
     565          84 :     if (node3 == node1)
     566             :     {
     567          48 :       _boundary.push_back(node4);
     568          48 :       node1 = node2;
     569          48 :       node2 = node4;
     570             :     }
     571          36 :     else if (node4 == node1)
     572             :     {
     573          36 :       _boundary.push_back(node3);
     574          36 :       node1 = node2;
     575          36 :       node2 = node3;
     576             :     }
     577             :     else
     578           0 :       mooseError("Discontinuity in cutter boundary");
     579             :   }
     580          12 : }
     581             : 
     582             : Real
     583         416 : CrackMeshCut3DUserObject::findDistance(dof_id_type node1, dof_id_type node2)
     584             : {
     585         416 :   Node * n1 = _cut_mesh->node_ptr(node1);
     586             :   mooseAssert(n1 != nullptr, "Node is NULL");
     587         416 :   Node * n2 = _cut_mesh->node_ptr(node2);
     588             :   mooseAssert(n2 != nullptr, "Node is NULL");
     589         416 :   Real distance = (*n1 - *n2).norm();
     590         416 :   return distance;
     591             : }
     592             : 
     593             : void
     594           0 : CrackMeshCut3DUserObject::refineBoundary()
     595             : {
     596           0 :   std::vector<dof_id_type> new_boundary_order(_boundary.begin(), _boundary.end());
     597             : 
     598             :   mooseAssert(_boundary.size() >= 2, "Boundary should have at least two nodes");
     599             : 
     600           0 :   for (unsigned int i = _boundary.size() - 1; i >= 1; --i)
     601             :   {
     602           0 :     dof_id_type node1 = _boundary[i - 1];
     603           0 :     dof_id_type node2 = _boundary[i];
     604             : 
     605           0 :     Real distance = findDistance(node1, node2);
     606             : 
     607           0 :     if (distance > _size_control)
     608             :     {
     609           0 :       unsigned int n = static_cast<unsigned int>(distance / _size_control);
     610             :       std::array<Real, 3> x1;
     611             :       std::array<Real, 3> x2;
     612             : 
     613           0 :       Node * n1 = _cut_mesh->node_ptr(node1);
     614             :       mooseAssert(n1 != nullptr, "Node is NULL");
     615             :       Point & p1 = *n1;
     616           0 :       Node * n2 = _cut_mesh->node_ptr(node2);
     617             :       mooseAssert(n2 != nullptr, "Node is NULL");
     618             :       Point & p2 = *n2;
     619             : 
     620           0 :       for (unsigned int j = 0; j < 3; ++j)
     621             :       {
     622           0 :         x1[j] = p1(j);
     623           0 :         x2[j] = p2(j);
     624             :       }
     625             : 
     626           0 :       for (unsigned int j = 0; j < n; ++j)
     627             :       {
     628             :         Point x;
     629           0 :         for (unsigned int k = 0; k < 3; ++k)
     630           0 :           x(k) = x2[k] - (x2[k] - x1[k]) * (j + 1) / (n + 1);
     631             : 
     632           0 :         Node * this_node = Node::build(x, _cut_mesh->n_nodes()).release();
     633           0 :         _cut_mesh->add_node(this_node);
     634             : 
     635           0 :         dof_id_type id = _cut_mesh->n_nodes() - 1;
     636             :         auto it = new_boundary_order.begin();
     637           0 :         new_boundary_order.insert(it + i, id);
     638             :       }
     639             :     }
     640             :   }
     641             : 
     642           0 :   _boundary = new_boundary_order;
     643             :   mooseAssert(_boundary.size() > 0, "Boundary should not have zero size");
     644             :   _boundary.pop_back();
     645           0 : }
     646             : 
     647             : void
     648          49 : CrackMeshCut3DUserObject::findActiveBoundaryNodes()
     649             : {
     650          49 :   _active_boundary.clear();
     651          49 :   _inactive_boundary_pos.clear();
     652             : 
     653          49 :   std::unique_ptr<PointLocatorBase> pl = _mesh.getPointLocator();
     654          49 :   pl->enable_out_of_mesh_mode();
     655             : 
     656          49 :   unsigned int n_boundary = _boundary.size();
     657             : 
     658             :   // if the node is outside of the structural model, store its position in _boundary to
     659             :   // _inactive_boundary_pos
     660         596 :   for (unsigned int j = 0; j < n_boundary; ++j)
     661             :   {
     662         547 :     Node * this_node = _cut_mesh->node_ptr(_boundary[j]);
     663             :     mooseAssert(this_node, "Node is NULL");
     664         547 :     Point & this_point = *this_node;
     665             : 
     666         547 :     const Elem * elem = (*pl)(this_point);
     667         547 :     if (elem == nullptr)
     668         449 :       _inactive_boundary_pos.push_back(j);
     669             :   }
     670             : 
     671          49 :   unsigned int n_inactive_boundary = _inactive_boundary_pos.size();
     672             : 
     673             :   // all nodes are inactive, stop
     674          49 :   if (n_inactive_boundary == n_boundary)
     675           0 :     _stop = 1;
     676             : 
     677             :   // find and store active boundary segments in "_active_boundary"
     678          49 :   if (n_inactive_boundary == 0)
     679           0 :     _active_boundary.push_back(_boundary);
     680             :   else
     681             :   {
     682         449 :     for (unsigned int i = 0; i < n_inactive_boundary - 1; ++i)
     683             :     {
     684         400 :       if (_inactive_boundary_pos[i + 1] - _inactive_boundary_pos[i] != 1)
     685             :       {
     686             :         std::vector<dof_id_type> temp;
     687         245 :         for (unsigned int j = _inactive_boundary_pos[i]; j <= _inactive_boundary_pos[i + 1]; ++j)
     688             :         {
     689         196 :           temp.push_back(_boundary[j]);
     690             :         }
     691          49 :         _active_boundary.push_back(temp);
     692          49 :       }
     693             :     }
     694          49 :     if (_inactive_boundary_pos[n_inactive_boundary - 1] - _inactive_boundary_pos[0] <
     695          49 :         n_boundary - 1)
     696             :     {
     697             :       std::vector<dof_id_type> temp;
     698           0 :       for (unsigned int j = _inactive_boundary_pos[n_inactive_boundary - 1]; j < n_boundary; ++j)
     699           0 :         temp.push_back(_boundary[j]);
     700           0 :       for (unsigned int j = 0; j <= _inactive_boundary_pos[0]; ++j)
     701           0 :         temp.push_back(_boundary[j]);
     702           0 :       _active_boundary.push_back(temp);
     703           0 :     }
     704             :   }
     705          49 : }
     706             : 
     707             : void
     708          32 : CrackMeshCut3DUserObject::findActiveBoundaryDirection()
     709             : {
     710             :   mooseAssert(!(_cfd && _active_boundary.size() != 1),
     711             :               "crack-front-definition using the cutter mesh only supports one active crack front "
     712             :               "segment for now");
     713             : 
     714          32 :   _active_direction.clear();
     715             : 
     716          64 :   for (unsigned int i = 0; i < _active_boundary.size(); ++i)
     717             :   {
     718             :     std::vector<Point> temp;
     719             :     Point dir;
     720             : 
     721          32 :     if (_inactive_boundary_pos.size() != 0)
     722             :     {
     723         128 :       for (unsigned int j = 0; j < 3; ++j)
     724          96 :         dir(j) = 0;
     725          32 :       temp.push_back(dir);
     726             :     }
     727             : 
     728             :     unsigned int i1 = 1;
     729          32 :     unsigned int i2 = _active_boundary[i].size() - 1;
     730          32 :     if (_inactive_boundary_pos.size() == 0)
     731             :     {
     732             :       i1 = 0;
     733             :       i2 = _active_boundary[i].size();
     734             :     }
     735             : 
     736          32 :     if (_growth_dir_method == GrowthDirectionEnum::FUNCTION)
     737             :       // loop over active front points
     738          60 :       for (unsigned int j = i1; j < i2; ++j)
     739             :       {
     740          40 :         Node * this_node = _cut_mesh->node_ptr(_active_boundary[i][j]);
     741             :         mooseAssert(this_node, "Node is NULL");
     742          40 :         Point & this_point = *this_node;
     743          40 :         dir(0) = _func_x->value(0, this_point);
     744          40 :         dir(1) = _func_y->value(0, this_point);
     745          40 :         dir(2) = _func_z->value(0, this_point);
     746             : 
     747          40 :         temp.push_back(dir);
     748             :       }
     749             :     // determine growth direction based on KI and KII at the crack front
     750          12 :     else if (_growth_dir_method == GrowthDirectionEnum::MAX_HOOP_STRESS)
     751             :     {
     752          36 :       const VectorPostprocessorValue & k1 = getVectorPostprocessorValueByName("II_KI_1", "II_KI_1");
     753             :       const VectorPostprocessorValue & k2 =
     754          36 :           getVectorPostprocessorValueByName("II_KII_1", "II_KII_1");
     755             :       mooseAssert(k1.size() == k2.size(), "KI and KII VPPs should have the same size");
     756             :       mooseAssert(k1.size() == _active_boundary[0].size(),
     757             :                   "the number of crack front nodes in the self-similar method should equal to the "
     758             :                   "size of VPP defined at the crack front");
     759             :       mooseAssert(_crack_front_points.size() == _active_boundary[0].size(),
     760             :                   "the number of crack front nodes should be the same in _crack_front_points and "
     761             :                   "_active_boundary[0]");
     762             : 
     763             :       // the node order in _active_boundary[0] and _crack_front_points may be the same or opposite,
     764             :       // their correspondence is needed
     765          12 :       std::vector<int> index = getFrontPointsIndex();
     766             : 
     767          36 :       for (unsigned int j = i1; j < i2; ++j)
     768             :       {
     769          24 :         int ind = index[j];
     770          24 :         Real theta = 2 * std::atan((k1[ind] - std::sqrt(k1[ind] * k1[ind] + k2[ind] * k2[ind])) /
     771          24 :                                    (4 * k2[ind]));
     772             :         RealVectorValue dir_cfc; // growth direction in crack front coord (cfc) system based on the
     773             :                                  // max hoop stress criterion
     774             :         RealVectorValue
     775             :             dir; // growth direction in global coord system based on the max hoop stress criterion
     776          24 :         dir_cfc(0) = std::cos(theta);
     777          24 :         dir_cfc(1) = std::sin(theta);
     778             :         dir_cfc(2) = 0;
     779          24 :         dir = _crack_front_definition->rotateFromCrackFrontCoordsToGlobal(dir_cfc, ind);
     780             : 
     781          24 :         temp.push_back(dir);
     782             :       }
     783          12 :     }
     784             :     else
     785           0 :       mooseError("This growth_dir_method is not pre-defined!");
     786             : 
     787          32 :     if (_inactive_boundary_pos.size() != 0)
     788             :     {
     789         128 :       for (unsigned int j = 0; j < 3; ++j)
     790          96 :         dir(j) = 0;
     791          32 :       temp.push_back(dir);
     792             :     }
     793             : 
     794          32 :     _active_direction.push_back(temp);
     795          32 :   }
     796             : 
     797             :   // normalize the directional vector
     798             :   Real maxl = 0;
     799             : 
     800          64 :   for (unsigned int i = 0; i < _active_direction.size(); ++i)
     801         160 :     for (unsigned int j = 0; j < _active_direction[i].size(); ++j)
     802             :     {
     803         128 :       Point pt = _active_direction[i][j];
     804         128 :       Real length = std::sqrt(pt * pt);
     805         128 :       if (length > maxl)
     806             :         maxl = length;
     807             :     }
     808             : 
     809          64 :   for (unsigned int i = 0; i < _active_direction.size(); ++i)
     810         160 :     for (unsigned int j = 0; j < _active_direction[i].size(); ++j)
     811             :       _active_direction[i][j] /= maxl;
     812          32 : }
     813             : 
     814             : void
     815          32 : CrackMeshCut3DUserObject::growFront()
     816             : {
     817          32 :   _front.clear();
     818             : 
     819          64 :   for (unsigned int i = 0; i < _active_boundary.size(); ++i)
     820             :   {
     821             :     std::vector<dof_id_type> temp;
     822             : 
     823             :     unsigned int i1 = 1;
     824          32 :     unsigned int i2 = _active_boundary[i].size() - 1;
     825          32 :     if (_inactive_boundary_pos.size() == 0)
     826             :     {
     827             :       i1 = 0;
     828             :       i2 = _active_boundary[i].size();
     829             :     }
     830             : 
     831          96 :     for (unsigned int j = i1; j < i2; ++j)
     832             :     {
     833          64 :       Node * this_node = _cut_mesh->node_ptr(_active_boundary[i][j]);
     834             :       mooseAssert(this_node, "Node is NULL");
     835             :       Point & this_point = *this_node;
     836          64 :       Point dir = _active_direction[i][j];
     837             : 
     838             :       Point x;
     839             : 
     840          64 :       if (_growth_rate_method == GrowthRateEnum::FUNCTION)
     841         160 :         for (unsigned int k = 0; k < 3; ++k)
     842             :         {
     843         120 :           Real velo = _func_v->value(0, Point(0, 0, 0));
     844         120 :           x(k) = this_point(k) + dir(k) * velo;
     845             :         }
     846          24 :       else if (_growth_rate_method == GrowthRateEnum::FATIGUE)
     847             :       {
     848             :         // get the number of loading cycles for this growth increament
     849          24 :         if (j == i1)
     850             :         {
     851          12 :           unsigned long int dn = (unsigned long int)_func_v->value(0, Point(0, 0, 0));
     852          12 :           _dn.push_back(dn);
     853          12 :           _n.push_back(_n.size() == 0 ? dn : dn + _n[_n.size() - 1]);
     854             :         }
     855             : 
     856          24 :         Real growth_size = _growth_size[j];
     857          96 :         for (unsigned int k = 0; k < 3; ++k)
     858          72 :           x(k) = this_point(k) + dir(k) * growth_size;
     859             :       }
     860             :       else
     861           0 :         mooseError("This growth_rate_method is not pre-defined!");
     862             : 
     863         128 :       this_node = Node::build(x, _cut_mesh->n_nodes()).release();
     864          64 :       _cut_mesh->add_node(this_node);
     865             : 
     866          64 :       dof_id_type id = _cut_mesh->n_nodes() - 1;
     867          64 :       temp.push_back(id);
     868             : 
     869          64 :       if (_cfd)
     870             :       {
     871          48 :         auto it = std::find(_tracked_crack_front_points.begin(),
     872             :                             _tracked_crack_front_points.end(),
     873             :                             _active_boundary[0][j]);
     874          48 :         if (it != _tracked_crack_front_points.end())
     875             :         {
     876             :           unsigned int pos = std::distance(_tracked_crack_front_points.begin(), it);
     877          48 :           _tracked_crack_front_points[pos] = id;
     878             :         }
     879             :       }
     880             :     }
     881             : 
     882          32 :     _front.push_back(temp);
     883          32 :   }
     884          32 : }
     885             : 
     886             : void
     887          32 : CrackMeshCut3DUserObject::sortFrontNodes()
     888             : // TBD; it is not needed for current problems but will be useful for fracture growth
     889             : {
     890          32 : }
     891             : 
     892             : void
     893          32 : CrackMeshCut3DUserObject::findFrontIntersection()
     894             : {
     895          32 :   ConstBndElemRange & range = *_mesh.getBoundaryElementRange();
     896             : 
     897          64 :   for (unsigned int i = 0; i < _front.size(); ++i)
     898             :   {
     899          32 :     if (_front[i].size() >= 2)
     900             :     {
     901             :       std::vector<Point> pint1;
     902             :       std::vector<Point> pint2;
     903             :       std::vector<Real> length1;
     904             :       std::vector<Real> length2;
     905             : 
     906          32 :       Real node_id = _front[i][0];
     907          32 :       Node * this_node = _cut_mesh->node_ptr(node_id);
     908             :       mooseAssert(this_node, "Node is NULL");
     909          32 :       Point & p2 = *this_node;
     910             : 
     911          32 :       if (_front[i].size() >= 4)
     912           0 :         node_id = _front[i][2];
     913             :       else
     914          32 :         node_id = _front[i][1];
     915             : 
     916          32 :       this_node = _cut_mesh->node_ptr(node_id);
     917             :       mooseAssert(this_node, "Node is NULL");
     918          32 :       Point & p1 = *this_node;
     919             : 
     920          32 :       node_id = _front[i].back();
     921          32 :       this_node = _cut_mesh->node_ptr(node_id);
     922             :       mooseAssert(this_node, "Node is NULL");
     923          32 :       Point & p4 = *this_node;
     924             : 
     925          32 :       if (_front[i].size() >= 4)
     926           0 :         node_id = _front[i][_front[i].size() - 3];
     927             :       else
     928          32 :         node_id = _front[i][_front[i].size() - 2];
     929             : 
     930          32 :       this_node = _cut_mesh->node_ptr(node_id);
     931             :       mooseAssert(this_node, "Node is NULL");
     932          32 :       Point & p3 = *this_node;
     933             : 
     934             :       bool do_inter1 = 1;
     935             :       bool do_inter2 = 1;
     936             : 
     937          32 :       std::unique_ptr<PointLocatorBase> pl = _mesh.getPointLocator();
     938          32 :       pl->enable_out_of_mesh_mode();
     939          32 :       const Elem * elem = (*pl)(p1);
     940          32 :       if (elem == nullptr)
     941             :         do_inter1 = 0;
     942          32 :       elem = (*pl)(p4);
     943          32 :       if (elem == nullptr)
     944             :         do_inter2 = 0;
     945             : 
     946        3120 :       for (const auto & belem : range)
     947             :       {
     948             :         Point pt;
     949             :         std::vector<Point> vertices;
     950             : 
     951        3088 :         elem = belem->_elem;
     952        3088 :         std::unique_ptr<const Elem> curr_side = elem->side_ptr(belem->_side);
     953       15440 :         for (unsigned int j = 0; j < curr_side->n_nodes(); ++j)
     954             :         {
     955             :           const Node * node = curr_side->node_ptr(j);
     956       12352 :           const Point & this_point = *node;
     957       12352 :           vertices.push_back(this_point);
     958             :         }
     959             : 
     960        3088 :         if (findIntersection(p1, p2, vertices, pt))
     961             :         {
     962          32 :           pint1.push_back(pt);
     963          32 :           length1.push_back((pt - p1) * (pt - p1));
     964             :         }
     965        3088 :         if (findIntersection(p3, p4, vertices, pt))
     966             :         {
     967          32 :           pint2.push_back(pt);
     968          32 :           length2.push_back((pt - p3) * (pt - p3));
     969             :         }
     970        3088 :       }
     971             : 
     972          32 :       if (length1.size() != 0 && do_inter1)
     973             :       {
     974             :         auto it1 = std::min_element(length1.begin(), length1.end());
     975          32 :         Point inter1 = pint1[std::distance(length1.begin(), it1)];
     976             :         inter1 += (inter1 - p1) * _const_intersection;
     977             : 
     978          64 :         Node * this_node = Node::build(inter1, _cut_mesh->n_nodes()).release();
     979          32 :         _cut_mesh->add_node(this_node);
     980             : 
     981             :         mooseAssert(_cut_mesh->n_nodes() - 1 > 0, "The cut mesh should have at least one element.");
     982          32 :         unsigned int n = _cut_mesh->n_nodes() - 1;
     983             : 
     984             :         auto it = _front[i].begin();
     985          32 :         _front[i].insert(it, n);
     986             : 
     987          32 :         if (_cfd)
     988          24 :           _tracked_crack_front_points[_tracked_crack_front_points.size() - 1] = n;
     989             :       }
     990             : 
     991          32 :       if (length2.size() != 0 && do_inter2)
     992             :       {
     993             :         auto it2 = std::min_element(length2.begin(), length2.end());
     994          32 :         Point inter2 = pint2[std::distance(length2.begin(), it2)];
     995             :         inter2 += (inter2 - p2) * _const_intersection;
     996             : 
     997          64 :         Node * this_node = Node::build(inter2, _cut_mesh->n_nodes()).release();
     998          32 :         _cut_mesh->add_node(this_node);
     999             : 
    1000          32 :         dof_id_type n = _cut_mesh->n_nodes() - 1;
    1001             : 
    1002             :         auto it = _front[i].begin();
    1003             :         unsigned int m = _front[i].size();
    1004          32 :         _front[i].insert(it + m, n);
    1005             : 
    1006          32 :         if (_cfd)
    1007          24 :           _tracked_crack_front_points[0] = n;
    1008             :       }
    1009          32 :     }
    1010             :   }
    1011          32 : }
    1012             : 
    1013             : void
    1014          32 : CrackMeshCut3DUserObject::refineFront()
    1015             : {
    1016          32 :   std::vector<std::vector<dof_id_type>> new_front(_front.begin(), _front.end());
    1017             : 
    1018          64 :   for (unsigned int ifront = 0; ifront < _front.size(); ++ifront)
    1019             :   {
    1020          32 :     unsigned int i1 = _front[ifront].size() - 1;
    1021          32 :     if (_inactive_boundary_pos.size() == 0)
    1022             :       i1 = _front[ifront].size();
    1023             : 
    1024         128 :     for (unsigned int i = i1; i >= 1; --i)
    1025             :     {
    1026             :       unsigned int i2 = i;
    1027          96 :       if (_inactive_boundary_pos.size() == 0)
    1028           0 :         i2 = (i <= _front[ifront].size() - 1 ? i : 0);
    1029             : 
    1030          96 :       dof_id_type node1 = _front[ifront][i - 1];
    1031          96 :       dof_id_type node2 = _front[ifront][i2];
    1032          96 :       Real distance = findDistance(node1, node2);
    1033             : 
    1034          96 :       if (distance > _size_control)
    1035             :       {
    1036           0 :         unsigned int n = static_cast<int>(distance / _size_control);
    1037             :         std::array<Real, 3> x1;
    1038             :         std::array<Real, 3> x2;
    1039             : 
    1040           0 :         Node * this_node = _cut_mesh->node_ptr(node1);
    1041             :         mooseAssert(this_node, "Node is NULL");
    1042             :         Point & p1 = *this_node;
    1043           0 :         this_node = _cut_mesh->node_ptr(node2);
    1044             :         mooseAssert(this_node, "Node is NULL");
    1045             :         Point & p2 = *this_node;
    1046             : 
    1047           0 :         for (unsigned int j = 0; j < 3; ++j)
    1048             :         {
    1049           0 :           x1[j] = p1(j);
    1050           0 :           x2[j] = p2(j);
    1051             :         }
    1052             : 
    1053           0 :         for (unsigned int j = 0; j < n; ++j)
    1054             :         {
    1055             :           Point x;
    1056           0 :           for (unsigned int k = 0; k < 3; ++k)
    1057           0 :             x(k) = x2[k] - (x2[k] - x1[k]) * (j + 1) / (n + 1);
    1058             : 
    1059           0 :           Node * this_node = Node::build(x, _cut_mesh->n_nodes()).release();
    1060           0 :           _cut_mesh->add_node(this_node);
    1061             : 
    1062           0 :           dof_id_type id = _cut_mesh->n_nodes() - 1;
    1063             : 
    1064             :           auto it = new_front[ifront].begin();
    1065           0 :           new_front[ifront].insert(it + i, id);
    1066             :         }
    1067             :       }
    1068             :     }
    1069             :   }
    1070             : 
    1071          32 :   _front = new_front;
    1072             : 
    1073          32 :   if (_cfd)
    1074             :   {
    1075          24 :     if (_front[0][0] == _tracked_crack_front_points[0] &&
    1076           0 :         _front[0].back() == _tracked_crack_front_points.back())
    1077           0 :       _crack_front_points = _front[0];
    1078          24 :     else if (_front[0][0] == _tracked_crack_front_points.back() &&
    1079          24 :              _front[0].back() == _tracked_crack_front_points[0])
    1080             :     {
    1081          24 :       _crack_front_points = _front[0];
    1082          24 :       std::reverse(_crack_front_points.begin(), _crack_front_points.end());
    1083             :     }
    1084             :     else
    1085           0 :       mooseError("the crack front and the tracked crack front definition must match in terms of "
    1086             :                  "their end nodes");
    1087             : 
    1088          24 :     _num_crack_front_points = _crack_front_points.size();
    1089          24 :     _crack_front_definition->updateNumberOfCrackFrontPoints(_num_crack_front_points);
    1090             :   }
    1091          32 : }
    1092             : 
    1093             : void
    1094          32 : CrackMeshCut3DUserObject::triangulation()
    1095             : {
    1096             : 
    1097             :   mooseAssert(_active_boundary.size() == _front.size(),
    1098             :               "_active_boundary and _front should have the same size!");
    1099             : 
    1100          32 :   if (_inactive_boundary_pos.size() == 0)
    1101             :   {
    1102           0 :     _active_boundary[0].push_back(_active_boundary[0][0]);
    1103           0 :     _front[0].push_back(_front[0][0]);
    1104             :   }
    1105             : 
    1106             :   // loop over active segments
    1107          64 :   for (unsigned int k = 0; k < _front.size(); ++k)
    1108             :   {
    1109          32 :     unsigned int n1 = _active_boundary[k].size();
    1110          32 :     unsigned int n2 = _front[k].size();
    1111             : 
    1112             :     unsigned int i1 = 0;
    1113             :     unsigned int i2 = 0;
    1114             : 
    1115             :     // stop when all nodes are associated with an element
    1116         224 :     while (!(i1 == n1 - 1 && i2 == n2 - 1))
    1117             :     {
    1118             :       std::vector<dof_id_type> elem;
    1119             : 
    1120         192 :       dof_id_type p1 = _active_boundary[k][i1]; // node in the old front
    1121         192 :       dof_id_type p2 = _front[k][i2];           // node in the new front
    1122             : 
    1123         192 :       if (i1 != n1 - 1 && i2 != n2 - 1)
    1124             :       {
    1125         160 :         dof_id_type p3 = _active_boundary[k][i1 + 1]; // next node in the old front
    1126         160 :         dof_id_type p4 = _front[k][i2 + 1];           // next node in the new front
    1127             : 
    1128         160 :         elem.push_back(p1);
    1129         160 :         elem.push_back(p2);
    1130             : 
    1131         160 :         Real d1 = findDistance(p1, p4);
    1132         160 :         Real d2 = findDistance(p3, p2);
    1133             : 
    1134         160 :         if (d1 < d2)
    1135             :         {
    1136          76 :           elem.push_back(p4);
    1137             :           i2++;
    1138             :         }
    1139             : 
    1140             :         else
    1141             :         {
    1142          84 :           elem.push_back(p3);
    1143             :           i1++;
    1144             :         }
    1145         160 :       }
    1146             : 
    1147          32 :       else if (i1 == n1 - 1)
    1148             :       {
    1149          20 :         dof_id_type p4 = _front[k][i2 + 1]; // next node in the new front
    1150             : 
    1151          20 :         elem.push_back(p1);
    1152          20 :         elem.push_back(p2);
    1153          20 :         elem.push_back(p4);
    1154             :         i2++;
    1155             :       }
    1156             : 
    1157          12 :       else if (i2 == n2 - 1)
    1158             :       {
    1159          12 :         dof_id_type p3 = _active_boundary[k][i1 + 1]; // next node in the old front
    1160             : 
    1161          12 :         elem.push_back(p1);
    1162          12 :         elem.push_back(p2);
    1163          12 :         elem.push_back(p3);
    1164             :         i1++;
    1165             :       }
    1166             : 
    1167         192 :       Elem * new_elem = Elem::build(TRI3).release();
    1168             : 
    1169         768 :       for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
    1170             :       {
    1171             :         mooseAssert(_cut_mesh->node_ptr(elem[i]) != nullptr, "Node is NULL");
    1172         576 :         new_elem->set_node(i, _cut_mesh->node_ptr(elem[i]));
    1173             :       }
    1174             : 
    1175         192 :       _cut_mesh->add_elem(new_elem);
    1176         192 :     }
    1177             :   }
    1178          32 : }
    1179             : 
    1180             : void
    1181          32 : CrackMeshCut3DUserObject::joinBoundary()
    1182             : {
    1183          32 :   if (_inactive_boundary_pos.size() == 0)
    1184             :   {
    1185           0 :     _boundary = _front[0];
    1186             :     _boundary.pop_back();
    1187           0 :     return;
    1188             :   }
    1189             : 
    1190             :   std::vector<dof_id_type> full_front;
    1191             : 
    1192          32 :   unsigned int size1 = _active_boundary.size();
    1193             : 
    1194          64 :   for (unsigned int i = 0; i < size1; ++i)
    1195             :   {
    1196          32 :     unsigned int size2 = _active_boundary[i].size();
    1197             : 
    1198          32 :     dof_id_type bd1 = _active_boundary[i][size2 - 1];
    1199          32 :     dof_id_type bd2 = _active_boundary[i + 1 < size1 ? i + 1 : 0][0];
    1200             : 
    1201          32 :     full_front.insert(full_front.end(), _front[i].begin(), _front[i].end());
    1202             : 
    1203          32 :     auto it1 = std::find(_boundary.begin(), _boundary.end(), bd1);
    1204          32 :     unsigned int pos1 = std::distance(_boundary.begin(), it1);
    1205          32 :     auto it2 = std::find(_boundary.begin(), _boundary.end(), bd2);
    1206          32 :     unsigned int pos2 = std::distance(_boundary.begin(), it2);
    1207             : 
    1208          32 :     if (pos1 <= pos2)
    1209           0 :       full_front.insert(full_front.end(), _boundary.begin() + pos1, _boundary.begin() + pos2 + 1);
    1210             :     else
    1211             :     {
    1212          32 :       full_front.insert(full_front.end(), _boundary.begin() + pos1, _boundary.end());
    1213          32 :       full_front.insert(full_front.end(), _boundary.begin(), _boundary.begin() + pos2 + 1);
    1214             :     }
    1215             :   }
    1216             : 
    1217          32 :   _boundary = full_front;
    1218          32 : }
    1219             : 
    1220             : const std::vector<Point>
    1221         162 : CrackMeshCut3DUserObject::getCrackFrontPoints(unsigned int number_crack_front_points) const
    1222             : {
    1223         162 :   std::vector<Point> crack_front_points(number_crack_front_points);
    1224             :   // number_crack_front_points is updated via
    1225             :   // _crack_front_definition->updateNumberOfCrackFrontPoints(_crack_front_points.size())
    1226         162 :   if (number_crack_front_points != _crack_front_points.size())
    1227           0 :     mooseError("number_points_from_provider does not match the number of nodes given in "
    1228             :                "crack_front_nodes");
    1229         810 :   for (unsigned int i = 0; i < number_crack_front_points; ++i)
    1230             :   {
    1231         648 :     dof_id_type id = _crack_front_points[i];
    1232         648 :     Node * this_node = _cut_mesh->node_ptr(id);
    1233             :     mooseAssert(this_node, "Node is NULL");
    1234             :     Point & this_point = *this_node;
    1235         648 :     crack_front_points[i] = this_point;
    1236             :   }
    1237         162 :   return crack_front_points;
    1238           0 : }
    1239             : 
    1240             : const std::vector<RealVectorValue>
    1241         162 : CrackMeshCut3DUserObject::getCrackPlaneNormals(unsigned int number_crack_front_points) const
    1242             : {
    1243         162 :   std::vector<RealVectorValue> crack_plane_normals(number_crack_front_points);
    1244             : 
    1245             :   // build the node-to-elems map
    1246             :   std::unordered_map<dof_id_type, std::vector<dof_id_type>> node_to_elems_map;
    1247             :   node_to_elems_map.clear();
    1248        6132 :   for (const auto & elem : _cut_mesh->element_ptr_range())
    1249       11616 :     for (auto & node : elem->node_ref_range())
    1250        8874 :       node_to_elems_map[node.id()].push_back(elem->id());
    1251             : 
    1252             :   // build the elem-to-normal map
    1253             :   std::unordered_map<dof_id_type, RealVectorValue> elem_to_normal_map;
    1254             :   elem_to_normal_map.clear();
    1255        6132 :   for (const auto & elem : _cut_mesh->element_ptr_range())
    1256             :   {
    1257        2904 :     Point & p1 = *elem->node_ptr(0);
    1258        2904 :     Point & p2 = *elem->node_ptr(1);
    1259        2904 :     Point & p3 = *elem->node_ptr(2);
    1260        2904 :     Plane elem_plane(p3, p2, p1); // to match the current normal of 0,0,-1;
    1261        2904 :     RealVectorValue normal = elem_plane.unit_normal(p1);
    1262        2904 :     elem_to_normal_map[elem->id()] = normal;
    1263        3066 :   }
    1264             : 
    1265             :   // for any front node, the normal is averaged based on the normals of all elements sharing this
    1266             :   // node this code may fail when the front node has no element connected to it, e.g. refinement at
    1267             :   // step 1 has to be disabled
    1268         810 :   for (unsigned int i = 0; i < number_crack_front_points; ++i)
    1269             :   {
    1270         648 :     dof_id_type id = _crack_front_points[i];
    1271         648 :     std::vector<dof_id_type> elems = node_to_elems_map[id];
    1272         648 :     unsigned int n_elem = elems.size();
    1273             : 
    1274             :     RealVectorValue normal_avr = 0;
    1275        2090 :     for (unsigned int j = 0; j < n_elem; ++j)
    1276        1442 :       normal_avr += elem_to_normal_map[elems[j]];
    1277             :     normal_avr = normal_avr / n_elem;
    1278         648 :     crack_plane_normals[i] = normal_avr;
    1279         648 :   }
    1280         162 :   return crack_plane_normals;
    1281           0 : }
    1282             : 
    1283             : std::vector<int>
    1284          29 : CrackMeshCut3DUserObject::getFrontPointsIndex()
    1285             : {
    1286             :   // Crack front definition using the cutter mesh currently only supports one active crack front
    1287             :   // segment
    1288             :   unsigned int ibnd = 0;
    1289          29 :   unsigned int size_this_segment = _active_boundary[ibnd].size();
    1290          29 :   unsigned int n_inactive_nodes = _inactive_boundary_pos.size();
    1291             : 
    1292          29 :   std::vector<int> index(size_this_segment, -1);
    1293             : 
    1294          29 :   unsigned int i1 = n_inactive_nodes == 0 ? 0 : 1;
    1295          29 :   unsigned int i2 = n_inactive_nodes == 0 ? size_this_segment : size_this_segment - 1;
    1296             : 
    1297             :   // loop over active front points
    1298          87 :   for (unsigned int j = i1; j < i2; ++j)
    1299             :   {
    1300          58 :     dof_id_type id = _active_boundary[ibnd][j];
    1301          58 :     auto it = std::find(_crack_front_points.begin(), _crack_front_points.end(), id);
    1302          58 :     index[j] = std::distance(_crack_front_points.begin(), it);
    1303             :   }
    1304             : 
    1305          29 :   return index;
    1306             : }
    1307             : 
    1308             : void
    1309          17 : CrackMeshCut3DUserObject::setSubCriticalGrowthSize(std::vector<Real> & growth_size)
    1310             : {
    1311          17 :   _growth_size = growth_size;
    1312          17 : }
    1313             : 
    1314             : unsigned int
    1315           0 : CrackMeshCut3DUserObject::getNumberOfCrackFrontPoints() const
    1316             : {
    1317           0 :   return _num_crack_front_points;
    1318             : }

Generated by: LCOV version 1.14