https://mooseframework.inl.gov
StressDivergenceRZTensors.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 
11 #include "Assembly.h"
12 #include "ElasticityTensorTools.h"
13 #include "libmesh/quadrature.h"
14 
16 
19 {
21  params.addClassDescription(
22  "Calculate stress divergence for an axisymmetric problem in cylindrical coordinates.");
23  params.addRequiredRangeCheckedParam<unsigned int>(
24  "component",
25  "component < 2",
26  "An integer corresponding to the direction the variable this kernel acts in. (0 "
27  "refers to the radial and 1 to the axial displacement.)");
28  params.set<bool>("use_displaced_mesh") = true;
29  return params;
30 }
31 
33  : StressDivergenceTensors(parameters)
34 {
35 }
36 
37 void
39 {
40  // check if any of the eigenstrains provide derivatives wrt variables that are not coupled
41  for (auto eigenstrain_name : getParam<std::vector<MaterialPropertyName>>("eigenstrain_names"))
42  validateNonlinearCoupling<RankTwoTensor>(eigenstrain_name);
43 
44  if (getBlockCoordSystem() != Moose::COORD_RZ)
45  mooseError("The coordinate system in the Problem block must be set to RZ for axisymmetric "
46  "geometries.");
47 
48  if (getBlockCoordSystem() == Moose::COORD_RZ && _fe_problem.getAxisymmetricRadialCoord() != 0)
49  mooseError("rz_coord_axis=Y is the only supported option for StressDivergenceRZTensors");
50 }
51 
52 Real
54 {
55  Real div = 0.0;
56  if (_component == 0)
57  {
58  div = _grad_test[_i][_qp](0) * _stress[_qp](0, 0) +
59  +(_test[_i][_qp] / _q_point[_qp](0)) * _stress[_qp](2, 2) +
60  +_grad_test[_i][_qp](1) * _stress[_qp](0, 1); // stress_{rz}
61 
62  // volumetric locking correction
64  div += (_avg_grad_test[_i][0] - _grad_test[_i][_qp](0) - _test[_i][_qp] / _q_point[_qp](0)) *
65  (_stress[_qp].trace()) / 3.0;
66  }
67  else if (_component == 1)
68  {
69  div = _grad_test[_i][_qp](1) * _stress[_qp](1, 1) +
70  +_grad_test[_i][_qp](0) * _stress[_qp](1, 0); // stress_{zr}
71 
72  // volumetric locking correction
74  div += (_avg_grad_test[_i][1] - _grad_test[_i][_qp](1)) * (_stress[_qp].trace()) / 3.0;
75  }
76  else
77  mooseError("Invalid component for this AxisymmetricRZ problem.");
78 
79  return div;
80 }
81 
82 Real
84 {
86 }
87 
88 Real
90 {
91  for (unsigned int i = 0; i < _ndisp; ++i)
92  if (jvar == _disp_var[i])
93  return calculateJacobian(_component, i);
94 
95  // bail out if jvar is not coupled
96  if (getJvarMap()[jvar] < 0)
97  return 0.0;
98 
99  // off-diagonal Jacobian with respect to any other coupled variable
100  const unsigned int cvar = mapJvarToCvar(jvar);
101  RankTwoTensor total_deigenstrain;
102  for (const auto deigenstrain_darg : _deigenstrain_dargs[cvar])
103  total_deigenstrain += (*deigenstrain_darg)[_qp];
104 
105  Real jac = 0.0;
106  if (_component == 0)
107  {
108  for (unsigned k = 0; k < LIBMESH_DIM; ++k)
109  for (unsigned l = 0; l < LIBMESH_DIM; ++l)
110  jac -= (_grad_test[_i][_qp](0) * _Jacobian_mult[_qp](0, 0, k, l) +
111  _test[_i][_qp] / _q_point[_qp](0) * _Jacobian_mult[_qp](2, 2, k, l) +
112  _grad_test[_i][_qp](1) * _Jacobian_mult[_qp](0, 1, k, l)) *
113  total_deigenstrain(k, l);
114  return jac * _phi[_j][_qp];
115  }
116  else if (_component == 1)
117  {
118  for (unsigned k = 0; k < LIBMESH_DIM; ++k)
119  for (unsigned l = 0; l < LIBMESH_DIM; ++l)
120  jac -= (_grad_test[_i][_qp](1) * _Jacobian_mult[_qp](1, 1, k, l) +
121  _grad_test[_i][_qp](0) * _Jacobian_mult[_qp](1, 0, k, l)) *
122  total_deigenstrain(k, l);
123  return jac * _phi[_j][_qp];
124  }
125 
126  return 0.0;
127 }
128 
129 Real
130 StressDivergenceRZTensors::calculateJacobian(unsigned int ivar, unsigned int jvar)
131 {
132  // B^T_i * C * B_j
133  RealGradient test, test_z, phi, phi_z;
134  Real first_term = 0.0;
135  if (ivar == 0) // Case grad_test for x, requires contributions from stress_xx, stress_xy, and
136  // stress_zz
137  {
138  test(0) = _grad_test[_i][_qp](0);
139  test(1) = _grad_test[_i][_qp](1);
140  test_z(2) = _test[_i][_qp] / _q_point[_qp](0);
141  }
142  else // Case grad_test for y
143  {
144  test(0) = _grad_test[_i][_qp](0);
145  test(1) = _grad_test[_i][_qp](1);
146  }
147 
148  if (jvar == 0)
149  {
150  phi(0) = _grad_phi[_j][_qp](0);
151  phi(1) = _grad_phi[_j][_qp](1);
152  phi_z(2) = _phi[_j][_qp] / _q_point[_qp](0);
153  }
154  else
155  {
156  phi(0) = _grad_phi[_j][_qp](0);
157  phi(1) = _grad_phi[_j][_qp](1);
158  }
159 
160  if (ivar == 0 &&
161  jvar == 0) // Case when both phi and test are functions of x and z; requires four terms
162  {
164  _Jacobian_mult[_qp], ivar, jvar, test, phi); // test_x and phi_x
165  const Real second_sum = ElasticityTensorTools::elasticJacobian(
166  _Jacobian_mult[_qp], 2, 2, test_z, phi_z); // test_z and phi_z
167  const Real mixed_sum1 = ElasticityTensorTools::elasticJacobian(
168  _Jacobian_mult[_qp], ivar, 2, test, phi_z); // test_x and phi_z
169  const Real mixed_sum2 = ElasticityTensorTools::elasticJacobian(
170  _Jacobian_mult[_qp], 2, jvar, test_z, phi); // test_z and phi_x
171 
172  first_term = first_sum + second_sum + mixed_sum1 + mixed_sum2;
173  }
174  else if (ivar == 0 && jvar == 1)
175  {
177  _Jacobian_mult[_qp], ivar, jvar, test, phi); // test_x and phi_y
178  const Real mixed_sum2 = ElasticityTensorTools::elasticJacobian(
179  _Jacobian_mult[_qp], 2, jvar, test_z, phi); // test_z and phi_y
180 
181  first_term = first_sum + mixed_sum2;
182  }
183  else if (ivar == 1 && jvar == 0)
184  {
185  const Real second_sum = ElasticityTensorTools::elasticJacobian(
186  _Jacobian_mult[_qp], ivar, jvar, test, phi); // test_y and phi_x
187  const Real mixed_sum1 = ElasticityTensorTools::elasticJacobian(
188  _Jacobian_mult[_qp], ivar, 2, test, phi_z); // test_y and phi_z
189 
190  first_term = second_sum + mixed_sum1;
191  }
192  else if (ivar == 1 && jvar == 1)
194  _Jacobian_mult[_qp], ivar, jvar, test, phi); // test_y and phi_y
195  else
196  mooseError("Invalid component in Jacobian Calculation");
197 
198  Real val = 0.0;
199  // volumetric locking correction
200  // K = Bbar^T_i * C * Bbar^T_j where Bbar = B + Bvol
201  // K = B^T_i * C * B_j + Bvol^T_i * C * Bvol_j + B^T_i * C * Bvol_j + Bvol^T_i * C * B_j
203  {
204  RealGradient new_test(2, 0.0);
205  RealGradient new_phi(2, 0.0);
206 
207  new_test(0) = _grad_test[_i][_qp](0) + _test[_i][_qp] / _q_point[_qp](0);
208  new_test(1) = _grad_test[_i][_qp](1);
209  new_phi(0) = _grad_phi[_j][_qp](0) + _phi[_j][_qp] / _q_point[_qp](0);
210  new_phi(1) = _grad_phi[_j][_qp](1);
211 
212  // Bvol^T_i * C * Bvol_j
213  val += _Jacobian_mult[_qp].sum3x3() * (_avg_grad_test[_i][ivar] - new_test(ivar)) *
214  (_avg_grad_phi[_j][jvar] - new_phi(jvar)) / 3.0;
215 
216  // B^T_i * C * Bvol_j
217  RealGradient sum_3x1 = _Jacobian_mult[_qp].sum3x1();
218  if (ivar == 0 && jvar == 0)
219  val += (sum_3x1(0) * test(0) + sum_3x1(2) * test_z(2)) * (_avg_grad_phi[_j][0] - new_phi(0));
220  else if (ivar == 0 && jvar == 1)
221  val += (sum_3x1(0) * test(0) + sum_3x1(2) * test_z(2)) * (_avg_grad_phi[_j][1] - new_phi(1));
222  else if (ivar == 1 && jvar == 0)
223  val += sum_3x1(1) * test(1) * (_avg_grad_phi[_j][0] - new_phi(0));
224  else
225  val += sum_3x1(1) * test(1) * (_avg_grad_phi[_j][1] - new_phi(1));
226 
227  // Bvol^T_i * C * B_j
228  // val = trace (C * B_j) *(avg_grad_test[_i][ivar] - new_test(ivar))
229  if (jvar == 0)
230  for (unsigned int i = 0; i < 3; ++i)
231  val +=
232  (_Jacobian_mult[_qp](i, i, 0, 0) * phi(0) + _Jacobian_mult[_qp](i, i, 0, 1) * phi(1) +
233  _Jacobian_mult[_qp](i, i, 2, 2) * phi_z(2)) *
234  (_avg_grad_test[_i][ivar] - new_test(ivar));
235  else if (jvar == 1)
236  for (unsigned int i = 0; i < 3; ++i)
237  val +=
238  (_Jacobian_mult[_qp](i, i, 0, 1) * phi(0) + _Jacobian_mult[_qp](i, i, 1, 1) * phi(1)) *
239  (_avg_grad_test[_i][ivar] - new_test(ivar));
240  }
241 
242  return val / 3.0 + first_term;
243 }
244 
245 void
247 {
248  // calculate volume averaged value of shape function derivative
249  _avg_grad_test.resize(_test.size());
250  for (_i = 0; _i < _test.size(); ++_i)
251  {
252  _avg_grad_test[_i].resize(2);
253  _avg_grad_test[_i][_component] = 0.0;
254  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
255  {
256  if (_component == 0)
257  _avg_grad_test[_i][_component] +=
258  (_grad_test[_i][_qp](_component) + _test[_i][_qp] / _q_point[_qp](0)) * _JxW[_qp] *
259  _coord[_qp];
260  else
261  _avg_grad_test[_i][_component] += _grad_test[_i][_qp](_component) * _JxW[_qp] * _coord[_qp];
262  }
263  _avg_grad_test[_i][_component] /= _current_elem_volume;
264  }
265 }
266 
267 void
269 {
270  _avg_grad_phi.resize(_phi.size());
271  for (_i = 0; _i < _phi.size(); ++_i)
272  {
273  _avg_grad_phi[_i].resize(2);
274  for (unsigned int component = 0; component < 2; ++component)
275  {
276  _avg_grad_phi[_i][component] = 0.0;
277  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
278  {
279  if (component == 0)
280  _avg_grad_phi[_i][component] +=
281  (_grad_phi[_i][_qp](component) + _phi[_i][_qp] / _q_point[_qp](0)) * _JxW[_qp] *
282  _coord[_qp];
283  else
284  _avg_grad_phi[_i][component] += _grad_phi[_i][_qp](component) * _JxW[_qp] * _coord[_qp];
285  }
286  _avg_grad_phi[_i][component] /= _current_elem_volume;
287  }
288  }
289 }
std::vector< std::vector< Real > > _avg_grad_phi
Gradient of phi function averaged over the element. Used in volumetric locking correction calculation...
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, 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...
registerMooseObject("SolidMechanicsApp", StressDivergenceRZTensors)
static const std::string component
Definition: NS.h:153
virtual void initialSetup() override
T & set(const std::string &name, bool quiet_mode=false)
const unsigned int _component
An integer corresponding to the direction this kernel acts in.
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...
StressDivergenceRZTensors(const InputParameters &parameters)
std::vector< unsigned int > _disp_var
Displacement variables IDs.
static InputParameters validParams()
StressDivergenceRZTensors is a modification of StressDivergenceTensors to accommodate the Axisymmetri...
virtual Real computeQpResidual() override
std::vector< std::vector< const MaterialProperty< RankTwoTensor > * > > _deigenstrain_dargs
eigen strain derivatives wrt coupled variables
virtual Real computeQpJacobian() override
unsigned int mapJvarToCvar(unsigned int jvar)
const MaterialProperty< RankFourTensor > & _Jacobian_mult
Real calculateJacobian(unsigned int ivar, unsigned int jvar)
virtual void computeAverageGradientTest() override
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void computeAverageGradientPhi() override
static InputParameters validParams()
void addClassDescription(const std::string &doc_string)
virtual Real computeQpOffDiagJacobian(unsigned int jvar) override
static const std::string k
Definition: NS.h:130
const MaterialProperty< RankTwoTensor > & _stress
The stress tensor that the divergence operator operates on.
StressDivergenceTensors mostly copies from StressDivergence.