https://mooseframework.inl.gov
Loading...
Searching...
No Matches
CrackFrontDefinition.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// MOOSE includes
13#include "MooseError.h"
14#include "MooseMesh.h"
15#include "MooseVariable.h"
16#include "RankTwoTensor.h"
17
18#include "libmesh/mesh_tools.h"
19#include "libmesh/string_to_enum.h"
20#include "libmesh/quadrature.h"
21
23
26{
28 params.addClassDescription("Used to describe geometric characteristics of the crack front for "
29 "fracture integral calculations");
32 params.set<bool>("use_displaced_mesh") = false;
33
34 params.addRelationshipManager("ElementSideNeighborLayers",
35 Moose::RelationshipManagerType::ALGEBRAIC,
36 [](const InputParameters &, InputParameters & rm_params)
37 { rm_params.set<unsigned short>("layers") = 2; });
38 return params;
39}
40
41void
43{
44 MooseEnum direction_method("CrackDirectionVector CrackMouth CurvedCrackFront");
45 MooseEnum end_direction_method("NoSpecialTreatment CrackDirectionVector CrackTangentVector",
46 "NoSpecialTreatment");
47 params.addParam<std::vector<Point>>("crack_front_points", "Set of points to define crack front");
48 params.addParam<bool>("closed_loop", false, "Set of points forms forms a closed loop");
50 "crack_direction_method",
51 direction_method,
52 "Method to determine direction of crack propagation. Choices are: " +
53 direction_method.getRawNames());
54 params.addParam<MooseEnum>(
55 "crack_end_direction_method",
56 end_direction_method,
57 "Method to determine direction of crack propagation at ends of crack. Choices are: " +
58 end_direction_method.getRawNames());
59 params.addParam<RealVectorValue>("crack_direction_vector", "Direction of crack propagation");
60 params.addParam<RealVectorValue>(
61 "crack_direction_vector_end_1",
62 "Direction of crack propagation for the node at end 1 of the crack");
63 params.addParam<RealVectorValue>(
64 "crack_direction_vector_end_2",
65 "Direction of crack propagation for the node at end 2 of the crack");
66 params.addParam<RealVectorValue>("crack_tangent_vector_end_1",
67 "Direction of crack tangent for the node at end 1 of the crack");
68 params.addParam<RealVectorValue>("crack_tangent_vector_end_2",
69 "Direction of crack tangent for the node at end 2 of the crack");
70 params.addParam<std::vector<BoundaryName>>(
71 "crack_mouth_boundary", "Boundaries whose average coordinate defines the crack mouth");
72 params.addParam<std::vector<BoundaryName>>("intersecting_boundary",
73 "Boundaries intersected by ends of crack");
74 params.addParam<bool>("2d", false, "Treat body as two-dimensional");
75 params.addRangeCheckedParam<unsigned int>(
76 "axis_2d",
77 2,
78 "axis_2d>=0 & axis_2d<=2",
79 "Out of plane axis for models treated as two-dimensional (0=x, 1=y, 2=z)");
80 params.addParam<unsigned int>("symmetry_plane",
81 "Account for a symmetry plane passing through "
82 "the plane of the crack, normal to the specified "
83 "axis (0=x, 1=y, 2=z)");
84 params.addParam<bool>("t_stress", false, "Calculate T-stress");
85 params.addParam<bool>("q_function_rings", false, "Generate rings of nodes for q-function");
86 params.addParam<unsigned int>("last_ring", "The number of rings of nodes to generate");
87 params.addParam<unsigned int>("first_ring", "The number of rings of nodes to generate");
88 params.addParam<unsigned int>("nrings", "The number of rings of nodes to generate");
89 params.addParam<VariableName>("disp_x", "Variable containing the x displacement");
90 params.addParam<VariableName>("disp_y", "Variable containing the y displacement");
91 params.addParam<VariableName>("disp_z", "Variable containing the z displacement");
92 params.addParam<std::vector<Real>>(
93 "j_integral_radius_inner", {}, "Radius for J-Integral calculation");
94 params.addParam<std::vector<Real>>(
95 "j_integral_radius_outer", {}, "Radius for J-Integral calculation");
96 MooseEnum q_function_type("Geometry Topology", "Geometry");
97 params.addParam<MooseEnum>("q_function_type",
98 q_function_type,
99 "The method used to define the integration domain. Options are: " +
100 q_function_type.getRawNames());
101 params.addParam<UserObjectName>(
102 "crack_front_points_provider",
103 "The UserObject provides the crack front points from XFEM GeometricCutObject");
104
105 params.addParam<unsigned int>("number_points_from_provider",
106 "The number of crack front points, only needed for "
107 "crack_front_points_provider that do not use a cut mesh.");
108 params.addParam<Real>(
109 "crack_front_node_tolerance",
110 1e-10,
111 "General tolerance for locating nodes on the crack front based on xyz coordinates.");
112}
113
115 : GeneralUserObject(parameters),
116 BoundaryRestrictable(this, true), // false means nodesets
117 _direction_method(getParam<MooseEnum>("crack_direction_method").getEnum<DIRECTION_METHOD>()),
118 _end_direction_method(
119 getParam<MooseEnum>("crack_end_direction_method").getEnum<END_DIRECTION_METHOD>()),
120 _aux(_fe_problem.getAuxiliarySystem()),
121 _mesh(_subproblem.mesh()),
122 _treat_as_2d(getParam<bool>("2d")),
123 _use_mesh_cutter(false),
124 _is_cutter_modified(false),
125 _closed_loop(getParam<bool>("closed_loop")),
126 _axis_2d(getParam<unsigned int>("axis_2d")),
127 _has_symmetry_plane(isParamValid("symmetry_plane")),
128 _symmetry_plane(_has_symmetry_plane ? getParam<unsigned int>("symmetry_plane")
129 : std::numeric_limits<unsigned int>::max()),
130 _t_stress(getParam<bool>("t_stress")),
131 _q_function_rings(getParam<bool>("q_function_rings")),
132 _q_function_type(getParam<MooseEnum>("q_function_type")),
133 _crack_front_points_provider(nullptr),
134 _tol(getParam<Real>("crack_front_node_tolerance"))
135{
136 auto boundary = isParamValid("boundary") ? getParam<std::vector<BoundaryName>>("boundary")
137 : std::vector<BoundaryName>{};
138 if (isParamValid("crack_front_points"))
139 {
140 if (boundary.size())
141 paramError("crack_front_points",
142 "CrackFrontDefinition error: since boundary is defined, crack_front_points should "
143 "not be added.");
144 if (isParamValid("crack_front_points_provider"))
145 paramError("crack_front_points_provider",
146 "As crack_front_points have been provided, the crack_front_points_provider will "
147 "not be used and needs to be removed.");
148 _crack_front_points = getParam<std::vector<Point>>("crack_front_points");
150 if (_t_stress)
151 paramError("t_stress", "t_stress not yet supported with crack_front_points");
153 paramError("q_function_rings", "q_function_rings not supported with crack_front_points");
154 }
155 else if (isParamValid("crack_front_points_provider"))
156 {
157 if (boundary.size())
158 paramError("crack_front_points_provider",
159 "CrackFrontDefinition error: since boundary is defined, "
160 "crack_front_points_provider should not be added.");
161
163 }
164 else if (isParamValid("number_points_from_provider"))
165 paramError("number_points_from_provider",
166 "CrackFrontDefinition error: number_points_from_provider is provided but "
167 "crack_front_points_provider cannot be found.");
168 else if (boundary.size())
169 {
171 if (parameters.isParamSetByUser("closed_loop"))
172 paramError("closed_loop",
173 "In CrackFrontDefinition, if 'boundary' is defined, 'closed_loop' should not be "
174 "set by user because closed loops are detected automatically");
175 }
176 else
177 mooseError("In CrackFrontDefinition, must define one of 'boundary', 'crack_front_points' "
178 "and 'crack_front_points_provider'");
179
180 if (isParamValid("crack_mouth_boundary"))
181 _crack_mouth_boundary_names = getParam<std::vector<BoundaryName>>("crack_mouth_boundary");
182
184 if (_symmetry_plane > 2)
185 paramError("symmetry_plane",
186 "symmetry_plane out of bounds: ",
188 " Must be >=0 and <=2.");
189
190 switch (_direction_method)
191 {
193 if (!isParamValid("crack_direction_vector"))
194 paramError("crack_direction_vector",
195 "crack_direction_vector must be specified if crack_direction_method = "
196 "CrackDirectionVector");
197 _crack_direction_vector = getParam<RealVectorValue>("crack_direction_vector");
198 break;
200 if (isParamValid("crack_direction_vector"))
201 paramError("crack_direction_vector",
202 "crack_direction_vector must not be specified if crack_direction_method = "
203 "CrackMouthNodes");
204 if (_crack_mouth_boundary_names.size() == 0)
206 "crack_mouth_boundary",
207 "crack_mouth_boundary must be specified if crack_direction_method = CrackMouthNodes");
208 break;
210 if (isParamValid("crack_direction_vector"))
211 paramError("crack_direction_vector",
212 "crack_direction_vector must not be specified if crack_direction_method = "
213 "CurvedCrackFront");
214 break;
215 default:
216 paramError("crack_direction_method", "Invalid direction_method");
217 }
218
219 if (isParamValid("intersecting_boundary"))
220 _intersecting_boundary_names = getParam<std::vector<BoundaryName>>("intersecting_boundary");
221
223 {
224 if (!isParamValid("crack_direction_vector_end_1"))
225 paramError("crack_direction_vector_end_1",
226 "crack_direction_vector_end_1 must be specified if crack_end_direction_method = "
227 "CrackDirectionVector");
228 if (!isParamValid("crack_direction_vector_end_2"))
229 paramError("crack_direction_vector_end_2",
230 "crack_direction_vector_end_2 must be specified if crack_end_direction_method = "
231 "CrackDirectionVector");
232 _crack_direction_vector_end_1 = getParam<RealVectorValue>("crack_direction_vector_end_1");
233 _crack_direction_vector_end_2 = getParam<RealVectorValue>("crack_direction_vector_end_2");
234 }
235
237 {
238 if (!isParamValid("crack_tangent_vector_end_1"))
239 paramError("crack_tangent_vector_end_1",
240 "crack_tangent_vector_end_1 must be specified if crack_end_tangent_method = "
241 "CrackTangentVector");
242 if (!isParamValid("crack_tangent_vector_end_2"))
243 paramError("crack_tangent_vector_end_2",
244 "crack_tangent_vector_end_2 must be specified if crack_end_tangent_method = "
245 "CrackTangentVector");
246 _crack_tangent_vector_end_1 = getParam<RealVectorValue>("crack_tangent_vector_end_1");
247 _crack_tangent_vector_end_2 = getParam<RealVectorValue>("crack_tangent_vector_end_2");
248 }
249
250 if (isParamValid("disp_x") && isParamValid("disp_y") && isParamValid("disp_z"))
251 {
252 _disp_x_var_name = getParam<VariableName>("disp_x");
253 _disp_y_var_name = getParam<VariableName>("disp_y");
254 _disp_z_var_name = getParam<VariableName>("disp_z");
255 }
256 else if (_t_stress == true && _treat_as_2d == false)
257 paramError("displacements", "Displacement variables must be provided for T-stress calculation");
258
260 {
261 if (!isParamValid("last_ring"))
262 paramError("last_ring",
263 "The max number of rings of nodes to generate must be provided if "
264 "q_function_rings = true");
265 _last_ring = getParam<unsigned int>("last_ring");
266 _first_ring = getParam<unsigned int>("first_ring");
267 }
268 else
269 {
270 _j_integral_radius_inner = getParam<std::vector<Real>>("j_integral_radius_inner");
271 _j_integral_radius_outer = getParam<std::vector<Real>>("j_integral_radius_outer");
272 }
273}
274
275void
277{
278 // Because J-Integral is based on original geometry, the crack front geometry
279 // is never updated, so everything that needs to happen is done in initialSetup()
280 if (_t_stress == true && _treat_as_2d == false)
282}
283
284void
286{
287 if (isParamValid("crack_front_points_provider"))
288 {
289 _crack_front_points_provider = &getUserObjectByName<CrackFrontPointsProvider>(
290 getParam<UserObjectName>("crack_front_points_provider"));
292 {
293 _use_mesh_cutter = true;
294
295 // Automatically get number of crack front points from mesh-based provider
297 mooseInfo("CrackFrontDefinition: Automatically detected ",
299 " crack front points from mesh-based provider");
300
302 paramError("crack_direction_method",
303 "Using a `crack_front_points_provider` that uses an XFEM cutter mesh also "
304 "requires setting 'crack_direction_method = CurvedCrackFront'");
305 if (isParamValid("crack_mouth_boundary"))
306 paramError("crack_mouth_boundary",
307 "'crack_mouth_boundary' cannot be set when using a "
308 "'crack_front_points_provider' that uses an XFEM cutter mesh");
309 }
310 else if (isParamValid("number_points_from_provider"))
311 {
312 _num_points_from_provider = getParam<unsigned int>("number_points_from_provider");
313 }
314 else if (_num_points_from_provider == 0)
315 {
317 "number_points_from_provider",
318 "CrackFrontDefinition error: When using a non-mesh-based crack_front_points_provider, "
319 "the number_points_from_provider parameter must be provided.");
320 }
321 }
322 if (_crack_front_points_provider != nullptr)
323 {
326 }
327
330
332 {
333 std::set<dof_id_type> nodes;
334 getCrackFrontNodes(nodes);
336 }
337
339 paramError("intersecting_boundary", "Cannot use intersecting_boundary with closed-loop cracks");
340
342
345
346 if (_t_stress)
347 {
348 std::size_t num_crack_front_nodes = _ordered_crack_front_nodes.size();
349 for (std::size_t i = 0; i < num_crack_front_nodes; ++i)
350 _strain_along_front.push_back(-std::numeric_limits<Real>::max());
351 }
352
353 std::size_t num_crack_front_points = getNumCrackFrontPoints();
354 if (_q_function_type == "GEOMETRY")
355 {
356 if (!_treat_as_2d)
357 if (num_crack_front_points < 1)
358 mooseError("num_crack_front_points is not > 0");
359 for (std::size_t i = 0; i < num_crack_front_points; ++i)
360 {
361 bool is_point_on_intersecting_boundary = isPointWithIndexOnIntersectingBoundary(i);
362 _is_point_on_intersecting_boundary.push_back(is_point_on_intersecting_boundary);
363 }
364 }
365}
366
367void
369{
370 // Update the crack front for fracture integral calculations
371 // This is only useful for growing cracks which are currently described by the mesh
372 // cutter
374 {
375 // Automatically update the number of crack front points as crack grows
377
381 std::size_t num_crack_front_points = getNumCrackFrontPoints();
382 if (_q_function_type == "GEOMETRY")
383 for (std::size_t i = 0; i < num_crack_front_points; ++i)
384 {
385 bool is_point_on_intersecting_boundary = isPointWithIndexOnIntersectingBoundary(i);
386 _is_point_on_intersecting_boundary.push_back(is_point_on_intersecting_boundary);
387 }
388 }
389}
390
391void
397
398void
399CrackFrontDefinition::getCrackFrontNodes(std::set<dof_id_type> & nodes)
400{
402 for (auto nd = bnd_nodes.begin(); nd != bnd_nodes.end(); ++nd)
403 {
404 const BndNode * bnode = *nd;
405 BoundaryID boundary_id = bnode->_bnd_id;
406
407 if (hasBoundary(boundary_id))
408 nodes.insert(bnode->_node->id());
409 }
410
411 if (_treat_as_2d && _use_mesh_cutter == false)
412 {
413 if (nodes.size() > 1)
414 {
415 // Check that the nodes are collinear in the axis normal to the 2d plane
416 unsigned int axis0;
417 unsigned int axis1;
418
419 switch (_axis_2d)
420 {
421 case 0:
422 axis0 = 1;
423 axis1 = 2;
424 break;
425 case 1:
426 axis0 = 0;
427 axis1 = 2;
428 break;
429 case 2:
430 axis0 = 0;
431 axis1 = 1;
432 break;
433 default:
434 mooseError("Invalid axis.");
435 }
436
437 Real node0coor0 = 0;
438 Real node0coor1 = 0;
439
440 for (auto sit = nodes.begin(); sit != nodes.end(); ++sit)
441 {
442 Node & curr_node = _mesh.nodeRef(*sit);
443 if (sit == nodes.begin())
444 {
445 node0coor0 = curr_node(axis0);
446 node0coor1 = curr_node(axis1);
447 }
448 else
449 {
450 if (!MooseUtils::absoluteFuzzyEqual(curr_node(axis0), node0coor0, _tol) ||
451 !MooseUtils::absoluteFuzzyEqual(curr_node(axis1), node0coor1, _tol))
452 mooseError("Boundary provided in CrackFrontDefinition contains ",
453 nodes.size(),
454 " nodes, which are not collinear in the ",
455 _axis_2d,
456 " axis. Must contain either 1 node or collinear nodes to treat as 2D.");
457 }
458 }
459 }
460 }
461}
462
463void
464CrackFrontDefinition::orderCrackFrontNodes(std::set<dof_id_type> & nodes)
465{
467 if (nodes.size() < 1)
468 mooseError("No crack front nodes");
469 else if (nodes.size() == 1)
470 {
471 _ordered_crack_front_nodes.push_back(*nodes.begin());
472 if (!_treat_as_2d)
473 mooseError("Boundary provided in CrackFrontDefinition contains 1 node, but model is not "
474 "treated as 2d");
475 }
476 else if (_treat_as_2d && _use_mesh_cutter)
477 {
478 // This is for the 2D case that uses a mesh cutter object so every node is its own crack front
479 // and is not connected to other crack front nodes. Copying the order here makes it the same
480 // as that given in the MeshCut2DFractureUserObject
481 std::copy(nodes.begin(), nodes.end(), _ordered_crack_front_nodes.begin());
482 }
483 else
484 {
485 // Loop through the set of crack front nodes, and create a node to element map for just the
486 // crack front nodes
487 // The main reason for creating a second map is that we need to do a sort prior to the
488 // set_intersection.
489 // The original map contains vectors, and we can't sort them, so we create sets in the local
490 // map.
491 const auto & node_to_elem_map = _mesh.nodeToElemMap();
492 std::map<dof_id_type, std::set<dof_id_type>> crack_front_node_to_elem_map;
493
494 for (const auto & node_id : nodes)
495 {
496 const auto & node_to_elem_pair = node_to_elem_map.find(node_id);
497 mooseAssert(node_to_elem_pair != node_to_elem_map.end(),
498 "Could not find crack front node " << node_id << " in the node to elem map");
499
500 const std::vector<dof_id_type> & connected_elems = node_to_elem_pair->second;
501 for (std::size_t i = 0; i < connected_elems.size(); ++i)
502 crack_front_node_to_elem_map[node_id].insert(connected_elems[i]);
503 }
504
505 // Determine which nodes are connected to each other via elements, and construct line elements
506 // to represent
507 // those connections
508 std::vector<std::vector<dof_id_type>> line_elems;
509 std::map<dof_id_type, std::vector<dof_id_type>> node_to_line_elem_map;
510
511 for (auto cfnemit = crack_front_node_to_elem_map.begin();
512 cfnemit != crack_front_node_to_elem_map.end();
513 ++cfnemit)
514 {
515 auto cfnemit2 = cfnemit;
516 for (++cfnemit2; cfnemit2 != crack_front_node_to_elem_map.end(); ++cfnemit2)
517 {
518
519 std::vector<dof_id_type> common_elements;
520 std::set<dof_id_type> & elements_connected_to_node1 = cfnemit->second;
521 std::set<dof_id_type> & elements_connected_to_node2 = cfnemit2->second;
522 std::set_intersection(elements_connected_to_node1.begin(),
523 elements_connected_to_node1.end(),
524 elements_connected_to_node2.begin(),
525 elements_connected_to_node2.end(),
526 std::inserter(common_elements, common_elements.end()));
527
528 if (common_elements.size() > 0)
529 {
530 std::vector<dof_id_type> my_line_elem;
531 my_line_elem.push_back(cfnemit->first);
532 my_line_elem.push_back(cfnemit2->first);
533 node_to_line_elem_map[cfnemit->first].push_back(line_elems.size());
534 node_to_line_elem_map[cfnemit2->first].push_back(line_elems.size());
535 line_elems.push_back(my_line_elem);
536 }
537 }
538 }
539
540 // Find nodes on ends of line (those connected to only one line element)
541 std::vector<dof_id_type> end_nodes;
542 for (auto nlemit = node_to_line_elem_map.begin(); nlemit != node_to_line_elem_map.end();
543 ++nlemit)
544 {
545 std::size_t num_connected_elems = nlemit->second.size();
546 if (num_connected_elems == 1)
547 end_nodes.push_back(nlemit->first);
548 else if (num_connected_elems != 2)
550 "Node ", nlemit->first, " is connected to >2 line segments in CrackFrontDefinition");
551 }
552
553 // For embedded crack with closed loop of crack front nodes, must pick the end nodes
554 if (end_nodes.size() == 0) // Crack front is a loop. Pick nodes to be end nodes.
555 {
556 pickLoopCrackEndNodes(end_nodes, nodes, node_to_line_elem_map, line_elems);
557 _closed_loop = true;
560 paramError("end_direction_method",
561 "In CrackFrontDefinition, end_direction_method cannot be CrackDirectionVector "
562 "or CrackTangentVector for a closed-loop crack");
563 if (_intersecting_boundary_names.size() > 0)
564 paramError("intersecting_boundary",
565 "In CrackFrontDefinition, intersecting_boundary cannot be specified for a "
566 "closed-loop crack");
567 }
568 else if (end_nodes.size() == 2) // Rearrange the order of the end nodes if needed
569 orderEndNodes(end_nodes);
570 else
571 mooseError("In CrackFrontDefinition wrong number of end nodes. Number end nodes = ",
572 end_nodes.size());
573
574 // Create an ordered list of the nodes going along the line of the crack front
575 _ordered_crack_front_nodes.push_back(end_nodes[0]);
576
577 dof_id_type last_node = end_nodes[0];
578 dof_id_type second_last_node = last_node;
579 while (last_node != end_nodes[1])
580 {
581 std::vector<dof_id_type> & curr_node_line_elems = node_to_line_elem_map[last_node];
582 bool found_new_node = false;
583 for (std::size_t i = 0; i < curr_node_line_elems.size(); ++i)
584 {
585 std::vector<dof_id_type> curr_line_elem = line_elems[curr_node_line_elems[i]];
586 for (std::size_t j = 0; j < curr_line_elem.size(); ++j)
587 {
588 dof_id_type line_elem_node = curr_line_elem[j];
589 if (_closed_loop &&
590 (last_node == end_nodes[0] &&
591 line_elem_node == end_nodes[1])) // wrong direction around closed loop
592 continue;
593 if (line_elem_node != last_node && line_elem_node != second_last_node)
594 {
595 _ordered_crack_front_nodes.push_back(line_elem_node);
596 found_new_node = true;
597 break;
598 }
599 }
600 if (found_new_node)
601 break;
602 }
603 second_last_node = last_node;
604 last_node = _ordered_crack_front_nodes.back();
605 }
606 }
607}
608
609void
610CrackFrontDefinition::orderEndNodes(std::vector<dof_id_type> & end_nodes)
611{
612 // Choose the node to be the first node. Do that based on undeformed coordinates for
613 // repeatability.
614 Node & node0 = _mesh.nodeRef(end_nodes[0]);
615 Node & node1 = _mesh.nodeRef(end_nodes[1]);
616
617 std::size_t num_positive_coor0 = 0;
618 std::size_t num_positive_coor1 = 0;
619 Real dist_from_origin0 = 0.0;
620 Real dist_from_origin1 = 0.0;
621 for (std::size_t i = 0; i < 3; ++i)
622 {
623 dist_from_origin0 += node0(i) * node0(i);
624 dist_from_origin1 += node1(i) * node1(i);
625 if (MooseUtils::absoluteFuzzyGreaterThan(node0(i), 0.0, _tol))
626 ++num_positive_coor0;
627 if (MooseUtils::absoluteFuzzyGreaterThan(node1(i), 0.0, _tol))
628 ++num_positive_coor1;
629 }
630 dist_from_origin0 = std::sqrt(dist_from_origin0);
631 dist_from_origin1 = std::sqrt(dist_from_origin1);
632
633 bool switch_ends = false;
634 if (num_positive_coor1 > num_positive_coor0)
635 {
636 switch_ends = true;
637 }
638 else
639 {
640 if (!MooseUtils::absoluteFuzzyEqual(dist_from_origin1, dist_from_origin0, _tol))
641 {
642 if (dist_from_origin1 < dist_from_origin0)
643 switch_ends = true;
644 }
645 else
646 {
647 if (end_nodes[1] < end_nodes[0])
648 switch_ends = true;
649 }
650 }
651 if (switch_ends)
652 {
653 std::size_t tmp_node = end_nodes[1];
654 end_nodes[1] = end_nodes[0];
655 end_nodes[0] = tmp_node;
656 }
657}
658
659void
661 std::vector<dof_id_type> & end_nodes,
662 std::set<dof_id_type> & nodes,
663 std::map<dof_id_type, std::vector<dof_id_type>> & node_to_line_elem_map,
664 std::vector<std::vector<dof_id_type>> & line_elems)
665{
666 dof_id_type max_dist_node = 0;
667 Real min_dist = std::numeric_limits<Real>::max();
668 Real max_dist = -std::numeric_limits<Real>::max();
669 // Pick the node farthest from the origin as the end node, or the one with
670 // the greatest x coordinate if the nodes are equidistant from the origin
671 for (auto nit = nodes.begin(); nit != nodes.end(); ++nit)
672 {
673 Node & node = _mesh.nodeRef(*nit);
674 Real dist = node.norm();
675 if (dist > max_dist)
676 {
677 max_dist = dist;
678 max_dist_node = *nit;
679 }
680 else if (dist < min_dist)
681 min_dist = dist;
682 }
683
684 dof_id_type end_node;
685 if (MooseUtils::absoluteFuzzyGreaterThan(max_dist, min_dist, _tol))
686 end_node = max_dist_node;
687 else
688 {
689 std::vector<Node *> node_vec;
690 for (auto nit = nodes.begin(); nit != nodes.end(); ++nit)
691 node_vec.push_back(_mesh.nodePtr(*nit));
692 end_node = maxNodeCoor(node_vec);
693 }
694
695 end_nodes.push_back(end_node);
696
697 // Find the two nodes connected to the node identified as the end node, and pick one of those to
698 // be the other end node
699 auto end_node_line_elems = node_to_line_elem_map[end_node];
700 if (end_node_line_elems.size() != 2)
702 "Crack front nodes are in a loop, but crack end node is only connected to one other node");
703 std::vector<Node *> candidate_other_end_nodes;
704
705 for (std::size_t i = 0; i < 2; ++i)
706 {
707 auto end_line_elem = line_elems[end_node_line_elems[i]];
708 for (std::size_t j = 0; j < end_line_elem.size(); ++j)
709 {
710 auto line_elem_node = end_line_elem[j];
711 if (line_elem_node != end_node)
712 candidate_other_end_nodes.push_back(_mesh.nodePtr(line_elem_node));
713 }
714 }
715 if (candidate_other_end_nodes.size() != 2)
717 "Crack front nodes are in a loop, but crack end node is not connected to two other nodes");
718 end_nodes.push_back(maxNodeCoor(candidate_other_end_nodes, 1));
719}
720
721dof_id_type
722CrackFrontDefinition::maxNodeCoor(std::vector<Node *> & nodes, unsigned int dir0)
723{
724 Real dirs[3];
725 if (dir0 == 0)
726 {
727 dirs[0] = 0;
728 dirs[1] = 1;
729 dirs[2] = 2;
730 }
731 else if (dir0 == 1)
732 {
733 dirs[0] = 1;
734 dirs[1] = 2;
735 dirs[2] = 0;
736 }
737 else if (dir0 == 2)
738 {
739 dirs[0] = 2;
740 dirs[1] = 0;
741 dirs[2] = 1;
742 }
743 else
744 mooseError("Invalid dir0 in CrackFrontDefinition::maxNodeCoor()");
745
746 Real max_coor0 = -std::numeric_limits<Real>::max();
747 std::vector<Node *> max_coor0_nodes;
748 for (std::size_t i = 0; i < nodes.size(); ++i)
749 {
750 Real coor0 = (*nodes[i])(dirs[0]);
751 if (coor0 > max_coor0)
752 max_coor0 = coor0;
753 }
754 for (std::size_t i = 0; i < nodes.size(); ++i)
755 {
756 Real coor0 = (*nodes[i])(dirs[0]);
757 if (MooseUtils::absoluteFuzzyEqual(coor0, max_coor0, _tol))
758 max_coor0_nodes.push_back(nodes[i]);
759 }
760 if (max_coor0_nodes.size() > 1)
761 {
762 Real max_coor1 = -std::numeric_limits<Real>::max();
763 std::vector<Node *> max_coor1_nodes;
764 for (std::size_t i = 0; i < nodes.size(); ++i)
765 {
766 Real coor1 = (*nodes[i])(dirs[1]);
767 if (coor1 > max_coor1)
768 max_coor1 = coor1;
769 }
770 for (std::size_t i = 0; i < nodes.size(); ++i)
771 {
772 Real coor1 = (*nodes[i])(dirs[1]);
773 if (MooseUtils::absoluteFuzzyEqual(coor1, max_coor1, _tol))
774 max_coor1_nodes.push_back(nodes[i]);
775 }
776 if (max_coor1_nodes.size() > 1)
777 {
778 Real max_coor2 = -std::numeric_limits<Real>::max();
779 std::vector<Node *> max_coor2_nodes;
780 for (std::size_t i = 0; i < nodes.size(); ++i)
781 {
782 Real coor2 = (*nodes[i])(dirs[2]);
783 if (coor2 > max_coor2)
784 max_coor2 = coor2;
785 }
786 for (std::size_t i = 0; i < nodes.size(); ++i)
787 {
788 Real coor2 = (*nodes[i])(dirs[2]);
789 if (MooseUtils::absoluteFuzzyEqual(coor2, max_coor2, _tol))
790 max_coor2_nodes.push_back(nodes[i]);
791 }
792 if (max_coor2_nodes.size() > 1)
793 mooseError("Multiple nodes with same x,y,z coordinates within tolerance");
794 else
795 return max_coor2_nodes[0]->id();
796 }
797 else
798 return max_coor1_nodes[0]->id();
799 }
800 else
801 return max_coor0_nodes[0]->id();
802}
803
804void
806{
808 _segment_lengths.clear();
809 _tangent_directions.clear();
810 _crack_directions.clear();
811 _overall_length = 0.0;
812 _rot_matrix.clear();
814 _angles_along_front.clear();
815 _strain_along_front.clear();
816 _crack_plane_normals.clear();
817
818 if (_treat_as_2d)
819 {
820 std::size_t num_crack_front_points = getNumCrackFrontPoints();
821 _segment_lengths.reserve(num_crack_front_points);
822 _tangent_directions.reserve(num_crack_front_points);
823 _crack_directions.reserve(num_crack_front_points);
826 _crack_front_points_provider->getCrackPlaneNormals(num_crack_front_points);
827
828 for (std::size_t i = 0; i < num_crack_front_points; ++i)
829 {
830 RealVectorValue tangent_direction;
831 RealVectorValue crack_direction;
832 tangent_direction(_axis_2d) = 1.0;
833 _tangent_directions.push_back(tangent_direction);
834 const Point * crack_front_point = getCrackFrontPoint(i);
835
836 crack_direction =
837 calculateCrackFrontDirection(*crack_front_point, tangent_direction, MIDDLE_NODE, i);
838 _crack_directions.push_back(crack_direction);
839
840 RankTwoTensor rot_mat;
841 rot_mat.fillRow(0, crack_direction);
842 rot_mat(2, _axis_2d) = 1.0;
843
844 if (!_use_mesh_cutter)
845 _crack_plane_normals.push_back(tangent_direction.cross(crack_direction));
846
847 mooseAssert(i <= _crack_plane_normals.size(), "_crack_plane_normals is the wrong size.");
848 rot_mat.fillRow(1, _crack_plane_normals[i]);
849 _rot_matrix.push_back(rot_mat);
850
851 _segment_lengths.push_back(std::make_pair(0.0, 0.0));
852 _distances_along_front.push_back(0.0);
853 _angles_along_front.push_back(0.0);
854 }
855 }
856 else
857 {
858 // filling crack_plane_normals from mesh_cutter
862 // else filling crack_plane_normals from curvedCrackFront
864
865 std::size_t num_crack_front_points = getNumCrackFrontPoints();
866 _segment_lengths.reserve(num_crack_front_points);
867 _tangent_directions.reserve(num_crack_front_points);
868 _crack_directions.reserve(num_crack_front_points);
869
870 RealVectorValue back_segment;
871 Real back_segment_len = 0.0;
872 if (_closed_loop)
873 {
874 back_segment = *getCrackFrontPoint(0) - *getCrackFrontPoint(num_crack_front_points - 1);
875 back_segment_len = back_segment.norm();
876 }
877
878 for (std::size_t i = 0; i < num_crack_front_points; ++i)
879 {
880 CRACK_NODE_TYPE ntype;
881 if (_closed_loop)
882 ntype = MIDDLE_NODE;
883 else if (i == 0)
884 ntype = END_1_NODE;
885 else if (i == num_crack_front_points - 1)
886 ntype = END_2_NODE;
887 else
888 ntype = MIDDLE_NODE;
889
890 RealVectorValue forward_segment;
891 Real forward_segment_len;
892 if (ntype == END_2_NODE)
893 forward_segment_len = 0.0;
894 else if (_closed_loop && i == num_crack_front_points - 1)
895 {
896 forward_segment = *getCrackFrontPoint(0) - *getCrackFrontPoint(i);
897 forward_segment_len = forward_segment.norm();
898 }
899 else
900 {
901 forward_segment = *getCrackFrontPoint(i + 1) - *getCrackFrontPoint(i);
902 forward_segment_len = forward_segment.norm();
903 _overall_length += forward_segment_len;
904 }
905
906 _segment_lengths.push_back(std::make_pair(back_segment_len, forward_segment_len));
907 if (i == 0)
908 _distances_along_front.push_back(0.0);
909 else
910 _distances_along_front.push_back(back_segment_len + _distances_along_front[i - 1]);
911
912 RealVectorValue tangent_direction = back_segment + forward_segment;
913 tangent_direction = tangent_direction / tangent_direction.norm();
914
915 // If end tangent directions are given, correct the tangent at the end nodes
918 {
919 if (ntype == END_1_NODE)
920 tangent_direction = _crack_tangent_vector_end_1;
921 else if (ntype == END_2_NODE)
922 tangent_direction = _crack_tangent_vector_end_2;
923 }
924
925 _tangent_directions.push_back(tangent_direction);
926 _crack_directions.push_back(
927 calculateCrackFrontDirection(*getCrackFrontPoint(i), tangent_direction, ntype, i));
928
929 // correct tangent direction in the case of _use_mesh_cutter
932
933 // If the end directions are given by the user, correct also the tangent at the end nodes
936 (ntype == END_1_NODE || ntype == END_2_NODE))
937 {
939 }
940
941 back_segment = forward_segment;
942 back_segment_len = forward_segment_len;
943 }
944
945 // For CURVED_CRACK_FRONT, _crack_plane_normals gets computed in
946 // computeCurvedCrackFrontCrackPlaneNormals
948 {
949 std::size_t mid_id = (num_crack_front_points - 1) / 2;
950 _crack_plane_normals.assign(num_crack_front_points,
951 _tangent_directions[mid_id].cross(_crack_directions[mid_id]));
952
953 // Make sure the normal vector is non-zero
954 RealVectorValue zero_vec(0.0);
955 if (_crack_plane_normals.front().absolute_fuzzy_equals(zero_vec, _tol))
956 mooseError("Crack plane normal vector evaluates to zero");
957 }
958
959 // Calculate angles of each point along the crack front for an elliptical crack projected
960 // to a circle.
961 if (hasAngleAlongFront())
962 {
963 RealVectorValue origin_to_first_node = *getCrackFrontPoint(0) - _crack_mouth_coordinates;
964 Real hyp = origin_to_first_node.norm();
965 RealVectorValue norm_origin_to_first_node = origin_to_first_node / hyp;
966 RealVectorValue tangent_to_first_node =
967 -norm_origin_to_first_node.cross(_crack_plane_normals.front());
968 tangent_to_first_node /= tangent_to_first_node.norm();
969
970 for (std::size_t i = 0; i < num_crack_front_points; ++i)
971 {
972 RealVectorValue origin_to_curr_node = *getCrackFrontPoint(i) - _crack_mouth_coordinates;
973
974 Real adj = origin_to_curr_node * norm_origin_to_first_node;
975 Real opp = origin_to_curr_node * tangent_to_first_node;
976
977 Real angle = acos(adj / hyp) * 180.0 / libMesh::pi;
978 if (opp < 0.0)
979 angle = 360.0 - angle;
980 _angles_along_front.push_back(angle);
981 }
982
983 // Correct angle on end nodes if they are 0 or 360 to be consistent with neighboring node
984 if (num_crack_front_points > 1)
985 {
986 if (MooseUtils::absoluteFuzzyEqual(_angles_along_front[0], 0.0, _tol) &&
987 _angles_along_front[1] > 180.0)
988 _angles_along_front[0] = 360.0;
989 else if (MooseUtils::absoluteFuzzyEqual(_angles_along_front[0], 360.0, _tol) &&
990 _angles_along_front[1] < 180.0)
991 _angles_along_front[0] = 0.0;
992
993 if (MooseUtils::absoluteFuzzyEqual(
994 _angles_along_front[num_crack_front_points - 1], 0.0, _tol) &&
995 _angles_along_front[num_crack_front_points - 2] > 180.0)
996 _angles_along_front[num_crack_front_points - 1] = 360.0;
997 else if (MooseUtils::absoluteFuzzyEqual(
998 _angles_along_front[num_crack_front_points - 1], 360.0, _tol) &&
999 _angles_along_front[num_crack_front_points - 2] < 180.0)
1000 _angles_along_front[num_crack_front_points - 1] = 0.0;
1001 }
1002 }
1003 else
1004 _angles_along_front.resize(num_crack_front_points, 0.0);
1005
1006 // Create rotation matrix
1007 for (std::size_t i = 0; i < num_crack_front_points; ++i)
1008 {
1009 RankTwoTensor rot_mat;
1010 rot_mat.fillRow(0, _crack_directions[i]);
1011 rot_mat.fillRow(1, _crack_plane_normals[i]);
1012 rot_mat.fillRow(2, _tangent_directions[i]);
1013 _rot_matrix.push_back(rot_mat);
1014 }
1015
1016 _console << "Summary of crack front geometry (used for fracture integrals):" << std::endl;
1017 _console << "index node id x coord y coord z coord x dir y dir "
1018 " z dir angle position seg length"
1019 << std::endl;
1020 for (std::size_t i = 0; i < num_crack_front_points; ++i)
1021 {
1022 std::size_t point_id;
1024 point_id = _ordered_crack_front_nodes[i];
1025 else
1026 point_id = i;
1027 _console << std::left << std::setw(8) << i + 1 << std::setw(10) << point_id << std::setw(14)
1028 << (*getCrackFrontPoint(i))(0) << std::setw(14) << (*getCrackFrontPoint(i))(1)
1029 << std::setw(14) << (*getCrackFrontPoint(i))(2) << std::setw(14)
1030 << _crack_directions[i](0) << std::setw(14) << _crack_directions[i](1)
1031 << std::setw(14) << _crack_directions[i](2);
1032 if (hasAngleAlongFront())
1033 _console << std::left << std::setw(14) << _angles_along_front[i];
1034 else
1035 _console << std::left << std::setw(14) << "--";
1036 _console << std::left << std::setw(14) << _distances_along_front[i] << std::setw(14)
1037 << (_segment_lengths[i].first + _segment_lengths[i].second) / 2.0 << std::endl;
1038 }
1039 _console << "overall length: " << _overall_length << std::endl;
1040 }
1041}
1042
1043void
1045{
1046 if (_crack_mouth_boundary_ids.size() > 0)
1047 {
1049
1050 std::set<Node *> crack_mouth_nodes;
1052 for (auto nd = bnd_nodes.begin(); nd != bnd_nodes.end(); ++nd)
1053 {
1054 const BndNode * bnode = *nd;
1055 BoundaryID boundary_id = bnode->_bnd_id;
1056
1057 for (std::size_t ibid = 0; ibid < _crack_mouth_boundary_ids.size(); ++ibid)
1058 {
1059 if (boundary_id == _crack_mouth_boundary_ids[ibid])
1060 {
1061 crack_mouth_nodes.insert(bnode->_node);
1062 break;
1063 }
1064 }
1065 }
1066
1067 for (auto nit = crack_mouth_nodes.begin(); nit != crack_mouth_nodes.end(); ++nit)
1068 {
1069 _crack_mouth_coordinates += **nit;
1070 }
1071 _crack_mouth_coordinates /= static_cast<Real>(crack_mouth_nodes.size());
1072
1075 }
1076}
1077
1078void
1080{
1082 {
1083 _crack_plane_normals.clear();
1084
1085 // Get 3 nodes on crack front
1086 std::size_t num_points = getNumCrackFrontPoints();
1087 if (num_points < 3)
1088 {
1089 mooseError("Crack front must contain at least 3 nodes to use CurvedCrackFront option");
1090 }
1091 std::size_t start_id;
1092 std::size_t mid_id;
1093 std::size_t end_id;
1094
1095 if (_closed_loop)
1096 {
1097 start_id = 0;
1098 mid_id = (num_points - 1) / 3;
1099 end_id = 2 * mid_id;
1100 }
1101 else
1102 {
1103 start_id = 0;
1104 mid_id = (num_points - 1) / 2;
1105 end_id = num_points - 1;
1106 }
1107 const Point * start = getCrackFrontPoint(start_id);
1108 const Point * mid = getCrackFrontPoint(mid_id);
1109 const Point * end = getCrackFrontPoint(end_id);
1110
1111 // Create two vectors connecting them
1112 RealVectorValue v1 = *mid - *start;
1113 RealVectorValue v2 = *end - *mid;
1114
1115 // Take cross product to get normal
1116 Point crack_plane_normal = v1.cross(v2);
1117 crack_plane_normal = crack_plane_normal.unit();
1118 _crack_plane_normals.assign(num_points, crack_plane_normal);
1119
1120 // Make sure they're not collinear
1121 RealVectorValue zero_vec(0.0);
1122 if (_crack_plane_normals.front().absolute_fuzzy_equals(zero_vec, _tol))
1123 {
1124 mooseError("Nodes on crack front are too close to being collinear");
1125 }
1126 }
1127}
1128
1129RealVectorValue
1131 const RealVectorValue & tangent_direction,
1132 const CRACK_NODE_TYPE ntype,
1133 const std::size_t crack_front_point_index) const
1134{
1135 RealVectorValue crack_dir;
1136 RealVectorValue zero_vec(0.0);
1137
1138 bool calc_dir = true;
1140 {
1141 if (ntype == END_1_NODE)
1142 {
1144 calc_dir = false;
1145 }
1146 else if (ntype == END_2_NODE)
1147 {
1149 calc_dir = false;
1150 }
1151 }
1152
1153 if (calc_dir)
1154 {
1156 {
1157 crack_dir = _crack_direction_vector;
1158 }
1160 {
1161 if (_crack_mouth_coordinates.absolute_fuzzy_equals(crack_front_point, _tol))
1162 {
1163 mooseError("Crack mouth too close to crack front node");
1164 }
1165 RealVectorValue mouth_to_front = crack_front_point - _crack_mouth_coordinates;
1166
1167 RealVectorValue crack_plane_normal = mouth_to_front.cross(tangent_direction);
1168 if (crack_plane_normal.absolute_fuzzy_equals(zero_vec, _tol))
1169 {
1170 mooseError(
1171 "Vector from crack mouth to crack front node is collinear with crack front segment");
1172 }
1173
1174 crack_dir = tangent_direction.cross(crack_plane_normal);
1175 Real dotprod = crack_dir * mouth_to_front;
1176 if (dotprod < 0)
1177 {
1178 crack_dir = -crack_dir;
1179 }
1180 }
1182 {
1183 crack_dir = tangent_direction.cross(_crack_plane_normals[crack_front_point_index]);
1184 }
1185 }
1186 crack_dir = crack_dir.unit();
1187
1188 return crack_dir;
1189}
1190
1191void
1193{
1194 _num_points_from_provider = num_points;
1195}
1196
1197const Node *
1198CrackFrontDefinition::getCrackFrontNodePtr(const std::size_t node_index) const
1199{
1200 mooseAssert(node_index < _ordered_crack_front_nodes.size(), "node_index out of range");
1201 const Node * crack_front_node = _mesh.nodePtr(_ordered_crack_front_nodes[node_index]);
1202 mooseAssert(crack_front_node != nullptr, "invalid crack front node");
1203 return crack_front_node;
1204}
1205
1206const Point *
1207CrackFrontDefinition::getCrackFrontPoint(const std::size_t point_index) const
1208{
1210 {
1211 return getCrackFrontNodePtr(point_index);
1212 }
1213 else
1214 {
1215 mooseAssert(point_index < _crack_front_points.size(), "point_index out of range");
1216 return &_crack_front_points[point_index];
1217 }
1218}
1219
1220const RealVectorValue &
1221CrackFrontDefinition::getCrackFrontTangent(const std::size_t point_index) const
1222{
1223 mooseAssert(point_index < _tangent_directions.size(), "point_index out of range");
1224 return _tangent_directions[point_index];
1225}
1226
1227const RealVectorValue &
1228CrackFrontDefinition::getCrackFrontNormal(const std::size_t point_index) const
1229{
1230 mooseAssert(point_index < _crack_plane_normals.size(), "point_index out of range");
1231 return _crack_plane_normals[point_index];
1232}
1233
1234Real
1236{
1237 return _segment_lengths[point_index].second;
1238}
1239
1240Real
1242{
1243 return _segment_lengths[point_index].first;
1244}
1245
1246const RealVectorValue &
1247CrackFrontDefinition::getCrackDirection(const std::size_t point_index) const
1248{
1249 return _crack_directions[point_index];
1250}
1251
1252Real
1253CrackFrontDefinition::getDistanceAlongFront(const std::size_t point_index) const
1254{
1255 return _distances_along_front[point_index];
1256}
1257
1258bool
1260{
1261 return (_crack_mouth_boundary_names.size() > 0);
1262}
1263
1264Real
1265CrackFrontDefinition::getAngleAlongFront(const std::size_t point_index) const
1266{
1267 if (!hasAngleAlongFront())
1268 paramError(
1269 "crack_mouth_boundary",
1270 "In CrackFrontDefinition, Requested angle along crack front, but definition of crack mouth "
1271 "boundary using 'crack_mouth_boundary' parameter is necessary to do that.");
1272 return _angles_along_front[point_index];
1273}
1274
1275std::size_t
1283
1284RealVectorValue
1286 const std::size_t point_index) const
1287{
1288 return _rot_matrix[point_index] * vector;
1289}
1290
1291RealVectorValue
1293 const std::size_t point_index) const
1294{
1295 RealVectorValue vec = _rot_matrix[point_index].transpose() * vector;
1296 return vec;
1297}
1298
1301 const std::size_t point_index) const
1302{
1303 RankTwoTensor tmp_tensor(tensor);
1304 tmp_tensor.rotate(_rot_matrix[point_index]);
1305 return tmp_tensor;
1306}
1307
1308void
1310 const std::size_t point_index,
1311 Real & r,
1312 Real & theta) const
1313{
1314 std::size_t num_points = getNumCrackFrontPoints();
1315 Point closest_point(0.0);
1316 RealVectorValue closest_point_to_p;
1317
1318 const Point * crack_front_point = getCrackFrontPoint(point_index);
1319 RealVectorValue crack_front_point_rot = rotateToCrackFrontCoords(*crack_front_point, point_index);
1320
1321 RealVectorValue crack_front_edge =
1322 rotateToCrackFrontCoords(_tangent_directions[point_index], point_index);
1323
1324 Point p_rot = rotateToCrackFrontCoords(qp, point_index);
1325 p_rot = p_rot - crack_front_point_rot;
1326
1327 if (_treat_as_2d)
1328 {
1329 // In 2D, the closest node is the crack tip node and the position of the crack tip node is
1330 // (0,0,0) in the crack front coordinate system
1331 // In case this is a 3D mesh treated as 2D, project point onto same plane as crack front node.
1332 // Note: In the crack front coordinate system, z is always in the tangent direction to the crack
1333 // front
1334 p_rot(2) = closest_point(2);
1335 closest_point_to_p = p_rot;
1336
1337 // Find r, the distance between the qp and the crack front
1338 RealVectorValue r_vec = p_rot;
1339 r = r_vec.norm();
1340 }
1341 else
1342 {
1343 // Loop over crack front points to find the one closest to the point qp
1344 Real min_dist = std::numeric_limits<Real>::max();
1345 for (std::size_t pit = 0; pit != num_points; ++pit)
1346 {
1347 const Point * crack_front_point = getCrackFrontPoint(pit);
1348 RealVectorValue crack_point_to_current_point = qp - *crack_front_point;
1349 Real dist = crack_point_to_current_point.norm();
1350
1351 if (dist < min_dist)
1352 {
1353 min_dist = dist;
1354 closest_point = *crack_front_point;
1355 }
1356 }
1357
1358 // Rotate coordinates to crack front coordinate system
1359 closest_point = rotateToCrackFrontCoords(closest_point, point_index);
1360 closest_point = closest_point - crack_front_point_rot;
1361
1362 // Find r, the distance between the qp and the crack front
1363 Real edge_length_sq = crack_front_edge.norm_sq();
1364 closest_point_to_p = p_rot - closest_point;
1365 Real perp = crack_front_edge * closest_point_to_p;
1366 Real dist_along_edge = perp / edge_length_sq;
1367 RealVectorValue point_on_edge = closest_point + crack_front_edge * dist_along_edge;
1368 RealVectorValue r_vec = p_rot - point_on_edge;
1369 r = r_vec.norm();
1370 }
1371
1372 // Find theta, the angle between r and the crack front plane
1373 RealVectorValue crack_plane_normal =
1374 rotateToCrackFrontCoords(_crack_plane_normals[point_index], point_index);
1375 Real p_to_plane_dist = std::abs(closest_point_to_p * crack_plane_normal);
1376
1377 // Determine if qp is above or below the crack plane
1378 Real y_local = p_rot(1) - closest_point(1);
1379
1380 // Determine if qp is in front of or behind the crack front
1381 RealVectorValue p2(p_rot);
1382 p2(1) = 0;
1383 RealVectorValue p2_vec = p2 - closest_point;
1384 Real ahead = crack_front_edge(2) * p2_vec(0) - crack_front_edge(0) * p2_vec(2);
1385
1386 Real x_local(0);
1387 if (ahead >= 0)
1388 x_local = 1;
1389 else
1390 x_local = -1;
1391
1392 // Calculate theta based on in which quadrant in the crack front coordinate
1393 // system the qp is located
1394 if (r > 0)
1395 {
1396 Real theta_quadrant1(0.0);
1397 if (MooseUtils::absoluteFuzzyEqual(r, p_to_plane_dist, _tol))
1398 theta_quadrant1 = 0.5 * libMesh::pi;
1399 else if (p_to_plane_dist > r)
1400 mooseError(
1401 "Invalid distance p_to_plane_dist in CrackFrontDefinition::calculateRThetaToCrackFront");
1402 else
1403 theta_quadrant1 = std::asin(p_to_plane_dist / r);
1404
1405 if (x_local >= 0 && y_local >= 0)
1406 theta = theta_quadrant1;
1407
1408 else if (x_local < 0 && y_local >= 0)
1409 theta = libMesh::pi - theta_quadrant1;
1410
1411 else if (x_local < 0 && y_local < 0)
1412 theta = -(libMesh::pi - theta_quadrant1);
1413
1414 else if (x_local >= 0 && y_local < 0)
1415 theta = -theta_quadrant1;
1416 }
1417 else if (r == 0)
1418 theta = 0;
1419 else
1420 mooseError("Invalid distance r in CrackFrontDefinition::calculateRThetaToCrackFront");
1421}
1422
1423std::size_t
1424CrackFrontDefinition::calculateRThetaToCrackFront(const Point qp, Real & r, Real & theta) const
1425{
1426 std::size_t num_points = getNumCrackFrontPoints();
1427
1428 // Loop over crack front points to find the one closest to the point qp
1429 Real min_dist = std::numeric_limits<Real>::max();
1430 std::size_t point_index = 0;
1431 for (std::size_t pit = 0; pit != num_points; ++pit)
1432 {
1433 const Point * crack_front_point = getCrackFrontPoint(pit);
1434 RealVectorValue crack_point_to_current_point = qp - *crack_front_point;
1435 Real dist = crack_point_to_current_point.norm();
1436
1437 if (dist < min_dist)
1438 {
1439 min_dist = dist;
1440 point_index = pit;
1441 }
1442 }
1443
1444 calculateRThetaToCrackFront(qp, point_index, r, theta);
1445
1446 return point_index;
1447}
1448
1449bool
1451{
1452 bool is_on_boundary = false;
1453 mooseAssert(node, "Invalid node");
1454 dof_id_type node_id = node->id();
1455 for (std::size_t i = 0; i < _intersecting_boundary_ids.size(); ++i)
1456 {
1458 {
1459 is_on_boundary = true;
1460 break;
1461 }
1462 }
1463 return is_on_boundary;
1464}
1465
1466bool
1468{
1469 bool is_on_boundary = false;
1471 {
1472 const Node * crack_front_node = getCrackFrontNodePtr(point_index);
1473 is_on_boundary = isNodeOnIntersectingBoundary(crack_front_node);
1474 }
1475 else
1476 {
1477 // If the intersecting boundary option is used with crack front points, the
1478 // first and last points are assumed to be on the intersecting boundaries.
1479 std::size_t num_crack_front_points = getNumCrackFrontPoints();
1480 if (point_index == 0 || point_index == num_crack_front_points - 1)
1481 is_on_boundary = true;
1482 }
1483 return is_on_boundary;
1484}
1485
1486void
1488{
1489 RealVectorValue disp_current_node;
1490 RealVectorValue disp_previous_node;
1491 RealVectorValue disp_next_node;
1492
1493 RealVectorValue forward_segment0;
1494 RealVectorValue forward_segment1;
1495 Real forward_segment0_len;
1496 Real forward_segment1_len;
1497 RealVectorValue back_segment0;
1498 RealVectorValue back_segment1;
1499 Real back_segment0_len;
1500 Real back_segment1_len;
1501
1502 std::size_t num_crack_front_nodes = _ordered_crack_front_nodes.size();
1503 const Node * current_node;
1504 const Node * previous_node;
1505 const Node * next_node;
1506
1507 _strain_along_front.reserve(num_crack_front_nodes);
1508
1509 // In finalize(), gatherMax builds and distributes the complete strain vector on all processors
1510 // -> reset the vector every time
1511 for (std::size_t i = 0; i < num_crack_front_nodes; ++i)
1512 _strain_along_front[i] = -std::numeric_limits<Real>::max();
1513
1517
1518 current_node = getCrackFrontNodePtr(0);
1519 if (current_node->processor_id() == processor_id())
1520 {
1521 disp_current_node(0) = disp_x_var.getNodalValue(*current_node);
1522 disp_current_node(1) = disp_y_var.getNodalValue(*current_node);
1523 disp_current_node(2) = disp_z_var.getNodalValue(*current_node);
1524
1525 next_node = getCrackFrontNodePtr(1);
1526 disp_next_node(0) = disp_x_var.getNodalValue(*next_node);
1527 disp_next_node(1) = disp_y_var.getNodalValue(*next_node);
1528 disp_next_node(2) = disp_z_var.getNodalValue(*next_node);
1529
1530 forward_segment0 = *next_node - *current_node;
1531 forward_segment0 = (forward_segment0 * _tangent_directions[0]) * _tangent_directions[0];
1532 forward_segment0_len = forward_segment0.norm();
1533
1534 forward_segment1 = (*next_node + disp_next_node) - (*current_node + disp_current_node);
1535 forward_segment1 = (forward_segment1 * _tangent_directions[0]) * _tangent_directions[0];
1536 forward_segment1_len = forward_segment1.norm();
1537
1538 _strain_along_front[0] = (forward_segment1_len - forward_segment0_len) / forward_segment0_len;
1539 }
1540
1541 for (std::size_t i = 1; i < num_crack_front_nodes - 1; ++i)
1542 {
1543 current_node = getCrackFrontNodePtr(i);
1544 if (current_node->processor_id() == processor_id())
1545 {
1546 disp_current_node(0) = disp_x_var.getNodalValue(*current_node);
1547 disp_current_node(1) = disp_y_var.getNodalValue(*current_node);
1548 disp_current_node(2) = disp_z_var.getNodalValue(*current_node);
1549
1550 previous_node = getCrackFrontNodePtr(i - 1);
1551 disp_previous_node(0) = disp_x_var.getNodalValue(*previous_node);
1552 disp_previous_node(1) = disp_y_var.getNodalValue(*previous_node);
1553 disp_previous_node(2) = disp_z_var.getNodalValue(*previous_node);
1554
1555 next_node = getCrackFrontNodePtr(i + 1);
1556 disp_next_node(0) = disp_x_var.getNodalValue(*next_node);
1557 disp_next_node(1) = disp_y_var.getNodalValue(*next_node);
1558 disp_next_node(2) = disp_z_var.getNodalValue(*next_node);
1559
1560 back_segment0 = *current_node - *previous_node;
1561 back_segment0 = (back_segment0 * _tangent_directions[i]) * _tangent_directions[i];
1562 back_segment0_len = back_segment0.norm();
1563
1564 back_segment1 = (*current_node + disp_current_node) - (*previous_node + disp_previous_node);
1565 back_segment1 = (back_segment1 * _tangent_directions[i]) * _tangent_directions[i];
1566 back_segment1_len = back_segment1.norm();
1567
1568 forward_segment0 = *next_node - *current_node;
1569 forward_segment0 = (forward_segment0 * _tangent_directions[i]) * _tangent_directions[i];
1570 forward_segment0_len = forward_segment0.norm();
1571
1572 forward_segment1 = (*next_node + disp_next_node) - (*current_node + disp_current_node);
1573 forward_segment1 = (forward_segment1 * _tangent_directions[i]) * _tangent_directions[i];
1574 forward_segment1_len = forward_segment1.norm();
1575
1577 0.5 * ((back_segment1_len - back_segment0_len) / back_segment0_len +
1578 (forward_segment1_len - forward_segment0_len) / forward_segment0_len);
1579 }
1580 }
1581
1582 current_node = getCrackFrontNodePtr(num_crack_front_nodes - 1);
1583 if (current_node->processor_id() == processor_id())
1584 {
1585 disp_current_node(0) = disp_x_var.getNodalValue(*current_node);
1586 disp_current_node(1) = disp_y_var.getNodalValue(*current_node);
1587 disp_current_node(2) = disp_z_var.getNodalValue(*current_node);
1588
1589 previous_node = getCrackFrontNodePtr(num_crack_front_nodes - 2);
1590 disp_previous_node(0) = disp_x_var.getNodalValue(*previous_node);
1591 disp_previous_node(1) = disp_y_var.getNodalValue(*previous_node);
1592 disp_previous_node(2) = disp_z_var.getNodalValue(*previous_node);
1593
1594 back_segment0 = *current_node - *previous_node;
1595 back_segment0 = (back_segment0 * _tangent_directions[num_crack_front_nodes - 1]) *
1596 _tangent_directions[num_crack_front_nodes - 1];
1597 back_segment0_len = back_segment0.norm();
1598
1599 back_segment1 = (*current_node + disp_current_node) - (*previous_node + disp_previous_node);
1600 back_segment1 = (back_segment1 * _tangent_directions[num_crack_front_nodes - 1]) *
1601 _tangent_directions[num_crack_front_nodes - 1];
1602 back_segment1_len = back_segment1.norm();
1603
1604 _strain_along_front[num_crack_front_nodes - 1] =
1605 (back_segment1_len - back_segment0_len) / back_segment0_len;
1606 }
1607}
1608
1609Real
1611{
1612 Real strain;
1613 if (_t_stress)
1614 {
1615 strain = _strain_along_front[node_index];
1616 mooseAssert(strain > -std::numeric_limits<Real>::max(),
1617 "Failure in parallel communication of crack tangential strain");
1618 }
1619 else
1620 mooseError("In CrackFrontDefinition, tangential strain not available");
1621
1622 return strain;
1623}
1624
1625void
1627{
1628 // In the variable names, "cfn" = crack front node
1629 if (_treat_as_2d && _use_mesh_cutter == false) // 2D: the q-function defines an integral domain
1630 // that is constant along the crack front
1631 {
1632 std::vector<std::vector<const Elem *>> nodes_to_elem_map;
1633 MeshTools::build_nodes_to_elem_map(_mesh.getMesh(), nodes_to_elem_map);
1634
1635 std::set<dof_id_type> nodes_prev_ring;
1636 nodes_prev_ring.insert(_ordered_crack_front_nodes.begin(), _ordered_crack_front_nodes.end());
1637
1638 std::set<dof_id_type> connected_nodes_this_cfn;
1639 connected_nodes_this_cfn.insert(_ordered_crack_front_nodes.begin(),
1641
1642 std::set<dof_id_type> old_ring_nodes_this_cfn = connected_nodes_this_cfn;
1643
1644 // The first ring contains only the crack front node(s)
1645 std::pair<dof_id_type, std::size_t> node_ring_index =
1646 std::make_pair(_ordered_crack_front_nodes[0], 1);
1647 _crack_front_node_to_node_map[node_ring_index].insert(connected_nodes_this_cfn.begin(),
1648 connected_nodes_this_cfn.end());
1649
1650 // Build rings of nodes around the crack front node
1651 for (std::size_t ring = 2; ring <= _last_ring; ++ring)
1652 {
1653
1654 // Find nodes connected to the nodes of the previous ring
1655 std::set<dof_id_type> new_ring_nodes_this_cfn;
1656 for (auto nit = old_ring_nodes_this_cfn.begin(); nit != old_ring_nodes_this_cfn.end(); ++nit)
1657 {
1658 std::vector<const Node *> neighbors;
1659 MeshTools::find_nodal_neighbors(
1660 _mesh.getMesh(), _mesh.nodeRef(*nit), nodes_to_elem_map, neighbors);
1661 for (std::size_t inei = 0; inei < neighbors.size(); ++inei)
1662 {
1663 auto thisit = connected_nodes_this_cfn.find(neighbors[inei]->id());
1664
1665 // Add only nodes that are not already present in any of the rings
1666 if (thisit == connected_nodes_this_cfn.end())
1667 new_ring_nodes_this_cfn.insert(neighbors[inei]->id());
1668 }
1669 }
1670
1671 // Add new nodes to rings
1672 connected_nodes_this_cfn.insert(new_ring_nodes_this_cfn.begin(),
1673 new_ring_nodes_this_cfn.end());
1674 old_ring_nodes_this_cfn = new_ring_nodes_this_cfn;
1675
1676 std::pair<dof_id_type, std::size_t> node_ring_index =
1677 std::make_pair(_ordered_crack_front_nodes[0], ring);
1678 _crack_front_node_to_node_map[node_ring_index].insert(connected_nodes_this_cfn.begin(),
1679 connected_nodes_this_cfn.end());
1680 }
1681 }
1682 else // The q-function defines one integral domain around each crack front node
1683 {
1684 std::size_t num_crack_front_points = _ordered_crack_front_nodes.size();
1685 std::vector<std::vector<const Elem *>> nodes_to_elem_map;
1686 MeshTools::build_nodes_to_elem_map(_mesh.getMesh(), nodes_to_elem_map);
1687 for (std::size_t icfn = 0; icfn < num_crack_front_points; ++icfn)
1688 {
1689 std::set<dof_id_type> nodes_prev_ring;
1690 nodes_prev_ring.insert(_ordered_crack_front_nodes[icfn]);
1691
1692 std::set<dof_id_type> connected_nodes_prev_cfn;
1693 std::set<dof_id_type> connected_nodes_this_cfn;
1694 std::set<dof_id_type> connected_nodes_next_cfn;
1695
1696 connected_nodes_this_cfn.insert(_ordered_crack_front_nodes[icfn]);
1697
1698 if (_closed_loop && icfn == 0)
1699 {
1700 connected_nodes_prev_cfn.insert(_ordered_crack_front_nodes[num_crack_front_points - 1]);
1701 connected_nodes_next_cfn.insert(_ordered_crack_front_nodes[icfn + 1]);
1702 }
1703 else if (_closed_loop && icfn == num_crack_front_points - 1)
1704 {
1705 connected_nodes_prev_cfn.insert(_ordered_crack_front_nodes[icfn - 1]);
1706 connected_nodes_next_cfn.insert(_ordered_crack_front_nodes[0]);
1707 }
1708 else if (icfn == 0)
1709 {
1710 connected_nodes_next_cfn.insert(_ordered_crack_front_nodes[icfn + 1]);
1711 }
1712 else if (icfn == num_crack_front_points - 1)
1713 {
1714 connected_nodes_prev_cfn.insert(_ordered_crack_front_nodes[icfn - 1]);
1715 }
1716 else
1717 {
1718 connected_nodes_prev_cfn.insert(_ordered_crack_front_nodes[icfn - 1]);
1719 connected_nodes_next_cfn.insert(_ordered_crack_front_nodes[icfn + 1]);
1720 }
1721
1722 std::set<dof_id_type> old_ring_nodes_prev_cfn = connected_nodes_prev_cfn;
1723 std::set<dof_id_type> old_ring_nodes_this_cfn = connected_nodes_this_cfn;
1724 std::set<dof_id_type> old_ring_nodes_next_cfn = connected_nodes_next_cfn;
1725
1726 // The first ring contains only the crack front node
1727 std::pair<dof_id_type, std::size_t> node_ring_index =
1728 std::make_pair(_ordered_crack_front_nodes[icfn], 1);
1729 _crack_front_node_to_node_map[node_ring_index].insert(connected_nodes_this_cfn.begin(),
1730 connected_nodes_this_cfn.end());
1731
1732 // Build rings of nodes around the crack front node
1733 for (std::size_t ring = 2; ring <= _last_ring; ++ring)
1734 {
1735
1736 // Find nodes connected to the nodes of the previous ring, but exclude nodes in rings of
1737 // neighboring crack front nodes
1738 std::set<dof_id_type> new_ring_nodes_this_cfn;
1739 addNodesToQFunctionRing(new_ring_nodes_this_cfn,
1740 old_ring_nodes_this_cfn,
1741 connected_nodes_this_cfn,
1742 connected_nodes_prev_cfn,
1743 connected_nodes_next_cfn,
1744 nodes_to_elem_map);
1745
1746 std::set<dof_id_type> new_ring_nodes_prev_cfn;
1747 addNodesToQFunctionRing(new_ring_nodes_prev_cfn,
1748 old_ring_nodes_prev_cfn,
1749 connected_nodes_prev_cfn,
1750 connected_nodes_this_cfn,
1751 connected_nodes_next_cfn,
1752 nodes_to_elem_map);
1753
1754 std::set<dof_id_type> new_ring_nodes_next_cfn;
1755 addNodesToQFunctionRing(new_ring_nodes_next_cfn,
1756 old_ring_nodes_next_cfn,
1757 connected_nodes_next_cfn,
1758 connected_nodes_prev_cfn,
1759 connected_nodes_this_cfn,
1760 nodes_to_elem_map);
1761
1762 // Add new nodes to the three sets of nodes
1763 connected_nodes_prev_cfn.insert(new_ring_nodes_prev_cfn.begin(),
1764 new_ring_nodes_prev_cfn.end());
1765 connected_nodes_this_cfn.insert(new_ring_nodes_this_cfn.begin(),
1766 new_ring_nodes_this_cfn.end());
1767 connected_nodes_next_cfn.insert(new_ring_nodes_next_cfn.begin(),
1768 new_ring_nodes_next_cfn.end());
1769 old_ring_nodes_prev_cfn = new_ring_nodes_prev_cfn;
1770 old_ring_nodes_this_cfn = new_ring_nodes_this_cfn;
1771 old_ring_nodes_next_cfn = new_ring_nodes_next_cfn;
1772
1773 std::pair<dof_id_type, std::size_t> node_ring_index =
1774 std::make_pair(_ordered_crack_front_nodes[icfn], ring);
1775 _crack_front_node_to_node_map[node_ring_index].insert(connected_nodes_this_cfn.begin(),
1776 connected_nodes_this_cfn.end());
1777 }
1778 }
1779 }
1780}
1781
1782void
1784 std::set<dof_id_type> & nodes_new_ring,
1785 const std::set<dof_id_type> & nodes_old_ring,
1786 const std::set<dof_id_type> & nodes_all_rings,
1787 const std::set<dof_id_type> & nodes_neighbor1,
1788 const std::set<dof_id_type> & nodes_neighbor2,
1789 std::vector<std::vector<const Elem *>> & nodes_to_elem_map)
1790{
1791 for (auto nit = nodes_old_ring.begin(); nit != nodes_old_ring.end(); ++nit)
1792 {
1793 std::vector<const Node *> neighbors;
1794 MeshTools::find_nodal_neighbors(
1795 _mesh.getMesh(), _mesh.nodeRef(*nit), nodes_to_elem_map, neighbors);
1796 for (std::size_t inei = 0; inei < neighbors.size(); ++inei)
1797 {
1798 auto previt = nodes_all_rings.find(neighbors[inei]->id());
1799 auto thisit = nodes_neighbor1.find(neighbors[inei]->id());
1800 auto nextit = nodes_neighbor2.find(neighbors[inei]->id());
1801
1802 // Add only nodes that are not already present in any of the three sets of nodes
1803 if (previt == nodes_all_rings.end() && thisit == nodes_neighbor1.end() &&
1804 nextit == nodes_neighbor2.end())
1805 nodes_new_ring.insert(neighbors[inei]->id());
1806 }
1807 }
1808}
1809
1810bool
1811CrackFrontDefinition::isNodeInRing(const std::size_t ring_index,
1812 const dof_id_type connected_node_id,
1813 const std::size_t node_index) const
1814{
1815 bool is_node_in_ring = false;
1816 std::pair<dof_id_type, std::size_t> node_ring_key =
1817 std::make_pair(_ordered_crack_front_nodes[node_index], ring_index);
1818 auto nnmit = _crack_front_node_to_node_map.find(node_ring_key);
1819
1820 if (nnmit == _crack_front_node_to_node_map.end())
1821 mooseError("Could not find crack front node ",
1822 _ordered_crack_front_nodes[node_index],
1823 " in the crack front node to q-function ring-node map for ring ",
1824 ring_index);
1825
1826 std::set<dof_id_type> q_func_nodes = nnmit->second;
1827 if (q_func_nodes.find(connected_node_id) != q_func_nodes.end())
1828 is_node_in_ring = true;
1829
1830 return is_node_in_ring;
1831}
1832
1833Real
1834CrackFrontDefinition::DomainIntegralQFunction(std::size_t crack_front_point_index,
1835 std::size_t ring_index,
1836 const Node * const current_node) const
1837{
1838 Real dist_to_crack_front;
1839 Real dist_along_tangent;
1841 dist_to_crack_front, dist_along_tangent, crack_front_point_index, current_node);
1842
1843 Real q = 1.0;
1844 if (dist_to_crack_front > _j_integral_radius_inner[ring_index] &&
1845 dist_to_crack_front < _j_integral_radius_outer[ring_index])
1846 q = (_j_integral_radius_outer[ring_index] - dist_to_crack_front) /
1847 (_j_integral_radius_outer[ring_index] - _j_integral_radius_inner[ring_index]);
1848 else if (dist_to_crack_front >= _j_integral_radius_outer[ring_index])
1849 q = 0.0;
1850
1851 if (q > 0.0)
1852 {
1853 Real tangent_multiplier = 1.0;
1854 if (!_treat_as_2d)
1855 {
1856 const Real forward_segment_length =
1857 getCrackFrontForwardSegmentLength(crack_front_point_index);
1858 const Real backward_segment_length =
1859 getCrackFrontBackwardSegmentLength(crack_front_point_index);
1860
1861 if (dist_along_tangent >= 0.0)
1862 {
1863 if (forward_segment_length > 0.0)
1864 tangent_multiplier = 1.0 - dist_along_tangent / forward_segment_length;
1865 }
1866 else
1867 {
1868 if (backward_segment_length > 0.0)
1869 tangent_multiplier = 1.0 + dist_along_tangent / backward_segment_length;
1870 }
1871 }
1872
1873 tangent_multiplier = std::max(tangent_multiplier, 0.0);
1874 tangent_multiplier = std::min(tangent_multiplier, 1.0);
1875
1876 // Set to zero if a node is on a designated free surface and its crack front node is not.
1877 if (isNodeOnIntersectingBoundary(current_node) &&
1878 !_is_point_on_intersecting_boundary[crack_front_point_index])
1879 tangent_multiplier = 0.0;
1880
1881 q *= tangent_multiplier;
1882 }
1883
1884 return q;
1885}
1886
1887Real
1889 std::size_t ring_index,
1890 const Node * const current_node) const
1891{
1892 Real q = 0;
1893 bool is_node_in_ring = isNodeInRing(ring_index, current_node->id(), crack_front_point_index);
1894 if (is_node_in_ring)
1895 q = 1;
1896
1897 return q;
1898}
1899
1900void
1902 Real & dist_along_tangent,
1903 std::size_t crack_front_point_index,
1904 const Node * const current_node) const
1905{
1906 const Point * crack_front_point = getCrackFrontPoint(crack_front_point_index);
1907
1908 Point p = *current_node;
1909 const RealVectorValue & crack_front_tangent = getCrackFrontTangent(crack_front_point_index);
1910
1911 RealVectorValue crack_node_to_current_node = p - *crack_front_point;
1912 dist_along_tangent = crack_node_to_current_node * crack_front_tangent;
1913 RealVectorValue projection_point = *crack_front_point + dist_along_tangent * crack_front_tangent;
1914 RealVectorValue axis_to_current_node = p - projection_point;
1915 dist_to_front = axis_to_current_node.norm();
1916}
1917
1918void
1919CrackFrontDefinition::isCutterModified(const bool is_cutter_modified)
1920{
1921 _is_cutter_modified = is_cutter_modified;
1922}
boundary_id_type BoundaryID
registerMooseObject("SolidMechanicsApp", CrackFrontDefinition)
const Real p
void ErrorVector unsigned int
bool hasBoundary(const BoundaryName &name) const
static InputParameters validParams()
const ConsoleStream _console
Class used in fracture integrals to define geometric characteristics of the crack front.
const Point * getCrackFrontPoint(const std::size_t point_index) const
Get a Point object for a specified point on the crack front.
std::vector< RankTwoTensor > _rot_matrix
Vector of rotation matrices along the crack front.
static void includeCrackFrontDefinitionParams(InputParameters &params)
used by Actions to add CrackFrontDefinitionParams
std::vector< BoundaryName > _intersecting_boundary_names
Names of boundaries that intersect crack at its ends.
std::size_t _num_points_from_provider
Number of points coming from the CrackFrontPointsProvider.
const CrackFrontPointsProvider * _crack_front_points_provider
Pointer to a CrackFrontPointsProvider object optionally used to define the crack front points.
void updateNumberOfCrackFrontPoints(const std::size_t num_points)
Change the number of crack front nodes.
MooseMesh & _mesh
Reference to the mesh.
CRACK_NODE_TYPE
Enum used to define the type of the nodes on the crack front (end or middle)
void createQFunctionRings()
Create the data defining the rings used to define the q function when the topological option is used ...
const Node * getCrackFrontNodePtr(const std::size_t node_index) const
Get the node pointer for a specified node on the crack front.
DIRECTION_METHOD
Enum used to define the method for computing the crack extension direction.
std::vector< dof_id_type > _ordered_crack_front_nodes
Crack front nodes ordered from the start to end of the crack front.
dof_id_type maxNodeCoor(std::vector< Node * > &nodes, unsigned int dir0=0)
Find the node with the maximum value of its coordinate.
Real getCrackFrontBackwardSegmentLength(const std::size_t point_index) const
Get the length of the line segment on the crack front behind the specified position.
void orderCrackFrontNodes(std::set< dof_id_type > &nodes)
Arrange the crack front nodes by their position along the crack front, and put them in the _ordered_c...
std::vector< BoundaryID > _intersecting_boundary_ids
IDs of boundaries that intersect crack at its ends.
virtual void initialize() override
bool hasAngleAlongFront() const
Whether the distance along the crack front is available as an angle.
std::size_t _first_ring
Numer of elements from crack tip to first topological ring.
RealVectorValue _crack_tangent_vector_end_2
Fixed vector optionally used to define crack tangent direction at end 2 of crack front.
Real _overall_length
Overall length of the crack.
void pickLoopCrackEndNodes(std::vector< dof_id_type > &end_nodes, std::set< dof_id_type > &nodes, std::map< dof_id_type, std::vector< dof_id_type > > &node_to_line_elem_map, std::vector< std::vector< dof_id_type > > &line_elems)
For the case of a crack that is a complete loop, determine which of the nodes should be the start and...
std::vector< Point > _crack_front_points
Vector of points along the crack front.
enum CrackFrontDefinition::DIRECTION_METHOD _direction_method
enum CrackFrontDefinition::CRACK_GEOM_DEFINITION _geom_definition_method
bool _use_mesh_cutter
Whether to describe the crack as a mesh cutter.
std::vector< Real > _j_integral_radius_outer
Vector of outer radii of the rings used for geometric q functions.
std::vector< RealVectorValue > _tangent_directions
Vector of tangent directions along the crack front.
void projectToFrontAtPoint(Real &dist_to_front, Real &dist_along_tangent, std::size_t crack_front_point_index, const Node *const current_node) const
Project a point to a specified point along the crack front and compute the projected normal and tange...
MooseEnum _q_function_type
Method used to define the q function.
const Real _tol
tolerance for matching nodes at crack front
void computeCrackMouthNodes()
compute node and coordinate data for crack fronts defined by crack_mouth_boundary_ids sidesets
const RealVectorValue & getCrackFrontNormal(const std::size_t point_index) const
Get the vector normal to the crack front at a specified position.
bool _closed_loop
Whether the crack forms a closed loop.
RealVectorValue _crack_mouth_coordinates
Coordinates of crack mouth.
std::vector< bool > _is_point_on_intersecting_boundary
Vector of bools indicating whether individual crack front points are on an intersecting boundary.
CrackFrontDefinition(const InputParameters &parameters)
virtual void initialSetup() override
virtual void execute() override
std::vector< std::pair< Real, Real > > _segment_lengths
Vector of segment lengths along the crack front.
std::vector< Real > _distances_along_front
Vector of distances along the crack front.
static InputParameters validParams()
Real getCrackFrontForwardSegmentLength(const std::size_t point_index) const
Get the length of the line segment on the crack front ahead of the specified position.
Real DomainIntegralTopologicalQFunction(std::size_t crack_front_point_index, std::size_t ring_index, const Node *const current_node) const
Compute the q function for the case where it is defined through element connectivity.
Real getCrackFrontTangentialStrain(const std::size_t node_index) const
Get the strain in the direction tangent to the crack front at a given point.
void computeCurvedCrackFrontCrackPlaneNormals()
Compute crack plane face normals for cracks that have a curved crack front but do not use a mesh cutt...
std::size_t _last_ring
Numer of elements from crack tip to last topological ring.
void addNodesToQFunctionRing(std::set< dof_id_type > &nodes_new_ring, const std::set< dof_id_type > &nodes_old_ring, const std::set< dof_id_type > &nodes_all_rings, const std::set< dof_id_type > &nodes_neighbor1, const std::set< dof_id_type > &nodes_neighbor2, std::vector< std::vector< const Elem * > > &nodes_to_elem_map)
Find nodes that are connected through elements to the nodes in the previous node ring.
bool _is_cutter_modified
Indicator that shows if the cutter mesh is modified or not in the calculation step.
std::vector< Real > _angles_along_front
Vector of angles along the crack front.
unsigned int _symmetry_plane
Which plane is the symmetry plane.
std::map< std::pair< dof_id_type, std::size_t >, std::set< dof_id_type > > _crack_front_node_to_node_map
Data structure used to store information about topological rings Key is a pair of the crack front nod...
Real getDistanceAlongFront(const std::size_t point_index) const
Get the distance along the crack front from the beginning of the crack to the specified position.
std::size_t getNumCrackFrontPoints() const
Get the number of points defining the crack front as a set of line segments.
bool _treat_as_2d
Whether to treat the model as 2D for computation of fracture integrals.
void orderEndNodes(std::vector< dof_id_type > &end_nodes)
Determine which of the end nodes should be the starting point of the crack front.
virtual void finalize() override
std::vector< Real > _j_integral_radius_inner
Vector of inner radii of the rings used for geometric q functions.
void isCutterModified(const bool is_cutter_modified)
Set the value of _is_cutter_modified.
std::vector< RealVectorValue > _crack_plane_normals
Vector normals to a nonplanar crack.
bool isNodeInRing(const std::size_t ring_index, const dof_id_type connected_node_id, const std::size_t node_index) const
Determine whether a node is contained within a specified volume integral element ring for a given nod...
END_DIRECTION_METHOD
Enum used to define the method for computing the crack extension direction at the ends of the crack.
std::vector< BoundaryName > _crack_mouth_boundary_names
Names of boundaries used to define location of crack mouth.
void getCrackFrontNodes(std::set< dof_id_type > &nodes)
Get the set of all crack front nodes.
std::vector< Real > _strain_along_front
Vector of tangential strain along the crack front.
enum CrackFrontDefinition::END_DIRECTION_METHOD _end_direction_method
void calculateRThetaToCrackFront(const Point qp, const std::size_t point_index, Real &r, Real &theta) const
Calculate r and theta of a point in the crack front polar coordinates for a given crack point index.
RealVectorValue _crack_direction_vector
Fixed vector optionally used to define crack extension direction.
Real getAngleAlongFront(const std::size_t point_index) const
Get the angle along the crack front from the beginning of the crack to the specified position.
const RealVectorValue & getCrackDirection(const std::size_t point_index) const
Get the unit vector of the crack extension direction at the specified position.
RealVectorValue _crack_direction_vector_end_2
Fixed vector optionally used to define crack extension direction at end 2 of crack front.
bool isNodeOnIntersectingBoundary(const Node *const node) const
Determine whether a given node is on one of the boundaries that intersects an end of the crack front.
RealVectorValue _crack_direction_vector_end_1
Fixed vector optionally used to define crack extension direction at end 1 of crack front.
RealVectorValue calculateCrackFrontDirection(const Point &crack_front_point, const RealVectorValue &tangent_direction, const CRACK_NODE_TYPE ntype, const std::size_t crack_front_point_index=0) const
Compute the direction of crack extension for a given point on the crack front.
void calculateTangentialStrainAlongFront()
Compute the strain in the direction tangent to the crack at all points on the crack front.
RealVectorValue rotateToCrackFrontCoords(const RealVectorValue vector, const std::size_t point_index) const
Rotate a vector in the global coordinate coordinate system to the crack front local coordinate system...
std::vector< BoundaryID > _crack_mouth_boundary_ids
IDs of boundaries used to define location of crack mouth.
bool _t_stress
Whether the T-stress is being computed.
bool _has_symmetry_plane
Whether the crack plane is also a symmetry plane in the model.
RealVectorValue rotateFromCrackFrontCoordsToGlobal(const RealVectorValue vector, const std::size_t point_index) const
Rotate a vector from crack front cartesian coordinate to global cartesian coordinate.
bool _q_function_rings
Whether topological rings are used to define the q functions.
const RealVectorValue & getCrackFrontTangent(const std::size_t point_index) const
Get the vector tangent to the crack front at a specified position.
std::vector< RealVectorValue > _crack_directions
Vector of crack extension directions along the crack front.
std::string _disp_x_var_name
Names of the x, y, and z displacement variables.
bool isPointWithIndexOnIntersectingBoundary(const std::size_t point_index) const
Determine whether a given crack front point is on one of the boundaries that intersects an end of the...
RealVectorValue _crack_tangent_vector_end_1
Fixed vector optionally used to define crack tangent direction at end 1 of crack front.
unsigned int _axis_2d
Out of plane axis when crack is treated as 2D.
Real DomainIntegralQFunction(std::size_t crack_front_point_index, std::size_t ring_index, const Node *const current_node) const
Compute the q function for the case where it is defined geometrically.
void updateCrackFrontGeometry()
Update the data structures defining the crack front geometry such as the ordered crack front nodes/po...
virtual const std::vector< RealVectorValue > getCrackPlaneNormals(unsigned int) const =0
get a set of normal vectors along a crack front from a XFEM GeometricCutUserObject
virtual unsigned int getNumberOfCrackFrontPoints() const =0
Get the current number of crack front points.
virtual const std::vector< Point > getCrackFrontPoints(unsigned int) const =0
get a set of points along a crack front from a XFEM GeometricCutUserObject
bool usesMesh() const
Getter for if a cutter mesh is used in a derived class.
static InputParameters validParams()
bool isParamSetByUser(const std::string &name) const
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addRelationshipManager(const std::string &name, Moose::RelationshipManagerType rm_type, Moose::RelationshipManagerInputParameterCallback input_parameter_callback=nullptr)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
const InputParameters & parameters() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
void mooseInfo(Args &&... args) const
bool isParamValid(const std::string &name) const
std::string getRawNames() const
virtual const Node & nodeRef(const dof_id_type i) const
MeshBase & getMesh()
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & nodeToElemMap()
std::vector< BoundaryID > getBoundaryIDs(const Elem *const elem, const unsigned short int side) const
virtual const Node * nodePtr(const dof_id_type i) const
libMesh::StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > * getBoundaryNodeRange()
bool isBoundaryNode(dof_id_type node_id) const
DofValue getNodalValue(const Node &node) const
void fillRow(unsigned int r, const libMesh::TypeVector< T > &v)
void rotate(const RankTwoTensorTempl< T > &R)
virtual MooseVariable & getStandardVariable(const THREAD_ID tid, const std::string &var_name)=0
void max(const T &r, T &o, Request &req) const
SubProblem & _subproblem
const THREAD_ID _tid
dof_id_type id() const
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
const_iterator begin() const
const_iterator end() const
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
MeshBase & mesh
const Real pi
BoundaryID _bnd_id
libMesh::Node * _node