https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
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
56void
58{
59 const auto uo_name = getParam<UserObjectName>("crack_front_definition");
61}
62
63bool
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
116bool
118 std::vector<Xfem::CutFace> & /*cut_faces*/) const
119{
120 mooseError("Invalid method for 2D mesh cutting.");
121 return false;
122}
123
124bool
125MeshCut2DUserObjectBase::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
153bool
154MeshCut2DUserObjectBase::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
161unsigned int
166
167const std::vector<Point>
168MeshCut2DUserObjectBase::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 {
181 dof_id_type id = _original_and_current_front_node_ids[i].second;
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
190const std::vector<RealVectorValue>
191MeshCut2DUserObjectBase::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
274void
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
295void
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
353void
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
407void
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
440void
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}
const std::vector< double > x
const Real p
Class used in fracture integrals to define geometric characteristics of the crack front.
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)
void addClassDescription(const std::string &doc_string)
std::map< unsigned int, std::pair< RealVectorValue, RealVectorValue > > getNucleatedElemsMap() const
Provide getter to MeshCut2DUserObjectBase for a map of nucleated cracks.
Real getNucleationRadius() const
Provide getter to MeshCut2DUserObjectBase for member data set in input.
void addNucleatedCracksToMesh()
Calls into MeshCutNucleation UO to add cracks.
void growFront()
grow the cutter mesh
void findOriginalCrackFrontNodes()
Find the original crack front nodes in the cutter mesh and use to populate _original_and_current_fron...
virtual unsigned int getNumberOfCrackFrontPoints() const override
Get the current number of crack front points.
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
virtual bool cutFragmentByGeometry(std::vector< std::vector< Point > > &frag_edges, std::vector< Xfem::CutEdge > &cut_edges) const override
static InputParameters validParams()
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.
const MeshCut2DNucleationBase * _nucleate_uo
2D UO for nucleating cracks
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...
std::vector< std::pair< dof_id_type, Point > > _active_front_node_growth_vectors
contains the active node ids and their growth vectors
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...
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.
virtual bool cutElementByGeometry(const Elem *elem, std::vector< Xfem::CutEdge > &cut_edges, std::vector< Xfem::CutNode > &cut_nodes) const override
bool _is_mesh_modified
Indicator that shows if the cutting mesh is modified or not in this calculation step.
virtual void initialSetup() override final
MeshCut2DUserObjectBase(const InputParameters &parameters)
MooseMesh & _mesh
The FE solution mesh.
CrackFrontDefinition * _crack_front_definition
user object for communicating between solid_mechanics interaction integrals and xfem cutter mesh
Simple base class for XFEM cutting objects that use a mesh to cut.
static InputParameters validParams()
std::unique_ptr< MeshBase > _cutter_mesh
The xfem cutter mesh.
void mooseError(Args &&... args) const
virtual std::unique_ptr< libMesh::PointLocatorBase > getPointLocator() const
std::set< std::string > _depend_uo
FEProblemBase & _fe_problem
MeshBase & mesh
std::string stringify(const T &t)
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
static const double tol
Definition XFEMFuncs.h:21
Data structure defining a cut on an element edge.
unsigned int _id1
ID of the first node on the edge.
Real _distance
Fractional distance along the edge (from node 1 to 2) where the cut is located.
unsigned int _id2
ID of the second node on the edge.
unsigned int _host_side_id
Local ID of this side in the host element.
Data structure defining a cut through a node.
unsigned int _id
ID of the cut node.
unsigned int _host_id
Local ID of this node in the host element.