www.mooseframework.org
StressDivergenceTensors.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 
12 // MOOSE includes
13 #include "ElasticityTensorTools.h"
14 #include "Material.h"
15 #include "MooseMesh.h"
16 #include "MooseVariable.h"
17 #include "SystemBase.h"
18 
19 #include "libmesh/quadrature.h"
20 
21 registerMooseObject("SolidMechanicsApp", StressDivergenceTensors);
22 
25 {
27  params.addClassDescription("Stress divergence kernel for the Cartesian coordinate system");
28  params.addRequiredRangeCheckedParam<unsigned int>("component",
29  "component < 3",
30  "An integer corresponding to the direction "
31  "the variable this kernel acts in. (0 for x, "
32  "1 for y, 2 for z)");
33  params.addRequiredCoupledVar("displacements",
34  "The string of displacements suitable for the problem statement");
35 
36  // maybe this should be deprecated in favor of args
37  params.addCoupledVar("temperature",
38  "The name of the temperature variable used in the "
39  "ComputeThermalExpansionEigenstrain. (Not required for "
40  "simulations without temperature coupling.)");
41 
42  params.addParam<std::vector<MaterialPropertyName>>(
43  "eigenstrain_names",
44  {},
45  "List of eigenstrains used in the strain calculation. Used for computing their derivatives "
46  "for off-diagonal Jacobian terms.");
47  params.addCoupledVar("out_of_plane_strain",
48  "The name of the out_of_plane_strain variable used in the "
49  "WeakPlaneStress kernel.");
50  MooseEnum out_of_plane_direction("x y z", "z");
51  params.addParam<MooseEnum>(
52  "out_of_plane_direction",
53  out_of_plane_direction,
54  "The direction of the out_of_plane_strain variable used in the WeakPlaneStress kernel.");
55  params.addParam<std::string>("base_name", "Material property base name");
56  params.set<bool>("use_displaced_mesh") = false;
57  params.addParam<bool>(
58  "use_finite_deform_jacobian", false, "Jacobian for corotational finite strain");
59  params.addParam<bool>("volumetric_locking_correction",
60  false,
61  "Set to false to turn off volumetric locking correction");
62  return params;
63 }
64 
66  : JvarMapKernelInterface<ALEKernel>(parameters),
67  _base_name(isParamValid("base_name") ? getParam<std::string>("base_name") + "_" : ""),
68  _use_finite_deform_jacobian(getParam<bool>("use_finite_deform_jacobian")),
69  _stress(getMaterialPropertyByName<RankTwoTensor>(_base_name + "stress")),
70  _Jacobian_mult(getMaterialPropertyByName<RankFourTensor>(_base_name + "Jacobian_mult")),
71  _component(getParam<unsigned int>("component")),
72  _ndisp(coupledComponents("displacements")),
73  _disp_var(_ndisp),
74  _out_of_plane_strain_coupled(isCoupled("out_of_plane_strain")),
75  _out_of_plane_strain(_out_of_plane_strain_coupled ? &coupledValue("out_of_plane_strain")
76  : nullptr),
77  _out_of_plane_strain_var(_out_of_plane_strain_coupled ? coupled("out_of_plane_strain") : 0),
78  _out_of_plane_direction(getParam<MooseEnum>("out_of_plane_direction")),
79  _use_displaced_mesh(getParam<bool>("use_displaced_mesh")),
80  _avg_grad_test(_test.size(), std::vector<Real>(3, 0.0)),
81  _avg_grad_phi(_phi.size(), std::vector<Real>(3, 0.0)),
82  _volumetric_locking_correction(getParam<bool>("volumetric_locking_correction"))
83 {
84  // get coupled displacements
85  for (unsigned int i = 0; i < _ndisp; ++i)
86  _disp_var[i] = coupled("displacements", i);
87 
88  // fetch eigenstrain derivatives
89  const auto nvar = _coupled_moose_vars.size();
90  _deigenstrain_dargs.resize(nvar);
91  for (std::size_t i = 0; i < nvar; ++i)
92  for (auto eigenstrain_name : getParam<std::vector<MaterialPropertyName>>("eigenstrain_names"))
93  _deigenstrain_dargs[i].push_back(&getMaterialPropertyDerivative<RankTwoTensor>(
94  eigenstrain_name, _coupled_moose_vars[i]->name()));
95 
96  // Checking for consistency between mesh size and length of the provided displacements vector
97  if (_out_of_plane_direction != 2 && _ndisp != 3)
98  mooseError("For 2D simulations where the out-of-plane direction is x or y coordinate "
99  "directions the number of supplied displacements must be three.");
100  else if (_out_of_plane_direction == 2 && _ndisp != _mesh.dimension())
101  mooseError("The number of displacement variables supplied must match the mesh dimension");
102 
104  {
106  &getMaterialProperty<RankTwoTensor>(_base_name + "deformation_gradient");
108  &getMaterialPropertyOld<RankTwoTensor>(_base_name + "deformation_gradient");
109  _rotation_increment = &getMaterialProperty<RankTwoTensor>(_base_name + "rotation_increment");
110  }
111 
112  // Error if volumetric locking correction is turned on for 1D problems
114  mooseError("Volumetric locking correction should be set to false for 1-D problems.");
115 
116  // Generate warning when volumetric locking correction is used with second order elements
117  if (_mesh.hasSecondOrderElements() && _volumetric_locking_correction)
118  mooseWarning("Volumteric locking correction is not required for second order elements. Using "
119  "volumetric locking with second order elements could cause zigzag patterns in "
120  "stresses and strains.");
121 }
122 
123 void
125 {
126  // check if any of the eigenstrains provide derivatives wrt variables that are not coupled
127  for (auto eigenstrain_name : getParam<std::vector<MaterialPropertyName>>("eigenstrain_names"))
128  validateNonlinearCoupling<RankTwoTensor>(eigenstrain_name);
129 
130  // make sure the coordinate system is cartesioan
131  if (getBlockCoordSystem() != Moose::COORD_XYZ)
132  mooseError("The coordinate system in the Problem block must be set to XYZ for cartesian "
133  "geometries.");
134 }
135 
136 void
138 {
139  prepareVectorTag(_assembly, _var.number());
140 
143 
144  precalculateResidual();
145  for (_i = 0; _i < _test.size(); ++_i)
146  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
147  _local_re(_i) += _JxW[_qp] * _coord[_qp] * computeQpResidual();
148 
149  accumulateTaggedLocalResidual();
150 
151  if (_has_save_in)
152  {
153  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
154  for (const auto & var : _save_in)
155  var->sys().solution().add_vector(_local_re, var->dofIndices());
156  }
157 }
158 
159 Real
161 {
162  Real residual = _stress[_qp].row(_component) * _grad_test[_i][_qp];
163  // volumetric locking correction
165  residual += _stress[_qp].trace() / 3.0 *
166  (_avg_grad_test[_i][_component] - _grad_test[_i][_qp](_component));
167 
169  {
170  const Real out_of_plane_thickness = std::exp((*_out_of_plane_strain)[_qp]);
171  residual *= out_of_plane_thickness;
172  }
173 
174  return residual;
175 }
176 
177 void
179 {
181  {
184  }
185 
187  {
188  _finite_deform_Jacobian_mult.resize(_qrule->n_points());
189 
190  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
192 
194  }
195  else
197 }
198 
199 void
201 {
203  {
206  }
207 
209  {
210  _finite_deform_Jacobian_mult.resize(_qrule->n_points());
211 
212  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
214 
216  }
217  else
219 }
220 
221 Real
223 {
226  _component,
227  _component,
228  _grad_test[_i][_qp],
229  _grad_phi_undisplaced[_j][_qp]);
230 
231  Real sum_C3x3 = _Jacobian_mult[_qp].sum3x3();
232  RealGradient sum_C3x1 = _Jacobian_mult[_qp].sum3x1();
233 
234  Real jacobian = 0.0;
235  // B^T_i * C * B_j
237  _Jacobian_mult[_qp], _component, _component, _grad_test[_i][_qp], _grad_phi[_j][_qp]);
238 
240  {
241  // jacobian = Bbar^T_i * C * Bbar_j where Bbar = B + Bvol
242  // jacobian = B^T_i * C * B_j + Bvol^T_i * C * Bvol_j + Bvol^T_i * C * B_j + B^T_i * C *
243  // Bvol_j
244 
245  // Bvol^T_i * C * Bvol_j
246  jacobian += sum_C3x3 * (_avg_grad_test[_i][_component] - _grad_test[_i][_qp](_component)) *
247  (_avg_grad_phi[_j][_component] - _grad_phi[_j][_qp](_component)) / 9.0;
248 
249  // B^T_i * C * Bvol_j
250  jacobian += sum_C3x1(_component) * _grad_test[_i][_qp](_component) *
251  (_avg_grad_phi[_j][_component] - _grad_phi[_j][_qp](_component)) / 3.0;
252 
253  // Bvol^T_i * C * B_j
254  RankTwoTensor phi;
255  switch (_component)
256  {
257  case 0:
258  phi(0, 0) = _grad_phi[_j][_qp](0);
259  phi(0, 1) = phi(1, 0) = _grad_phi[_j][_qp](1);
260  phi(0, 2) = phi(2, 0) = _grad_phi[_j][_qp](2);
261  break;
262 
263  case 1:
264  phi(1, 1) = _grad_phi[_j][_qp](1);
265  phi(0, 1) = phi(1, 0) = _grad_phi[_j][_qp](0);
266  phi(1, 2) = phi(2, 1) = _grad_phi[_j][_qp](2);
267  break;
268 
269  case 2:
270  phi(2, 2) = _grad_phi[_j][_qp](2);
271  phi(0, 2) = phi(2, 0) = _grad_phi[_j][_qp](0);
272  phi(1, 2) = phi(2, 1) = _grad_phi[_j][_qp](1);
273  break;
274  }
275 
276  jacobian += (_Jacobian_mult[_qp] * phi).trace() *
277  (_avg_grad_test[_i][_component] - _grad_test[_i][_qp](_component)) / 3.0;
278  }
279 
281  {
282  const Real out_of_plane_thickness = std::exp((*_out_of_plane_strain)[_qp]);
283  jacobian *= out_of_plane_thickness;
284  }
285 
286  return jacobian;
287 }
288 
289 Real
291 {
292  // off-diagonal Jacobian with respect to a coupled displacement component
293  for (unsigned int coupled_component = 0; coupled_component < _ndisp; ++coupled_component)
294  if (jvar == _disp_var[coupled_component])
295  {
296  if (_out_of_plane_direction != 2)
297  {
298  if (coupled_component == _out_of_plane_direction)
299  continue;
300  }
301 
304  _component,
305  coupled_component,
306  _grad_test[_i][_qp],
307  _grad_phi_undisplaced[_j][_qp]);
308 
309  const Real sum_C3x3 = _Jacobian_mult[_qp].sum3x3();
310  const RealGradient sum_C3x1 = _Jacobian_mult[_qp].sum3x1();
311  Real jacobian = 0.0;
312 
313  // B^T_i * C * B_j
315  _component,
316  coupled_component,
317  _grad_test[_i][_qp],
318  _grad_phi[_j][_qp]);
319 
321  {
322  // jacobian = Bbar^T_i * C * Bbar_j where Bbar = B + Bvol
323  // jacobian = B^T_i * C * B_j + Bvol^T_i * C * Bvol_j + Bvol^T_i * C * B_j + B^T_i * C *
324  // Bvol_j
325 
326  // Bvol^T_i * C * Bvol_j
327  jacobian += sum_C3x3 * (_avg_grad_test[_i][_component] - _grad_test[_i][_qp](_component)) *
328  (_avg_grad_phi[_j][coupled_component] - _grad_phi[_j][_qp](coupled_component)) /
329  9.0;
330 
331  // B^T_i * C * Bvol_j
332  jacobian += sum_C3x1(_component) * _grad_test[_i][_qp](_component) *
333  (_avg_grad_phi[_j][coupled_component] - _grad_phi[_j][_qp](coupled_component)) /
334  3.0;
335 
336  // Bvol^T_i * C * B_i
337  RankTwoTensor phi;
338  for (unsigned int i = 0; i < 3; ++i)
339  phi(coupled_component, i) = _grad_phi[_j][_qp](i);
340 
341  jacobian += (_Jacobian_mult[_qp] * phi).trace() *
342  (_avg_grad_test[_i][_component] - _grad_test[_i][_qp](_component)) / 3.0;
343  }
344 
345  return jacobian;
346  }
347 
348  // off-diagonal Jacobian with respect to a coupled out_of_plane_strain variable
350  return _Jacobian_mult[_qp](
352  _grad_test[_i][_qp](_component) * _phi[_j][_qp];
353 
354  // bail out if jvar is not coupled
355  if (getJvarMap()[jvar] < 0)
356  return 0.0;
357 
358  // off-diagonal Jacobian with respect to any other coupled variable
359  const unsigned int cvar = mapJvarToCvar(jvar);
360  RankTwoTensor total_deigenstrain;
361  for (const auto deigenstrain_darg : _deigenstrain_dargs[cvar])
362  total_deigenstrain += (*deigenstrain_darg)[_qp];
363 
364  return -((_Jacobian_mult[_qp] * total_deigenstrain) *
365  _grad_test[_i][_qp])(_component)*_phi[_j][_qp];
366 }
367 
368 void
370 {
371  usingTensorIndices(i_, j_, k_, l_);
372  const auto I = RankTwoTensor::Identity();
373  const RankFourTensor I2 = I.times<i_, k_, j_, l_>(I);
374 
375  // Bring back to unrotated config
376  const RankTwoTensor unrotated_stress =
377  (*_rotation_increment)[_qp].transpose() * _stress[_qp] * (*_rotation_increment)[_qp];
378 
379  // Incremental deformation gradient Fhat
380  const RankTwoTensor Fhat =
381  (*_deformation_gradient)[_qp] * (*_deformation_gradient_old)[_qp].inverse();
382  const RankTwoTensor Fhatinv = Fhat.inverse();
383 
384  const RankTwoTensor rot_times_stress = (*_rotation_increment)[_qp] * unrotated_stress;
385  const RankFourTensor dstress_drot =
386  I.times<i_, k_, j_, l_>(rot_times_stress) + I.times<j_, k_, i_, l_>(rot_times_stress);
387  const RankFourTensor rot_rank_four =
388  (*_rotation_increment)[_qp].times<i_, k_, j_, l_>((*_rotation_increment)[_qp]);
389  const RankFourTensor drot_dUhatinv = Fhat.times<i_, k_, j_, l_>(I);
390 
391  const RankTwoTensor A = I - Fhatinv;
392 
393  // Ctilde = Chat^-1 - I
394  const RankTwoTensor Ctilde = A * A.transpose() - A - A.transpose();
395  const RankFourTensor dCtilde_dFhatinv =
396  -I.times<i_, k_, j_, l_>(A) - I.times<j_, k_, i_, l_>(A) + I2 + I.times<j_, k_, i_, l_>(I);
397 
398  // Second order approximation of Uhat - consistent with strain increment definition
399  // const RankTwoTensor Uhat = I - 0.5 * Ctilde - 3.0/8.0 * Ctilde * Ctilde;
400 
401  RankFourTensor dUhatinv_dCtilde =
402  0.5 * I2 - 1.0 / 8.0 * (I.times<i_, k_, j_, l_>(Ctilde) + Ctilde.times<i_, k_, j_, l_>(I));
403  RankFourTensor drot_dFhatinv = drot_dUhatinv * dUhatinv_dCtilde * dCtilde_dFhatinv;
404 
405  drot_dFhatinv -= Fhat.times<i_, k_, j_, l_>((*_rotation_increment)[_qp].transpose());
406  _finite_deform_Jacobian_mult[_qp] = dstress_drot * drot_dFhatinv;
407 
408  const RankFourTensor dstrain_increment_dCtilde =
409  -0.5 * I2 + 0.25 * (I.times<i_, k_, j_, l_>(Ctilde) + Ctilde.times<i_, k_, j_, l_>(I));
411  rot_rank_four * _Jacobian_mult[_qp] * dstrain_increment_dCtilde * dCtilde_dFhatinv;
412  _finite_deform_Jacobian_mult[_qp] += Fhat.times<j_, k_, i_, l_>(_stress[_qp]);
413 
414  const RankFourTensor dFhat_dFhatinv = -Fhat.times<i_, k_, j_, l_>(Fhat.transpose());
415  const RankTwoTensor dJ_dFhatinv = dFhat_dFhatinv.innerProductTranspose(Fhat.ddet());
416 
417  // Component from Jacobian derivative
418  _finite_deform_Jacobian_mult[_qp] += _stress[_qp].times<i_, j_, k_, l_>(dJ_dFhatinv);
419 
420  // Derivative of Fhatinv w.r.t. undisplaced coordinates
421  const RankTwoTensor Finv = (*_deformation_gradient)[_qp].inverse();
422  const RankFourTensor dFhatinv_dGradu = -Fhatinv.times<i_, k_, j_, l_>(Finv.transpose());
423  _finite_deform_Jacobian_mult[_qp] = _finite_deform_Jacobian_mult[_qp] * dFhatinv_dGradu;
424 }
425 
426 void
428 {
429  // Calculate volume averaged value of shape function derivative
430  _avg_grad_test.resize(_test.size());
431  for (_i = 0; _i < _test.size(); ++_i)
432  {
433  _avg_grad_test[_i].resize(3);
434  _avg_grad_test[_i][_component] = 0.0;
435  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
436  _avg_grad_test[_i][_component] += _grad_test[_i][_qp](_component) * _JxW[_qp] * _coord[_qp];
437 
438  _avg_grad_test[_i][_component] /= _current_elem_volume;
439  }
440 }
441 
442 void
444 {
445  // Calculate volume average derivatives for phi
446  _avg_grad_phi.resize(_phi.size());
447  for (_i = 0; _i < _phi.size(); ++_i)
448  {
449  _avg_grad_phi[_i].resize(3);
450  for (unsigned int component = 0; component < _mesh.dimension(); ++component)
451  {
452  _avg_grad_phi[_i][component] = 0.0;
453  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
454  _avg_grad_phi[_i][component] += _grad_phi[_i][_qp](component) * _JxW[_qp] * _coord[_qp];
455 
456  _avg_grad_phi[_i][component] /= _current_elem_volume;
457  }
458  }
459 }
const std::string _base_name
Base name of the material system that this kernel applies to.
std::vector< std::vector< Real > > _avg_grad_phi
Gradient of phi function averaged over the element. Used in volumetric locking correction calculation...
RankTwoTensorTempl< Real > inverse() const
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
const VariableValue *const _out_of_plane_strain
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
bool _volumetric_locking_correction
Flag for volumetric locking correction.
void mooseError(Args &&... args)
unsigned int _ndisp
Coupled displacement variables.
std::vector< std::vector< Real > > _avg_grad_test
Gradient of test function averaged over the element. Used in volumetric locking correction calculatio...
static const std::string component
Definition: NS.h:138
const unsigned int _out_of_plane_strain_var
void mooseWarning(Args &&... args)
T & set(const std::string &name, bool quiet_mode=false)
virtual void computeOffDiagJacobian(unsigned int jvar) override
const unsigned int _component
An integer corresponding to the direction this kernel acts in.
const MaterialProperty< RankTwoTensor > * _deformation_gradient_old
virtual void computeJacobian() override
const unsigned int _out_of_plane_direction
static RankTwoTensorTempl Identity()
virtual Real computeQpJacobian() override
const MaterialProperty< RankTwoTensor > * _rotation_increment
Real elasticJacobian(const RankFourTensor &r4t, unsigned int i, unsigned int k, const RealGradient &grad_test, const RealGradient &grad_phi)
This is used for the standard kernel stress_ij*d(test)/dx_j, when varied wrt u_k Jacobian entry: d(st...
std::vector< unsigned int > _disp_var
Displacement variables IDs.
std::vector< RankFourTensor > _finite_deform_Jacobian_mult
StressDivergenceTensors(const InputParameters &parameters)
const std::string name
Definition: Setup.h:20
static InputParameters validParams()
virtual Real computeQpOffDiagJacobian(unsigned int jvar) override
std::vector< std::vector< const MaterialProperty< RankTwoTensor > * > > _deigenstrain_dargs
eigen strain derivatives wrt coupled variables
registerMooseObject("SolidMechanicsApp", StressDivergenceTensors)
virtual void initialSetup() override
const bool _use_displaced_mesh
Whether this object is acting on the displaced mesh.
unsigned int mapJvarToCvar(unsigned int jvar)
const MaterialProperty< RankTwoTensor > * _deformation_gradient
const MaterialProperty< RankFourTensor > & _Jacobian_mult
void addCoupledVar(const std::string &name, const std::string &doc_string)
virtual void computeOffDiagJacobian(unsigned int jvar) override
Definition: ALEKernel.C:41
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
virtual void computeJacobian() override
RankTwoTensorTempl< Real > transpose() const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void computeFiniteDeformJacobian()
RankFourTensorTempl< Real > times(const RankTwoTensorTempl< Real > &b) const
virtual void computeResidual() override
static InputParameters validParams()
virtual Real computeQpResidual() override
RankTwoTensorTempl< Real > ddet() const
void addClassDescription(const std::string &doc_string)
virtual void computeOffDiagJacobian(unsigned int jvar) override
void ErrorVector unsigned int
const MaterialProperty< RankTwoTensor > & _stress
The stress tensor that the divergence operator operates on.
virtual void computeJacobian() override
Definition: ALEKernel.C:34
StressDivergenceTensors mostly copies from StressDivergence.