www.mooseframework.org
MeshCut3DUserObject.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "MeshCut3DUserObject.h"
11 
12 #include "XFEMFuncs.h"
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"
20 #include "Function.h"
21 
23 
24 template <>
25 InputParameters
27 {
28  InputParameters params = validParams<GeometricCutUserObject>();
29  params.addRequiredParam<MeshFileName>(
30  "mesh_file",
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");
39  return params;
40 }
41 
42 // this code does not allow predefined crack growth as a function of time
43 // all inital cracks are defined at t_start = t_end = 0
44 MeshCut3DUserObject::MeshCut3DUserObject(const InputParameters & parameters)
45  : GeometricCutUserObject(parameters),
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"))
51 {
52  _grow = (_n_step_growth == 0 ? 0 : 1);
53 
54  if (_grow)
55  {
56  if (!isParamValid("size_control"))
57  mooseError("Crack growth needs size control");
58 
59  _size_control = getParam<Real>("size_control");
60  }
61 
62  // only the xda type is currently supported
63  MeshFileName xfem_cut_mesh_file = getParam<MeshFileName>("mesh_file");
64  _cut_mesh = libmesh_make_unique<ReplicatedMesh>(_communicator);
65  _cut_mesh->read(xfem_cut_mesh_file);
66 
67  // test element type; only tri3 elements are allowed
68  for (const auto & cut_elem : _cut_mesh->element_ptr_range())
69  {
70  if (cut_elem->n_nodes() != _cut_elem_nnode)
71  mooseError("The input cut mesh should include tri elements only!");
72  if (cut_elem->dim() != _cut_elem_dim)
73  mooseError("The input cut mesh should have 2D elements only!");
74  }
75 }
76 
77 void
79 {
80  if (_grow)
81  {
86  }
87 }
88 
89 void
91 {
92  if (_grow)
93  {
94  unsigned int timestep = _fe_problem.timeStep();
95 
96  if (timestep == 1)
98 
99  _stop = 0;
100 
101  if (timestep > 1 && timestep != _last_step_initialized)
102  {
103  _last_step_initialized = timestep;
104 
105  for (unsigned int i = 0; i < _n_step_growth; ++i)
107 
108  if (_stop != 1)
109  {
111  growFront();
112  sortFrontNodes();
113  if (_inactive_boundary_pos.size() != 0)
115  refineFront();
116  triangulation();
117  joinBoundary();
118  }
119  }
120  }
121 }
122 
123 bool
125  std::vector<Xfem::CutEdge> & /*cut_edges*/,
126  std::vector<Xfem::CutNode> & /*cut_nodes*/,
127  Real /*time*/) const
128 {
129  mooseError("invalid method for 3D mesh cutting");
130  return false;
131 }
132 
133 bool
135  std::vector<Xfem::CutFace> & cut_faces,
136  Real /*time*/) const
137 // With the crack defined by a planar mesh, this method cuts a solid element by all elements in the
138 // planar mesh
139 // TODO: Time evolving cuts not yet supported in 3D (hence the lack of use of the time variable)
140 {
141  bool elem_cut = false;
142 
143  if (elem->dim() != _elem_dim)
144  mooseError("The structural mesh to be cut by a surface mesh must be 3D!");
145 
146  for (unsigned int i = 0; i < elem->n_sides(); ++i)
147  {
148  // This returns the lowest-order type of side.
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 ",
152  curr_side->dim());
153  unsigned int n_edges = curr_side->n_sides();
154 
155  std::vector<unsigned int> cut_edges;
156  std::vector<Real> cut_pos;
157 
158  for (unsigned int j = 0; j < n_edges; j++)
159  {
160  // This returns the lowest-order type of side.
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);
169 
170  for (const auto & cut_elem : _cut_mesh->element_ptr_range())
171  {
172  std::vector<Point> vertices;
173 
174  for (auto & node : cut_elem->node_ref_range())
175  {
176  Point & this_point = node;
177  vertices.push_back(this_point);
178  }
179 
180  Point intersection;
181  if (intersectWithEdge(*node1, *node2, vertices, intersection))
182  {
183  cut_edges.push_back(j);
184  cut_pos.emplace_back(getRelativePosition(*node1, *node2, intersection));
185  }
186  }
187  }
188 
189  // if two edges of an element are cut, it is considered as an element being cut
190  if (cut_edges.size() == 2)
191  {
192  elem_cut = true;
193  Xfem::CutFace mycut;
194  mycut._face_id = i;
195  mycut._face_edge.push_back(cut_edges[0]);
196  mycut._face_edge.push_back(cut_edges[1]);
197  mycut._position.push_back(cut_pos[0]);
198  mycut._position.push_back(cut_pos[1]);
199  cut_faces.push_back(mycut);
200  }
201  }
202  return elem_cut;
203 }
204 
205 bool
206 MeshCut3DUserObject::cutFragmentByGeometry(std::vector<std::vector<Point>> & /*frag_edges*/,
207  std::vector<Xfem::CutEdge> & /*cut_edges*/,
208  Real /*time*/) const
209 {
210  mooseError("invalid method for 3D mesh cutting");
211  return false;
212 }
213 
214 bool
215 MeshCut3DUserObject::cutFragmentByGeometry(std::vector<std::vector<Point>> & /*frag_faces*/,
216  std::vector<Xfem::CutFace> & /*cut_faces*/,
217  Real /*time*/) const
218 {
219  // TODO: Need this for branching in 3D
220  mooseError("cutFragmentByGeometry not yet implemented for 3D mesh cutting");
221  return false;
222 }
223 
224 bool
226  const Point & p2,
227  const std::vector<Point> & vertices,
228  Point & pint) const
229 {
230  bool has_intersection = false;
231 
232  Plane elem_plane(vertices[0], vertices[1], vertices[2]);
233  Point point = vertices[0];
234  Point normal = elem_plane.unit_normal(point);
235 
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}};
241 
243  &plane_point[0], &planenormal[0], &edge_point1[0], &edge_point2[0], &cut_point[0]) == 1)
244  {
245  Point temp_p(cut_point[0], cut_point[1], cut_point[2]);
246  if (isInsideCutPlane(vertices, temp_p) && isInsideEdge(p1, p2, temp_p))
247  {
248  pint = temp_p;
249  has_intersection = true;
250  }
251  }
252  return has_intersection;
253 }
254 
255 bool
257  const Point & p2,
258  const std::vector<Point> & vertices,
259  Point & pint) const
260 {
261  bool has_intersection = false;
262 
263  Plane elem_plane(vertices[0], vertices[1], vertices[2]);
264  Point point = vertices[0];
265  Point normal = elem_plane.unit_normal(point);
266 
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}};
272 
274  &plane_point[0], &planenormal[0], &p_begin[0], &p_end[0], &cut_point[0]) == 1)
275  {
276  Point p(cut_point[0], cut_point[1], cut_point[2]);
277  Real dotp = ((p - p1) * (p2 - p1)) / ((p2 - p1) * (p2 - p1));
278  if (isInsideCutPlane(vertices, p) && dotp > 1)
279  {
280  pint = p;
281  has_intersection = true;
282  }
283  }
284  return has_intersection;
285 }
286 
287 bool
288 MeshCut3DUserObject::isInsideEdge(const Point & p1, const Point & p2, const Point & p) const
289 {
290  Real dotp1 = (p1 - p) * (p2 - p1);
291  Real dotp2 = (p2 - p) * (p2 - p1);
292  return (dotp1 * dotp2 <= 0.0);
293 }
294 
295 Real
296 MeshCut3DUserObject::getRelativePosition(const Point & p1, const Point & p2, const Point & p) const
297 {
298  Real full_len = (p2 - p1).norm();
299  Real len_p1_p = (p - p1).norm();
300  return len_p1_p / full_len;
301 }
302 
303 bool
304 MeshCut3DUserObject::isInsideCutPlane(const std::vector<Point> & vertices, const Point & p) const
305 {
306  unsigned int n_node = vertices.size();
307 
308  Plane elem_plane(vertices[0], vertices[1], vertices[2]);
309  Point normal = elem_plane.unit_normal(vertices[0]);
310 
311  bool inside = false;
312  unsigned int counter = 0;
313 
314  for (unsigned int i = 0; i < n_node; ++i)
315  {
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);
320  Xfem::normalizePoint(middle2p);
321  Xfem::normalizePoint(side_norm);
322  if (middle2p * side_norm <= 0.0)
323  counter += 1;
324  }
325  if (counter == n_node)
326  inside = true;
327  return inside;
328 }
329 
330 void
332 {
333  unsigned int n_nodes = _cut_mesh->n_nodes();
334  std::vector<Real> angle(n_nodes, 0); // this assumes that the cutter mesh has compressed node id
335  std::vector<dof_id_type> node_id(_cut_elem_nnode);
336 
337  std::vector<Point> vertices(_cut_elem_nnode);
338 
339  for (const auto & cut_elem : _cut_mesh->element_ptr_range())
340  {
341  for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
342  {
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();
347  }
348 
349  for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
350  mooseAssert(node_id[i] < n_nodes, "Node ID is out of range");
351 
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));
355  }
356 
357  // In each element, angles at three vertices are calculated. Angles associated with all nodes are
358  // evaluated.
359  // Interior nodes will have a total angle = 2*pi; otherwise, it is a boundary node
360  // This assumes the cutter surface is flat.
361  for (const auto & node : _cut_mesh->node_ptr_range())
362  {
363  dof_id_type id = node->id();
364  if (!MooseUtils::relativeFuzzyEqual(angle[id], libMesh::pi * 2))
365  {
366  std::vector<dof_id_type> neighbors;
367  _boundary_map[id] = neighbors;
368  }
369  }
370 }
371 
372 void
374 {
375  _boundary_edges.clear();
376 
377  std::vector<dof_id_type> corner_elem_id;
378  unsigned int counter = 0;
379 
380  std::vector<dof_id_type> node_id(_cut_elem_nnode);
381  std::vector<bool> is_node_on_boundary(_cut_elem_nnode);
382 
383  for (const auto & cut_elem : _cut_mesh->element_ptr_range())
384  {
385  for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
386  {
387  node_id[i] = cut_elem->node_ptr(i)->id();
388  is_node_on_boundary[i] = (_boundary_map.find(node_id[i]) != _boundary_map.end());
389  }
390 
391  if (is_node_on_boundary[0] && is_node_on_boundary[1] && is_node_on_boundary[2])
392  {
393  // this is an element at the corner; all nodes are on the boundary but not all edges are on
394  // the boundary
395  corner_elem_id.push_back(counter);
396  }
397  else
398  {
399  // for other elements, find and store boundary edges
400  for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
401  {
402  // if both nodes on an edge are on the boundary, it is a boundary edge.
403  if (is_node_on_boundary[i] && is_node_on_boundary[(i + 1 <= 2) ? i + 1 : 0])
404  {
405  dof_id_type node1 = node_id[i];
406  dof_id_type node2 = node_id[(i + 1 <= 2) ? i + 1 : 0];
407  if (node1 > node2)
408  std::swap(node1, node2);
409 
410  Xfem::CutEdge ce;
411 
412  if (node1 > node2)
413  std::swap(node1, node2);
414  ce._id1 = node1;
415  ce._id2 = node2;
416 
417  _boundary_edges.insert(ce);
418  }
419  }
420  }
421  ++counter;
422  }
423 
424  // loop over edges in corner elements
425  // if an edge is shared by two elements, it is not an boundary edge (is_edge_inside = 1)
426  for (unsigned int i = 0; i < corner_elem_id.size(); ++i)
427  {
428  auto elem_it = _cut_mesh->elements_begin();
429 
430  for (dof_id_type j = 0; j < corner_elem_id[i]; ++j)
431  ++elem_it;
432  Elem * cut_elem = *elem_it;
433 
434  for (unsigned int j = 0; j < _cut_elem_nnode; ++j)
435  {
436  bool is_edge_inside = 0;
437 
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();
440  if (node1 > node2)
441  std::swap(node1, node2);
442 
443  unsigned int counter = 0;
444  for (const auto & cut_elem2 : _cut_mesh->element_ptr_range())
445  {
446  if (counter != corner_elem_id[i])
447  {
448  for (unsigned int k = 0; k < _cut_elem_nnode; ++k)
449  {
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();
452  if (node3 > node4)
453  std::swap(node3, node4);
454 
455  if (node1 == node3 && node2 == node4)
456  {
457  is_edge_inside = 1;
458  goto endloop;
459  }
460  }
461  }
462  ++counter;
463  }
464  endloop:
465  if (is_edge_inside == 0)
466  {
467  // store boundary edges
468  Xfem::CutEdge ce;
469 
470  if (node1 > node2)
471  std::swap(node1, node2);
472  ce._id1 = node1;
473  ce._id2 = node2;
474 
475  _boundary_edges.insert(ce);
476  }
477  else
478  {
479  // this is not a boundary edge; remove it from existing edge list
480  for (auto it = _boundary_edges.begin(); it != _boundary_edges.end();)
481  {
482  if ((*it)._id1 == node1 && (*it)._id2 == node2)
483  it = _boundary_edges.erase(it);
484  else
485  ++it;
486  }
487  }
488  }
489  }
490 }
491 
492 void
494 {
495  _boundary.clear();
496 
497  for (auto it = _boundary_edges.begin(); it != _boundary_edges.end(); ++it)
498  {
499  dof_id_type node1 = (*it)._id1;
500  dof_id_type node2 = (*it)._id2;
501 
502  mooseAssert(_boundary_map.find(node1) != _boundary_map.end(),
503  "_boundary_map does not have this key");
504  mooseAssert(_boundary_map.find(node2) != _boundary_map.end(),
505  "_boundary_map does not have this key");
506 
507  _boundary_map.find(node1)->second.push_back(node2);
508  _boundary_map.find(node2)->second.push_back(node1);
509  }
510 
511  auto it = _boundary_map.begin();
512  while (it != _boundary_map.end())
513  {
514  if (it->second.size() != 2)
515  mooseError(
516  "Boundary nodes in the cutter mesh must have exactly two neighbors; this one has: ",
517  it->second.size());
518  ++it;
519  }
520 
521  auto it2 = _boundary_edges.begin();
522  dof_id_type node1 = (*it2)._id1;
523  dof_id_type node2 = (*it2)._id2;
524  _boundary.push_back(node1);
525  _boundary.push_back(node2);
526 
527  for (unsigned int i = 0; i < _boundary_edges.size() - 1; ++i)
528  {
529  mooseAssert(_boundary_map.find(node2) != _boundary_map.end(),
530  "_boundary_map does not have this key");
531 
532  dof_id_type node3 = _boundary_map.find(node2)->second[0];
533  dof_id_type node4 = _boundary_map.find(node2)->second[1];
534 
535  if (node3 == node1)
536  {
537  _boundary.push_back(node4);
538  node1 = node2;
539  node2 = node4;
540  }
541  else if (node4 == node1)
542  {
543  _boundary.push_back(node3);
544  node1 = node2;
545  node2 = node3;
546  }
547  else
548  mooseError("Discontinuity in cutter boundary");
549  }
550 }
551 
552 Real
553 MeshCut3DUserObject::findDistance(dof_id_type node1, dof_id_type node2)
554 {
555  Node * n1 = _cut_mesh->node_ptr(node1);
556  mooseAssert(n1 != nullptr, "Node is NULL");
557  Node * n2 = _cut_mesh->node_ptr(node2);
558  mooseAssert(n2 != nullptr, "Node is NULL");
559  Real distance = (*n1 - *n2).norm();
560  return distance;
561 }
562 
563 void
565 {
566  std::vector<dof_id_type> new_boundary_order(_boundary.begin(), _boundary.end());
567 
568  mooseAssert(_boundary.size() >= 2, "Boundary should have at least two nodes");
569 
570  for (unsigned int i = _boundary.size() - 1; i >= 1; --i)
571  {
572  dof_id_type node1 = _boundary[i - 1];
573  dof_id_type node2 = _boundary[i];
574 
575  Real distance = findDistance(node1, node2);
576 
577  if (distance > _size_control)
578  {
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;
582 
583  Node * n1 = _cut_mesh->node_ptr(node1);
584  mooseAssert(n1 != nullptr, "Node is NULL");
585  Point & p1 = *n1;
586  Node * n2 = _cut_mesh->node_ptr(node2);
587  mooseAssert(n2 != nullptr, "Node is NULL");
588  Point & p2 = *n2;
589 
590  for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
591  {
592  x1[j] = p1(j);
593  x2[j] = p2(j);
594  }
595 
596  for (unsigned int j = 0; j < n; ++j)
597  {
598  Point x;
599  for (unsigned int k = 0; k < LIBMESH_DIM; ++k)
600  x(k) = x2[k] - (x2[k] - x1[k]) * (j + 1) / (n + 1);
601 
602  Node * this_node = Node::build(x, _cut_mesh->n_nodes()).release();
603  _cut_mesh->add_node(this_node);
604 
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);
608  }
609  }
610  }
611 
612  _boundary = new_boundary_order;
613  mooseAssert(_boundary.size() > 0, "Boundary should not have zero size");
614  _boundary.pop_back();
615 }
616 
617 void
619 {
620  _active_boundary.clear();
621  _inactive_boundary_pos.clear();
622 
623  std::unique_ptr<PointLocatorBase> pl = _mesh.getPointLocator();
624  pl->enable_out_of_mesh_mode();
625 
626  unsigned int n_boundary = _boundary.size();
627 
628  // if the node is outside of the structural model, store its position in _boundary to
629  // _inactive_boundary_pos
630  for (unsigned int j = 0; j < n_boundary; ++j)
631  {
632  Node * this_node = _cut_mesh->node_ptr(_boundary[j]);
633  mooseAssert(this_node, "Node is NULL");
634  Point & this_point = *this_node;
635 
636  const Elem * elem = (*pl)(this_point);
637  if (elem == NULL)
638  _inactive_boundary_pos.push_back(j);
639  }
640 
641  unsigned int n_inactive_boundary = _inactive_boundary_pos.size();
642 
643  // all nodes are inactive, stop
644  if (n_inactive_boundary == n_boundary)
645  _stop = 1;
646 
647  // find and store active boundary segments in "_active_boundary"
648  if (n_inactive_boundary == 0)
649  _active_boundary.push_back(_boundary);
650  else
651  {
652  for (unsigned int i = 0; i < n_inactive_boundary - 1; ++i)
653  {
654  if (_inactive_boundary_pos[i + 1] - _inactive_boundary_pos[i] != 1)
655  {
656  std::vector<dof_id_type> temp;
657  for (unsigned int j = _inactive_boundary_pos[i]; j <= _inactive_boundary_pos[i + 1]; ++j)
658  {
659  temp.push_back(_boundary[j]);
660  }
661  _active_boundary.push_back(temp);
662  }
663  }
664  if (_inactive_boundary_pos[n_inactive_boundary - 1] - _inactive_boundary_pos[0] <
665  n_boundary - 1)
666  {
667  std::vector<dof_id_type> temp;
668  for (unsigned int j = _inactive_boundary_pos[n_inactive_boundary - 1]; j < n_boundary; ++j)
669  temp.push_back(_boundary[j]);
670  for (unsigned int j = 0; j <= _inactive_boundary_pos[0]; ++j)
671  temp.push_back(_boundary[j]);
672  _active_boundary.push_back(temp);
673  }
674  }
675 }
676 
677 void
679 // find growth direction of each boundary node; this is currently assgined by parsed functions;
680 // it will be updated to (1) growth function specified in the input file,
681 // and/or (2) growth direction determined by fracture mechanics
682 {
683  _active_direction.clear();
684 
685  for (unsigned int i = 0; i < _active_boundary.size(); ++i)
686  {
687  std::vector<Point> temp;
688  Point dir;
689 
690  if (_inactive_boundary_pos.size() != 0)
691  {
692  for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
693  dir(j) = 0;
694  temp.push_back(dir);
695  }
696 
697  unsigned int i1 = 1;
698  unsigned int i2 = _active_boundary[i].size() - 1;
699  if (_inactive_boundary_pos.size() == 0)
700  {
701  i1 = 0;
702  i2 = _active_boundary[i].size();
703  }
704 
705  for (unsigned int j = i1; j < i2; ++j)
706  {
707  Node * this_node = _cut_mesh->node_ptr(_active_boundary[i][j]);
708  mooseAssert(this_node, "Node is NULL");
709  Point & this_point = *this_node;
710 
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);
714 
715  temp.push_back(dir);
716  }
717 
718  if (_inactive_boundary_pos.size() != 0)
719  {
720  for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
721  dir(j) = 0;
722  temp.push_back(dir);
723  }
724 
725  _active_direction.push_back(temp);
726  }
727 
728  Real maxl = 0;
729 
730  for (unsigned int i = 0; i < _active_direction.size(); ++i)
731  for (unsigned int j = 0; j < _active_direction[i].size(); ++j)
732  {
733  Point pt = _active_direction[i][j];
734  Real length = std::sqrt(pt * pt);
735  if (length > maxl)
736  maxl = length;
737  }
738 
739  for (unsigned int i = 0; i < _active_direction.size(); ++i)
740  for (unsigned int j = 0; j < _active_direction[i].size(); ++j)
741  _active_direction[i][j] /= maxl;
742 }
743 
744 void
746 {
747  _front.clear();
748 
749  for (unsigned int i = 0; i < _active_boundary.size(); ++i)
750  {
751  std::vector<dof_id_type> temp;
752 
753  unsigned int i1 = 1;
754  unsigned int i2 = _active_boundary[i].size() - 1;
755  if (_inactive_boundary_pos.size() == 0)
756  {
757  i1 = 0;
758  i2 = _active_boundary[i].size();
759  }
760 
761  for (unsigned int j = i1; j < i2; ++j)
762  {
763  Node * this_node = _cut_mesh->node_ptr(_active_boundary[i][j]);
764  mooseAssert(this_node, "Node is NULL");
765  Point & this_point = *this_node;
766  Point dir = _active_direction[i][j];
767 
768  Point x;
769  for (unsigned int k = 0; k < LIBMESH_DIM; ++k)
770  x(k) = this_point(k) + dir(k) * _size_control;
771 
772  this_node = Node::build(x, _cut_mesh->n_nodes()).release();
773  _cut_mesh->add_node(this_node);
774 
775  dof_id_type id = _cut_mesh->n_nodes() - 1;
776  temp.push_back(id);
777  }
778 
779  _front.push_back(temp);
780  }
781 }
782 
783 void
785 // TBD; it is not needed for current problems but will be useful for fracture growth
786 {
787 }
788 
789 void
791 {
792  ConstBndElemRange & range = *_mesh.getBoundaryElementRange();
793 
794  for (unsigned int i = 0; i < _front.size(); ++i)
795  {
796  if (_front[i].size() >= 2)
797  {
798  std::vector<Point> pint1;
799  std::vector<Point> pint2;
800  std::vector<Real> length1;
801  std::vector<Real> length2;
802 
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;
807 
808  node_id = _front[i][1];
809  this_node = _cut_mesh->node_ptr(node_id);
810  mooseAssert(this_node, "Node is NULL");
811  Point & p1 = *this_node;
812 
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;
817 
818  node_id = _front[i][_front[i].size() - 2];
819  this_node = _cut_mesh->node_ptr(node_id);
820  mooseAssert(this_node, "Node is NULL");
821  Point & p3 = *this_node;
822 
823  bool do_inter1 = 1;
824  bool do_inter2 = 1;
825 
826  std::unique_ptr<PointLocatorBase> pl = _mesh.getPointLocator();
827  pl->enable_out_of_mesh_mode();
828  const Elem * elem = (*pl)(p1);
829  if (elem == NULL)
830  do_inter1 = 0;
831  elem = (*pl)(p4);
832  if (elem == NULL)
833  do_inter2 = 0;
834 
835  for (const auto & belem : range)
836  {
837  Point pt;
838  std::vector<Point> vertices;
839 
840  elem = belem->_elem;
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)
843  {
844  const Node * node = curr_side->node_ptr(j);
845  const Point & this_point = *node;
846  vertices.push_back(this_point);
847  }
848 
849  if (findIntersection(p1, p2, vertices, pt))
850  {
851  pint1.push_back(pt);
852  length1.push_back((pt - p1) * (pt - p1));
853  }
854  if (findIntersection(p3, p4, vertices, pt))
855  {
856  pint2.push_back(pt);
857  length2.push_back((pt - p3) * (pt - p3));
858  }
859  }
860 
861  if (length1.size() != 0 && do_inter1)
862  {
863  auto it1 = std::min_element(length1.begin(), length1.end());
864  Point inter1 = pint1[std::distance(length1.begin(), it1)];
865  inter1 += (inter1 - p1) * _const_intersection;
866 
867  Node * this_node = Node::build(inter1, _cut_mesh->n_nodes()).release();
868  _cut_mesh->add_node(this_node);
869 
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;
872 
873  auto it = _front[i].begin();
874  _front[i].insert(it, n);
875  }
876 
877  if (length2.size() != 0 && do_inter2)
878  {
879  auto it2 = std::min_element(length2.begin(), length2.end());
880  Point inter2 = pint2[std::distance(length2.begin(), it2)];
881  inter2 += (inter2 - p2) * _const_intersection;
882 
883  Node * this_node = Node::build(inter2, _cut_mesh->n_nodes()).release();
884  _cut_mesh->add_node(this_node);
885 
886  dof_id_type n = _cut_mesh->n_nodes() - 1;
887 
888  auto it = _front[i].begin();
889  unsigned int m = _front[i].size();
890  _front[i].insert(it + m, n);
891  }
892  }
893  }
894 }
895 
896 void
898 {
899  std::vector<std::vector<dof_id_type>> new_front(_front.begin(), _front.end());
900 
901  for (unsigned int ifront = 0; ifront < _front.size(); ++ifront)
902  {
903  unsigned int i1 = _front[ifront].size() - 1;
904  if (_inactive_boundary_pos.size() == 0)
905  i1 = _front[ifront].size();
906 
907  for (unsigned int i = i1; i >= 1; --i)
908  {
909  unsigned int i2 = i;
910  if (_inactive_boundary_pos.size() == 0)
911  i2 = (i <= _front[ifront].size() - 1 ? i : 0);
912 
913  dof_id_type node1 = _front[ifront][i - 1];
914  dof_id_type node2 = _front[ifront][i2];
915  Real distance = findDistance(node1, node2);
916 
917  if (distance > _size_control)
918  {
919  unsigned int n = static_cast<int>(distance / _size_control);
920  std::array<Real, LIBMESH_DIM> x1;
921  std::array<Real, LIBMESH_DIM> x2;
922 
923  Node * this_node = _cut_mesh->node_ptr(node1);
924  mooseAssert(this_node, "Node is NULL");
925  Point & p1 = *this_node;
926  this_node = _cut_mesh->node_ptr(node2);
927  mooseAssert(this_node, "Node is NULL");
928  Point & p2 = *this_node;
929 
930  for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
931  {
932  x1[j] = p1(j);
933  x2[j] = p2(j);
934  }
935 
936  for (unsigned int j = 0; j < n; ++j)
937  {
938  Point x;
939  for (unsigned int k = 0; k < LIBMESH_DIM; ++k)
940  x(k) = x2[k] - (x2[k] - x1[k]) * (j + 1) / (n + 1);
941 
942  Node * this_node = Node::build(x, _cut_mesh->n_nodes()).release();
943  _cut_mesh->add_node(this_node);
944 
945  dof_id_type id = _cut_mesh->n_nodes() - 1;
946 
947  auto it = new_front[ifront].begin();
948  new_front[ifront].insert(it + i, id);
949  }
950  }
951  }
952  }
953 
954  _front = new_front;
955 }
956 
957 void
959 {
960 
961  mooseAssert(_active_boundary.size() == _front.size(),
962  "_active_boundary and _front should have the same size!");
963 
964  if (_inactive_boundary_pos.size() == 0)
965  {
966  _active_boundary[0].push_back(_active_boundary[0][0]);
967  _front[0].push_back(_front[0][0]);
968  }
969 
970  // loop over active segments
971  for (unsigned int k = 0; k < _front.size(); ++k)
972  {
973  unsigned int n1 = _active_boundary[k].size();
974  unsigned int n2 = _front[k].size();
975 
976  unsigned int i1 = 0;
977  unsigned int i2 = 0;
978 
979  // stop when all nodes are associated with an element
980  while (!(i1 == n1 - 1 && i2 == n2 - 1))
981  {
982  std::vector<dof_id_type> elem;
983 
984  dof_id_type p1 = _active_boundary[k][i1]; // node in the old front
985  dof_id_type p2 = _front[k][i2]; // node in the new front
986 
987  if (i1 != n1 - 1 && i2 != n2 - 1)
988  {
989  dof_id_type p3 = _active_boundary[k][i1 + 1]; // next node in the old front
990  dof_id_type p4 = _front[k][i2 + 1]; // next node in the new front
991 
992  elem.push_back(p1);
993  elem.push_back(p2);
994 
995  Real d1 = findDistance(p1, p4);
996  Real d2 = findDistance(p3, p2);
997 
998  if (d1 < d2)
999  {
1000  elem.push_back(p4);
1001  i2++;
1002  }
1003 
1004  else
1005  {
1006  elem.push_back(p3);
1007  i1++;
1008  }
1009  }
1010 
1011  else if (i1 == n1 - 1)
1012  {
1013  dof_id_type p4 = _front[k][i2 + 1]; // next node in the new front
1014 
1015  elem.push_back(p1);
1016  elem.push_back(p2);
1017  elem.push_back(p4);
1018  i2++;
1019  }
1020 
1021  else if (i2 == n2 - 1)
1022  {
1023  dof_id_type p3 = _active_boundary[k][i1 + 1]; // next node in the old front
1024 
1025  elem.push_back(p1);
1026  elem.push_back(p2);
1027  elem.push_back(p3);
1028  i1++;
1029  }
1030 
1031  Elem * new_elem = Elem::build(TRI3).release();
1032 
1033  for (unsigned int i = 0; i < _cut_elem_nnode; ++i)
1034  {
1035  mooseAssert(_cut_mesh->node_ptr(elem[i]) != nullptr, "Node is NULL");
1036  new_elem->set_node(i) = _cut_mesh->node_ptr(elem[i]);
1037  }
1038 
1039  _cut_mesh->add_elem(new_elem);
1040  }
1041  }
1042 }
1043 
1044 void
1046 {
1047  if (_inactive_boundary_pos.size() == 0)
1048  {
1049  _boundary = _front[0];
1050  _boundary.pop_back();
1051  return;
1052  }
1053 
1054  std::vector<dof_id_type> full_front;
1055 
1056  unsigned int size1 = _active_boundary.size();
1057 
1058  for (unsigned int i = 0; i < size1; ++i)
1059  {
1060  unsigned int size2 = _active_boundary[i].size();
1061 
1062  dof_id_type bd1 = _active_boundary[i][size2 - 1];
1063  dof_id_type bd2 = _active_boundary[i + 1 < size1 ? i + 1 : 0][0];
1064 
1065  full_front.insert(full_front.end(), _front[i].begin(), _front[i].end());
1066 
1067  auto it1 = std::find(_boundary.begin(), _boundary.end(), bd1);
1068  unsigned int pos1 = std::distance(_boundary.begin(), it1);
1069  auto it2 = std::find(_boundary.begin(), _boundary.end(), bd2);
1070  unsigned int pos2 = std::distance(_boundary.begin(), it2);
1071 
1072  if (pos1 <= pos2)
1073  full_front.insert(full_front.end(), _boundary.begin() + pos1, _boundary.begin() + pos2 + 1);
1074  else
1075  {
1076  full_front.insert(full_front.end(), _boundary.begin() + pos1, _boundary.end());
1077  full_front.insert(full_front.end(), _boundary.begin(), _boundary.begin() + pos2 + 1);
1078  }
1079  }
1080 
1081  _boundary = full_front;
1082 }
1083 
1084 const std::vector<Point>
1085 MeshCut3DUserObject::getCrackFrontPoints(unsigned int /*num_crack_front_points*/) const
1086 {
1087  mooseError("getCrackFrontPoints() is not implemented for this object.");
1088 }
MeshCut3DUserObject::findBoundaryNodes
void findBoundaryNodes()
Find boundary nodes of the cutter mesh This is a simple algorithm simply based on the added angle = 3...
Definition: MeshCut3DUserObject.C:331
MeshCut3DUserObject::findActiveBoundaryDirection
void findActiveBoundaryDirection()
Find growth direction at each active node.
Definition: MeshCut3DUserObject.C:678
Xfem::CutEdge::_id1
unsigned int _id1
ID of the first node on the edge.
Definition: GeometricCutUserObject.h:29
MeshCut3DUserObject::getRelativePosition
Real getRelativePosition(const Point &p1, const Point &p2, const Point &p) const
Get the relative position of p from p1.
Definition: MeshCut3DUserObject.C:296
MeshCut3DUserObject::refineFront
void refineFront()
Refine the mesh at the front.
Definition: MeshCut3DUserObject.C:897
MeshCut3DUserObject.h
MeshCut3DUserObject
MeshCut3DUserObject: (1) reads in a mesh describing the crack surface, (2) uses the mesh to do initia...
Definition: MeshCut3DUserObject.h:28
validParams< GeometricCutUserObject >
InputParameters validParams< GeometricCutUserObject >()
Definition: GeometricCutUserObject.C:25
registerMooseObject
registerMooseObject("XFEMApp", MeshCut3DUserObject)
MeshCut3DUserObject::_func_y
const Function & _func_y
Definition: MeshCut3DUserObject.h:204
MeshCut3DUserObject::isInsideEdge
bool isInsideEdge(const Point &p1, const Point &p2, const Point &p) const
Check if point p is inside the edge p1-p2.
Definition: MeshCut3DUserObject.C:288
MeshCut3DUserObject::_func_z
const Function & _func_z
Definition: MeshCut3DUserObject.h:205
MeshCut3DUserObject::refineBoundary
void refineBoundary()
If boundary nodes are too sparse, add nodes in between.
Definition: MeshCut3DUserObject.C:564
MeshCut3DUserObject::_grow
bool _grow
Definition: MeshCut3DUserObject.h:77
MeshCut3DUserObject::findBoundaryEdges
void findBoundaryEdges()
Find boundary edges of the cutter mesh.
Definition: MeshCut3DUserObject.C:373
counter
static unsigned int counter
Definition: ContactPenetrationAuxAction.C:17
MeshCut3DUserObject::_size_control
Real _size_control
Used for cutter mesh refinement and front advancement.
Definition: MeshCut3DUserObject.h:70
Xfem::CutFace::_position
std::vector< Real > _position
Fractional distance along the cut edges where the cut is located.
Definition: GeometricCutUserObject.h:71
Xfem::CutEdge::_id2
unsigned int _id2
ID of the second node on the edge.
Definition: GeometricCutUserObject.h:31
MeshCut3DUserObject::findActiveBoundaryNodes
void findActiveBoundaryNodes()
Find all active boundary nodes in the cutter mesh Find boundary nodes that will grow; nodes outside o...
Definition: MeshCut3DUserObject.C:618
GeometricCutUserObject
Definition: GeometricCutUserObject.h:106
MeshCut3DUserObject::MeshCut3DUserObject
MeshCut3DUserObject(const InputParameters &parameters)
Definition: MeshCut3DUserObject.C:44
Xfem::plane_normal_line_exp_int_3d
int plane_normal_line_exp_int_3d(double pp[3], double normal[3], double p1[3], double p2[3], double pint[3])
Definition: XFEMFuncs.C:403
MeshCut3DUserObject::_front
std::vector< std::vector< dof_id_type > > _front
New boundary after growth.
Definition: MeshCut3DUserObject.h:98
Xfem::CutFace::_face_id
unsigned int _face_id
ID of the cut face.
Definition: GeometricCutUserObject.h:67
XFEMFuncs.h
MeshCut3DUserObject::_active_boundary
std::vector< std::vector< dof_id_type > > _active_boundary
Active boundary nodes where growth is allowed.
Definition: MeshCut3DUserObject.h:89
MeshCut3DUserObject::initialSetup
virtual void initialSetup() override
Definition: MeshCut3DUserObject.C:78
MeshCut3DUserObject::_mesh
MooseMesh & _mesh
The structural mesh.
Definition: MeshCut3DUserObject.h:61
MeshCut3DUserObject::_boundary_edges
std::set< Xfem::CutEdge > _boundary_edges
Edges at the boundary.
Definition: MeshCut3DUserObject.h:83
MeshCut3DUserObject::_boundary
std::vector< dof_id_type > _boundary
Boundary nodes of the cutter mesh.
Definition: MeshCut3DUserObject.h:80
MeshCut3DUserObject::sortBoundaryNodes
void sortBoundaryNodes()
Sort boundary nodes to be in the right order along the boundary.
Definition: MeshCut3DUserObject.C:493
MeshCut3DUserObject::findDistance
Real findDistance(dof_id_type node1, dof_id_type node2)
Find distance between two nodes.
Definition: MeshCut3DUserObject.C:553
MeshCut3DUserObject::findIntersection
bool findIntersection(const Point &p1, const Point &p2, const std::vector< Point > &vertices, Point &pint) const
Find directional intersection along the positive extension of the vector from p1 to p2.
Definition: MeshCut3DUserObject.C:256
MeshCut3DUserObject::isInsideCutPlane
bool isInsideCutPlane(const std::vector< Point > &_vertices, const Point &p) const
Check if point p is inside a plane.
Definition: MeshCut3DUserObject.C:304
Xfem::CutFace::_face_edge
std::vector< unsigned int > _face_edge
IDs of all cut faces.
Definition: GeometricCutUserObject.h:69
MeshCut3DUserObject::_const_intersection
const Real _const_intersection
Used to define intersection points.
Definition: MeshCut3DUserObject.h:67
validParams< MeshCut3DUserObject >
InputParameters validParams< MeshCut3DUserObject >()
Definition: MeshCut3DUserObject.C:26
MeshCut3DUserObject::initialize
virtual void initialize() override
Definition: MeshCut3DUserObject.C:90
GeometricCutUserObject::_last_step_initialized
unsigned int _last_step_initialized
Time step information needed to advance a 3D crack only at the real beginning of a time step.
Definition: GeometricCutUserObject.h:198
MeshCut3DUserObject::_cut_elem_dim
const unsigned int _cut_elem_dim
Definition: MeshCut3DUserObject.h:58
MeshCut3DUserObject::_boundary_map
std::map< dof_id_type, std::vector< dof_id_type > > _boundary_map
A map of boundary nodes and their neighbors.
Definition: MeshCut3DUserObject.h:86
MeshCut3DUserObject::_func_x
const Function & _func_x
Parsed functions of front growth.
Definition: MeshCut3DUserObject.h:203
MeshCut3DUserObject::_active_direction
std::vector< std::vector< Point > > _active_direction
Growth direction for active boundaries.
Definition: MeshCut3DUserObject.h:92
MeshCut3DUserObject::_n_step_growth
unsigned int _n_step_growth
Number of steps to grow the mesh.
Definition: MeshCut3DUserObject.h:73
MeshCut3DUserObject::sortFrontNodes
void sortFrontNodes()
Sort the front nodes.
Definition: MeshCut3DUserObject.C:784
MeshCut3DUserObject::_cut_mesh
std::unique_ptr< MeshBase > _cut_mesh
The cutter mesh.
Definition: MeshCut3DUserObject.h:54
Xfem::normalizePoint
void normalizePoint(Point &p)
Definition: XFEMFuncs.C:621
MeshCut3DUserObject::joinBoundary
void joinBoundary()
Join active boundaries and inactive boundaries to be the new boundary.
Definition: MeshCut3DUserObject.C:1045
MeshCut3DUserObject::cutFragmentByGeometry
virtual bool cutFragmentByGeometry(std::vector< std::vector< Point >> &frag_edges, std::vector< Xfem::CutEdge > &cut_edges, Real time) const override
Check to see whether a fragment of a 2D element should be cut based on geometric conditions.
Definition: MeshCut3DUserObject.C:206
MeshCut3DUserObject::findFrontIntersection
void findFrontIntersection()
Find front-structure intersections.
Definition: MeshCut3DUserObject.C:790
MeshCut3DUserObject::_cut_elem_nnode
const unsigned int _cut_elem_nnode
The cutter mesh has triangluar elements only.
Definition: MeshCut3DUserObject.h:57
MeshCut3DUserObject::_stop
bool _stop
variables to help control the work flow
Definition: MeshCut3DUserObject.h:76
MeshCut3DUserObject::intersectWithEdge
virtual bool intersectWithEdge(const Point &p1, const Point &p2, const std::vector< Point > &_vertices, Point &pint) const
Check if a line intersects with an element.
Definition: MeshCut3DUserObject.C:225
MeshCut3DUserObject::triangulation
void triangulation()
Create tri3 elements between the new front and the old front.
Definition: MeshCut3DUserObject.C:958
Xfem::CutFace
Data structure defining a cut through a face.
Definition: GeometricCutUserObject.h:64
Xfem::angle_rad_3d
double angle_rad_3d(double p1[3], double p2[3], double p3[3])
Definition: XFEMFuncs.C:691
MeshCut3DUserObject::getCrackFrontPoints
virtual const std::vector< Point > getCrackFrontPoints(unsigned int num_crack_front_points) const override
get a set of points along a crack front from a XFEM GeometricCutUserObject
Definition: MeshCut3DUserObject.C:1085
MeshCut3DUserObject::cutElementByGeometry
virtual bool cutElementByGeometry(const Elem *elem, std::vector< Xfem::CutEdge > &cut_edges, std::vector< Xfem::CutNode > &cut_nodes, Real time) const override
Check to see whether a specified 2D element should be cut based on geometric conditions.
Definition: MeshCut3DUserObject.C:124
Xfem::CutEdge
Data structure defining a cut on an element edge.
Definition: GeometricCutUserObject.h:26
MeshCut3DUserObject::growFront
void growFront()
Grow the cutter mesh.
Definition: MeshCut3DUserObject.C:745
MeshCut3DUserObject::_inactive_boundary_pos
std::vector< unsigned int > _inactive_boundary_pos
Inactive boundary.
Definition: MeshCut3DUserObject.h:95