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