https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
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
25using namespace libMesh;
26
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{
47 paramError("variable_secondary",
48 "Primary variable must be identical to secondary variable. "
49 "Different variables are currently not supported.");
50
52}
53
54void
59
60void
62{
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();
183 if (MooseUtils::absoluteFuzzyEqual(d, 0.0))
184 {
185 _primary_conn.push_back(i);
186 _connected_nodes.push_back(secondary_nodelist[j]);
187 break;
188 }
189 }
190 }
191 }
192}
193
194void
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 {
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
238void
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 {
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
270Real
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
283Real
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}
registerMooseObject("SolidMechanicsApp", NodalStickConstraint)
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
const std::string & type() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
virtual const Node & nodeRef(const dof_id_type i) const
MeshBase & getMesh()
void update()
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & nodeToElemMap()
virtual Elem * queryElemPtr(const dof_id_type i)
BoundaryID getBoundaryID(const BoundaryName &boundary_name) const
const std::vector< dof_id_type > & getNodeList(boundary_id_type nodeset_id) const
void scalingFactor(const std::vector< Real > &factor)
unsigned int number() const
const std::vector< dof_id_type > & dofIndicesNeighbor() const final
const std::vector< dof_id_type > & dofIndices() const final
Moose::ConstraintFormulationType _formulation
unsigned int _i
unsigned int _j
std::vector< dof_id_type > _primary_node_vector
std::vector< dof_id_type > _connected_nodes
static InputParameters validParams()
MooseVariable & _var
MooseVariable & _var_secondary
const VariableValue & _u_secondary
const VariableValue & _u_primary
const Real & _penalty
Tangential stiffness of spring in all directions.
virtual void meshChanged() override
virtual void computeResidual() override final
std::vector< dof_id_type > _primary_conn
primary node id connected to each secondary node in _connected_nodes
NodalStickConstraint(const InputParameters &parameters)
virtual Real computeQpResidual(Moose::ConstraintType type) override
void updateConstrainedNodes()
Update the sets of nodes with constrained DOFs.
static InputParameters validParams()
BoundaryName _secondary_boundary_id
Holds the secondary node set or side set.
BoundaryName _primary_boundary_id
Holds the secondary node set or side set.
virtual Real computeQpJacobian(Moose::ConstraintJacobianType type) override
virtual void computeJacobian() override final
MooseMesh & _mesh
Assembly & _assembly
SubProblem & _subproblem
virtual void addGhostedElem(dof_id_type elem_id)=0
void max(const T &r, T &o, Request &req) const
void allgather_packed_range(Context *context, Iter range_begin, const Iter range_end, OutputIter out, std::size_t approx_buffer_size=1000000) const
void addJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
void addResiduals(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
virtual void zero() override final
virtual void zero() override final
processor_id_type processor_id() const
virtual unsigned int n_nodes() const=0
const Node * node_ptr(const unsigned int i) const
virtual bool is_serial() const
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
ConstraintType
ConstraintJacobianType
SecondarySecondary
SecondaryPrimary
PrimarySecondary
PrimaryPrimary
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const dof_id_type n_nodes