LCOV - code coverage report
Current view: top level - src/base - XFEM.C (source / functions) Hit Total Coverage
Test: idaholab/moose xfem: #31730 (e8b711) with base e0c998 Lines: 863 1054 81.9 %
Date: 2025-10-29 16:56:32 Functions: 57 62 91.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 "XFEM.h"
      11             : 
      12             : // XFEM includes
      13             : #include "XFEMAppTypes.h"
      14             : #include "XFEMCutElem2D.h"
      15             : #include "XFEMCutElem3D.h"
      16             : #include "XFEMFuncs.h"
      17             : #include "EFANode.h"
      18             : #include "EFAEdge.h"
      19             : #include "EFAFace.h"
      20             : #include "EFAFragment2D.h"
      21             : #include "EFAFragment3D.h"
      22             : #include "EFAFuncs.h"
      23             : 
      24             : // MOOSE includes
      25             : #include "AuxiliarySystem.h"
      26             : #include "MooseVariable.h"
      27             : #include "NonlinearSystem.h"
      28             : #include "FEProblem.h"
      29             : #include "Assembly.h"
      30             : #include "MooseUtils.h"
      31             : #include "MaterialPropertyStorage.h"
      32             : 
      33             : #include "libmesh/mesh_communication.h"
      34             : #include "libmesh/partitioner.h"
      35             : 
      36         441 : XFEM::XFEM(const InputParameters & params)
      37             :   : XFEMInterface(params),
      38         441 :     _efa_mesh(Moose::out),
      39         441 :     _debug_output_level(1),
      40         441 :     _min_weight_multiplier(0.0)
      41             : {
      42             : #ifndef LIBMESH_ENABLE_UNIQUE_ID
      43             :   mooseError("MOOSE requires unique ids to be enabled in libmesh (configure with "
      44             :              "--enable-unique-id) to use XFEM!");
      45             : #endif
      46         441 :   _has_secondary_cut = false;
      47         441 : }
      48             : 
      49         878 : XFEM::~XFEM()
      50             : {
      51         439 :   for (std::map<unique_id_type, XFEMCutElem *>::iterator cemit = _cut_elem_map.begin();
      52        9402 :        cemit != _cut_elem_map.end();
      53             :        ++cemit)
      54        8963 :     delete cemit->second;
      55        1317 : }
      56             : 
      57             : void
      58         501 : XFEM::addGeometricCut(GeometricCutUserObject * geometric_cut)
      59             : {
      60         501 :   _geometric_cuts.push_back(geometric_cut);
      61             : 
      62         501 :   geometric_cut->setInterfaceID(_geometric_cuts.size() - 1);
      63             : 
      64         501 :   _geom_marker_id_map[geometric_cut] = _geometric_cuts.size() - 1;
      65         501 : }
      66             : 
      67             : void
      68           0 : XFEM::getCrackTipOrigin(std::map<unsigned int, const Elem *> & elem_id_crack_tip,
      69             :                         std::vector<Point> & crack_front_points)
      70             : {
      71             :   elem_id_crack_tip.clear();
      72           0 :   crack_front_points.clear();
      73           0 :   crack_front_points.resize(_elem_crack_origin_direction_map.size());
      74             : 
      75           0 :   unsigned int crack_tip_index = 0;
      76             :   // This map is used to sort the order in _elem_crack_origin_direction_map such that every process
      77             :   // has same order
      78             :   std::map<unsigned int, const Elem *> elem_id_map;
      79             : 
      80             :   int m = -1;
      81           0 :   for (std::map<const Elem *, std::vector<Point>>::iterator mit1 =
      82             :            _elem_crack_origin_direction_map.begin();
      83           0 :        mit1 != _elem_crack_origin_direction_map.end();
      84             :        ++mit1)
      85             :   {
      86           0 :     unsigned int elem_id = mit1->first->id();
      87           0 :     if (elem_id == std::numeric_limits<unsigned int>::max())
      88             :     {
      89           0 :       elem_id_map[m] = mit1->first;
      90           0 :       m--;
      91             :     }
      92             :     else
      93           0 :       elem_id_map[elem_id] = mit1->first;
      94             :   }
      95             : 
      96           0 :   for (std::map<unsigned int, const Elem *>::iterator mit1 = elem_id_map.begin();
      97           0 :        mit1 != elem_id_map.end();
      98             :        mit1++)
      99             :   {
     100           0 :     const Elem * elem = mit1->second;
     101             :     std::map<const Elem *, std::vector<Point>>::iterator mit2 =
     102             :         _elem_crack_origin_direction_map.find(elem);
     103           0 :     if (mit2 != _elem_crack_origin_direction_map.end())
     104             :     {
     105           0 :       elem_id_crack_tip[crack_tip_index] = mit2->first;
     106           0 :       crack_front_points[crack_tip_index] =
     107             :           (mit2->second)[0]; // [0] stores origin coordinates and [1] stores direction
     108           0 :       crack_tip_index++;
     109             :     }
     110             :   }
     111           0 : }
     112             : 
     113             : void
     114          18 : XFEM::addStateMarkedElem(unsigned int elem_id, RealVectorValue & normal)
     115             : {
     116          18 :   Elem * elem = _mesh->elem_ptr(elem_id);
     117             :   std::map<const Elem *, RealVectorValue>::iterator mit;
     118             :   mit = _state_marked_elems.find(elem);
     119          18 :   if (mit != _state_marked_elems.end())
     120           0 :     mooseError(" ERROR: element ", elem->id(), " already marked for crack growth.");
     121          18 :   _state_marked_elems[elem] = normal;
     122          18 : }
     123             : 
     124             : void
     125           0 : XFEM::addStateMarkedElem(unsigned int elem_id, RealVectorValue & normal, unsigned int marked_side)
     126             : {
     127           0 :   addStateMarkedElem(elem_id, normal);
     128           0 :   Elem * elem = _mesh->elem_ptr(elem_id);
     129             :   std::map<const Elem *, unsigned int>::iterator mit;
     130             :   mit = _state_marked_elem_sides.find(elem);
     131           0 :   if (mit != _state_marked_elem_sides.end())
     132           0 :     mooseError(" ERROR: side of element ", elem->id(), " already marked for crack initiation.");
     133             : 
     134           0 :   _state_marked_elem_sides[elem] = marked_side;
     135           0 : }
     136             : 
     137             : void
     138           0 : XFEM::addStateMarkedFrag(unsigned int elem_id, RealVectorValue & normal)
     139             : {
     140           0 :   addStateMarkedElem(elem_id, normal);
     141           0 :   Elem * elem = _mesh->elem_ptr(elem_id);
     142             :   std::set<const Elem *>::iterator mit;
     143             :   mit = _state_marked_frags.find(elem);
     144           0 :   if (mit != _state_marked_frags.end())
     145           0 :     mooseError(
     146           0 :         " ERROR: element ", elem->id(), " already marked for fragment-secondary crack initiation.");
     147             : 
     148           0 :   _state_marked_frags.insert(elem);
     149           0 : }
     150             : 
     151             : void
     152        2029 : XFEM::clearStateMarkedElems()
     153             : {
     154             :   _state_marked_elems.clear();
     155             :   _state_marked_frags.clear();
     156             :   _state_marked_elem_sides.clear();
     157        2029 : }
     158             : 
     159             : void
     160       25186 : XFEM::addGeomMarkedElem2D(const unsigned int elem_id,
     161             :                           const Xfem::GeomMarkedElemInfo2D geom_info,
     162             :                           const unsigned int interface_id)
     163             : {
     164       25186 :   Elem * elem = _mesh->elem_ptr(elem_id);
     165       25186 :   _geom_marked_elems_2d[elem].push_back(geom_info);
     166       25186 :   _geom_marker_id_elems[interface_id].insert(elem_id);
     167       25186 : }
     168             : 
     169             : void
     170        5288 : XFEM::addGeomMarkedElem3D(const unsigned int elem_id,
     171             :                           const Xfem::GeomMarkedElemInfo3D geom_info,
     172             :                           const unsigned int interface_id)
     173             : {
     174        5288 :   Elem * elem = _mesh->elem_ptr(elem_id);
     175        5288 :   _geom_marked_elems_3d[elem].push_back(geom_info);
     176        5288 :   _geom_marker_id_elems[interface_id].insert(elem_id);
     177        5288 : }
     178             : 
     179             : void
     180        2011 : XFEM::clearGeomMarkedElems()
     181             : {
     182             :   _geom_marked_elems_2d.clear();
     183             :   _geom_marked_elems_3d.clear();
     184        2011 : }
     185             : 
     186             : bool
     187         991 : XFEM::didNearTipEnrichmentChange()
     188             : {
     189             :   bool cutter_mesh_changed = false;
     190        2059 :   for (unsigned int i = 0; i < _geometric_cuts.size(); ++i)
     191             :   {
     192        1084 :     cutter_mesh_changed = _geometric_cuts[i]->isCutterMeshChanged();
     193        1084 :     if (cutter_mesh_changed)
     194             :       break;
     195             :   }
     196         991 :   return cutter_mesh_changed;
     197             : }
     198             : 
     199             : void
     200        3031 : XFEM::storeCrackTipOriginAndDirection()
     201             : {
     202             :   _elem_crack_origin_direction_map.clear();
     203             :   std::set<EFAElement *> CrackTipElements = _efa_mesh.getCrackTipElements();
     204             :   std::set<EFAElement *>::iterator sit;
     205        5694 :   for (sit = CrackTipElements.begin(); sit != CrackTipElements.end(); ++sit)
     206             :   {
     207        2663 :     if (_mesh->mesh_dimension() == 2)
     208             :     {
     209        2079 :       EFAElement2D * CEMElem = dynamic_cast<EFAElement2D *>(*sit);
     210        2079 :       EFANode * tip_node = CEMElem->getTipEmbeddedNode();
     211        2079 :       unsigned int cts_id = CEMElem->getCrackTipSplitElementID();
     212             : 
     213             :       Point origin(0, 0, 0);
     214             :       Point direction(0, 0, 0);
     215             : 
     216             :       std::map<unique_id_type, XFEMCutElem *>::const_iterator it;
     217        2079 :       it = _cut_elem_map.find(_mesh->elem_ptr(cts_id)->unique_id());
     218        2079 :       if (it != _cut_elem_map.end())
     219             :       {
     220        2079 :         const XFEMCutElem * xfce = it->second;
     221        2079 :         const EFAElement * EFAelem = xfce->getEFAElement();
     222        2079 :         if (EFAelem->isPartial()) // exclude the full crack tip elements
     223             :         {
     224        2079 :           xfce->getCrackTipOriginAndDirection(tip_node->id(), origin, direction);
     225             :         }
     226             :       }
     227             : 
     228             :       std::vector<Point> tip_data;
     229        2079 :       tip_data.push_back(origin);
     230        2079 :       tip_data.push_back(direction);
     231        2079 :       const Elem * elem = _mesh->elem_ptr((*sit)->id());
     232        4158 :       _elem_crack_origin_direction_map.insert(
     233        2079 :           std::pair<const Elem *, std::vector<Point>>(elem, tip_data));
     234        2079 :     }
     235             :   }
     236        3031 : }
     237             : 
     238             : bool
     239        2012 : XFEM::updateHeal()
     240             : {
     241             :   bool mesh_changed = false;
     242             : 
     243        2012 :   mesh_changed = healMesh();
     244             : 
     245        2012 :   if (mesh_changed)
     246         280 :     buildEFAMesh();
     247             : 
     248             :   if (mesh_changed)
     249             :   {
     250         280 :     _mesh->update_parallel_id_counts();
     251         280 :     MeshCommunication().make_elems_parallel_consistent(*_mesh);
     252         280 :     MeshCommunication().make_nodes_parallel_consistent(*_mesh);
     253             :     //    _mesh->find_neighbors();
     254             :     //    _mesh->contract();
     255         280 :     _mesh->allow_renumbering(false);
     256             :     _mesh->skip_partitioning(true);
     257         280 :     _mesh->prepare_for_use();
     258             : 
     259         280 :     if (_displaced_mesh)
     260             :     {
     261          48 :       _displaced_mesh->update_parallel_id_counts();
     262          48 :       MeshCommunication().make_elems_parallel_consistent(*_displaced_mesh);
     263          48 :       MeshCommunication().make_nodes_parallel_consistent(*_displaced_mesh);
     264          48 :       _displaced_mesh->allow_renumbering(false);
     265             :       _displaced_mesh->skip_partitioning(true);
     266          48 :       _displaced_mesh->prepare_for_use();
     267             :     }
     268             :   }
     269             : 
     270             :   _geom_marker_id_elems.clear();
     271             : 
     272        2012 :   return mesh_changed;
     273             : }
     274             : 
     275             : bool
     276        2012 : XFEM::update(Real time,
     277             :              const std::vector<std::shared_ptr<NonlinearSystemBase>> & nl,
     278             :              AuxiliarySystem & aux)
     279             : {
     280        2012 :   if (_moose_mesh->isDistributedMesh())
     281           0 :     mooseError("Use of XFEM with distributed mesh is not yet supported");
     282             : 
     283             :   bool mesh_changed = false;
     284             : 
     285        2012 :   buildEFAMesh();
     286             : 
     287        2012 :   _fe_problem->execute(EXEC_XFEM_MARK);
     288             : 
     289        2011 :   storeCrackTipOriginAndDirection();
     290             : 
     291        2011 :   if (markCuts(time))
     292        1756 :     mesh_changed = cutMeshWithEFA(nl, aux);
     293             : 
     294        1756 :   if (mesh_changed)
     295             :   {
     296        1020 :     buildEFAMesh();
     297        1020 :     storeCrackTipOriginAndDirection();
     298             :   }
     299             : 
     300        1275 :   if (mesh_changed)
     301             :   {
     302        1020 :     _mesh->allow_renumbering(false);
     303             :     _mesh->skip_partitioning(true);
     304        1020 :     _mesh->prepare_for_use();
     305             : 
     306        1020 :     if (_displaced_mesh)
     307             :     {
     308             :       _displaced_mesh->allow_renumbering(false);
     309             :       _displaced_mesh->skip_partitioning(true);
     310         536 :       _displaced_mesh->prepare_for_use();
     311             :     }
     312             :   }
     313             : 
     314        2011 :   clearStateMarkedElems();
     315        2011 :   clearGeomMarkedElems();
     316             : 
     317        2011 :   return mesh_changed;
     318             : }
     319             : 
     320             : void
     321        1020 : XFEM::initSolution(const std::vector<std::shared_ptr<NonlinearSystemBase>> & nls,
     322             :                    AuxiliarySystem & aux)
     323             : {
     324        1020 :   if (nls.size() != 1)
     325           0 :     mooseError("XFEM does not currently support multiple nonlinear systems");
     326             : 
     327        1020 :   nls[0]->serializeSolution();
     328        1020 :   aux.serializeSolution();
     329        1020 :   NumericVector<Number> & current_solution = *nls[0]->system().current_local_solution;
     330        1020 :   NumericVector<Number> & old_solution = nls[0]->solutionOld();
     331        1020 :   NumericVector<Number> & older_solution = nls[0]->solutionOlder();
     332        1020 :   NumericVector<Number> & current_aux_solution = *aux.system().current_local_solution;
     333        1020 :   NumericVector<Number> & old_aux_solution = aux.solutionOld();
     334             :   NumericVector<Number> & older_aux_solution = aux.solutionOlder();
     335             : 
     336        1020 :   setSolution(*nls[0], _cached_solution, current_solution, old_solution, older_solution);
     337        1020 :   setSolution(
     338        1020 :       aux, _cached_aux_solution, current_aux_solution, old_aux_solution, older_aux_solution);
     339             : 
     340        1020 :   current_solution.close();
     341        1020 :   old_solution.close();
     342        1020 :   older_solution.close();
     343        1020 :   current_aux_solution.close();
     344        1020 :   old_aux_solution.close();
     345        1020 :   older_aux_solution.close();
     346             : 
     347             :   _cached_solution.clear();
     348             :   _cached_aux_solution.clear();
     349        1020 : }
     350             : 
     351             : void
     352        3312 : XFEM::buildEFAMesh()
     353             : {
     354        3312 :   _efa_mesh.reset();
     355             : 
     356             :   // Load all existing elements in to EFA mesh
     357     1560996 :   for (auto & elem : _mesh->element_ptr_range())
     358             :   {
     359             :     std::vector<unsigned int> quad;
     360     4225030 :     for (unsigned int i = 0; i < elem->n_nodes(); ++i)
     361     3447844 :       quad.push_back(elem->node_id(i));
     362             : 
     363      777186 :     if (_mesh->mesh_dimension() == 2)
     364      732396 :       _efa_mesh.add2DElement(quad, elem->id());
     365       44790 :     else if (_mesh->mesh_dimension() == 3)
     366       44790 :       _efa_mesh.add3DElement(quad, elem->id());
     367             :     else
     368           0 :       mooseError("XFEM only works for 2D and 3D");
     369      780498 :   }
     370             : 
     371             :   // Restore fragment information for elements that have been previously cut
     372     1560996 :   for (auto & elem : _mesh->element_ptr_range())
     373             :   {
     374      777186 :     std::map<unique_id_type, XFEMCutElem *>::iterator cemit = _cut_elem_map.find(elem->unique_id());
     375      777186 :     if (cemit != _cut_elem_map.end())
     376             :     {
     377       44131 :       XFEMCutElem * xfce = cemit->second;
     378       44131 :       EFAElement * CEMElem = _efa_mesh.getElemByID(elem->id());
     379       44131 :       _efa_mesh.restoreFragmentInfo(CEMElem, xfce->getEFAElement());
     380             :     }
     381        3312 :   }
     382             : 
     383             :   // Must update edge neighbors before restore edge intersections. Otherwise, when we
     384             :   // add edge intersections, we do not have neighbor information to use.
     385             :   // Correction: no need to use neighbor info now
     386        3312 :   _efa_mesh.updateEdgeNeighbors();
     387        3312 :   _efa_mesh.initCrackTipTopology();
     388        3312 : }
     389             : 
     390             : bool
     391        2011 : XFEM::markCuts(Real time)
     392             : {
     393             :   bool marked_sides = false;
     394        2011 :   if (_mesh->mesh_dimension() == 2)
     395             :   {
     396        1696 :     marked_sides = markCutEdgesByGeometry();
     397        1696 :     marked_sides |= markCutEdgesByState(time);
     398             :   }
     399         315 :   else if (_mesh->mesh_dimension() == 3)
     400             :   {
     401         315 :     marked_sides = markCutFacesByGeometry();
     402         315 :     marked_sides |= markCutFacesByState();
     403             :   }
     404        2011 :   return marked_sides;
     405             : }
     406             : 
     407             : bool
     408        1696 : XFEM::markCutEdgesByGeometry()
     409             : {
     410             :   bool marked_edges = false;
     411             :   bool marked_nodes = false;
     412             : 
     413       26767 :   for (const auto & gme : _geom_marked_elems_2d)
     414             :   {
     415       50257 :     for (const auto & gmei : gme.second)
     416             :     {
     417       25186 :       EFAElement2D * EFAElem = getEFAElem2D(gme.first);
     418             : 
     419       73621 :       for (unsigned int i = 0; i < gmei._elem_cut_edges.size(); ++i) // mark element edges
     420             :       {
     421       48435 :         if (!EFAElem->isEdgePhantom(
     422       48435 :                 gmei._elem_cut_edges[i]._host_side_id)) // must not be phantom edge
     423             :         {
     424       48339 :           _efa_mesh.addElemEdgeIntersection(gme.first->id(),
     425       48339 :                                             gmei._elem_cut_edges[i]._host_side_id,
     426       48339 :                                             gmei._elem_cut_edges[i]._distance);
     427             :           marked_edges = true;
     428             :         }
     429             :       }
     430             : 
     431       25532 :       for (unsigned int i = 0; i < gmei._elem_cut_nodes.size(); ++i) // mark element edges
     432             :       {
     433         346 :         _efa_mesh.addElemNodeIntersection(gme.first->id(), gmei._elem_cut_nodes[i]._host_id);
     434             :         marked_nodes = true;
     435             :       }
     436             : 
     437       61545 :       for (unsigned int i = 0; i < gmei._frag_cut_edges.size();
     438             :            ++i) // MUST DO THIS AFTER MARKING ELEMENT EDGES
     439             :       {
     440       72718 :         if (!EFAElem->getFragment(0)->isSecondaryInteriorEdge(
     441       36359 :                 gmei._frag_cut_edges[i]._host_side_id))
     442             :         {
     443       36359 :           if (_efa_mesh.addFragEdgeIntersection(gme.first->id(),
     444       36359 :                                                 gmei._frag_cut_edges[i]._host_side_id,
     445       36359 :                                                 gmei._frag_cut_edges[i]._distance))
     446             :           {
     447             :             marked_edges = true;
     448         504 :             if (!isElemAtCrackTip(gme.first))
     449         144 :               _has_secondary_cut = true;
     450             :           }
     451             :         }
     452             :       }
     453             :     }
     454             :   }
     455             : 
     456        1696 :   return marked_edges || marked_nodes;
     457             : }
     458             : 
     459             : void
     460           0 : XFEM::correctCrackExtensionDirection(const Elem * elem,
     461             :                                      EFAElement2D * CEMElem,
     462             :                                      EFAEdge * orig_edge,
     463             :                                      Point normal,
     464             :                                      Point crack_tip_origin,
     465             :                                      Point crack_tip_direction,
     466             :                                      Real & distance_keep,
     467             :                                      unsigned int & edge_id_keep,
     468             :                                      Point & normal_keep)
     469             : {
     470           0 :   std::vector<Point> edge_ends(2, Point(0.0, 0.0, 0.0));
     471             :   Point edge1(0.0, 0.0, 0.0);
     472             :   Point edge2(0.0, 0.0, 0.0);
     473             :   Point left_angle(0.0, 0.0, 0.0);
     474             :   Point right_angle(0.0, 0.0, 0.0);
     475             :   Point left_angle_normal(0.0, 0.0, 0.0);
     476             :   Point right_angle_normal(0.0, 0.0, 0.0);
     477             :   Point crack_direction_normal(0.0, 0.0, 0.0);
     478             :   Point edge1_to_tip(0.0, 0.0, 0.0);
     479             :   Point edge2_to_tip(0.0, 0.0, 0.0);
     480             :   Point edge1_to_tip_normal(0.0, 0.0, 0.0);
     481             :   Point edge2_to_tip_normal(0.0, 0.0, 0.0);
     482             : 
     483             :   Real cos_45 = std::cos(45.0 / 180.0 * 3.14159);
     484             :   Real sin_45 = std::sin(45.0 / 180.0 * 3.14159);
     485             : 
     486           0 :   left_angle(0) = cos_45 * crack_tip_direction(0) - sin_45 * crack_tip_direction(1);
     487           0 :   left_angle(1) = sin_45 * crack_tip_direction(0) + cos_45 * crack_tip_direction(1);
     488             : 
     489           0 :   right_angle(0) = cos_45 * crack_tip_direction(0) + sin_45 * crack_tip_direction(1);
     490           0 :   right_angle(1) = -sin_45 * crack_tip_direction(0) + cos_45 * crack_tip_direction(1);
     491             : 
     492           0 :   left_angle_normal(0) = -left_angle(1);
     493             :   left_angle_normal(1) = left_angle(0);
     494             : 
     495           0 :   right_angle_normal(0) = -right_angle(1);
     496             :   right_angle_normal(1) = right_angle(0);
     497             : 
     498           0 :   crack_direction_normal(0) = -crack_tip_direction(1);
     499             :   crack_direction_normal(1) = crack_tip_direction(0);
     500             : 
     501             :   Real angle_min = 0.0;
     502           0 :   Real distance = 0.0;
     503           0 :   unsigned int nsides = CEMElem->numEdges();
     504             : 
     505           0 :   for (unsigned int i = 0; i < nsides; ++i)
     506             :   {
     507           0 :     if (!orig_edge->isPartialOverlap(*CEMElem->getEdge(i)))
     508             :     {
     509           0 :       edge_ends[0] = getEFANodeCoords(CEMElem->getEdge(i)->getNode(0), CEMElem, elem);
     510           0 :       edge_ends[1] = getEFANodeCoords(CEMElem->getEdge(i)->getNode(1), CEMElem, elem);
     511             : 
     512           0 :       edge1_to_tip = (edge_ends[0] * 0.95 + edge_ends[1] * 0.05) - crack_tip_origin;
     513           0 :       edge2_to_tip = (edge_ends[0] * 0.05 + edge_ends[1] * 0.95) - crack_tip_origin;
     514             : 
     515           0 :       edge1_to_tip /= pow(edge1_to_tip.norm_sq(), 0.5);
     516           0 :       edge2_to_tip /= pow(edge2_to_tip.norm_sq(), 0.5);
     517             : 
     518           0 :       edge1_to_tip_normal(0) = -edge1_to_tip(1);
     519           0 :       edge1_to_tip_normal(1) = edge1_to_tip(0);
     520             : 
     521           0 :       edge2_to_tip_normal(0) = -edge2_to_tip(1);
     522           0 :       edge2_to_tip_normal(1) = edge2_to_tip(0);
     523             : 
     524             :       Real angle_edge1_normal = edge1_to_tip_normal * normal;
     525             :       Real angle_edge2_normal = edge2_to_tip_normal * normal;
     526             : 
     527           0 :       if (std::abs(angle_edge1_normal) > std::abs(angle_min) &&
     528             :           (edge1_to_tip * crack_tip_direction) > std::cos(45.0 / 180.0 * 3.14159))
     529             :       {
     530           0 :         edge_id_keep = i;
     531           0 :         distance_keep = 0.05;
     532           0 :         normal_keep = edge1_to_tip_normal;
     533             :         angle_min = angle_edge1_normal;
     534             :       }
     535           0 :       else if (std::abs(angle_edge2_normal) > std::abs(angle_min) &&
     536             :                (edge2_to_tip * crack_tip_direction) > std::cos(45.0 / 180.0 * 3.14159))
     537             :       {
     538           0 :         edge_id_keep = i;
     539           0 :         distance_keep = 0.95;
     540           0 :         normal_keep = edge2_to_tip_normal;
     541             :         angle_min = angle_edge2_normal;
     542             :       }
     543             : 
     544           0 :       if (initCutIntersectionEdge(
     545           0 :               crack_tip_origin, left_angle_normal, edge_ends[0], edge_ends[1], distance) &&
     546           0 :           (!CEMElem->isEdgePhantom(i)))
     547             :       {
     548           0 :         if (std::abs(left_angle_normal * normal) > std::abs(angle_min) &&
     549             :             (edge1_to_tip * crack_tip_direction) > std::cos(45.0 / 180.0 * 3.14159))
     550             :         {
     551           0 :           edge_id_keep = i;
     552           0 :           distance_keep = distance;
     553           0 :           normal_keep = left_angle_normal;
     554             :           angle_min = left_angle_normal * normal;
     555             :         }
     556             :       }
     557           0 :       else if (initCutIntersectionEdge(
     558           0 :                    crack_tip_origin, right_angle_normal, edge_ends[0], edge_ends[1], distance) &&
     559           0 :                (!CEMElem->isEdgePhantom(i)))
     560             :       {
     561           0 :         if (std::abs(right_angle_normal * normal) > std::abs(angle_min) &&
     562             :             (edge2_to_tip * crack_tip_direction) > std::cos(45.0 / 180.0 * 3.14159))
     563             :         {
     564           0 :           edge_id_keep = i;
     565           0 :           distance_keep = distance;
     566           0 :           normal_keep = right_angle_normal;
     567             :           angle_min = right_angle_normal * normal;
     568             :         }
     569             :       }
     570           0 :       else if (initCutIntersectionEdge(crack_tip_origin,
     571             :                                        crack_direction_normal,
     572             :                                        edge_ends[0],
     573             :                                        edge_ends[1],
     574           0 :                                        distance) &&
     575           0 :                (!CEMElem->isEdgePhantom(i)))
     576             :       {
     577           0 :         if (std::abs(crack_direction_normal * normal) > std::abs(angle_min) &&
     578             :             (crack_tip_direction * crack_tip_direction) > std::cos(45.0 / 180.0 * 3.14159))
     579             :         {
     580           0 :           edge_id_keep = i;
     581           0 :           distance_keep = distance;
     582           0 :           normal_keep = crack_direction_normal;
     583             :           angle_min = crack_direction_normal * normal;
     584             :         }
     585             :       }
     586             :     }
     587             :   }
     588             : 
     589             :   // avoid small volume fraction cut
     590           0 :   if ((distance_keep - 0.05) < 0.0)
     591             :   {
     592           0 :     distance_keep = 0.05;
     593             :   }
     594           0 :   else if ((distance_keep - 0.95) > 0.0)
     595             :   {
     596           0 :     distance_keep = 0.95;
     597             :   }
     598           0 : }
     599             : 
     600             : bool
     601        1696 : XFEM::markCutEdgesByState(Real time)
     602             : {
     603             :   bool marked_edges = false;
     604        1696 :   for (std::map<const Elem *, RealVectorValue>::iterator pmeit = _state_marked_elems.begin();
     605        1705 :        pmeit != _state_marked_elems.end();
     606             :        ++pmeit)
     607             :   {
     608           9 :     const Elem * elem = pmeit->first;
     609           9 :     RealVectorValue normal = pmeit->second;
     610           9 :     EFAElement2D * CEMElem = getEFAElem2D(elem);
     611             : 
     612           9 :     Real volfrac_elem = getPhysicalVolumeFraction(elem);
     613           9 :     if (volfrac_elem < 0.25)
     614           0 :       continue;
     615             : 
     616             :     // continue if elem is already cut twice - IMPORTANT
     617           9 :     if (CEMElem->isFinalCut())
     618           0 :       continue;
     619             : 
     620             :     // find the first cut edge
     621           9 :     unsigned int nsides = CEMElem->numEdges();
     622             :     unsigned int orig_cut_side_id = std::numeric_limits<unsigned int>::max();
     623             :     Real orig_cut_distance = -1.0;
     624             :     EFANode * orig_node = nullptr;
     625             :     EFAEdge * orig_edge = nullptr;
     626             : 
     627             :     // crack tip origin coordinates and direction
     628             :     Point crack_tip_origin(0, 0, 0);
     629             :     Point crack_tip_direction(0, 0, 0);
     630           9 :     if (isElemAtCrackTip(elem)) // crack tip element's crack intiation
     631             :     {
     632           9 :       orig_cut_side_id = CEMElem->getTipEdgeID();
     633           9 :       if (orig_cut_side_id < nsides) // valid crack-tip edge found
     634             :       {
     635           9 :         orig_edge = CEMElem->getEdge(orig_cut_side_id);
     636           9 :         orig_node = CEMElem->getTipEmbeddedNode();
     637             :       }
     638             :       else
     639           0 :         mooseError("element ", elem->id(), " has no valid crack-tip edge");
     640             : 
     641             :       // obtain the crack tip origin coordinates and direction.
     642             :       std::map<const Elem *, std::vector<Point>>::iterator ecodm =
     643             :           _elem_crack_origin_direction_map.find(elem);
     644           9 :       if (ecodm != _elem_crack_origin_direction_map.end())
     645             :       {
     646           9 :         crack_tip_origin = (ecodm->second)[0];
     647           9 :         crack_tip_direction = (ecodm->second)[1];
     648             :       }
     649             :       else
     650           0 :         mooseError("element ", elem->id(), " cannot find its crack tip origin and direction.");
     651             :     }
     652             :     else
     653             :     {
     654             :       std::map<const Elem *, unsigned int>::iterator mit1;
     655             :       mit1 = _state_marked_elem_sides.find(elem);
     656             :       std::set<const Elem *>::iterator mit2;
     657             :       mit2 = _state_marked_frags.find(elem);
     658             : 
     659           0 :       if (mit1 != _state_marked_elem_sides.end()) // specified boundary crack initiation
     660             :       {
     661           0 :         orig_cut_side_id = mit1->second;
     662           0 :         if (!CEMElem->isEdgePhantom(orig_cut_side_id) &&
     663           0 :             !CEMElem->getEdge(orig_cut_side_id)->hasIntersection())
     664             :         {
     665             :           orig_cut_distance = 0.5;
     666           0 :           _efa_mesh.addElemEdgeIntersection(elem->id(), orig_cut_side_id, orig_cut_distance);
     667           0 :           orig_edge = CEMElem->getEdge(orig_cut_side_id);
     668           0 :           orig_node = orig_edge->getEmbeddedNode(0);
     669             :           // get a virtual crack tip direction
     670             :           Point elem_center(0.0, 0.0, 0.0);
     671             :           Point edge_center;
     672           0 :           for (unsigned int i = 0; i < nsides; ++i)
     673             :           {
     674           0 :             elem_center += getEFANodeCoords(CEMElem->getEdge(i)->getNode(0), CEMElem, elem);
     675           0 :             elem_center += getEFANodeCoords(CEMElem->getEdge(i)->getNode(1), CEMElem, elem);
     676             :           }
     677           0 :           elem_center /= nsides * 2.0;
     678           0 :           edge_center = getEFANodeCoords(orig_edge->getNode(0), CEMElem, elem) +
     679           0 :                         getEFANodeCoords(orig_edge->getNode(1), CEMElem, elem);
     680             :           edge_center /= 2.0;
     681           0 :           crack_tip_origin = edge_center;
     682           0 :           crack_tip_direction = elem_center - edge_center;
     683           0 :           crack_tip_direction /= pow(crack_tip_direction.norm_sq(), 0.5);
     684             :         }
     685             :         else
     686           0 :           continue; // skip this elem if specified boundary edge is phantom
     687             :       }
     688           0 :       else if (mit2 != _state_marked_frags.end()) // cut-surface secondary crack initiation
     689             :       {
     690           0 :         if (CEMElem->numFragments() != 1)
     691           0 :           mooseError("element ",
     692           0 :                      elem->id(),
     693             :                      " flagged for a secondary crack, but has ",
     694           0 :                      CEMElem->numFragments(),
     695             :                      " fragments");
     696           0 :         std::vector<unsigned int> interior_edge_id = CEMElem->getFragment(0)->getInteriorEdgeID();
     697           0 :         if (interior_edge_id.size() == 1)
     698           0 :           orig_cut_side_id = interior_edge_id[0];
     699             :         else
     700             :           continue; // skip this elem if more than one interior edges found (i.e. elem's been cut
     701             :                     // twice)
     702             :         orig_cut_distance = 0.5;
     703           0 :         _efa_mesh.addFragEdgeIntersection(elem->id(), orig_cut_side_id, orig_cut_distance);
     704           0 :         orig_edge = CEMElem->getFragmentEdge(0, orig_cut_side_id);
     705           0 :         orig_node = orig_edge->getEmbeddedNode(0); // must be an interior embedded node
     706             :         Point elem_center(0.0, 0.0, 0.0);
     707             :         Point edge_center;
     708           0 :         unsigned int nsides_frag = CEMElem->getFragment(0)->numEdges();
     709           0 :         for (unsigned int i = 0; i < nsides_frag; ++i)
     710             :         {
     711             :           elem_center +=
     712           0 :               getEFANodeCoords(CEMElem->getFragmentEdge(0, i)->getNode(0), CEMElem, elem);
     713             :           elem_center +=
     714           0 :               getEFANodeCoords(CEMElem->getFragmentEdge(0, i)->getNode(1), CEMElem, elem);
     715             :         }
     716           0 :         elem_center /= nsides_frag * 2.0;
     717           0 :         edge_center = getEFANodeCoords(orig_edge->getNode(0), CEMElem, elem) +
     718           0 :                       getEFANodeCoords(orig_edge->getNode(1), CEMElem, elem);
     719             :         edge_center /= 2.0;
     720           0 :         crack_tip_origin = edge_center;
     721           0 :         crack_tip_direction = elem_center - edge_center;
     722           0 :         crack_tip_direction /= pow(crack_tip_direction.norm_sq(), 0.5);
     723           0 :       }
     724             :       else
     725           0 :         mooseError("element ",
     726           0 :                    elem->id(),
     727             :                    " flagged for state-based growth, but has no edge intersections");
     728             :     }
     729             : 
     730             :     Point cut_origin(0.0, 0.0, 0.0);
     731           9 :     if (orig_node)
     732           9 :       cut_origin = getEFANodeCoords(orig_node, CEMElem, elem); // cutting plane origin's coords
     733             :     else
     734           0 :       mooseError("element ", elem->id(), " does not have valid orig_node");
     735             : 
     736             :     // loop through element edges to add possible second cut points
     737           9 :     std::vector<Point> edge_ends(2, Point(0.0, 0.0, 0.0));
     738             :     Point edge1(0.0, 0.0, 0.0);
     739             :     Point edge2(0.0, 0.0, 0.0);
     740             :     Point cut_edge_point(0.0, 0.0, 0.0);
     741             :     bool find_compatible_direction = false;
     742           9 :     unsigned int edge_id_keep = 0;
     743           9 :     Real distance_keep = 0.0;
     744             :     Point normal_keep(0.0, 0.0, 0.0);
     745           9 :     Real distance = 0.0;
     746             :     bool edge_cut = false;
     747             : 
     748          36 :     for (unsigned int i = 0; i < nsides; ++i)
     749             :     {
     750          36 :       if (!orig_edge->isPartialOverlap(*CEMElem->getEdge(i)))
     751             :       {
     752          27 :         edge_ends[0] = getEFANodeCoords(CEMElem->getEdge(i)->getNode(0), CEMElem, elem);
     753          27 :         edge_ends[1] = getEFANodeCoords(CEMElem->getEdge(i)->getNode(1), CEMElem, elem);
     754          27 :         if ((initCutIntersectionEdge(
     755          36 :                  crack_tip_origin, normal, edge_ends[0], edge_ends[1], distance) &&
     756           9 :              (!CEMElem->isEdgePhantom(i))))
     757             :         {
     758           9 :           cut_edge_point = distance * edge_ends[1] + (1.0 - distance) * edge_ends[0];
     759           9 :           distance_keep = distance;
     760           9 :           edge_id_keep = i;
     761           9 :           normal_keep = normal;
     762             :           edge_cut = true;
     763           9 :           break;
     764             :         }
     765             :       }
     766             :     }
     767             : 
     768             :     Point between_two_cuts = (cut_edge_point - crack_tip_origin);
     769           9 :     between_two_cuts /= pow(between_two_cuts.norm_sq(), 0.5);
     770             :     Real angle_between_two_cuts = between_two_cuts * crack_tip_direction;
     771             : 
     772           9 :     if (angle_between_two_cuts > std::cos(45.0 / 180.0 * 3.14159)) // original cut direction is good
     773             :       find_compatible_direction = true;
     774             : 
     775           9 :     if (!find_compatible_direction && edge_cut)
     776           0 :       correctCrackExtensionDirection(elem,
     777             :                                      CEMElem,
     778             :                                      orig_edge,
     779             :                                      normal,
     780             :                                      crack_tip_origin,
     781             :                                      crack_tip_direction,
     782             :                                      distance_keep,
     783             :                                      edge_id_keep,
     784             :                                      normal_keep);
     785             : 
     786           9 :     if (edge_cut)
     787             :     {
     788           9 :       if (!_use_crack_growth_increment)
     789           0 :         _efa_mesh.addElemEdgeIntersection(elem->id(), edge_id_keep, distance_keep);
     790             :       else
     791             :       {
     792             :         Point growth_direction(0.0, 0.0, 0.0);
     793             : 
     794           9 :         growth_direction(0) = -normal_keep(1);
     795           9 :         growth_direction(1) = normal_keep(0);
     796             : 
     797           9 :         if (growth_direction * crack_tip_direction < 1.0e-10)
     798             :           growth_direction *= (-1.0);
     799             : 
     800             :         Real x0 = crack_tip_origin(0);
     801             :         Real y0 = crack_tip_origin(1);
     802           9 :         Real x1 = x0 + _crack_growth_increment * growth_direction(0);
     803           9 :         Real y1 = y0 + _crack_growth_increment * growth_direction(1);
     804             : 
     805           9 :         XFEMCrackGrowthIncrement2DCut geometric_cut(x0, y0, x1, y1, time * 0.9, time * 0.9);
     806             : 
     807        2250 :         for (const auto & elem : _mesh->element_ptr_range())
     808             :         {
     809             :           std::vector<CutEdgeForCrackGrowthIncr> elem_cut_edges;
     810        1116 :           EFAElement2D * CEMElem = getEFAElem2D(elem);
     811             : 
     812             :           // continue if elem has been already cut twice - IMPORTANT
     813        1116 :           if (CEMElem->isFinalCut())
     814             :             continue;
     815             : 
     816             :           // mark cut edges for the element and its fragment
     817        1116 :           geometric_cut.cutElementByCrackGrowthIncrement(elem, elem_cut_edges, time);
     818             : 
     819        1179 :           for (unsigned int i = 0; i < elem_cut_edges.size(); ++i) // mark element edges
     820             :           {
     821          63 :             if (!CEMElem->isEdgePhantom(
     822             :                     elem_cut_edges[i]._host_side_id)) // must not be phantom edge
     823             :             {
     824          63 :               _efa_mesh.addElemEdgeIntersection(
     825          63 :                   elem->id(), elem_cut_edges[i]._host_side_id, elem_cut_edges[i]._distance);
     826             :             }
     827             :           }
     828        1125 :         }
     829             :       }
     830             :     }
     831             :     // loop though framgent boundary edges to add possible second cut points
     832             :     // N.B. must do this after marking element edges
     833           9 :     if (CEMElem->numFragments() > 0 && !edge_cut)
     834             :     {
     835           0 :       for (unsigned int i = 0; i < CEMElem->getFragment(0)->numEdges(); ++i)
     836             :       {
     837           0 :         if (!orig_edge->isPartialOverlap(*CEMElem->getFragmentEdge(0, i)))
     838             :         {
     839           0 :           edge_ends[0] =
     840           0 :               getEFANodeCoords(CEMElem->getFragmentEdge(0, i)->getNode(0), CEMElem, elem);
     841           0 :           edge_ends[1] =
     842           0 :               getEFANodeCoords(CEMElem->getFragmentEdge(0, i)->getNode(1), CEMElem, elem);
     843           0 :           if (initCutIntersectionEdge(
     844           0 :                   crack_tip_origin, normal, edge_ends[0], edge_ends[1], distance) &&
     845           0 :               (!CEMElem->getFragment(0)->isSecondaryInteriorEdge(i)))
     846             :           {
     847           0 :             if (_efa_mesh.addFragEdgeIntersection(elem->id(), edge_id_keep, distance_keep))
     848           0 :               if (!isElemAtCrackTip(elem))
     849           0 :                 _has_secondary_cut = true;
     850             :             break;
     851             :           }
     852             :         }
     853             :       }
     854             :     }
     855             : 
     856             :     marked_edges = true;
     857             : 
     858           9 :   } // loop over all state_marked_elems
     859             : 
     860        1696 :   return marked_edges;
     861             : }
     862             : 
     863             : bool
     864         315 : XFEM::markCutFacesByGeometry()
     865             : {
     866             :   bool marked_faces = false;
     867             : 
     868        5603 :   for (const auto & gme : _geom_marked_elems_3d)
     869             :   {
     870       10576 :     for (const auto & gmei : gme.second)
     871             :     {
     872        5288 :       EFAElement3D * EFAElem = getEFAElem3D(gme.first);
     873             : 
     874       23246 :       for (unsigned int i = 0; i < gmei._elem_cut_faces.size(); ++i) // mark element faces
     875             :       {
     876       17958 :         if (!EFAElem->isFacePhantom(gmei._elem_cut_faces[i]._face_id)) // must not be phantom face
     877             :         {
     878       17958 :           _efa_mesh.addElemFaceIntersection(gme.first->id(),
     879       17958 :                                             gmei._elem_cut_faces[i]._face_id,
     880       17958 :                                             gmei._elem_cut_faces[i]._face_edge,
     881       17958 :                                             gmei._elem_cut_faces[i]._position);
     882             :           marked_faces = true;
     883             :         }
     884             :       }
     885             : 
     886        5288 :       for (unsigned int i = 0; i < gmei._frag_cut_faces.size();
     887             :            ++i) // MUST DO THIS AFTER MARKING ELEMENT EDGES
     888             :       {
     889           0 :         if (!EFAElem->getFragment(0)->isThirdInteriorFace(gmei._frag_cut_faces[i]._face_id))
     890             :         {
     891           0 :           _efa_mesh.addFragFaceIntersection(gme.first->id(),
     892           0 :                                             gmei._frag_cut_faces[i]._face_id,
     893           0 :                                             gmei._frag_cut_faces[i]._face_edge,
     894           0 :                                             gmei._frag_cut_faces[i]._position);
     895             :           marked_faces = true;
     896             :         }
     897             :       }
     898             :     }
     899             :   }
     900             : 
     901         315 :   return marked_faces;
     902             : }
     903             : 
     904             : bool
     905         315 : XFEM::markCutFacesByState()
     906             : {
     907             :   bool marked_faces = false;
     908             :   // TODO: need to finish this for 3D problems
     909         315 :   return marked_faces;
     910             : }
     911             : 
     912             : bool
     913          27 : XFEM::initCutIntersectionEdge(
     914             :     Point cut_origin, RealVectorValue cut_normal, Point & edge_p1, Point & edge_p2, Real & dist)
     915             : {
     916          27 :   dist = 0.0;
     917             :   bool does_intersect = false;
     918             :   Point origin2p1 = edge_p1 - cut_origin;
     919          27 :   Real plane2p1 = cut_normal(0) * origin2p1(0) + cut_normal(1) * origin2p1(1);
     920             :   Point origin2p2 = edge_p2 - cut_origin;
     921          27 :   Real plane2p2 = cut_normal(0) * origin2p2(0) + cut_normal(1) * origin2p2(1);
     922             : 
     923          27 :   if (plane2p1 * plane2p2 < 0.0)
     924             :   {
     925           9 :     dist = -plane2p1 / (plane2p2 - plane2p1);
     926             :     does_intersect = true;
     927             :   }
     928          27 :   return does_intersect;
     929             : }
     930             : 
     931             : bool
     932        2012 : XFEM::healMesh()
     933             : {
     934             :   bool mesh_changed = false;
     935             : 
     936             :   std::set<Node *> nodes_to_delete;
     937             :   std::set<Node *> nodes_to_delete_displaced;
     938             :   std::set<unsigned int> cutelems_to_delete;
     939        2012 :   unsigned int deleted_elem_count = 0;
     940             :   std::vector<std::string> healed_geometric_cuts;
     941             : 
     942        4225 :   for (unsigned int i = 0; i < _geometric_cuts.size(); ++i)
     943             :   {
     944        2213 :     if (_geometric_cuts[i]->shouldHealMesh())
     945             :     {
     946         504 :       healed_geometric_cuts.push_back(_geometric_cuts[i]->name());
     947        1372 :       for (auto & it : _sibling_elems[_geometric_cuts[i]->getInterfaceID()])
     948             :       {
     949         868 :         Elem * elem1 = const_cast<Elem *>(it.first);
     950         868 :         Elem * elem2 = const_cast<Elem *>(it.second);
     951             : 
     952             :         std::map<unique_id_type, XFEMCutElem *>::iterator cemit =
     953         868 :             _cut_elem_map.find(elem1->unique_id());
     954         868 :         if (cemit != _cut_elem_map.end())
     955             :         {
     956         868 :           const XFEMCutElem * xfce = cemit->second;
     957             : 
     958         868 :           cutelems_to_delete.insert(elem1->unique_id());
     959             : 
     960        4596 :           for (unsigned int in = 0; in < elem1->n_nodes(); ++in)
     961             :           {
     962        3728 :             Node * e1node = elem1->node_ptr(in);
     963        3728 :             Node * e2node = elem2->node_ptr(in);
     964        3728 :             if (!xfce->isPointPhysical(*e1node) &&
     965             :                 e1node != e2node) // This would happen at the crack tip
     966             :             {
     967        1768 :               elem1->set_node(in, e2node);
     968        1768 :               nodes_to_delete.insert(e1node);
     969             :             }
     970        1960 :             else if (e1node != e2node)
     971        1912 :               nodes_to_delete.insert(e2node);
     972             :           }
     973             :         }
     974             :         else
     975           0 :           mooseError("Could not find XFEMCutElem for element to be kept in healing");
     976             : 
     977             :         // Store the material properties of the elements to be healed. So that if the element is
     978             :         // immediately re-cut, we can restore the material properties (especially those stateful
     979             :         // ones).
     980         868 :         std::vector<const Elem *> healed_elems = {elem1, elem2};
     981             : 
     982         868 :         if (_geometric_cuts[i]->shouldHealMesh())
     983             :           // If the parent element will not be re-cut, then all of its nodes must have the same
     984             :           // CutSubdomainID. Therefore, just query the first node in this parent element to
     985             :           // get its CutSubdomainID.
     986        2604 :           for (auto e : healed_elems)
     987        1736 :             if (elem1->processor_id() == _mesh->processor_id() &&
     988             :                 e->processor_id() == _mesh->processor_id())
     989             :             {
     990        1282 :               storeMaterialPropertiesForElement(/*parent_elem = */ elem1, /*child_elem = */ e);
     991             :               // In case the healed element is not re-cut, copy the corresponding material
     992             :               // properties to the parent element now than later.
     993             :               CutSubdomainID parent_gcsid =
     994        1282 :                   _geometric_cuts[i]->getCutSubdomainID(elem1->node_ptr(0));
     995        1282 :               CutSubdomainID gcsid = _geom_cut_elems[e]._cut_subdomain_id;
     996        1282 :               if (parent_gcsid == gcsid)
     997         641 :                 loadMaterialPropertiesForElement(elem1, e, _geom_cut_elems);
     998             :             }
     999             : 
    1000         868 :         if (_displaced_mesh)
    1001             :         {
    1002         408 :           Elem * elem1_displaced = _displaced_mesh->elem_ptr(it.first->id());
    1003         408 :           Elem * elem2_displaced = _displaced_mesh->elem_ptr(it.second->id());
    1004             : 
    1005             :           std::map<unique_id_type, XFEMCutElem *>::iterator cemit =
    1006         408 :               _cut_elem_map.find(elem1_displaced->unique_id());
    1007         408 :           if (cemit != _cut_elem_map.end())
    1008             :           {
    1009         408 :             const XFEMCutElem * xfce = cemit->second;
    1010             : 
    1011        2216 :             for (unsigned int in = 0; in < elem1_displaced->n_nodes(); ++in)
    1012             :             {
    1013        1808 :               Node * e1node_displaced = elem1_displaced->node_ptr(in);
    1014        1808 :               Node * e2node_displaced = elem2_displaced->node_ptr(in);
    1015        1808 :               if (!xfce->isPointPhysical(*elem1->node_ptr(in)) &&
    1016             :                   e1node_displaced != e2node_displaced)
    1017             :               {
    1018         856 :                 elem1_displaced->set_node(in, e2node_displaced);
    1019         856 :                 nodes_to_delete_displaced.insert(e1node_displaced);
    1020             :               }
    1021         952 :               else if (e1node_displaced != e2node_displaced)
    1022         904 :                 nodes_to_delete_displaced.insert(e2node_displaced);
    1023             :             }
    1024             :           }
    1025             :           else
    1026           0 :             mooseError("Could not find XFEMCutElem for element to be kept in healing");
    1027             : 
    1028         408 :           elem2_displaced->nullify_neighbors();
    1029         408 :           _displaced_mesh->get_boundary_info().remove(elem2_displaced);
    1030         408 :           _displaced_mesh->delete_elem(elem2_displaced);
    1031             :         }
    1032             : 
    1033             :         // remove the property storage of deleted element/side
    1034         868 :         _material_data[0]->eraseProperty(elem2);
    1035         868 :         _bnd_material_data[0]->eraseProperty(elem2);
    1036             : 
    1037         868 :         cutelems_to_delete.insert(elem2->unique_id());
    1038         868 :         elem2->nullify_neighbors();
    1039         868 :         _mesh->get_boundary_info().remove(elem2);
    1040         868 :         unsigned int deleted_elem_id = elem2->id();
    1041         868 :         _mesh->delete_elem(elem2);
    1042         868 :         if (_debug_output_level > 1)
    1043             :         {
    1044          60 :           if (deleted_elem_count == 0)
    1045          12 :             _console << "\n";
    1046          60 :           _console << "XFEM healing deleted element: " << deleted_elem_id << std::endl;
    1047             :         }
    1048         868 :         ++deleted_elem_count;
    1049             :         mesh_changed = true;
    1050         868 :       }
    1051             :     }
    1052             :   }
    1053             : 
    1054        4444 :   for (auto & sit : nodes_to_delete)
    1055             :   {
    1056        2432 :     Node * node_to_delete = sit;
    1057        2432 :     dof_id_type deleted_node_id = node_to_delete->id();
    1058        2432 :     _mesh->get_boundary_info().remove(node_to_delete);
    1059        2432 :     _mesh->delete_node(node_to_delete);
    1060        2432 :     if (_debug_output_level > 1)
    1061         144 :       _console << "XFEM healing deleted node: " << deleted_node_id << std::endl;
    1062             :   }
    1063             : 
    1064        2012 :   if (_displaced_mesh)
    1065             :   {
    1066        2111 :     for (auto & sit : nodes_to_delete_displaced)
    1067             :     {
    1068         960 :       Node * node_to_delete_displaced = sit;
    1069         960 :       _displaced_mesh->get_boundary_info().remove(node_to_delete_displaced);
    1070         960 :       _displaced_mesh->delete_node(node_to_delete_displaced);
    1071             :     }
    1072             :   }
    1073             : 
    1074        3748 :   for (auto & ced : cutelems_to_delete)
    1075        1736 :     if (_cut_elem_map.find(ced) != _cut_elem_map.end())
    1076             :     {
    1077        1736 :       delete _cut_elem_map.find(ced)->second;
    1078        1736 :       _cut_elem_map.erase(ced);
    1079             :     }
    1080             : 
    1081        4225 :   for (unsigned int i = 0; i < _geometric_cuts.size(); ++i)
    1082        2213 :     if (_geometric_cuts[i]->shouldHealMesh())
    1083        1008 :       _sibling_elems[_geometric_cuts[i]->getInterfaceID()].clear();
    1084             : 
    1085        2012 :   if (_displaced_mesh)
    1086             :   {
    1087        2399 :     for (unsigned int i = 0; i < _geometric_cuts.size(); ++i)
    1088        1248 :       if (_geometric_cuts[i]->shouldHealMesh())
    1089         176 :         _sibling_displaced_elems[_geometric_cuts[i]->getInterfaceID()].clear();
    1090             :   }
    1091             : 
    1092        2028 :   for (auto & ceh : _crack_tip_elems_to_be_healed)
    1093             :   {
    1094             :     _crack_tip_elems.erase(ceh);
    1095             :     _elem_crack_origin_direction_map.erase(ceh);
    1096          16 :     delete _cut_elem_map.find(ceh->unique_id())->second;
    1097          16 :     _cut_elem_map.erase(ceh->unique_id());
    1098             :   }
    1099             : 
    1100        2012 :   if (!healed_geometric_cuts.empty() && _debug_output_level > 0)
    1101             :   {
    1102         492 :     _console << "\nXFEM mesh healing complete\n";
    1103         492 :     _console << "Names of healed geometric cut objects: ";
    1104         984 :     for (auto geomcut : healed_geometric_cuts)
    1105         492 :       _console << geomcut << " ";
    1106         492 :     _console << "\n";
    1107         492 :     _console << "# deleted nodes:    " << nodes_to_delete.size() << "\n";
    1108         492 :     _console << "# deleted elements: " << deleted_elem_count << "\n";
    1109         492 :     _console << std::flush;
    1110             :   }
    1111             : 
    1112        2012 :   return mesh_changed;
    1113        2012 : }
    1114             : 
    1115             : bool
    1116        1756 : XFEM::cutMeshWithEFA(const std::vector<std::shared_ptr<NonlinearSystemBase>> & nls,
    1117             :                      AuxiliarySystem & aux)
    1118             : {
    1119        1756 :   if (nls.size() != 1)
    1120           0 :     mooseError("XFEM does not currently support multiple nonlinear systems");
    1121             : 
    1122             :   std::map<unsigned int, Node *> efa_id_to_new_node;
    1123             :   std::map<unsigned int, Node *> efa_id_to_new_node2;
    1124             :   std::map<unsigned int, Elem *> efa_id_to_new_elem;
    1125             :   _cached_solution.clear();
    1126             :   _cached_aux_solution.clear();
    1127             : 
    1128             :   // Copy the current geometric cut element info (from last time) into the
    1129             :   // _old_geom_cut_elems.
    1130             :   _old_geom_cut_elems.swap(_geom_cut_elems);
    1131             :   _geom_cut_elems.clear();
    1132             : 
    1133        1756 :   _efa_mesh.updatePhysicalLinksAndFragments();
    1134             : 
    1135        1756 :   if (_debug_output_level > 2)
    1136             :   {
    1137           9 :     _console << "\nXFEM Element fragment algorithm mesh prior to cutting:\n";
    1138           9 :     _console << std::flush;
    1139           9 :     _efa_mesh.printMesh();
    1140             :   }
    1141             : 
    1142        1756 :   _efa_mesh.updateTopology();
    1143             : 
    1144        1756 :   if (_debug_output_level > 2)
    1145             :   {
    1146           9 :     _console << "\nXFEM Element fragment algorithm mesh after cutting:\n";
    1147           9 :     _console << std::flush;
    1148           9 :     _efa_mesh.printMesh();
    1149             :   }
    1150             : 
    1151        1756 :   const std::vector<EFANode *> new_nodes = _efa_mesh.getNewNodes();
    1152        1756 :   const std::vector<EFAElement *> new_elements = _efa_mesh.getChildElements();
    1153        1756 :   const std::vector<EFAElement *> delete_elements = _efa_mesh.getParentElements();
    1154             : 
    1155        1756 :   bool mesh_changed = (new_nodes.size() + new_elements.size() + delete_elements.size() > 0);
    1156             : 
    1157             :   // Prepare to cache solution on DOFs modified by XFEM
    1158        1756 :   if (mesh_changed)
    1159             :   {
    1160        1020 :     nls[0]->serializeSolution();
    1161        1020 :     aux.serializeSolution();
    1162        1020 :     if (_debug_output_level > 1)
    1163          18 :       _console << "\n";
    1164             :   }
    1165        1756 :   NumericVector<Number> & current_solution = *nls[0]->system().current_local_solution;
    1166        1756 :   NumericVector<Number> & old_solution = nls[0]->solutionOld();
    1167        1756 :   NumericVector<Number> & older_solution = nls[0]->solutionOlder();
    1168        1756 :   NumericVector<Number> & current_aux_solution = *aux.system().current_local_solution;
    1169        1756 :   NumericVector<Number> & old_aux_solution = aux.solutionOld();
    1170             :   NumericVector<Number> & older_aux_solution = aux.solutionOlder();
    1171             : 
    1172             :   std::map<Node *, Node *> new_nodes_to_parents;
    1173             : 
    1174             :   // Add new nodes
    1175       16334 :   for (unsigned int i = 0; i < new_nodes.size(); ++i)
    1176             :   {
    1177       14578 :     unsigned int new_node_id = new_nodes[i]->id();
    1178       14578 :     unsigned int parent_id = new_nodes[i]->parent()->id();
    1179             : 
    1180       14578 :     Node * parent_node = _mesh->node_ptr(parent_id);
    1181       14578 :     Node * new_node = Node::build(*parent_node, _mesh->max_node_id()).release();
    1182       14578 :     _mesh->add_node(new_node);
    1183             : 
    1184       14578 :     new_nodes_to_parents[new_node] = parent_node;
    1185             : 
    1186       29156 :     new_node->set_n_systems(parent_node->n_systems());
    1187       14578 :     efa_id_to_new_node.insert(std::make_pair(new_node_id, new_node));
    1188       14578 :     if (_debug_output_level > 1)
    1189         264 :       _console << "XFEM added new node: " << new_node->id() << std::endl;
    1190       14578 :     if (_displaced_mesh)
    1191             :     {
    1192        7470 :       const Node * parent_node2 = _displaced_mesh->node_ptr(parent_id);
    1193        7470 :       Node * new_node2 = Node::build(*parent_node2, _displaced_mesh->max_node_id()).release();
    1194        7470 :       _displaced_mesh->add_node(new_node2);
    1195             : 
    1196       14940 :       new_node2->set_n_systems(parent_node2->n_systems());
    1197        7470 :       efa_id_to_new_node2.insert(std::make_pair(new_node_id, new_node2));
    1198             :     }
    1199             :   }
    1200             : 
    1201             :   // Add new elements
    1202             :   std::map<unsigned int, std::vector<const Elem *>> temporary_parent_children_map;
    1203             : 
    1204             :   std::vector<boundary_id_type> parent_boundary_ids;
    1205             : 
    1206       13806 :   for (unsigned int i = 0; i < new_elements.size(); ++i)
    1207             :   {
    1208       12050 :     unsigned int parent_id = new_elements[i]->getParent()->id();
    1209       12050 :     unsigned int efa_child_id = new_elements[i]->id();
    1210             : 
    1211       12050 :     Elem * parent_elem = _mesh->elem_ptr(parent_id);
    1212       12050 :     Elem * libmesh_elem = Elem::build(parent_elem->type()).release();
    1213             : 
    1214       26280 :     for (unsigned int m = 0; m < _geometric_cuts.size(); ++m)
    1215             :     {
    1216       65175 :       for (auto & it : _sibling_elems[_geometric_cuts[m]->getInterfaceID()])
    1217             :       {
    1218       50945 :         if (parent_elem == it.first)
    1219         413 :           it.first = libmesh_elem;
    1220       50532 :         else if (parent_elem == it.second)
    1221         513 :           it.second = libmesh_elem;
    1222             :       }
    1223             :     }
    1224             : 
    1225             :     // parent has at least two children
    1226       12050 :     if (new_elements[i]->getParent()->numChildren() > 1)
    1227       10210 :       temporary_parent_children_map[parent_elem->id()].push_back(libmesh_elem);
    1228             : 
    1229             :     Elem * parent_elem2 = nullptr;
    1230             :     Elem * libmesh_elem2 = nullptr;
    1231       12050 :     if (_displaced_mesh)
    1232             :     {
    1233        7546 :       parent_elem2 = _displaced_mesh->elem_ptr(parent_id);
    1234        7546 :       libmesh_elem2 = Elem::build(parent_elem2->type()).release();
    1235             : 
    1236       16072 :       for (unsigned int m = 0; m < _geometric_cuts.size(); ++m)
    1237             :       {
    1238      225927 :         for (auto & it : _sibling_displaced_elems[_geometric_cuts[m]->getInterfaceID()])
    1239             :         {
    1240      217401 :           if (parent_elem2 == it.first)
    1241         830 :             it.first = libmesh_elem2;
    1242      216571 :           else if (parent_elem2 == it.second)
    1243        1046 :             it.second = libmesh_elem2;
    1244             :         }
    1245             :       }
    1246             :     }
    1247             : 
    1248       80974 :     for (unsigned int j = 0; j < new_elements[i]->numNodes(); ++j)
    1249             :     {
    1250       68924 :       unsigned int node_id = new_elements[i]->getNode(j)->id();
    1251             :       Node * libmesh_node;
    1252             : 
    1253             :       std::map<unsigned int, Node *>::iterator nit = efa_id_to_new_node.find(node_id);
    1254       68924 :       if (nit != efa_id_to_new_node.end())
    1255       28054 :         libmesh_node = nit->second;
    1256             :       else
    1257       40870 :         libmesh_node = _mesh->node_ptr(node_id);
    1258             : 
    1259       68924 :       if (libmesh_node->processor_id() == DofObject::invalid_processor_id)
    1260       14578 :         libmesh_node->processor_id() = parent_elem->processor_id();
    1261             : 
    1262       68924 :       libmesh_elem->set_node(j, libmesh_node);
    1263             : 
    1264             :       // Store solution for all nodes affected by XFEM (even existing nodes)
    1265       68924 :       if (parent_elem->is_semilocal(_mesh->processor_id()))
    1266             :       {
    1267       57781 :         Node * solution_node = libmesh_node; // Node from which to store solution
    1268       57781 :         if (new_nodes_to_parents.find(libmesh_node) != new_nodes_to_parents.end())
    1269       23351 :           solution_node = new_nodes_to_parents[libmesh_node];
    1270             : 
    1271       57781 :         if ((_moose_mesh->isSemiLocal(solution_node)) ||
    1272       30134 :             (libmesh_node->processor_id() == _mesh->processor_id()))
    1273             :         {
    1274       52465 :           storeSolutionForNode(libmesh_node,
    1275             :                                solution_node,
    1276             :                                *nls[0],
    1277       52465 :                                _cached_solution,
    1278             :                                current_solution,
    1279             :                                old_solution,
    1280             :                                older_solution);
    1281       52465 :           storeSolutionForNode(libmesh_node,
    1282             :                                solution_node,
    1283             :                                aux,
    1284       52465 :                                _cached_aux_solution,
    1285             :                                current_aux_solution,
    1286             :                                old_aux_solution,
    1287             :                                older_aux_solution);
    1288             :         }
    1289             :       }
    1290             : 
    1291       68924 :       Node * parent_node = parent_elem->node_ptr(j);
    1292       68924 :       _mesh->get_boundary_info().boundary_ids(parent_node, parent_boundary_ids);
    1293       68924 :       _mesh->get_boundary_info().add_node(libmesh_node, parent_boundary_ids);
    1294             : 
    1295       68924 :       if (_displaced_mesh)
    1296             :       {
    1297             :         std::map<unsigned int, Node *>::iterator nit2 = efa_id_to_new_node2.find(node_id);
    1298       36144 :         if (nit2 != efa_id_to_new_node2.end())
    1299       13806 :           libmesh_node = nit2->second;
    1300             :         else
    1301       22338 :           libmesh_node = _displaced_mesh->node_ptr(node_id);
    1302             : 
    1303       36144 :         if (libmesh_node->processor_id() == DofObject::invalid_processor_id)
    1304        7470 :           libmesh_node->processor_id() = parent_elem2->processor_id();
    1305             : 
    1306       36144 :         libmesh_elem2->set_node(j, libmesh_node);
    1307             : 
    1308             :         parent_node = parent_elem2->node_ptr(j);
    1309       36144 :         _displaced_mesh->get_boundary_info().boundary_ids(parent_node, parent_boundary_ids);
    1310       36144 :         _displaced_mesh->get_boundary_info().add_node(libmesh_node, parent_boundary_ids);
    1311             :       }
    1312             :     }
    1313             : 
    1314       12050 :     libmesh_elem->set_p_level(parent_elem->p_level());
    1315       12050 :     libmesh_elem->set_p_refinement_flag(parent_elem->p_refinement_flag());
    1316       12050 :     _mesh->add_elem(libmesh_elem);
    1317       24100 :     libmesh_elem->set_n_systems(parent_elem->n_systems());
    1318       12050 :     libmesh_elem->subdomain_id() = parent_elem->subdomain_id();
    1319       12050 :     libmesh_elem->processor_id() = parent_elem->processor_id();
    1320             : 
    1321             :     // The crack tip origin map is stored before cut, thus the elem should be updated with new
    1322             :     // element.
    1323             :     std::map<const Elem *, std::vector<Point>>::iterator mit =
    1324             :         _elem_crack_origin_direction_map.find(parent_elem);
    1325       12050 :     if (mit != _elem_crack_origin_direction_map.end())
    1326             :     {
    1327         369 :       std::vector<Point> crack_data = _elem_crack_origin_direction_map[parent_elem];
    1328         369 :       _elem_crack_origin_direction_map.erase(mit);
    1329         369 :       _elem_crack_origin_direction_map[libmesh_elem] = crack_data;
    1330         369 :     }
    1331             : 
    1332       12050 :     if (_debug_output_level > 1)
    1333         270 :       _console << "XFEM added new element: " << libmesh_elem->id() << std::endl;
    1334             : 
    1335             :     XFEMCutElem * xfce = nullptr;
    1336       12050 :     if (_mesh->mesh_dimension() == 2)
    1337             :     {
    1338        9370 :       EFAElement2D * new_efa_elem2d = dynamic_cast<EFAElement2D *>(new_elements[i]);
    1339        9370 :       if (!new_efa_elem2d)
    1340           0 :         mooseError("EFAelem is not of EFAelement2D type");
    1341             :       xfce = new XFEMCutElem2D(libmesh_elem,
    1342             :                                new_efa_elem2d,
    1343        9370 :                                _fe_problem->assembly(0, /*nl_sys_num=*/0).qRule()->n_points(),
    1344       18740 :                                libmesh_elem->n_sides());
    1345             :     }
    1346        2680 :     else if (_mesh->mesh_dimension() == 3)
    1347             :     {
    1348        2680 :       EFAElement3D * new_efa_elem3d = dynamic_cast<EFAElement3D *>(new_elements[i]);
    1349        2680 :       if (!new_efa_elem3d)
    1350           0 :         mooseError("EFAelem is not of EFAelement3D type");
    1351             :       xfce = new XFEMCutElem3D(libmesh_elem,
    1352             :                                new_efa_elem3d,
    1353        2680 :                                _fe_problem->assembly(0, /*nl_sys_num=*/0).qRule()->n_points(),
    1354        5360 :                                libmesh_elem->n_sides());
    1355             :     }
    1356       12050 :     _cut_elem_map.insert(std::pair<unique_id_type, XFEMCutElem *>(libmesh_elem->unique_id(), xfce));
    1357       12050 :     efa_id_to_new_elem.insert(std::make_pair(efa_child_id, libmesh_elem));
    1358             : 
    1359       12050 :     if (_displaced_mesh)
    1360             :     {
    1361        7546 :       libmesh_elem2->set_p_level(parent_elem2->p_level());
    1362             :       libmesh_elem2->set_p_refinement_flag(parent_elem2->p_refinement_flag());
    1363        7546 :       _displaced_mesh->add_elem(libmesh_elem2);
    1364       15092 :       libmesh_elem2->set_n_systems(parent_elem2->n_systems());
    1365        7546 :       libmesh_elem2->subdomain_id() = parent_elem2->subdomain_id();
    1366        7546 :       libmesh_elem2->processor_id() = parent_elem2->processor_id();
    1367             :     }
    1368             : 
    1369       12050 :     unsigned int n_sides = parent_elem->n_sides();
    1370       61930 :     for (unsigned int side = 0; side < n_sides; ++side)
    1371             :     {
    1372       49880 :       _mesh->get_boundary_info().boundary_ids(parent_elem, side, parent_boundary_ids);
    1373       49880 :       _mesh->get_boundary_info().add_side(libmesh_elem, side, parent_boundary_ids);
    1374             :     }
    1375       12050 :     if (_displaced_mesh)
    1376             :     {
    1377        7546 :       n_sides = parent_elem2->n_sides();
    1378       38314 :       for (unsigned int side = 0; side < n_sides; ++side)
    1379             :       {
    1380       30768 :         _displaced_mesh->get_boundary_info().boundary_ids(parent_elem2, side, parent_boundary_ids);
    1381       30768 :         _displaced_mesh->get_boundary_info().add_side(libmesh_elem2, side, parent_boundary_ids);
    1382             :       }
    1383             :     }
    1384             : 
    1385       12050 :     unsigned int n_edges = parent_elem->n_edges();
    1386       72890 :     for (unsigned int edge = 0; edge < n_edges; ++edge)
    1387             :     {
    1388       60840 :       _mesh->get_boundary_info().edge_boundary_ids(parent_elem, edge, parent_boundary_ids);
    1389       60840 :       _mesh->get_boundary_info().add_edge(libmesh_elem, edge, parent_boundary_ids);
    1390             :     }
    1391       12050 :     if (_displaced_mesh)
    1392             :     {
    1393        7546 :       n_edges = parent_elem2->n_edges();
    1394       43018 :       for (unsigned int edge = 0; edge < n_edges; ++edge)
    1395             :       {
    1396       35472 :         _displaced_mesh->get_boundary_info().edge_boundary_ids(
    1397             :             parent_elem2, edge, parent_boundary_ids);
    1398       35472 :         _displaced_mesh->get_boundary_info().add_edge(libmesh_elem2, edge, parent_boundary_ids);
    1399             :       }
    1400             :     }
    1401             : 
    1402             :     // TODO: Also need to copy neighbor material data
    1403       12050 :     if (parent_elem->processor_id() == _mesh->processor_id())
    1404             :     {
    1405        8816 :       if (_material_data[0]->getMaterialPropertyStorage().hasStatefulProperties())
    1406        2870 :         _material_data[0]->copy(*libmesh_elem, *parent_elem, 0);
    1407             : 
    1408        8816 :       if (_bnd_material_data[0]->getMaterialPropertyStorage().hasStatefulProperties())
    1409       15238 :         for (unsigned int side = 0; side < parent_elem->n_sides(); ++side)
    1410             :         {
    1411       12368 :           _mesh->get_boundary_info().boundary_ids(parent_elem, side, parent_boundary_ids);
    1412             :           std::vector<boundary_id_type>::iterator it_bd = parent_boundary_ids.begin();
    1413       13210 :           for (; it_bd != parent_boundary_ids.end(); ++it_bd)
    1414             :           {
    1415         842 :             if (_fe_problem->needBoundaryMaterialOnSide(*it_bd, 0))
    1416           0 :               _bnd_material_data[0]->copy(*libmesh_elem, *parent_elem, side);
    1417             :           }
    1418             :         }
    1419             : 
    1420             :       // Store the current information about the geometrically cut element, and load cached material
    1421             :       // properties into the new child element, if any.
    1422        8816 :       const GeometricCutUserObject * gcuo = getGeometricCutForElem(parent_elem);
    1423        8816 :       if (gcuo && gcuo->shouldHealMesh())
    1424             :       {
    1425        1980 :         CutSubdomainID gcsid = getCutSubdomainID(gcuo, libmesh_elem, parent_elem);
    1426        1980 :         Xfem::CutElemInfo cei(parent_elem, gcuo, gcsid);
    1427             :         _geom_cut_elems.emplace(libmesh_elem, cei);
    1428             :         // Find the element to copy data from.
    1429             :         // Iterate through the old geometrically cut elements, if its parent element AND the
    1430             :         // geometric cut user object AND the cut subdomain ID are the same as the
    1431             :         // current element, then that must be it.
    1432       11123 :         for (auto old_cei : _old_geom_cut_elems)
    1433             :           if (cei.match(old_cei.second))
    1434             :           {
    1435         806 :             loadMaterialPropertiesForElement(libmesh_elem, old_cei.first, _old_geom_cut_elems);
    1436         806 :             if (_debug_output_level > 1)
    1437          80 :               _console << "XFEM set material properties for element: " << libmesh_elem->id()
    1438          40 :                        << "\n";
    1439             :             break;
    1440             :           }
    1441             :       }
    1442             : 
    1443             :       // Store solution for all elements affected by XFEM
    1444        8816 :       storeSolutionForElement(libmesh_elem,
    1445             :                               parent_elem,
    1446             :                               *nls[0],
    1447        8816 :                               _cached_solution,
    1448             :                               current_solution,
    1449             :                               old_solution,
    1450             :                               older_solution);
    1451        8816 :       storeSolutionForElement(libmesh_elem,
    1452             :                               parent_elem,
    1453             :                               aux,
    1454        8816 :                               _cached_aux_solution,
    1455             :                               current_aux_solution,
    1456             :                               old_aux_solution,
    1457             :                               older_aux_solution);
    1458             :     }
    1459             :   }
    1460             : 
    1461             :   // delete elements
    1462        8701 :   for (std::size_t i = 0; i < delete_elements.size(); ++i)
    1463             :   {
    1464        6945 :     Elem * elem_to_delete = _mesh->elem_ptr(delete_elements[i]->id());
    1465             : 
    1466             :     // delete the XFEMCutElem object for any elements that are to be deleted
    1467             :     std::map<unique_id_type, XFEMCutElem *>::iterator cemit =
    1468        6945 :         _cut_elem_map.find(elem_to_delete->unique_id());
    1469        6945 :     if (cemit != _cut_elem_map.end())
    1470             :     {
    1471        1335 :       delete cemit->second;
    1472        1335 :       _cut_elem_map.erase(cemit);
    1473             :     }
    1474             : 
    1475             :     // remove the property storage of deleted element/side
    1476        6945 :     _material_data[0]->eraseProperty(elem_to_delete);
    1477        6945 :     _bnd_material_data[0]->eraseProperty(elem_to_delete);
    1478             : 
    1479        6945 :     elem_to_delete->nullify_neighbors();
    1480        6945 :     _mesh->get_boundary_info().remove(elem_to_delete);
    1481        6945 :     unsigned int deleted_elem_id = elem_to_delete->id();
    1482        6945 :     _mesh->delete_elem(elem_to_delete);
    1483        6945 :     if (_debug_output_level > 1)
    1484         156 :       _console << "XFEM deleted element: " << deleted_elem_id << std::endl;
    1485             : 
    1486        6945 :     if (_displaced_mesh)
    1487             :     {
    1488        4533 :       Elem * elem_to_delete2 = _displaced_mesh->elem_ptr(delete_elements[i]->id());
    1489        4533 :       elem_to_delete2->nullify_neighbors();
    1490        4533 :       _displaced_mesh->get_boundary_info().remove(elem_to_delete2);
    1491        4533 :       _displaced_mesh->delete_elem(elem_to_delete2);
    1492             :     }
    1493             :   }
    1494             : 
    1495        1756 :   for (std::map<unsigned int, std::vector<const Elem *>>::iterator it =
    1496             :            temporary_parent_children_map.begin();
    1497        6861 :        it != temporary_parent_children_map.end();
    1498             :        ++it)
    1499             :   {
    1500             :     std::vector<const Elem *> & sibling_elem_vec = it->second;
    1501             :     // TODO: for cut-node case, how to find the sibling elements?
    1502             :     // if (sibling_elem_vec.size() != 2)
    1503             :     // mooseError("Must have exactly 2 sibling elements");
    1504             : 
    1505       11186 :     for (unsigned int i = 0; i < _geometric_cuts.size(); ++i)
    1506      137737 :       for (auto const & elem_id : _geom_marker_id_elems[_geometric_cuts[i]->getInterfaceID()])
    1507      131656 :         if (it->first == elem_id)
    1508        5112 :           _sibling_elems[_geometric_cuts[i]->getInterfaceID()].push_back(
    1509        5112 :               std::make_pair(sibling_elem_vec[0], sibling_elem_vec[1]));
    1510             :   }
    1511             : 
    1512             :   // add sibling elems on displaced mesh
    1513        1756 :   if (_displaced_mesh)
    1514             :   {
    1515        2167 :     for (unsigned int i = 0; i < _geometric_cuts.size(); ++i)
    1516             :     {
    1517       13873 :       for (auto & se : _sibling_elems[_geometric_cuts[i]->getInterfaceID()])
    1518             :       {
    1519       12745 :         Elem * elem = _displaced_mesh->elem_ptr(se.first->id());
    1520       12745 :         Elem * elem_pair = _displaced_mesh->elem_ptr(se.second->id());
    1521       25490 :         _sibling_displaced_elems[_geometric_cuts[i]->getInterfaceID()].push_back(
    1522             :             std::make_pair(elem, elem_pair));
    1523             :       }
    1524             :     }
    1525             :   }
    1526             : 
    1527             :   // clear the temporary map
    1528             :   temporary_parent_children_map.clear();
    1529             : 
    1530             :   // Store information about crack tip elements
    1531        1756 :   if (mesh_changed)
    1532             :   {
    1533             :     _crack_tip_elems.clear();
    1534             :     _crack_tip_elems_to_be_healed.clear();
    1535             :     const std::set<EFAElement *> CrackTipElements = _efa_mesh.getCrackTipElements();
    1536             :     std::set<EFAElement *>::const_iterator sit;
    1537        2106 :     for (sit = CrackTipElements.begin(); sit != CrackTipElements.end(); ++sit)
    1538             :     {
    1539        1086 :       unsigned int eid = (*sit)->id();
    1540             :       Elem * crack_tip_elem;
    1541             :       std::map<unsigned int, Elem *>::iterator eit = efa_id_to_new_elem.find(eid);
    1542        1086 :       if (eit != efa_id_to_new_elem.end())
    1543         846 :         crack_tip_elem = eit->second;
    1544             :       else
    1545         240 :         crack_tip_elem = _mesh->elem_ptr(eid);
    1546        1086 :       _crack_tip_elems.insert(crack_tip_elem);
    1547             : 
    1548             :       // Store the crack tip elements which are going to be healed
    1549        2272 :       for (unsigned int i = 0; i < _geometric_cuts.size(); ++i)
    1550             :       {
    1551        1186 :         if (_geometric_cuts[i]->shouldHealMesh())
    1552             :         {
    1553         672 :           for (auto const & mie : _geom_marker_id_elems[_geometric_cuts[i]->getInterfaceID()])
    1554         592 :             if ((*sit)->getParent() != nullptr)
    1555             :             {
    1556         592 :               if (_mesh->mesh_dimension() == 2)
    1557             :               {
    1558         416 :                 EFAElement2D * efa_elem2d = dynamic_cast<EFAElement2D *>((*sit)->getParent());
    1559         416 :                 if (!efa_elem2d)
    1560           0 :                   mooseError("EFAelem is not of EFAelement2D type");
    1561             : 
    1562        2080 :                 for (unsigned int edge_id = 0; edge_id < efa_elem2d->numEdges(); ++edge_id)
    1563             :                 {
    1564        3248 :                   for (unsigned int en_iter = 0; en_iter < efa_elem2d->numEdgeNeighbors(edge_id);
    1565             :                        ++en_iter)
    1566             :                   {
    1567        1584 :                     EFAElement2D * edge_neighbor = efa_elem2d->getEdgeNeighbor(edge_id, en_iter);
    1568        1584 :                     if (edge_neighbor != nullptr && edge_neighbor->id() == mie)
    1569          16 :                       _crack_tip_elems_to_be_healed.insert(crack_tip_elem);
    1570             :                   }
    1571             :                 }
    1572             :               }
    1573         176 :               else if (_mesh->mesh_dimension() == 3)
    1574             :               {
    1575         176 :                 EFAElement3D * efa_elem3d = dynamic_cast<EFAElement3D *>((*sit)->getParent());
    1576         176 :                 if (!efa_elem3d)
    1577           0 :                   mooseError("EFAelem is not of EFAelement3D type");
    1578             : 
    1579        1232 :                 for (unsigned int face_id = 0; face_id < efa_elem3d->numFaces(); ++face_id)
    1580             :                 {
    1581        1760 :                   for (unsigned int fn_iter = 0; fn_iter < efa_elem3d->numFaceNeighbors(face_id);
    1582             :                        ++fn_iter)
    1583             :                   {
    1584         704 :                     EFAElement3D * face_neighbor = efa_elem3d->getFaceNeighbor(face_id, fn_iter);
    1585         704 :                     if (face_neighbor != nullptr && face_neighbor->id() == mie)
    1586          16 :                       _crack_tip_elems_to_be_healed.insert(crack_tip_elem);
    1587             :                   }
    1588             :                 }
    1589             :               }
    1590             :             }
    1591             :         }
    1592             :       }
    1593             :     }
    1594             :   }
    1595             : 
    1596        1756 :   if (_debug_output_level > 0)
    1597             :   {
    1598        1747 :     _console << "\nXFEM mesh cutting with element fragment algorithm complete\n";
    1599        1747 :     _console << "# new nodes:        " << new_nodes.size() << "\n";
    1600        1747 :     _console << "# new elements:     " << new_elements.size() << "\n";
    1601        1747 :     _console << "# deleted elements: " << delete_elements.size() << "\n";
    1602        1747 :     _console << std::flush;
    1603             :   }
    1604             : 
    1605             :   // store virtual nodes
    1606             :   // store cut edge info
    1607        1756 :   return mesh_changed;
    1608        3512 : }
    1609             : 
    1610             : Point
    1611      199603 : XFEM::getEFANodeCoords(EFANode * CEMnode,
    1612             :                        EFAElement * CEMElem,
    1613             :                        const Elem * elem,
    1614             :                        MeshBase * displaced_mesh) const
    1615             : {
    1616             :   Point node_coor(0.0, 0.0, 0.0);
    1617             :   std::vector<EFANode *> master_nodes;
    1618             :   std::vector<Point> master_points;
    1619             :   std::vector<double> master_weights;
    1620             : 
    1621      199603 :   CEMElem->getMasterInfo(CEMnode, master_nodes, master_weights);
    1622      497199 :   for (std::size_t i = 0; i < master_nodes.size(); ++i)
    1623             :   {
    1624      297596 :     if (master_nodes[i]->category() == EFANode::N_CATEGORY_PERMANENT)
    1625             :     {
    1626      297596 :       unsigned int local_node_id = CEMElem->getLocalNodeIndex(master_nodes[i]);
    1627             :       const Node * node = elem->node_ptr(local_node_id);
    1628      297596 :       if (displaced_mesh)
    1629           0 :         node = displaced_mesh->node_ptr(node->id());
    1630      297596 :       Point node_p((*node)(0), (*node)(1), (*node)(2));
    1631      297596 :       master_points.push_back(node_p);
    1632             :     }
    1633             :     else
    1634           0 :       mooseError("master nodes must be permanent");
    1635             :   }
    1636      497199 :   for (std::size_t i = 0; i < master_nodes.size(); ++i)
    1637             :     node_coor += master_weights[i] * master_points[i];
    1638             : 
    1639      199603 :   return node_coor;
    1640      199603 : }
    1641             : 
    1642             : Real
    1643     1671829 : XFEM::getPhysicalVolumeFraction(const Elem * elem) const
    1644             : {
    1645             :   Real phys_volfrac = 1.0;
    1646             :   std::map<unique_id_type, XFEMCutElem *>::const_iterator it;
    1647     1671829 :   it = _cut_elem_map.find(elem->unique_id());
    1648     1671829 :   if (it != _cut_elem_map.end())
    1649             :   {
    1650      142433 :     XFEMCutElem * xfce = it->second;
    1651      142433 :     const EFAElement * EFAelem = xfce->getEFAElement();
    1652      142433 :     if (EFAelem->isPartial())
    1653             :     { // exclude the full crack tip elements
    1654      133566 :       xfce->computePhysicalVolumeFraction();
    1655      133566 :       phys_volfrac = xfce->getPhysicalVolumeFraction();
    1656             :     }
    1657             :   }
    1658             : 
    1659     1671829 :   return phys_volfrac;
    1660             : }
    1661             : 
    1662             : bool
    1663      689190 : XFEM::isPointInsidePhysicalDomain(const Elem * elem, const Point & point) const
    1664             : {
    1665             :   std::map<unique_id_type, XFEMCutElem *>::const_iterator it;
    1666      689190 :   it = _cut_elem_map.find(elem->unique_id());
    1667      689190 :   if (it != _cut_elem_map.end())
    1668             :   {
    1669      170562 :     XFEMCutElem * xfce = it->second;
    1670             : 
    1671      170562 :     if (xfce->isPointPhysical(point))
    1672             :       return true;
    1673             :   }
    1674             :   else
    1675             :     return true;
    1676             : 
    1677             :   return false;
    1678             : }
    1679             : 
    1680             : Real
    1681    15622200 : XFEM::getCutPlane(const Elem * elem,
    1682             :                   const Xfem::XFEM_CUTPLANE_QUANTITY quantity,
    1683             :                   unsigned int plane_id) const
    1684             : {
    1685             :   Real comp = 0.0;
    1686             :   Point planedata(0.0, 0.0, 0.0);
    1687             :   std::map<unique_id_type, XFEMCutElem *>::const_iterator it;
    1688    15622200 :   it = _cut_elem_map.find(elem->unique_id());
    1689    15622200 :   if (it != _cut_elem_map.end())
    1690             :   {
    1691     1345608 :     const XFEMCutElem * xfce = it->second;
    1692     1345608 :     const EFAElement * EFAelem = xfce->getEFAElement();
    1693     1345608 :     if (EFAelem->isPartial()) // exclude the full crack tip elements
    1694             :     {
    1695     1263984 :       if ((unsigned int)quantity < 3)
    1696             :       {
    1697             :         unsigned int index = (unsigned int)quantity;
    1698      631992 :         planedata = xfce->getCutPlaneOrigin(plane_id, _displaced_mesh);
    1699      631992 :         comp = planedata(index);
    1700             :       }
    1701      631992 :       else if ((unsigned int)quantity < 6)
    1702             :       {
    1703      631992 :         unsigned int index = (unsigned int)quantity - 3;
    1704      631992 :         planedata = xfce->getCutPlaneNormal(plane_id, _displaced_mesh);
    1705      631992 :         comp = planedata(index);
    1706             :       }
    1707             :       else
    1708           0 :         mooseError("In get_cut_plane index out of range");
    1709             :     }
    1710             :   }
    1711    15622200 :   return comp;
    1712             : }
    1713             : 
    1714             : bool
    1715        2013 : XFEM::isElemAtCrackTip(const Elem * elem) const
    1716             : {
    1717        2013 :   return (_crack_tip_elems.find(elem) != _crack_tip_elems.end());
    1718             : }
    1719             : 
    1720             : bool
    1721     6391554 : XFEM::isElemCut(const Elem * elem, XFEMCutElem *& xfce) const
    1722             : {
    1723     6391554 :   const auto it = _cut_elem_map.find(elem->unique_id());
    1724     6391554 :   if (it != _cut_elem_map.end())
    1725             :   {
    1726      718604 :     xfce = it->second;
    1727      718604 :     const EFAElement * EFAelem = xfce->getEFAElement();
    1728      718604 :     if (EFAelem->isPartial()) // exclude the full crack tip elements
    1729             :       return true;
    1730             :   }
    1731             : 
    1732     5730883 :   xfce = nullptr;
    1733     5730883 :   return false;
    1734             : }
    1735             : 
    1736             : bool
    1737       58044 : XFEM::isElemCut(const Elem * elem) const
    1738             : {
    1739             :   XFEMCutElem * xfce;
    1740       58044 :   return isElemCut(elem, xfce);
    1741             : }
    1742             : 
    1743             : void
    1744           0 : XFEM::getFragmentFaces(const Elem * elem,
    1745             :                        std::vector<std::vector<Point>> & frag_faces,
    1746             :                        bool displaced_mesh) const
    1747             : {
    1748             :   std::map<unique_id_type, XFEMCutElem *>::const_iterator it;
    1749           0 :   it = _cut_elem_map.find(elem->unique_id());
    1750           0 :   if (it != _cut_elem_map.end())
    1751             :   {
    1752           0 :     const XFEMCutElem * xfce = it->second;
    1753           0 :     if (displaced_mesh)
    1754           0 :       xfce->getFragmentFaces(frag_faces, _displaced_mesh);
    1755             :     else
    1756           0 :       xfce->getFragmentFaces(frag_faces);
    1757             :   }
    1758           0 : }
    1759             : 
    1760             : EFAElement2D *
    1761      350258 : XFEM::getEFAElem2D(const Elem * elem)
    1762             : {
    1763      350258 :   EFAElement * EFAelem = _efa_mesh.getElemByID(elem->id());
    1764      350258 :   EFAElement2D * EFAelem2D = dynamic_cast<EFAElement2D *>(EFAelem);
    1765             : 
    1766      350258 :   if (!EFAelem2D)
    1767           0 :     mooseError("EFAelem is not of EFAelement2D type");
    1768             : 
    1769      350258 :   return EFAelem2D;
    1770             : }
    1771             : 
    1772             : EFAElement3D *
    1773       28676 : XFEM::getEFAElem3D(const Elem * elem)
    1774             : {
    1775       28676 :   EFAElement * EFAelem = _efa_mesh.getElemByID(elem->id());
    1776       28676 :   EFAElement3D * EFAelem3D = dynamic_cast<EFAElement3D *>(EFAelem);
    1777             : 
    1778       28676 :   if (!EFAelem3D)
    1779           0 :     mooseError("EFAelem is not of EFAelement3D type");
    1780             : 
    1781       28676 :   return EFAelem3D;
    1782             : }
    1783             : 
    1784             : void
    1785      323827 : XFEM::getFragmentEdges(const Elem * elem,
    1786             :                        EFAElement2D * CEMElem,
    1787             :                        std::vector<std::vector<Point>> & frag_edges) const
    1788             : {
    1789             :   // N.B. CEMElem here has global EFAnode
    1790      323827 :   frag_edges.clear();
    1791      323827 :   if (CEMElem->numFragments() > 0)
    1792             :   {
    1793       17024 :     if (CEMElem->numFragments() > 1)
    1794           0 :       mooseError("element ", elem->id(), " has more than one fragment at this point");
    1795       84336 :     for (unsigned int i = 0; i < CEMElem->getFragment(0)->numEdges(); ++i)
    1796             :     {
    1797       67312 :       std::vector<Point> p_line(2, Point(0.0, 0.0, 0.0));
    1798       67312 :       p_line[0] = getEFANodeCoords(CEMElem->getFragmentEdge(0, i)->getNode(0), CEMElem, elem);
    1799       67312 :       p_line[1] = getEFANodeCoords(CEMElem->getFragmentEdge(0, i)->getNode(1), CEMElem, elem);
    1800       67312 :       frag_edges.push_back(p_line);
    1801       67312 :     }
    1802             :   }
    1803      323827 : }
    1804             : 
    1805             : void
    1806       23388 : XFEM::getFragmentFaces(const Elem * elem,
    1807             :                        EFAElement3D * CEMElem,
    1808             :                        std::vector<std::vector<Point>> & frag_faces) const
    1809             : {
    1810             :   // N.B. CEMElem here has global EFAnode
    1811       23388 :   frag_faces.clear();
    1812       23388 :   if (CEMElem->numFragments() > 0)
    1813             :   {
    1814        3084 :     if (CEMElem->numFragments() > 1)
    1815           0 :       mooseError("element ", elem->id(), " has more than one fragment at this point");
    1816       20074 :     for (unsigned int i = 0; i < CEMElem->getFragment(0)->numFaces(); ++i)
    1817             :     {
    1818       16990 :       unsigned int num_face_nodes = CEMElem->getFragmentFace(0, i)->numNodes();
    1819       16990 :       std::vector<Point> p_line(num_face_nodes, Point(0.0, 0.0, 0.0));
    1820       81906 :       for (unsigned int j = 0; j < num_face_nodes; ++j)
    1821       64916 :         p_line[j] = getEFANodeCoords(CEMElem->getFragmentFace(0, i)->getNode(j), CEMElem, elem);
    1822       16990 :       frag_faces.push_back(p_line);
    1823       16990 :     }
    1824             :   }
    1825       23388 : }
    1826             : 
    1827             : Xfem::XFEM_QRULE &
    1828      657971 : XFEM::getXFEMQRule()
    1829             : {
    1830      657971 :   return _XFEM_qrule;
    1831             : }
    1832             : 
    1833             : void
    1834         440 : XFEM::setXFEMQRule(std::string & xfem_qrule)
    1835             : {
    1836         440 :   if (xfem_qrule == "volfrac")
    1837         384 :     _XFEM_qrule = Xfem::VOLFRAC;
    1838          56 :   else if (xfem_qrule == "moment_fitting")
    1839          56 :     _XFEM_qrule = Xfem::MOMENT_FITTING;
    1840           0 :   else if (xfem_qrule == "direct")
    1841           0 :     _XFEM_qrule = Xfem::DIRECT;
    1842         440 : }
    1843             : 
    1844             : void
    1845         440 : XFEM::setCrackGrowthMethod(bool use_crack_growth_increment, Real crack_growth_increment)
    1846             : {
    1847         440 :   _use_crack_growth_increment = use_crack_growth_increment;
    1848         440 :   _crack_growth_increment = crack_growth_increment;
    1849         440 : }
    1850             : 
    1851             : void
    1852         440 : XFEM::setDebugOutputLevel(unsigned int debug_output_level)
    1853             : {
    1854         440 :   _debug_output_level = debug_output_level;
    1855         440 : }
    1856             : 
    1857             : void
    1858         440 : XFEM::setMinWeightMultiplier(Real min_weight_multiplier)
    1859             : {
    1860         440 :   _min_weight_multiplier = min_weight_multiplier;
    1861         440 : }
    1862             : 
    1863             : bool
    1864     6245694 : XFEM::getXFEMWeights(MooseArray<Real> & weights,
    1865             :                      const Elem * elem,
    1866             :                      QBase * qrule,
    1867             :                      const MooseArray<Point> & q_points)
    1868             : {
    1869             :   bool have_weights = false;
    1870     6245694 :   XFEMCutElem * xfce = nullptr;
    1871     6245694 :   if (isElemCut(elem, xfce))
    1872             :   {
    1873             :     mooseAssert(xfce != nullptr, "Must have valid XFEMCutElem object here");
    1874      657681 :     xfce->getWeightMultipliers(weights, qrule, getXFEMQRule(), q_points);
    1875             :     have_weights = true;
    1876             : 
    1877             :     Real ave_weight_multiplier = 0;
    1878     5322867 :     for (unsigned int i = 0; i < weights.size(); ++i)
    1879     4665186 :       ave_weight_multiplier += weights[i];
    1880      657681 :     ave_weight_multiplier /= weights.size();
    1881             : 
    1882      657681 :     if (ave_weight_multiplier < _min_weight_multiplier)
    1883             :     {
    1884        1422 :       const Real amount_to_add = _min_weight_multiplier - ave_weight_multiplier;
    1885       12120 :       for (unsigned int i = 0; i < weights.size(); ++i)
    1886       10698 :         weights[i] += amount_to_add;
    1887             :     }
    1888             :   }
    1889     6245694 :   return have_weights;
    1890             : }
    1891             : 
    1892             : bool
    1893       87816 : XFEM::getXFEMFaceWeights(MooseArray<Real> & weights,
    1894             :                          const Elem * elem,
    1895             :                          QBase * qrule,
    1896             :                          const MooseArray<Point> & q_points,
    1897             :                          unsigned int side)
    1898             : {
    1899             :   bool have_weights = false;
    1900       87816 :   XFEMCutElem * xfce = nullptr;
    1901       87816 :   if (isElemCut(elem, xfce))
    1902             :   {
    1903             :     mooseAssert(xfce != nullptr, "Must have valid XFEMCutElem object here");
    1904         290 :     xfce->getFaceWeightMultipliers(weights, qrule, getXFEMQRule(), q_points, side);
    1905             :     have_weights = true;
    1906             :   }
    1907       87816 :   return have_weights;
    1908             : }
    1909             : 
    1910             : void
    1911      857466 : XFEM::getXFEMIntersectionInfo(const Elem * elem,
    1912             :                               unsigned int plane_id,
    1913             :                               Point & normal,
    1914             :                               std::vector<Point> & intersectionPoints,
    1915             :                               bool displaced_mesh) const
    1916             : {
    1917             :   std::map<unique_id_type, XFEMCutElem *>::const_iterator it;
    1918      857466 :   it = _cut_elem_map.find(elem->unique_id());
    1919      857466 :   if (it != _cut_elem_map.end())
    1920             :   {
    1921      857466 :     const XFEMCutElem * xfce = it->second;
    1922      857466 :     if (displaced_mesh)
    1923      843304 :       xfce->getIntersectionInfo(plane_id, normal, intersectionPoints, _displaced_mesh);
    1924             :     else
    1925       14162 :       xfce->getIntersectionInfo(plane_id, normal, intersectionPoints);
    1926             :   }
    1927      857466 : }
    1928             : 
    1929             : void
    1930      653748 : XFEM::getXFEMqRuleOnLine(std::vector<Point> & intersection_points,
    1931             :                          std::vector<Point> & quad_pts,
    1932             :                          std::vector<Real> & quad_wts) const
    1933             : {
    1934      653748 :   Point p1 = intersection_points[0];
    1935      653748 :   Point p2 = intersection_points[1];
    1936             : 
    1937             :   // number of quadrature points
    1938             :   std::size_t num_qpoints = 2;
    1939             : 
    1940             :   // quadrature coordinates
    1941             :   Real xi0 = -std::sqrt(1.0 / 3.0);
    1942             :   Real xi1 = std::sqrt(1.0 / 3.0);
    1943             : 
    1944      653748 :   quad_wts.resize(num_qpoints);
    1945      653748 :   quad_pts.resize(num_qpoints);
    1946             : 
    1947      653748 :   Real integ_jacobian = pow((p1 - p2).norm_sq(), 0.5) * 0.5;
    1948             : 
    1949      653748 :   quad_wts[0] = 1.0 * integ_jacobian;
    1950      653748 :   quad_wts[1] = 1.0 * integ_jacobian;
    1951             : 
    1952      653748 :   quad_pts[0] = (1.0 - xi0) / 2.0 * p1 + (1.0 + xi0) / 2.0 * p2;
    1953      653748 :   quad_pts[1] = (1.0 - xi1) / 2.0 * p1 + (1.0 + xi1) / 2.0 * p2;
    1954      653748 : }
    1955             : 
    1956             : void
    1957      203718 : XFEM::getXFEMqRuleOnSurface(std::vector<Point> & intersection_points,
    1958             :                             std::vector<Point> & quad_pts,
    1959             :                             std::vector<Real> & quad_wts) const
    1960             : {
    1961             :   std::size_t nnd_pe = intersection_points.size();
    1962             :   Point xcrd(0.0, 0.0, 0.0);
    1963     1017194 :   for (std::size_t i = 0; i < nnd_pe; ++i)
    1964             :     xcrd += intersection_points[i];
    1965      203718 :   xcrd /= nnd_pe;
    1966             : 
    1967      203718 :   quad_pts.resize(nnd_pe);
    1968      203718 :   quad_wts.resize(nnd_pe);
    1969             : 
    1970      203718 :   Real jac = 0.0;
    1971             : 
    1972     1017194 :   for (std::size_t j = 0; j < nnd_pe; ++j) // loop all sub-tris
    1973             :   {
    1974      813476 :     std::vector<std::vector<Real>> shape(3, std::vector<Real>(3, 0.0));
    1975      813476 :     std::vector<Point> subtrig_points(3, Point(0.0, 0.0, 0.0)); // sub-trig nodal coords
    1976             : 
    1977      813476 :     int jplus1 = j < nnd_pe - 1 ? j + 1 : 0;
    1978      813476 :     subtrig_points[0] = xcrd;
    1979      813476 :     subtrig_points[1] = intersection_points[j];
    1980      813476 :     subtrig_points[2] = intersection_points[jplus1];
    1981             : 
    1982             :     std::vector<std::vector<Real>> sg2;
    1983      813476 :     Xfem::stdQuadr2D(3, 1, sg2);                 // get sg2
    1984     1626952 :     for (std::size_t l = 0; l < sg2.size(); ++l) // loop all int pts on a sub-trig
    1985             :     {
    1986      813476 :       Xfem::shapeFunc2D(3, sg2[l], subtrig_points, shape, jac, true); // Get shape
    1987      813476 :       std::vector<Real> tsg_line(3, 0.0);
    1988     3253904 :       for (std::size_t k = 0; k < 3; ++k) // loop sub-trig nodes
    1989             :       {
    1990     2440428 :         tsg_line[0] += shape[k][2] * subtrig_points[k](0);
    1991     2440428 :         tsg_line[1] += shape[k][2] * subtrig_points[k](1);
    1992     2440428 :         tsg_line[2] += shape[k][2] * subtrig_points[k](2);
    1993             :       }
    1994      813476 :       quad_pts[j + l] = Point(tsg_line[0], tsg_line[1], tsg_line[2]);
    1995      813476 :       quad_wts[j + l] = sg2[l][3] * jac;
    1996      813476 :     }
    1997      813476 :   }
    1998      203718 : }
    1999             : 
    2000             : void
    2001      104930 : XFEM::storeSolutionForNode(const Node * node_to_store_to,
    2002             :                            const Node * node_to_store_from,
    2003             :                            SystemBase & sys,
    2004             :                            std::map<unique_id_type, std::vector<Real>> & stored_solution,
    2005             :                            const NumericVector<Number> & current_solution,
    2006             :                            const NumericVector<Number> & old_solution,
    2007             :                            const NumericVector<Number> & older_solution)
    2008             : {
    2009      104930 :   std::vector<dof_id_type> stored_solution_dofs = getNodeSolutionDofs(node_to_store_from, sys);
    2010             :   std::vector<Real> stored_solution_scratch;
    2011             :   // Size for current solution, as well as for old, and older solution only for transient case
    2012             :   std::size_t stored_solution_size =
    2013      104930 :       (_fe_problem->isTransient() ? stored_solution_dofs.size() * 3 : stored_solution_dofs.size());
    2014      104930 :   stored_solution_scratch.reserve(stored_solution_size);
    2015             : 
    2016             :   // Store in the order defined in stored_solution_dofs first for the current, then for old and
    2017             :   // older if applicable
    2018      225682 :   for (auto dof : stored_solution_dofs)
    2019      120752 :     stored_solution_scratch.push_back(current_solution(dof));
    2020             : 
    2021      104930 :   if (_fe_problem->isTransient())
    2022             :   {
    2023      225610 :     for (auto dof : stored_solution_dofs)
    2024      120728 :       stored_solution_scratch.push_back(old_solution(dof));
    2025             : 
    2026      225610 :     for (auto dof : stored_solution_dofs)
    2027      120728 :       stored_solution_scratch.push_back(older_solution(dof));
    2028             :   }
    2029             : 
    2030      104930 :   if (stored_solution_scratch.size() > 0)
    2031       70537 :     stored_solution[node_to_store_to->unique_id()] = stored_solution_scratch;
    2032      104930 : }
    2033             : 
    2034             : void
    2035       17632 : XFEM::storeSolutionForElement(const Elem * elem_to_store_to,
    2036             :                               const Elem * elem_to_store_from,
    2037             :                               SystemBase & sys,
    2038             :                               std::map<unique_id_type, std::vector<Real>> & stored_solution,
    2039             :                               const NumericVector<Number> & current_solution,
    2040             :                               const NumericVector<Number> & old_solution,
    2041             :                               const NumericVector<Number> & older_solution)
    2042             : {
    2043       17632 :   std::vector<dof_id_type> stored_solution_dofs = getElementSolutionDofs(elem_to_store_from, sys);
    2044             :   std::vector<Real> stored_solution_scratch;
    2045             :   // Size for current solution, as well as for old, and older solution only for transient case
    2046             :   std::size_t stored_solution_size =
    2047       17632 :       (_fe_problem->isTransient() ? stored_solution_dofs.size() * 3 : stored_solution_dofs.size());
    2048       17632 :   stored_solution_scratch.reserve(stored_solution_size);
    2049             : 
    2050             :   // Store in the order defined in stored_solution_dofs first for the current, then for old and
    2051             :   // older if applicable
    2052      152155 :   for (auto dof : stored_solution_dofs)
    2053      134523 :     stored_solution_scratch.push_back(current_solution(dof));
    2054             : 
    2055       17632 :   if (_fe_problem->isTransient())
    2056             :   {
    2057      152065 :     for (auto dof : stored_solution_dofs)
    2058      134445 :       stored_solution_scratch.push_back(old_solution(dof));
    2059             : 
    2060      152065 :     for (auto dof : stored_solution_dofs)
    2061      134445 :       stored_solution_scratch.push_back(older_solution(dof));
    2062             :   }
    2063             : 
    2064       17632 :   if (stored_solution_scratch.size() > 0)
    2065        8816 :     stored_solution[elem_to_store_to->unique_id()] = stored_solution_scratch;
    2066       17632 : }
    2067             : 
    2068             : void
    2069        2040 : XFEM::setSolution(SystemBase & sys,
    2070             :                   const std::map<unique_id_type, std::vector<Real>> & stored_solution,
    2071             :                   NumericVector<Number> & current_solution,
    2072             :                   NumericVector<Number> & old_solution,
    2073             :                   NumericVector<Number> & older_solution)
    2074             : {
    2075     1143196 :   for (auto & node : _mesh->local_node_ptr_range())
    2076             :   {
    2077      569558 :     auto mit = stored_solution.find(node->unique_id());
    2078      569558 :     if (mit != stored_solution.end())
    2079             :     {
    2080       35739 :       const std::vector<Real> & stored_node_solution = mit->second;
    2081       35739 :       std::vector<dof_id_type> stored_solution_dofs = getNodeSolutionDofs(node, sys);
    2082       35739 :       setSolutionForDOFs(stored_node_solution,
    2083             :                          stored_solution_dofs,
    2084             :                          current_solution,
    2085             :                          old_solution,
    2086             :                          older_solution);
    2087       35739 :     }
    2088        2040 :   }
    2089             : 
    2090      952040 :   for (auto & elem : as_range(_mesh->local_elements_begin(), _mesh->local_elements_end()))
    2091             :   {
    2092      472960 :     auto mit = stored_solution.find(elem->unique_id());
    2093      472960 :     if (mit != stored_solution.end())
    2094             :     {
    2095        8816 :       const std::vector<Real> & stored_elem_solution = mit->second;
    2096        8816 :       std::vector<dof_id_type> stored_solution_dofs = getElementSolutionDofs(elem, sys);
    2097        8816 :       setSolutionForDOFs(stored_elem_solution,
    2098             :                          stored_solution_dofs,
    2099             :                          current_solution,
    2100             :                          old_solution,
    2101             :                          older_solution);
    2102        8816 :     }
    2103        2040 :   }
    2104        2040 : }
    2105             : 
    2106             : void
    2107       44555 : XFEM::setSolutionForDOFs(const std::vector<Real> & stored_solution,
    2108             :                          const std::vector<dof_id_type> & stored_solution_dofs,
    2109             :                          NumericVector<Number> & current_solution,
    2110             :                          NumericVector<Number> & old_solution,
    2111             :                          NumericVector<Number> & older_solution)
    2112             : {
    2113             :   // Solution vector is stored first for current, then old and older solutions.
    2114             :   // These are the offsets to the beginning of the old and older solutions in the vector.
    2115             :   const auto old_solution_offset = stored_solution_dofs.size();
    2116       44555 :   const auto older_solution_offset = old_solution_offset * 2;
    2117             : 
    2118      239927 :   for (std::size_t i = 0; i < stored_solution_dofs.size(); ++i)
    2119             :   {
    2120      195372 :     current_solution.set(stored_solution_dofs[i], stored_solution[i]);
    2121      195372 :     if (_fe_problem->isTransient())
    2122             :     {
    2123      195270 :       old_solution.set(stored_solution_dofs[i], stored_solution[old_solution_offset + i]);
    2124      195270 :       older_solution.set(stored_solution_dofs[i], stored_solution[older_solution_offset + i]);
    2125             :     }
    2126             :   }
    2127       44555 : }
    2128             : 
    2129             : std::vector<dof_id_type>
    2130       26448 : XFEM::getElementSolutionDofs(const Elem * elem, SystemBase & sys) const
    2131             : {
    2132       26448 :   SubdomainID sid = elem->subdomain_id();
    2133             :   const std::vector<MooseVariableFEBase *> & vars = sys.getVariables(0);
    2134             :   std::vector<dof_id_type> solution_dofs;
    2135       26448 :   solution_dofs.reserve(vars.size()); // just an approximation
    2136      323896 :   for (auto var : vars)
    2137             :   {
    2138      297448 :     if (!var->isNodal())
    2139             :     {
    2140      268710 :       const std::set<SubdomainID> & var_subdomains = sys.getSubdomainsForVar(var->number());
    2141      268710 :       if (var_subdomains.empty() || var_subdomains.find(sid) != var_subdomains.end())
    2142             :       {
    2143      268710 :         unsigned int n_comp = elem->n_comp(sys.number(), var->number());
    2144      537756 :         for (unsigned int icomp = 0; icomp < n_comp; ++icomp)
    2145             :         {
    2146      269046 :           dof_id_type elem_dof = elem->dof_number(sys.number(), var->number(), icomp);
    2147      269046 :           solution_dofs.push_back(elem_dof);
    2148             :         }
    2149             :       }
    2150             :     }
    2151             :   }
    2152       26448 :   return solution_dofs;
    2153           0 : }
    2154             : 
    2155             : std::vector<dof_id_type>
    2156      140669 : XFEM::getNodeSolutionDofs(const Node * node, SystemBase & sys) const
    2157             : {
    2158      140669 :   const std::set<SubdomainID> & sids = _moose_mesh->getNodeBlockIds(*node);
    2159             :   const std::vector<MooseVariableFEBase *> & vars = sys.getVariables(0);
    2160             :   std::vector<dof_id_type> solution_dofs;
    2161      140669 :   solution_dofs.reserve(vars.size()); // just an approximation
    2162     1285621 :   for (auto var : vars)
    2163             :   {
    2164     1144952 :     if (var->isNodal())
    2165             :     {
    2166      181697 :       const std::set<SubdomainID> & var_subdomains = sys.getSubdomainsForVar(var->number());
    2167             :       std::set<SubdomainID> intersect;
    2168      181697 :       set_intersection(var_subdomains.begin(),
    2169             :                        var_subdomains.end(),
    2170             :                        sids.begin(),
    2171             :                        sids.end(),
    2172             :                        std::inserter(intersect, intersect.begin()));
    2173      181697 :       if (var_subdomains.empty() || !intersect.empty())
    2174             :       {
    2175      181697 :         unsigned int n_comp = node->n_comp(sys.number(), var->number());
    2176      363298 :         for (unsigned int icomp = 0; icomp < n_comp; ++icomp)
    2177             :         {
    2178      181601 :           dof_id_type node_dof = node->dof_number(sys.number(), var->number(), icomp);
    2179      181601 :           solution_dofs.push_back(node_dof);
    2180             :         }
    2181             :       }
    2182             :     }
    2183             :   }
    2184      140669 :   return solution_dofs;
    2185           0 : }
    2186             : 
    2187             : const GeometricCutUserObject *
    2188        8816 : XFEM::getGeometricCutForElem(const Elem * elem) const
    2189             : {
    2190        9488 :   for (auto gcmit : _geom_marker_id_elems)
    2191             :   {
    2192             :     std::set<unsigned int> elems = gcmit.second;
    2193        9434 :     if (elems.find(elem->id()) != elems.end())
    2194        8762 :       return _geometric_cuts[gcmit.first];
    2195             :   }
    2196          54 :   return nullptr;
    2197             : }
    2198             : 
    2199             : void
    2200        1282 : XFEM::storeMaterialPropertiesForElementHelper(const Elem * elem, MaterialPropertyStorage & storage)
    2201             : {
    2202        1642 :   for (const auto state : storage.statefulIndexRange())
    2203             :   {
    2204             :     const auto & elem_props = storage.props(state).at(elem);
    2205         360 :     auto & serialized_props = _geom_cut_elems[elem]._elem_material_properties[state - 1];
    2206             :     serialized_props.clear();
    2207         720 :     for (const auto & side_props_pair : elem_props)
    2208             :     {
    2209         360 :       const auto side = side_props_pair.first;
    2210         360 :       std::ostringstream oss;
    2211         360 :       dataStore(oss, storage.setProps(elem, side, state), nullptr);
    2212         360 :       serialized_props[side].assign(oss.str());
    2213         360 :     }
    2214             :   }
    2215        1282 : }
    2216             : 
    2217             : void
    2218        1282 : XFEM::storeMaterialPropertiesForElement(const Elem * parent_elem, const Elem * child_elem)
    2219             : {
    2220             :   // Set the parent element so that it is consistent post-healing
    2221        1282 :   _geom_cut_elems[child_elem]._parent_elem = parent_elem;
    2222             : 
    2223             :   // Locally store the element material properties
    2224        1282 :   storeMaterialPropertiesForElementHelper(child_elem,
    2225             :                                           _material_data[0]->getMaterialPropertyStorageForXFEM({}));
    2226             : 
    2227             :   // Locally store the boundary material properties
    2228             :   // First check if any of the side need material properties
    2229             :   bool need_boundary_materials = false;
    2230        6602 :   for (unsigned int side = 0; side < child_elem->n_sides(); ++side)
    2231             :   {
    2232             :     std::vector<boundary_id_type> elem_boundary_ids;
    2233        5320 :     _mesh->get_boundary_info().boundary_ids(child_elem, side, elem_boundary_ids);
    2234        6404 :     for (auto bdid : elem_boundary_ids)
    2235        1084 :       if (_fe_problem->needBoundaryMaterialOnSide(bdid, 0))
    2236             :         need_boundary_materials = true;
    2237        5320 :   }
    2238             : 
    2239             :   // If boundary material properties are needed for this element, then store them.
    2240        1282 :   if (need_boundary_materials)
    2241           0 :     storeMaterialPropertiesForElementHelper(
    2242             :         child_elem, _bnd_material_data[0]->getMaterialPropertyStorageForXFEM({}));
    2243        1282 : }
    2244             : 
    2245             : void
    2246        1447 : XFEM::loadMaterialPropertiesForElementHelper(const Elem * elem,
    2247             :                                              const Xfem::CachedMaterialProperties & cached_props,
    2248             :                                              MaterialPropertyStorage & storage) const
    2249             : {
    2250        1447 :   if (!storage.hasStatefulProperties())
    2251             :     return;
    2252             : 
    2253         840 :   for (const auto state : storage.statefulIndexRange())
    2254             :   {
    2255         420 :     const auto & serialized_props = cached_props[state - 1];
    2256         840 :     for (const auto & [side, serialized_side_props] : serialized_props)
    2257             :     {
    2258         420 :       std::istringstream iss;
    2259             :       iss.str(serialized_side_props);
    2260         420 :       iss.clear();
    2261             : 
    2262             :       // This is very dirty. We should not write to MOOSE's stateful properties.
    2263             :       // Please remove me :(
    2264         420 :       dataLoad(iss, storage.setProps(elem, side, state), nullptr);
    2265         420 :     }
    2266             :   }
    2267             : }
    2268             : 
    2269             : void
    2270        1447 : XFEM::loadMaterialPropertiesForElement(
    2271             :     const Elem * elem,
    2272             :     const Elem * elem_from,
    2273             :     std::unordered_map<const Elem *, Xfem::CutElemInfo> & cached_cei) const
    2274             : {
    2275             :   // Restore the element material properties
    2276             :   mooseAssert(cached_cei.count(elem_from) > 0, "XFEM: Unable to find cached material properties.");
    2277             :   Xfem::CutElemInfo & cei = cached_cei[elem_from];
    2278             : 
    2279             :   // Load element material properties from cached properties
    2280        1447 :   loadMaterialPropertiesForElementHelper(elem,
    2281        1447 :                                          cei._elem_material_properties,
    2282             :                                          _material_data[0]->getMaterialPropertyStorageForXFEM({}));
    2283             : 
    2284             :   // Check if any of the element side need material properties
    2285             :   bool need_boundary_materials = false;
    2286        7487 :   for (unsigned int side = 0; side < elem->n_sides(); ++side)
    2287             :   {
    2288             :     std::vector<boundary_id_type> elem_boundary_ids;
    2289        6040 :     _mesh->get_boundary_info().boundary_ids(elem, side, elem_boundary_ids);
    2290        7358 :     for (auto bdid : elem_boundary_ids)
    2291        1318 :       if (_fe_problem->needBoundaryMaterialOnSide(bdid, 0))
    2292             :         need_boundary_materials = true;
    2293        6040 :   }
    2294             : 
    2295             :   // Load boundary material properties from cached properties
    2296        1447 :   if (need_boundary_materials)
    2297           0 :     loadMaterialPropertiesForElementHelper(
    2298             :         elem,
    2299           0 :         cei._bnd_material_properties,
    2300             :         _bnd_material_data[0]->getMaterialPropertyStorageForXFEM({}));
    2301        1447 : }
    2302             : 
    2303             : CutSubdomainID
    2304        7764 : XFEM::getCutSubdomainID(const GeometricCutUserObject * gcuo,
    2305             :                         const Elem * cut_elem,
    2306             :                         const Elem * parent_elem) const
    2307             : {
    2308        7764 :   if (!parent_elem)
    2309             :     parent_elem = cut_elem;
    2310             :   // Pick any node from the parent element that is inside the physical domain and return its
    2311             :   // CutSubdomainID.
    2312        7764 :   const Node * node = pickFirstPhysicalNode(cut_elem, parent_elem);
    2313        7764 :   return gcuo->getCutSubdomainID(node);
    2314             : }
    2315             : 
    2316             : const Node *
    2317        7764 : XFEM::pickFirstPhysicalNode(const Elem * e, const Elem * e0) const
    2318             : {
    2319       10272 :   for (auto i : e0->node_index_range())
    2320       10272 :     if (isPointInsidePhysicalDomain(e, e0->node_ref(i)))
    2321             :       return e0->node_ptr(i);
    2322           0 :   mooseError("cannot find a physical node in the current element");
    2323             :   return nullptr;
    2324             : }

Generated by: LCOV version 1.14