13 #include "MooseMesh.h"
14 #include "MooseVariable.h"
15 #include "RankTwoTensor.h"
17 #include "libmesh/mesh_tools.h"
18 #include "libmesh/string_to_enum.h"
19 #include "libmesh/quadrature.h"
29 params.addClassDescription(
"Gathers information about nodes at the crack front; this object is "
30 "normally created by the DomainIntegralAction.");
33 params.set<
bool>(
"use_displaced_mesh") =
false;
40 MooseEnum direction_method(
"CrackDirectionVector CrackMouth CurvedCrackFront");
41 MooseEnum end_direction_method(
"NoSpecialTreatment CrackDirectionVector",
"NoSpecialTreatment");
42 params.addParam<std::vector<Point>>(
"crack_front_points",
"Set of points to define crack front");
43 params.addParam<
bool>(
"closed_loop",
false,
"Set of points forms forms a closed loop");
44 params.addRequiredParam<MooseEnum>(
45 "crack_direction_method",
47 "Method to determine direction of crack propagation. Choices are: " +
48 direction_method.getRawNames());
49 params.addParam<MooseEnum>(
50 "crack_end_direction_method",
52 "Method to determine direction of crack propagation at ends of crack. Choices are: " +
53 end_direction_method.getRawNames());
54 params.addParam<RealVectorValue>(
"crack_direction_vector",
"Direction of crack propagation");
55 params.addParam<RealVectorValue>(
56 "crack_direction_vector_end_1",
57 "Direction of crack propagation for the node at end 1 of the crack");
58 params.addParam<RealVectorValue>(
59 "crack_direction_vector_end_2",
60 "Direction of crack propagation for the node at end 2 of the crack");
61 params.addParam<std::vector<BoundaryName>>(
62 "crack_mouth_boundary",
"Boundaries whose average coordinate defines the crack mouth");
63 params.addParam<std::vector<BoundaryName>>(
"intersecting_boundary",
64 "Boundaries intersected by ends of crack");
65 params.addParam<
bool>(
"2d",
false,
"Treat body as two-dimensional");
66 params.addRangeCheckedParam<
unsigned int>(
69 "axis_2d>=0 & axis_2d<=2",
70 "Out of plane axis for models treated as two-dimensional (0=x, 1=y, 2=z)");
71 params.addParam<
unsigned int>(
"symmetry_plane",
72 "Account for a symmetry plane passing through "
73 "the plane of the crack, normal to the specified "
74 "axis (0=x, 1=y, 2=z)");
75 params.addParam<
bool>(
"t_stress",
false,
"Calculate T-stress");
76 params.addParam<
bool>(
"q_function_rings",
false,
"Generate rings of nodes for q-function");
77 params.addParam<
unsigned int>(
"last_ring",
"The number of rings of nodes to generate");
78 params.addParam<
unsigned int>(
"first_ring",
"The number of rings of nodes to generate");
79 params.addParam<
unsigned int>(
"nrings",
"The number of rings of nodes to generate");
80 params.addParam<VariableName>(
"disp_x",
"Variable containing the x displacement");
81 params.addParam<VariableName>(
"disp_y",
"Variable containing the y displacement");
82 params.addParam<VariableName>(
"disp_z",
"Variable containing the z displacement");
83 params.addParam<std::vector<Real>>(
"j_integral_radius_inner",
84 "Radius for J-Integral calculation");
85 params.addParam<std::vector<Real>>(
"j_integral_radius_outer",
86 "Radius for J-Integral calculation");
87 MooseEnum q_function_type(
"Geometry Topology",
"Geometry");
88 params.addParam<MooseEnum>(
"q_function_type",
90 "The method used to define the integration domain. Options are: " +
91 q_function_type.getRawNames());
92 params.addParam<UserObjectName>(
93 "crack_front_points_provider",
94 "The UserObject provides the crack front points from XFEM GeometricCutObject");
95 params.addParam<
unsigned int>(
96 "number_points_from_provider",
97 "The number of crack front points, only needed if crack_front_points_provider is used.");
103 : GeneralUserObject(parameters),
104 BoundaryRestrictable(this, true),
105 _aux(_fe_problem.getAuxiliarySystem()),
106 _mesh(_subproblem.mesh()),
107 _treat_as_2d(getParam<bool>(
"2d")),
108 _closed_loop(getParam<bool>(
"closed_loop")),
109 _axis_2d(getParam<unsigned int>(
"axis_2d")),
110 _has_symmetry_plane(isParamValid(
"symmetry_plane")),
111 _symmetry_plane(_has_symmetry_plane ? getParam<unsigned int>(
"symmetry_plane")
112 : std::numeric_limits<unsigned int>::max()),
113 _t_stress(getParam<bool>(
"t_stress")),
114 _q_function_rings(getParam<bool>(
"q_function_rings")),
115 _q_function_type(getParam<MooseEnum>(
"q_function_type")),
116 _crack_front_points_provider(nullptr)
118 if (isParamValid(
"crack_front_points"))
120 if (isParamValid(
"boundary"))
121 mooseError(
"CrackFrontDefinition error: since boundary is defined, crack_front_points should "
123 if (isParamValid(
"crack_front_points_provider"))
124 mooseError(
"As crack_front_points have been provided, the crack_front_points_provider will "
125 "not be used and needs to be removed.");
129 mooseError(
"t_stress not yet supported with crack_front_points");
131 mooseError(
"q_function_rings not supported with crack_front_points");
133 else if (isParamValid(
"crack_front_points_provider"))
135 if (isParamValid(
"boundary"))
136 mooseError(
"CrackFrontDefinition error: since boundary is defined, "
137 "crack_front_points_provider should not be added.");
138 if (!isParamValid(
"number_points_from_provider"))
139 mooseError(
"CrackFrontDefinition error: When crack_front_points_provider is used, the "
140 "number_points_from_provider must be "
143 getParam<UserObjectName>(
"crack_front_points_provider"));
147 else if (isParamValid(
"number_points_from_provider"))
148 mooseError(
"CrackFrontDefinition error: number_points_from_provider is provided but "
149 "crack_front_points_provider cannot "
151 else if (isParamValid(
"boundary"))
154 if (parameters.isParamSetByUser(
"closed_loop"))
155 mooseError(
"In CrackFrontDefinition, if 'boundary' is defined, 'closed_loop' should not be "
159 mooseError(
"In CrackFrontDefinition, must define one of 'boundary', 'crack_front_points' "
160 "and 'crack_front_points_provider'");
162 if (isParamValid(
"crack_mouth_boundary"))
167 mooseError(
"symmetry_plane out of bounds: ",
_symmetry_plane,
" Must be >=0 and <=2.");
169 MooseEnum direction_method_moose_enum = getParam<MooseEnum>(
"crack_direction_method");
174 if (!isParamValid(
"crack_direction_vector"))
175 mooseError(
"crack_direction_vector must be specified if crack_direction_method = "
176 "CrackDirectionVector");
180 if (isParamValid(
"crack_direction_vector"))
181 mooseError(
"crack_direction_vector must not be specified if crack_direction_method = "
185 "crack_mouth_boundary must be specified if crack_direction_method = CrackMouthNodes");
188 if (isParamValid(
"crack_direction_vector"))
189 mooseError(
"crack_direction_vector must not be specified if crack_direction_method = "
193 mooseError(
"Invalid direction_method");
196 if (isParamValid(
"intersecting_boundary"))
199 MooseEnum end_direction_method_moose_enum = getParam<MooseEnum>(
"crack_end_direction_method");
200 if (end_direction_method_moose_enum.isValid())
205 if (!isParamValid(
"crack_direction_vector_end_1"))
206 mooseError(
"crack_direction_vector_end_1 must be specified if crack_end_direction_method = "
207 "CrackDirectionVector");
208 if (!isParamValid(
"crack_direction_vector_end_2"))
209 mooseError(
"crack_direction_vector_end_2 must be specified if crack_end_direction_method = "
210 "CrackDirectionVector");
216 if (isParamValid(
"disp_x") && isParamValid(
"disp_y") && isParamValid(
"disp_z"))
223 mooseError(
"Displacement variables must be provided for T-stress calculation");
227 if (!isParamValid(
"last_ring"))
228 mooseError(
"The max number of rings of nodes to generate must be provided if "
229 "q_function_rings = true");
230 _last_ring = getParam<unsigned int>(
"last_ring");
231 _first_ring = getParam<unsigned int>(
"first_ring");
263 std::set<dof_id_type> nodes;
269 mooseError(
"Cannot use intersecting_boundary with closed-loop cracks");
279 for (
unsigned int i = 0; i < num_crack_front_nodes; ++i)
287 if (num_crack_front_points < 1)
288 mooseError(
"num_crack_front_points is not > 0");
289 for (
unsigned int i = 0; i < num_crack_front_points; ++i)
312 ConstBndNodeRange & bnd_nodes = *
_mesh.getBoundaryNodeRange();
313 for (ConstBndNodeRange::const_iterator nd = bnd_nodes.begin(); nd != bnd_nodes.end(); ++nd)
315 const BndNode * bnode = *nd;
316 BoundaryID boundary_id = bnode->_bnd_id;
318 if (hasBoundary(boundary_id))
319 nodes.insert(bnode->_node->id());
324 if (nodes.size() > 1)
345 mooseError(
"Invalid axis.");
351 for (std::set<dof_id_type>::iterator sit = nodes.begin(); sit != nodes.end(); ++sit)
353 Node & curr_node =
_mesh.nodeRef(*sit);
354 if (sit == nodes.begin())
356 node0coor0 = curr_node(axis0);
357 node0coor1 = curr_node(axis1);
361 if (!MooseUtils::absoluteFuzzyEqual(curr_node(axis0), node0coor0,
_tol) ||
362 !MooseUtils::absoluteFuzzyEqual(curr_node(axis1), node0coor1,
_tol))
363 mooseError(
"Boundary provided in CrackFrontDefinition contains ",
365 " nodes, which are not collinear in the ",
367 " axis. Must contain either 1 node or collinear nodes to treat as 2D.");
378 if (nodes.size() < 1)
379 mooseError(
"No crack front nodes");
380 else if (nodes.size() == 1)
384 mooseError(
"Boundary provided in CrackFrontDefinition contains 1 node, but model is not "
395 const std::map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map =
396 _mesh.nodeToElemMap();
397 std::map<dof_id_type, std::set<dof_id_type>> crack_front_node_to_elem_map;
399 for (
const auto & node_id : nodes)
401 const auto & node_to_elem_pair = node_to_elem_map.find(node_id);
402 mooseAssert(node_to_elem_pair != node_to_elem_map.end(),
403 "Could not find crack front node " << node_id <<
"in the node to elem map");
405 const std::vector<dof_id_type> & connected_elems = node_to_elem_pair->second;
406 for (
unsigned int i = 0; i < connected_elems.size(); ++i)
407 crack_front_node_to_elem_map[node_id].insert(connected_elems[i]);
413 std::vector<std::vector<dof_id_type>> line_elems;
414 std::map<dof_id_type, std::vector<dof_id_type>> node_to_line_elem_map;
416 for (std::map<dof_id_type, std::set<dof_id_type>>::iterator cfnemit =
417 crack_front_node_to_elem_map.begin();
418 cfnemit != crack_front_node_to_elem_map.end();
421 std::map<dof_id_type, std::set<dof_id_type>>::iterator cfnemit2 = cfnemit;
422 for (++cfnemit2; cfnemit2 != crack_front_node_to_elem_map.end(); ++cfnemit2)
425 std::vector<dof_id_type> common_elements;
426 std::set<dof_id_type> & elements_connected_to_node1 = cfnemit->second;
427 std::set<dof_id_type> & elements_connected_to_node2 = cfnemit2->second;
428 std::set_intersection(elements_connected_to_node1.begin(),
429 elements_connected_to_node1.end(),
430 elements_connected_to_node2.begin(),
431 elements_connected_to_node2.end(),
432 std::inserter(common_elements, common_elements.end()));
434 if (common_elements.size() > 0)
436 std::vector<dof_id_type> my_line_elem;
437 my_line_elem.push_back(cfnemit->first);
438 my_line_elem.push_back(cfnemit2->first);
439 node_to_line_elem_map[cfnemit->first].push_back(line_elems.size());
440 node_to_line_elem_map[cfnemit2->first].push_back(line_elems.size());
441 line_elems.push_back(my_line_elem);
447 std::vector<dof_id_type> end_nodes;
448 for (std::map<dof_id_type, std::vector<dof_id_type>>::iterator nlemit =
449 node_to_line_elem_map.begin();
450 nlemit != node_to_line_elem_map.end();
453 unsigned int num_connected_elems = nlemit->second.size();
454 if (num_connected_elems == 1)
455 end_nodes.push_back(nlemit->first);
456 else if (num_connected_elems != 2)
458 "Node ", nlemit->first,
" is connected to >2 line segments in CrackFrontDefinition");
462 if (end_nodes.size() == 0)
467 mooseError(
"In CrackFrontDefinition, end_direction_method cannot be CrackDirectionVector "
468 "for a closed-loop crack");
470 mooseError(
"In CrackFrontDefinition, intersecting_boundary cannot be specified for a "
471 "closed-loop crack");
473 else if (end_nodes.size() == 2)
476 mooseError(
"In CrackFrontDefinition wrong number of end nodes. Number end nodes = ",
482 dof_id_type last_node = end_nodes[0];
483 dof_id_type second_last_node = last_node;
484 while (last_node != end_nodes[1])
486 std::vector<dof_id_type> & curr_node_line_elems = node_to_line_elem_map[last_node];
487 bool found_new_node =
false;
488 for (
unsigned int i = 0; i < curr_node_line_elems.size(); ++i)
490 std::vector<dof_id_type> curr_line_elem = line_elems[curr_node_line_elems[i]];
491 for (
unsigned int j = 0; j < curr_line_elem.size(); ++j)
493 dof_id_type line_elem_node = curr_line_elem[j];
495 (last_node == end_nodes[0] &&
496 line_elem_node == end_nodes[1]))
498 if (line_elem_node != last_node && line_elem_node != second_last_node)
501 found_new_node =
true;
508 second_last_node = last_node;
519 Node & node0 =
_mesh.nodeRef(end_nodes[0]);
520 Node & node1 =
_mesh.nodeRef(end_nodes[1]);
522 unsigned int num_positive_coor0 = 0;
523 unsigned int num_positive_coor1 = 0;
524 Real dist_from_origin0 = 0.0;
525 Real dist_from_origin1 = 0.0;
526 for (
unsigned int i = 0; i < 3; ++i)
528 dist_from_origin0 += node0(i) * node0(i);
529 dist_from_origin1 += node1(i) * node1(i);
530 if (MooseUtils::absoluteFuzzyGreaterThan(node0(i), 0.0,
_tol))
531 ++num_positive_coor0;
532 if (MooseUtils::absoluteFuzzyGreaterThan(node1(i), 0.0,
_tol))
533 ++num_positive_coor1;
535 dist_from_origin0 = std::sqrt(dist_from_origin0);
536 dist_from_origin1 = std::sqrt(dist_from_origin1);
538 bool switch_ends =
false;
539 if (num_positive_coor1 > num_positive_coor0)
545 if (!MooseUtils::absoluteFuzzyEqual(dist_from_origin1, dist_from_origin0,
_tol))
547 if (dist_from_origin1 < dist_from_origin0)
552 if (end_nodes[1] < end_nodes[0])
558 unsigned int tmp_node = end_nodes[1];
559 end_nodes[1] = end_nodes[0];
560 end_nodes[0] = tmp_node;
566 std::vector<dof_id_type> & end_nodes,
567 std::set<dof_id_type> & nodes,
568 std::map<dof_id_type, std::vector<dof_id_type>> & node_to_line_elem_map,
569 std::vector<std::vector<dof_id_type>> & line_elems)
571 unsigned int max_dist_node = 0;
572 Real min_dist = std::numeric_limits<Real>::max();
573 Real max_dist = -std::numeric_limits<Real>::max();
576 for (std::set<dof_id_type>::iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
578 Node & node =
_mesh.nodeRef(*nit);
579 Real dist = node.norm();
583 max_dist_node = *nit;
585 else if (dist < min_dist)
589 unsigned int end_node;
590 if (MooseUtils::absoluteFuzzyGreaterThan(max_dist, min_dist,
_tol))
591 end_node = max_dist_node;
594 std::vector<Node *> node_vec;
595 for (std::set<dof_id_type>::iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
596 node_vec.push_back(
_mesh.nodePtr(*nit));
600 end_nodes.push_back(end_node);
604 std::vector<dof_id_type> end_node_line_elems = node_to_line_elem_map[end_node];
605 if (end_node_line_elems.size() != 2)
607 "Crack front nodes are in a loop, but crack end node is only connected to one other node");
608 std::vector<Node *> candidate_other_end_nodes;
610 for (
unsigned int i = 0; i < 2; ++i)
612 std::vector<dof_id_type> end_line_elem = line_elems[end_node_line_elems[i]];
613 for (
unsigned int j = 0; j < end_line_elem.size(); ++j)
615 unsigned int line_elem_node = end_line_elem[j];
616 if (line_elem_node != end_node)
617 candidate_other_end_nodes.push_back(
_mesh.nodePtr(line_elem_node));
620 if (candidate_other_end_nodes.size() != 2)
622 "Crack front nodes are in a loop, but crack end node is not connected to two other nodes");
623 end_nodes.push_back(
maxNodeCoor(candidate_other_end_nodes, 1));
649 mooseError(
"Invalid dir0 in CrackFrontDefinition::maxNodeCoor()");
651 Real max_coor0 = -std::numeric_limits<Real>::max();
652 std::vector<Node *> max_coor0_nodes;
653 for (
unsigned int i = 0; i < nodes.size(); ++i)
655 Real coor0 = (*nodes[i])(dirs[0]);
656 if (coor0 > max_coor0)
659 for (
unsigned int i = 0; i < nodes.size(); ++i)
661 Real coor0 = (*nodes[i])(dirs[0]);
662 if (MooseUtils::absoluteFuzzyEqual(coor0, max_coor0,
_tol))
663 max_coor0_nodes.push_back(nodes[i]);
665 if (max_coor0_nodes.size() > 1)
667 Real max_coor1 = -std::numeric_limits<Real>::max();
668 std::vector<Node *> max_coor1_nodes;
669 for (
unsigned int i = 0; i < nodes.size(); ++i)
671 Real coor1 = (*nodes[i])(dirs[1]);
672 if (coor1 > max_coor1)
675 for (
unsigned int i = 0; i < nodes.size(); ++i)
677 Real coor1 = (*nodes[i])(dirs[1]);
678 if (MooseUtils::absoluteFuzzyEqual(coor1, max_coor1,
_tol))
679 max_coor1_nodes.push_back(nodes[i]);
681 if (max_coor1_nodes.size() > 1)
683 Real max_coor2 = -std::numeric_limits<Real>::max();
684 std::vector<Node *> max_coor2_nodes;
685 for (
unsigned int i = 0; i < nodes.size(); ++i)
687 Real coor2 = (*nodes[i])(dirs[2]);
688 if (coor2 > max_coor2)
691 for (
unsigned int i = 0; i < nodes.size(); ++i)
693 Real coor2 = (*nodes[i])(dirs[2]);
694 if (MooseUtils::absoluteFuzzyEqual(coor2, max_coor2,
_tol))
695 max_coor2_nodes.push_back(nodes[i]);
697 if (max_coor2_nodes.size() > 1)
698 mooseError(
"Multiple nodes with same x,y,z coordinates within tolerance");
700 return max_coor2_nodes[0]->id();
703 return max_coor1_nodes[0]->id();
706 return max_coor0_nodes[0]->id();
723 RealVectorValue tangent_direction;
724 RealVectorValue crack_direction;
733 rot_mat.fillRow(0, crack_direction);
751 RealVectorValue back_segment;
752 Real back_segment_len = 0.0;
756 back_segment_len = back_segment.norm();
759 for (
unsigned int i = 0; i < num_crack_front_points; ++i)
766 else if (i == num_crack_front_points - 1)
771 RealVectorValue forward_segment;
772 Real forward_segment_len;
774 forward_segment_len = 0.0;
775 else if (
_closed_loop && i == num_crack_front_points - 1)
778 forward_segment_len = forward_segment.norm();
783 forward_segment_len = forward_segment.norm();
787 _segment_lengths.push_back(std::make_pair(back_segment_len, forward_segment_len));
793 RealVectorValue tangent_direction = back_segment + forward_segment;
794 tangent_direction = tangent_direction / tangent_direction.norm();
805 back_segment = forward_segment;
806 back_segment_len = forward_segment_len;
812 unsigned int mid_id = (num_crack_front_points - 1) / 2;
816 RealVectorValue zero_vec(0.0);
818 mooseError(
"Crack plane normal vector evaluates to zero");
826 Real hyp = origin_to_first_node.norm();
827 RealVectorValue norm_origin_to_first_node = origin_to_first_node / hyp;
828 RealVectorValue tangent_to_first_node = -norm_origin_to_first_node.cross(
_crack_plane_normal);
829 tangent_to_first_node /= tangent_to_first_node.norm();
831 for (
unsigned int i = 0; i < num_crack_front_points; ++i)
835 Real adj = origin_to_curr_node * norm_origin_to_first_node;
836 Real opp = origin_to_curr_node * tangent_to_first_node;
838 Real angle = acos(adj / hyp) * 180.0 / libMesh::pi;
840 angle = 360.0 - angle;
845 if (num_crack_front_points > 1)
854 if (MooseUtils::absoluteFuzzyEqual(
858 else if (MooseUtils::absoluteFuzzyEqual(
868 for (
unsigned int i = 0; i < num_crack_front_points; ++i)
877 _console <<
"Summary of J-Integral crack front geometry:" << std::endl;
878 _console <<
"index node id x coord y coord z coord x dir y dir "
879 " z dir angle position seg length"
881 for (
unsigned int i = 0; i < num_crack_front_points; ++i)
883 unsigned int point_id;
888 _console << std::left << std::setw(8) << i + 1 << std::setw(10) << point_id << std::setw(14)
896 _console << std::left << std::setw(14) <<
"--";
911 std::set<Node *> crack_mouth_nodes;
912 ConstBndNodeRange & bnd_nodes = *
_mesh.getBoundaryNodeRange();
913 for (ConstBndNodeRange::const_iterator nd = bnd_nodes.begin(); nd != bnd_nodes.end(); ++nd)
915 const BndNode * bnode = *nd;
916 BoundaryID boundary_id = bnode->_bnd_id;
922 crack_mouth_nodes.insert(bnode->_node);
928 for (std::set<Node *>::iterator nit = crack_mouth_nodes.begin(); nit != crack_mouth_nodes.end();
947 mooseError(
"Crack front must contain at least 3 nodes to use CurvedCrackFront option");
949 unsigned int start_id;
956 mid_id = (num_points - 1) / 3;
962 mid_id = (num_points - 1) / 2;
963 end_id = num_points - 1;
970 RealVectorValue v1 = *mid - *start;
971 RealVectorValue v2 = *end - *mid;
978 RealVectorValue zero_vec(0.0);
981 mooseError(
"Nodes on crack front are too close to being collinear");
988 const RealVectorValue & tangent_direction,
991 RealVectorValue crack_dir;
992 RealVectorValue zero_vec(0.0);
994 bool calc_dir =
true;
1019 mooseError(
"Crack mouth too close to crack front node");
1023 RealVectorValue crack_plane_normal = mouth_to_front.cross(tangent_direction);
1024 if (crack_plane_normal.absolute_fuzzy_equals(zero_vec,
_tol))
1027 "Vector from crack mouth to crack front node is collinear with crack front segment");
1030 crack_dir = tangent_direction.cross(crack_plane_normal);
1031 Real dotprod = crack_dir * mouth_to_front;
1034 crack_dir = -crack_dir;
1042 crack_dir = crack_dir.unit();
1052 mooseAssert(crack_front_node != NULL,
"invalid crack front node");
1053 return crack_front_node;
1070 const RealVectorValue &
1089 const RealVectorValue &
1111 mooseError(
"In CrackFrontDefinition, Requested angle along crack front, but not available. "
1112 "Must specify crack_mouth_boundary.");
1127 const unsigned int point_index)
const
1134 const unsigned int point_index)
const
1136 RealVectorValue vec =
_rot_matrix[point_index].transpose() * vector;
1142 const unsigned int point_index)
const
1151 const unsigned int point_index,
1156 Point closest_point(0.0);
1157 RealVectorValue closest_point_to_p;
1162 RealVectorValue crack_front_edge =
1166 p_rot = p_rot - crack_front_point_rot;
1175 p_rot(2) = closest_point(2);
1176 closest_point_to_p = p_rot;
1179 RealVectorValue r_vec = p_rot;
1185 Real min_dist = std::numeric_limits<Real>::max();
1186 for (
unsigned int pit = 0; pit != num_points; ++pit)
1189 RealVectorValue crack_point_to_current_point = qp - *crack_front_point;
1190 Real dist = crack_point_to_current_point.norm();
1192 if (dist < min_dist)
1195 closest_point = *crack_front_point;
1201 closest_point = closest_point - crack_front_point_rot;
1204 Real edge_length_sq = crack_front_edge.norm_sq();
1205 closest_point_to_p = p_rot - closest_point;
1206 Real perp = crack_front_edge * closest_point_to_p;
1207 Real dist_along_edge = perp / edge_length_sq;
1208 RealVectorValue point_on_edge = closest_point + crack_front_edge * dist_along_edge;
1209 RealVectorValue r_vec = p_rot - point_on_edge;
1215 Real p_to_plane_dist = std::abs(closest_point_to_p * crack_plane_normal);
1218 Real y_local = p_rot(1) - closest_point(1);
1221 RealVectorValue p2(p_rot);
1223 RealVectorValue p2_vec = p2 - closest_point;
1224 Real ahead = crack_front_edge(2) * p2_vec(0) - crack_front_edge(0) * p2_vec(2);
1236 Real theta_quadrant1(0.0);
1237 if (MooseUtils::absoluteFuzzyEqual(r, p_to_plane_dist,
_tol))
1238 theta_quadrant1 = 0.5 * libMesh::pi;
1239 else if (p_to_plane_dist > r)
1241 "Invalid distance p_to_plane_dist in CrackFrontDefinition::calculateRThetaToCrackFront");
1243 theta_quadrant1 = std::asin(p_to_plane_dist / r);
1245 if (x_local >= 0 && y_local >= 0)
1246 theta = theta_quadrant1;
1248 else if (x_local < 0 && y_local >= 0)
1249 theta = libMesh::pi - theta_quadrant1;
1251 else if (x_local < 0 && y_local < 0)
1252 theta = -(libMesh::pi - theta_quadrant1);
1254 else if (x_local >= 0 && y_local < 0)
1255 theta = -theta_quadrant1;
1260 mooseError(
"Invalid distance r in CrackFrontDefinition::calculateRThetaToCrackFront");
1269 Real min_dist = std::numeric_limits<Real>::max();
1270 unsigned int point_index = 0;
1271 for (
unsigned int pit = 0; pit != num_points; ++pit)
1274 RealVectorValue crack_point_to_current_point = qp - *crack_front_point;
1275 Real dist = crack_point_to_current_point.norm();
1277 if (dist < min_dist)
1292 bool is_on_boundary =
false;
1293 mooseAssert(node,
"Invalid node");
1294 dof_id_type node_id = node->id();
1299 is_on_boundary =
true;
1303 return is_on_boundary;
1309 bool is_on_boundary =
false;
1320 if (point_index == 0 || point_index == num_crack_front_points - 1)
1321 is_on_boundary =
true;
1323 return is_on_boundary;
1329 RealVectorValue disp_current_node;
1330 RealVectorValue disp_previous_node;
1331 RealVectorValue disp_next_node;
1333 RealVectorValue forward_segment0;
1334 RealVectorValue forward_segment1;
1335 Real forward_segment0_len;
1336 Real forward_segment1_len;
1337 RealVectorValue back_segment0;
1338 RealVectorValue back_segment1;
1339 Real back_segment0_len;
1340 Real back_segment1_len;
1343 const Node * current_node;
1344 const Node * previous_node;
1345 const Node * next_node;
1349 for (
unsigned int i = 0; i < num_crack_front_nodes; ++i)
1352 MooseVariable & disp_x_var = _subproblem.getStandardVariable(_tid,
_disp_x_var_name);
1353 MooseVariable & disp_y_var = _subproblem.getStandardVariable(_tid,
_disp_y_var_name);
1354 MooseVariable & disp_z_var = _subproblem.getStandardVariable(_tid,
_disp_z_var_name);
1357 if (current_node->processor_id() == processor_id())
1359 disp_current_node(0) = disp_x_var.getNodalValue(*current_node);
1360 disp_current_node(1) = disp_y_var.getNodalValue(*current_node);
1361 disp_current_node(2) = disp_z_var.getNodalValue(*current_node);
1364 disp_next_node(0) = disp_x_var.getNodalValue(*next_node);
1365 disp_next_node(1) = disp_y_var.getNodalValue(*next_node);
1366 disp_next_node(2) = disp_z_var.getNodalValue(*next_node);
1368 forward_segment0 = *next_node - *current_node;
1370 forward_segment0_len = forward_segment0.norm();
1372 forward_segment1 = (*next_node + disp_next_node) - (*current_node + disp_current_node);
1374 forward_segment1_len = forward_segment1.norm();
1376 _strain_along_front[0] = (forward_segment1_len - forward_segment0_len) / forward_segment0_len;
1379 for (
unsigned int i = 1; i < num_crack_front_nodes - 1; ++i)
1382 if (current_node->processor_id() == processor_id())
1384 disp_current_node(0) = disp_x_var.getNodalValue(*current_node);
1385 disp_current_node(1) = disp_y_var.getNodalValue(*current_node);
1386 disp_current_node(2) = disp_z_var.getNodalValue(*current_node);
1389 disp_previous_node(0) = disp_x_var.getNodalValue(*previous_node);
1390 disp_previous_node(1) = disp_y_var.getNodalValue(*previous_node);
1391 disp_previous_node(2) = disp_z_var.getNodalValue(*previous_node);
1394 disp_next_node(0) = disp_x_var.getNodalValue(*next_node);
1395 disp_next_node(1) = disp_y_var.getNodalValue(*next_node);
1396 disp_next_node(2) = disp_z_var.getNodalValue(*next_node);
1398 back_segment0 = *current_node - *previous_node;
1400 back_segment0_len = back_segment0.norm();
1402 back_segment1 = (*current_node + disp_current_node) - (*previous_node + disp_previous_node);
1404 back_segment1_len = back_segment1.norm();
1406 forward_segment0 = *next_node - *current_node;
1408 forward_segment0_len = forward_segment0.norm();
1410 forward_segment1 = (*next_node + disp_next_node) - (*current_node + disp_current_node);
1412 forward_segment1_len = forward_segment1.norm();
1415 0.5 * ((back_segment1_len - back_segment0_len) / back_segment0_len +
1416 (forward_segment1_len - forward_segment0_len) / forward_segment0_len);
1421 if (current_node->processor_id() == processor_id())
1423 disp_current_node(0) = disp_x_var.getNodalValue(*current_node);
1424 disp_current_node(1) = disp_y_var.getNodalValue(*current_node);
1425 disp_current_node(2) = disp_z_var.getNodalValue(*current_node);
1428 disp_previous_node(0) = disp_x_var.getNodalValue(*previous_node);
1429 disp_previous_node(1) = disp_y_var.getNodalValue(*previous_node);
1430 disp_previous_node(2) = disp_z_var.getNodalValue(*previous_node);
1432 back_segment0 = *current_node - *previous_node;
1435 back_segment0_len = back_segment0.norm();
1437 back_segment1 = (*current_node + disp_current_node) - (*previous_node + disp_previous_node);
1440 back_segment1_len = back_segment1.norm();
1443 (back_segment1_len - back_segment0_len) / back_segment0_len;
1454 mooseError(
"In CrackFrontDefinition, tangential strain not available");
1467 std::vector<std::vector<const Elem *>> nodes_to_elem_map;
1468 MeshTools::build_nodes_to_elem_map(
_mesh.getMesh(), nodes_to_elem_map);
1470 std::set<dof_id_type> nodes_prev_ring;
1473 std::set<dof_id_type> connected_nodes_this_cfn;
1477 std::set<dof_id_type> old_ring_nodes_this_cfn = connected_nodes_this_cfn;
1480 std::pair<dof_id_type, unsigned int> node_ring_index =
1483 connected_nodes_this_cfn.end());
1486 for (
unsigned int ring = 2; ring <=
_last_ring; ++ring)
1490 std::set<dof_id_type> new_ring_nodes_this_cfn;
1491 for (std::set<dof_id_type>::iterator nit = old_ring_nodes_this_cfn.begin();
1492 nit != old_ring_nodes_this_cfn.end();
1495 std::vector<const Node *> neighbors;
1496 MeshTools::find_nodal_neighbors(
1497 _mesh.getMesh(),
_mesh.nodeRef(*nit), nodes_to_elem_map, neighbors);
1498 for (
unsigned int inei = 0; inei < neighbors.size(); ++inei)
1500 std::set<dof_id_type>::iterator thisit =
1501 connected_nodes_this_cfn.find(neighbors[inei]->
id());
1504 if (thisit == connected_nodes_this_cfn.end())
1505 new_ring_nodes_this_cfn.insert(neighbors[inei]->id());
1510 connected_nodes_this_cfn.insert(new_ring_nodes_this_cfn.begin(),
1511 new_ring_nodes_this_cfn.end());
1512 old_ring_nodes_this_cfn = new_ring_nodes_this_cfn;
1514 std::pair<dof_id_type, unsigned int> node_ring_index =
1517 connected_nodes_this_cfn.end());
1523 std::vector<std::vector<const Elem *>> nodes_to_elem_map;
1524 MeshTools::build_nodes_to_elem_map(
_mesh.getMesh(), nodes_to_elem_map);
1525 for (
unsigned int icfn = 0; icfn < num_crack_front_points; ++icfn)
1527 std::set<dof_id_type> nodes_prev_ring;
1530 std::set<dof_id_type> connected_nodes_prev_cfn;
1531 std::set<dof_id_type> connected_nodes_this_cfn;
1532 std::set<dof_id_type> connected_nodes_next_cfn;
1541 else if (
_closed_loop && icfn == num_crack_front_points - 1)
1550 else if (icfn == num_crack_front_points - 1)
1560 std::set<dof_id_type> old_ring_nodes_prev_cfn = connected_nodes_prev_cfn;
1561 std::set<dof_id_type> old_ring_nodes_this_cfn = connected_nodes_this_cfn;
1562 std::set<dof_id_type> old_ring_nodes_next_cfn = connected_nodes_next_cfn;
1565 std::pair<dof_id_type, unsigned int> node_ring_index =
1568 connected_nodes_this_cfn.end());
1571 for (
unsigned int ring = 2; ring <=
_last_ring; ++ring)
1576 std::set<dof_id_type> new_ring_nodes_this_cfn;
1578 old_ring_nodes_this_cfn,
1579 connected_nodes_this_cfn,
1580 connected_nodes_prev_cfn,
1581 connected_nodes_next_cfn,
1584 std::set<dof_id_type> new_ring_nodes_prev_cfn;
1586 old_ring_nodes_prev_cfn,
1587 connected_nodes_prev_cfn,
1588 connected_nodes_this_cfn,
1589 connected_nodes_next_cfn,
1592 std::set<dof_id_type> new_ring_nodes_next_cfn;
1594 old_ring_nodes_next_cfn,
1595 connected_nodes_next_cfn,
1596 connected_nodes_prev_cfn,
1597 connected_nodes_this_cfn,
1601 connected_nodes_prev_cfn.insert(new_ring_nodes_prev_cfn.begin(),
1602 new_ring_nodes_prev_cfn.end());
1603 connected_nodes_this_cfn.insert(new_ring_nodes_this_cfn.begin(),
1604 new_ring_nodes_this_cfn.end());
1605 connected_nodes_next_cfn.insert(new_ring_nodes_next_cfn.begin(),
1606 new_ring_nodes_next_cfn.end());
1607 old_ring_nodes_prev_cfn = new_ring_nodes_prev_cfn;
1608 old_ring_nodes_this_cfn = new_ring_nodes_this_cfn;
1609 old_ring_nodes_next_cfn = new_ring_nodes_next_cfn;
1611 std::pair<dof_id_type, unsigned int> node_ring_index =
1614 connected_nodes_this_cfn.end());
1622 std::set<dof_id_type> & nodes_new_ring,
1623 const std::set<dof_id_type> & nodes_old_ring,
1624 const std::set<dof_id_type> & nodes_all_rings,
1625 const std::set<dof_id_type> & nodes_neighbor1,
1626 const std::set<dof_id_type> & nodes_neighbor2,
1627 std::vector<std::vector<const Elem *>> & nodes_to_elem_map)
1629 for (std::set<dof_id_type>::const_iterator nit = nodes_old_ring.begin();
1630 nit != nodes_old_ring.end();
1633 std::vector<const Node *> neighbors;
1634 MeshTools::find_nodal_neighbors(
1635 _mesh.getMesh(),
_mesh.nodeRef(*nit), nodes_to_elem_map, neighbors);
1636 for (
unsigned int inei = 0; inei < neighbors.size(); ++inei)
1638 std::set<dof_id_type>::const_iterator previt = nodes_all_rings.find(neighbors[inei]->
id());
1639 std::set<dof_id_type>::const_iterator thisit = nodes_neighbor1.find(neighbors[inei]->
id());
1640 std::set<dof_id_type>::const_iterator nextit = nodes_neighbor2.find(neighbors[inei]->
id());
1643 if (previt == nodes_all_rings.end() && thisit == nodes_neighbor1.end() &&
1644 nextit == nodes_neighbor2.end())
1645 nodes_new_ring.insert(neighbors[inei]->id());
1652 const dof_id_type connected_node_id,
1653 const unsigned int node_index)
const
1655 bool is_node_in_ring =
false;
1656 std::pair<dof_id_type, unsigned int> node_ring_key =
1658 std::map<std::pair<dof_id_type, unsigned int>, std::set<dof_id_type>>::const_iterator nnmit =
1662 mooseError(
"Could not find crack front node ",
1664 "in the crack front node to q-function ring-node map for ring ",
1667 std::set<dof_id_type> q_func_nodes = nnmit->second;
1668 if (q_func_nodes.find(connected_node_id) != q_func_nodes.end())
1669 is_node_in_ring =
true;
1671 return is_node_in_ring;
1676 unsigned int ring_index,
1677 const Node *
const current_node)
const
1679 Real dist_to_crack_front;
1680 Real dist_along_tangent;
1682 dist_to_crack_front, dist_along_tangent, crack_front_point_index, current_node);
1694 Real tangent_multiplier = 1.0;
1697 const Real forward_segment_length =
1699 const Real backward_segment_length =
1702 if (dist_along_tangent >= 0.0)
1704 if (forward_segment_length > 0.0)
1705 tangent_multiplier = 1.0 - dist_along_tangent / forward_segment_length;
1709 if (backward_segment_length > 0.0)
1710 tangent_multiplier = 1.0 + dist_along_tangent / backward_segment_length;
1714 tangent_multiplier = std::max(tangent_multiplier, 0.0);
1715 tangent_multiplier = std::min(tangent_multiplier, 1.0);
1720 tangent_multiplier = 0.0;
1722 q *= tangent_multiplier;
1730 unsigned int ring_index,
1731 const Node *
const current_node)
const
1734 bool is_node_in_ring =
isNodeInRing(ring_index, current_node->id(), crack_front_point_index);
1735 if (is_node_in_ring)
1743 Real & dist_along_tangent,
1744 unsigned int crack_front_point_index,
1745 const Node *
const current_node)
const
1749 Point p = *current_node;
1752 RealVectorValue crack_node_to_current_node = p - *crack_front_point;
1753 dist_along_tangent = crack_node_to_current_node * crack_front_tangent;
1754 RealVectorValue projection_point = *crack_front_point + dist_along_tangent * crack_front_tangent;
1755 RealVectorValue axis_to_current_node = p - projection_point;
1756 dist_to_front = axis_to_current_node.norm();