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