www.mooseframework.org
RANFSNormalMechanicalContact.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 
11 #include "PenetrationLocator.h"
12 #include "PenetrationInfo.h"
13 #include "SystemBase.h"
14 #include "Assembly.h"
15 #include "NearestNodeLocator.h"
16 #include "MooseVariableFE.h"
17 
18 #include "libmesh/numeric_vector.h"
19 #include "libmesh/enum_fe_family.h"
20 
22 
23 template <>
24 InputParameters
26 {
27  InputParameters params = validParams<NodeFaceConstraint>();
28  params.set<bool>("use_displaced_mesh") = true;
29 
30  MooseEnum component("x=0 y=1 z=2");
31  params.addRequiredParam<MooseEnum>(
32  "component", component, "The force component constraint that this object is supplying");
33  params.addRequiredCoupledVar(
34  "displacements",
35  "The displacements appropriate for the simulation geometry and coordinate system");
36  params.addClassDescription("Applies the Reduced Active Nonlinear Function Set scheme in which "
37  "the slave node's non-linear residual function is replaced by the "
38  "zero penetration constraint equation when the constraint is active");
39  return params;
40 }
41 
43  : NodeFaceConstraint(parameters),
44  _component(getParam<MooseEnum>("component")),
45  _mesh_dimension(_mesh.dimension()),
46  _residual_copy(_sys.residualGhosted()),
47  _dof_number_to_value(coupledComponents("displacements")),
48  _disp_coupling(coupledComponents("displacements"))
49 {
50  // modern parameter scheme for displacements
51  for (unsigned int i = 0; i < coupledComponents("displacements"); ++i)
52  {
53  _vars.push_back(coupled("displacements", i));
54  _var_objects.push_back(getVar("displacements", i));
55  }
56 
57  for (auto & var : _var_objects)
58  if (var->feType().family != LAGRANGE)
59  mooseError("This object only works when the displacement variables use a Lagrange basis");
60 
61  if (_vars.size() != _mesh_dimension)
62  mooseError("The number of displacement variables does not match the mesh dimension!");
63 }
64 
65 void
67 {
68  auto system_coupling_matrix = _subproblem.couplingMatrix();
69 
70  for (MooseIndex(_vars) i = 0; i < _vars.size(); ++i)
71  for (MooseIndex(_vars) j = 0; j < _vars.size(); ++j)
72  _disp_coupling(i, j) = (*system_coupling_matrix)(_vars[i], _vars[j]);
73 }
74 
75 void
77 {
80 }
81 
82 void
84 {
85  _node_to_contact_lm.clear();
86  _node_to_tied_lm.clear();
87  NodeFaceConstraint::residualSetup();
88 }
89 
90 bool
92 {
93  if (_tie_nodes)
94  return true;
95  else
96  return _largest_component == static_cast<unsigned int>(_component);
97 }
98 
99 bool
101 {
102  std::map<dof_id_type, PenetrationInfo *>::iterator found =
103  _penetration_locator._penetration_info.find(_current_node->id());
104  if (found != _penetration_locator._penetration_info.end())
105  {
106  _pinfo = found->second;
107  if (_pinfo)
108  {
109  // We overwrite the slave residual when constraints are active so we cannot use the residual
110  // copy for determining the Lagrange multiplier when computing the Jacobian
111  if (!_subproblem.currentlyComputingJacobian())
112  {
113  // Build up residual vector corresponding to contact forces
114  _res_vec.zero();
115  for (unsigned int i = 0; i < _mesh_dimension; ++i)
116  {
117  dof_id_type dof_number = _current_node->dof_number(0, _vars[i], 0);
118  _res_vec(i) = _residual_copy(dof_number) / _var_objects[i]->scalingFactor();
119  }
120 
121  _node_to_contact_lm.insert(std::make_pair(_current_node->id(), _res_vec * _pinfo->_normal));
122  _node_to_tied_lm.insert(std::make_pair(_current_node->id(), _res_vec(_component)));
123  }
124  else
125  {
126  std::vector<dof_id_type> cols;
127  std::vector<Number> values;
128 
129  for (auto & d_to_v : _dof_number_to_value)
130  d_to_v.clear();
131 
132  mooseAssert(_vars.size() == _dof_number_to_value.size() &&
133  _vars.size() == _var_objects.size(),
134  "Somehow the sizes of our variable containers got out of sync");
135  for (MooseIndex(_var_objects) i = 0; i < _var_objects.size(); ++i)
136  {
137  auto slave_dof_number = _current_node->dof_number(0, _vars[i], 0);
138 
139  _jacobian->get_row(slave_dof_number, cols, values);
140  mooseAssert(cols.size() == values.size(),
141  "The size of the dof container and value container are different");
142 
143  for (MooseIndex(cols) j = 0; j < cols.size(); ++j)
144  _dof_number_to_value[i].insert(
145  std::make_pair(cols[j], values[j] / _var_objects[i]->scalingFactor()));
146  }
147  }
148 
149  mooseAssert(_node_to_contact_lm.find(_current_node->id()) != _node_to_contact_lm.end(),
150  "The node " << _current_node->id()
151  << " should map to a contact lagrange multiplier");
152  mooseAssert(_node_to_tied_lm.find(_current_node->id()) != _node_to_tied_lm.end(),
153  "The node " << _current_node->id()
154  << " should map to a tied lagrange multiplier");
155  _contact_lm = _node_to_contact_lm[_current_node->id()];
156  _tied_lm = _node_to_tied_lm[_current_node->id()];
157 
158  // Check to see whether we've locked a ping-ponging node
159  if (_ping_pong_slave_node_to_master_node.find(_current_node->id()) ==
161  {
162  if (_contact_lm > -_pinfo->_distance)
163  {
164  // Ok, our math is telling us we should apply the constraint, but what if we are
165  // ping-ponging back and forth between different master faces?
166 
167  // This only works for a basic line search! Write assertion here
168  if (_subproblem.computingNonlinearResid())
169  {
170  auto & master_elem_sequence = _node_to_master_elem_sequence[_current_node->id()];
171  mooseAssert(
172  _current_master == _pinfo->_elem,
173  "The current master element and the PenetrationInfo object's element should "
174  "be the same");
175  master_elem_sequence.push_back(_pinfo->_elem);
176 
177  // 5 here is a heuristic choice. In testing, generally speaking, if a node ping-pongs
178  // back and forth 5 times then it will ping pong indefinitely. However, if it goes 3
179  // times for example, it is not guaranteed to ping-pong indefinitely and the Newton
180  // iteration may naturally resolve the correct face dofs that need to be involved in the
181  // constraint.
182  if (master_elem_sequence.size() >= 5 &&
183  _pinfo->_elem == *(master_elem_sequence.rbegin() + 2) &&
184  _pinfo->_elem == *(master_elem_sequence.rbegin() + 4) &&
185  _pinfo->_elem != *(master_elem_sequence.rbegin() + 1) &&
186  *(master_elem_sequence.rbegin() + 1) == *(master_elem_sequence.rbegin() + 3))
187  {
188  // Ok we are ping-ponging. Determine the master node
189  auto master_node =
190  _penetration_locator._nearest_node.nearestNode(_current_node->id());
191 
192  // Sanity checks
193  mooseAssert(_pinfo->_elem->get_node_index(master_node) != libMesh::invalid_uint,
194  "The master node is not on the current element");
195  mooseAssert((*(master_elem_sequence.rbegin() + 1))->get_node_index(master_node) !=
196  libMesh::invalid_uint,
197  "The master node is not on the other ping-ponging element");
198 
200  std::make_pair(_current_node->id(), master_node));
201  }
202  }
203  }
204  else
205  // We have not locked the node into contact nor is the gap smaller than the Lagrange
206  // Multiplier so we should not apply
207  return false;
208  }
209 
210  // Determine whether we're going to apply the tied node equality constraint or the contact
211  // inequality constraint
212  auto it = _ping_pong_slave_node_to_master_node.find(_current_node->id());
213  if (it != _ping_pong_slave_node_to_master_node.end())
214  {
215  _tie_nodes = true;
216  _nearest_node = it->second;
217  _master_index = _current_master->get_node_index(_nearest_node);
218  mooseAssert(_master_index != libMesh::invalid_uint,
219  "nearest node not a node on the current master element");
220  }
221  else
222  {
223  _distance = _pinfo->_distance;
224  // Do this to make sure constraint equation has a positive on the diagonal
225  if (_pinfo->_normal(_component) > 0)
226  _distance *= -1;
227  _tie_nodes = false;
228 
229  // The contact constraint is active -> we're going to use our linear solve to ensure that
230  // the gap is driven to zero. We only have one zero-penetration constraint per node, so we
231  // choose to apply the zero penetration constraint only to the displacement component with
232  // the largest magnitude normal
233  auto largest_component_magnitude = std::abs(_pinfo->_normal(0));
234  _largest_component = 0;
235  for (MooseIndex(_mesh_dimension) i = 1; i < _mesh_dimension; ++i)
236  {
237  auto component_magnitude = std::abs(_pinfo->_normal(i));
238  if (component_magnitude > largest_component_magnitude)
239  {
240  largest_component_magnitude = component_magnitude;
241  _largest_component = i;
242  }
243  }
244  }
245 
246  return true;
247  }
248  }
249 
250  // If we're not applying the constraint then we can clear the node to master elem sequence for
251  // this node
252  if (_node_to_master_elem_sequence.find(_current_node->id()) !=
254  _node_to_master_elem_sequence[_current_node->id()].clear();
255  return false;
256 }
257 
258 Real
260 {
261  switch (type)
262  {
263  case Moose::ConstraintType::Slave:
264  {
265  if (_tie_nodes)
266  return (*_current_node - *_nearest_node)(_component);
267  else
268  {
269  if (_largest_component == static_cast<unsigned int>(_component))
270  {
271  mooseAssert(_pinfo->_normal(_component) != 0,
272  "We should be selecting the largest normal component, hence it should be "
273  "impossible for this normal component to be zero");
274 
275  return _distance;
276  }
277 
278  else
279  // The normal points out of the master face
280  return _contact_lm * -_pinfo->_normal(_component);
281  }
282  }
283 
284  case Moose::ConstraintType::Master:
285  {
286  if (_tie_nodes)
287  {
288  if (_i == _master_index)
289  return _tied_lm;
290  else
291  return 0;
292  }
293  else
294  return _test_master[_i][_qp] * _contact_lm * _pinfo->_normal(_component);
295  }
296 
297  default:
298  return 0;
299  }
300 }
301 
302 Real
303 RANFSNormalMechanicalContact::computeQpJacobian(Moose::ConstraintJacobianType type)
304 {
305  switch (type)
306  {
307  case Moose::ConstraintJacobianType::SlaveSlave:
308  {
309  if (_tie_nodes)
310  return _phi_slave[_j][_qp];
311 
312  // doing contact
313  else
314  {
315  // corresponds to gap equation
316  if (_largest_component == static_cast<unsigned int>(_component))
317  // _phi_slave has been set such that it is 1 when _j corresponds to the degree of freedom
318  // associated with the _current node and 0 otherwise
319  return std::abs(_pinfo->_normal(_component)) * _phi_slave[_j][_qp];
320 
321  // corresponds to regular residual with Lagrange Multiplier applied
322  else
323  {
324  Real ret_val = 0;
325  for (MooseIndex(_disp_coupling) i = 0; i < _disp_coupling.size(); ++i)
326  if (_disp_coupling(_component, i))
327  {
328  mooseAssert(
329  _dof_number_to_value[i].find(_connected_dof_indices[_j]) !=
330  _dof_number_to_value[i].end(),
331  "The connected dof index is not found in the _dof_number_to_value container. "
332  "This must mean that insufficient sparsity was allocated");
333  ret_val += -_pinfo->_normal(_component) * _pinfo->_normal(i) *
334  _dof_number_to_value[i][_connected_dof_indices[_j]];
335  }
336  return ret_val;
337  }
338  }
339  }
340 
341  case Moose::ConstraintJacobianType::SlaveMaster:
342  {
343  if (_tie_nodes)
344  {
345  if (_master_index == _j)
346  return -1;
347 
348  // We're tying the slave node to only one node on the master side (specified by
349  // _master_index). If the current _j doesn't correspond to that tied master node, then the
350  // slave residual doesn't depend on it
351  else
352  return 0;
353  }
354  else
355  {
356  if (_largest_component == static_cast<unsigned int>(_component))
357  return -std::abs(_pinfo->_normal(_component)) * _phi_master[_j][_qp];
358 
359  // If we're not applying the gap constraint equation on this _component, then we're
360  // applying a Lagrange multiplier, and consequently there is no dependence of the slave
361  // residual on the master dofs because the Lagrange multiplier is only a functon of the
362  // slave residuals
363  else
364  return 0;
365  }
366  }
367 
368  case Moose::ConstraintJacobianType::MasterSlave:
369  {
370  if (_tie_nodes)
371  {
372  if (_i == _master_index)
373  {
374  mooseAssert(_dof_number_to_value[_component].find(_connected_dof_indices[_j]) !=
376  "The connected dof index is not found in the _dof_number_to_value container. "
377  "This must mean that insufficient sparsity was allocated");
378  return _dof_number_to_value[_component][_connected_dof_indices[_j]];
379  }
380 
381  // We only apply the tied node Lagrange multiplier to the closest master node
382  else
383  return 0;
384  }
385  else
386  {
387  Real ret_val = 0;
388  for (MooseIndex(_disp_coupling) i = 0; i < _disp_coupling.size(); ++i)
389  if (_disp_coupling(_component, i))
390  {
391  mooseAssert(
392  _dof_number_to_value[i].find(_connected_dof_indices[_j]) !=
393  _dof_number_to_value[i].end(),
394  "The connected dof index is not found in the _dof_number_to_value container. "
395  "This must mean that insufficient sparsity was allocated");
396  ret_val += _test_master[_i][_qp] * _pinfo->_normal(_component) * _pinfo->_normal(i) *
397  _dof_number_to_value[i][_connected_dof_indices[_j]];
398  }
399  return ret_val;
400  }
401  }
402 
403  // The only master-master dependence would come from the dependence of the normal and also the
404  // location of the integration (quadrature) points. We assume (valid or not) that this
405  // dependence is weak
406  // case MooseConstraintJacobianType::MasterMaster
407 
408  default:
409  return 0;
410  }
411 }
412 
413 void
415 {
416 }
417 
418 Real
420 {
421  mooseError("We overrode commputeSlaveValue so computeQpSlaveValue should never get called");
422 }
RANFSNormalMechanicalContact.h
RANFSNormalMechanicalContact::computeQpJacobian
virtual Real computeQpJacobian(Moose::ConstraintJacobianType type) override
Definition: RANFSNormalMechanicalContact.C:303
RANFSNormalMechanicalContact::RANFSNormalMechanicalContact
RANFSNormalMechanicalContact(const InputParameters &parameters)
Definition: RANFSNormalMechanicalContact.C:42
RANFSNormalMechanicalContact::residualSetup
void residualSetup() override
Definition: RANFSNormalMechanicalContact.C:83
RANFSNormalMechanicalContact
Definition: RANFSNormalMechanicalContact.h:33
RANFSNormalMechanicalContact::shouldApply
bool shouldApply() override
Definition: RANFSNormalMechanicalContact.C:100
RANFSNormalMechanicalContact::_node_to_tied_lm
std::unordered_map< dof_id_type, Real > _node_to_tied_lm
Definition: RANFSNormalMechanicalContact.h:59
RANFSNormalMechanicalContact::_largest_component
unsigned int _largest_component
Definition: RANFSNormalMechanicalContact.h:55
registerMooseObject
registerMooseObject("ContactApp", RANFSNormalMechanicalContact)
RANFSNormalMechanicalContact::_tied_lm
Real _tied_lm
Definition: RANFSNormalMechanicalContact.h:62
RANFSNormalMechanicalContact::_contact_lm
Real _contact_lm
Definition: RANFSNormalMechanicalContact.h:61
RANFSNormalMechanicalContact::_disp_coupling
CouplingMatrix _disp_coupling
Definition: RANFSNormalMechanicalContact.h:71
RANFSNormalMechanicalContact::computeSlaveValue
void computeSlaveValue(NumericVector< Number > &solution) override
Definition: RANFSNormalMechanicalContact.C:414
RANFSNormalMechanicalContact::_mesh_dimension
const unsigned int _mesh_dimension
Definition: RANFSNormalMechanicalContact.h:52
RANFSNormalMechanicalContact::_var_objects
std::vector< MooseVariable * > _var_objects
Definition: RANFSNormalMechanicalContact.h:57
RANFSNormalMechanicalContact::initialSetup
void initialSetup() override
Definition: RANFSNormalMechanicalContact.C:66
RANFSNormalMechanicalContact::_residual_copy
NumericVector< Number > & _residual_copy
Definition: RANFSNormalMechanicalContact.h:53
RANFSNormalMechanicalContact::_master_index
unsigned int _master_index
Definition: RANFSNormalMechanicalContact.h:67
RANFSNormalMechanicalContact::computeQpResidual
virtual Real computeQpResidual(Moose::ConstraintType type) override
Definition: RANFSNormalMechanicalContact.C:259
RANFSNormalMechanicalContact::_vars
std::vector< unsigned int > _vars
Definition: RANFSNormalMechanicalContact.h:56
RANFSNormalMechanicalContact::_res_vec
RealVectorValue _res_vec
Definition: RANFSNormalMechanicalContact.h:68
MaterialTensorCalculatorTools::component
Real component(const SymmTensor &symm_tensor, unsigned int index)
Definition: MaterialTensorCalculatorTools.C:16
RANFSNormalMechanicalContact::timestepSetup
void timestepSetup() override
Definition: RANFSNormalMechanicalContact.C:76
RANFSNormalMechanicalContact::_node_to_master_elem_sequence
std::unordered_map< dof_id_type, std::vector< const Elem * > > _node_to_master_elem_sequence
Definition: RANFSNormalMechanicalContact.h:60
RANFSNormalMechanicalContact::_node_to_contact_lm
std::unordered_map< dof_id_type, Real > _node_to_contact_lm
Definition: RANFSNormalMechanicalContact.h:58
RANFSNormalMechanicalContact::_distance
Real _distance
Definition: RANFSNormalMechanicalContact.h:65
validParams< RANFSNormalMechanicalContact >
InputParameters validParams< RANFSNormalMechanicalContact >()
Definition: RANFSNormalMechanicalContact.C:25
RANFSNormalMechanicalContact::computeQpSlaveValue
virtual Real computeQpSlaveValue() override
Definition: RANFSNormalMechanicalContact.C:419
RANFSNormalMechanicalContact::overwriteSlaveResidual
bool overwriteSlaveResidual() override
Definition: RANFSNormalMechanicalContact.C:91
RANFSNormalMechanicalContact::_dof_number_to_value
std::vector< std::unordered_map< dof_id_type, Number > > _dof_number_to_value
Definition: RANFSNormalMechanicalContact.h:70
RANFSNormalMechanicalContact::_nearest_node
const Node * _nearest_node
Definition: RANFSNormalMechanicalContact.h:69
RANFSNormalMechanicalContact::_pinfo
PenetrationInfo * _pinfo
Definition: RANFSNormalMechanicalContact.h:63
RANFSNormalMechanicalContact::_ping_pong_slave_node_to_master_node
std::unordered_map< dof_id_type, const Node * > _ping_pong_slave_node_to_master_node
Definition: RANFSNormalMechanicalContact.h:64
RANFSNormalMechanicalContact::_component
const MooseEnum _component
Definition: RANFSNormalMechanicalContact.h:51
RANFSNormalMechanicalContact::_tie_nodes
bool _tie_nodes
Definition: RANFSNormalMechanicalContact.h:66