Line data Source code
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 "NodalFrictionalConstraint.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", NodalFrictionalConstraint);
28 :
29 : InputParameters
30 24 : NodalFrictionalConstraint::validParams()
31 : {
32 24 : InputParameters params = NodalConstraint::validParams();
33 24 : params.addClassDescription("Frictional nodal constraint for contact");
34 48 : params.addRequiredParam<BoundaryName>("boundary", "The primary boundary");
35 48 : params.addRequiredParam<BoundaryName>("secondary", "The secondary boundary");
36 48 : params.addRequiredParam<Real>("friction_coefficient",
37 : "Friction coefficient for slippage in the normal direction");
38 48 : params.addRequiredParam<Real>("normal_force",
39 : "Normal force used together with friction_coefficient to compute "
40 : "the normal frictional capacity.");
41 48 : params.addRequiredParam<Real>("tangential_penalty",
42 : "Stiffness of the spring in the tangential direction.");
43 24 : return params;
44 0 : }
45 :
46 12 : NodalFrictionalConstraint::NodalFrictionalConstraint(const InputParameters & parameters)
47 : : NodalConstraint(parameters),
48 24 : _primary_boundary_id(getParam<BoundaryName>("boundary")),
49 12 : _secondary_boundary_id(getParam<BoundaryName>("secondary")),
50 24 : _normal_force(getParam<Real>("normal_force")),
51 24 : _tangential_penalty(getParam<Real>("tangential_penalty")),
52 24 : _friction_coefficient(getParam<Real>("friction_coefficient")),
53 12 : _u_secondary_old(_var.dofValuesOldNeighbor()),
54 24 : _u_primary_old(_var.dofValuesOld())
55 : {
56 12 : if (_var.number() != _var_secondary.number())
57 0 : paramError("variable_secondary",
58 : "Primary variable must be identical to secondary "
59 : "variable. Different variables are currently not supported.");
60 :
61 12 : updateConstrainedNodes();
62 :
63 24 : MooseEnum temp_formulation = getParam<MooseEnum>("formulation");
64 12 : if (temp_formulation == "penalty")
65 12 : _formulation = Moose::Penalty;
66 0 : else if (temp_formulation == "kinematic")
67 0 : mooseError("NodalFrictionalConstraint: Kinematic formulation is currently not supported for "
68 : "this constraint.");
69 : else
70 0 : mooseError("Formulation must be set to Penalty.");
71 12 : }
72 :
73 : void
74 0 : NodalFrictionalConstraint::meshChanged()
75 : {
76 0 : updateConstrainedNodes();
77 0 : }
78 :
79 : void
80 12 : NodalFrictionalConstraint::updateConstrainedNodes()
81 : {
82 12 : _primary_node_vector.clear();
83 12 : _connected_nodes.clear();
84 12 : _primary_conn.clear();
85 :
86 : std::vector<dof_id_type> secondary_nodelist =
87 12 : _mesh.getNodeList(_mesh.getBoundaryID(_secondary_boundary_id));
88 : std::vector<dof_id_type> primary_nodelist =
89 12 : _mesh.getNodeList(_mesh.getBoundaryID(_primary_boundary_id));
90 :
91 : // Fill in _connected_nodes, which defines secondary nodes in the base class
92 24 : for (auto in : secondary_nodelist)
93 : {
94 12 : if (_mesh.nodeRef(in).processor_id() == _subproblem.processor_id())
95 8 : _connected_nodes.push_back(in);
96 : }
97 :
98 : // Fill in _primary_node_vector, which defines secondary nodes in the base class
99 36 : for (auto in : primary_nodelist)
100 24 : _primary_node_vector.push_back(in);
101 :
102 12 : const auto & node_to_elem_map = _mesh.nodeToElemMap();
103 12 : 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 12 : if (!_mesh.getMesh().is_serial())
110 : {
111 : std::set<Elem *, CompareElemsByLevel> primary_elems_to_ghost;
112 : std::set<Node *> nodes_to_ghost;
113 :
114 0 : 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 0 : if (found_elems)
127 : {
128 0 : for (auto id : node_to_elem_pair->second)
129 : {
130 0 : Elem * elem = _mesh.queryElemPtr(id);
131 0 : if (elem)
132 : {
133 0 : primary_elems_to_ghost.insert(elem);
134 :
135 0 : const unsigned int n_nodes = elem->n_nodes();
136 0 : for (unsigned int n = 0; n != n_nodes; ++n)
137 0 : nodes_to_ghost.insert(elem->node_ptr(n));
138 : }
139 : }
140 : }
141 : }
142 :
143 : // Send nodes first since elements need them
144 0 : _mesh.getMesh().comm().allgather_packed_range(&_mesh.getMesh(),
145 : nodes_to_ghost.begin(),
146 : nodes_to_ghost.end(),
147 : null_output_iterator<Node>());
148 :
149 0 : _mesh.getMesh().comm().allgather_packed_range(&_mesh.getMesh(),
150 : primary_elems_to_ghost.begin(),
151 : primary_elems_to_ghost.end(),
152 : null_output_iterator<Elem>());
153 :
154 0 : _mesh.update(); // Rebuild node_to_elem_map
155 :
156 : // Find elems again now that we know they're there
157 0 : 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 0 : if (!found_elems)
162 0 : mooseError("Colundn't find any elements connected to primary node.");
163 :
164 0 : for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
165 0 : elems[i] = node_to_elem_pair->second;
166 : }
167 : else // serial mesh
168 : {
169 36 : 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 24 : if (!found_elems)
175 0 : mooseError("Couldn't find any elements connected to primary node");
176 :
177 24 : elems[i] = node_to_elem_pair->second;
178 : }
179 : }
180 :
181 36 : for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
182 : {
183 24 : if (elems[i].size() == 0)
184 0 : mooseError("Couldn't find any elements connected to primary node");
185 :
186 48 : for (unsigned int j = 0; j < elems[i].size(); ++j)
187 24 : _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 24 : for (unsigned int j = 0; j < secondary_nodelist.size(); ++j)
194 : {
195 12 : if (_mesh.nodeRef(secondary_nodelist[j]).processor_id() == _subproblem.processor_id())
196 : {
197 8 : Node & secondary_node = _mesh.nodeRef(secondary_nodelist[j]);
198 16 : for (unsigned int i = 0; i < _primary_node_vector.size(); ++i)
199 : {
200 16 : Node & primary_node = _mesh.nodeRef(_primary_node_vector[i]);
201 16 : Real d = (secondary_node - primary_node).norm();
202 16 : if (MooseUtils::absoluteFuzzyEqual(d, 0.0))
203 : {
204 8 : _primary_conn.push_back(i);
205 8 : _connected_nodes.push_back(secondary_nodelist[j]);
206 : break;
207 : }
208 : }
209 : }
210 : }
211 :
212 12 : _console << "total secondary nodes, primary nodes: " << _primary_conn.size() << ", "
213 12 : << _primary_node_vector.size() << '\n';
214 24 : }
215 :
216 : void
217 622 : NodalFrictionalConstraint::computeResidual(NumericVector<Number> &
218 : /*residual*/)
219 : {
220 622 : const auto & primarydof = _var.dofIndices();
221 : const auto & secondarydof = _var.dofIndicesNeighbor();
222 622 : std::vector<Number> re(primarydof.size());
223 622 : std::vector<Number> neighbor_re(secondarydof.size());
224 :
225 622 : for (_i = 0; _i < secondarydof.size(); ++_i)
226 : {
227 622 : _j = _primary_conn[_i];
228 622 : re[_j] += computeQpResidual(Moose::Primary);
229 622 : neighbor_re[_i] += computeQpResidual(Moose::Secondary);
230 622 : break;
231 : }
232 622 : addResiduals(_assembly, re, primarydof, _var.scalingFactor());
233 622 : addResiduals(_assembly, neighbor_re, secondarydof, _var.scalingFactor());
234 622 : }
235 :
236 : Real
237 1244 : NodalFrictionalConstraint::computeQpResidual(Moose::ConstraintType type)
238 : {
239 : // check whether the tangential spring is already in the yielded state
240 1244 : Real old_force = (_u_secondary_old[_i] - _u_primary_old[_j]) * _tangential_penalty;
241 1244 : if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(old_force),
242 1244 : _friction_coefficient * _normal_force))
243 476 : old_force = _friction_coefficient * _normal_force * old_force / std::abs(old_force);
244 :
245 : Real current_force =
246 1244 : ((_u_secondary[_i] - _u_secondary_old[_i]) - (_u_primary[_j] - _u_primary_old[_j])) *
247 : _tangential_penalty +
248 1244 : old_force;
249 1244 : if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(current_force),
250 : _friction_coefficient * _normal_force))
251 908 : current_force = _friction_coefficient * _normal_force * current_force / std::abs(current_force);
252 :
253 1244 : switch (type)
254 : {
255 : case Moose::Secondary:
256 : return current_force;
257 622 : case Moose::Primary:
258 622 : return -current_force;
259 : }
260 0 : return 0;
261 : }
262 :
263 : void
264 122 : NodalFrictionalConstraint::computeJacobian(SparseMatrix<Number> & /*jacobian*/)
265 : {
266 : // Calculate Jacobian entries and cache those entries along with the row and column indices
267 122 : std::vector<dof_id_type> secondarydof = _var.dofIndicesNeighbor();
268 122 : std::vector<dof_id_type> primarydof = _var.dofIndices();
269 :
270 122 : DenseMatrix<Number> Kee(primarydof.size(), primarydof.size());
271 122 : DenseMatrix<Number> Ken(primarydof.size(), secondarydof.size());
272 122 : DenseMatrix<Number> Kne(secondarydof.size(), primarydof.size());
273 122 : DenseMatrix<Number> Knn(secondarydof.size(), secondarydof.size());
274 :
275 : Kee.zero();
276 : Ken.zero();
277 : Kne.zero();
278 : Knn.zero();
279 :
280 244 : for (_i = 0; _i < secondarydof.size(); ++_i)
281 : {
282 122 : _j = _primary_conn[_i];
283 122 : Kee(_j, _j) += computeQpJacobian(Moose::PrimaryPrimary);
284 122 : Ken(_j, _i) += computeQpJacobian(Moose::PrimarySecondary);
285 122 : Kne(_i, _j) += computeQpJacobian(Moose::SecondaryPrimary);
286 122 : Knn(_i, _i) += computeQpJacobian(Moose::SecondarySecondary);
287 : }
288 122 : addJacobian(_assembly, Kee, primarydof, primarydof, _var.scalingFactor());
289 122 : addJacobian(_assembly, Ken, primarydof, secondarydof, _var.scalingFactor());
290 122 : addJacobian(_assembly, Kne, secondarydof, primarydof, _var.scalingFactor());
291 122 : addJacobian(_assembly, Knn, secondarydof, secondarydof, _var.scalingFactor());
292 244 : }
293 :
294 : Real
295 488 : NodalFrictionalConstraint::computeQpJacobian(Moose::ConstraintJacobianType type)
296 : {
297 488 : Real jac = _tangential_penalty;
298 :
299 : // set jacobian to zero if spring has yielded
300 488 : Real old_force = (_u_secondary_old[_i] - _u_primary_old[_j]) * _tangential_penalty;
301 488 : if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(old_force),
302 488 : _friction_coefficient * _normal_force))
303 296 : old_force = _friction_coefficient * _normal_force * old_force / std::abs(old_force);
304 :
305 : Real current_force =
306 488 : ((_u_secondary[_i] - _u_secondary_old[_i]) - (_u_primary[_j] - _u_primary_old[_j])) *
307 : _tangential_penalty +
308 488 : old_force;
309 488 : if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(current_force),
310 : _friction_coefficient * _normal_force))
311 : jac = 0.0;
312 :
313 488 : switch (type)
314 : {
315 : case Moose::SecondarySecondary:
316 : return jac;
317 122 : case Moose::SecondaryPrimary:
318 122 : return -jac;
319 : case Moose::PrimaryPrimary:
320 : return jac;
321 122 : case Moose::PrimarySecondary:
322 122 : return -jac;
323 0 : default:
324 0 : mooseError("Invalid type");
325 : }
326 : return 0.;
327 : }
|