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