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