https://mooseframework.inl.gov
RankFourTensorImplementation.h
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 #pragma once
11 
12 #include "RankFourTensor.h"
13 
14 // MOOSE includes
15 #include "RankTwoTensor.h"
16 #include "RankThreeTensor.h"
17 #include "MooseEnum.h"
18 #include "MooseException.h"
19 #include "MooseUtils.h"
20 #include "MatrixTools.h"
21 #include "PermutationTensor.h"
22 
23 #include "libmesh/utility.h"
24 #include "libmesh/tensor_value.h"
25 #include "libmesh/vector_value.h"
26 
27 // Eigen needs LU
28 #include "Eigen/LU"
29 
30 // C++ includes
31 #include <iomanip>
32 #include <ostream>
33 
34 namespace MathUtils
35 {
36 template <>
37 void mooseSetToZero<RankFourTensorTempl<Real>>(RankFourTensorTempl<Real> & v);
38 template <>
39 void mooseSetToZero<RankFourTensorTempl<ADReal>>(RankFourTensorTempl<ADReal> & v);
40 }
41 
42 template <typename T>
45 {
46  return MooseEnum("antisymmetric symmetric9 symmetric21 general_isotropic symmetric_isotropic "
47  "symmetric_isotropic_E_nu antisymmetric_isotropic axisymmetric_rz general "
48  "principal orthotropic");
49 }
50 
51 template <typename T>
53 {
54  mooseAssert(N == 3, "RankFourTensorTempl<T> is currently only tested for 3 dimensions.");
55 
56  for (auto i : make_range(N4))
57  _vals[i] = 0.0;
58 }
59 
60 template <typename T>
62 {
63  unsigned int index = 0;
64  switch (init)
65  {
66  case initNone:
67  break;
68 
69  case initIdentity:
70  zero();
71  for (auto i : make_range(N))
72  (*this)(i, i, i, i) = 1.0;
73  break;
74 
75  case initIdentityFour:
76  for (auto i : make_range(N))
77  for (auto j : make_range(N))
78  for (auto k : make_range(N))
79  for (auto l : make_range(N))
80  _vals[index++] = Real(i == k && j == l);
81  break;
82 
83  case initIdentitySymmetricFour:
84  for (auto i : make_range(N))
85  for (auto j : make_range(N))
86  for (auto k : make_range(N))
87  for (auto l : make_range(N))
88  _vals[index++] = 0.5 * Real(i == k && j == l) + 0.5 * Real(i == l && j == k);
89  break;
90 
91  case initIdentityDeviatoric:
92  for (unsigned int i = 0; i < N; ++i)
93  for (unsigned int j = 0; j < N; ++j)
94  for (unsigned int k = 0; k < N; ++k)
95  for (unsigned int l = 0; l < N; ++l)
96  {
97  _vals[index] = Real(i == k && j == l);
98  if ((i == j) && (k == l))
99  _vals[index] -= 1.0 / 3.0;
100  index++;
101  }
102  break;
103 
104  default:
105  mooseError("Unknown RankFourTensorTempl<T> initialization pattern.");
106  }
107 }
108 
109 template <typename T>
110 RankFourTensorTempl<T>::RankFourTensorTempl(const std::vector<T> & input, FillMethod fill_method)
111 {
112  fillFromInputVector(input, fill_method);
113 }
114 
115 template <typename T>
116 void
118 {
119  for (auto i : make_range(N4))
120  _vals[i] = 0.0;
121 }
122 
123 template <typename T>
124 template <template <typename> class Tensor, typename T2>
125 auto
127  typename std::enable_if<TwoTensorMultTraits<Tensor, T2>::value,
129 {
130  typedef decltype(T() * T2()) ValueType;
132 
133  unsigned int index = 0;
134  for (unsigned int ij = 0; ij < N2; ++ij)
135  {
136  ValueType tmp = 0;
137  for (unsigned int kl = 0; kl < N2; ++kl)
138  tmp += _vals[index++] * b(kl / LIBMESH_DIM, kl % LIBMESH_DIM);
139  result._coords[ij] = tmp;
140  }
141 
142  return result;
143 }
144 
145 template <typename T>
148 {
149  for (auto i : make_range(N4))
150  _vals[i] *= a;
151  return *this;
152 }
153 
154 template <typename T>
157 {
158  for (auto i : make_range(N4))
159  _vals[i] /= a;
160  return *this;
161 }
162 
163 template <typename T>
166 {
167  for (auto i : make_range(N4))
168  _vals[i] += a._vals[i];
169  return *this;
170 }
171 
172 template <typename T>
173 template <typename T2>
174 auto
177 {
179  for (auto i : make_range(N4))
180  result._vals[i] = _vals[i] + b._vals[i];
181  return result;
182 }
183 
184 template <typename T>
187 {
188  for (auto i : make_range(N4))
189  _vals[i] -= a._vals[i];
190  return *this;
191 }
192 
193 template <typename T>
194 template <typename T2>
195 auto
197  -> RankFourTensorTempl<decltype(T() - T2())>
198 {
199  RankFourTensorTempl<decltype(T() - T2())> result;
200  for (auto i : make_range(N4))
201  result._vals[i] = _vals[i] - b._vals[i];
202  return result;
203 }
204 
205 template <typename T>
208 {
209  RankFourTensorTempl<T> result;
210  for (auto i : make_range(N4))
211  result._vals[i] = -_vals[i];
212  return result;
213 }
214 
215 template <typename T>
216 template <typename T2>
217 auto
220 {
221  typedef decltype(T() * T2()) ValueType;
223 
224  for (auto i : make_range(N))
225  for (auto j : make_range(N))
226  for (auto k : make_range(N))
227  for (auto l : make_range(N))
228  for (auto p : make_range(N))
229  for (auto q : make_range(N))
230  result(i, j, k, l) += (*this)(i, j, p, q) * b(p, q, k, l);
231 
232  return result;
233 }
234 
235 template <typename T>
236 T
238 {
239  T l2 = 0;
240 
241  for (auto i : make_range(N4))
242  l2 += Utility::pow<2>(_vals[i]);
243 
244  using std::sqrt;
245  return sqrt(l2);
246 }
247 
248 template <typename T>
251 {
252  mooseError("The invSymm operation calls to LAPACK and only supports plain Real type tensors.");
253 }
254 
255 template <>
258 {
259  unsigned int ntens = N * (N + 1) / 2;
260  int nskip = N - 1;
261 
263  std::vector<PetscScalar> mat;
264  mat.assign(ntens * ntens, 0);
265 
266  // We use the LAPACK matrix inversion routine here. Form the matrix
267  //
268  // mat[0] mat[1] mat[2] mat[3] mat[4] mat[5]
269  // mat[6] mat[7] mat[8] mat[9] mat[10] mat[11]
270  // mat[12] mat[13] mat[14] mat[15] mat[16] mat[17]
271  // mat[18] mat[19] mat[20] mat[21] mat[22] mat[23]
272  // mat[24] mat[25] mat[26] mat[27] mat[28] mat[29]
273  // mat[30] mat[31] mat[32] mat[33] mat[34] mat[35]
274  //
275  // This is filled from the indpendent components of C assuming
276  // the symmetry C_ijkl = C_ijlk = C_jikl.
277  //
278  // If there are two rank-four tensors X and Y then the reason for
279  // this filling becomes apparent if we want to calculate
280  // X_ijkl*Y_klmn = Z_ijmn
281  // For denote the "mat" versions of X, Y and Z by x, y and z.
282  // Then
283  // z_ab = x_ac*y_cb
284  // Eg
285  // z_00 = Z_0000 = X_0000*Y_0000 + X_0011*Y_1111 + X_0022*Y_2200 + 2*X_0001*Y_0100 +
286  // 2*X_0002*Y_0200 + 2*X_0012*Y_1200 (the factors of 2 come from the assumed symmetries)
287  // z_03 = 2*Z_0001 = X_0000*2*Y_0001 + X_0011*2*Y_1101 + X_0022*2*Y_2201 + 2*X_0001*2*Y_0101 +
288  // 2*X_0002*2*Y_0201 + 2*X_0012*2*Y_1201
289  // z_22 = 2*Z_0102 = X_0100*2*Y_0002 + X_0111*2*X_1102 + X_0122*2*Y_2202 + 2*X_0101*2*Y_0102 +
290  // 2*X_0102*2*Y_0202 + 2*X_0112*2*Y_1202
291  // Finally, we use LAPACK to find x^-1, and put it back into rank-4 tensor form
292  //
293  // mat[0] = C(0,0,0,0)
294  // mat[1] = C(0,0,1,1)
295  // mat[2] = C(0,0,2,2)
296  // mat[3] = C(0,0,0,1)*2
297  // mat[4] = C(0,0,0,2)*2
298  // mat[5] = C(0,0,1,2)*2
299 
300  // mat[6] = C(1,1,0,0)
301  // mat[7] = C(1,1,1,1)
302  // mat[8] = C(1,1,2,2)
303  // mat[9] = C(1,1,0,1)*2
304  // mat[10] = C(1,1,0,2)*2
305  // mat[11] = C(1,1,1,2)*2
306 
307  // mat[12] = C(2,2,0,0)
308  // mat[13] = C(2,2,1,1)
309  // mat[14] = C(2,2,2,2)
310  // mat[15] = C(2,2,0,1)*2
311  // mat[16] = C(2,2,0,2)*2
312  // mat[17] = C(2,2,1,2)*2
313 
314  // mat[18] = C(0,1,0,0)
315  // mat[19] = C(0,1,1,1)
316  // mat[20] = C(0,1,2,2)
317  // mat[21] = C(0,1,0,1)*2
318  // mat[22] = C(0,1,0,2)*2
319  // mat[23] = C(0,1,1,2)*2
320 
321  // mat[24] = C(0,2,0,0)
322  // mat[25] = C(0,2,1,1)
323  // mat[26] = C(0,2,2,2)
324  // mat[27] = C(0,2,0,1)*2
325  // mat[28] = C(0,2,0,2)*2
326  // mat[29] = C(0,2,1,2)*2
327 
328  // mat[30] = C(1,2,0,0)
329  // mat[31] = C(1,2,1,1)
330  // mat[32] = C(1,2,2,2)
331  // mat[33] = C(1,2,0,1)*2
332  // mat[34] = C(1,2,0,2)*2
333  // mat[35] = C(1,2,1,2)*2
334 
335  unsigned int index = 0;
336  for (auto i : make_range(N))
337  for (auto j : make_range(N))
338  for (auto k : make_range(N))
339  for (auto l : make_range(N))
340  {
341  if (i == j)
342  mat[k == l ? i * ntens + k : i * ntens + k + nskip + l] += _vals[index];
343  else
344  // i!=j
345  mat[k == l ? (nskip + i + j) * ntens + k : (nskip + i + j) * ntens + k + nskip + l] +=
346  _vals[index]; // note the +=, which results in double-counting and is rectified
347  // below
348  index++;
349  }
350 
351  for (unsigned int i = 3; i < ntens; ++i)
352  for (auto j : make_range(ntens))
353  mat[i * ntens + j] /= 2.0; // because of double-counting above
354 
355  // use LAPACK to find the inverse
356  MatrixTools::inverse(mat, ntens);
357 
358  // build the resulting rank-four tensor
359  // using the inverse of the above algorithm
360  index = 0;
361  for (auto i : make_range(N))
362  for (auto j : make_range(N))
363  for (auto k : make_range(N))
364  for (auto l : make_range(N))
365  {
366  if (i == j)
367  result._vals[index] =
368  k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
369  else
370  // i!=j
371  result._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
372  : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
373  index++;
374  }
375 
376  return result;
377 }
378 
379 template <typename T>
380 void
381 RankFourTensorTempl<T>::rotate(const TypeTensor<T> & R)
382 {
383  RankFourTensorTempl<T> old = *this;
384 
385  unsigned int index = 0;
386  for (auto i : make_range(N))
387  for (auto j : make_range(N))
388  for (auto k : make_range(N))
389  for (auto l : make_range(N))
390  {
391  unsigned int index2 = 0;
392  T sum = 0.0;
393  for (auto m : make_range(N))
394  {
395  const T & a = R(i, m);
396  for (auto n : make_range(N))
397  {
398  const T & ab = a * R(j, n);
399  for (auto o : make_range(N))
400  {
401  const T & abc = ab * R(k, o);
402  for (auto p : make_range(N))
403  sum += abc * R(l, p) * old._vals[index2++];
404  }
405  }
406  }
407  _vals[index++] = sum;
408  }
409 }
410 
411 template <typename T>
412 void
413 RankFourTensorTempl<T>::print(std::ostream & stm) const
414 {
415  for (auto i : make_range(N))
416  for (auto j : make_range(N))
417  {
418  stm << "i = " << i << " j = " << j << '\n';
419  for (auto k : make_range(N))
420  {
421  for (auto l : make_range(N))
422  stm << std::setw(15) << (*this)(i, j, k, l) << " ";
423 
424  stm << '\n';
425  }
426  }
427 
428  stm << std::flush;
429 }
430 
431 template <typename T>
432 void
433 RankFourTensorTempl<T>::printReal(std::ostream & stm) const
434 {
435  for (unsigned int i = 0; i < N; ++i)
436  for (unsigned int j = 0; j < N; ++j)
437  {
438  stm << "i = " << i << " j = " << j << '\n';
439  for (unsigned int k = 0; k < N; ++k)
440  {
441  for (unsigned int l = 0; l < N; ++l)
442  stm << std::setw(15) << MetaPhysicL::raw_value((*this)(i, j, k, l)) << " ";
443 
444  stm << '\n';
445  }
446  }
447 
448  stm << std::flush;
449 }
450 
451 template <typename T>
454 {
455  RankFourTensorTempl<T> result;
456 
457  unsigned int index = 0;
458  for (auto i : make_range(N))
459  for (auto j : make_range(N))
460  for (auto k : make_range(N))
461  for (auto l : make_range(N))
462  result._vals[index++] = _vals[k * N3 + i * N + j + l * N2];
463 
464  return result;
465 }
466 
467 template <typename T>
470 {
471  RankFourTensorTempl<T> result;
472 
473  for (auto i : make_range(N))
474  for (auto j : make_range(N))
475  for (auto k : make_range(N))
476  for (auto l : make_range(N))
477  result(i, j, k, l) = (*this)(j, i, k, l);
478 
479  return result;
480 }
481 
482 template <typename T>
485 {
486  RankFourTensorTempl<T> result;
487 
488  for (auto i : make_range(N))
489  for (auto j : make_range(N))
490  for (auto k : make_range(N))
491  for (auto l : make_range(N))
492  result(i, j, k, l) = (*this)(i, j, l, k);
493 
494  return result;
495 }
496 
497 template <typename T>
498 void
500 {
501  zero();
502 
503  if (input.size() == 9)
504  {
505  // then fill from vector C_1111, C_1112, C_1122, C_1212, C_1222, C_1211, C_2211, C_2212, C_2222
506  (*this)(0, 0, 0, 0) = input[0];
507  (*this)(0, 0, 0, 1) = input[1];
508  (*this)(0, 0, 1, 1) = input[2];
509  (*this)(0, 1, 0, 1) = input[3];
510  (*this)(0, 1, 1, 1) = input[4];
511  (*this)(0, 1, 0, 0) = input[5];
512  (*this)(1, 1, 0, 0) = input[6];
513  (*this)(1, 1, 0, 1) = input[7];
514  (*this)(1, 1, 1, 1) = input[8];
515 
516  // fill in remainders from C_ijkl = C_ijlk = C_jikl
517  (*this)(0, 0, 1, 0) = (*this)(0, 0, 0, 1);
518  (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
519  (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
520  (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
521  (*this)(1, 0, 1, 1) = (*this)(0, 1, 1, 1);
522  (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
523  (*this)(1, 1, 1, 0) = (*this)(1, 1, 0, 1);
524  }
525  else if (input.size() == 2)
526  {
527  // only two independent constants, C_1111 and C_1122
528  (*this)(0, 0, 0, 0) = input[0];
529  (*this)(0, 0, 1, 1) = input[1];
530  // use symmetries
531  (*this)(1, 1, 1, 1) = (*this)(0, 0, 0, 0);
532  (*this)(1, 1, 0, 0) = (*this)(0, 0, 1, 1);
533  (*this)(0, 1, 0, 1) = 0.5 * ((*this)(0, 0, 0, 0) - (*this)(0, 0, 1, 1));
534  (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
535  (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
536  (*this)(1, 0, 1, 0) = (*this)(0, 1, 0, 1);
537  }
538  else
539  mooseError("Please provide correct number of inputs for surface RankFourTensorTempl<T> "
540  "initialization.");
541 }
542 
543 template <typename T>
544 void
545 RankFourTensorTempl<T>::fillFromInputVector(const std::vector<T> & input, FillMethod fill_method)
546 {
547 
548  switch (fill_method)
549  {
550  case antisymmetric:
551  fillAntisymmetricFromInputVector(input);
552  break;
553  case symmetric9:
554  fillSymmetric9FromInputVector(input);
555  break;
556  case symmetric21:
557  fillSymmetric21FromInputVector(input);
558  break;
559  case general_isotropic:
560  fillGeneralIsotropicFromInputVector(input);
561  break;
562  case symmetric_isotropic:
563  fillSymmetricIsotropicFromInputVector(input);
564  break;
565  case symmetric_isotropic_E_nu:
566  fillSymmetricIsotropicEandNuFromInputVector(input);
567  break;
568  case antisymmetric_isotropic:
569  fillAntisymmetricIsotropicFromInputVector(input);
570  break;
571  case axisymmetric_rz:
572  fillAxisymmetricRZFromInputVector(input);
573  break;
574  case general:
575  fillGeneralFromInputVector(input);
576  break;
577  case principal:
578  fillPrincipalFromInputVector(input);
579  break;
580  case orthotropic:
581  fillGeneralOrthotropicFromInputVector(input);
582  break;
583  default:
584  mooseError("fillFromInputVector called with unknown fill_method of ", fill_method);
585  }
586 }
587 
588 template <typename T>
589 void
591 {
592  if (input.size() != 6)
593  mooseError(
594  "To use fillAntisymmetricFromInputVector, your input must have size 6. Yours has size ",
595  input.size());
596 
597  zero();
598 
599  (*this)(0, 1, 0, 1) = input[0]; // B1212
600  (*this)(0, 1, 0, 2) = input[1]; // B1213
601  (*this)(0, 1, 1, 2) = input[2]; // B1223
602 
603  (*this)(0, 2, 0, 2) = input[3]; // B1313
604  (*this)(0, 2, 1, 2) = input[4]; // B1323
605 
606  (*this)(1, 2, 1, 2) = input[5]; // B2323
607 
608  // symmetry on the two pairs
609  (*this)(0, 2, 0, 1) = (*this)(0, 1, 0, 2);
610  (*this)(1, 2, 0, 1) = (*this)(0, 1, 1, 2);
611  (*this)(1, 2, 0, 2) = (*this)(0, 2, 1, 2);
612  // have now got the upper parts of vals[0][1], vals[0][2] and vals[1][2]
613 
614  // fill in from antisymmetry relations
615  for (auto i : make_range(N))
616  for (auto j : make_range(N))
617  {
618  (*this)(0, 1, j, i) = -(*this)(0, 1, i, j);
619  (*this)(0, 2, j, i) = -(*this)(0, 2, i, j);
620  (*this)(1, 2, j, i) = -(*this)(1, 2, i, j);
621  }
622  // have now got all of vals[0][1], vals[0][2] and vals[1][2]
623 
624  // fill in from antisymmetry relations
625  for (auto i : make_range(N))
626  for (auto j : make_range(N))
627  {
628  (*this)(1, 0, i, j) = -(*this)(0, 1, i, j);
629  (*this)(2, 0, i, j) = -(*this)(0, 2, i, j);
630  (*this)(2, 1, i, j) = -(*this)(1, 2, i, j);
631  }
632 }
633 
634 template <typename T>
635 void
637 {
638  if (input.size() != 3)
639  mooseError("To use fillGeneralIsotropicFromInputVector, your input must have size 3. Yours "
640  "has size ",
641  input.size());
642 
643  fillGeneralIsotropic(input[0], input[1], input[2]);
644 }
645 
646 template <typename T>
647 void
648 RankFourTensorTempl<T>::fillGeneralIsotropic(const T & i0, const T & i1, const T & i2)
649 {
650  for (auto i : make_range(N))
651  for (auto j : make_range(N))
652  for (auto k : make_range(N))
653  for (auto l : make_range(N))
654  {
655  (*this)(i, j, k, l) = i0 * Real(i == j) * Real(k == l) +
656  i1 * Real(i == k) * Real(j == l) + i1 * Real(i == l) * Real(j == k);
657  for (auto m : make_range(N))
658  (*this)(i, j, k, l) +=
659  i2 * Real(PermutationTensor::eps(i, j, m)) * Real(PermutationTensor::eps(k, l, m));
660  }
661 }
662 
663 template <typename T>
664 void
666 {
667  if (input.size() != 1)
668  mooseError("To use fillAntisymmetricIsotropicFromInputVector, your input must have size 1. "
669  "Yours has size ",
670  input.size());
671 
672  fillGeneralIsotropic(0.0, 0.0, input[0]);
673 }
674 
675 template <typename T>
676 void
678 {
679  fillGeneralIsotropic(0.0, 0.0, i0);
680 }
681 
682 template <typename T>
683 void
685 {
686  mooseAssert(input.size() == 2,
687  "To use fillSymmetricIsotropicFromInputVector, your input must have size 2.");
688  fillSymmetricIsotropic(input[0], input[1]);
689 }
690 
691 template <typename T>
692 void
694 {
695  // clang-format off
696  fillSymmetric21FromInputVector(std::array<T,21>
697  {{lambda + 2.0 * G, lambda, lambda, 0.0, 0.0, 0.0,
698  lambda + 2.0 * G, lambda, 0.0, 0.0, 0.0,
699  lambda + 2.0 * G, 0.0, 0.0, 0.0,
700  G, 0.0, 0.0,
701  G, 0.0,
702  G}});
703  // clang-format on
704 }
705 
706 template <typename T>
707 void
709 {
710  if (input.size() != 2)
711  mooseError(
712  "To use fillSymmetricIsotropicEandNuFromInputVector, your input must have size 2. Yours "
713  "has size ",
714  input.size());
715 
716  fillSymmetricIsotropicEandNu(input[0], input[1]);
717 }
718 
719 template <typename T>
720 void
722 {
723  // Calculate lambda and the shear modulus from the given young's modulus and poisson's ratio
724  const T & lambda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu));
725  const T & G = E / (2.0 * (1.0 + nu));
726 
727  fillSymmetricIsotropic(lambda, G);
728 }
729 
730 template <typename T>
731 void
733 {
734  mooseAssert(input.size() == 5,
735  "To use fillAxisymmetricRZFromInputVector, your input must have size 5.");
736 
737  // C1111 C1122 C1133 0 0 0
738  // C2222 C2233=C1133 0 0 0
739  // C3333 0 0 0
740  // C2323 0 0
741  // C3131=C2323 0
742  // C1212
743  // clang-format off
744  fillSymmetric21FromInputVector(std::array<T,21>
745  {{input[0],input[1],input[2], 0.0, 0.0, 0.0,
746  input[0],input[2], 0.0, 0.0, 0.0,
747  input[3], 0.0, 0.0, 0.0,
748  input[4], 0.0, 0.0,
749  input[4], 0.0,
750  (input[0] - input[1]) * 0.5}});
751  // clang-format on
752 }
753 
754 template <typename T>
755 void
757 {
758  if (input.size() != 81)
759  mooseError("To use fillGeneralFromInputVector, your input must have size 81. Yours has size ",
760  input.size());
761 
762  for (auto i : make_range(N4))
763  _vals[i] = input[i];
764 }
765 
766 template <typename T>
767 void
769 {
770  if (input.size() != 9)
771  mooseError("To use fillPrincipalFromInputVector, your input must have size 9. Yours has size ",
772  input.size());
773 
774  zero();
775 
776  (*this)(0, 0, 0, 0) = input[0];
777  (*this)(0, 0, 1, 1) = input[1];
778  (*this)(0, 0, 2, 2) = input[2];
779  (*this)(1, 1, 0, 0) = input[3];
780  (*this)(1, 1, 1, 1) = input[4];
781  (*this)(1, 1, 2, 2) = input[5];
782  (*this)(2, 2, 0, 0) = input[6];
783  (*this)(2, 2, 1, 1) = input[7];
784  (*this)(2, 2, 2, 2) = input[8];
785 }
786 
787 template <typename T>
788 void
790 {
791  if (input.size() != 12)
792  mooseError("To use fillGeneralOrhotropicFromInputVector, your input must have size 12. Yours "
793  "has size ",
794  input.size());
795 
796  const T & Ea = input[0];
797  const T & Eb = input[1];
798  const T & Ec = input[2];
799  const T & Gab = input[3];
800  const T & Gbc = input[4];
801  const T & Gca = input[5];
802  const T & nuba = input[6];
803  const T & nuca = input[7];
804  const T & nucb = input[8];
805  const T & nuab = input[9];
806  const T & nuac = input[10];
807  const T & nubc = input[11];
808 
809  // Input must satisfy constraints.
810  bool preserve_symmetry = MooseUtils::relativeFuzzyEqual(nuab * Eb, nuba * Ea) &&
811  MooseUtils::relativeFuzzyEqual(nuca * Ea, nuac * Ec) &&
812  MooseUtils::relativeFuzzyEqual(nubc * Ec, nucb * Eb);
813 
814  if (!preserve_symmetry)
815  mooseError("Orthotropic elasticity tensor input is not consistent with symmetry requirements. "
816  "Check input for accuracy");
817 
818  unsigned int ntens = N * (N + 1) / 2;
819 
820  std::vector<T> mat;
821  mat.assign(ntens * ntens, 0);
822 
823  T k = 1 - nuab * nuba - nubc * nucb - nuca * nuac - nuab * nubc * nuca - nuba * nucb * nuac;
824 
825  bool is_positive_definite =
826  (k > 0) && (1 - nubc * nucb) > 0 && (1 - nuac * nuca) > 0 && (1 - nuab * nuba) > 0;
827  if (!is_positive_definite)
828  mooseError("Orthotropic elasticity tensor input is not positive definite. Check input for "
829  "accuracy");
830 
831  mat[0] = Ea * (1 - nubc * nucb) / k;
832  mat[1] = Ea * (nubc * nuca + nuba) / k;
833  mat[2] = Ea * (nuba * nucb + nuca) / k;
834 
835  mat[6] = Eb * (nuac * nucb + nuab) / k;
836  mat[7] = Eb * (1 - nuac * nuca) / k;
837  mat[8] = Eb * (nuab * nuca + nucb) / k;
838 
839  mat[12] = Ec * (nuab * nubc + nuac) / k;
840  mat[13] = Ec * (nuac * nuba + nubc) / k;
841  mat[14] = Ec * (1 - nuab * nuba) / k;
842 
843  mat[21] = 2 * Gab;
844  mat[28] = 2 * Gca;
845  mat[35] = 2 * Gbc;
846 
847  // Switching from Voigt to fourth order tensor
848  // Copied from existing code (invSymm)
849  int nskip = N - 1;
850 
851  unsigned int index = 0;
852  for (auto i : make_range(N))
853  for (auto j : make_range(N))
854  for (auto k : make_range(N))
855  for (auto l : make_range(N))
856  {
857  if (i == j)
858  (*this)._vals[index] =
859  k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
860  else
861  (*this)._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
862  : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
863  index++;
864  }
865 }
866 
867 template <typename T>
870 {
871  RankTwoTensorTempl<T> result;
872 
873  unsigned int index = 0;
874  for (unsigned int ij = 0; ij < N2; ++ij)
875  {
876  T bb = b._coords[ij];
877  for (unsigned int kl = 0; kl < N2; ++kl)
878  result._coords[kl] += _vals[index++] * bb;
879  }
880 
881  return result;
882 }
883 
884 template <typename T>
885 T
887  unsigned int j,
888  const RankTwoTensorTempl<T> & M) const
889 {
890  T val = 0;
891  for (unsigned int k = 0; k < N; k++)
892  for (unsigned int l = 0; l < N; l++)
893  val += (*this)(i, j, k, l) * M(k, l);
894 
895  return val;
896 }
897 
898 template <typename T>
899 T
901  unsigned int l,
902  const RankTwoTensorTempl<T> & M) const
903 {
904  T val = 0;
905  for (unsigned int i = 0; i < N; i++)
906  for (unsigned int j = 0; j < N; j++)
907  val += (*this)(i, j, k, l) * M(i, j);
908 
909  return val;
910 }
911 
912 template <typename T>
913 T
915 {
916  // used in the volumetric locking correction
917  T sum = 0;
918  for (auto i : make_range(N))
919  for (auto j : make_range(N))
920  sum += (*this)(i, i, j, j);
921  return sum;
922 }
923 
924 template <typename T>
927 {
928  // used for volumetric locking correction
930  a(0) = (*this)(0, 0, 0, 0) + (*this)(0, 0, 1, 1) + (*this)(0, 0, 2, 2); // C0000 + C0011 + C0022
931  a(1) = (*this)(1, 1, 0, 0) + (*this)(1, 1, 1, 1) + (*this)(1, 1, 2, 2); // C1100 + C1111 + C1122
932  a(2) = (*this)(2, 2, 0, 0) + (*this)(2, 2, 1, 1) + (*this)(2, 2, 2, 2); // C2200 + C2211 + C2222
933  return a;
934 }
935 
936 template <typename T>
939  const RankTwoTensorTempl<T> & B,
940  const RankTwoTensorTempl<T> & C) const
941 {
943  for (unsigned int i = 0; i < N; i++)
944  for (unsigned int j = 0; j < N; j++)
945  for (unsigned int k = 0; k < N; k++)
946  for (unsigned int l = 0; l < N; l++)
947  for (unsigned int m = 0; m < N; m++)
948  for (unsigned int n = 0; n < N; n++)
949  for (unsigned int t = 0; t < N; t++)
950  R(i, j, k, l) += (*this)(i, m, n, t) * A(j, m) * B(k, n) * C(l, t);
951 
952  return R;
953 }
954 
955 template <typename T>
958  const RankTwoTensorTempl<T> & B,
959  const RankTwoTensorTempl<T> & C) const
960 {
962  for (unsigned int i = 0; i < N; i++)
963  for (unsigned int j = 0; j < N; j++)
964  for (unsigned int k = 0; k < N; k++)
965  for (unsigned int l = 0; l < N; l++)
966  for (unsigned int m = 0; m < N; m++)
967  for (unsigned int n = 0; n < N; n++)
968  for (unsigned int t = 0; t < N; t++)
969  R(i, j, k, l) += (*this)(m, j, n, t) * A(i, m) * B(k, n) * C(l, t);
970 
971  return R;
972 }
973 
974 template <typename T>
977  const RankTwoTensorTempl<T> & B,
978  const RankTwoTensorTempl<T> & C) const
979 {
981  for (unsigned int i = 0; i < N; i++)
982  for (unsigned int j = 0; j < N; j++)
983  for (unsigned int k = 0; k < N; k++)
984  for (unsigned int l = 0; l < N; l++)
985  for (unsigned int m = 0; m < N; m++)
986  for (unsigned int n = 0; n < N; n++)
987  for (unsigned int t = 0; t < N; t++)
988  R(i, j, k, l) += (*this)(m, n, k, t) * A(i, m) * B(j, n) * C(l, t);
989 
990  return R;
991 }
992 
993 template <typename T>
996  const RankTwoTensorTempl<T> & B,
997  const RankTwoTensorTempl<T> & C) const
998 {
1000  for (unsigned int i = 0; i < N; i++)
1001  for (unsigned int j = 0; j < N; j++)
1002  for (unsigned int k = 0; k < N; k++)
1003  for (unsigned int l = 0; l < N; l++)
1004  for (unsigned int m = 0; m < N; m++)
1005  for (unsigned int n = 0; n < N; n++)
1006  for (unsigned int t = 0; t < N; t++)
1007  R(i, j, k, l) += (*this)(m, n, t, l) * A(i, m) * B(j, n) * C(k, t);
1008 
1009  return R;
1010 }
1011 
1012 template <typename T>
1015 {
1017 
1018  for (unsigned int i = 0; i < N; i++)
1019  for (unsigned int j = 0; j < N; j++)
1020  for (unsigned int k = 0; k < N; k++)
1021  for (unsigned int l = 0; l < N; l++)
1022  for (unsigned int m = 0; m < N; m++)
1023  R(i, j, k, l) += (*this)(m, j, k, l) * A(i, m);
1024 
1025  return R;
1026 }
1027 
1028 template <typename T>
1031 {
1033 
1034  for (unsigned int i = 0; i < N; i++)
1035  for (unsigned int j = 0; j < N; j++)
1036  for (unsigned int k = 0; k < N; k++)
1037  for (unsigned int l = 0; l < N; l++)
1038  for (unsigned int m = 0; m < N; m++)
1039  R(i, j, k, l) += (*this)(i, m, k, l) * A(j, m);
1040 
1041  return R;
1042 }
1043 
1044 template <typename T>
1047 {
1049 
1050  for (unsigned int i = 0; i < N; i++)
1051  for (unsigned int j = 0; j < N; j++)
1052  for (unsigned int k = 0; k < N; k++)
1053  for (unsigned int l = 0; l < N; l++)
1054  for (unsigned int m = 0; m < N; m++)
1055  R(i, j, k, l) += (*this)(i, j, m, l) * A(k, m);
1056 
1057  return R;
1058 }
1059 
1060 template <typename T>
1063 {
1065 
1066  for (unsigned int i = 0; i < N; i++)
1067  for (unsigned int j = 0; j < N; j++)
1068  for (unsigned int k = 0; k < N; k++)
1069  for (unsigned int l = 0; l < N; l++)
1070  for (unsigned int m = 0; m < N; m++)
1071  R(i, j, k, l) += (*this)(i, j, k, m) * A(l, m);
1072 
1073  return R;
1074 }
1075 
1076 template <typename T>
1077 bool
1079 {
1080  for (auto i : make_range(1u, N))
1081  for (auto j : make_range(i))
1082  for (auto k : make_range(1u, N))
1083  for (auto l : make_range(k))
1084  {
1085  // minor symmetries
1086  if ((*this)(i, j, k, l) != (*this)(j, i, k, l) ||
1087  (*this)(i, j, k, l) != (*this)(i, j, l, k))
1088  return false;
1089 
1090  // major symmetry
1091  if ((*this)(i, j, k, l) != (*this)(k, l, i, j))
1092  return false;
1093  }
1094  return true;
1095 }
1096 
1097 template <typename T>
1098 bool
1100 {
1101  // prerequisite is symmetry
1102  if (!isSymmetric())
1103  return false;
1104 
1105  // inspect shear components
1106  const T & mu = (*this)(0, 1, 0, 1);
1107  // ...diagonal
1108  if ((*this)(1, 2, 1, 2) != mu || (*this)(2, 0, 2, 0) != mu)
1109  return false;
1110  // ...off-diagonal
1111  if ((*this)(2, 0, 1, 2) != 0.0 || (*this)(0, 1, 1, 2) != 0.0 || (*this)(0, 1, 2, 0) != 0.0)
1112  return false;
1113 
1114  // off diagonal blocks in Voigt
1115  for (auto i : make_range(N))
1116  for (auto j : make_range(N))
1117  if (_vals[i * (N3 + N2) + ((j + 1) % N) * N + (j + 2) % N] != 0.0)
1118  return false;
1119 
1120  // top left block
1121  const T & K1 = (*this)(0, 0, 0, 0);
1122  const T & K2 = (*this)(0, 0, 1, 1);
1123  if (!MooseUtils::relativeFuzzyEqual(K1 - 4.0 * mu / 3.0, K2 + 2.0 * mu / 3.0))
1124  return false;
1125  if ((*this)(1, 1, 1, 1) != K1 || (*this)(2, 2, 2, 2) != K1)
1126  return false;
1127  for (auto i : make_range(1u, N))
1128  for (auto j : make_range(i))
1129  if ((*this)(i, i, j, j) != K2)
1130  return false;
1131 
1132  return true;
1133 }
RankFourTensorTempl is designed to handle any N-dimensional fourth order tensor, C.
RankFourTensorTempl< T > singleProductJ(const RankTwoTensorTempl< T > &) const
Calculates C_imkl A_jm.
int eps(unsigned int i, unsigned int j)
2D version
void fillGeneralFromInputVector(const std::vector< T > &input)
fillGeneralFromInputVector takes 81 inputs to fill the Rank-4 tensor No symmetries are explicitly mai...
void fillGeneralOrthotropicFromInputVector(const std::vector< T > &input)
fillGeneralOrhotropicFromInputVector takes 10 inputs to fill the Rank-4 tensor It defines a general o...
void fillAntisymmetricFromInputVector(const std::vector< T > &input)
fillAntisymmetricFromInputVector takes 6 inputs to fill the the antisymmetric Rank-4 tensor with the ...
RankFourTensorTempl< T > singleProductL(const RankTwoTensorTempl< T > &) const
Calculates C_ijkm A_lm.
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323
RankFourTensorTempl< T > tripleProductIjl(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_mnkt A_im B_jn C_lt.
RankFourTensorTempl< T > & operator*=(const T &a)
C_ijkl *= a.
RankTwoTensorTempl< T > innerProductTranspose(const RankTwoTensorTempl< T > &) const
Inner product of the major transposed tensor with a rank two tensor.
auto raw_value(const Eigen::Map< T > &in)
Definition: EigenADReal.h:100
void fillPrincipalFromInputVector(const std::vector< T > &input)
fillPrincipalFromInputVector takes 9 inputs to fill a Rank-4 tensor C1111 = input0 C1122 = input1 C11...
void inverse(const std::vector< std::vector< Real >> &m, std::vector< std::vector< Real >> &m_inv)
Inverse the dense square matrix m using LAPACK routines.
Definition: MatrixTools.C:23
void fillFromInputVector(const std::vector< T > &input, FillMethod fill_method)
fillFromInputVector takes some number of inputs to fill the Rank-4 tensor.
const Number zero
T _coords[LIBMESH_DIM *LIBMESH_DIM]
void surfaceFillFromInputVector(const std::vector< T > &input)
Fills the tensor entries ignoring the last dimension (ie, C_ijkl=0 if any of i, j, k, or l = 3).
T contractionIj(unsigned int, unsigned int, const RankTwoTensorTempl< T > &) const
Sum C_ijkl M_kl for a given i,j.
void print(std::ostream &stm=Moose::out) const
Print the rank four tensor.
RankFourTensorTempl< T > & operator/=(const T &a)
C_ijkl /= a for all i, j, k, l.
auto operator*(const Tensor< T2 > &a) const -> typename std::enable_if< TwoTensorMultTraits< Tensor, T2 >::value, RankTwoTensorTempl< decltype(T() *T2())>>::type
C_ijkl*a_kl.
RankFourTensorTempl< T > tripleProductJkl(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_imnt A_jm B_kn C_lt.
RankFourTensorTempl< T > tripleProductIjk(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_mntl A_im B_jn C_kt.
void fillSymmetricIsotropic(const T &i0, const T &i1)
auto operator+(const RankFourTensorTempl< T2 > &a) const -> RankFourTensorTempl< decltype(T()+T2())>
C_ijkl + a_ijkl.
void fillGeneralIsotropicFromInputVector(const std::vector< T > &input)
fillGeneralIsotropicFromInputVector takes 3 inputs to fill the Rank-4 tensor with symmetries C_ijkl =...
void zero()
Zeros out the tensor.
FillMethod
To fill up the 81 entries in the 4th-order tensor, fillFromInputVector is called with one of the foll...
T L2norm() const
sqrt(C_ijkl*C_ijkl)
void fillSymmetricIsotropicEandNu(const T &E, const T &nu)
bool isSymmetric() const
checks if the tensor is symmetric
void fillGeneralIsotropic(const T &i0, const T &i1, const T &i2)
Vector-less fill API functions. See docs of the corresponding ...FromInputVector methods.
RankFourTensorTempl()
Default constructor; fills to zero.
void init(triangulateio &t)
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
void printReal(std::ostream &stm=Moose::out) const
Print the values of the rank four tensor.
T sum3x3() const
Calculates the sum of Ciijj for i and j varying from 0 to 2.
RankFourTensorTempl< T > transposeMajor() const
Transpose the tensor by swapping the first pair with the second pair of indices.
bool isIsotropic() const
checks if the tensor is isotropic
T _vals[N4]
The values of the rank-four tensor stored by index=(((i * LIBMESH_DIM + j) * LIBMESH_DIM + k) * LIBME...
void fillAxisymmetricRZFromInputVector(const std::vector< T > &input)
fillAxisymmetricRZFromInputVector takes 5 inputs to fill the axisymmetric Rank-4 tensor with the appr...
void rotate(const TypeTensor< T > &R)
Rotate the tensor using C_ijkl = R_im R_jn R_ko R_lp C_mnop.
void fillSymmetricIsotropicEandNuFromInputVector(const std::vector< T > &input)
fillSymmetricIsotropicEandNuFromInputVector is a variation of the fillSymmetricIsotropicFromInputVect...
libMesh::VectorValue< T > sum3x1() const
Calculates the vector a[i] = sum over j Ciijj for i and j varying from 0 to 2.
T contractionKl(unsigned int, unsigned int, const RankTwoTensorTempl< T > &) const
Sum M_ij C_ijkl for a given k,l.
RankFourTensorTempl< T > transposeIj() const
Transpose the tensor by swapping the first two indeces.
void fillSymmetricIsotropicFromInputVector(const std::vector< T > &input)
fillSymmetricIsotropicFromInputVector takes 2 inputs to fill the the symmetric Rank-4 tensor with the...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
NumberTensorValue Tensor
RankFourTensorTempl< T > singleProductI(const RankTwoTensorTempl< T > &) const
Calculates C_mjkl A_im.
RankTwoTensorTempl is designed to handle the Stress or Strain Tensor for a fully anisotropic material...
Definition: RankTwoTensor.h:87
InitMethod
Initialization method.
RankFourTensorTempl< T > operator-() const
-C_ijkl
IntRange< T > make_range(T beg, T end)
void fillAntisymmetricIsotropicFromInputVector(const std::vector< T > &input)
fillAntisymmetricIsotropicFromInputVector takes 1 input to fill the the antisymmetric Rank-4 tensor w...
static MooseEnum fillMethodEnum()
Static method for use in validParams for getting the "fill_method".
RankFourTensorTempl< T > singleProductK(const RankTwoTensorTempl< T > &) const
Calculates C_ijml A_km.
RankFourTensorTempl< T > & operator+=(const RankFourTensorTempl< T > &a)
C_ijkl += a_ijkl for all i, j, k, l.
void fillAntisymmetricIsotropic(const T &i0)
RankFourTensorTempl< T > tripleProductIkl(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_mjnt A_im B_kn C_lt.
RankFourTensorTempl< T > transposeKl() const
Transpose the tensor by swapping the last two indeces.
RankFourTensorTempl< T > invSymm() const
This returns A_ijkl such that C_ijkl*A_klmn = 0.5*(de_im de_jn + de_in de_jm) This routine assumes th...
RankFourTensorTempl< T > & operator-=(const RankFourTensorTempl< T > &a)
C_ijkl -= a_ijkl.
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template pow< 2 >(tan(_arg))+1.0) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(sqrt