https://mooseframework.inl.gov
NodalFrictionalConstraint.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 includes
12 #include "NodalConstraintUtils.h"
13 #include "MooseMesh.h"
14 #include "Assembly.h"
15 #include "SystemBase.h"
16 
17 #include "libmesh/null_output_iterator.h"
18 #include "libmesh/parallel.h"
19 #include "libmesh/parallel_elem.h"
20 #include "libmesh/parallel_node.h"
21 
22 // C++ includes
23 #include <limits.h>
24 
25 using namespace libMesh;
26 
28 
31 {
33  params.addClassDescription("Frictional nodal constraint for contact");
34  params.addRequiredParam<BoundaryName>("boundary", "The primary boundary");
35  params.addRequiredParam<BoundaryName>("secondary", "The secondary boundary");
36  params.addRequiredParam<Real>("friction_coefficient",
37  "Friction coefficient for slippage in the normal direction");
38  params.addRequiredParam<Real>("normal_force",
39  "Normal force used together with friction_coefficient to compute "
40  "the normal frictional capacity.");
41  params.addRequiredParam<Real>("tangential_penalty",
42  "Stiffness of the spring in the tangential direction.");
43  return params;
44 }
45 
47  : NodalConstraint(parameters),
48  _primary_boundary_id(getParam<BoundaryName>("boundary")),
49  _secondary_boundary_id(getParam<BoundaryName>("secondary")),
50  _normal_force(getParam<Real>("normal_force")),
51  _tangential_penalty(getParam<Real>("tangential_penalty")),
52  _friction_coefficient(getParam<Real>("friction_coefficient")),
53  _u_secondary_old(_var.dofValuesOldNeighbor()),
54  _u_primary_old(_var.dofValuesOld())
55 {
56  if (_var.number() != _var_secondary.number())
57  paramError("variable_secondary",
58  "Primary variable must be identical to secondary "
59  "variable. Different variables are currently not supported.");
60 
62 
63  MooseEnum temp_formulation = getParam<MooseEnum>("formulation");
64  if (temp_formulation == "penalty")
66  else if (temp_formulation == "kinematic")
67  mooseError("NodalFrictionalConstraint: Kinematic formulation is currently not supported for "
68  "this constraint.");
69  else
70  mooseError("Formulation must be set to Penalty.");
71 }
72 
73 void
75 {
77 }
78 
79 void
81 {
82  _primary_node_vector.clear();
83  _connected_nodes.clear();
84  _primary_conn.clear();
85 
86  std::vector<dof_id_type> secondary_nodelist =
88  std::vector<dof_id_type> primary_nodelist =
90 
91  // Fill in _connected_nodes, which defines secondary nodes in the base class
92  for (auto in : secondary_nodelist)
93  {
95  _connected_nodes.push_back(in);
96  }
97 
98  // Fill in _primary_node_vector, which defines secondary nodes in the base class
99  for (auto in : primary_nodelist)
100  _primary_node_vector.push_back(in);
101 
102  const auto & node_to_elem_map = _mesh.nodeToElemMap();
103  std::vector<std::vector<dof_id_type>> elems(_primary_node_vector.size());
104 
105  // Add elements connected to primary node to Ghosted Elements.
106 
107  // On a distributed mesh, these elements might have already been
108  // remoted, in which case we need to gather them back first.
109  if (!_mesh.getMesh().is_serial())
110  {
111  std::set<Elem *, CompareElemsByLevel> primary_elems_to_ghost;
112  std::set<Node *> nodes_to_ghost;
113 
114  for (unsigned int i = 0; i < primary_nodelist.size(); ++i)
115  {
116  auto node_to_elem_pair = node_to_elem_map.find(_primary_node_vector[i]);
117 
118  bool found_elems = (node_to_elem_pair != node_to_elem_map.end());
119 
120 #ifndef NDEBUG
121  bool someone_found_elems = found_elems;
122  _mesh.getMesh().comm().max(someone_found_elems);
123  mooseAssert(someone_found_elems, "Missing entry in node to elem map");
124 #endif
125 
126  if (found_elems)
127  {
128  for (auto id : node_to_elem_pair->second)
129  {
130  Elem * elem = _mesh.queryElemPtr(id);
131  if (elem)
132  {
133  primary_elems_to_ghost.insert(elem);
134 
135  const unsigned int n_nodes = elem->n_nodes();
136  for (unsigned int n = 0; n != n_nodes; ++n)
137  nodes_to_ghost.insert(elem->node_ptr(n));
138  }
139  }
140  }
141  }
142 
143  // Send nodes first since elements need them
145  nodes_to_ghost.begin(),
146  nodes_to_ghost.end(),
148 
150  primary_elems_to_ghost.begin(),
151  primary_elems_to_ghost.end(),
153 
154  _mesh.update(); // Rebuild node_to_elem_map
155 
156  // Find elems again now that we know they're there
157  const auto & new_node_to_elem_map = _mesh.nodeToElemMap();
158  auto node_to_elem_pair = new_node_to_elem_map.find(_primary_node_vector[0]);
159  bool found_elems = (node_to_elem_pair != new_node_to_elem_map.end());
160 
161  if (!found_elems)
162  mooseError("Colundn't find any elements connected to primary node.");
163 
164  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
165  elems[i] = node_to_elem_pair->second;
166  }
167  else // serial mesh
168  {
169  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
170  {
171  auto node_to_elem_pair = node_to_elem_map.find(_primary_node_vector[i]);
172  bool found_elems = (node_to_elem_pair != node_to_elem_map.end());
173 
174  if (!found_elems)
175  mooseError("Couldn't find any elements connected to primary node");
176 
177  elems[i] = node_to_elem_pair->second;
178  }
179  }
180 
181  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
182  {
183  if (elems[i].size() == 0)
184  mooseError("Couldn't find any elements connected to primary node");
185 
186  for (unsigned int j = 0; j < elems[i].size(); ++j)
187  _subproblem.addGhostedElem(elems[i][j]);
188  }
189 
190  // Cache map between secondary node and primary node
191  _connected_nodes.clear();
192  _primary_conn.clear();
193  for (unsigned int j = 0; j < secondary_nodelist.size(); ++j)
194  {
195  if (_mesh.nodeRef(secondary_nodelist[j]).processor_id() == _subproblem.processor_id())
196  {
197  Node & secondary_node = _mesh.nodeRef(secondary_nodelist[j]);
198  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
199  {
200  Node & primary_node = _mesh.nodeRef(_primary_node_vector[i]);
201  Real d = (secondary_node - primary_node).norm();
203  {
204  _primary_conn.push_back(i);
205  _connected_nodes.push_back(secondary_nodelist[j]);
206  break;
207  }
208  }
209  }
210  }
211 
212  _console << "total secondary nodes, primary nodes: " << _primary_conn.size() << ", "
213  << _primary_node_vector.size() << '\n';
214 }
215 
216 void
218  /*residual*/)
219 {
220  const auto & primarydof = _var.dofIndices();
221  const auto & secondarydof = _var.dofIndicesNeighbor();
222  std::vector<Number> re(primarydof.size());
223  std::vector<Number> neighbor_re(secondarydof.size());
224 
225  for (_i = 0; _i < secondarydof.size(); ++_i)
226  {
227  _j = _primary_conn[_i];
229  neighbor_re[_i] += computeQpResidual(Moose::Secondary);
230  break;
231  }
232  addResiduals(_assembly, re, primarydof, _var.scalingFactor());
233  addResiduals(_assembly, neighbor_re, secondarydof, _var.scalingFactor());
234 }
235 
236 Real
238 {
239  // check whether the tangential spring is already in the yielded state
241  if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(old_force),
243  old_force = _friction_coefficient * _normal_force * old_force / std::abs(old_force);
244 
245  Real current_force =
248  old_force;
249  if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(current_force),
251  current_force = _friction_coefficient * _normal_force * current_force / std::abs(current_force);
252 
253  switch (type)
254  {
255  case Moose::Secondary:
256  return current_force;
257  case Moose::Primary:
258  return -current_force;
259  }
260  return 0;
261 }
262 
263 void
265 {
266  // Calculate Jacobian entries and cache those entries along with the row and column indices
267  std::vector<dof_id_type> secondarydof = _var.dofIndicesNeighbor();
268  std::vector<dof_id_type> primarydof = _var.dofIndices();
269 
270  DenseMatrix<Number> Kee(primarydof.size(), primarydof.size());
271  DenseMatrix<Number> Ken(primarydof.size(), secondarydof.size());
272  DenseMatrix<Number> Kne(secondarydof.size(), primarydof.size());
273  DenseMatrix<Number> Knn(secondarydof.size(), secondarydof.size());
274 
275  Kee.zero();
276  Ken.zero();
277  Kne.zero();
278  Knn.zero();
279 
280  for (_i = 0; _i < secondarydof.size(); ++_i)
281  {
282  _j = _primary_conn[_i];
287  }
288  addJacobian(_assembly, Kee, primarydof, primarydof, _var.scalingFactor());
289  addJacobian(_assembly, Ken, primarydof, secondarydof, _var.scalingFactor());
290  addJacobian(_assembly, Kne, secondarydof, primarydof, _var.scalingFactor());
291  addJacobian(_assembly, Knn, secondarydof, secondarydof, _var.scalingFactor());
292 }
293 
294 Real
296 {
298 
299  // set jacobian to zero if spring has yielded
301  if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(old_force),
303  old_force = _friction_coefficient * _normal_force * old_force / std::abs(old_force);
304 
305  Real current_force =
308  old_force;
309  if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(current_force),
311  jac = 0.0;
312 
313  switch (type)
314  {
316  return jac;
318  return -jac;
320  return jac;
322  return -jac;
323  default:
324  mooseError("Invalid type");
325  }
326  return 0.;
327 }
MooseMesh & _mesh
const VariableValue & _u_secondary_old
Old value of the constrainted variable on the secondary nodes.
ConstraintType
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
PrimaryPrimary
const VariableValue & _u_primary
virtual void zero() override final
unsigned int number() const
unsigned int _j
void addResiduals(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
const VariableValue & _u_secondary
const Real & _tangential_penalty
Tangential stiffness of spring.
const Parallel::Communicator & comm() const
void allgather_packed_range(Context *context, Iter range_begin, const Iter range_end, OutputIter out, std::size_t approx_buffer_size=1000000) const
virtual void computeResidual() override final
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
virtual Real computeQpResidual(Moose::ConstraintType type) override
static InputParameters validParams()
virtual const Node & nodeRef(const dof_id_type i) const
unsigned int _i
void addRequiredParam(const std::string &name, const std::string &doc_string)
virtual Elem * queryElemPtr(const dof_id_type i)
const VariableValue & _u_primary_old
Old value of the constrainted variable on the primary nodes.
std::vector< dof_id_type > _primary_node_vector
void addJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
virtual Real computeQpJacobian(Moose::ConstraintJacobianType type) override
virtual bool is_serial() const
const dof_id_type n_nodes
const std::vector< dof_id_type > & dofIndices() const final
MeshBase & getMesh()
std::vector< dof_id_type > _connected_nodes
virtual unsigned int n_nodes() const=0
Moose::ConstraintFormulationType _formulation
SubProblem & _subproblem
const std::vector< dof_id_type > & getNodeList(boundary_id_type nodeset_id) const
const Real & _normal_force
Normal stiffness of spring.
SecondaryPrimary
void update()
std::vector< dof_id_type > _primary_conn
primary node id connected to each secondary node in _connected_nodes
const std::string & type() const
void paramError(const std::string &param, Args... args) const
auto norm(const T &a) -> decltype(std::abs(a))
void updateConstrainedNodes()
Update the sets of nodes with constrained DOFs.
virtual void meshChanged() override
const Real & _friction_coefficient
Coefficient of friction.
Assembly & _assembly
PrimarySecondary
virtual void computeJacobian() override final
static InputParameters validParams()
registerMooseObject("SolidMechanicsApp", NodalFrictionalConstraint)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void max(const T &r, T &o, Request &req) const
const Node * node_ptr(const unsigned int i) const
BoundaryName _primary_boundary_id
Holds the secondary node set or side set.
const std::vector< dof_id_type > & dofIndicesNeighbor() const final
virtual void addGhostedElem(dof_id_type elem_id)=0
ConstraintJacobianType
NodalFrictionalConstraint(const InputParameters &parameters)
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
const ConsoleStream _console
processor_id_type processor_id() const
SecondarySecondary
processor_id_type processor_id() const
MooseVariable & _var_secondary
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
void scalingFactor(const std::vector< Real > &factor)
BoundaryID getBoundaryID(const BoundaryName &boundary_name) const
const std::map< dof_id_type, std::vector< dof_id_type > > & nodeToElemMap()
MooseVariable & _var
BoundaryName _secondary_boundary_id
Holds the secondary node set or side set.