https://mooseframework.inl.gov
MeshCut2DUserObjectBase.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
12 #include "CrackFrontDefinition.h"
13 
14 #include "XFEMFuncs.h"
15 #include "MooseError.h"
16 #include "MooseMesh.h"
17 #include "libmesh/edge_edge2.h"
18 #include "libmesh/serial_mesh.h"
19 #include "libmesh/mesh_tools.h"
20 
23 {
25  params.addParam<UserObjectName>("nucleate_uo", "The MeshCutNucleation UO for nucleating cracks.");
26  params.addParam<UserObjectName>("crack_front_definition",
27  "crackFrontDefinition",
28  "The CrackFrontDefinition user object name");
29  params.addClassDescription("Creates a UserObject base class for a mesh cutter in 2D problems");
30  return params;
31 }
32 
34  : MeshCutUserObjectBase(parameters),
35  _mesh(_subproblem.mesh()),
36  _nucleate_uo(isParamValid("nucleate_uo")
37  ? &getUserObject<MeshCut2DNucleationBase>("nucleate_uo")
38  : nullptr),
39  _is_mesh_modified(false)
40 {
41  _depend_uo.insert(getParam<UserObjectName>("crack_front_definition"));
42 
43  // test element type; only line elements are allowed
44  for (const auto & cut_elem : _cutter_mesh->element_ptr_range())
45  {
46  if (cut_elem->n_nodes() != 2)
47  mooseError("The input cut mesh should include EDGE2 elements only!");
48  if (cut_elem->dim() != 1)
49  mooseError("The input cut mesh should have 1D elements (in a 2D space) only!");
50  }
51 
52  // find node fronts of the original cutmesh. This is used to order EVERYTHING.
54 }
55 
56 void
58 {
59  const auto uo_name = getParam<UserObjectName>("crack_front_definition");
61 }
62 
63 bool
65  std::vector<Xfem::CutEdge> & cut_edges,
66  std::vector<Xfem::CutNode> & cut_nodes) const
67 {
68  // With the crack defined by a line, this method cuts a 2D elements by a line
69  // Fixme lynn Copy and paste from InterfaceMeshCut2DUserObject::cutElementByGeometry
70  mooseAssert(elem->dim() == 2, "Dimension of element to be cut must be 2");
71 
72  bool elem_cut = false;
73 
74  for (const auto & cut_elem : _cutter_mesh->element_ptr_range())
75  {
76  unsigned int n_sides = elem->n_sides();
77 
78  for (unsigned int i = 0; i < n_sides; ++i)
79  {
80  std::unique_ptr<const Elem> curr_side = elem->side_ptr(i);
81 
82  mooseAssert(curr_side->type() == EDGE2, "Element side type must be EDGE2.");
83 
84  const Node * node1 = curr_side->node_ptr(0);
85  const Node * node2 = curr_side->node_ptr(1);
86  Real seg_int_frac = 0.0;
87 
88  const std::pair<Point, Point> elem_endpoints(cut_elem->node_ref(0), cut_elem->node_ref(1));
89 
90  if (Xfem::intersectSegmentWithCutLine(*node1, *node2, elem_endpoints, 1.0, seg_int_frac))
91  {
92  if (seg_int_frac > Xfem::tol && seg_int_frac < 1.0 - Xfem::tol)
93  {
94  elem_cut = true;
95  Xfem::CutEdge mycut;
96  mycut._id1 = node1->id();
97  mycut._id2 = node2->id();
98  mycut._distance = seg_int_frac;
99  mycut._host_side_id = i;
100  cut_edges.push_back(mycut);
101  }
102  else if (seg_int_frac < Xfem::tol)
103  {
104  elem_cut = true;
105  Xfem::CutNode mycut;
106  mycut._id = node1->id();
107  mycut._host_id = i;
108  cut_nodes.push_back(mycut);
109  }
110  }
111  }
112  }
113  return elem_cut;
114 }
115 
116 bool
118  std::vector<Xfem::CutFace> & /*cut_faces*/) const
119 {
120  mooseError("Invalid method for 2D mesh cutting.");
121  return false;
122 }
123 
124 bool
125 MeshCut2DUserObjectBase::cutFragmentByGeometry(std::vector<std::vector<Point>> & frag_edges,
126  std::vector<Xfem::CutEdge> & cut_edges) const
127 {
128  bool cut_frag = false;
129 
130  for (const auto & cut_elem : _cutter_mesh->element_ptr_range())
131  {
132  const std::pair<Point, Point> elem_endpoints(cut_elem->node_ref(0), cut_elem->node_ref(1));
133  unsigned int n_sides = frag_edges.size();
134  for (unsigned int i = 0; i < n_sides; ++i)
135  {
136  Real seg_int_frac = 0.0;
138  frag_edges[i][0], frag_edges[i][1], elem_endpoints, 1, seg_int_frac))
139  {
140  cut_frag = true;
141  Xfem::CutEdge mycut;
142  mycut._id1 = i;
143  mycut._id2 = (i < (n_sides - 1) ? (i + 1) : 0);
144  mycut._distance = seg_int_frac;
145  mycut._host_side_id = i;
146  cut_edges.push_back(mycut);
147  }
148  }
149  }
150  return cut_frag;
151 }
152 
153 bool
154 MeshCut2DUserObjectBase::cutFragmentByGeometry(std::vector<std::vector<Point>> & /*frag_faces*/,
155  std::vector<Xfem::CutFace> & /*cut_faces*/) const
156 {
157  mooseError("Invalid method for 2D mesh fragment cutting.");
158  return false;
159 }
160 
161 unsigned int
163 {
165 }
166 
167 const std::vector<Point>
168 MeshCut2DUserObjectBase::getCrackFrontPoints(unsigned int number_crack_front_points) const
169 {
170  std::vector<Point> crack_front_points(number_crack_front_points);
171  // number_crack_front_points is updated via
172  // _crack_front_definition->updateNumberOfCrackFrontPoints(_crack_front_points.size())
173  if (number_crack_front_points != _original_and_current_front_node_ids.size())
174  mooseError("Number of nodes in CrackFrontDefinition does not match the number of nodes in the "
175  "cutter_mesh.\nCrackFrontDefinition nodes = " +
176  Moose::stringify(number_crack_front_points) + "\ncutter_mesh nodes = " +
178 
179  for (unsigned int i = 0; i < number_crack_front_points; ++i)
180  {
182  Node * this_node = _cutter_mesh->node_ptr(id);
183  mooseAssert(this_node, "Node is NULL");
184  Point & this_point = *this_node;
185  crack_front_points[i] = this_point;
186  }
187  return crack_front_points;
188 }
189 
190 const std::vector<RealVectorValue>
191 MeshCut2DUserObjectBase::getCrackPlaneNormals(unsigned int number_crack_front_points) const
192 {
193  if (number_crack_front_points != _original_and_current_front_node_ids.size())
194  mooseError("MeshCut2DFractureUserObject::getCrackPlaneNormals: number_crack_front_points=" +
195  Moose::stringify(number_crack_front_points) +
196  " does not match the number of nodes given in "
197  "_original_and_current_front_node_ids=" +
199  ". This will happen if a crack front exits the boundary because the number of "
200  "points in the CrackFrontDefinition is never updated.");
201 
202  std::vector<std::pair<dof_id_type, RealVectorValue>> crack_plane_normals;
203  for (const auto & elem : _cutter_mesh->element_ptr_range())
204  {
205  dof_id_type id0 = elem->node_id(0);
206  dof_id_type id1 = elem->node_id(1);
207  dof_id_type id;
208 
209  auto it0 = std::find_if(_original_and_current_front_node_ids.begin(),
211  [&id0](const std::pair<dof_id_type, dof_id_type> & element)
212  { return element.second == id0; });
213  auto it1 = std::find_if(_original_and_current_front_node_ids.begin(),
215  [&id1](const std::pair<dof_id_type, dof_id_type> & element)
216  { return element.second == id1; });
217 
218  bool found_it0 = (it0 != _original_and_current_front_node_ids.end());
219  bool found_it1 = (it1 != _original_and_current_front_node_ids.end());
220 
221  // Newly nucleated crack elements can have one normal if they are on the edge OR
222  // two normals if they are in the bulk.
223  if (found_it0)
224  {
225  Point end_pt, connecting_pt;
226 
227  end_pt = elem->node_ref(0);
228  connecting_pt = elem->node_ref(1);
229  id = it0->first; // sort by original crack front node ids
230 
231  Point fracture_dir = end_pt - connecting_pt;
232  // The crack normal is orthogonal to the crack extension direction (fracture_dir),
233  // and is defined in this implementation as the cross product of the direction of crack
234  // extension with the tangent direction, which is always (0, 0, 1) in 2D.
235  RealVectorValue normal_dir{fracture_dir(1), -fracture_dir(0), 0};
236  normal_dir /= normal_dir.norm();
237  crack_plane_normals.push_back(std::make_pair(id, normal_dir));
238  }
239 
240  if (found_it1)
241  {
242  Point end_pt, connecting_pt;
243 
244  end_pt = elem->node_ref(1);
245  connecting_pt = elem->node_ref(0);
246  id = it1->first; // sort by original crack front node ids
247 
248  Point fracture_dir = end_pt - connecting_pt;
249  // The crack normal is orthogonal to the crack extension direction (fracture_dir),
250  // and is defined in this implementation as the cross product of the direction of crack
251  // extension with the tangent direction, which is always (0, 0, 1) in 2D.
252  RealVectorValue normal_dir{fracture_dir(1), -fracture_dir(0), 0};
253  normal_dir /= normal_dir.norm();
254  crack_plane_normals.push_back(std::make_pair(id, normal_dir));
255  }
256  }
257  mooseAssert(
258  _original_and_current_front_node_ids.size() == crack_plane_normals.size(),
259  "Boundary nodes are attached to more than one element. This should not happen for a 1D "
260  "cutter mesh."
261  "\n Number of _original_and_current_front_node_ids=" +
263  "\n Number of crack_plane_normals=" + Moose::stringify(crack_plane_normals.size()));
264 
265  // the crack_plane_normals are now sorted by the ORIGINAL crack front ids
266  std::sort(crack_plane_normals.begin(), crack_plane_normals.end());
267  std::vector<RealVectorValue> sorted_crack_plane_normals;
268  for (auto & crack : crack_plane_normals)
269  sorted_crack_plane_normals.push_back(crack.second);
270 
271  return sorted_crack_plane_normals;
272 }
273 
274 void
276 {
277  std::unique_ptr<PointLocatorBase> pl = _mesh.getPointLocator();
278  pl->enable_out_of_mesh_mode();
279  std::unordered_set boundary_nodes = MeshTools::find_boundary_nodes(*_cutter_mesh);
280  for (const auto & node : boundary_nodes)
281  {
282  auto node_id = node;
283  Node * this_node = _cutter_mesh->node_ptr(node_id);
284  mooseAssert(this_node, "Node is NULL");
285  Point & this_point = *this_node;
286 
287  const Elem * elem = (*pl)(this_point);
288  if (elem != NULL)
289  _original_and_current_front_node_ids.push_back(std::make_pair(node, node));
290  }
291  std::sort(_original_and_current_front_node_ids.begin(),
293 }
294 
295 void
297 {
298  dof_id_type current_front_node_id;
299  for (std::size_t i = 0; i < _original_and_current_front_node_ids.size(); ++i)
300  {
301  current_front_node_id = _original_and_current_front_node_ids[i].second;
302  // check if node front node id is active
303  auto direction_iter =
304  std::find_if(_active_front_node_growth_vectors.begin(),
306  [&current_front_node_id](const std::pair<dof_id_type, Point> & element)
307  { return element.first == current_front_node_id; });
308  // only add an element for active node front ids
309  if (direction_iter != _active_front_node_growth_vectors.end())
310  {
311  Node * this_node = _cutter_mesh->node_ptr(current_front_node_id);
312  mooseAssert(this_node, "Node is NULL");
313  Point & this_point = *this_node;
314 
315  Point new_node_offset = direction_iter->second;
316  Point x = this_point + new_node_offset;
317 
318  // TODO: Should check if cut line segment created between "this_point" and "x" crosses
319  // another line element in the cutter mesh or solid mesh boundary.
320  // Crossing another line element would be a special case that still needs to be handled,
321  // however, it doesnot cause an error, it will just ignore the other line segment and recut
322  // the solid mesh element.
323  // Crossing a solid mesh boundary would be for aesthetics reasons so
324  // that element was trimmed close to the boundary but would have not effect on the simulation.
325  // Crossing a solid mesh boundary should be handled by something like
326  // MeshCut2DRankTwoTensorNucleation::lineLineIntersect2D
327 
328  // add node to front
329  this_node = Node::build(x, _cutter_mesh->n_nodes()).release();
330  _cutter_mesh->add_node(this_node);
331  dof_id_type new_front_node_id = _cutter_mesh->n_nodes() - 1;
332 
333  // add element to front
334  std::vector<dof_id_type> elem;
335  elem.push_back(current_front_node_id);
336  elem.push_back(new_front_node_id);
337  Elem * new_elem = Elem::build(EDGE2).release();
338  for (unsigned int i = 0; i < new_elem->n_nodes(); ++i)
339  {
340  mooseAssert(_cutter_mesh->node_ptr(elem[i]) != nullptr, "Node is NULL");
341  new_elem->set_node(i, _cutter_mesh->node_ptr(elem[i]));
342  }
343  _cutter_mesh->add_elem(new_elem);
344  // now push to the end of _original_and_current_front_node_ids for tracking and fracture
345  // integrals
346  _original_and_current_front_node_ids[i].second = new_front_node_id;
347  _is_mesh_modified = true;
348  }
349  }
350  _cutter_mesh->prepare_for_use();
351 }
352 
353 void
355 {
356  if (_nucleate_uo)
357  {
358  std::map<unsigned int, std::pair<RealVectorValue, RealVectorValue>> nucleated_elems_map =
360  const Real nucleationRadius = _nucleate_uo->getNucleationRadius();
361 
362  removeNucleatedCracksTooCloseToEachOther(nucleated_elems_map, nucleationRadius);
363  removeNucleatedCracksTooCloseToExistingCracks(nucleated_elems_map, nucleationRadius);
364 
365  std::unique_ptr<PointLocatorBase> pl = _mesh.getPointLocator();
366  pl->enable_out_of_mesh_mode();
367  for (const auto & elem_nodes : nucleated_elems_map)
368  {
369  std::pair<RealVectorValue, RealVectorValue> nodes = elem_nodes.second;
370  // add nodes for the elements that define the nucleated cracks
371  Node * node_0 = Node::build(nodes.first, _cutter_mesh->n_nodes()).release();
372  _cutter_mesh->add_node(node_0);
373  dof_id_type node_id_0 = _cutter_mesh->n_nodes() - 1;
374  Node * node_1 = Node::build(nodes.second, _cutter_mesh->n_nodes()).release();
375  _cutter_mesh->add_node(node_1);
376  dof_id_type node_id_1 = _cutter_mesh->n_nodes() - 1;
377  // add elements that define nucleated cracks
378  std::vector<dof_id_type> elem;
379  elem.push_back(node_id_0);
380  elem.push_back(node_id_1);
381  Elem * new_elem = Elem::build(EDGE2).release();
382  for (unsigned int i = 0; i < new_elem->n_nodes(); ++i)
383  {
384  mooseAssert(_cutter_mesh->node_ptr(elem[i]) != nullptr, "Node is NULL");
385  new_elem->set_node(i, _cutter_mesh->node_ptr(elem[i]));
386  }
387  _cutter_mesh->add_elem(new_elem);
388  // now add the nucleated nodes to the crack id data struct
389  // edge nucleated cracks will add one node to _original_and_current_front_node_ids
390  // bulk nucleated cracks will add two nodes to _original_and_current_front_node_ids
391  Point & point_0 = *node_0;
392  const Elem * crack_front_elem_0 = (*pl)(point_0);
393  if (crack_front_elem_0 != NULL)
394  _original_and_current_front_node_ids.push_back(std::make_pair(node_id_0, node_id_0));
395 
396  Point & point_1 = *node_1;
397  const Elem * crack_front_elem_1 = (*pl)(point_1);
398  if (crack_front_elem_1 != NULL)
399  _original_and_current_front_node_ids.push_back(std::make_pair(node_id_1, node_id_1));
400 
401  _is_mesh_modified = true;
402  }
403  _cutter_mesh->prepare_for_use();
404  }
405 }
406 
407 void
409  std::map<unsigned int, std::pair<RealVectorValue, RealVectorValue>> & nucleated_elems_map,
410  const Real nucleationRadius)
411 {
412  // remove nucleated elements that are too close too each other. Lowest key wins
413  for (auto it1 = nucleated_elems_map.begin(); it1 != nucleated_elems_map.end(); ++it1)
414  {
415  std::pair<RealVectorValue, RealVectorValue> nodes = it1->second;
416  Point p2 = nodes.first;
417  Point p1 = nodes.second;
418  Point p = p1 + (p2 - p1) / 2;
419  for (auto it2 = nucleated_elems_map.begin(); it2 != nucleated_elems_map.end();)
420  {
421  if (it1 == it2)
422  {
423  ++it2;
424  continue;
425  }
426 
427  nodes = it2->second;
428  p2 = nodes.first;
429  p1 = nodes.second;
430  Point q = p1 + (p2 - p1) / 2;
431  Point pq = q - p;
432  if (pq.norm() <= nucleationRadius)
433  it2 = nucleated_elems_map.erase(it2);
434  else
435  ++it2;
436  }
437  }
438 }
439 
440 void
442  std::map<unsigned int, std::pair<RealVectorValue, RealVectorValue>> & nucleated_elems_map,
443  const Real nucleationRadius)
444 {
445  for (auto it = nucleated_elems_map.begin(); it != nucleated_elems_map.end();)
446  {
447  std::pair<RealVectorValue, RealVectorValue> nodes = it->second;
448  Point p2 = nodes.first;
449  Point p1 = nodes.second;
450  Point p = p1 + (p2 - p1) / 2;
451  bool removeNucleatedElem = false;
452  for (const auto & cutter_elem :
453  as_range(_cutter_mesh->active_elements_begin(), _cutter_mesh->active_elements_end()))
454  {
455  const Node * const * cutter_elem_nodes = cutter_elem->get_nodes();
456  Point m = *cutter_elem_nodes[1] - *cutter_elem_nodes[0];
457  Real t = m * (p - *cutter_elem_nodes[0]) / m.norm_sq();
458  Real d = std::numeric_limits<Real>::max();
459  if (t <= 0)
460  {
461  Point j = p - *cutter_elem_nodes[0];
462  d = j.norm();
463  }
464  else if (t >= 0)
465  {
466  Point j = p - *cutter_elem_nodes[1];
467  d = j.norm();
468  }
469  else
470  {
471  Point j = p - (*cutter_elem_nodes[0] + t * m);
472  d = j.norm();
473  }
474  if (d <= nucleationRadius)
475  {
476  removeNucleatedElem = true;
477  break;
478  }
479  }
480  if (removeNucleatedElem)
481  it = nucleated_elems_map.erase(it);
482  else
483  ++it;
484  }
485 }
Data structure defining a cut through a node.
auto norm() const
virtual bool cutElementByGeometry(const Elem *elem, std::vector< Xfem::CutEdge > &cut_edges, std::vector< Xfem::CutNode > &cut_nodes) const override
T & getUserObject(const std::string &name, unsigned int tid=0) const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
static InputParameters validParams()
void findOriginalCrackFrontNodes()
Find the original crack front nodes in the cutter mesh and use to populate _original_and_current_fron...
MeshCut2DUserObjectBase(const InputParameters &parameters)
static InputParameters validParams()
unsigned int _host_id
Local ID of this node in the host element.
MeshBase & mesh
std::set< std::string > _depend_uo
std::vector< std::pair< dof_id_type, dof_id_type > > _original_and_current_front_node_ids
This vector of pairs orders crack tips to make the order used in this class the same as those for the...
virtual bool cutFragmentByGeometry(std::vector< std::vector< Point >> &frag_edges, std::vector< Xfem::CutEdge > &cut_edges) const override
Data structure defining a cut on an element edge.
FEProblemBase & _fe_problem
Class used in fracture integrals to define geometric characteristics of the crack front...
Real getNucleationRadius() const
Provide getter to MeshCut2DUserObjectBase for member data set in input.
unsigned int _id1
ID of the first node on the edge.
const std::vector< double > x
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
std::string stringify(const T &t)
static const double tol
Definition: XFEMFuncs.h:21
bool intersectSegmentWithCutLine(const Point &segment_point1, const Point &segment_point2, const std::pair< Point, Point > &cutting_line_points, const Real &cutting_line_fraction, Real &segment_intersection_fraction)
Determine whether a line segment is intersected by a cutting line, and compute the fraction along tha...
Definition: XFEMFuncs.C:774
MooseMesh & _mesh
The FE solution mesh.
std::map< unsigned int, std::pair< RealVectorValue, RealVectorValue > > getNucleatedElemsMap() const
Provide getter to MeshCut2DUserObjectBase for a map of nucleated cracks.
void addNucleatedCracksToMesh()
Calls into MeshCutNucleation UO to add cracks.
const MeshCut2DNucleationBase * _nucleate_uo
2D UO for nucleating cracks
virtual void initialSetup() override final
Real _distance
Fractional distance along the edge (from node 1 to 2) where the cut is located.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
unsigned int _id2
ID of the second node on the edge.
Simple base class for XFEM cutting objects that use a mesh to cut.
const Real p
bool _is_mesh_modified
Indicator that shows if the cutting mesh is modified or not in this calculation step.
virtual unsigned int getNumberOfCrackFrontPoints() const override
Get the current number of crack front points.
void mooseError(Args &&... args) const
unsigned int _host_side_id
Local ID of this side in the host element.
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
void addClassDescription(const std::string &doc_string)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
virtual std::unique_ptr< libMesh::PointLocatorBase > getPointLocator() const
void growFront()
grow the cutter mesh
void removeNucleatedCracksTooCloseToExistingCracks(std::map< unsigned int, std::pair< RealVectorValue, RealVectorValue >> &nucleated_elems_map, Real nucleationRadius)
Remove nucleated cracks that are too close to a pre-existing crack in the mesh.
std::unique_ptr< MeshBase > _cutter_mesh
The xfem cutter mesh.
void removeNucleatedCracksTooCloseToEachOther(std::map< unsigned int, std::pair< RealVectorValue, RealVectorValue >> &nucleated_elems_map, Real nucleationRadius)
Remove nucleated cracks that are too close too each other.
std::vector< std::pair< dof_id_type, Point > > _active_front_node_growth_vectors
contains the active node ids and their growth vectors
unsigned int _id
ID of the cut node.
virtual const std::vector< RealVectorValue > getCrackPlaneNormals(unsigned int num_crack_front_points) const override
get a set of normal vectors along a crack front from a XFEM GeometricCutUserObject CrackFrontDefiniti...
CrackFrontDefinition * _crack_front_definition
user object for communicating between solid_mechanics interaction integrals and xfem cutter mesh ...
uint8_t dof_id_type