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  return std::sqrt(l2);
245 }
246 
247 template <typename T>
250 {
251  mooseError("The invSymm operation calls to LAPACK and only supports plain Real type tensors.");
252 }
253 
254 template <>
257 {
258  unsigned int ntens = N * (N + 1) / 2;
259  int nskip = N - 1;
260 
262  std::vector<PetscScalar> mat;
263  mat.assign(ntens * ntens, 0);
264 
265  // We use the LAPACK matrix inversion routine here. Form the matrix
266  //
267  // mat[0] mat[1] mat[2] mat[3] mat[4] mat[5]
268  // mat[6] mat[7] mat[8] mat[9] mat[10] mat[11]
269  // mat[12] mat[13] mat[14] mat[15] mat[16] mat[17]
270  // mat[18] mat[19] mat[20] mat[21] mat[22] mat[23]
271  // mat[24] mat[25] mat[26] mat[27] mat[28] mat[29]
272  // mat[30] mat[31] mat[32] mat[33] mat[34] mat[35]
273  //
274  // This is filled from the indpendent components of C assuming
275  // the symmetry C_ijkl = C_ijlk = C_jikl.
276  //
277  // If there are two rank-four tensors X and Y then the reason for
278  // this filling becomes apparent if we want to calculate
279  // X_ijkl*Y_klmn = Z_ijmn
280  // For denote the "mat" versions of X, Y and Z by x, y and z.
281  // Then
282  // z_ab = x_ac*y_cb
283  // Eg
284  // z_00 = Z_0000 = X_0000*Y_0000 + X_0011*Y_1111 + X_0022*Y_2200 + 2*X_0001*Y_0100 +
285  // 2*X_0002*Y_0200 + 2*X_0012*Y_1200 (the factors of 2 come from the assumed symmetries)
286  // 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 +
287  // 2*X_0002*2*Y_0201 + 2*X_0012*2*Y_1201
288  // 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 +
289  // 2*X_0102*2*Y_0202 + 2*X_0112*2*Y_1202
290  // Finally, we use LAPACK to find x^-1, and put it back into rank-4 tensor form
291  //
292  // mat[0] = C(0,0,0,0)
293  // mat[1] = C(0,0,1,1)
294  // mat[2] = C(0,0,2,2)
295  // mat[3] = C(0,0,0,1)*2
296  // mat[4] = C(0,0,0,2)*2
297  // mat[5] = C(0,0,1,2)*2
298 
299  // mat[6] = C(1,1,0,0)
300  // mat[7] = C(1,1,1,1)
301  // mat[8] = C(1,1,2,2)
302  // mat[9] = C(1,1,0,1)*2
303  // mat[10] = C(1,1,0,2)*2
304  // mat[11] = C(1,1,1,2)*2
305 
306  // mat[12] = C(2,2,0,0)
307  // mat[13] = C(2,2,1,1)
308  // mat[14] = C(2,2,2,2)
309  // mat[15] = C(2,2,0,1)*2
310  // mat[16] = C(2,2,0,2)*2
311  // mat[17] = C(2,2,1,2)*2
312 
313  // mat[18] = C(0,1,0,0)
314  // mat[19] = C(0,1,1,1)
315  // mat[20] = C(0,1,2,2)
316  // mat[21] = C(0,1,0,1)*2
317  // mat[22] = C(0,1,0,2)*2
318  // mat[23] = C(0,1,1,2)*2
319 
320  // mat[24] = C(0,2,0,0)
321  // mat[25] = C(0,2,1,1)
322  // mat[26] = C(0,2,2,2)
323  // mat[27] = C(0,2,0,1)*2
324  // mat[28] = C(0,2,0,2)*2
325  // mat[29] = C(0,2,1,2)*2
326 
327  // mat[30] = C(1,2,0,0)
328  // mat[31] = C(1,2,1,1)
329  // mat[32] = C(1,2,2,2)
330  // mat[33] = C(1,2,0,1)*2
331  // mat[34] = C(1,2,0,2)*2
332  // mat[35] = C(1,2,1,2)*2
333 
334  unsigned int index = 0;
335  for (auto i : make_range(N))
336  for (auto j : make_range(N))
337  for (auto k : make_range(N))
338  for (auto l : make_range(N))
339  {
340  if (i == j)
341  mat[k == l ? i * ntens + k : i * ntens + k + nskip + l] += _vals[index];
342  else
343  // i!=j
344  mat[k == l ? (nskip + i + j) * ntens + k : (nskip + i + j) * ntens + k + nskip + l] +=
345  _vals[index]; // note the +=, which results in double-counting and is rectified
346  // below
347  index++;
348  }
349 
350  for (unsigned int i = 3; i < ntens; ++i)
351  for (auto j : make_range(ntens))
352  mat[i * ntens + j] /= 2.0; // because of double-counting above
353 
354  // use LAPACK to find the inverse
355  MatrixTools::inverse(mat, ntens);
356 
357  // build the resulting rank-four tensor
358  // using the inverse of the above algorithm
359  index = 0;
360  for (auto i : make_range(N))
361  for (auto j : make_range(N))
362  for (auto k : make_range(N))
363  for (auto l : make_range(N))
364  {
365  if (i == j)
366  result._vals[index] =
367  k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
368  else
369  // i!=j
370  result._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
371  : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
372  index++;
373  }
374 
375  return result;
376 }
377 
378 template <typename T>
379 void
380 RankFourTensorTempl<T>::rotate(const TypeTensor<T> & R)
381 {
382  RankFourTensorTempl<T> old = *this;
383 
384  unsigned int index = 0;
385  for (auto i : make_range(N))
386  for (auto j : make_range(N))
387  for (auto k : make_range(N))
388  for (auto l : make_range(N))
389  {
390  unsigned int index2 = 0;
391  T sum = 0.0;
392  for (auto m : make_range(N))
393  {
394  const T & a = R(i, m);
395  for (auto n : make_range(N))
396  {
397  const T & ab = a * R(j, n);
398  for (auto o : make_range(N))
399  {
400  const T & abc = ab * R(k, o);
401  for (auto p : make_range(N))
402  sum += abc * R(l, p) * old._vals[index2++];
403  }
404  }
405  }
406  _vals[index++] = sum;
407  }
408 }
409 
410 template <typename T>
411 void
412 RankFourTensorTempl<T>::print(std::ostream & stm) const
413 {
414  for (auto i : make_range(N))
415  for (auto j : make_range(N))
416  {
417  stm << "i = " << i << " j = " << j << '\n';
418  for (auto k : make_range(N))
419  {
420  for (auto l : make_range(N))
421  stm << std::setw(15) << (*this)(i, j, k, l) << " ";
422 
423  stm << '\n';
424  }
425  }
426 
427  stm << std::flush;
428 }
429 
430 template <typename T>
431 void
432 RankFourTensorTempl<T>::printReal(std::ostream & stm) const
433 {
434  for (unsigned int i = 0; i < N; ++i)
435  for (unsigned int j = 0; j < N; ++j)
436  {
437  stm << "i = " << i << " j = " << j << '\n';
438  for (unsigned int k = 0; k < N; ++k)
439  {
440  for (unsigned int l = 0; l < N; ++l)
441  stm << std::setw(15) << MetaPhysicL::raw_value((*this)(i, j, k, l)) << " ";
442 
443  stm << '\n';
444  }
445  }
446 
447  stm << std::flush;
448 }
449 
450 template <typename T>
453 {
454  RankFourTensorTempl<T> result;
455 
456  unsigned int index = 0;
457  for (auto i : make_range(N))
458  for (auto j : make_range(N))
459  for (auto k : make_range(N))
460  for (auto l : make_range(N))
461  result._vals[index++] = _vals[k * N3 + i * N + j + l * N2];
462 
463  return result;
464 }
465 
466 template <typename T>
469 {
470  RankFourTensorTempl<T> result;
471 
472  for (auto i : make_range(N))
473  for (auto j : make_range(N))
474  for (auto k : make_range(N))
475  for (auto l : make_range(N))
476  result(i, j, k, l) = (*this)(j, i, k, l);
477 
478  return result;
479 }
480 
481 template <typename T>
484 {
485  RankFourTensorTempl<T> result;
486 
487  for (auto i : make_range(N))
488  for (auto j : make_range(N))
489  for (auto k : make_range(N))
490  for (auto l : make_range(N))
491  result(i, j, k, l) = (*this)(i, j, l, k);
492 
493  return result;
494 }
495 
496 template <typename T>
497 void
499 {
500  zero();
501 
502  if (input.size() == 9)
503  {
504  // then fill from vector C_1111, C_1112, C_1122, C_1212, C_1222, C_1211, C_2211, C_2212, C_2222
505  (*this)(0, 0, 0, 0) = input[0];
506  (*this)(0, 0, 0, 1) = input[1];
507  (*this)(0, 0, 1, 1) = input[2];
508  (*this)(0, 1, 0, 1) = input[3];
509  (*this)(0, 1, 1, 1) = input[4];
510  (*this)(0, 1, 0, 0) = input[5];
511  (*this)(1, 1, 0, 0) = input[6];
512  (*this)(1, 1, 0, 1) = input[7];
513  (*this)(1, 1, 1, 1) = input[8];
514 
515  // fill in remainders from C_ijkl = C_ijlk = C_jikl
516  (*this)(0, 0, 1, 0) = (*this)(0, 0, 0, 1);
517  (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
518  (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
519  (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
520  (*this)(1, 0, 1, 1) = (*this)(0, 1, 1, 1);
521  (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
522  (*this)(1, 1, 1, 0) = (*this)(1, 1, 0, 1);
523  }
524  else if (input.size() == 2)
525  {
526  // only two independent constants, C_1111 and C_1122
527  (*this)(0, 0, 0, 0) = input[0];
528  (*this)(0, 0, 1, 1) = input[1];
529  // use symmetries
530  (*this)(1, 1, 1, 1) = (*this)(0, 0, 0, 0);
531  (*this)(1, 1, 0, 0) = (*this)(0, 0, 1, 1);
532  (*this)(0, 1, 0, 1) = 0.5 * ((*this)(0, 0, 0, 0) - (*this)(0, 0, 1, 1));
533  (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
534  (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
535  (*this)(1, 0, 1, 0) = (*this)(0, 1, 0, 1);
536  }
537  else
538  mooseError("Please provide correct number of inputs for surface RankFourTensorTempl<T> "
539  "initialization.");
540 }
541 
542 template <typename T>
543 void
544 RankFourTensorTempl<T>::fillFromInputVector(const std::vector<T> & input, FillMethod fill_method)
545 {
546 
547  switch (fill_method)
548  {
549  case antisymmetric:
550  fillAntisymmetricFromInputVector(input);
551  break;
552  case symmetric9:
553  fillSymmetric9FromInputVector(input);
554  break;
555  case symmetric21:
556  fillSymmetric21FromInputVector(input);
557  break;
558  case general_isotropic:
559  fillGeneralIsotropicFromInputVector(input);
560  break;
561  case symmetric_isotropic:
562  fillSymmetricIsotropicFromInputVector(input);
563  break;
564  case symmetric_isotropic_E_nu:
565  fillSymmetricIsotropicEandNuFromInputVector(input);
566  break;
567  case antisymmetric_isotropic:
568  fillAntisymmetricIsotropicFromInputVector(input);
569  break;
570  case axisymmetric_rz:
571  fillAxisymmetricRZFromInputVector(input);
572  break;
573  case general:
574  fillGeneralFromInputVector(input);
575  break;
576  case principal:
577  fillPrincipalFromInputVector(input);
578  break;
579  case orthotropic:
580  fillGeneralOrthotropicFromInputVector(input);
581  break;
582  default:
583  mooseError("fillFromInputVector called with unknown fill_method of ", fill_method);
584  }
585 }
586 
587 template <typename T>
588 void
590 {
591  if (input.size() != 6)
592  mooseError(
593  "To use fillAntisymmetricFromInputVector, your input must have size 6. Yours has size ",
594  input.size());
595 
596  zero();
597 
598  (*this)(0, 1, 0, 1) = input[0]; // B1212
599  (*this)(0, 1, 0, 2) = input[1]; // B1213
600  (*this)(0, 1, 1, 2) = input[2]; // B1223
601 
602  (*this)(0, 2, 0, 2) = input[3]; // B1313
603  (*this)(0, 2, 1, 2) = input[4]; // B1323
604 
605  (*this)(1, 2, 1, 2) = input[5]; // B2323
606 
607  // symmetry on the two pairs
608  (*this)(0, 2, 0, 1) = (*this)(0, 1, 0, 2);
609  (*this)(1, 2, 0, 1) = (*this)(0, 1, 1, 2);
610  (*this)(1, 2, 0, 2) = (*this)(0, 2, 1, 2);
611  // have now got the upper parts of vals[0][1], vals[0][2] and vals[1][2]
612 
613  // fill in from antisymmetry relations
614  for (auto i : make_range(N))
615  for (auto j : make_range(N))
616  {
617  (*this)(0, 1, j, i) = -(*this)(0, 1, i, j);
618  (*this)(0, 2, j, i) = -(*this)(0, 2, i, j);
619  (*this)(1, 2, j, i) = -(*this)(1, 2, i, j);
620  }
621  // have now got all of vals[0][1], vals[0][2] and vals[1][2]
622 
623  // fill in from antisymmetry relations
624  for (auto i : make_range(N))
625  for (auto j : make_range(N))
626  {
627  (*this)(1, 0, i, j) = -(*this)(0, 1, i, j);
628  (*this)(2, 0, i, j) = -(*this)(0, 2, i, j);
629  (*this)(2, 1, i, j) = -(*this)(1, 2, i, j);
630  }
631 }
632 
633 template <typename T>
634 void
636 {
637  if (input.size() != 3)
638  mooseError("To use fillGeneralIsotropicFromInputVector, your input must have size 3. Yours "
639  "has size ",
640  input.size());
641 
642  fillGeneralIsotropic(input[0], input[1], input[2]);
643 }
644 
645 template <typename T>
646 void
647 RankFourTensorTempl<T>::fillGeneralIsotropic(const T & i0, const T & i1, const T & i2)
648 {
649  for (auto i : make_range(N))
650  for (auto j : make_range(N))
651  for (auto k : make_range(N))
652  for (auto l : make_range(N))
653  {
654  (*this)(i, j, k, l) = i0 * Real(i == j) * Real(k == l) +
655  i1 * Real(i == k) * Real(j == l) + i1 * Real(i == l) * Real(j == k);
656  for (auto m : make_range(N))
657  (*this)(i, j, k, l) +=
658  i2 * Real(PermutationTensor::eps(i, j, m)) * Real(PermutationTensor::eps(k, l, m));
659  }
660 }
661 
662 template <typename T>
663 void
665 {
666  if (input.size() != 1)
667  mooseError("To use fillAntisymmetricIsotropicFromInputVector, your input must have size 1. "
668  "Yours has size ",
669  input.size());
670 
671  fillGeneralIsotropic(0.0, 0.0, input[0]);
672 }
673 
674 template <typename T>
675 void
677 {
678  fillGeneralIsotropic(0.0, 0.0, i0);
679 }
680 
681 template <typename T>
682 void
684 {
685  mooseAssert(input.size() == 2,
686  "To use fillSymmetricIsotropicFromInputVector, your input must have size 2.");
687  fillSymmetricIsotropic(input[0], input[1]);
688 }
689 
690 template <typename T>
691 void
693 {
694  // clang-format off
695  fillSymmetric21FromInputVector(std::array<T,21>
696  {{lambda + 2.0 * G, lambda, lambda, 0.0, 0.0, 0.0,
697  lambda + 2.0 * G, lambda, 0.0, 0.0, 0.0,
698  lambda + 2.0 * G, 0.0, 0.0, 0.0,
699  G, 0.0, 0.0,
700  G, 0.0,
701  G}});
702  // clang-format on
703 }
704 
705 template <typename T>
706 void
708 {
709  if (input.size() != 2)
710  mooseError(
711  "To use fillSymmetricIsotropicEandNuFromInputVector, your input must have size 2. Yours "
712  "has size ",
713  input.size());
714 
715  fillSymmetricIsotropicEandNu(input[0], input[1]);
716 }
717 
718 template <typename T>
719 void
721 {
722  // Calculate lambda and the shear modulus from the given young's modulus and poisson's ratio
723  const T & lambda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu));
724  const T & G = E / (2.0 * (1.0 + nu));
725 
726  fillSymmetricIsotropic(lambda, G);
727 }
728 
729 template <typename T>
730 void
732 {
733  mooseAssert(input.size() == 5,
734  "To use fillAxisymmetricRZFromInputVector, your input must have size 5.");
735 
736  // C1111 C1122 C1133 0 0 0
737  // C2222 C2233=C1133 0 0 0
738  // C3333 0 0 0
739  // C2323 0 0
740  // C3131=C2323 0
741  // C1212
742  // clang-format off
743  fillSymmetric21FromInputVector(std::array<T,21>
744  {{input[0],input[1],input[2], 0.0, 0.0, 0.0,
745  input[0],input[2], 0.0, 0.0, 0.0,
746  input[3], 0.0, 0.0, 0.0,
747  input[4], 0.0, 0.0,
748  input[4], 0.0,
749  (input[0] - input[1]) * 0.5}});
750  // clang-format on
751 }
752 
753 template <typename T>
754 void
756 {
757  if (input.size() != 81)
758  mooseError("To use fillGeneralFromInputVector, your input must have size 81. Yours has size ",
759  input.size());
760 
761  for (auto i : make_range(N4))
762  _vals[i] = input[i];
763 }
764 
765 template <typename T>
766 void
768 {
769  if (input.size() != 9)
770  mooseError("To use fillPrincipalFromInputVector, your input must have size 9. Yours has size ",
771  input.size());
772 
773  zero();
774 
775  (*this)(0, 0, 0, 0) = input[0];
776  (*this)(0, 0, 1, 1) = input[1];
777  (*this)(0, 0, 2, 2) = input[2];
778  (*this)(1, 1, 0, 0) = input[3];
779  (*this)(1, 1, 1, 1) = input[4];
780  (*this)(1, 1, 2, 2) = input[5];
781  (*this)(2, 2, 0, 0) = input[6];
782  (*this)(2, 2, 1, 1) = input[7];
783  (*this)(2, 2, 2, 2) = input[8];
784 }
785 
786 template <typename T>
787 void
789 {
790  if (input.size() != 12)
791  mooseError("To use fillGeneralOrhotropicFromInputVector, your input must have size 12. Yours "
792  "has size ",
793  input.size());
794 
795  const T & Ea = input[0];
796  const T & Eb = input[1];
797  const T & Ec = input[2];
798  const T & Gab = input[3];
799  const T & Gbc = input[4];
800  const T & Gca = input[5];
801  const T & nuba = input[6];
802  const T & nuca = input[7];
803  const T & nucb = input[8];
804  const T & nuab = input[9];
805  const T & nuac = input[10];
806  const T & nubc = input[11];
807 
808  // Input must satisfy constraints.
809  bool preserve_symmetry = MooseUtils::absoluteFuzzyEqual(nuab * Eb, nuba * Ea) &&
810  MooseUtils::absoluteFuzzyEqual(nuca * Ea, nuac * Ec) &&
811  MooseUtils::absoluteFuzzyEqual(nubc * Ec, nucb * Eb);
812 
813  if (!preserve_symmetry)
814  mooseError("Orthotropic elasticity tensor input is not consistent with symmetry requirements. "
815  "Check input for accuracy");
816 
817  unsigned int ntens = N * (N + 1) / 2;
818 
819  std::vector<T> mat;
820  mat.assign(ntens * ntens, 0);
821 
822  T k = 1 - nuab * nuba - nubc * nucb - nuca * nuac - nuab * nubc * nuca - nuba * nucb * nuac;
823 
824  bool is_positive_definite =
825  (k > 0) && (1 - nubc * nucb) > 0 && (1 - nuac * nuca) > 0 && (1 - nuab * nuba) > 0;
826  if (!is_positive_definite)
827  mooseError("Orthotropic elasticity tensor input is not positive definite. Check input for "
828  "accuracy");
829 
830  mat[0] = Ea * (1 - nubc * nucb) / k;
831  mat[1] = Ea * (nubc * nuca + nuba) / k;
832  mat[2] = Ea * (nuba * nucb + nuca) / k;
833 
834  mat[6] = Eb * (nuac * nucb + nuab) / k;
835  mat[7] = Eb * (1 - nuac * nuca) / k;
836  mat[8] = Eb * (nuab * nuca + nucb) / k;
837 
838  mat[12] = Ec * (nuab * nubc + nuac) / k;
839  mat[13] = Ec * (nuac * nuba + nubc) / k;
840  mat[14] = Ec * (1 - nuab * nuba) / k;
841 
842  mat[21] = 2 * Gab;
843  mat[28] = 2 * Gca;
844  mat[35] = 2 * Gbc;
845 
846  // Switching from Voigt to fourth order tensor
847  // Copied from existing code (invSymm)
848  int nskip = N - 1;
849 
850  unsigned int index = 0;
851  for (auto i : make_range(N))
852  for (auto j : make_range(N))
853  for (auto k : make_range(N))
854  for (auto l : make_range(N))
855  {
856  if (i == j)
857  (*this)._vals[index] =
858  k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
859  else
860  (*this)._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
861  : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
862  index++;
863  }
864 }
865 
866 template <typename T>
869 {
870  RankTwoTensorTempl<T> result;
871 
872  unsigned int index = 0;
873  for (unsigned int ij = 0; ij < N2; ++ij)
874  {
875  T bb = b._coords[ij];
876  for (unsigned int kl = 0; kl < N2; ++kl)
877  result._coords[kl] += _vals[index++] * bb;
878  }
879 
880  return result;
881 }
882 
883 template <typename T>
884 T
886  unsigned int j,
887  const RankTwoTensorTempl<T> & M) const
888 {
889  T val = 0;
890  for (unsigned int k = 0; k < N; k++)
891  for (unsigned int l = 0; l < N; l++)
892  val += (*this)(i, j, k, l) * M(k, l);
893 
894  return val;
895 }
896 
897 template <typename T>
898 T
900  unsigned int l,
901  const RankTwoTensorTempl<T> & M) const
902 {
903  T val = 0;
904  for (unsigned int i = 0; i < N; i++)
905  for (unsigned int j = 0; j < N; j++)
906  val += (*this)(i, j, k, l) * M(i, j);
907 
908  return val;
909 }
910 
911 template <typename T>
912 T
914 {
915  // used in the volumetric locking correction
916  T sum = 0;
917  for (auto i : make_range(N))
918  for (auto j : make_range(N))
919  sum += (*this)(i, i, j, j);
920  return sum;
921 }
922 
923 template <typename T>
926 {
927  // used for volumetric locking correction
929  a(0) = (*this)(0, 0, 0, 0) + (*this)(0, 0, 1, 1) + (*this)(0, 0, 2, 2); // C0000 + C0011 + C0022
930  a(1) = (*this)(1, 1, 0, 0) + (*this)(1, 1, 1, 1) + (*this)(1, 1, 2, 2); // C1100 + C1111 + C1122
931  a(2) = (*this)(2, 2, 0, 0) + (*this)(2, 2, 1, 1) + (*this)(2, 2, 2, 2); // C2200 + C2211 + C2222
932  return a;
933 }
934 
935 template <typename T>
938  const RankTwoTensorTempl<T> & B,
939  const RankTwoTensorTempl<T> & C) const
940 {
942  for (unsigned int i = 0; i < N; i++)
943  for (unsigned int j = 0; j < N; j++)
944  for (unsigned int k = 0; k < N; k++)
945  for (unsigned int l = 0; l < N; l++)
946  for (unsigned int m = 0; m < N; m++)
947  for (unsigned int n = 0; n < N; n++)
948  for (unsigned int t = 0; t < N; t++)
949  R(i, j, k, l) += (*this)(i, m, n, t) * A(j, m) * B(k, n) * C(l, t);
950 
951  return R;
952 }
953 
954 template <typename T>
957  const RankTwoTensorTempl<T> & B,
958  const RankTwoTensorTempl<T> & C) const
959 {
961  for (unsigned int i = 0; i < N; i++)
962  for (unsigned int j = 0; j < N; j++)
963  for (unsigned int k = 0; k < N; k++)
964  for (unsigned int l = 0; l < N; l++)
965  for (unsigned int m = 0; m < N; m++)
966  for (unsigned int n = 0; n < N; n++)
967  for (unsigned int t = 0; t < N; t++)
968  R(i, j, k, l) += (*this)(m, j, n, t) * A(i, m) * B(k, n) * C(l, t);
969 
970  return R;
971 }
972 
973 template <typename T>
976  const RankTwoTensorTempl<T> & B,
977  const RankTwoTensorTempl<T> & C) const
978 {
980  for (unsigned int i = 0; i < N; i++)
981  for (unsigned int j = 0; j < N; j++)
982  for (unsigned int k = 0; k < N; k++)
983  for (unsigned int l = 0; l < N; l++)
984  for (unsigned int m = 0; m < N; m++)
985  for (unsigned int n = 0; n < N; n++)
986  for (unsigned int t = 0; t < N; t++)
987  R(i, j, k, l) += (*this)(m, n, k, t) * A(i, m) * B(j, n) * C(l, t);
988 
989  return R;
990 }
991 
992 template <typename T>
995  const RankTwoTensorTempl<T> & B,
996  const RankTwoTensorTempl<T> & C) const
997 {
999  for (unsigned int i = 0; i < N; i++)
1000  for (unsigned int j = 0; j < N; j++)
1001  for (unsigned int k = 0; k < N; k++)
1002  for (unsigned int l = 0; l < N; l++)
1003  for (unsigned int m = 0; m < N; m++)
1004  for (unsigned int n = 0; n < N; n++)
1005  for (unsigned int t = 0; t < N; t++)
1006  R(i, j, k, l) += (*this)(m, n, t, l) * A(i, m) * B(j, n) * C(k, t);
1007 
1008  return R;
1009 }
1010 
1011 template <typename T>
1014 {
1016 
1017  for (unsigned int i = 0; i < N; i++)
1018  for (unsigned int j = 0; j < N; j++)
1019  for (unsigned int k = 0; k < N; k++)
1020  for (unsigned int l = 0; l < N; l++)
1021  for (unsigned int m = 0; m < N; m++)
1022  R(i, j, k, l) += (*this)(m, j, k, l) * A(i, m);
1023 
1024  return R;
1025 }
1026 
1027 template <typename T>
1030 {
1032 
1033  for (unsigned int i = 0; i < N; i++)
1034  for (unsigned int j = 0; j < N; j++)
1035  for (unsigned int k = 0; k < N; k++)
1036  for (unsigned int l = 0; l < N; l++)
1037  for (unsigned int m = 0; m < N; m++)
1038  R(i, j, k, l) += (*this)(i, m, k, l) * A(j, m);
1039 
1040  return R;
1041 }
1042 
1043 template <typename T>
1046 {
1048 
1049  for (unsigned int i = 0; i < N; i++)
1050  for (unsigned int j = 0; j < N; j++)
1051  for (unsigned int k = 0; k < N; k++)
1052  for (unsigned int l = 0; l < N; l++)
1053  for (unsigned int m = 0; m < N; m++)
1054  R(i, j, k, l) += (*this)(i, j, m, l) * A(k, m);
1055 
1056  return R;
1057 }
1058 
1059 template <typename T>
1062 {
1064 
1065  for (unsigned int i = 0; i < N; i++)
1066  for (unsigned int j = 0; j < N; j++)
1067  for (unsigned int k = 0; k < N; k++)
1068  for (unsigned int l = 0; l < N; l++)
1069  for (unsigned int m = 0; m < N; m++)
1070  R(i, j, k, l) += (*this)(i, j, k, m) * A(l, m);
1071 
1072  return R;
1073 }
1074 
1075 template <typename T>
1076 bool
1078 {
1079  for (auto i : make_range(1u, N))
1080  for (auto j : make_range(i))
1081  for (auto k : make_range(1u, N))
1082  for (auto l : make_range(k))
1083  {
1084  // minor symmetries
1085  if ((*this)(i, j, k, l) != (*this)(j, i, k, l) ||
1086  (*this)(i, j, k, l) != (*this)(i, j, l, k))
1087  return false;
1088 
1089  // major symmetry
1090  if ((*this)(i, j, k, l) != (*this)(k, l, i, j))
1091  return false;
1092  }
1093  return true;
1094 }
1095 
1096 template <typename T>
1097 bool
1099 {
1100  // prerequisite is symmetry
1101  if (!isSymmetric())
1102  return false;
1103 
1104  // inspect shear components
1105  const T & mu = (*this)(0, 1, 0, 1);
1106  // ...diagonal
1107  if ((*this)(1, 2, 1, 2) != mu || (*this)(2, 0, 2, 0) != mu)
1108  return false;
1109  // ...off-diagonal
1110  if ((*this)(2, 0, 1, 2) != 0.0 || (*this)(0, 1, 1, 2) != 0.0 || (*this)(0, 1, 2, 0) != 0.0)
1111  return false;
1112 
1113  // off diagonal blocks in Voigt
1114  for (auto i : make_range(N))
1115  for (auto j : make_range(N))
1116  if (_vals[i * (N3 + N2) + ((j + 1) % N) * N + (j + 2) % N] != 0.0)
1117  return false;
1118 
1119  // top left block
1120  const T & K1 = (*this)(0, 0, 0, 0);
1121  const T & K2 = (*this)(0, 0, 1, 1);
1122  if (!MooseUtils::relativeFuzzyEqual(K1 - 4.0 * mu / 3.0, K2 + 2.0 * mu / 3.0))
1123  return false;
1124  if ((*this)(1, 1, 1, 1) != K1 || (*this)(2, 2, 2, 2) != K1)
1125  return false;
1126  for (auto i : make_range(1u, N))
1127  for (auto j : make_range(i))
1128  if ((*this)(i, i, j, j) != K2)
1129  return false;
1130 
1131  return true;
1132 }
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...
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
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:302
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:73
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
bool relativeFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within a relative tolerance.
Definition: MooseUtils.h:492
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