https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PenetrationThread.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
10// Moose
11#include "PenetrationThread.h"
12#include "ParallelUniqueId.h"
13#include "FindContactPoint.h"
14#include "NearestNodeLocator.h"
15#include "SubProblem.h"
16#include "MooseVariableFE.h"
17#include "MooseMesh.h"
18#include "MooseUtils.h"
19
20#include "libmesh/threads.h"
21
22#include <algorithm>
23
24// Anonymous namespace for helper functions that ought to be moved
25// into libMesh
26namespace
27{
28Point
29closest_point_to_edge(const Point & src, const Point & p0, const Point & p1)
30{
31 const Point line01 = p1 - p0;
32 const Real line0c_xi = ((src - p0) * line01) / line01.norm_sq();
33 // The projection would be behind p0; p0 is closest
34 if (line0c_xi <= 0)
35 return p0;
36 // The projection would be past p1; p1 is closest
37 if (line0c_xi >= 1)
38 return p1;
39 // The projection is on the segment between p0 to p1.
40 return p0 + line0c_xi * line01;
41}
42
43Point
44closest_point_to_side(const Point & src, const Elem & side)
45{
46 switch (side.type())
47 {
48 case EDGE2:
49 case EDGE3:
50 case EDGE4:
51 mooseAssert(side.has_affine_map(),
52 "Penetration of elements with curved sides not implemented");
53 return closest_point_to_edge(src, side.point(0), side.point(1));
54 case TRI3:
55 case TRI6:
56 {
57 mooseAssert(side.has_affine_map(),
58 "Penetration of elements with curved sides not implemented");
59 const Point p0 = side.point(0), p1 = side.point(1), p2 = side.point(2);
60 const Point l01 = p1 - p0, l02 = p2 - p0;
61 const Point tri_normal = (l01.cross(l02)).unit();
62 const Point linecs = ((src - p0) * tri_normal) / tri_normal.norm_sq() * tri_normal;
63 const Point in_plane = src - linecs;
64 const Point planar_offset = in_plane - p0;
65 // If we're outside the triangle past line 01, our closest point
66 // is on that line.
67 if (planar_offset.cross(l01) * tri_normal > 0)
68 return closest_point_to_edge(src, p0, p1);
69 // If we're outside the triangle past line 02, our closest point
70 // is on that line.
71 if (planar_offset.cross(l02) * tri_normal < 0)
72 return closest_point_to_edge(src, p0, p2);
73 // If we're outside the triangle past line 12, our closest point
74 // is on that line.
75 if ((in_plane - p1).cross(p2 - p1) * tri_normal > 0)
76 return closest_point_to_edge(src, p1, p2);
77 // We must be inside the triangle!
78 return in_plane;
79 }
80 case QUAD4:
81 case QUAD8:
82 case QUAD9:
83 case C0POLYGON:
84 mooseError("Not implemented");
85 default:
86 mooseError("Side type not recognized");
87 break;
88 }
89}
90
91} // anonymous namespace
92
93// Mutex to use when accessing _penetration_info;
95
97 SubProblem & subproblem,
98 const MooseMesh & mesh,
99 BoundaryID primary_boundary,
100 BoundaryID secondary_boundary,
101 std::map<dof_id_type, PenetrationInfo *> & penetration_info,
102 bool check_whether_reasonable,
103 bool update_location,
104 Real tangential_tolerance,
105 bool do_normal_smoothing,
106 Real normal_smoothing_distance,
107 PenetrationLocator::NORMAL_SMOOTHING_METHOD normal_smoothing_method,
108 bool use_point_locator,
109 std::vector<std::vector<FEBase *>> & fes,
110 FEType & fe_type,
111 NearestNodeLocator & nearest_node,
112 const std::unordered_map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map)
113 : _subproblem(subproblem),
114 _mesh(mesh),
115 _primary_boundary(primary_boundary),
116 _secondary_boundary(secondary_boundary),
117 _penetration_info(penetration_info),
118 _check_whether_reasonable(check_whether_reasonable),
119 _update_location(update_location),
120 _tangential_tolerance(tangential_tolerance),
121 _do_normal_smoothing(do_normal_smoothing),
122 _normal_smoothing_distance(normal_smoothing_distance),
123 _normal_smoothing_method(normal_smoothing_method),
124 _use_point_locator(use_point_locator),
125 _nodal_normal_x(NULL),
126 _nodal_normal_y(NULL),
127 _nodal_normal_z(NULL),
128 _fes(fes),
129 _fe_type(fe_type),
130 _nearest_node(nearest_node),
131 _node_to_elem_map(node_to_elem_map)
132{
133}
134
135// Splitting Constructor
137 : _subproblem(x._subproblem),
138 _mesh(x._mesh),
139 _primary_boundary(x._primary_boundary),
140 _secondary_boundary(x._secondary_boundary),
141 _penetration_info(x._penetration_info),
142 _check_whether_reasonable(x._check_whether_reasonable),
143 _update_location(x._update_location),
144 _tangential_tolerance(x._tangential_tolerance),
145 _do_normal_smoothing(x._do_normal_smoothing),
146 _normal_smoothing_distance(x._normal_smoothing_distance),
147 _normal_smoothing_method(x._normal_smoothing_method),
148 _use_point_locator(x._use_point_locator),
149 _fes(x._fes),
150 _fe_type(x._fe_type),
151 _nearest_node(x._nearest_node),
152 _node_to_elem_map(x._node_to_elem_map)
153{
154}
155
156void
158{
159 ParallelUniqueId puid;
160 _tid = puid.id;
161
162 // Must get the variables every time this is run because _tid can change
165 {
169 }
170
171 const BoundaryInfo & boundary_info = _mesh.getMesh().get_boundary_info();
172 std::unique_ptr<PointLocatorBase> point_locator;
174 point_locator = _mesh.getPointLocator();
175
176 for (const auto & node_id : range)
177 {
178 const Node & node = _mesh.nodeRef(node_id);
179
180 // We're going to get a reference to the pointer for the pinfo for this node
181 // This will allow us to manipulate this pointer without having to go through
182 // the _penetration_info map... meaning this is the only mutex we'll have to do!
184 PenetrationInfo *& info = _penetration_info[node.id()];
186
187 std::vector<PenetrationInfo *> p_info;
188 bool info_set(false);
189
190 // See if we already have info about this node
191 if (info)
192 {
193 FEBase * fe_elem = _fes[_tid][info->_elem->dim()];
194 FEBase * fe_side = _fes[_tid][info->_side->dim()];
195
196 if (!_update_location && (info->_distance >= 0 || info->isCaptured()))
197 {
198 const Point contact_ref = info->_closest_point_ref;
199 bool contact_point_on_side(false);
200
201 // Secondary position must be the previous contact point
202 // Use the previous reference coordinates
203 std::vector<Point> points(1);
204 points[0] = contact_ref;
205 const std::vector<Point> & secondary_pos = fe_side->get_xyz();
206 bool search_succeeded = false;
207
209 fe_elem,
210 fe_side,
211 _fe_type,
212 secondary_pos[0],
213 false,
215 contact_point_on_side,
216 search_succeeded);
217
218 // Restore the original reference coordinates
219 info->_closest_point_ref = contact_ref;
220 // Just calculated as the distance of the contact point off the surface (0). Set to 0 to
221 // avoid round-off.
222 info->_distance = 0.0;
223 info_set = true;
224 }
225 else
226 {
227 Real old_tangential_distance(info->_tangential_distance);
228 bool contact_point_on_side(false);
229 bool search_succeeded = false;
230
232 fe_elem,
233 fe_side,
234 _fe_type,
235 node,
236 false,
238 contact_point_on_side,
239 search_succeeded);
240
241 if (contact_point_on_side)
242 {
243 if (info->_tangential_distance <= 0.0) // on the face
244 {
245 info_set = true;
246 }
247 else if (info->_tangential_distance > 0.0 && old_tangential_distance > 0.0)
248 { // off the face but within tolerance, was that way on the last step too
249 if (info->_side->dim() == 2 && info->_off_edge_nodes.size() < 2)
250 { // Closest point on face is on a node rather than an edge. Another
251 // face might be a better candidate.
252 }
253 else
254 {
255 info_set = true;
256 }
257 }
258 }
259 }
260 }
261
262 if (!info_set)
263 {
264 const Node * closest_node = _nearest_node.nearestNode(node.id());
265
266 std::vector<dof_id_type> located_elem_ids;
267 const std::vector<dof_id_type> * closest_elems;
268
270 {
271 std::set<const Elem *> candidate_elements;
272 (*point_locator)(*closest_node, candidate_elements);
273
274 if (candidate_elements.empty())
275 mooseError("No proximate elements found at node ",
276 closest_node->id(),
277 " at ",
278 cast_ref<const Point &>(*closest_node),
279 " on boundary ",
281 ". This should never happen.");
282
283 for (const Elem * elem : candidate_elements)
284 {
285 for (auto s : elem->side_index_range())
286 if (boundary_info.has_boundary_id(elem, s, _primary_boundary))
287 {
288 located_elem_ids.push_back(elem->id());
289 break;
290 }
291 }
292
293 if (located_elem_ids.empty())
294 mooseError("No proximate elements found at node ",
295 closest_node->id(),
296 " at ",
297 cast_ref<const Point &>(*closest_node),
298 " on boundary ",
300 " share that boundary. This may happen if the mesh uses the same boundary id "
301 "for a nodeset and an unrelated sideset.");
302
303 closest_elems = &located_elem_ids;
304 }
305 else
306 {
307 auto node_to_elem_pair = _node_to_elem_map.find(closest_node->id());
308 mooseAssert(node_to_elem_pair != _node_to_elem_map.end(),
309 "Missing entry in node to elem map");
310 closest_elems = &(node_to_elem_pair->second);
311 }
312
313 for (const auto & elem_id : *closest_elems)
314 {
315 const Elem * elem = _mesh.elemPtr(elem_id);
316
317 std::vector<PenetrationInfo *> thisElemInfo;
318
319 std::vector<const Node *> nodesThatMustBeOnSide;
320 // If we have a disconnected mesh, we might not have *any*
321 // nodes that must be on a side we check; we'll rely on
322 // boundary info to find valid sides, then rely on comparing
323 // closest points from each to find the best.
324 //
325 // If we don't have a disconnected mesh, then for maximum
326 // backwards compatibility we're still using the older ridge
327 // and peak detection code, which depends on us ruling out
328 // sides that don't touch closest_node.
330 nodesThatMustBeOnSide.push_back(closest_node);
332 thisElemInfo, p_info, &node, elem, nodesThatMustBeOnSide, _check_whether_reasonable);
333 }
334
336 {
337 Real min_distance_sq = std::numeric_limits<Real>::max();
338 Point best_point;
339 unsigned int best_i = invalid_uint;
340
341 // Find closest point in all p_info to the node of interest
342 for (unsigned int i = 0; i < p_info.size(); ++i)
343 {
344 const Point closest_point = closest_point_to_side(node, *p_info[i]->_side);
345 const Real distance_sq = (closest_point - node).norm_sq();
346 if (distance_sq < min_distance_sq)
347 {
348 min_distance_sq = distance_sq;
349 best_point = closest_point;
350 best_i = i;
351 }
352 }
353
354 p_info[best_i]->_closest_point = best_point;
355 p_info[best_i]->_distance =
356 (p_info[best_i]->_distance >= 0.0 ? 1.0 : -1.0) * std::sqrt(min_distance_sq);
358 mooseError("Normal smoothing not implemented with point locator code");
359 Point normal = (best_point - node).unit();
360 const Real dot = normal * p_info[best_i]->_normal;
361 if (dot < 0)
362 normal *= -1;
363 p_info[best_i]->_normal = normal;
364
365 switchInfo(info, p_info[best_i]);
366 info_set = true;
367 }
368 else
369 {
370 if (p_info.size() == 1)
371 {
372 if (p_info[0]->_tangential_distance <= _tangential_tolerance)
373 {
374 switchInfo(info, p_info[0]);
375 info_set = true;
376 }
377 }
378 else if (p_info.size() > 1)
379 {
380 // Loop through all pairs of faces, and check for contact on ridge between each face pair
381 std::vector<RidgeData> ridgeDataVec;
382 for (unsigned int i = 0; i + 1 < p_info.size(); ++i)
383 for (unsigned int j = i + 1; j < p_info.size(); ++j)
384 {
385 Point closest_coor;
386 Real tangential_distance(0.0);
387 const Node * closest_node_on_ridge = NULL;
388 unsigned int index = 0;
389 Point closest_coor_ref;
390 bool found_ridge_contact_point = findRidgeContactPoint(closest_coor,
391 tangential_distance,
392 closest_node_on_ridge,
393 index,
394 closest_coor_ref,
395 p_info,
396 i,
397 j);
398 if (found_ridge_contact_point)
399 {
400 RidgeData rpd;
401 rpd._closest_coor = closest_coor;
402 rpd._tangential_distance = tangential_distance;
403 rpd._closest_node = closest_node_on_ridge;
404 rpd._index = index;
405 rpd._closest_coor_ref = closest_coor_ref;
406 ridgeDataVec.push_back(rpd);
407 }
408 }
409
410 if (ridgeDataVec.size() > 0) // Either find the ridge pair that is the best or find a peak
411 {
412 // Group together ridges for which we are off the edge of a common node.
413 // Those are peaks.
414 std::vector<RidgeSetData> ridgeSetDataVec;
415 for (unsigned int i = 0; i < ridgeDataVec.size(); ++i)
416 {
417 bool foundSetWithMatchingNode = false;
418 for (unsigned int j = 0; j < ridgeSetDataVec.size(); ++j)
419 {
420 if (ridgeDataVec[i]._closest_node != NULL &&
421 ridgeDataVec[i]._closest_node == ridgeSetDataVec[j]._closest_node)
422 {
423 foundSetWithMatchingNode = true;
424 ridgeSetDataVec[j]._ridge_data_vec.push_back(ridgeDataVec[i]);
425 break;
426 }
427 }
428 if (!foundSetWithMatchingNode)
429 {
430 RidgeSetData rsd;
431 rsd._distance = std::numeric_limits<Real>::max();
432 rsd._ridge_data_vec.push_back(ridgeDataVec[i]);
433 rsd._closest_node = ridgeDataVec[i]._closest_node;
434 ridgeSetDataVec.push_back(rsd);
435 }
436 }
437 // Compute distance to each set of ridges
438 for (unsigned int i = 0; i < ridgeSetDataVec.size(); ++i)
439 {
440 if (ridgeSetDataVec[i]._closest_node !=
441 NULL) // Either a peak or off the edge of single ridge
442 {
443 if (ridgeSetDataVec[i]._ridge_data_vec.size() == 1) // off edge of single ridge
444 {
445 if (ridgeSetDataVec[i]._ridge_data_vec[0]._tangential_distance <=
446 _tangential_tolerance) // off within tolerance
447 {
448 ridgeSetDataVec[i]._closest_coor =
449 ridgeSetDataVec[i]._ridge_data_vec[0]._closest_coor;
450 Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
451 ridgeSetDataVec[i]._distance = contact_point_vec.norm();
452 }
453 }
454 else // several ridges join at common node to make a peak. The common node is the
455 // contact point
456 {
457 ridgeSetDataVec[i]._closest_coor = *ridgeSetDataVec[i]._closest_node;
458 Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
459 ridgeSetDataVec[i]._distance = contact_point_vec.norm();
460 }
461 }
462 else // on a single ridge
463 {
464 ridgeSetDataVec[i]._closest_coor =
465 ridgeSetDataVec[i]._ridge_data_vec[0]._closest_coor;
466 Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
467 ridgeSetDataVec[i]._distance = contact_point_vec.norm();
468 }
469 }
470 // Find the set of ridges closest to us.
471 unsigned int closest_ridge_set_index(0);
472 Real closest_distance(ridgeSetDataVec[0]._distance);
473 Point closest_point(ridgeSetDataVec[0]._closest_coor);
474 for (unsigned int i = 1; i < ridgeSetDataVec.size(); ++i)
475 {
476 if (ridgeSetDataVec[i]._distance < closest_distance)
477 {
478 closest_ridge_set_index = i;
479 closest_distance = ridgeSetDataVec[i]._distance;
480 closest_point = ridgeSetDataVec[i]._closest_coor;
481 }
482 }
483
484 if (closest_distance <
485 std::numeric_limits<Real>::max()) // contact point is on the closest ridge set
486 {
487 // find the face in the ridge set with the smallest index, assign that one to the
488 // interaction
489 // TODO: We may need to select the face with the largest projected distance
490 // rather than the smallest index, similar to what is done when picking the
491 // face in findRidgeContactPoint() to better condition corner-case checks for
492 // sign changes. That's less likely to be a problem here, though, and the
493 // code to do that would be messier.
494 unsigned int face_index(std::numeric_limits<unsigned int>::max());
495 for (unsigned int i = 0;
496 i < ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec.size();
497 ++i)
498 {
499 if (ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[i]._index < face_index)
500 face_index = ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[i]._index;
501 }
502
503 mooseAssert(face_index < std::numeric_limits<unsigned int>::max(),
504 "face_index invalid");
505
506 p_info[face_index]->_closest_point = closest_point;
507 p_info[face_index]->_distance =
508 (p_info[face_index]->_distance >= 0.0 ? 1.0 : -1.0) * closest_distance;
509 // Calculate the normal as the vector from the ridge to the point only if we're not
510 // doing normal
511 // smoothing. Normal smoothing will average out the normals on its own.
513 {
514 Point normal(closest_point - node);
515 const Real len(normal.norm());
516 if (len > 0)
517 {
518 normal /= len;
519 }
520 const Real dot(normal * p_info[face_index]->_normal);
521 if (dot < 0)
522 normal *= -1;
523 p_info[face_index]->_normal = normal;
524 }
525 p_info[face_index]->_tangential_distance = 0.0;
526
527 Point closest_point_ref;
528 if (ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec.size() ==
529 1) // contact with a single ridge rather than a peak
530 {
531 p_info[face_index]->_tangential_distance = ridgeSetDataVec[closest_ridge_set_index]
532 ._ridge_data_vec[0]
533 ._tangential_distance;
534 p_info[face_index]->_closest_point_ref =
535 ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[0]._closest_coor_ref;
536 }
537 else
538 { // peak
539 const Node * closest_node_on_face;
540 bool restricted = restrictPointToFace(p_info[face_index]->_closest_point_ref,
541 closest_node_on_face,
542 p_info[face_index]->_side);
543 if (restricted)
544 {
545 if (closest_node_on_face !=
546 ridgeSetDataVec[closest_ridge_set_index]._closest_node)
547 {
548 mooseError("Closest node when restricting point to face != closest node from "
549 "RidgeSetData");
550 }
551 }
552 }
553
554 FEBase * fe = _fes[_tid][p_info[face_index]->_side->dim()];
555 std::vector<Point> points(1);
556 points[0] = p_info[face_index]->_closest_point_ref;
557 fe->reinit(p_info[face_index]->_side, &points);
558 p_info[face_index]->_side_phi = fe->get_phi();
559 p_info[face_index]->_side_grad_phi = fe->get_dphi();
560 p_info[face_index]->_dxyzdxi = fe->get_dxyzdxi();
561 p_info[face_index]->_dxyzdeta = fe->get_dxyzdeta();
562 p_info[face_index]->_d2xyzdxideta = fe->get_d2xyzdxideta();
563
564 switchInfo(info, p_info[face_index]);
565 info_set = true;
566 }
567 else
568 { // todo:remove invalid ridge cases so they don't mess up individual face
569 // competition????
570 }
571 }
572
573 if (!info_set) // contact wasn't on a ridge -- compete individual interactions
574 {
575 unsigned int best(0), i(1);
576 do
577 {
578 CompeteInteractionResult CIResult = competeInteractions(p_info[best], p_info[i]);
579 if (CIResult == FIRST_WINS)
580 {
581 i++;
582 }
583 else if (CIResult == SECOND_WINS)
584 {
585 best = i;
586 i++;
587 }
588 else if (CIResult == NEITHER_WINS)
589 {
590 best = i + 1;
591 i += 2;
592 }
593 } while (i < p_info.size() && best < p_info.size());
594 if (best < p_info.size())
595 {
596 // Ensure final info is within the tangential tolerance
597 if (p_info[best]->_tangential_distance <= _tangential_tolerance)
598 {
599 switchInfo(info, p_info[best]);
600 info_set = true;
601 }
602 }
603 }
604 }
605 }
606 }
607
608 if (!info_set)
609 {
610 // If penetration is not detected within the saved patch, it is possible
611 // that the secondary node has moved outside the saved patch. So, the patch
612 // for the secondary nodes saved in _recheck_secondary_nodes has to be updated
613 // and the penetration detection has to be re-run on the updated patch.
614
615 _recheck_secondary_nodes.push_back(node_id);
616
617 delete info;
618 info = NULL;
619 }
620 else
621 {
622 smoothNormal(info, p_info, node);
623 FEBase * fe = _fes[_tid][info->_side->dim()];
624 computeSlip(*fe, *info);
625 }
626
627 for (unsigned int j = 0; j < p_info.size(); ++j)
628 {
629 if (p_info[j])
630 {
631 delete p_info[j];
632 p_info[j] = NULL;
633 }
634 }
635 }
636}
637
638void
645
646void
648{
649 mooseAssert(infoNew != NULL, "infoNew object is null");
650 if (info)
651 {
652 infoNew->_starting_elem = info->_starting_elem;
653 infoNew->_starting_side_num = info->_starting_side_num;
654 infoNew->_starting_closest_point_ref = info->_starting_closest_point_ref;
655 infoNew->_incremental_slip = info->_incremental_slip;
656 infoNew->_accumulated_slip = info->_accumulated_slip;
657 infoNew->_accumulated_slip_old = info->_accumulated_slip_old;
658 infoNew->_frictional_energy = info->_frictional_energy;
659 infoNew->_frictional_energy_old = info->_frictional_energy_old;
660 infoNew->_contact_force = info->_contact_force;
661 infoNew->_contact_force_old = info->_contact_force_old;
662 infoNew->_lagrange_multiplier = info->_lagrange_multiplier;
663 infoNew->_lagrange_multiplier_slip = info->_lagrange_multiplier_slip;
664 infoNew->_locked_this_step = info->_locked_this_step;
665 infoNew->_stick_locked_this_step = info->_stick_locked_this_step;
666 infoNew->_mech_status = info->_mech_status;
667 infoNew->_mech_status_old = info->_mech_status_old;
668 }
669 else
670 {
671 infoNew->_starting_elem = infoNew->_elem;
672 infoNew->_starting_side_num = infoNew->_side_num;
674 }
675 delete info;
676 info = infoNew;
677 infoNew = NULL; // Set this to NULL so that we don't delete it (now owned by _penetration_info).
678}
679
682{
683
685
687 pi2->_tangential_distance > _tangential_tolerance) // out of tol on both faces
688 result = NEITHER_WINS;
689
690 else if (pi1->_tangential_distance == 0.0 &&
691 pi2->_tangential_distance > 0.0) // on face 1, off face 2
692 result = FIRST_WINS;
693
694 else if (pi2->_tangential_distance == 0.0 &&
695 pi1->_tangential_distance > 0.0) // on face 2, off face 1
696 result = SECOND_WINS;
697
699 pi2->_tangential_distance > _tangential_tolerance) // in face 1 tol, out of face 2 tol
700 result = FIRST_WINS;
701
703 pi1->_tangential_distance > _tangential_tolerance) // in face 2 tol, out of face 1 tol
704 result = SECOND_WINS;
705
706 else if (pi1->_tangential_distance == 0.0 && pi2->_tangential_distance == 0.0) // on both faces
707 result = competeInteractionsBothOnFace(pi1, pi2);
708
710 pi2->_tangential_distance <= _tangential_tolerance) // off but within tol of both faces
711 {
713 if (cer == COMMON_EDGE || cer == COMMON_NODE) // ridge case.
714 {
715 // We already checked for ridges, and it got rejected, so neither face must be valid
716 result = NEITHER_WINS;
717 // mooseError("Erroneously encountered ridge case");
718 }
719 else if (cer == EDGE_AND_COMMON_NODE) // off side of face, off corner of another face. Favor
720 // the off-side face
721 {
722 if (pi1->_off_edge_nodes.size() == pi2->_off_edge_nodes.size())
723 mooseError("Invalid off_edge_nodes counts");
724
725 else if (pi1->_off_edge_nodes.size() == 2)
726 result = FIRST_WINS;
727
728 else if (pi2->_off_edge_nodes.size() == 2)
729 result = SECOND_WINS;
730
731 else
732 mooseError("Invalid off_edge_nodes counts");
733 }
734 else // The node projects to both faces within tangential tolerance.
735 result = competeInteractionsBothOnFace(pi1, pi2);
736 }
737
738 return result;
739}
740
743{
745
746 if (pi1->_distance >= 0.0 && pi2->_distance < 0.0)
747 result = FIRST_WINS; // favor face with positive distance (penetrated) -- first in this case
748
749 else if (pi2->_distance >= 0.0 && pi1->_distance < 0.0)
750 result = SECOND_WINS; // favor face with positive distance (penetrated) -- second in this case
751
752 // TODO: This logic below could cause an abrupt jump from one face to the other with small mesh
753 // movement. If there is some way to smooth the transition, we should do it.
754 else if (MooseUtils::relativeFuzzyLessThan(std::abs(pi1->_distance), std::abs(pi2->_distance)))
755 result = FIRST_WINS; // otherwise, favor the closer face -- first in this case
756
757 else if (MooseUtils::relativeFuzzyLessThan(std::abs(pi2->_distance), std::abs(pi1->_distance)))
758 result = SECOND_WINS; // otherwise, favor the closer face -- second in this case
759
760 else // Equal within tolerance. Favor the one with a smaller element id (for repeatibility)
761 {
762 if (pi1->_elem->id() < pi2->_elem->id())
763 result = FIRST_WINS;
764
765 else
766 result = SECOND_WINS;
767 }
768
769 return result;
770}
771
774{
775 CommonEdgeResult common_edge(NO_COMMON);
776 const std::vector<const Node *> & off_edge_nodes1 = pi1->_off_edge_nodes;
777 const std::vector<const Node *> & off_edge_nodes2 = pi2->_off_edge_nodes;
778 const unsigned dim1 = pi1->_side->dim();
779
780 if (dim1 == 1)
781 {
782 mooseAssert(pi2->_side->dim() == 1, "Incompatible dimensions.");
783 mooseAssert(off_edge_nodes1.size() < 2 && off_edge_nodes2.size() < 2,
784 "off_edge_nodes size should be <2 for 2D contact");
785 if (off_edge_nodes1.size() == 1 && off_edge_nodes2.size() == 1 &&
786 off_edge_nodes1[0] == off_edge_nodes2[0])
787 common_edge = COMMON_EDGE;
788 }
789 else
790 {
791 mooseAssert(dim1 == 2 && pi2->_side->dim() == 2, "Incompatible dimensions.");
792 mooseAssert(off_edge_nodes1.size() < 3 && off_edge_nodes2.size() < 3,
793 "off_edge_nodes size should be <3 for 3D contact");
794 if (off_edge_nodes1.size() == 1)
795 {
796 if (off_edge_nodes2.size() == 1)
797 {
798 if (off_edge_nodes1[0] == off_edge_nodes2[0])
799 common_edge = COMMON_NODE;
800 }
801 else if (off_edge_nodes2.size() == 2)
802 {
803 if (off_edge_nodes1[0] == off_edge_nodes2[0] || off_edge_nodes1[0] == off_edge_nodes2[1])
804 common_edge = EDGE_AND_COMMON_NODE;
805 }
806 }
807 else if (off_edge_nodes1.size() == 2)
808 {
809 if (off_edge_nodes2.size() == 1)
810 {
811 if (off_edge_nodes1[0] == off_edge_nodes2[0] || off_edge_nodes1[1] == off_edge_nodes2[0])
812 common_edge = EDGE_AND_COMMON_NODE;
813 }
814 else if (off_edge_nodes2.size() == 2)
815 {
816 if ((off_edge_nodes1[0] == off_edge_nodes2[0] &&
817 off_edge_nodes1[1] == off_edge_nodes2[1]) ||
818 (off_edge_nodes1[1] == off_edge_nodes2[0] && off_edge_nodes1[0] == off_edge_nodes2[1]))
819 common_edge = COMMON_EDGE;
820 }
821 }
822 }
823 return common_edge;
824}
825
826bool
828 Real & tangential_distance,
829 const Node *& closest_node,
830 unsigned int & index,
831 Point & contact_point_ref,
832 std::vector<PenetrationInfo *> & p_info,
833 const unsigned int index1,
834 const unsigned int index2)
835{
836 tangential_distance = 0.0;
837 closest_node = NULL;
838 PenetrationInfo * pi1 = p_info[index1];
839 PenetrationInfo * pi2 = p_info[index2];
840 const unsigned sidedim(pi1->_side->dim());
841 mooseAssert(sidedim == pi2->_side->dim(), "Incompatible dimensionalities");
842
843 // Nodes on faces for the two interactions
844 std::vector<const Node *> side1_nodes;
845 getSideCornerNodes(pi1->_side, side1_nodes);
846 std::vector<const Node *> side2_nodes;
847 getSideCornerNodes(pi2->_side, side2_nodes);
848
849 std::sort(side1_nodes.begin(), side1_nodes.end());
850 std::sort(side2_nodes.begin(), side2_nodes.end());
851
852 // Find nodes shared by the two faces
853 std::vector<const Node *> common_nodes;
854 std::set_intersection(side1_nodes.begin(),
855 side1_nodes.end(),
856 side2_nodes.begin(),
857 side2_nodes.end(),
858 std::inserter(common_nodes, common_nodes.end()));
859
860 if (common_nodes.size() != sidedim)
861 return false;
862
863 bool found_point1, found_point2;
864 Point closest_coor_ref1(pi1->_closest_point_ref);
865 const Node * closest_node1;
867 closest_coor_ref1, closest_node1, pi1->_side, common_nodes);
868
869 Point closest_coor_ref2(pi2->_closest_point_ref);
870 const Node * closest_node2;
872 closest_coor_ref2, closest_node2, pi2->_side, common_nodes);
873
874 if (!found_point1 || !found_point2)
875 return false;
876
877 // if (sidedim == 2)
878 // {
879 // TODO:
880 // We have the parametric coordinates of the closest intersection point for both faces.
881 // We need to find a point somewhere in the middle of them so there's not an abrupt jump.
882 // Find that point by taking dot products of vector from contact point to secondary node point
883 // with face normal vectors to see which face we're closer to.
884 // }
885
886 FEBase * fe = NULL;
887 std::vector<Point> points(1);
888
889 // We have to pick one of the two faces to own the contact point. Either one would
890 // generally work, but to avoid some corner-case numerical roundoff issues when
891 // determining signs of the normal and displacement, pick the one that the point is
892 // closer to projecting onto, which is the one with the larger projected distance.
893 // If that distance is the same for both faces (within numerical precision), pick the
894 // face with the lowest index for repeatability.
895 if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(pi1->_distance), std::abs(pi2->_distance)) ||
896 (MooseUtils::absoluteFuzzyEqual(std::abs(pi1->_distance), std::abs(pi2->_distance)) &&
897 index1 < index2))
898 {
899 fe = _fes[_tid][pi1->_side->dim()];
900 contact_point_ref = closest_coor_ref1;
901 points[0] = closest_coor_ref1;
902 fe->reinit(pi1->_side, &points);
903 index = index1;
904 }
905 else
906 {
907 fe = _fes[_tid][pi2->_side->dim()];
908 contact_point_ref = closest_coor_ref2;
909 points[0] = closest_coor_ref2;
910 fe->reinit(pi2->_side, &points);
911 index = index2;
912 }
913
914 contact_point = fe->get_xyz()[0];
915
916 if (sidedim == 2)
917 {
918 if (closest_node1) // point is off the ridge between the two elements
919 {
920 mooseAssert((closest_node1 == closest_node2 || closest_node2 == NULL),
921 "If off edge of ridge, closest node must be the same on both elements");
922 closest_node = closest_node1;
923
924 RealGradient off_face = *closest_node1 - contact_point;
925 tangential_distance = off_face.norm();
926 }
927 }
928
929 return true;
930}
931
932void
933PenetrationThread::getSideCornerNodes(const Elem * side, std::vector<const Node *> & corner_nodes)
934{
935 const ElemType t(side->type());
936 corner_nodes.clear();
937
938 corner_nodes.push_back(side->node_ptr(0));
939 corner_nodes.push_back(side->node_ptr(1));
940 switch (t)
941 {
942 case EDGE2:
943 case EDGE3:
944 case EDGE4:
945 {
946 break;
947 }
948
949 case TRI3:
950 case TRI6:
951 case TRI7:
952 {
953 corner_nodes.push_back(side->node_ptr(2));
954 break;
955 }
956
957 case QUAD4:
958 case QUAD8:
959 case QUAD9:
960 {
961 corner_nodes.push_back(side->node_ptr(2));
962 corner_nodes.push_back(side->node_ptr(3));
963 break;
964 }
965
966 default:
967 {
968 mooseError("Unsupported face type: ", t);
969 break;
970 }
971 }
972}
973
974bool
976 const Node *& closest_node,
977 const Elem * side,
978 const std::vector<const Node *> & edge_nodes)
979{
980 const ElemType t = side->type();
981 Real & xi = p(0);
982 Real & eta = p(1);
983 closest_node = NULL;
984
985 std::vector<unsigned int> local_node_indices;
986 for (const auto & edge_node : edge_nodes)
987 {
988 unsigned int local_index = side->get_node_index(edge_node);
989 if (local_index == libMesh::invalid_uint)
990 mooseError("Side does not contain node");
991 local_node_indices.push_back(local_index);
992 }
993 mooseAssert(local_node_indices.size() == side->dim(),
994 "Number of edge nodes must match side dimensionality");
995 std::sort(local_node_indices.begin(), local_node_indices.end());
996
997 bool off_of_this_edge = false;
998
999 switch (t)
1000 {
1001 case EDGE2:
1002 case EDGE3:
1003 case EDGE4:
1004 {
1005 if (local_node_indices[0] == 0)
1006 {
1007 if (xi <= -1.0)
1008 {
1009 xi = -1.0;
1010 off_of_this_edge = true;
1011 closest_node = side->node_ptr(0);
1012 }
1013 }
1014 else if (local_node_indices[0] == 1)
1015 {
1016 if (xi >= 1.0)
1017 {
1018 xi = 1.0;
1019 off_of_this_edge = true;
1020 closest_node = side->node_ptr(1);
1021 }
1022 }
1023 else
1024 {
1025 mooseError("Invalid local node indices");
1026 }
1027 break;
1028 }
1029
1030 case TRI3:
1031 case TRI6:
1032 case TRI7:
1033 {
1034 if ((local_node_indices[0] == 0) && (local_node_indices[1] == 1))
1035 {
1036 if (eta <= 0.0)
1037 {
1038 eta = 0.0;
1039 off_of_this_edge = true;
1040 if (xi < 0.0)
1041 closest_node = side->node_ptr(0);
1042 else if (xi > 1.0)
1043 closest_node = side->node_ptr(1);
1044 }
1045 }
1046 else if ((local_node_indices[0] == 1) && (local_node_indices[1] == 2))
1047 {
1048 if ((xi + eta) > 1.0)
1049 {
1050 Real delta = (xi + eta - 1.0) / 2.0;
1051 xi -= delta;
1052 eta -= delta;
1053 off_of_this_edge = true;
1054 if (xi > 1.0)
1055 closest_node = side->node_ptr(1);
1056 else if (xi < 0.0)
1057 closest_node = side->node_ptr(2);
1058 }
1059 }
1060 else if ((local_node_indices[0] == 0) && (local_node_indices[1] == 2))
1061 {
1062 if (xi <= 0.0)
1063 {
1064 xi = 0.0;
1065 off_of_this_edge = true;
1066 if (eta > 1.0)
1067 closest_node = side->node_ptr(2);
1068 else if (eta < 0.0)
1069 closest_node = side->node_ptr(0);
1070 }
1071 }
1072 else
1073 {
1074 mooseError("Invalid local node indices");
1075 }
1076
1077 break;
1078 }
1079
1080 case QUAD4:
1081 case QUAD8:
1082 case QUAD9:
1083 {
1084 if ((local_node_indices[0] == 0) && (local_node_indices[1] == 1))
1085 {
1086 if (eta <= -1.0)
1087 {
1088 eta = -1.0;
1089 off_of_this_edge = true;
1090 if (xi < -1.0)
1091 closest_node = side->node_ptr(0);
1092 else if (xi > 1.0)
1093 closest_node = side->node_ptr(1);
1094 }
1095 }
1096 else if ((local_node_indices[0] == 1) && (local_node_indices[1] == 2))
1097 {
1098 if (xi >= 1.0)
1099 {
1100 xi = 1.0;
1101 off_of_this_edge = true;
1102 if (eta < -1.0)
1103 closest_node = side->node_ptr(1);
1104 else if (eta > 1.0)
1105 closest_node = side->node_ptr(2);
1106 }
1107 }
1108 else if ((local_node_indices[0] == 2) && (local_node_indices[1] == 3))
1109 {
1110 if (eta >= 1.0)
1111 {
1112 eta = 1.0;
1113 off_of_this_edge = true;
1114 if (xi < -1.0)
1115 closest_node = side->node_ptr(3);
1116 else if (xi > 1.0)
1117 closest_node = side->node_ptr(2);
1118 }
1119 }
1120 else if ((local_node_indices[0] == 0) && (local_node_indices[1] == 3))
1121 {
1122 if (xi <= -1.0)
1123 {
1124 xi = -1.0;
1125 off_of_this_edge = true;
1126 if (eta < -1.0)
1127 closest_node = side->node_ptr(0);
1128 else if (eta > 1.0)
1129 closest_node = side->node_ptr(3);
1130 }
1131 }
1132 else
1133 {
1134 mooseError("Invalid local node indices");
1135 }
1136 break;
1137 }
1138
1139 default:
1140 {
1141 mooseError("Unsupported face type: ", t);
1142 break;
1143 }
1144 }
1145 return off_of_this_edge;
1146}
1147
1148bool
1149PenetrationThread::restrictPointToFace(Point & p, const Node *& closest_node, const Elem * side)
1150{
1151 const ElemType t(side->type());
1152 Real & xi = p(0);
1153 Real & eta = p(1);
1154 closest_node = NULL;
1155
1156 bool off_of_this_face(false);
1157
1158 switch (t)
1159 {
1160 case EDGE2:
1161 case EDGE3:
1162 case EDGE4:
1163 {
1164 if (xi < -1.0)
1165 {
1166 xi = -1.0;
1167 off_of_this_face = true;
1168 closest_node = side->node_ptr(0);
1169 }
1170 else if (xi > 1.0)
1171 {
1172 xi = 1.0;
1173 off_of_this_face = true;
1174 closest_node = side->node_ptr(1);
1175 }
1176 break;
1177 }
1178
1179 case TRI3:
1180 case TRI6:
1181 case TRI7:
1182 {
1183 if (eta < 0.0)
1184 {
1185 eta = 0.0;
1186 off_of_this_face = true;
1187 if (xi < 0.5)
1188 {
1189 closest_node = side->node_ptr(0);
1190 if (xi < 0.0)
1191 xi = 0.0;
1192 }
1193 else
1194 {
1195 closest_node = side->node_ptr(1);
1196 if (xi > 1.0)
1197 xi = 1.0;
1198 }
1199 }
1200 else if ((xi + eta) > 1.0)
1201 {
1202 Real delta = (xi + eta - 1.0) / 2.0;
1203 xi -= delta;
1204 eta -= delta;
1205 off_of_this_face = true;
1206 if (xi > 0.5)
1207 {
1208 closest_node = side->node_ptr(1);
1209 if (xi > 1.0)
1210 {
1211 xi = 1.0;
1212 eta = 0.0;
1213 }
1214 }
1215 else
1216 {
1217 closest_node = side->node_ptr(2);
1218 if (xi < 0.0)
1219 {
1220 xi = 0.0;
1221 eta = 1.0;
1222 }
1223 }
1224 }
1225 else if (xi < 0.0)
1226 {
1227 xi = 0.0;
1228 off_of_this_face = true;
1229 if (eta > 0.5)
1230 {
1231 closest_node = side->node_ptr(2);
1232 if (eta > 1.0)
1233 eta = 1.0;
1234 }
1235 else
1236 {
1237 closest_node = side->node_ptr(0);
1238 if (eta < 0.0)
1239 eta = 0.0;
1240 }
1241 }
1242 break;
1243 }
1244
1245 case QUAD4:
1246 case QUAD8:
1247 case QUAD9:
1248 {
1249 if (eta < -1.0)
1250 {
1251 eta = -1.0;
1252 off_of_this_face = true;
1253 if (xi < 0.0)
1254 {
1255 closest_node = side->node_ptr(0);
1256 if (xi < -1.0)
1257 xi = -1.0;
1258 }
1259 else
1260 {
1261 closest_node = side->node_ptr(1);
1262 if (xi > 1.0)
1263 xi = 1.0;
1264 }
1265 }
1266 else if (xi > 1.0)
1267 {
1268 xi = 1.0;
1269 off_of_this_face = true;
1270 if (eta < 0.0)
1271 {
1272 closest_node = side->node_ptr(1);
1273 if (eta < -1.0)
1274 eta = -1.0;
1275 }
1276 else
1277 {
1278 closest_node = side->node_ptr(2);
1279 if (eta > 1.0)
1280 eta = 1.0;
1281 }
1282 }
1283 else if (eta > 1.0)
1284 {
1285 eta = 1.0;
1286 off_of_this_face = true;
1287 if (xi < 0.0)
1288 {
1289 closest_node = side->node_ptr(3);
1290 if (xi < -1.0)
1291 xi = -1.0;
1292 }
1293 else
1294 {
1295 closest_node = side->node_ptr(2);
1296 if (xi > 1.0)
1297 xi = 1.0;
1298 }
1299 }
1300 else if (xi < -1.0)
1301 {
1302 xi = -1.0;
1303 off_of_this_face = true;
1304 if (eta < 0.0)
1305 {
1306 closest_node = side->node_ptr(0);
1307 if (eta < -1.0)
1308 eta = -1.0;
1309 }
1310 else
1311 {
1312 closest_node = side->node_ptr(3);
1313 if (eta > 1.0)
1314 eta = 1.0;
1315 }
1316 }
1317 break;
1318 }
1319
1320 default:
1321 {
1322 mooseError("Unsupported face type: ", t);
1323 break;
1324 }
1325 }
1326 return off_of_this_face;
1327}
1328
1329bool
1331 const Elem * side,
1332 FEBase * fe,
1333 const Point * secondary_point,
1334 const Real tangential_tolerance)
1335{
1336 unsigned int dim = primary_elem->dim();
1337
1338 const std::vector<Point> & phys_point = fe->get_xyz();
1339
1340 const std::vector<RealGradient> & dxyz_dxi = fe->get_dxyzdxi();
1341 const std::vector<RealGradient> & dxyz_deta = fe->get_dxyzdeta();
1342
1343 Point ref_point;
1344
1345 std::vector<Point> points(1); // Default constructor gives us a point at 0,0,0
1346
1347 fe->reinit(side, &points);
1348
1349 RealGradient d = *secondary_point - phys_point[0];
1350
1351 const Real twosqrt2 = 2.8284; // way more precision than we actually need here
1352 Real max_face_length = side->hmax() + twosqrt2 * tangential_tolerance;
1353
1354 RealVectorValue normal;
1355 if (dim - 1 == 2)
1356 {
1357 normal = dxyz_dxi[0].cross(dxyz_deta[0]);
1358 }
1359 else if (dim - 1 == 1)
1360 {
1361 const Node * const * elem_nodes = primary_elem->get_nodes();
1362 const Point in_plane_vector1 = *elem_nodes[1] - *elem_nodes[0];
1363 const Point in_plane_vector2 = *elem_nodes[2] - *elem_nodes[0];
1364
1365 Point out_of_plane_normal = in_plane_vector1.cross(in_plane_vector2);
1366 out_of_plane_normal /= out_of_plane_normal.norm();
1367
1368 normal = dxyz_dxi[0].cross(out_of_plane_normal);
1369 }
1370 else
1371 {
1372 return true;
1373 }
1374 normal /= normal.norm();
1375
1376 const Real dot(d * normal);
1377
1378 const RealGradient normcomp = dot * normal;
1379 const RealGradient tangcomp = d - normcomp;
1380
1381 const Real tangdist = tangcomp.norm();
1382
1383 // Increase the size of the zone that we consider if the vector from the face
1384 // to the node has a larger normal component
1385 const Real faceExpansionFactor = 2.0 * (1.0 + normcomp.norm() / d.norm());
1386
1387 bool isReasonableCandidate = true;
1388 if (tangdist > faceExpansionFactor * max_face_length)
1389 {
1390 isReasonableCandidate = false;
1391 }
1392 return isReasonableCandidate;
1393}
1394
1395void
1397{
1398 // Slip is current projected position of secondary node minus
1399 // original projected position of secondary node
1400 std::vector<Point> points(1);
1401 points[0] = info._starting_closest_point_ref;
1402 const auto & side = _elem_side_builder(*info._starting_elem, info._starting_side_num);
1403 fe.reinit(&side, &points);
1404 const std::vector<Point> & starting_point = fe.get_xyz();
1405 info._incremental_slip = info._closest_point - starting_point[0];
1406 if (info.isCaptured())
1407 {
1408 info._frictional_energy =
1409 info._frictional_energy_old + info._contact_force * info._incremental_slip;
1410 info._accumulated_slip = info._accumulated_slip_old + info._incremental_slip.norm();
1411 }
1412}
1413
1414void
1416 std::vector<PenetrationInfo *> & p_info,
1417 const Node & node)
1418{
1420 {
1422 {
1423 // If we are within the smoothing distance of any edges or corners, find the
1424 // corner nodes for those edges/corners, and weights from distance to edge/corner
1425 std::vector<Real> edge_face_weights;
1426 std::vector<PenetrationInfo *> edge_face_info;
1427
1428 getSmoothingFacesAndWeights(info, edge_face_info, edge_face_weights, p_info, node);
1429
1430 mooseAssert(edge_face_info.size() == edge_face_weights.size(),
1431 "edge_face_info.size() != edge_face_weights.size()");
1432
1433 if (edge_face_info.size() > 0)
1434 {
1435 // Smooth the normal using the weighting functions for all participating faces.
1436 RealVectorValue new_normal;
1437 Real this_face_weight = 1.0;
1438
1439 for (unsigned int efwi = 0; efwi < edge_face_weights.size(); ++efwi)
1440 {
1441 PenetrationInfo * npi = edge_face_info[efwi];
1442 if (npi)
1443 new_normal += npi->_normal * edge_face_weights[efwi];
1444
1445 this_face_weight -= edge_face_weights[efwi];
1446 }
1447 mooseAssert(this_face_weight >= (0.25 - 1e-8),
1448 "Sum of weights of other faces shouldn't exceed 0.75");
1449 new_normal += info->_normal * this_face_weight;
1450
1451 const Real len = new_normal.norm();
1452 if (len > 0)
1453 new_normal /= len;
1454
1455 info->_normal = new_normal;
1456 }
1457 }
1459 {
1460 // params.addParam<VariableName>("var_name","description");
1461 // getParam<VariableName>("var_name")
1462 info->_normal(0) = _nodal_normal_x->getValue(info->_side, info->_side_phi);
1463 info->_normal(1) = _nodal_normal_y->getValue(info->_side, info->_side_phi);
1464 info->_normal(2) = _nodal_normal_z->getValue(info->_side, info->_side_phi);
1465 const Real len(info->_normal.norm());
1466 if (len > 0)
1467 info->_normal /= len;
1468 }
1469 }
1470}
1471
1472void
1474 std::vector<PenetrationInfo *> & edge_face_info,
1475 std::vector<Real> & edge_face_weights,
1476 std::vector<PenetrationInfo *> & p_info,
1477 const Node & secondary_node)
1478{
1479 const Elem * side = info->_side;
1480 const Point & p = info->_closest_point_ref;
1481 std::set<dof_id_type> elems_to_exclude;
1482 elems_to_exclude.insert(info->_elem->id());
1483
1484 std::vector<std::vector<const Node *>> edge_nodes;
1485
1486 // Get the pairs of nodes along every edge that we are close enough to smooth with
1487 getSmoothingEdgeNodesAndWeights(p, side, edge_nodes, edge_face_weights);
1488 std::vector<Elem *> edge_neighbor_elems;
1489 edge_face_info.resize(edge_nodes.size(), NULL);
1490
1491 std::vector<unsigned int> edges_without_neighbors;
1492
1493 for (unsigned int i = 0; i < edge_nodes.size(); ++i)
1494 {
1495 // Sort all sets of edge nodes (needed for comparing edges)
1496 std::sort(edge_nodes[i].begin(), edge_nodes[i].end());
1497
1498 std::vector<PenetrationInfo *> face_info_comm_edge;
1500 &secondary_node, elems_to_exclude, edge_nodes[i], face_info_comm_edge, p_info);
1501
1502 if (face_info_comm_edge.size() == 0)
1503 edges_without_neighbors.push_back(i);
1504 else if (face_info_comm_edge.size() > 1)
1505 mooseError("Only one neighbor allowed per edge");
1506 else
1507 edge_face_info[i] = face_info_comm_edge[0];
1508 }
1509
1510 // Remove edges without neighbors from the vector, starting from end
1511 std::vector<unsigned int>::reverse_iterator rit;
1512 for (rit = edges_without_neighbors.rbegin(); rit != edges_without_neighbors.rend(); ++rit)
1513 {
1514 unsigned int index = *rit;
1515 edge_nodes.erase(edge_nodes.begin() + index);
1516 edge_face_weights.erase(edge_face_weights.begin() + index);
1517 edge_face_info.erase(edge_face_info.begin() + index);
1518 }
1519
1520 // Handle corner case
1521 if (edge_nodes.size() > 1)
1522 {
1523 if (edge_nodes.size() != 2)
1524 mooseError("Invalid number of smoothing edges");
1525
1526 // find common node
1527 std::vector<const Node *> common_nodes;
1528 std::set_intersection(edge_nodes[0].begin(),
1529 edge_nodes[0].end(),
1530 edge_nodes[1].begin(),
1531 edge_nodes[1].end(),
1532 std::inserter(common_nodes, common_nodes.end()));
1533
1534 if (common_nodes.size() != 1)
1535 mooseError("Invalid number of common nodes");
1536
1537 for (const auto & pinfo : edge_face_info)
1538 elems_to_exclude.insert(pinfo->_elem->id());
1539
1540 std::vector<PenetrationInfo *> face_info_comm_edge;
1542 &secondary_node, elems_to_exclude, common_nodes, face_info_comm_edge, p_info);
1543
1544 unsigned int num_corner_neighbors = face_info_comm_edge.size();
1545
1546 if (num_corner_neighbors > 0)
1547 {
1548 Real fw0 = edge_face_weights[0];
1549 Real fw1 = edge_face_weights[1];
1550
1551 // Corner weight is product of edge weights. Spread out over multiple neighbors.
1552 Real fw_corner = (fw0 * fw1) / static_cast<Real>(num_corner_neighbors);
1553
1554 // Adjust original edge weights
1555 edge_face_weights[0] *= (1.0 - fw1);
1556 edge_face_weights[1] *= (1.0 - fw0);
1557
1558 for (unsigned int i = 0; i < num_corner_neighbors; ++i)
1559 {
1560 edge_face_weights.push_back(fw_corner);
1561 edge_face_info.push_back(face_info_comm_edge[i]);
1562 }
1563 }
1564 }
1565}
1566
1567void
1569 const Point & p,
1570 const Elem * side,
1571 std::vector<std::vector<const Node *>> & edge_nodes,
1572 std::vector<Real> & edge_face_weights)
1573{
1574 const ElemType t(side->type());
1575 const Real & xi = p(0);
1576 const Real & eta = p(1);
1577
1578 Real smooth_limit = 1.0 - _normal_smoothing_distance;
1579
1580 switch (t)
1581 {
1582 case EDGE2:
1583 case EDGE3:
1584 case EDGE4:
1585 {
1586 if (xi < -smooth_limit)
1587 {
1588 std::vector<const Node *> en;
1589 en.push_back(side->node_ptr(0));
1590 edge_nodes.push_back(en);
1591 Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1592 if (fw > 0.5)
1593 fw = 0.5;
1594 edge_face_weights.push_back(fw);
1595 }
1596 else if (xi > smooth_limit)
1597 {
1598 std::vector<const Node *> en;
1599 en.push_back(side->node_ptr(1));
1600 edge_nodes.push_back(en);
1601 Real fw = 0.5 - (1.0 - xi) / (2.0 * _normal_smoothing_distance);
1602 if (fw > 0.5)
1603 fw = 0.5;
1604 edge_face_weights.push_back(fw);
1605 }
1606 break;
1607 }
1608
1609 case TRI3:
1610 case TRI6:
1611 case TRI7:
1612 {
1613 if (eta < -smooth_limit)
1614 {
1615 std::vector<const Node *> en;
1616 en.push_back(side->node_ptr(0));
1617 en.push_back(side->node_ptr(1));
1618 edge_nodes.push_back(en);
1619 Real fw = 0.5 - (1.0 + eta) / (2.0 * _normal_smoothing_distance);
1620 if (fw > 0.5)
1621 fw = 0.5;
1622 edge_face_weights.push_back(fw);
1623 }
1624 if ((xi + eta) > smooth_limit)
1625 {
1626 std::vector<const Node *> en;
1627 en.push_back(side->node_ptr(1));
1628 en.push_back(side->node_ptr(2));
1629 edge_nodes.push_back(en);
1630 Real fw = 0.5 - (1.0 - xi - eta) / (2.0 * _normal_smoothing_distance);
1631 if (fw > 0.5)
1632 fw = 0.5;
1633 edge_face_weights.push_back(fw);
1634 }
1635 if (xi < -smooth_limit)
1636 {
1637 std::vector<const Node *> en;
1638 en.push_back(side->node_ptr(2));
1639 en.push_back(side->node_ptr(0));
1640 edge_nodes.push_back(en);
1641 Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1642 if (fw > 0.5)
1643 fw = 0.5;
1644 edge_face_weights.push_back(fw);
1645 }
1646 break;
1647 }
1648
1649 case QUAD4:
1650 case QUAD8:
1651 case QUAD9:
1652 {
1653 if (eta < -smooth_limit)
1654 {
1655 std::vector<const Node *> en;
1656 en.push_back(side->node_ptr(0));
1657 en.push_back(side->node_ptr(1));
1658 edge_nodes.push_back(en);
1659 Real fw = 0.5 - (1.0 + eta) / (2.0 * _normal_smoothing_distance);
1660 if (fw > 0.5)
1661 fw = 0.5;
1662 edge_face_weights.push_back(fw);
1663 }
1664 if (xi > smooth_limit)
1665 {
1666 std::vector<const Node *> en;
1667 en.push_back(side->node_ptr(1));
1668 en.push_back(side->node_ptr(2));
1669 edge_nodes.push_back(en);
1670 Real fw = 0.5 - (1.0 - xi) / (2.0 * _normal_smoothing_distance);
1671 if (fw > 0.5)
1672 fw = 0.5;
1673 edge_face_weights.push_back(fw);
1674 }
1675 if (eta > smooth_limit)
1676 {
1677 std::vector<const Node *> en;
1678 en.push_back(side->node_ptr(2));
1679 en.push_back(side->node_ptr(3));
1680 edge_nodes.push_back(en);
1681 Real fw = 0.5 - (1.0 - eta) / (2.0 * _normal_smoothing_distance);
1682 if (fw > 0.5)
1683 fw = 0.5;
1684 edge_face_weights.push_back(fw);
1685 }
1686 if (xi < -smooth_limit)
1687 {
1688 std::vector<const Node *> en;
1689 en.push_back(side->node_ptr(3));
1690 en.push_back(side->node_ptr(0));
1691 edge_nodes.push_back(en);
1692 Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1693 if (fw > 0.5)
1694 fw = 0.5;
1695 edge_face_weights.push_back(fw);
1696 }
1697 break;
1698 }
1699
1700 default:
1701 {
1702 mooseError("Unsupported face type: ", t);
1703 break;
1704 }
1705 }
1706}
1707
1708void
1710 const Node * secondary_node,
1711 const std::set<dof_id_type> & elems_to_exclude,
1712 const std::vector<const Node *> edge_nodes,
1713 std::vector<PenetrationInfo *> & face_info_comm_edge,
1714 std::vector<PenetrationInfo *> & p_info)
1715{
1716 // elems connected to a node on this edge, find one that has the same corners as this, and is not
1717 // the current elem
1718 auto node_to_elem_pair = _node_to_elem_map.find(edge_nodes[0]->id()); // just need one of the
1719 // nodes
1720 mooseAssert(node_to_elem_pair != _node_to_elem_map.end(), "Missing entry in node to elem map");
1721 const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second;
1722
1723 std::vector<const Elem *> elems_connected_to_edge;
1724
1725 for (unsigned int ecni = 0; ecni < elems_connected_to_node.size(); ecni++)
1726 {
1727 if (elems_to_exclude.find(elems_connected_to_node[ecni]) != elems_to_exclude.end())
1728 continue;
1729 const Elem * elem = _mesh.elemPtr(elems_connected_to_node[ecni]);
1730
1731 std::vector<const Node *> nodevec;
1732 for (unsigned int ni = 0; ni < elem->n_nodes(); ++ni)
1733 if (elem->is_vertex(ni))
1734 nodevec.push_back(elem->node_ptr(ni));
1735
1736 std::vector<const Node *> common_nodes;
1737 std::sort(nodevec.begin(), nodevec.end());
1738 std::set_intersection(edge_nodes.begin(),
1739 edge_nodes.end(),
1740 nodevec.begin(),
1741 nodevec.end(),
1742 std::inserter(common_nodes, common_nodes.end()));
1743
1744 if (common_nodes.size() == edge_nodes.size())
1745 elems_connected_to_edge.push_back(elem);
1746 }
1747
1748 if (elems_connected_to_edge.size() > 0)
1749 {
1750
1751 // There are potentially multiple elements that share a common edge
1752 // 2D:
1753 // There can only be one element on the same surface
1754 // 3D:
1755 // If there are two edge nodes, there can only be one element on the same surface
1756 // If there is only one edge node (a corner), there could be multiple elements on the same
1757 // surface
1758 bool allowMultipleNeighbors = false;
1759
1760 if (elems_connected_to_edge[0]->dim() == 3)
1761 {
1762 if (edge_nodes.size() == 1)
1763 {
1764 allowMultipleNeighbors = true;
1765 }
1766 }
1767
1768 for (unsigned int i = 0; i < elems_connected_to_edge.size(); ++i)
1769 {
1770 std::vector<PenetrationInfo *> thisElemInfo;
1771 getInfoForElem(thisElemInfo, p_info, elems_connected_to_edge[i]);
1772 if (thisElemInfo.size() > 0 && !allowMultipleNeighbors)
1773 {
1774 if (thisElemInfo.size() > 1)
1775 mooseError(
1776 "Found multiple neighbors to current edge/face on surface when only one is allowed");
1777 face_info_comm_edge.push_back(thisElemInfo[0]);
1778 break;
1779 }
1780
1782 thisElemInfo, p_info, secondary_node, elems_connected_to_edge[i], edge_nodes);
1783 if (thisElemInfo.size() > 0 && !allowMultipleNeighbors)
1784 {
1785 if (thisElemInfo.size() > 1)
1786 mooseError(
1787 "Found multiple neighbors to current edge/face on surface when only one is allowed");
1788 face_info_comm_edge.push_back(thisElemInfo[0]);
1789 break;
1790 }
1791
1792 for (unsigned int j = 0; j < thisElemInfo.size(); ++j)
1793 face_info_comm_edge.push_back(thisElemInfo[j]);
1794 }
1795 }
1796}
1797
1798void
1799PenetrationThread::getInfoForElem(std::vector<PenetrationInfo *> & thisElemInfo,
1800 std::vector<PenetrationInfo *> & p_info,
1801 const Elem * elem)
1802{
1803 for (const auto & pi : p_info)
1804 {
1805 if (!pi)
1806 continue;
1807
1808 if (pi->_elem == elem)
1809 thisElemInfo.push_back(pi);
1810 }
1811}
1812
1813void
1814PenetrationThread::createInfoForElem(std::vector<PenetrationInfo *> & thisElemInfo,
1815 std::vector<PenetrationInfo *> & p_info,
1816 const Node * secondary_node,
1817 const Elem * elem,
1818 const std::vector<const Node *> & nodes_that_must_be_on_side,
1819 const bool check_whether_reasonable)
1820{
1821 const BoundaryInfo & boundary_info = _mesh.getMesh().get_boundary_info();
1822
1823 for (auto s : elem->side_index_range())
1824 {
1825 if (!boundary_info.has_boundary_id(elem, s, _primary_boundary))
1826 continue;
1827
1828 // Don't create info for this side if one already exists
1829 bool already_have_info_this_side = false;
1830 for (const auto & pi : thisElemInfo)
1831 if (pi->_side_num == s)
1832 {
1833 already_have_info_this_side = true;
1834 break;
1835 }
1836
1837 if (already_have_info_this_side)
1838 break;
1839
1840 const Elem * side = elem->build_side_ptr(s).release();
1841
1842 // Only continue with creating info for this side if the side contains
1843 // all of the nodes in nodes_that_must_be_on_side
1844 std::vector<const Node *> nodevec;
1845 for (unsigned int ni = 0; ni < side->n_nodes(); ++ni)
1846 nodevec.push_back(side->node_ptr(ni));
1847
1848 std::sort(nodevec.begin(), nodevec.end());
1849 std::vector<const Node *> common_nodes;
1850 std::set_intersection(nodes_that_must_be_on_side.begin(),
1851 nodes_that_must_be_on_side.end(),
1852 nodevec.begin(),
1853 nodevec.end(),
1854 std::inserter(common_nodes, common_nodes.end()));
1855 if (common_nodes.size() != nodes_that_must_be_on_side.size())
1856 {
1857 delete side;
1858 break;
1859 }
1860
1861 FEBase * fe_elem = _fes[_tid][elem->dim()];
1862 FEBase * fe_side = _fes[_tid][side->dim()];
1863
1864 // Optionally check to see whether face is reasonable candidate based on an
1865 // estimate of how closely it is likely to project to the face
1866 if (check_whether_reasonable)
1867 if (!isFaceReasonableCandidate(elem, side, fe_side, secondary_node, _tangential_tolerance))
1868 {
1869 delete side;
1870 break;
1871 }
1872
1873 Point contact_phys;
1874 Point contact_ref;
1875 Point contact_on_face_ref;
1876 Real distance = 0.;
1877 Real tangential_distance = 0.;
1878 RealGradient normal;
1879 bool contact_point_on_side;
1880 std::vector<const Node *> off_edge_nodes;
1881 std::vector<std::vector<Real>> side_phi;
1882 std::vector<std::vector<RealGradient>> side_grad_phi;
1883 std::vector<RealGradient> dxyzdxi;
1884 std::vector<RealGradient> dxyzdeta;
1885 std::vector<RealGradient> d2xyzdxideta;
1886
1887 std::unique_ptr<PenetrationInfo> pen_info =
1888 std::make_unique<PenetrationInfo>(elem,
1889 side,
1890 s,
1891 normal,
1892 distance,
1893 tangential_distance,
1894 contact_phys,
1895 contact_ref,
1896 contact_on_face_ref,
1897 off_edge_nodes,
1898 side_phi,
1899 side_grad_phi,
1900 dxyzdxi,
1901 dxyzdeta,
1902 d2xyzdxideta);
1903
1904 bool search_succeeded = false;
1905 Moose::findContactPoint(*pen_info,
1906 fe_elem,
1907 fe_side,
1908 _fe_type,
1909 *secondary_node,
1910 true,
1912 contact_point_on_side,
1913 search_succeeded);
1914
1915 // Do not add contact info from failed searches
1916 if (search_succeeded)
1917 {
1918 thisElemInfo.push_back(pen_info.get());
1919 p_info.push_back(pen_info.release());
1920 }
1921 }
1922}
boundary_id_type BoundaryID
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
Point eta
Definition MortarUtils.C:60
Point xi
Definition MortarUtils.C:59
Threads::spin_mutex pinfo_mutex
unsigned int dim
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
virtual const Node & nodeRef(const dof_id_type i) const
Definition MooseMesh.C:844
virtual Elem * elemPtr(const dof_id_type i)
Definition MooseMesh.C:3222
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition MooseMesh.C:3557
virtual std::unique_ptr< libMesh::PointLocatorBase > getPointLocator() const
Proxy function to get a (sub)PointLocator from either the underlying libMesh mesh (default),...
Definition MooseMesh.C:3846
OutputType getValue(const Elem *elem, const std::vector< std::vector< OutputShape > > &phi) const
Compute the variable value at a point on an element.
Finds the nearest node to each node in boundary1 to each node in boundary2 and the other way around.
const Node * nearestNode(dof_id_type node_id)
Valid to call this after findNodes() has been called to get a pointer to the nearest node.
Data structure used to hold penetration information.
std::vector< const Node * > _off_edge_nodes
const Elem * _side
MECH_STATUS_ENUM _mech_status
RealVectorValue _contact_force_old
unsigned int _locked_this_step
MECH_STATUS_ENUM _mech_status_old
RealVectorValue _contact_force
unsigned int _starting_side_num
const Elem * _elem
const Elem * _starting_elem
Point _starting_closest_point_ref
unsigned int _side_num
RealVectorValue _lagrange_multiplier_slip
RealVectorValue _normal
unsigned int _stick_locked_this_step
void computeSlip(libMesh::FEBase &fe, PenetrationInfo &info)
void createInfoForElem(std::vector< PenetrationInfo * > &thisElemInfo, std::vector< PenetrationInfo * > &p_info, const Node *secondary_node, const Elem *elem, const std::vector< const Node * > &nodes_that_must_be_on_side, const bool check_whether_reasonable=false)
std::map< dof_id_type, PenetrationInfo * > & _penetration_info
MooseVariable * _nodal_normal_x
libMesh::ElemSideBuilder _elem_side_builder
Helper for building element sides without extraneous allocation.
void operator()(const NodeIdRange &range)
bool isFaceReasonableCandidate(const Elem *primary_elem, const Elem *side, libMesh::FEBase *fe, const libMesh::Point *secondary_point, const Real tangential_tolerance)
void getInfoForElem(std::vector< PenetrationInfo * > &thisElemInfo, std::vector< PenetrationInfo * > &p_info, const Elem *elem)
bool restrictPointToSpecifiedEdgeOfFace(libMesh::Point &p, const Node *&closest_node, const Elem *side, const std::vector< const Node * > &edge_nodes)
MooseVariable * _nodal_normal_z
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & _node_to_elem_map
std::vector< dof_id_type > _recheck_secondary_nodes
List of secondary nodes for which penetration was not detected in the current patch and for which pat...
MooseVariable * _nodal_normal_y
void getSideCornerNodes(const Elem *side, std::vector< const Node * > &corner_nodes)
bool findRidgeContactPoint(libMesh::Point &contact_point, Real &tangential_distance, const Node *&closest_node, unsigned int &index, libMesh::Point &contact_point_ref, std::vector< PenetrationInfo * > &p_info, const unsigned int index1, const unsigned int index2)
std::vector< std::vector< libMesh::FEBase * > > & _fes
void switchInfo(PenetrationInfo *&info, PenetrationInfo *&infoNew)
void getSmoothingEdgeNodesAndWeights(const libMesh::Point &p, const Elem *side, std::vector< std::vector< const Node * > > &edge_nodes, std::vector< Real > &edge_face_weights)
CommonEdgeResult interactionsOffCommonEdge(PenetrationInfo *pi1, PenetrationInfo *pi2)
bool restrictPointToFace(libMesh::Point &p, const Node *&closest_node, const Elem *side)
const MooseMesh & _mesh
libMesh::FEType & _fe_type
void getInfoForFacesWithCommonNodes(const Node *secondary_node, const std::set< dof_id_type > &elems_to_exclude, const std::vector< const Node * > edge_nodes, std::vector< PenetrationInfo * > &face_info_comm_edge, std::vector< PenetrationInfo * > &p_info)
void join(const PenetrationThread &other)
NearestNodeLocator & _nearest_node
PenetrationLocator::NORMAL_SMOOTHING_METHOD _normal_smoothing_method
PenetrationThread(SubProblem &subproblem, const MooseMesh &mesh, BoundaryID primary_boundary, BoundaryID secondary_boundary, std::map< dof_id_type, PenetrationInfo * > &penetration_info, bool check_whether_reasonable, bool update_location, Real tangential_tolerance, bool do_normal_smoothing, Real normal_smoothing_distance, PenetrationLocator::NORMAL_SMOOTHING_METHOD normal_smoothing_method, bool use_point_locator, std::vector< std::vector< libMesh::FEBase * > > &fes, libMesh::FEType &fe_type, NearestNodeLocator &nearest_node, const std::unordered_map< dof_id_type, std::vector< dof_id_type > > &node_to_elem_map)
void smoothNormal(PenetrationInfo *info, std::vector< PenetrationInfo * > &p_info, const Node &node)
void getSmoothingFacesAndWeights(PenetrationInfo *info, std::vector< PenetrationInfo * > &edge_face_info, std::vector< Real > &edge_face_weights, std::vector< PenetrationInfo * > &p_info, const Node &secondary_node)
SubProblem & _subproblem
BoundaryID _primary_boundary
CompeteInteractionResult competeInteractionsBothOnFace(PenetrationInfo *pi1, PenetrationInfo *pi2)
Determine whether first (pi1) or second (pi2) interaction is stronger when it is known that the node ...
CompeteInteractionResult competeInteractions(PenetrationInfo *pi1, PenetrationInfo *pi2)
When interactions are identified between a node and two faces, compete between the faces to determine...
Generic class for solving transient nonlinear problems.
Definition SubProblem.h:79
virtual MooseVariable & getStandardVariable(const THREAD_ID tid, const std::string &var_name)=0
Returns the variable reference for requested MooseVariable which may be in any system.
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
MeshBase & mesh
void findContactPoint(PenetrationInfo &p_info, libMesh::FEBase *fe_elem, libMesh::FEBase *fe_side, libMesh::FEType &fe_side_type, const libMesh::Point &secondary_point, bool start_with_centroid, const Real tangential_tolerance, bool &contact_point_on_side, bool &search_succeeded)
const unsigned int invalid_uint
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< RidgeData > _ridge_data_vec
Real distance(const Point &p)