18#include "libmesh/mesh_tools.h"
19#include "libmesh/string_to_enum.h"
20#include "libmesh/quadrature.h"
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;
35 Moose::RelationshipManagerType::ALGEBRAIC,
37 { rm_params.
set<
unsigned short>(
"layers") = 2; });
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",
52 "Method to determine direction of crack propagation. Choices are: " +
55 "crack_end_direction_method",
57 "Method to determine direction of crack propagation at ends of crack. Choices are: " +
59 params.
addParam<RealVectorValue>(
"crack_direction_vector",
"Direction of crack propagation");
61 "crack_direction_vector_end_1",
62 "Direction of crack propagation for the node at end 1 of the crack");
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");
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");
93 "j_integral_radius_inner", {},
"Radius for J-Integral calculation");
95 "j_integral_radius_outer", {},
"Radius for J-Integral calculation");
96 MooseEnum q_function_type(
"Geometry Topology",
"Geometry");
99 "The method used to define the integration domain. Options are: " +
100 q_function_type.getRawNames());
102 "crack_front_points_provider",
103 "The UserObject provides the crack front points from XFEM GeometricCutObject");
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.");
109 "crack_front_node_tolerance",
111 "General tolerance for locating nodes on the crack front based on xyz coordinates.");
118 _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"))
136 auto boundary =
isParamValid(
"boundary") ? getParam<std::vector<BoundaryName>>(
"boundary")
137 : std::vector<BoundaryName>{};
142 "CrackFrontDefinition error: since boundary is defined, crack_front_points should "
146 "As crack_front_points have been provided, the crack_front_points_provider will "
147 "not be used and needs to be removed.");
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");
159 "CrackFrontDefinition error: since boundary is defined, "
160 "crack_front_points_provider should not be added.");
166 "CrackFrontDefinition error: number_points_from_provider is provided but "
167 "crack_front_points_provider cannot be found.");
168 else if (boundary.size())
173 "In CrackFrontDefinition, if 'boundary' is defined, 'closed_loop' should not be "
174 "set by user because closed loops are detected automatically");
177 mooseError(
"In CrackFrontDefinition, must define one of 'boundary', 'crack_front_points' "
178 "and 'crack_front_points_provider'");
186 "symmetry_plane out of bounds: ",
188 " Must be >=0 and <=2.");
195 "crack_direction_vector must be specified if crack_direction_method = "
196 "CrackDirectionVector");
202 "crack_direction_vector must not be specified if crack_direction_method = "
206 "crack_mouth_boundary",
207 "crack_mouth_boundary must be specified if crack_direction_method = CrackMouthNodes");
212 "crack_direction_vector must not be specified if crack_direction_method = "
216 paramError(
"crack_direction_method",
"Invalid direction_method");
226 "crack_direction_vector_end_1 must be specified if crack_end_direction_method = "
227 "CrackDirectionVector");
230 "crack_direction_vector_end_2 must be specified if crack_end_direction_method = "
231 "CrackDirectionVector");
240 "crack_tangent_vector_end_1 must be specified if crack_end_tangent_method = "
241 "CrackTangentVector");
244 "crack_tangent_vector_end_2 must be specified if crack_end_tangent_method = "
245 "CrackTangentVector");
257 paramError(
"displacements",
"Displacement variables must be provided for T-stress calculation");
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");
290 getParam<UserObjectName>(
"crack_front_points_provider"));
297 mooseInfo(
"CrackFrontDefinition: Automatically detected ",
299 " crack front points from mesh-based provider");
303 "Using a `crack_front_points_provider` that uses an XFEM cutter mesh also "
304 "requires setting 'crack_direction_method = CurvedCrackFront'");
307 "'crack_mouth_boundary' cannot be set when using a "
308 "'crack_front_points_provider' that uses an XFEM cutter mesh");
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.");
333 std::set<dof_id_type> nodes;
339 paramError(
"intersecting_boundary",
"Cannot use intersecting_boundary with closed-loop cracks");
349 for (std::size_t i = 0; i < num_crack_front_nodes; ++i)
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)
383 for (std::size_t i = 0; i < num_crack_front_points; ++i)
402 for (
auto nd = bnd_nodes.
begin(); nd != bnd_nodes.
end(); ++nd)
408 nodes.insert(bnode->
_node->
id());
413 if (nodes.size() > 1)
440 for (
auto sit = nodes.begin(); sit != nodes.end(); ++sit)
443 if (sit == nodes.begin())
445 node0coor0 = curr_node(axis0);
446 node0coor1 = curr_node(axis1);
450 if (!MooseUtils::absoluteFuzzyEqual(curr_node(axis0), node0coor0,
_tol) ||
451 !MooseUtils::absoluteFuzzyEqual(curr_node(axis1), node0coor1,
_tol))
452 mooseError(
"Boundary provided in CrackFrontDefinition contains ",
454 " nodes, which are not collinear in the ",
456 " axis. Must contain either 1 node or collinear nodes to treat as 2D.");
467 if (nodes.size() < 1)
469 else if (nodes.size() == 1)
473 mooseError(
"Boundary provided in CrackFrontDefinition contains 1 node, but model is not "
492 std::map<dof_id_type, std::set<dof_id_type>> crack_front_node_to_elem_map;
494 for (
const auto & node_id : nodes)
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");
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]);
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;
511 for (
auto cfnemit = crack_front_node_to_elem_map.begin();
512 cfnemit != crack_front_node_to_elem_map.end();
515 auto cfnemit2 = cfnemit;
516 for (++cfnemit2; cfnemit2 != crack_front_node_to_elem_map.end(); ++cfnemit2)
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()));
528 if (common_elements.size() > 0)
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);
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();
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");
554 if (end_nodes.size() == 0)
561 "In CrackFrontDefinition, end_direction_method cannot be CrackDirectionVector "
562 "or CrackTangentVector for a closed-loop crack");
565 "In CrackFrontDefinition, intersecting_boundary cannot be specified for a "
566 "closed-loop crack");
568 else if (end_nodes.size() == 2)
571 mooseError(
"In CrackFrontDefinition wrong number of end nodes. Number end nodes = ",
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])
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)
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)
588 dof_id_type line_elem_node = curr_line_elem[j];
590 (last_node == end_nodes[0] &&
591 line_elem_node == end_nodes[1]))
593 if (line_elem_node != last_node && line_elem_node != second_last_node)
596 found_new_node =
true;
603 second_last_node = last_node;
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)
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;
630 dist_from_origin0 = std::sqrt(dist_from_origin0);
631 dist_from_origin1 = std::sqrt(dist_from_origin1);
633 bool switch_ends =
false;
634 if (num_positive_coor1 > num_positive_coor0)
640 if (!MooseUtils::absoluteFuzzyEqual(dist_from_origin1, dist_from_origin0,
_tol))
642 if (dist_from_origin1 < dist_from_origin0)
647 if (end_nodes[1] < end_nodes[0])
653 std::size_t tmp_node = end_nodes[1];
654 end_nodes[1] = end_nodes[0];
655 end_nodes[0] = tmp_node;
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)
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();
671 for (
auto nit = nodes.begin(); nit != nodes.end(); ++nit)
674 Real dist = node.norm();
678 max_dist_node = *nit;
680 else if (dist < min_dist)
684 dof_id_type end_node;
685 if (MooseUtils::absoluteFuzzyGreaterThan(max_dist, min_dist,
_tol))
686 end_node = max_dist_node;
689 std::vector<Node *> node_vec;
690 for (
auto nit = nodes.begin(); nit != nodes.end(); ++nit)
695 end_nodes.push_back(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;
705 for (std::size_t i = 0; i < 2; ++i)
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)
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));
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));
744 mooseError(
"Invalid dir0 in CrackFrontDefinition::maxNodeCoor()");
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)
750 Real coor0 = (*nodes[i])(dirs[0]);
751 if (coor0 > max_coor0)
754 for (std::size_t i = 0; i < nodes.size(); ++i)
756 Real coor0 = (*nodes[i])(dirs[0]);
757 if (MooseUtils::absoluteFuzzyEqual(coor0, max_coor0,
_tol))
758 max_coor0_nodes.push_back(nodes[i]);
760 if (max_coor0_nodes.size() > 1)
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)
766 Real coor1 = (*nodes[i])(dirs[1]);
767 if (coor1 > max_coor1)
770 for (std::size_t i = 0; i < nodes.size(); ++i)
772 Real coor1 = (*nodes[i])(dirs[1]);
773 if (MooseUtils::absoluteFuzzyEqual(coor1, max_coor1,
_tol))
774 max_coor1_nodes.push_back(nodes[i]);
776 if (max_coor1_nodes.size() > 1)
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)
782 Real coor2 = (*nodes[i])(dirs[2]);
783 if (coor2 > max_coor2)
786 for (std::size_t i = 0; i < nodes.size(); ++i)
788 Real coor2 = (*nodes[i])(dirs[2]);
789 if (MooseUtils::absoluteFuzzyEqual(coor2, max_coor2,
_tol))
790 max_coor2_nodes.push_back(nodes[i]);
792 if (max_coor2_nodes.size() > 1)
793 mooseError(
"Multiple nodes with same x,y,z coordinates within tolerance");
795 return max_coor2_nodes[0]->id();
798 return max_coor1_nodes[0]->id();
801 return max_coor0_nodes[0]->id();
828 for (std::size_t i = 0; i < num_crack_front_points; ++i)
830 RealVectorValue tangent_direction;
831 RealVectorValue crack_direction;
841 rot_mat.
fillRow(0, crack_direction);
870 RealVectorValue back_segment;
871 Real back_segment_len = 0.0;
875 back_segment_len = back_segment.norm();
878 for (std::size_t i = 0; i < num_crack_front_points; ++i)
885 else if (i == num_crack_front_points - 1)
890 RealVectorValue forward_segment;
891 Real forward_segment_len;
893 forward_segment_len = 0.0;
894 else if (
_closed_loop && i == num_crack_front_points - 1)
897 forward_segment_len = forward_segment.norm();
902 forward_segment_len = forward_segment.norm();
906 _segment_lengths.push_back(std::make_pair(back_segment_len, forward_segment_len));
912 RealVectorValue tangent_direction = back_segment + forward_segment;
913 tangent_direction = tangent_direction / tangent_direction.
norm();
941 back_segment = forward_segment;
942 back_segment_len = forward_segment_len;
949 std::size_t mid_id = (num_crack_front_points - 1) / 2;
954 RealVectorValue zero_vec(0.0);
956 mooseError(
"Crack plane normal vector evaluates to zero");
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 =
968 tangent_to_first_node /= tangent_to_first_node.norm();
970 for (std::size_t i = 0; i < num_crack_front_points; ++i)
974 Real adj = origin_to_curr_node * norm_origin_to_first_node;
975 Real opp = origin_to_curr_node * tangent_to_first_node;
977 Real angle = acos(adj / hyp) * 180.0 /
libMesh::pi;
979 angle = 360.0 - angle;
984 if (num_crack_front_points > 1)
993 if (MooseUtils::absoluteFuzzyEqual(
997 else if (MooseUtils::absoluteFuzzyEqual(
1007 for (std::size_t i = 0; i < num_crack_front_points; ++i)
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"
1020 for (std::size_t i = 0; i < num_crack_front_points; ++i)
1022 std::size_t point_id;
1027 _console << std::left << std::setw(8) << i + 1 << std::setw(10) << point_id << std::setw(14)
1035 _console << std::left << std::setw(14) <<
"--";
1050 std::set<Node *> crack_mouth_nodes;
1052 for (
auto nd = bnd_nodes.
begin(); nd != bnd_nodes.
end(); ++nd)
1061 crack_mouth_nodes.insert(bnode->
_node);
1067 for (
auto nit = crack_mouth_nodes.begin(); nit != crack_mouth_nodes.end(); ++nit)
1089 mooseError(
"Crack front must contain at least 3 nodes to use CurvedCrackFront option");
1091 std::size_t start_id;
1098 mid_id = (num_points - 1) / 3;
1099 end_id = 2 * mid_id;
1104 mid_id = (num_points - 1) / 2;
1105 end_id = num_points - 1;
1112 RealVectorValue v1 = *mid - *start;
1113 RealVectorValue v2 = *end - *mid;
1116 Point crack_plane_normal = v1.
cross(v2);
1117 crack_plane_normal = crack_plane_normal.unit();
1121 RealVectorValue zero_vec(0.0);
1124 mooseError(
"Nodes on crack front are too close to being collinear");
1131 const RealVectorValue & tangent_direction,
1133 const std::size_t crack_front_point_index)
const
1135 RealVectorValue crack_dir;
1136 RealVectorValue zero_vec(0.0);
1138 bool calc_dir =
true;
1163 mooseError(
"Crack mouth too close to crack front node");
1167 RealVectorValue crack_plane_normal = mouth_to_front.
cross(tangent_direction);
1168 if (crack_plane_normal.absolute_fuzzy_equals(zero_vec,
_tol))
1171 "Vector from crack mouth to crack front node is collinear with crack front segment");
1174 crack_dir = tangent_direction.cross(crack_plane_normal);
1175 Real dotprod = crack_dir * mouth_to_front;
1178 crack_dir = -crack_dir;
1186 crack_dir = crack_dir.unit();
1202 mooseAssert(crack_front_node !=
nullptr,
"invalid crack front node");
1203 return crack_front_node;
1220const RealVectorValue &
1227const RealVectorValue &
1246const RealVectorValue &
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.");
1286 const std::size_t point_index)
const
1293 const std::size_t point_index)
const
1295 RealVectorValue vec =
_rot_matrix[point_index].transpose() * vector;
1301 const std::size_t point_index)
const
1310 const std::size_t point_index,
1315 Point closest_point(0.0);
1316 RealVectorValue closest_point_to_p;
1321 RealVectorValue crack_front_edge =
1325 p_rot = p_rot - crack_front_point_rot;
1334 p_rot(2) = closest_point(2);
1335 closest_point_to_p = p_rot;
1338 RealVectorValue r_vec = p_rot;
1344 Real min_dist = std::numeric_limits<Real>::max();
1345 for (std::size_t pit = 0; pit != num_points; ++pit)
1348 RealVectorValue crack_point_to_current_point = qp - *crack_front_point;
1349 Real dist = crack_point_to_current_point.norm();
1351 if (dist < min_dist)
1354 closest_point = *crack_front_point;
1360 closest_point = closest_point - crack_front_point_rot;
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;
1373 RealVectorValue crack_plane_normal =
1375 Real p_to_plane_dist = std::abs(closest_point_to_p * crack_plane_normal);
1378 Real y_local = p_rot(1) - closest_point(1);
1381 RealVectorValue p2(p_rot);
1383 RealVectorValue p2_vec = p2 - closest_point;
1384 Real ahead = crack_front_edge(2) * p2_vec(0) - crack_front_edge(0) * p2_vec(2);
1396 Real theta_quadrant1(0.0);
1397 if (MooseUtils::absoluteFuzzyEqual(r, p_to_plane_dist,
_tol))
1399 else if (p_to_plane_dist > r)
1401 "Invalid distance p_to_plane_dist in CrackFrontDefinition::calculateRThetaToCrackFront");
1403 theta_quadrant1 = std::asin(p_to_plane_dist / r);
1405 if (x_local >= 0 && y_local >= 0)
1406 theta = theta_quadrant1;
1408 else if (x_local < 0 && y_local >= 0)
1411 else if (x_local < 0 && y_local < 0)
1414 else if (x_local >= 0 && y_local < 0)
1415 theta = -theta_quadrant1;
1420 mooseError(
"Invalid distance r in CrackFrontDefinition::calculateRThetaToCrackFront");
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)
1434 RealVectorValue crack_point_to_current_point = qp - *crack_front_point;
1435 Real dist = crack_point_to_current_point.norm();
1437 if (dist < min_dist)
1452 bool is_on_boundary =
false;
1453 mooseAssert(node,
"Invalid node");
1454 dof_id_type node_id = node->id();
1459 is_on_boundary =
true;
1463 return is_on_boundary;
1469 bool is_on_boundary =
false;
1480 if (point_index == 0 || point_index == num_crack_front_points - 1)
1481 is_on_boundary =
true;
1483 return is_on_boundary;
1489 RealVectorValue disp_current_node;
1490 RealVectorValue disp_previous_node;
1491 RealVectorValue disp_next_node;
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;
1503 const Node * current_node;
1504 const Node * previous_node;
1505 const Node * next_node;
1511 for (std::size_t i = 0; i < num_crack_front_nodes; ++i)
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);
1530 forward_segment0 = *next_node - *current_node;
1532 forward_segment0_len = forward_segment0.norm();
1534 forward_segment1 = (*next_node + disp_next_node) - (*current_node + disp_current_node);
1536 forward_segment1_len = forward_segment1.norm();
1538 _strain_along_front[0] = (forward_segment1_len - forward_segment0_len) / forward_segment0_len;
1541 for (std::size_t i = 1; i < num_crack_front_nodes - 1; ++i)
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);
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);
1560 back_segment0 = *current_node - *previous_node;
1562 back_segment0_len = back_segment0.norm();
1564 back_segment1 = (*current_node + disp_current_node) - (*previous_node + disp_previous_node);
1566 back_segment1_len = back_segment1.norm();
1568 forward_segment0 = *next_node - *current_node;
1570 forward_segment0_len = forward_segment0.norm();
1572 forward_segment1 = (*next_node + disp_next_node) - (*current_node + disp_current_node);
1574 forward_segment1_len = forward_segment1.norm();
1577 0.5 * ((back_segment1_len - back_segment0_len) / back_segment0_len +
1578 (forward_segment1_len - forward_segment0_len) / forward_segment0_len);
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);
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);
1594 back_segment0 = *current_node - *previous_node;
1597 back_segment0_len = back_segment0.norm();
1599 back_segment1 = (*current_node + disp_current_node) - (*previous_node + disp_previous_node);
1602 back_segment1_len = back_segment1.norm();
1605 (back_segment1_len - back_segment0_len) / back_segment0_len;
1616 mooseAssert(strain > -std::numeric_limits<Real>::max(),
1617 "Failure in parallel communication of crack tangential strain");
1620 mooseError(
"In CrackFrontDefinition, tangential strain not available");
1632 std::vector<std::vector<const Elem *>> nodes_to_elem_map;
1633 MeshTools::build_nodes_to_elem_map(
_mesh.
getMesh(), nodes_to_elem_map);
1635 std::set<dof_id_type> nodes_prev_ring;
1638 std::set<dof_id_type> connected_nodes_this_cfn;
1642 std::set<dof_id_type> old_ring_nodes_this_cfn = connected_nodes_this_cfn;
1645 std::pair<dof_id_type, std::size_t> node_ring_index =
1648 connected_nodes_this_cfn.end());
1651 for (std::size_t ring = 2; ring <=
_last_ring; ++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)
1658 std::vector<const Node *> neighbors;
1659 MeshTools::find_nodal_neighbors(
1661 for (std::size_t inei = 0; inei < neighbors.size(); ++inei)
1663 auto thisit = connected_nodes_this_cfn.find(neighbors[inei]->
id());
1666 if (thisit == connected_nodes_this_cfn.end())
1667 new_ring_nodes_this_cfn.insert(neighbors[inei]->
id());
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;
1676 std::pair<dof_id_type, std::size_t> node_ring_index =
1679 connected_nodes_this_cfn.end());
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)
1689 std::set<dof_id_type> nodes_prev_ring;
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;
1703 else if (
_closed_loop && icfn == num_crack_front_points - 1)
1712 else if (icfn == num_crack_front_points - 1)
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;
1727 std::pair<dof_id_type, std::size_t> node_ring_index =
1730 connected_nodes_this_cfn.end());
1733 for (std::size_t ring = 2; ring <=
_last_ring; ++ring)
1738 std::set<dof_id_type> 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,
1746 std::set<dof_id_type> 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,
1754 std::set<dof_id_type> 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,
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;
1773 std::pair<dof_id_type, std::size_t> node_ring_index =
1776 connected_nodes_this_cfn.end());
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)
1791 for (
auto nit = nodes_old_ring.begin(); nit != nodes_old_ring.end(); ++nit)
1793 std::vector<const Node *> neighbors;
1794 MeshTools::find_nodal_neighbors(
1796 for (std::size_t inei = 0; inei < neighbors.size(); ++inei)
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());
1803 if (previt == nodes_all_rings.end() && thisit == nodes_neighbor1.end() &&
1804 nextit == nodes_neighbor2.end())
1805 nodes_new_ring.insert(neighbors[inei]->
id());
1812 const dof_id_type connected_node_id,
1813 const std::size_t node_index)
const
1815 bool is_node_in_ring =
false;
1816 std::pair<dof_id_type, std::size_t> node_ring_key =
1821 mooseError(
"Could not find crack front node ",
1823 " in the crack front node to q-function ring-node map for ring ",
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;
1830 return is_node_in_ring;
1835 std::size_t ring_index,
1836 const Node *
const current_node)
const
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);
1853 Real tangent_multiplier = 1.0;
1856 const Real forward_segment_length =
1858 const Real backward_segment_length =
1861 if (dist_along_tangent >= 0.0)
1863 if (forward_segment_length > 0.0)
1864 tangent_multiplier = 1.0 - dist_along_tangent / forward_segment_length;
1868 if (backward_segment_length > 0.0)
1869 tangent_multiplier = 1.0 + dist_along_tangent / backward_segment_length;
1873 tangent_multiplier = std::max(tangent_multiplier, 0.0);
1874 tangent_multiplier = std::min(tangent_multiplier, 1.0);
1879 tangent_multiplier = 0.0;
1881 q *= tangent_multiplier;
1889 std::size_t ring_index,
1890 const Node *
const current_node)
const
1893 bool is_node_in_ring =
isNodeInRing(ring_index, current_node->id(), crack_front_point_index);
1894 if (is_node_in_ring)
1902 Real & dist_along_tangent,
1903 std::size_t crack_front_point_index,
1904 const Node *
const current_node)
const
1908 Point
p = *current_node;
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();
boundary_id_type BoundaryID
registerMooseObject("SolidMechanicsApp", CrackFrontDefinition)
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 ¶ms)
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 ¶meters)
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::string _disp_y_var_name
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.
@ END_CRACK_TANGENT_VECTOR
@ END_CRACK_DIRECTION_VECTOR
std::string _disp_z_var_name
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()
const InputParameters & parameters() const
void paramError(const std::string ¶m, 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
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
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