https://mooseframework.inl.gov
Loading...
Searching...
No Matches
FindContactPoint.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 "DenseMatrix.h"
12#include "FindContactPoint.h"
13#include "LineSegment.h"
14#include "PenetrationInfo.h"
15
16// libMesh
17#include "libmesh/fe_base.h"
18#include "libmesh/boundary_info.h"
19#include "libmesh/elem.h"
20#include "libmesh/plane.h"
21#include "libmesh/fe_interface.h"
22#include "libmesh/dense_vector.h"
23#include "libmesh/fe_base.h"
24#include "libmesh/vector_value.h"
25
26// C++
27#include <cstring> // for "Jacobian" exception test
28
29namespace Moose
30{
31
50void
52 FEBase * fe_elem,
53 FEBase * fe_side,
54 FEType & /* fe_side_type */,
55 const libMesh::Point & secondary_point,
56 bool start_with_centroid,
57 const Real tangential_tolerance,
58 bool & contact_point_on_side,
59 bool & search_succeeded)
60{
61 // Default to true and we'll switch on failures
62 search_succeeded = true;
63
64 const Elem * primary_elem = p_info._elem;
65
66 unsigned int dim = primary_elem->dim();
67
68 const Elem * side = p_info._side;
69
70 const std::vector<libMesh::Point> & phys_point = fe_side->get_xyz();
71
72 const std::vector<RealGradient> & dxyz_dxi = fe_side->get_dxyzdxi();
73 const std::vector<RealGradient> & d2xyz_dxi2 = fe_side->get_d2xyzdxi2();
74 const std::vector<RealGradient> & d2xyz_dxieta = fe_side->get_d2xyzdxideta();
75
76 const std::vector<RealGradient> & dxyz_deta = fe_side->get_dxyzdeta();
77 const std::vector<RealGradient> & d2xyz_deta2 = fe_side->get_d2xyzdeta2();
78 const std::vector<RealGradient> & d2xyz_detaxi = fe_side->get_d2xyzdxideta();
79
80 if (dim == 1)
81 {
82 const Node * nearest_node = side->node_ptr(0);
83 p_info._closest_point = *nearest_node;
84 p_info._closest_point_ref =
85 primary_elem->master_point(primary_elem->get_node_index(nearest_node));
86 std::vector<libMesh::Point> elem_points = {p_info._closest_point_ref};
87
88 const std::vector<RealGradient> & elem_dxyz_dxi = fe_elem->get_dxyzdxi();
89
90 fe_elem->reinit(primary_elem, &elem_points);
91 fe_side->reinit(side, &elem_points);
92
93 p_info._normal = elem_dxyz_dxi[0];
94 if (nearest_node->id() == primary_elem->node_id(0))
95 p_info._normal *= -1.0;
96 p_info._normal /= p_info._normal.norm();
97
98 libMesh::Point from_secondary_to_closest = p_info._closest_point - secondary_point;
99 p_info._distance = from_secondary_to_closest * p_info._normal;
100 libMesh::Point tangential = from_secondary_to_closest - p_info._distance * p_info._normal;
101 p_info._tangential_distance = tangential.norm();
102 p_info._dxyzdxi = dxyz_dxi;
103 p_info._dxyzdeta = dxyz_deta;
104 p_info._d2xyzdxideta = d2xyz_dxieta;
105 p_info._side_phi = fe_side->get_phi();
106 p_info._side_grad_phi = fe_side->get_dphi();
107 contact_point_on_side = true;
108 return;
109 }
110
111 libMesh::Point ref_point;
112
113 if (start_with_centroid)
114 ref_point = FEMap::inverse_map(dim - 1, side, side->vertex_average(), TOLERANCE, false);
115 else
116 ref_point = p_info._closest_point_ref;
117
118 std::vector<libMesh::Point> points = {ref_point};
119 fe_side->reinit(side, &points);
120 RealGradient d = secondary_point - phys_point[0];
121
122 Real update_size = std::numeric_limits<Real>::max();
123
124 // Least squares
125 for (unsigned int it = 0; it < 3 && update_size > TOLERANCE * 1e3; ++it)
126 {
127 DenseMatrix<Real> jac(dim - 1, dim - 1);
128 jac(0, 0) = -(dxyz_dxi[0] * dxyz_dxi[0]);
129
130 if (dim - 1 == 2)
131 {
132 jac(1, 0) = -(dxyz_dxi[0] * dxyz_deta[0]);
133 jac(0, 1) = -(dxyz_deta[0] * dxyz_dxi[0]);
134 jac(1, 1) = -(dxyz_deta[0] * dxyz_deta[0]);
135 }
136
137 DenseVector<Real> rhs(dim - 1);
138 rhs(0) = dxyz_dxi[0] * d;
139
140 if (dim - 1 == 2)
141 rhs(1) = dxyz_deta[0] * d;
142
143 DenseVector<Real> update(dim - 1);
144 jac.lu_solve(rhs, update);
145
146 ref_point(0) -= update(0);
147
148 if (dim - 1 == 2)
149 ref_point(1) -= update(1);
150
151 points[0] = ref_point;
152 fe_side->reinit(side, &points);
153 d = secondary_point - phys_point[0];
154
155 update_size = update.l2_norm();
156 }
157
158 update_size = std::numeric_limits<Real>::max();
159
160 unsigned nit = 0;
161
162 // Newton Loop
163 const auto max_newton_its = 25;
164 const auto tolerance_newton = 1e3 * TOLERANCE * TOLERANCE;
165 for (; nit < max_newton_its && update_size > tolerance_newton; nit++)
166 {
167 d = secondary_point - phys_point[0];
168
169 DenseMatrix<Real> jac(dim - 1, dim - 1);
170 jac(0, 0) = (d2xyz_dxi2[0] * d) - (dxyz_dxi[0] * dxyz_dxi[0]);
171
172 if (dim - 1 == 2)
173 {
174 jac(1, 0) = (d2xyz_dxieta[0] * d) - (dxyz_dxi[0] * dxyz_deta[0]);
175
176 jac(0, 1) = (d2xyz_detaxi[0] * d) - (dxyz_deta[0] * dxyz_dxi[0]);
177 jac(1, 1) = (d2xyz_deta2[0] * d) - (dxyz_deta[0] * dxyz_deta[0]);
178 }
179
180 DenseVector<Real> rhs(dim - 1);
181 rhs(0) = -dxyz_dxi[0] * d;
182
183 if (dim - 1 == 2)
184 rhs(1) = -dxyz_deta[0] * d;
185
186 DenseVector<Real> update(dim - 1);
187 jac.lu_solve(rhs, update);
188
189 // Improvised line search in case the update is too large and gets out of the element so bad
190 // that we cannot reinit at the new point
191 Real mult = 1;
192 while (true)
193 {
194 try
195 {
196 ref_point(0) += mult * update(0);
197
198 if (dim - 1 == 2)
199 ref_point(1) += mult * update(1);
200
201 points[0] = ref_point;
202 fe_side->reinit(side, &points);
203 d = secondary_point - phys_point[0];
204
205 // we don't multiply by 'mult' because it is used for convergence
206 update_size = update.l2_norm();
207 break;
208 }
209 // libMesh might throw here if we hit a zero/negative Jacobian
210 catch (std::exception & e)
211 {
212 // Make sure this *is* just a bad mapping Jacobian
213 if (!strstr(e.what(), "Jacobian") && !strstr(e.what(), "det != 0"))
214 throw;
215
216 ref_point(0) -= mult * update(0);
217 if (dim - 1 == 2)
218 ref_point(1) -= mult * update(1);
219
220 mult *= 0.5;
221 if (mult < 1e-6)
222 {
223#ifndef NDEBUG
224 mooseWarning("We could not solve for the contact point.", e.what());
225#endif
226 update_size = update.l2_norm();
227 d = (secondary_point - phys_point[0]) * mult;
228 break;
229 }
230 }
231 }
232 // We failed the line search, make sure to trigger the error
233 if (mult < 1e-6)
234 {
235 nit = max_newton_its;
236 update_size = 1;
237 break;
238 }
239 }
240
241 if (nit == max_newton_its && update_size > tolerance_newton)
242 {
243 search_succeeded = false;
244#ifndef NDEBUG
245 const auto initial_point =
246 start_with_centroid ? side->vertex_average() : ref_point = p_info._closest_point_ref;
247 Moose::err << "Warning! Newton solve for contact point failed to converge!\nLast update "
248 "distance was: "
249 << update_size << "\nInitial point guess: " << initial_point
250 << "\nLast considered point: " << phys_point[0]
251 << "\nThis potential contact pair (face, point) will be discarded." << std::endl;
252#endif
253 return;
254 }
255
256 p_info._closest_point_ref = ref_point;
257 p_info._closest_point = phys_point[0];
258 p_info._distance = d.norm();
259
260 if (dim - 1 == 2)
261 {
262 p_info._normal = dxyz_dxi[0].cross(dxyz_deta[0]);
263 if (!MooseUtils::absoluteFuzzyEqual(p_info._normal.norm(), 0))
264 p_info._normal /= p_info._normal.norm();
265 }
266 else
267 {
268 const Node * const * elem_nodes = primary_elem->get_nodes();
269 const libMesh::Point in_plane_vector1 = *elem_nodes[1] - *elem_nodes[0];
270 const libMesh::Point in_plane_vector2 = *elem_nodes[2] - *elem_nodes[0];
271
272 libMesh::Point out_of_plane_normal = in_plane_vector1.cross(in_plane_vector2);
273 out_of_plane_normal /= out_of_plane_normal.norm();
274
275 p_info._normal = dxyz_dxi[0].cross(out_of_plane_normal);
276 if (std::fabs(p_info._normal.norm()) > 1e-15)
277 p_info._normal /= p_info._normal.norm();
278 }
279
280 // If the point has not penetrated the face, make the distance negative
281 const Real dot(d * p_info._normal);
282 if (dot > 0.0)
283 p_info._distance = -p_info._distance;
284
285 contact_point_on_side = side->on_reference_element(ref_point);
286
287 p_info._tangential_distance = 0.0;
288
289 if (!contact_point_on_side)
290 {
291 p_info._closest_point_on_face_ref = ref_point;
293
294 points[0] = p_info._closest_point_on_face_ref;
295 fe_side->reinit(side, &points);
296 libMesh::Point closest_point_on_face(phys_point[0]);
297
298 RealGradient off_face = closest_point_on_face - p_info._closest_point;
299 Real tangential_distance = off_face.norm();
300 p_info._tangential_distance = tangential_distance;
301 if (tangential_distance <= tangential_tolerance)
302 {
303 contact_point_on_side = true;
304 }
305 }
306
307 const std::vector<std::vector<Real>> & phi = fe_side->get_phi();
308 const std::vector<std::vector<RealGradient>> & grad_phi = fe_side->get_dphi();
309
310 points[0] = p_info._closest_point_ref;
311 fe_side->reinit(side, &points);
312
313 p_info._side_phi = phi;
314 p_info._side_grad_phi = grad_phi;
315 p_info._dxyzdxi = dxyz_dxi;
316 p_info._dxyzdeta = dxyz_deta;
317 p_info._d2xyzdxideta = d2xyz_dxieta;
318}
319
320void
322 const Elem * side,
323 std::vector<const Node *> & off_edge_nodes)
324{
325 const ElemType t(side->type());
326 off_edge_nodes.clear();
327 Real & xi = p(0);
328 Real & eta = p(1);
329
330 switch (t)
331 {
332 case EDGE2:
333 case EDGE3:
334 case EDGE4:
335 {
336 // The reference 1D element is [-1,1].
337 if (xi < -1.0)
338 {
339 xi = -1.0;
340 off_edge_nodes.push_back(side->node_ptr(0));
341 }
342 else if (xi > 1.0)
343 {
344 xi = 1.0;
345 off_edge_nodes.push_back(side->node_ptr(1));
346 }
347 break;
348 }
349
350 case TRI3:
351 case TRI6:
352 case TRI7:
353 {
354 // The reference triangle is isosceles
355 // and is bound by xi=0, eta=0, and xi+eta=1.
356
357 if (xi <= 0.0 && eta <= 0.0)
358 {
359 xi = 0.0;
360 eta = 0.0;
361 off_edge_nodes.push_back(side->node_ptr(0));
362 }
363 else if (xi > 0.0 && xi < 1.0 && eta < 0.0)
364 {
365 eta = 0.0;
366 off_edge_nodes.push_back(side->node_ptr(0));
367 off_edge_nodes.push_back(side->node_ptr(1));
368 }
369 else if (eta > 0.0 && eta < 1.0 && xi < 0.0)
370 {
371 xi = 0.0;
372 off_edge_nodes.push_back(side->node_ptr(2));
373 off_edge_nodes.push_back(side->node_ptr(0));
374 }
375 else if (xi >= 1.0 && (eta - xi) <= -1.0)
376 {
377 xi = 1.0;
378 eta = 0.0;
379 off_edge_nodes.push_back(side->node_ptr(1));
380 }
381 else if (eta >= 1.0 && (eta - xi) >= 1.0)
382 {
383 xi = 0.0;
384 eta = 1.0;
385 off_edge_nodes.push_back(side->node_ptr(2));
386 }
387 else if ((xi + eta) > 1.0)
388 {
389 Real delta = (xi + eta - 1.0) / 2.0;
390 xi -= delta;
391 eta -= delta;
392 off_edge_nodes.push_back(side->node_ptr(1));
393 off_edge_nodes.push_back(side->node_ptr(2));
394 }
395 break;
396 }
397
398 case QUAD4:
399 case QUAD8:
400 case QUAD9:
401 {
402 // The reference quadrilateral element is [-1,1]^2.
403 if (xi < -1.0)
404 {
405 xi = -1.0;
406 if (eta < -1.0)
407 {
408 eta = -1.0;
409 off_edge_nodes.push_back(side->node_ptr(0));
410 }
411 else if (eta > 1.0)
412 {
413 eta = 1.0;
414 off_edge_nodes.push_back(side->node_ptr(3));
415 }
416 else
417 {
418 off_edge_nodes.push_back(side->node_ptr(3));
419 off_edge_nodes.push_back(side->node_ptr(0));
420 }
421 }
422 else if (xi > 1.0)
423 {
424 xi = 1.0;
425 if (eta < -1.0)
426 {
427 eta = -1.0;
428 off_edge_nodes.push_back(side->node_ptr(1));
429 }
430 else if (eta > 1.0)
431 {
432 eta = 1.0;
433 off_edge_nodes.push_back(side->node_ptr(2));
434 }
435 else
436 {
437 off_edge_nodes.push_back(side->node_ptr(1));
438 off_edge_nodes.push_back(side->node_ptr(2));
439 }
440 }
441 else
442 {
443 if (eta < -1.0)
444 {
445 eta = -1.0;
446 off_edge_nodes.push_back(side->node_ptr(0));
447 off_edge_nodes.push_back(side->node_ptr(1));
448 }
449 else if (eta > 1.0)
450 {
451 eta = 1.0;
452 off_edge_nodes.push_back(side->node_ptr(2));
453 off_edge_nodes.push_back(side->node_ptr(3));
454 }
455 }
456 break;
457 }
458
459 default:
460 {
461 mooseError("Unsupported face type: ", t);
462 break;
463 }
464 }
465}
466
467} // namespace Moose
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
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
Data structure used to hold penetration information.
std::vector< const Node * > _off_edge_nodes
const Elem * _side
std::vector< RealGradient > _d2xyzdxideta
Point _closest_point_on_face_ref
std::vector< RealGradient > _dxyzdeta
std::vector< RealGradient > _dxyzdxi
std::vector< std::vector< Real > > _side_phi
const Elem * _elem
std::vector< std::vector< RealGradient > > _side_grad_phi
RealVectorValue _normal
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
auto norm() const
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
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)
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition Moose.h:175
void restrictPointToFace(libMesh::Point &p, const libMesh::Elem *side, std::vector< const libMesh::Node * > &off_edge_nodes)