www.mooseframework.org
ContactMaster.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 includes
11 #include "ContactMaster.h"
12 #include "NodalArea.h"
13 #include "SystemBase.h"
14 #include "PenetrationInfo.h"
15 #include "MooseMesh.h"
16 #include "Executioner.h"
17 #include "ContactAction.h"
18 
19 #include "libmesh/sparse_matrix.h"
20 #include "libmesh/string_to_enum.h"
21 
22 registerMooseObject("ContactApp", ContactMaster);
23 
24 template <>
25 InputParameters
27 {
28  InputParameters params = validParams<DiracKernel>();
30 
31  params.addRequiredParam<BoundaryName>("boundary", "The master boundary");
32  params.addRequiredParam<BoundaryName>("slave", "The slave boundary");
33  params.addRequiredParam<unsigned int>("component",
34  "An integer corresponding to the direction "
35  "the variable this kernel acts in. (0 for x, "
36  "1 for y, 2 for z)");
37 
38  params.addCoupledVar("disp_x", "The x displacement");
39  params.addCoupledVar("disp_y", "The y displacement");
40  params.addCoupledVar("disp_z", "The z displacement");
41 
42  params.addCoupledVar(
43  "displacements",
44  "The displacements appropriate for the simulation geometry and coordinate system");
45 
46  params.addRequiredCoupledVar("nodal_area", "The nodal area");
47 
48  params.set<bool>("use_displaced_mesh") = true;
49  params.addParam<Real>(
50  "penalty",
51  1e8,
52  "The penalty to apply. This can vary depending on the stiffness of your materials");
53  params.addParam<Real>("friction_coefficient", 0, "The friction coefficient");
54  params.addParam<Real>("tangential_tolerance",
55  "Tangential distance to extend edges of contact surfaces");
56  params.addParam<Real>(
57  "capture_tolerance", 0, "Normal distance from surface within which nodes are captured");
58 
59  params.addParam<Real>("tension_release",
60  0.0,
61  "Tension release threshold. A node in contact "
62  "will not be released if its tensile load is below "
63  "this value. No tension release if negative.");
64 
65  params.addParam<bool>(
66  "normalize_penalty",
67  false,
68  "Whether to normalize the penalty parameter with the nodal area for penalty contact.");
69  return params;
70 }
71 
72 ContactMaster::ContactMaster(const InputParameters & parameters)
73  : DiracKernel(parameters),
74  _component(getParam<unsigned int>("component")),
75  _model(getParam<MooseEnum>("model").getEnum<ContactModel>()),
76  _formulation(getParam<MooseEnum>("formulation").getEnum<ContactFormulation>()),
77  _normalize_penalty(getParam<bool>("normalize_penalty")),
78  _penetration_locator(
79  getPenetrationLocator(getParam<BoundaryName>("boundary"),
80  getParam<BoundaryName>("slave"),
81  Utility::string_to_enum<Order>(getParam<MooseEnum>("order")))),
82  _penalty(getParam<Real>("penalty")),
83  _friction_coefficient(getParam<Real>("friction_coefficient")),
84  _tension_release(getParam<Real>("tension_release")),
85  _capture_tolerance(getParam<Real>("capture_tolerance")),
86  _residual_copy(_sys.residualGhosted()),
87  _mesh_dimension(_mesh.dimension()),
88  _vars(3, libMesh::invalid_uint),
89  _nodal_area_var(getVar("nodal_area", 0)),
90  _aux_system(_nodal_area_var->sys()),
91  _aux_solution(_aux_system.currentSolution())
92 {
93  if (isParamValid("displacements"))
94  {
95  // modern parameter scheme for displacements
96  for (unsigned int i = 0; i < coupledComponents("displacements"); ++i)
97  _vars[i] = coupled("displacements", i);
98  }
99  else
100  {
101  // Legacy parameter scheme for displacements
102  if (isParamValid("disp_x"))
103  _vars[0] = coupled("disp_x");
104  if (isParamValid("disp_y"))
105  _vars[1] = coupled("disp_y");
106  if (isParamValid("disp_z"))
107  _vars[2] = coupled("disp_z");
108 
109  mooseDeprecated("use the `displacements` parameter rather than the `disp_*` parameters (those "
110  "will go away with the deprecation of the Solid Mechanics module).");
111  }
112 
113  if (parameters.isParamValid("tangential_tolerance"))
114  _penetration_locator.setTangentialTolerance(getParam<Real>("tangential_tolerance"));
115 
116  if (parameters.isParamValid("normal_smoothing_distance"))
117  _penetration_locator.setNormalSmoothingDistance(getParam<Real>("normal_smoothing_distance"));
118 
119  if (parameters.isParamValid("normal_smoothing_method"))
120  _penetration_locator.setNormalSmoothingMethod(
121  parameters.get<MooseEnum>("normal_smoothing_method"));
122 
123  if (_model == ContactModel::GLUED ||
125  _penetration_locator.setUpdate(false);
126 
127  if (_friction_coefficient < 0)
128  mooseError("The friction coefficient must be nonnegative");
129 }
130 
131 void
133 {
134  if (_component == 0)
136 }
137 
138 void
140 {
141  std::map<dof_id_type, PenetrationInfo *>::iterator
142  it = _penetration_locator._penetration_info.begin(),
143  end = _penetration_locator._penetration_info.end();
144  for (; it != end; ++it)
145  {
146  PenetrationInfo * pinfo = it->second;
147 
148  // Skip this pinfo if there are no DOFs on this node.
149  if (!pinfo || pinfo->_node->n_comp(_sys.number(), _vars[_component]) < 1)
150  continue;
151 
152  pinfo->_locked_this_step = 0;
153  pinfo->_starting_elem = it->second->_elem;
154  pinfo->_starting_side_num = it->second->_side_num;
155  pinfo->_starting_closest_point_ref = it->second->_closest_point_ref;
156  pinfo->_contact_force_old = pinfo->_contact_force;
157  pinfo->_accumulated_slip_old = pinfo->_accumulated_slip;
158  pinfo->_frictional_energy_old = pinfo->_frictional_energy;
159  }
160 }
161 
162 void
164 {
165  _point_to_info.clear();
166 
167  std::map<dof_id_type, PenetrationInfo *>::iterator
168  it = _penetration_locator._penetration_info.begin(),
169  end = _penetration_locator._penetration_info.end();
170 
171  for (; it != end; ++it)
172  {
173  PenetrationInfo * pinfo = it->second;
174 
175  // Skip this pinfo if there are no DOFs on this node.
176  if (!pinfo || pinfo->_node->n_comp(_sys.number(), _vars[_component]) < 1)
177  continue;
178 
179  bool is_nonlinear =
180  _subproblem.getMooseApp().getExecutioner()->feProblem().computingNonlinearResid();
181 
182  if (_component == 0)
183  computeContactForce(pinfo, is_nonlinear);
184 
185  if (pinfo->isCaptured())
186  {
187  addPoint(pinfo->_elem, pinfo->_closest_point);
188  _point_to_info[pinfo->_closest_point] = pinfo;
189  }
190  }
191 }
192 
193 void
194 ContactMaster::computeContactForce(PenetrationInfo * pinfo, bool update_contact_set)
195 {
196  const Node * node = pinfo->_node;
197 
198  RealVectorValue res_vec;
199  // Build up residual vector
200  for (unsigned int i = 0; i < _mesh_dimension; ++i)
201  {
202  dof_id_type dof_number = node->dof_number(0, _vars[i], 0);
203  res_vec(i) = _residual_copy(dof_number);
204  }
205 
206  RealVectorValue distance_vec(_mesh.nodeRef(node->id()) - pinfo->_closest_point);
207  const Real gap_size = -1. * pinfo->_normal * distance_vec;
208 
209  // This is for preventing an increment of pinfo->_locked_this_step for nodes that are
210  // captured and released in this function
211  bool newly_captured = false;
212 
213  // Capture
214  if (update_contact_set && !pinfo->isCaptured() &&
215  MooseUtils::absoluteFuzzyGreaterEqual(gap_size, 0, _capture_tolerance))
216  {
217  newly_captured = true;
218  pinfo->capture();
219 
220  // Increment the lock count every time the node comes back into contact from not being in
221  // contact.
223  ++pinfo->_locked_this_step;
224  }
225 
226  if (!pinfo->isCaptured())
227  return;
228 
229  const Real area = nodalArea(*pinfo);
230 
231  RealVectorValue pen_force(_penalty * distance_vec);
232  if (_normalize_penalty)
233  pen_force *= area;
234 
236  {
237  switch (_formulation)
238  {
240  pinfo->_contact_force = -pinfo->_normal * (pinfo->_normal * res_vec);
241  break;
243  pinfo->_contact_force = pinfo->_normal * (pinfo->_normal * pen_force);
244  break;
246  pinfo->_contact_force =
247  (pinfo->_normal *
248  (pinfo->_normal * (pen_force + pinfo->_lagrange_multiplier * pinfo->_normal)));
249  break;
250  default:
251  mooseError("Invalid contact formulation");
252  break;
253  }
254  pinfo->_mech_status = PenetrationInfo::MS_SLIPPING;
255  }
257  {
258  distance_vec =
259  pinfo->_incremental_slip +
260  (pinfo->_normal * (_mesh.nodeRef(node->id()) - pinfo->_closest_point)) * pinfo->_normal;
261  pen_force = _penalty * distance_vec;
262  if (_normalize_penalty)
263  pen_force *= area;
264 
265  // Frictional capacity
266  // const Real capacity( _friction_coefficient * (pen_force * pinfo->_normal < 0 ? -pen_force *
267  // pinfo->_normal : 0) );
268  const Real capacity(_friction_coefficient *
269  (res_vec * pinfo->_normal > 0 ? res_vec * pinfo->_normal : 0));
270 
271  // Elastic predictor
272  pinfo->_contact_force =
273  pen_force +
274  (pinfo->_contact_force_old - pinfo->_normal * (pinfo->_normal * pinfo->_contact_force_old));
275  RealVectorValue contact_force_normal((pinfo->_contact_force * pinfo->_normal) * pinfo->_normal);
276  RealVectorValue contact_force_tangential(pinfo->_contact_force - contact_force_normal);
277 
278  // Tangential magnitude of elastic predictor
279  const Real tan_mag(contact_force_tangential.norm());
280 
281  if (tan_mag > capacity)
282  {
283  pinfo->_contact_force = contact_force_normal + capacity * contact_force_tangential / tan_mag;
284  pinfo->_mech_status = PenetrationInfo::MS_SLIPPING;
285  }
286  else
287  {
288  pinfo->_mech_status = PenetrationInfo::MS_STICKING;
289  }
290  }
291  else if (_model == ContactModel::GLUED ||
293  {
294  switch (_formulation)
295  {
297  pinfo->_contact_force = -res_vec;
298  break;
300  pinfo->_contact_force = pen_force;
301  break;
303  pinfo->_contact_force =
304  pen_force + pinfo->_lagrange_multiplier * distance_vec / distance_vec.norm();
305  break;
306  default:
307  mooseError("Invalid contact formulation");
308  break;
309  }
310  pinfo->_mech_status = PenetrationInfo::MS_STICKING;
311  }
312  else
313  {
314  mooseError("Invalid or unavailable contact model");
315  }
316 
317  // Release
318  if (update_contact_set && _model != ContactModel::GLUED && pinfo->isCaptured() &&
319  !newly_captured && _tension_release >= 0 && pinfo->_locked_this_step < 2)
320  {
321  const Real contact_pressure = -(pinfo->_normal * pinfo->_contact_force) / area;
322  if (-contact_pressure >= _tension_release)
323  {
324  pinfo->release();
325  pinfo->_contact_force.zero();
326  }
327  }
328 
329  if (_formulation == ContactFormulation::AUGMENTED_LAGRANGE && pinfo->isCaptured())
330  pinfo->_lagrange_multiplier -= getPenalty(*pinfo) * gap_size;
331 }
332 
333 Real
335 {
336  PenetrationInfo * pinfo = _point_to_info[_current_point];
337  Real resid = -pinfo->_contact_force(_component);
338  return _test[_i][_qp] * resid;
339 }
340 
341 Real
343 {
344 
345  PenetrationInfo * pinfo = _point_to_info[_current_point];
346  Real penalty = _penalty;
347  if (_normalize_penalty)
348  penalty *= nodalArea(*pinfo);
349 
350  switch (_model)
351  {
353  switch (_formulation)
354  {
356  return 0;
357  break;
360  return _test[_i][_qp] * penalty * _phi[_j][_qp] * pinfo->_normal(_component) *
361  pinfo->_normal(_component);
362  break;
363  default:
364  mooseError("Invalid contact formulation");
365  break;
366  }
367  break;
368  case ContactModel::GLUED:
370  switch (_formulation)
371  {
373  return 0;
374  break;
377  return _test[_i][_qp] * penalty * _phi[_j][_qp];
378  break;
379  default:
380  mooseError("Invalid contact formulation");
381  break;
382  }
383  break;
384  default:
385  mooseError("Invalid or unavailable contact model");
386  break;
387  }
388 
389  return 0;
390  /*
391  if (_i != _j)
392  return 0;
393 
394  Node * node = pinfo->_node;
395 
396  RealVectorValue jac_vec;
397 
398  // Build up jac vector
399  for (unsigned int i=0; i<_dim; i++)
400  {
401  long int dof_number = node->dof_number(0, _vars[i], 0);
402  jac_vec(i) = _jacobian_copy(dof_number, dof_number);
403  }
404 
405  Real jac_mag = pinfo->_normal * jac_vec;
406 
407  return _test[_i][_qp]*pinfo->_normal(_component)*jac_mag;
408  */
409 }
410 
411 Real
412 ContactMaster::nodalArea(PenetrationInfo & pinfo)
413 {
414  const Node * node = pinfo._node;
415 
416  dof_id_type dof = node->dof_number(_aux_system.number(), _nodal_area_var->number(), 0);
417 
418  Real area = (*_aux_solution)(dof);
419  if (area == 0.0)
420  {
421  if (_t_step > 1)
422  mooseError("Zero nodal area found");
423  else
424  area = 1.0; // Avoid divide by zero during initialization
425  }
426  return area;
427 }
428 
429 Real
430 ContactMaster::getPenalty(PenetrationInfo & pinfo)
431 {
432  Real penalty = _penalty;
433  if (_normalize_penalty)
434  penalty *= nodalArea(pinfo);
435 
436  return penalty;
437 }
ContactModel
ContactModel
Definition: ContactAction.h:16
ContactMaster::_component
const unsigned int _component
Definition: ContactMaster.h:42
libMesh
Definition: RANFSNormalMechanicalContact.h:24
ContactMaster::computeQpJacobian
virtual Real computeQpJacobian() override
Definition: ContactMaster.C:342
ContactModel::GLUED
ContactMaster::_aux_system
SystemBase & _aux_system
Definition: ContactMaster.h:62
ContactMaster::_penetration_locator
PenetrationLocator & _penetration_locator
Definition: ContactMaster.h:46
ContactMaster
Definition: ContactMaster.h:24
ContactMaster::_residual_copy
NumericVector< Number > & _residual_copy
Definition: ContactMaster.h:53
ContactMaster::_point_to_info
std::map< Point, PenetrationInfo * > _point_to_info
Definition: ContactMaster.h:55
ContactFormulation::KINEMATIC
ContactMaster::_formulation
const ContactFormulation _formulation
Definition: ContactMaster.h:44
ContactMaster::nodalArea
Real nodalArea(PenetrationInfo &pinfo)
Definition: ContactMaster.C:412
ContactMaster::_vars
std::vector< unsigned int > _vars
Definition: ContactMaster.h:59
ContactMaster::computeQpResidual
virtual Real computeQpResidual() override
Definition: ContactMaster.C:334
ContactMaster::_tension_release
const Real _tension_release
Definition: ContactMaster.h:50
ContactModel::COULOMB
ContactMaster::_capture_tolerance
const Real _capture_tolerance
Definition: ContactMaster.h:51
ContactModel::FRICTIONLESS
ContactMaster::addPoints
virtual void addPoints() override
Definition: ContactMaster.C:163
NodalArea.h
ContactMaster.h
ContactMaster::_normalize_penalty
const bool _normalize_penalty
Definition: ContactMaster.h:45
ContactFormulation
ContactFormulation
Definition: ContactAction.h:23
ContactMaster::_nodal_area_var
MooseVariable * _nodal_area_var
Definition: ContactMaster.h:61
ContactMaster::computeContactForce
void computeContactForce(PenetrationInfo *pinfo, bool update_contact_set)
Definition: ContactMaster.C:194
ContactMaster::getPenalty
Real getPenalty(PenetrationInfo &pinfo)
Definition: ContactMaster.C:430
ContactMaster::_mesh_dimension
const unsigned int _mesh_dimension
Definition: ContactMaster.h:57
ContactMaster::_friction_coefficient
const Real _friction_coefficient
Definition: ContactMaster.h:49
ContactFormulation::AUGMENTED_LAGRANGE
ContactMaster::_penalty
const Real _penalty
Definition: ContactMaster.h:48
ContactMaster::_model
const ContactModel _model
Definition: ContactMaster.h:43
ContactMaster::updateContactStatefulData
virtual void updateContactStatefulData()
Definition: ContactMaster.C:139
ContactMaster::timestepSetup
virtual void timestepSetup() override
Definition: ContactMaster.C:132
ContactAction.h
ContactAction::commonParameters
static InputParameters commonParameters()
Definition: ContactAction.C:441
registerMooseObject
registerMooseObject("ContactApp", ContactMaster)
ContactMaster::ContactMaster
ContactMaster(const InputParameters &parameters)
Definition: ContactMaster.C:72
ContactFormulation::PENALTY
validParams< ContactMaster >
InputParameters validParams< ContactMaster >()
Definition: ContactMaster.C:26