www.mooseframework.org
TensorMechanicsPlasticTensileMulti.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 #include "RankFourTensor.h"
12 
13 // Following is for perturbing eigvenvalues. This looks really bodgy, but works quite well!
14 #include "MooseRandom.h"
15 
17 
19 
20 InputParameters
22 {
23  InputParameters params = TensorMechanicsPlasticModel::validParams();
24  params.addClassDescription("Associative tensile plasticity with hardening/softening");
25  params.addRequiredParam<UserObjectName>(
26  "tensile_strength",
27  "A TensorMechanicsHardening UserObject that defines hardening of the tensile strength");
28  params.addParam<Real>("shift",
29  "Yield surface is shifted by this amount to avoid problems with "
30  "defining derivatives when eigenvalues are equal. If this is "
31  "larger than f_tol, a warning will be issued. Default = f_tol.");
32  params.addParam<unsigned int>("max_iterations",
33  10,
34  "Maximum number of Newton-Raphson iterations "
35  "allowed in the custom return-map algorithm. "
36  " For highly nonlinear hardening this may "
37  "need to be higher than 10.");
38  params.addParam<bool>("use_custom_returnMap",
39  true,
40  "Whether to use the custom returnMap "
41  "algorithm. Set to true if you are using "
42  "isotropic elasticity.");
43  params.addParam<bool>("use_custom_cto",
44  true,
45  "Whether to use the custom consistent tangent "
46  "operator computations. Set to true if you are "
47  "using isotropic elasticity.");
48  return params;
49 }
50 
52  const InputParameters & parameters)
53  : TensorMechanicsPlasticModel(parameters),
54  _strength(getUserObject<TensorMechanicsHardeningModel>("tensile_strength")),
55  _max_iters(getParam<unsigned int>("max_iterations")),
56  _shift(parameters.isParamValid("shift") ? getParam<Real>("shift") : _f_tol),
57  _use_custom_returnMap(getParam<bool>("use_custom_returnMap")),
58  _use_custom_cto(getParam<bool>("use_custom_cto"))
59 {
60  if (_shift < 0)
61  mooseError("Value of 'shift' in TensorMechanicsPlasticTensileMulti must not be negative\n");
62  if (_shift > _f_tol)
63  _console << "WARNING: value of 'shift' in TensorMechanicsPlasticTensileMulti is probably set "
64  "too high\n";
65  if (LIBMESH_DIM != 3)
66  mooseError("TensorMechanicsPlasticTensileMulti is only defined for LIBMESH_DIM=3");
67  MooseRandom::seed(0);
68 }
69 
70 unsigned int
72 {
73  return 3;
74 }
75 
76 void
78  Real intnl,
79  std::vector<Real> & f) const
80 {
81  std::vector<Real> eigvals;
82  stress.symmetricEigenvalues(eigvals);
83  const Real str = tensile_strength(intnl);
84 
85  f.resize(3);
86  f[0] = eigvals[0] + _shift - str;
87  f[1] = eigvals[1] - str;
88  f[2] = eigvals[2] - _shift - str;
89 }
90 
91 void
93  const RankTwoTensor & stress, Real /*intnl*/, std::vector<RankTwoTensor> & df_dstress) const
94 {
95  std::vector<Real> eigvals;
96  stress.dsymmetricEigenvalues(eigvals, df_dstress);
97 
98  if (eigvals[0] > eigvals[1] - 0.1 * _shift || eigvals[1] > eigvals[2] - 0.1 * _shift)
99  {
100  Real small_perturbation;
101  RankTwoTensor shifted_stress = stress;
102  while (eigvals[0] > eigvals[1] - 0.1 * _shift || eigvals[1] > eigvals[2] - 0.1 * _shift)
103  {
104  for (unsigned i = 0; i < 3; ++i)
105  for (unsigned j = 0; j <= i; ++j)
106  {
107  small_perturbation = 0.1 * _shift * 2 * (MooseRandom::rand() - 0.5);
108  shifted_stress(i, j) += small_perturbation;
109  shifted_stress(j, i) += small_perturbation;
110  }
111  shifted_stress.dsymmetricEigenvalues(eigvals, df_dstress);
112  }
113  }
114 }
115 
116 void
118  Real intnl,
119  std::vector<Real> & df_dintnl) const
120 {
121  df_dintnl.assign(3, -dtensile_strength(intnl));
122 }
123 
124 void
126  Real intnl,
127  std::vector<RankTwoTensor> & r) const
128 {
129  // This plasticity is associative so
130  dyieldFunction_dstressV(stress, intnl, r);
131 }
132 
133 void
135  const RankTwoTensor & stress, Real /*intnl*/, std::vector<RankFourTensor> & dr_dstress) const
136 {
137  stress.d2symmetricEigenvalues(dr_dstress);
138 }
139 
140 void
142  const RankTwoTensor & /*stress*/, Real /*intnl*/, std::vector<RankTwoTensor> & dr_dintnl) const
143 {
144  dr_dintnl.assign(3, RankTwoTensor());
145 }
146 
147 Real
149 {
150  return _strength.value(internal_param);
151 }
152 
153 Real
155 {
156  return _strength.derivative(internal_param);
157 }
158 
159 void
161  const RankTwoTensor & stress,
162  Real intnl,
163  const RankFourTensor & Eijkl,
164  std::vector<bool> & act,
165  RankTwoTensor & returned_stress) const
166 {
167  act.assign(3, false);
168 
169  if (f[0] <= _f_tol && f[1] <= _f_tol && f[2] <= _f_tol)
170  {
171  returned_stress = stress;
172  return;
173  }
174 
175  Real returned_intnl;
176  std::vector<Real> dpm(3);
177  RankTwoTensor delta_dp;
178  std::vector<Real> yf(3);
179  bool trial_stress_inadmissible;
180  doReturnMap(stress,
181  intnl,
182  Eijkl,
183  0.0,
184  returned_stress,
185  returned_intnl,
186  dpm,
187  delta_dp,
188  yf,
189  trial_stress_inadmissible);
190 
191  for (unsigned i = 0; i < 3; ++i)
192  act[i] = (dpm[i] > 0);
193 }
194 
195 Real
196 TensorMechanicsPlasticTensileMulti::dot(const std::vector<Real> & a,
197  const std::vector<Real> & b) const
198 {
199  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
200 }
201 
202 Real
203 TensorMechanicsPlasticTensileMulti::triple(const std::vector<Real> & a,
204  const std::vector<Real> & b,
205  const std::vector<Real> & c) const
206 {
207  return a[0] * (b[1] * c[2] - b[2] * c[1]) - a[1] * (b[0] * c[2] - b[2] * c[0]) +
208  a[2] * (b[0] * c[1] - b[1] * c[0]);
209 }
210 
211 std::string
213 {
214  return "TensileMulti";
215 }
216 
217 bool
219  Real intnl_old,
220  const RankFourTensor & E_ijkl,
221  Real ep_plastic_tolerance,
222  RankTwoTensor & returned_stress,
223  Real & returned_intnl,
224  std::vector<Real> & dpm,
225  RankTwoTensor & delta_dp,
226  std::vector<Real> & yf,
227  bool & trial_stress_inadmissible) const
228 {
230  return TensorMechanicsPlasticModel::returnMap(trial_stress,
231  intnl_old,
232  E_ijkl,
233  ep_plastic_tolerance,
234  returned_stress,
235  returned_intnl,
236  dpm,
237  delta_dp,
238  yf,
239  trial_stress_inadmissible);
240 
241  return doReturnMap(trial_stress,
242  intnl_old,
243  E_ijkl,
244  ep_plastic_tolerance,
245  returned_stress,
246  returned_intnl,
247  dpm,
248  delta_dp,
249  yf,
250  trial_stress_inadmissible);
251 }
252 
253 bool
255  Real intnl_old,
256  const RankFourTensor & E_ijkl,
257  Real ep_plastic_tolerance,
258  RankTwoTensor & returned_stress,
259  Real & returned_intnl,
260  std::vector<Real> & dpm,
261  RankTwoTensor & delta_dp,
262  std::vector<Real> & yf,
263  bool & trial_stress_inadmissible) const
264 {
265  mooseAssert(dpm.size() == 3,
266  "TensorMechanicsPlasticTensileMulti size of dpm should be 3 but it is "
267  << dpm.size());
268 
269  std::vector<Real> eigvals;
270  RankTwoTensor eigvecs;
271  trial_stress.symmetricEigenvaluesEigenvectors(eigvals, eigvecs);
272  eigvals[0] += _shift;
273  eigvals[2] -= _shift;
274 
275  Real str = tensile_strength(intnl_old);
276 
277  yf.resize(3);
278  yf[0] = eigvals[0] - str;
279  yf[1] = eigvals[1] - str;
280  yf[2] = eigvals[2] - str;
281 
282  if (yf[0] <= _f_tol && yf[1] <= _f_tol && yf[2] <= _f_tol)
283  {
284  // purely elastic (trial_stress, intnl_old)
285  trial_stress_inadmissible = false;
286  return true;
287  }
288 
289  trial_stress_inadmissible = true;
290  delta_dp.zero();
291  returned_stress.zero();
292 
293  // In the following i often assume that E_ijkl is
294  // for an isotropic situation. This reduces FLOPS
295  // substantially which is important since the returnMap
296  // is potentially the most compute-intensive function
297  // of a simulation.
298  // In many comments i write the general expression, and
299  // i hope that might guide future coders if they are
300  // generalising to a non-istropic E_ijkl
301 
302  // n[alpha] = E_ijkl*r[alpha]_kl expressed in principal stress space
303  // (alpha = 0, 1, 2, corresponding to the three surfaces)
304  // Note that in principal stress space, the flow
305  // directions are, expressed in 'vector' form,
306  // r[0] = (1,0,0), r[1] = (0,1,0), r[2] = (0,0,1).
307  // Similar for _n:
308  // so _n[0] = E_ij00*r[0], _n[1] = E_ij11*r[1], _n[2] = E_ij22*r[2]
309  // In the following I assume that the E_ijkl is
310  // for an isotropic situation.
311  // In the anisotropic situation, we couldn't express
312  // the flow directions as vectors in the same principal
313  // stress space as the stress: they'd be full rank-2 tensors
314  std::vector<RealVectorValue> n(3);
315  n[0](0) = E_ijkl(0, 0, 0, 0);
316  n[0](1) = E_ijkl(1, 1, 0, 0);
317  n[0](2) = E_ijkl(2, 2, 0, 0);
318  n[1](0) = E_ijkl(0, 0, 1, 1);
319  n[1](1) = E_ijkl(1, 1, 1, 1);
320  n[1](2) = E_ijkl(2, 2, 1, 1);
321  n[2](0) = E_ijkl(0, 0, 2, 2);
322  n[2](1) = E_ijkl(1, 1, 2, 2);
323  n[2](2) = E_ijkl(2, 2, 2, 2);
324 
325  // With non-zero Poisson's ratio and hardening
326  // it is not computationally cheap to know whether
327  // the trial stress will return to the tip, edge,
328  // or plane. The following is correct for zero
329  // Poisson's ratio and no hardening, and at least
330  // gives a not-completely-stupid guess in the
331  // more general case.
332  // trial_order[0] = type of return to try first
333  // trial_order[1] = type of return to try second
334  // trial_order[2] = type of return to try third
335  const unsigned int number_of_return_paths = 3;
336  std::vector<int> trial_order(number_of_return_paths);
337  if (yf[0] > _f_tol) // all the yield functions are positive, since eigvals are ordered eigvals[0]
338  // <= eigvals[1] <= eigvals[2]
339  {
340  trial_order[0] = tip;
341  trial_order[1] = edge;
342  trial_order[2] = plane;
343  }
344  else if (yf[1] > _f_tol) // two yield functions are positive
345  {
346  trial_order[0] = edge;
347  trial_order[1] = tip;
348  trial_order[2] = plane;
349  }
350  else
351  {
352  trial_order[0] = plane;
353  trial_order[1] = edge;
354  trial_order[2] = tip;
355  }
356 
357  unsigned trial;
358  bool nr_converged = false;
359  for (trial = 0; trial < number_of_return_paths; ++trial)
360  {
361  switch (trial_order[trial])
362  {
363  case tip:
364  nr_converged = returnTip(eigvals, n, dpm, returned_stress, intnl_old, 0);
365  break;
366  case edge:
367  nr_converged = returnEdge(eigvals, n, dpm, returned_stress, intnl_old, 0);
368  break;
369  case plane:
370  nr_converged = returnPlane(eigvals, n, dpm, returned_stress, intnl_old, 0);
371  break;
372  }
373 
374  str = tensile_strength(intnl_old + dpm[0] + dpm[1] + dpm[2]);
375 
376  if (nr_converged && KuhnTuckerOK(returned_stress, dpm, str, ep_plastic_tolerance))
377  break;
378  }
379 
380  if (trial == number_of_return_paths)
381  {
382  Moose::err << "Trial stress = \n";
383  trial_stress.print(Moose::err);
384  Moose::err << "Internal parameter = " << intnl_old << "\n";
385  mooseError("TensorMechanicsPlasticTensileMulti: FAILURE! You probably need to implement a "
386  "line search\n");
387  // failure - must place yield function values at trial stress into yf
388  str = tensile_strength(intnl_old);
389  yf[0] = eigvals[0] - str;
390  yf[1] = eigvals[1] - str;
391  yf[2] = eigvals[2] - str;
392  return false;
393  }
394 
395  // success
396 
397  returned_intnl = intnl_old;
398  for (unsigned i = 0; i < 3; ++i)
399  {
400  yf[i] = returned_stress(i, i) - str;
401  delta_dp(i, i) = dpm[i];
402  returned_intnl += dpm[i];
403  }
404  returned_stress = eigvecs * returned_stress * (eigvecs.transpose());
405  delta_dp = eigvecs * delta_dp * (eigvecs.transpose());
406  return true;
407 }
408 
409 bool
410 TensorMechanicsPlasticTensileMulti::returnTip(const std::vector<Real> & eigvals,
411  const std::vector<RealVectorValue> & n,
412  std::vector<Real> & dpm,
413  RankTwoTensor & returned_stress,
414  Real intnl_old,
415  Real initial_guess) const
416 {
417  // The returned point is defined by f0=f1=f2=0.
418  // that is, returned_stress = diag(str, str, str), where
419  // str = tensile_strength(intnl),
420  // where intnl = intnl_old + dpm[0] + dpm[1] + dpm[2]
421  // The 3 plastic multipliers, dpm, are defiend by the normality condition
422  // eigvals - str = dpm[0]*n[0] + dpm[1]*n[1] + dpm[2]*n[2]
423  // (Kuhn-Tucker demands that all dpm are non-negative, but we leave
424  // that checking for later.)
425  // This is a vector equation with solution (A):
426  // dpm[0] = triple(eigvals - str, n[1], n[2])/trip;
427  // dpm[1] = triple(eigvals - str, n[2], n[0])/trip;
428  // dpm[2] = triple(eigvals - str, n[0], n[1])/trip;
429  // where trip = triple(n[0], n[1], n[2]).
430  // By adding the three components of that solution together
431  // we can get an equation for x = dpm[0] + dpm[1] + dpm[2],
432  // and then our Newton-Raphson only involves one variable (x).
433  // In the following, i specialise to the isotropic situation.
434 
435  Real x = initial_guess;
436  const Real denom = (n[0](0) - n[0](1)) * (n[0](0) + 2 * n[0](1));
437  Real str = tensile_strength(intnl_old + x);
438 
439  if (_strength.modelName().compare("Constant") != 0)
440  {
441  // Finding x is expensive. Therefore
442  // although x!=0 for Constant Hardening, solution (A)
443  // demonstrates that we don't
444  // actually need to know x to find the dpm for
445  // Constant Hardening.
446  //
447  // However, for nontrivial Hardening, the following
448  // is necessary
449  const Real eig = eigvals[0] + eigvals[1] + eigvals[2];
450  const Real bul = (n[0](0) + 2 * n[0](1));
451 
452  // and finally, the equation we want to solve is:
453  // bul*x - eig + 3*str = 0
454  // where str=tensile_strength(intnl_old + x)
455  // and x = dpm[0] + dpm[1] + dpm[2]
456  // (Note this has units of stress, so using _f_tol as a convergence check is reasonable.)
457  // Use Netwon-Raphson with initial guess x = 0
458  Real residual = bul * x - eig + 3 * str;
459  Real jacobian;
460  unsigned int iter = 0;
461  do
462  {
463  jacobian = bul + 3 * dtensile_strength(intnl_old + x);
464  x += -residual / jacobian;
465  if (iter > _max_iters) // not converging
466  return false;
467  str = tensile_strength(intnl_old + x);
468  residual = bul * x - eig + 3 * str;
469  iter++;
470  } while (residual * residual > _f_tol * _f_tol);
471  }
472 
473  // The following is the solution (A) written above
474  // (dpm[0] = triple(eigvals - str, n[1], n[2])/trip, etc)
475  // in the isotropic situation
476  dpm[0] = (n[0](0) * (eigvals[0] - str) + n[0](1) * (eigvals[0] - eigvals[1] - eigvals[2] + str)) /
477  denom;
478  dpm[1] = (n[0](0) * (eigvals[1] - str) + n[0](1) * (eigvals[1] - eigvals[2] - eigvals[0] + str)) /
479  denom;
480  dpm[2] = (n[0](0) * (eigvals[2] - str) + n[0](1) * (eigvals[2] - eigvals[0] - eigvals[1] + str)) /
481  denom;
482  returned_stress(0, 0) = returned_stress(1, 1) = returned_stress(2, 2) = str;
483  return true;
484 }
485 
486 bool
487 TensorMechanicsPlasticTensileMulti::returnEdge(const std::vector<Real> & eigvals,
488  const std::vector<RealVectorValue> & n,
489  std::vector<Real> & dpm,
490  RankTwoTensor & returned_stress,
491  Real intnl_old,
492  Real initial_guess) const
493 {
494  // work out the point to which we would return, "a". It is defined by
495  // f1 = 0 = f2, and the normality condition:
496  // (eigvals - a).(n1 x n2) = 0,
497  // where eigvals is the starting position
498  // (it is a vector in principal stress space).
499  // To get f1=0=f2, we need a = (a0, str, str), and a0 is found
500  // by expanding the normality condition to yield:
501  // a0 = (-str*n1xn2[1] - str*n1xn2[2] + edotn1xn2)/n1xn2[0];
502  // where edotn1xn2 = eigvals.(n1 x n2)
503  //
504  // We need to find the plastic multipliers, dpm, defined by
505  // eigvals - a = dpm[1]*n1 + dpm[2]*n2
506  // For the isotropic case, and defining eminusa = eigvals - a,
507  // the solution is easy:
508  // dpm[0] = 0;
509  // dpm[1] = (eminusa[1] - eminusa[0])/(n[1][1] - n[1][0]);
510  // dpm[2] = (eminusa[2] - eminusa[0])/(n[2][2] - n[2][0]);
511  //
512  // Now specialise to the isotropic case. Define
513  // x = dpm[1] + dpm[2] = (eigvals[1] + eigvals[2] - 2*str)/(n[0][0] + n[0][1])
514  // Notice that the RHS is a function of x, so we solve using
515  // Newton-Raphson starting with x=initial_guess
516  Real x = initial_guess;
517  const Real denom = n[0](0) + n[0](1);
518  Real str = tensile_strength(intnl_old + x);
519 
520  if (_strength.modelName().compare("Constant") != 0)
521  {
522  // Finding x is expensive. Therefore
523  // although x!=0 for Constant Hardening, solution
524  // for dpm above demonstrates that we don't
525  // actually need to know x to find the dpm for
526  // Constant Hardening.
527  //
528  // However, for nontrivial Hardening, the following
529  // is necessary
530  const Real eig = eigvals[1] + eigvals[2];
531  Real residual = denom * x - eig + 2 * str;
532  Real jacobian;
533  unsigned int iter = 0;
534  do
535  {
536  jacobian = denom + 2 * dtensile_strength(intnl_old + x);
537  x += -residual / jacobian;
538  if (iter > _max_iters) // not converging
539  return false;
540  str = tensile_strength(intnl_old + x);
541  residual = denom * x - eig + 2 * str;
542  iter++;
543  } while (residual * residual > _f_tol * _f_tol);
544  }
545 
546  dpm[0] = 0;
547  dpm[1] = ((eigvals[1] * n[0](0) - eigvals[2] * n[0](1)) / (n[0](0) - n[0](1)) - str) / denom;
548  dpm[2] = ((eigvals[2] * n[0](0) - eigvals[1] * n[0](1)) / (n[0](0) - n[0](1)) - str) / denom;
549 
550  returned_stress(0, 0) = eigvals[0] - n[0](1) * (dpm[1] + dpm[2]);
551  returned_stress(1, 1) = returned_stress(2, 2) = str;
552  return true;
553 }
554 
555 bool
556 TensorMechanicsPlasticTensileMulti::returnPlane(const std::vector<Real> & eigvals,
557  const std::vector<RealVectorValue> & n,
558  std::vector<Real> & dpm,
559  RankTwoTensor & returned_stress,
560  Real intnl_old,
561  Real initial_guess) const
562 {
563  // the returned point, "a", is defined by f2=0 and
564  // a = p - dpm[2]*n2.
565  // This is a vector equation in
566  // principal stress space, and dpm[2] is the third
567  // plasticity multiplier (dpm[0]=0=dpm[1] for return
568  // to the plane) and "p" is the starting
569  // position (p=eigvals).
570  // (Kuhn-Tucker demands that dpm[2]>=0, but we leave checking
571  // that condition for later.)
572  // Since f2=0, we must have a[2]=tensile_strength,
573  // so we can just look at the [2] component of the
574  // equation, which yields
575  // n[2][2]*dpm[2] - eigvals[2] + str = 0
576  // For hardening, str=tensile_strength(intnl_old+dpm[2]),
577  // and we want to solve for dpm[2].
578  // Use Newton-Raphson with initial guess dpm[2] = initial_guess
579  dpm[2] = initial_guess;
580  Real residual = n[2](2) * dpm[2] - eigvals[2] + tensile_strength(intnl_old + dpm[2]);
581  Real jacobian;
582  unsigned int iter = 0;
583  do
584  {
585  jacobian = n[2](2) + dtensile_strength(intnl_old + dpm[2]);
586  dpm[2] += -residual / jacobian;
587  if (iter > _max_iters) // not converging
588  return false;
589  residual = n[2](2) * dpm[2] - eigvals[2] + tensile_strength(intnl_old + dpm[2]);
590  iter++;
591  } while (residual * residual > _f_tol * _f_tol);
592 
593  dpm[0] = 0;
594  dpm[1] = 0;
595  returned_stress(0, 0) = eigvals[0] - dpm[2] * n[2](0);
596  returned_stress(1, 1) = eigvals[1] - dpm[2] * n[2](1);
597  returned_stress(2, 2) = eigvals[2] - dpm[2] * n[2](2);
598  return true;
599 }
600 
601 bool
603  const std::vector<Real> & dpm,
604  Real str,
605  Real ep_plastic_tolerance) const
606 {
607  for (unsigned i = 0; i < 3; ++i)
609  returned_diagonal_stress(i, i) - str, dpm[i], ep_plastic_tolerance))
610  return false;
611  return true;
612 }
613 
616  const RankTwoTensor & trial_stress,
617  Real intnl_old,
618  const RankTwoTensor & stress,
619  Real intnl,
620  const RankFourTensor & E_ijkl,
621  const std::vector<Real> & cumulative_pm) const
622 {
623  if (!_use_custom_cto)
625  trial_stress, intnl_old, stress, intnl, E_ijkl, cumulative_pm);
626 
627  mooseAssert(cumulative_pm.size() == 3,
628  "TensorMechanicsPlasticTensileMulti size of cumulative_pm should be 3 but it is "
629  << cumulative_pm.size());
630 
631  if (cumulative_pm[2] <= 0) // All cumulative_pm are non-positive, so this is admissible
632  return E_ijkl;
633 
634  // Need the eigenvalues at the returned configuration
635  std::vector<Real> eigvals;
636  stress.symmetricEigenvalues(eigvals);
637 
638  // need to rotate to and from principal stress space
639  // using the eigenvectors of the trial configuration
640  // (not the returned configuration).
641  std::vector<Real> trial_eigvals;
642  RankTwoTensor trial_eigvecs;
643  trial_stress.symmetricEigenvaluesEigenvectors(trial_eigvals, trial_eigvecs);
644 
645  // The returnMap will have returned to the Tip, Edge or
646  // Plane. The consistentTangentOperator describes the
647  // change in stress for an arbitrary change in applied
648  // strain. I assume that the change in strain will not
649  // change the type of return (Tip remains Tip, Edge remains
650  // Edge, Plane remains Plane).
651  // I assume isotropic elasticity.
652  //
653  // The consistent tangent operator is a little different
654  // than cases where no rotation to principal stress space
655  // is made during the returnMap. Let S_ij be the stress
656  // in original coordinates, and s_ij be the stress in the
657  // principal stress coordinates, so that
658  // s_ij = diag(eigvals[0], eigvals[1], eigvals[2])
659  // We want dS_ij under an arbitrary change in strain (ep->ep+dep)
660  // dS = S(ep+dep) - S(ep)
661  // = R(ep+dep) s(ep+dep) R(ep+dep)^T - R(ep) s(ep) R(ep)^T
662  // Here R = the rotation to principal-stress space, ie
663  // R_ij = eigvecs[i][j] = i^th component of j^th eigenvector
664  // Expanding to first order in dep,
665  // dS = R(ep) (s(ep+dep) - s(ep)) R(ep)^T
666  // + dR/dep s(ep) R^T + R(ep) s(ep) dR^T/dep
667  // The first line is all that is usually calculated in the
668  // consistent tangent operator calculation, and is called
669  // cto below.
670  // The second line involves changes in the eigenvectors, and
671  // is called sec below.
672 
673  RankFourTensor cto;
674  const Real hard = dtensile_strength(intnl);
675  const Real la = E_ijkl(0, 0, 1, 1);
676  const Real mu = 0.5 * (E_ijkl(0, 0, 0, 0) - la);
677 
678  if (cumulative_pm[1] <= 0)
679  {
680  // only cumulative_pm[2] is positive, so this is return to the Plane
681  const Real denom = hard + la + 2 * mu;
682  const Real al = la * la / denom;
683  const Real be = la * (la + 2 * mu) / denom;
684  const Real ga = hard * (la + 2 * mu) / denom;
685  std::vector<Real> comps(9);
686  comps[0] = comps[4] = la + 2 * mu - al;
687  comps[1] = comps[3] = la - al;
688  comps[2] = comps[5] = comps[6] = comps[7] = la - be;
689  comps[8] = ga;
690  cto.fillFromInputVector(comps, RankFourTensor::principal);
691  }
692  else if (cumulative_pm[0] <= 0)
693  {
694  // both cumulative_pm[2] and cumulative_pm[1] are positive, so Edge
695  const Real denom = 2 * hard + 2 * la + 2 * mu;
696  const Real al = hard * 2 * la / denom;
697  const Real be = hard * (2 * la + 2 * mu) / denom;
698  std::vector<Real> comps(9);
699  comps[0] = la + 2 * mu - 2 * la * la / denom;
700  comps[1] = comps[2] = al;
701  comps[3] = comps[6] = al;
702  comps[4] = comps[5] = comps[7] = comps[8] = be;
703  cto.fillFromInputVector(comps, RankFourTensor::principal);
704  }
705  else
706  {
707  // all cumulative_pm are positive, so Tip
708  const Real denom = 3 * hard + 3 * la + 2 * mu;
709  std::vector<Real> comps(2);
710  comps[0] = hard * (3 * la + 2 * mu) / denom;
711  comps[1] = 0;
712  cto.fillFromInputVector(comps, RankFourTensor::symmetric_isotropic);
713  }
714 
715  cto.rotate(trial_eigvecs);
716 
717  // drdsig = change in eigenvectors under a small stress change
718  // drdsig(i,j,m,n) = dR(i,j)/dS_mn
719  // The formula below is fairly easily derived:
720  // S R = R s, so taking the variation
721  // dS R + S dR = dR s + R ds, and multiplying by R^T
722  // R^T dS R + R^T S dR = R^T dR s + ds .... (eqn 1)
723  // I demand that RR^T = 1 = R^T R, and also that
724  // (R+dR)(R+dR)^T = 1 = (R+dT)^T (R+dR), which means
725  // that dR = R*c, for some antisymmetric c, so Eqn1 reads
726  // R^T dS R + s c = c s + ds
727  // Grabbing the components of this gives ds/dS (already
728  // in RankTwoTensor), and c, which is:
729  // dR_ik/dS_mn = drdsig(i, k, m, n) = trial_eigvecs(m, b)*trial_eigvecs(n, k)*trial_eigvecs(i,
730  // b)/(trial_eigvals[k] - trial_eigvals[b]);
731  // (sum over b!=k).
732 
733  RankFourTensor drdsig;
734  for (unsigned k = 0; k < 3; ++k)
735  for (unsigned b = 0; b < 3; ++b)
736  {
737  if (b == k)
738  continue;
739  for (unsigned m = 0; m < 3; ++m)
740  for (unsigned n = 0; n < 3; ++n)
741  for (unsigned i = 0; i < 3; ++i)
742  drdsig(i, k, m, n) += trial_eigvecs(m, b) * trial_eigvecs(n, k) * trial_eigvecs(i, b) /
743  (trial_eigvals[k] - trial_eigvals[b]);
744  }
745 
746  // With diagla = diag(eigvals[0], eigvals[1], digvals[2])
747  // The following implements
748  // ans(i, j, a, b) += (drdsig(i, k, m, n)*trial_eigvecs(j, l)*diagla(k, l) + trial_eigvecs(i,
749  // k)*drdsig(j, l, m, n)*diagla(k, l))*E_ijkl(m, n, a, b);
750  // (sum over k, l, m and n)
751 
752  RankFourTensor ans;
753  for (unsigned i = 0; i < 3; ++i)
754  for (unsigned j = 0; j < 3; ++j)
755  for (unsigned a = 0; a < 3; ++a)
756  for (unsigned k = 0; k < 3; ++k)
757  for (unsigned m = 0; m < 3; ++m)
758  ans(i, j, a, a) += (drdsig(i, k, m, m) * trial_eigvecs(j, k) +
759  trial_eigvecs(i, k) * drdsig(j, k, m, m)) *
760  eigvals[k] * la; // E_ijkl(m, n, a, b) = la*(m==n)*(a==b);
761 
762  for (unsigned i = 0; i < 3; ++i)
763  for (unsigned j = 0; j < 3; ++j)
764  for (unsigned a = 0; a < 3; ++a)
765  for (unsigned b = 0; b < 3; ++b)
766  for (unsigned k = 0; k < 3; ++k)
767  {
768  ans(i, j, a, b) += (drdsig(i, k, a, b) * trial_eigvecs(j, k) +
769  trial_eigvecs(i, k) * drdsig(j, k, a, b)) *
770  eigvals[k] * mu; // E_ijkl(m, n, a, b) = mu*(m==a)*(n==b)
771  ans(i, j, a, b) += (drdsig(i, k, b, a) * trial_eigvecs(j, k) +
772  trial_eigvecs(i, k) * drdsig(j, k, b, a)) *
773  eigvals[k] * mu; // E_ijkl(m, n, a, b) = mu*(m==b)*(n==a)
774  }
775 
776  return cto + ans;
777 }
778 
779 bool
781 {
782  return _use_custom_returnMap;
783 }
784 
785 bool
787 {
788  return _use_custom_cto;
789 }
TensorMechanicsPlasticTensileMulti::tensile_strength
virtual Real tensile_strength(const Real internal_param) const
tensile strength as a function of residual value, rate, and internal_param
Definition: TensorMechanicsPlasticTensileMulti.C:148
TensorMechanicsPlasticTensileMulti::_shift
const Real _shift
yield function is shifted by this amount to avoid problems with stress-derivatives at equal eigenvalu...
Definition: TensorMechanicsPlasticTensileMulti.h:102
TensorMechanicsPlasticTensileMulti::edge
Definition: TensorMechanicsPlasticTensileMulti.h:222
TensorMechanicsPlasticModel::validParams
static InputParameters validParams()
Definition: TensorMechanicsPlasticModel.C:18
TensorMechanicsPlasticTensileMulti::returnTip
bool returnTip(const std::vector< Real > &eigvals, const std::vector< RealVectorValue > &n, std::vector< Real > &dpm, RankTwoTensor &returned_stress, Real intnl_old, Real initial_guess) const
Tries to return-map to the Tensile tip.
Definition: TensorMechanicsPlasticTensileMulti.C:410
TensorMechanicsPlasticTensileMulti::_strength
const TensorMechanicsHardeningModel & _strength
Definition: TensorMechanicsPlasticTensileMulti.h:96
TensorMechanicsPlasticTensileMulti::_use_custom_returnMap
const bool _use_custom_returnMap
Whether to use the custom return-map algorithm.
Definition: TensorMechanicsPlasticTensileMulti.h:105
TensorMechanicsPlasticTensileMulti::plane
Definition: TensorMechanicsPlasticTensileMulti.h:223
TensorMechanicsPlasticTensileMulti::KuhnTuckerOK
bool KuhnTuckerOK(const RankTwoTensor &returned_diagonal_stress, const std::vector< Real > &dpm, Real str, Real ep_plastic_tolerance) const
Returns true if the Kuhn-Tucker conditions are satisfied.
Definition: TensorMechanicsPlasticTensileMulti.C:602
TensorMechanicsPlasticModel::KuhnTuckerSingleSurface
bool KuhnTuckerSingleSurface(Real yf, Real dpm, Real dpm_tol) const
Returns true if the Kuhn-Tucker conditions for the single surface are satisfied.
Definition: TensorMechanicsPlasticModel.C:248
TensorMechanicsPlasticTensileMulti.h
TensorMechanicsPlasticTensileMulti::triple
Real triple(const std::vector< Real > &a, const std::vector< Real > &b, const std::vector< Real > &c) const
triple product of three 3-dimensional vectors
Definition: TensorMechanicsPlasticTensileMulti.C:203
TensorMechanicsPlasticTensileMulti::yieldFunctionV
virtual void yieldFunctionV(const RankTwoTensor &stress, Real intnl, std::vector< Real > &f) const override
Calculates the yield functions.
Definition: TensorMechanicsPlasticTensileMulti.C:77
TensorMechanicsPlasticTensileMulti::flowPotentialV
virtual void flowPotentialV(const RankTwoTensor &stress, Real intnl, std::vector< RankTwoTensor > &r) const override
The flow potentials.
Definition: TensorMechanicsPlasticTensileMulti.C:125
TensorMechanicsPlasticTensileMulti::_use_custom_cto
const bool _use_custom_cto
Whether to use the custom consistent tangent operator calculation.
Definition: TensorMechanicsPlasticTensileMulti.h:108
TensorMechanicsPlasticTensileMulti::consistentTangentOperator
virtual RankFourTensor consistentTangentOperator(const RankTwoTensor &trial_stress, Real intnl_old, const RankTwoTensor &stress, Real intnl, const RankFourTensor &E_ijkl, const std::vector< Real > &cumulative_pm) const override
Calculates a custom consistent tangent operator.
Definition: TensorMechanicsPlasticTensileMulti.C:615
TensorMechanicsPlasticTensileMulti::returnMap
virtual bool returnMap(const RankTwoTensor &trial_stress, Real intnl_old, const RankFourTensor &E_ijkl, Real ep_plastic_tolerance, RankTwoTensor &returned_stress, Real &returned_intnl, std::vector< Real > &dpm, RankTwoTensor &delta_dp, std::vector< Real > &yf, bool &trial_stress_inadmissible) const override
Performs a custom return-map.
Definition: TensorMechanicsPlasticTensileMulti.C:218
TensorMechanicsPlasticModel::_f_tol
const Real _f_tol
Tolerance on yield function.
Definition: TensorMechanicsPlasticModel.h:175
TensorMechanicsPlasticTensileMulti::TensorMechanicsPlasticTensileMulti
TensorMechanicsPlasticTensileMulti(const InputParameters &parameters)
Definition: TensorMechanicsPlasticTensileMulti.C:51
TensorMechanicsPlasticTensileMulti::returnPlane
bool returnPlane(const std::vector< Real > &eigvals, const std::vector< RealVectorValue > &n, std::vector< Real > &dpm, RankTwoTensor &returned_stress, Real intnl_old, Real initial_guess) const
Tries to return-map to the Tensile plane The return value is true if the internal Newton-Raphson proc...
Definition: TensorMechanicsPlasticTensileMulti.C:556
TensorMechanicsHardeningModel::modelName
virtual std::string modelName() const =0
TensorMechanicsPlasticTensileMulti::dflowPotential_dstressV
virtual void dflowPotential_dstressV(const RankTwoTensor &stress, Real intnl, std::vector< RankFourTensor > &dr_dstress) const override
The derivative of the flow potential with respect to stress.
Definition: TensorMechanicsPlasticTensileMulti.C:134
TensorMechanicsPlasticTensileMulti::numberSurfaces
virtual unsigned int numberSurfaces() const override
The number of yield surfaces for this plasticity model.
Definition: TensorMechanicsPlasticTensileMulti.C:71
TensorMechanicsPlasticTensileMulti::validParams
static InputParameters validParams()
Definition: TensorMechanicsPlasticTensileMulti.C:21
TensorMechanicsPlasticTensileMulti::_max_iters
const unsigned int _max_iters
maximum iterations allowed in the custom return-map algorithm
Definition: TensorMechanicsPlasticTensileMulti.h:99
TensorMechanicsHardeningModel::derivative
virtual Real derivative(Real intnl) const
Definition: TensorMechanicsHardeningModel.C:47
TensorMechanicsPlasticTensileMulti::activeConstraints
virtual void activeConstraints(const std::vector< Real > &f, const RankTwoTensor &stress, Real intnl, const RankFourTensor &Eijkl, std::vector< bool > &act, RankTwoTensor &returned_stress) const override
The active yield surfaces, given a vector of yield functions.
Definition: TensorMechanicsPlasticTensileMulti.C:160
TensorMechanicsPlasticModel::returnMap
virtual bool returnMap(const RankTwoTensor &trial_stress, Real intnl_old, const RankFourTensor &E_ijkl, Real ep_plastic_tolerance, RankTwoTensor &returned_stress, Real &returned_intnl, std::vector< Real > &dpm, RankTwoTensor &delta_dp, std::vector< Real > &yf, bool &trial_stress_inadmissible) const
Performs a custom return-map.
Definition: TensorMechanicsPlasticModel.C:221
TensorMechanicsHardeningModel::value
virtual Real value(Real intnl) const
Definition: TensorMechanicsHardeningModel.C:45
RankTwoTensor
RankTwoTensorTempl< Real > RankTwoTensor
Definition: ACGrGrElasticDrivingForce.h:17
TensorMechanicsPlasticTensileMulti
FiniteStrainTensileMulti implements rate-independent associative tensile failure with hardening/softe...
Definition: TensorMechanicsPlasticTensileMulti.h:24
TensorMechanicsPlasticTensileMulti::dot
Real dot(const std::vector< Real > &a, const std::vector< Real > &b) const
dot product of two 3-dimensional vectors
Definition: TensorMechanicsPlasticTensileMulti.C:196
TensorMechanicsPlasticModel::consistentTangentOperator
virtual RankFourTensor consistentTangentOperator(const RankTwoTensor &trial_stress, Real intnl_old, const RankTwoTensor &stress, Real intnl, const RankFourTensor &E_ijkl, const std::vector< Real > &cumulative_pm) const
Calculates a custom consistent tangent operator.
Definition: TensorMechanicsPlasticModel.C:254
TensorMechanicsPlasticTensileMulti::returnEdge
bool returnEdge(const std::vector< Real > &eigvals, const std::vector< RealVectorValue > &n, std::vector< Real > &dpm, RankTwoTensor &returned_stress, Real intnl_old, Real initial_guess) const
Tries to return-map to the Tensile edge.
Definition: TensorMechanicsPlasticTensileMulti.C:487
TensorMechanicsPlasticTensileMulti::dyieldFunction_dintnlV
virtual void dyieldFunction_dintnlV(const RankTwoTensor &stress, Real intnl, std::vector< Real > &df_dintnl) const override
The derivative of yield functions with respect to the internal parameter.
Definition: TensorMechanicsPlasticTensileMulti.C:117
RankFourTensorTempl< Real >
TensorMechanicsPlasticTensileMulti::dflowPotential_dintnlV
virtual void dflowPotential_dintnlV(const RankTwoTensor &stress, Real intnl, std::vector< RankTwoTensor > &dr_dintnl) const override
The derivative of the flow potential with respect to the internal parameter.
Definition: TensorMechanicsPlasticTensileMulti.C:141
TensorMechanicsPlasticTensileMulti::doReturnMap
virtual bool doReturnMap(const RankTwoTensor &trial_stress, Real intnl_old, const RankFourTensor &E_ijkl, Real ep_plastic_tolerance, RankTwoTensor &returned_stress, Real &returned_intnl, std::vector< Real > &dpm, RankTwoTensor &delta_dp, std::vector< Real > &yf, bool &trial_stress_inadmissible) const
Just like returnMap, but a protected interface that definitely uses the algorithm,...
Definition: TensorMechanicsPlasticTensileMulti.C:254
TensorMechanicsPlasticModel
Plastic Model base class The virtual functions written below must be over-ridden in derived classes t...
Definition: TensorMechanicsPlasticModel.h:42
TensorMechanicsPlasticTensileMulti::modelName
virtual std::string modelName() const override
Definition: TensorMechanicsPlasticTensileMulti.C:212
TensorMechanicsPlasticTensileMulti::useCustomReturnMap
virtual bool useCustomReturnMap() const override
Returns false. You will want to override this in your derived class if you write a custom returnMap f...
Definition: TensorMechanicsPlasticTensileMulti.C:780
RankTwoTensorTempl< Real >
TensorMechanicsPlasticTensileMulti::dyieldFunction_dstressV
virtual void dyieldFunction_dstressV(const RankTwoTensor &stress, Real intnl, std::vector< RankTwoTensor > &df_dstress) const override
The derivative of yield functions with respect to stress.
Definition: TensorMechanicsPlasticTensileMulti.C:92
defineLegacyParams
defineLegacyParams(TensorMechanicsPlasticTensileMulti)
TensorMechanicsPlasticTensileMulti::useCustomCTO
virtual bool useCustomCTO() const override
Returns false. You will want to override this in your derived class if you write a custom consistent ...
Definition: TensorMechanicsPlasticTensileMulti.C:786
TensorMechanicsPlasticTensileMulti::tip
Definition: TensorMechanicsPlasticTensileMulti.h:221
registerMooseObject
registerMooseObject("TensorMechanicsApp", TensorMechanicsPlasticTensileMulti)
TensorMechanicsHardeningModel
Hardening Model base class.
Definition: TensorMechanicsHardeningModel.h:27
TensorMechanicsPlasticTensileMulti::dtensile_strength
virtual Real dtensile_strength(const Real internal_param) const
d(tensile strength)/d(internal_param) as a function of residual value, rate, and internal_param
Definition: TensorMechanicsPlasticTensileMulti.C:154