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