13 #include "MooseError.h"
14 #include "libmesh/string_to_enum.h"
15 #include "MooseMesh.h"
16 #include "libmesh/face_tri3.h"
17 #include "libmesh/edge_edge2.h"
18 #include "libmesh/serial_mesh.h"
19 #include "libmesh/plane.h"
29 params.addRequiredParam<MeshFileName>(
31 "Mesh file for the XFEM geometric cut; currently only the xda type is supported");
32 params.addRequiredParam<FunctionName>(
"function_x",
"Growth function for x direction");
33 params.addRequiredParam<FunctionName>(
"function_y",
"Growth function for y direction");
34 params.addRequiredParam<FunctionName>(
"function_z",
"Growth function for z direction");
35 params.addParam<Real>(
36 "size_control", 0,
"Criterion for refining elements while growing the crack");
37 params.addParam<
unsigned int>(
"n_step_growth", 0,
"Number of steps for crack growth");
38 params.addClassDescription(
"Creates a UserObject for a mesh cutter in 3D problems");
46 _mesh(_subproblem.mesh()),
47 _n_step_growth(getParam<unsigned int>(
"n_step_growth")),
48 _func_x(getFunction(
"function_x")),
49 _func_y(getFunction(
"function_y")),
50 _func_z(getFunction(
"function_z"))
56 if (!isParamValid(
"size_control"))
57 mooseError(
"Crack growth needs size control");
63 MeshFileName xfem_cut_mesh_file = getParam<MeshFileName>(
"mesh_file");
64 _cut_mesh = libmesh_make_unique<ReplicatedMesh>(_communicator);
68 for (
const auto & cut_elem :
_cut_mesh->element_ptr_range())
71 mooseError(
"The input cut mesh should include tri elements only!");
73 mooseError(
"The input cut mesh should have 2D elements only!");
94 unsigned int timestep = _fe_problem.timeStep();
125 std::vector<Xfem::CutEdge> & ,
126 std::vector<Xfem::CutNode> & ,
129 mooseError(
"invalid method for 3D mesh cutting");
135 std::vector<Xfem::CutFace> & cut_faces,
141 bool elem_cut =
false;
143 if (elem->dim() != _elem_dim)
144 mooseError(
"The structural mesh to be cut by a surface mesh must be 3D!");
146 for (
unsigned int i = 0; i < elem->n_sides(); ++i)
149 std::unique_ptr<const Elem> curr_side = elem->side_ptr(i);
150 if (curr_side->dim() != 2)
151 mooseError(
"In cutElementByGeometry dimension of side must be 2, but it is ",
153 unsigned int n_edges = curr_side->n_sides();
155 std::vector<unsigned int> cut_edges;
156 std::vector<Real> cut_pos;
158 for (
unsigned int j = 0; j < n_edges; j++)
161 std::unique_ptr<const Elem> curr_edge = curr_side->side_ptr(j);
162 if (curr_edge->type() != EDGE2)
163 mooseError(
"In cutElementByGeometry face edge must be EDGE2, but type is: ",
164 libMesh::Utility::enum_to_string(curr_edge->type()),
165 " base element type is: ",
166 libMesh::Utility::enum_to_string(elem->type()));
167 const Node * node1 = curr_edge->node_ptr(0);
168 const Node * node2 = curr_edge->node_ptr(1);
170 for (
const auto & cut_elem : _cut_mesh->element_ptr_range())
172 std::vector<Point> vertices;
174 for (
auto & node : cut_elem->node_ref_range())
176 Point & this_point = node;
177 vertices.push_back(this_point);
181 if (intersectWithEdge(*node1, *node2, vertices, intersection))
183 cut_edges.push_back(j);
184 cut_pos.emplace_back(getRelativePosition(*node1, *node2, intersection));
190 if (cut_edges.size() == 2)
199 cut_faces.push_back(mycut);
207 std::vector<Xfem::CutEdge> & ,
210 mooseError(
"invalid method for 3D mesh cutting");
216 std::vector<Xfem::CutFace> & ,
220 mooseError(
"cutFragmentByGeometry not yet implemented for 3D mesh cutting");
227 const std::vector<Point> & vertices,
230 bool has_intersection =
false;
232 Plane elem_plane(vertices[0], vertices[1], vertices[2]);
233 Point point = vertices[0];
234 Point normal = elem_plane.unit_normal(point);
236 std::array<Real, 3> plane_point = {{point(0), point(1), point(2)}};
237 std::array<Real, 3> planenormal = {{normal(0), normal(1), normal(2)}};
238 std::array<Real, 3> edge_point1 = {{p1(0), p1(1), p1(2)}};
239 std::array<Real, 3> edge_point2 = {{p2(0), p2(1), p2(2)}};
240 std::array<Real, 3> cut_point = {{0.0, 0.0, 0.0}};
243 &plane_point[0], &planenormal[0], &edge_point1[0], &edge_point2[0], &cut_point[0]) == 1)
245 Point temp_p(cut_point[0], cut_point[1], cut_point[2]);
249 has_intersection =
true;
252 return has_intersection;
258 const std::vector<Point> & vertices,
261 bool has_intersection =
false;
263 Plane elem_plane(vertices[0], vertices[1], vertices[2]);
264 Point point = vertices[0];
265 Point normal = elem_plane.unit_normal(point);
267 std::array<Real, 3> plane_point = {{point(0), point(1), point(2)}};
268 std::array<Real, 3> planenormal = {{normal(0), normal(1), normal(2)}};
269 std::array<Real, 3> p_begin = {{p1(0), p1(1), p1(2)}};
270 std::array<Real, 3> p_end = {{p2(0), p2(1), p2(2)}};
271 std::array<Real, 3> cut_point = {{0.0, 0.0, 0.0}};
274 &plane_point[0], &planenormal[0], &p_begin[0], &p_end[0], &cut_point[0]) == 1)
276 Point p(cut_point[0], cut_point[1], cut_point[2]);
277 Real dotp = ((p - p1) * (p2 - p1)) / ((p2 - p1) * (p2 - p1));
281 has_intersection =
true;
284 return has_intersection;
290 Real dotp1 = (p1 - p) * (p2 - p1);
291 Real dotp2 = (p2 - p) * (p2 - p1);
292 return (dotp1 * dotp2 <= 0.0);
298 Real full_len = (p2 - p1).norm();
299 Real len_p1_p = (p - p1).norm();
300 return len_p1_p / full_len;
306 unsigned int n_node = vertices.size();
308 Plane elem_plane(vertices[0], vertices[1], vertices[2]);
309 Point normal = elem_plane.unit_normal(vertices[0]);
314 for (
unsigned int i = 0; i < n_node; ++i)
316 unsigned int iplus1 = (i < n_node - 1 ? i + 1 : 0);
317 Point middle2p = p - 0.5 * (vertices[i] + vertices[iplus1]);
318 const Point side_tang = vertices[iplus1] - vertices[i];
319 Point side_norm = side_tang.cross(normal);
322 if (middle2p * side_norm <= 0.0)
333 unsigned int n_nodes =
_cut_mesh->n_nodes();
334 std::vector<Real> angle(n_nodes, 0);
339 for (
const auto & cut_elem :
_cut_mesh->element_ptr_range())
343 Node * this_node = cut_elem->node_ptr(i);
344 Point & this_point = *this_node;
345 vertices[i] = this_point;
346 node_id[i] = this_node->id();
350 mooseAssert(node_id[i] < n_nodes,
"Node ID is out of range");
352 angle[node_id[0]] +=
Xfem::angle_rad_3d(&vertices[2](0), &vertices[0](0), &vertices[1](0));
353 angle[node_id[1]] +=
Xfem::angle_rad_3d(&vertices[0](0), &vertices[1](0), &vertices[2](0));
354 angle[node_id[2]] +=
Xfem::angle_rad_3d(&vertices[1](0), &vertices[2](0), &vertices[0](0));
361 for (
const auto & node :
_cut_mesh->node_ptr_range())
363 dof_id_type
id = node->id();
364 if (!MooseUtils::relativeFuzzyEqual(angle[
id], libMesh::pi * 2))
366 std::vector<dof_id_type> neighbors;
377 std::vector<dof_id_type> corner_elem_id;
383 for (
const auto & cut_elem :
_cut_mesh->element_ptr_range())
387 node_id[i] = cut_elem->node_ptr(i)->id();
391 if (is_node_on_boundary[0] && is_node_on_boundary[1] && is_node_on_boundary[2])
395 corner_elem_id.push_back(
counter);
403 if (is_node_on_boundary[i] && is_node_on_boundary[(i + 1 <= 2) ? i + 1 : 0])
405 dof_id_type node1 = node_id[i];
406 dof_id_type node2 = node_id[(i + 1 <= 2) ? i + 1 : 0];
408 std::swap(node1, node2);
413 std::swap(node1, node2);
426 for (
unsigned int i = 0; i < corner_elem_id.size(); ++i)
428 auto elem_it =
_cut_mesh->elements_begin();
430 for (dof_id_type j = 0; j < corner_elem_id[i]; ++j)
432 Elem * cut_elem = *elem_it;
436 bool is_edge_inside = 0;
438 dof_id_type node1 = cut_elem->node_ptr(j)->id();
439 dof_id_type node2 = cut_elem->node_ptr((j + 1 <= 2) ? j + 1 : 0)->id();
441 std::swap(node1, node2);
444 for (
const auto & cut_elem2 :
_cut_mesh->element_ptr_range())
446 if (
counter != corner_elem_id[i])
450 dof_id_type node3 = cut_elem2->node_ptr(k)->id();
451 dof_id_type node4 = cut_elem2->node_ptr((k + 1 <= 2) ? k + 1 : 0)->id();
453 std::swap(node3, node4);
455 if (node1 == node3 && node2 == node4)
465 if (is_edge_inside == 0)
471 std::swap(node1, node2);
482 if ((*it)._id1 == node1 && (*it)._id2 == node2)
499 dof_id_type node1 = (*it)._id1;
500 dof_id_type node2 = (*it)._id2;
503 "_boundary_map does not have this key");
505 "_boundary_map does not have this key");
514 if (it->second.size() != 2)
516 "Boundary nodes in the cutter mesh must have exactly two neighbors; this one has: ",
522 dof_id_type node1 = (*it2)._id1;
523 dof_id_type node2 = (*it2)._id2;
530 "_boundary_map does not have this key");
541 else if (node4 == node1)
548 mooseError(
"Discontinuity in cutter boundary");
556 mooseAssert(n1 !=
nullptr,
"Node is NULL");
558 mooseAssert(n2 !=
nullptr,
"Node is NULL");
559 Real distance = (*n1 - *n2).norm();
568 mooseAssert(
_boundary.size() >= 2,
"Boundary should have at least two nodes");
570 for (
unsigned int i =
_boundary.size() - 1; i >= 1; --i)
579 unsigned int n = static_cast<unsigned int>(distance /
_size_control);
580 std::array<Real, LIBMESH_DIM> x1;
581 std::array<Real, LIBMESH_DIM> x2;
584 mooseAssert(n1 !=
nullptr,
"Node is NULL");
587 mooseAssert(n2 !=
nullptr,
"Node is NULL");
590 for (
unsigned int j = 0; j < LIBMESH_DIM; ++j)
596 for (
unsigned int j = 0; j < n; ++j)
599 for (
unsigned int k = 0; k < LIBMESH_DIM; ++k)
600 x(k) = x2[k] - (x2[k] - x1[k]) * (j + 1) / (n + 1);
602 Node * this_node = Node::build(x,
_cut_mesh->n_nodes()).release();
605 dof_id_type
id =
_cut_mesh->n_nodes() - 1;
606 auto it = new_boundary_order.begin();
607 new_boundary_order.insert(it + i,
id);
613 mooseAssert(
_boundary.size() > 0,
"Boundary should not have zero size");
623 std::unique_ptr<PointLocatorBase> pl =
_mesh.getPointLocator();
624 pl->enable_out_of_mesh_mode();
626 unsigned int n_boundary =
_boundary.size();
630 for (
unsigned int j = 0; j < n_boundary; ++j)
633 mooseAssert(this_node,
"Node is NULL");
634 Point & this_point = *this_node;
636 const Elem * elem = (*pl)(this_point);
644 if (n_inactive_boundary == n_boundary)
648 if (n_inactive_boundary == 0)
652 for (
unsigned int i = 0; i < n_inactive_boundary - 1; ++i)
656 std::vector<dof_id_type> temp;
667 std::vector<dof_id_type> temp;
687 std::vector<Point> temp;
692 for (
unsigned int j = 0; j < LIBMESH_DIM; ++j)
705 for (
unsigned int j = i1; j < i2; ++j)
708 mooseAssert(this_node,
"Node is NULL");
709 Point & this_point = *this_node;
711 dir(0) =
_func_x.value(0, this_point);
712 dir(1) =
_func_y.value(0, this_point);
713 dir(2) =
_func_z.value(0, this_point);
720 for (
unsigned int j = 0; j < LIBMESH_DIM; ++j)
734 Real length = std::sqrt(pt * pt);
751 std::vector<dof_id_type> temp;
761 for (
unsigned int j = i1; j < i2; ++j)
764 mooseAssert(this_node,
"Node is NULL");
765 Point & this_point = *this_node;
769 for (
unsigned int k = 0; k < LIBMESH_DIM; ++k)
772 this_node = Node::build(x,
_cut_mesh->n_nodes()).release();
775 dof_id_type
id =
_cut_mesh->n_nodes() - 1;
792 ConstBndElemRange & range = *
_mesh.getBoundaryElementRange();
794 for (
unsigned int i = 0; i <
_front.size(); ++i)
796 if (
_front[i].size() >= 2)
798 std::vector<Point> pint1;
799 std::vector<Point> pint2;
800 std::vector<Real> length1;
801 std::vector<Real> length2;
803 Real node_id =
_front[i][0];
804 Node * this_node =
_cut_mesh->node_ptr(node_id);
805 mooseAssert(this_node,
"Node is NULL");
806 Point & p2 = *this_node;
809 this_node =
_cut_mesh->node_ptr(node_id);
810 mooseAssert(this_node,
"Node is NULL");
811 Point & p1 = *this_node;
813 node_id =
_front[i].back();
814 this_node =
_cut_mesh->node_ptr(node_id);
815 mooseAssert(this_node,
"Node is NULL");
816 Point & p4 = *this_node;
819 this_node =
_cut_mesh->node_ptr(node_id);
820 mooseAssert(this_node,
"Node is NULL");
821 Point & p3 = *this_node;
826 std::unique_ptr<PointLocatorBase> pl =
_mesh.getPointLocator();
827 pl->enable_out_of_mesh_mode();
828 const Elem * elem = (*pl)(p1);
835 for (
const auto & belem : range)
838 std::vector<Point> vertices;
841 std::unique_ptr<const Elem> curr_side = elem->side_ptr(belem->_side);
842 for (
unsigned int j = 0; j < curr_side->n_nodes(); ++j)
844 const Node * node = curr_side->node_ptr(j);
845 const Point & this_point = *node;
846 vertices.push_back(this_point);
852 length1.push_back((pt - p1) * (pt - p1));
857 length2.push_back((pt - p3) * (pt - p3));
861 if (length1.size() != 0 && do_inter1)
863 auto it1 = std::min_element(length1.begin(), length1.end());
864 Point inter1 = pint1[std::distance(length1.begin(), it1)];
867 Node * this_node = Node::build(inter1,
_cut_mesh->n_nodes()).release();
870 mooseAssert(
_cut_mesh->n_nodes() - 1 > 0,
"The cut mesh should have at least one element.");
871 unsigned int n =
_cut_mesh->n_nodes() - 1;
873 auto it =
_front[i].begin();
877 if (length2.size() != 0 && do_inter2)
879 auto it2 = std::min_element(length2.begin(), length2.end());
880 Point inter2 = pint2[std::distance(length2.begin(), it2)];
883 Node * this_node = Node::build(inter2,
_cut_mesh->n_nodes()).release();
886 dof_id_type n =
_cut_mesh->n_nodes() - 1;
888 auto it =
_front[i].begin();
889 unsigned int m =
_front[i].size();
890 _front[i].insert(it + m, n);
899 std::vector<std::vector<dof_id_type>> new_front(
_front.begin(),
_front.end());
901 for (
unsigned int ifront = 0; ifront <
_front.size(); ++ifront)
903 unsigned int i1 =
_front[ifront].size() - 1;
905 i1 =
_front[ifront].size();
907 for (
unsigned int i = i1; i >= 1; --i)
911 i2 = (i <=
_front[ifront].size() - 1 ? i : 0);
913 dof_id_type node1 =
_front[ifront][i - 1];
914 dof_id_type node2 =
_front[ifront][i2];
920 std::array<Real, LIBMESH_DIM> x1;
921 std::array<Real, LIBMESH_DIM> x2;
923 Node * this_node =
_cut_mesh->node_ptr(node1);
924 mooseAssert(this_node,
"Node is NULL");
925 Point & p1 = *this_node;
927 mooseAssert(this_node,
"Node is NULL");
928 Point & p2 = *this_node;
930 for (
unsigned int j = 0; j < LIBMESH_DIM; ++j)
936 for (
unsigned int j = 0; j < n; ++j)
939 for (
unsigned int k = 0; k < LIBMESH_DIM; ++k)
940 x(k) = x2[k] - (x2[k] - x1[k]) * (j + 1) / (n + 1);
942 Node * this_node = Node::build(x,
_cut_mesh->n_nodes()).release();
945 dof_id_type
id =
_cut_mesh->n_nodes() - 1;
947 auto it = new_front[ifront].begin();
948 new_front[ifront].insert(it + i,
id);
962 "_active_boundary and _front should have the same size!");
971 for (
unsigned int k = 0; k <
_front.size(); ++k)
974 unsigned int n2 =
_front[k].size();
980 while (!(i1 == n1 - 1 && i2 == n2 - 1))
982 std::vector<dof_id_type> elem;
985 dof_id_type p2 =
_front[k][i2];
987 if (i1 != n1 - 1 && i2 != n2 - 1)
990 dof_id_type p4 =
_front[k][i2 + 1];
1011 else if (i1 == n1 - 1)
1013 dof_id_type p4 =
_front[k][i2 + 1];
1021 else if (i2 == n2 - 1)
1031 Elem * new_elem = Elem::build(TRI3).release();
1035 mooseAssert(
_cut_mesh->node_ptr(elem[i]) !=
nullptr,
"Node is NULL");
1036 new_elem->set_node(i) =
_cut_mesh->node_ptr(elem[i]);
1054 std::vector<dof_id_type> full_front;
1058 for (
unsigned int i = 0; i < size1; ++i)
1065 full_front.insert(full_front.end(),
_front[i].begin(),
_front[i].end());
1068 unsigned int pos1 = std::distance(
_boundary.begin(), it1);
1070 unsigned int pos2 = std::distance(
_boundary.begin(), it2);
1073 full_front.insert(full_front.end(),
_boundary.begin() + pos1,
_boundary.begin() + pos2 + 1);
1077 full_front.insert(full_front.end(),
_boundary.begin(),
_boundary.begin() + pos2 + 1);
1084 const std::vector<Point>
1087 mooseError(
"getCrackFrontPoints() is not implemented for this object.");