https://mooseframework.inl.gov
NodalStickConstraint.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
11 #include "NodalStickConstraint.h"
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 
27 registerMooseObject("SolidMechanicsApp", NodalStickConstraint);
28 
31 {
33  params.addClassDescription("Sticky nodal constraint for contact");
34  params.addRequiredParam<BoundaryName>("boundary", "The primary boundary");
35  params.addRequiredParam<BoundaryName>("secondary", "The secondary boundary");
36  params.addRequiredParam<Real>("penalty", "Stiffness of the spring.");
37  return params;
38 }
39 
41  : NodalConstraint(parameters),
42  _primary_boundary_id(getParam<BoundaryName>("boundary")),
43  _secondary_boundary_id(getParam<BoundaryName>("secondary")),
44  _penalty(getParam<Real>("penalty"))
45 {
46  if (_var.number() != _var_secondary.number())
47  paramError("variable_secondary",
48  "Primary variable must be identical to secondary variable. "
49  "Different variables are currently not supported.");
50 
52 }
53 
54 void
56 {
58 }
59 
60 void
62 {
63  _primary_node_vector.clear();
64  _connected_nodes.clear();
65  _primary_conn.clear();
66 
67  std::vector<dof_id_type> secondary_nodelist =
69  std::vector<dof_id_type> primary_nodelist =
71 
72  // Fill in _connected_nodes, which defines secondary nodes in the base class
73  for (auto in : secondary_nodelist)
74  {
76  _connected_nodes.push_back(in);
77  }
78 
79  // Fill in _primary_node_vector, which defines secondary nodes in the base class
80  for (auto in : primary_nodelist)
81  _primary_node_vector.push_back(in);
82 
83  const auto & node_to_elem_map = _mesh.nodeToElemMap();
84  std::vector<std::vector<dof_id_type>> elems(_primary_node_vector.size());
85 
86  // Add elements connected to primary node to Ghosted Elements.
87 
88  // On a distributed mesh, these elements might have already been
89  // remoted, in which case we need to gather them back first.
90  if (!_mesh.getMesh().is_serial())
91  {
92  std::set<Elem *, CompareElemsByLevel> primary_elems_to_ghost;
93  std::set<Node *> nodes_to_ghost;
94 
95  for (unsigned int i = 0; i < primary_nodelist.size(); ++i)
96  {
97  auto node_to_elem_pair = node_to_elem_map.find(_primary_node_vector[i]);
98 
99  bool found_elems = (node_to_elem_pair != node_to_elem_map.end());
100 
101 #ifndef NDEBUG
102  bool someone_found_elems = found_elems;
103  _mesh.getMesh().comm().max(someone_found_elems);
104  mooseAssert(someone_found_elems, "Missing entry in node to elem map");
105 #endif
106 
107  if (found_elems)
108  {
109  for (auto id : node_to_elem_pair->second)
110  {
111  Elem * elem = _mesh.queryElemPtr(id);
112  if (elem)
113  {
114  primary_elems_to_ghost.insert(elem);
115 
116  const unsigned int n_nodes = elem->n_nodes();
117  for (unsigned int n = 0; n != n_nodes; ++n)
118  nodes_to_ghost.insert(elem->node_ptr(n));
119  }
120  }
121  }
122  }
123 
124  // Send nodes first since elements need them
126  nodes_to_ghost.begin(),
127  nodes_to_ghost.end(),
129 
131  primary_elems_to_ghost.begin(),
132  primary_elems_to_ghost.end(),
134 
135  _mesh.update(); // Rebuild node_to_elem_map
136 
137  // Find elems again now that we know they're there
138  const auto & new_node_to_elem_map = _mesh.nodeToElemMap();
139  auto node_to_elem_pair = new_node_to_elem_map.find(_primary_node_vector[0]);
140  bool found_elems = (node_to_elem_pair != new_node_to_elem_map.end());
141 
142  if (!found_elems)
143  mooseError("Colundn't find any elements connected to primary node.");
144 
145  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
146  elems[i] = node_to_elem_pair->second;
147  }
148  else // serial mesh
149  {
150  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
151  {
152  auto node_to_elem_pair = node_to_elem_map.find(_primary_node_vector[i]);
153  bool found_elems = (node_to_elem_pair != node_to_elem_map.end());
154 
155  if (!found_elems)
156  mooseError("Couldn't find any elements connected to primary node");
157 
158  elems[i] = node_to_elem_pair->second;
159  }
160  }
161 
162  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
163  {
164  if (elems[i].size() == 0)
165  mooseError("Couldn't find any elements connected to primary node");
166 
167  for (unsigned int j = 0; j < elems[i].size(); ++j)
168  _subproblem.addGhostedElem(elems[i][j]);
169  }
170 
171  // Cache map between secondary node and primary node
172  _connected_nodes.clear();
173  _primary_conn.clear();
174  for (unsigned int j = 0; j < secondary_nodelist.size(); ++j)
175  {
176  if (_mesh.nodeRef(secondary_nodelist[j]).processor_id() == _subproblem.processor_id())
177  {
178  Node & secondary_node = _mesh.nodeRef(secondary_nodelist[j]);
179  for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
180  {
181  Node & primary_node = _mesh.nodeRef(_primary_node_vector[i]);
182  Real d = (secondary_node - primary_node).norm();
184  {
185  _primary_conn.push_back(i);
186  _connected_nodes.push_back(secondary_nodelist[j]);
187  break;
188  }
189  }
190  }
191  }
192 }
193 
194 void
196 {
197  // Calculate Jacobian enteries and cache those entries along with the row and column indices
198  std::vector<dof_id_type> secondarydof = _var.dofIndicesNeighbor();
199  std::vector<dof_id_type> primarydof = _var.dofIndices();
200 
201  DenseMatrix<Number> Kee(primarydof.size(), primarydof.size());
202  DenseMatrix<Number> Ken(primarydof.size(), secondarydof.size());
203  DenseMatrix<Number> Kne(secondarydof.size(), primarydof.size());
204  DenseMatrix<Number> Knn(secondarydof.size(), secondarydof.size());
205 
206  Kee.zero();
207  Ken.zero();
208  Kne.zero();
209  Knn.zero();
210 
211  for (_i = 0; _i < secondarydof.size(); ++_i)
212  {
213  _j = _primary_conn[_i];
214  switch (_formulation)
215  {
216  case Moose::Penalty:
221  break;
222  case Moose::Kinematic:
223  Kee(_j, _j) = 0.;
224  Ken(_j, _i) += jacobian(secondarydof[_i], primarydof[_j]);
225  Kne(_i, _j) += -jacobian(secondarydof[_i], primarydof[_j]) +
227  Knn(_i, _i) += -jacobian(secondarydof[_i], secondarydof[_i]) +
229  break;
230  }
231  }
232  addJacobian(_assembly, Kee, primarydof, primarydof, _var.scalingFactor());
233  addJacobian(_assembly, Ken, primarydof, secondarydof, _var.scalingFactor());
234  addJacobian(_assembly, Kne, secondarydof, primarydof, _var.scalingFactor());
235  addJacobian(_assembly, Knn, secondarydof, secondarydof, _var.scalingFactor());
236 }
237 
238 void
240 {
241  std::vector<dof_id_type> primarydof = _var.dofIndices();
242  std::vector<dof_id_type> secondarydof = _var.dofIndicesNeighbor();
243  DenseVector<Number> re(primarydof.size());
244  DenseVector<Number> neighbor_re(secondarydof.size());
245 
246  re.zero();
247  neighbor_re.zero();
248  for (_i = 0; _i < secondarydof.size(); ++_i)
249  {
250  _j = _primary_conn[_i];
251  switch (_formulation)
252  {
253  case Moose::Penalty:
256  break;
257  case Moose::Kinematic:
258  // Transfer the current residual of the secondary node to the primary nodes
259  Real res = residual(secondarydof[_i]);
260  re(_j) += res;
261  neighbor_re(_i) += -res + computeQpResidual(Moose::Secondary);
262  break;
263  }
264  }
265  // We've already applied scaling
266  addResiduals(_assembly, re, primarydof, /*scaling_factor=*/1);
267  addResiduals(_assembly, neighbor_re, secondarydof, /*scaling_fator=*/1);
268 }
269 
270 Real
272 {
273  switch (type)
274  {
275  case Moose::Secondary:
276  return (_u_secondary[_i] - _u_primary[_j]) * _penalty;
277  case Moose::Primary:
278  return (_u_primary[_j] - _u_secondary[_i]) * _penalty;
279  }
280  return 0.;
281 }
282 
283 Real
285 {
286  switch (type)
287  {
289  return _penalty;
291  return -_penalty;
293  return _penalty;
295  return -_penalty;
296  default:
297  mooseError("Invalid type");
298  }
299  return 0.;
300 }
MooseMesh & _mesh
NodalStickConstraint(const InputParameters &parameters)
ConstraintType
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
PrimaryPrimary
virtual void zero() override final
const VariableValue & _u_primary
const Real & _penalty
Tangential stiffness of spring in all directions.
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 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...
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)
std::vector< dof_id_type > _primary_node_vector
void addJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
static InputParameters validParams()
virtual bool is_serial() const
const dof_id_type n_nodes
const std::vector< dof_id_type > & dofIndices() const final
MeshBase & getMesh()
virtual void meshChanged() override
std::vector< dof_id_type > _connected_nodes
virtual unsigned int n_nodes() const=0
Moose::ConstraintFormulationType _formulation
SubProblem & _subproblem
virtual Real computeQpResidual(Moose::ConstraintType type) override
const std::vector< dof_id_type > & getNodeList(boundary_id_type nodeset_id) const
SecondaryPrimary
void update()
const std::string & type() const
void paramError(const std::string &param, Args... args) const
auto norm(const T &a) -> decltype(std::abs(a))
Assembly & _assembly
BoundaryName _primary_boundary_id
Holds the secondary node set or side set.
PrimarySecondary
virtual void computeJacobian() override final
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
const std::vector< dof_id_type > & dofIndicesNeighbor() const final
virtual void addGhostedElem(dof_id_type elem_id)=0
ConstraintJacobianType
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")
void updateConstrainedNodes()
Update the sets of nodes with constrained DOFs.
virtual Real computeQpJacobian(Moose::ConstraintJacobianType type) override
processor_id_type processor_id() const
SecondarySecondary
std::vector< dof_id_type > _primary_conn
primary node id connected to each secondary node in _connected_nodes
processor_id_type processor_id() const
BoundaryName _secondary_boundary_id
Holds the secondary node set or side set.
MooseVariable & _var_secondary
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
registerMooseObject("SolidMechanicsApp", NodalStickConstraint)