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