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  if constexpr (std::is_same_v<T, Real> && std::is_same_v<T2, Real>)
134  {
135  // result_ij = A_ijkl b_kl: an (N2 x N2) . (N2) matvec over the flat storage. Gather b into a
136  // contiguous vector once (b's storage type is generic here), then use Eigen's kernel -- the
137  // scalar loop below re-derives b's index with a div/mod on every inner iteration.
138  Eigen::Matrix<Real, N2, 1> bvec;
139  for (unsigned int kl = 0; kl < N2; ++kl)
140  bvec(kl) = b(kl / N, kl % N);
141  const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> a(_vals);
142  Eigen::Map<Eigen::Matrix<Real, N2, 1>> r(result._coords);
143  r.noalias() = a * bvec;
144  return result;
145  }
146  else
147  {
148  unsigned int index = 0;
149  for (unsigned int ij = 0; ij < N2; ++ij)
150  {
151  ValueType tmp = 0;
152  for (unsigned int kl = 0; kl < N2; ++kl)
153  tmp += _vals[index++] * b(kl / LIBMESH_DIM, kl % LIBMESH_DIM);
154  result._coords[ij] = tmp;
155  }
156 
157  return result;
158  }
159 }
160 
161 template <typename T>
164 {
165  for (auto i : make_range(N4))
166  _vals[i] *= a;
167  return *this;
168 }
169 
170 template <typename T>
173 {
174  for (auto i : make_range(N4))
175  _vals[i] /= a;
176  return *this;
177 }
178 
179 template <typename T>
182 {
183  for (auto i : make_range(N4))
184  _vals[i] += a._vals[i];
185  return *this;
186 }
187 
188 template <typename T>
189 template <typename T2>
190 auto
193 {
195  for (auto i : make_range(N4))
196  result._vals[i] = _vals[i] + b._vals[i];
197  return result;
198 }
199 
200 template <typename T>
203 {
204  for (auto i : make_range(N4))
205  _vals[i] -= a._vals[i];
206  return *this;
207 }
208 
209 template <typename T>
210 template <typename T2>
211 auto
213  -> RankFourTensorTempl<decltype(T() - T2())>
214 {
215  RankFourTensorTempl<decltype(T() - T2())> result;
216  for (auto i : make_range(N4))
217  result._vals[i] = _vals[i] - b._vals[i];
218  return result;
219 }
220 
221 template <typename T>
224 {
225  RankFourTensorTempl<T> result;
226  for (auto i : make_range(N4))
227  result._vals[i] = -_vals[i];
228  return result;
229 }
230 
231 template <typename T>
232 template <typename T2>
233 auto
236 {
237  typedef decltype(T() * T2()) ValueType;
239 
240  if constexpr (std::is_same_v<T, Real> && std::is_same_v<T2, Real>)
241  {
242  // C_ijkl = A_ijpq B_pqkl. With the row-major flat storage (linear index ij*N2 + kl, where
243  // ij = i*N + j and kl = k*N + l) this is exactly an (N2 x N2) matrix product over the
244  // flattened tensors. Dispatch to Eigen's vectorized kernel instead of the six-deep loop,
245  // which recomputes the flat index via operator() ~3x per innermost iteration.
246  const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> a(_vals);
247  const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> bmat(b._vals);
248  Eigen::Map<Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> r(result._vals);
249  r.noalias() = a * bmat;
250  return result;
251  }
252  else
253  {
254  for (auto i : make_range(N))
255  for (auto j : make_range(N))
256  for (auto k : make_range(N))
257  for (auto l : make_range(N))
258  for (auto p : make_range(N))
259  for (auto q : make_range(N))
260  result(i, j, k, l) += (*this)(i, j, p, q) * b(p, q, k, l);
261 
262  return result;
263  }
264 }
265 
266 template <typename T>
267 T
269 {
270  T l2 = 0;
271 
272  for (auto i : make_range(N4))
273  l2 += Utility::pow<2>(_vals[i]);
274 
275  using std::sqrt;
276  return sqrt(l2);
277 }
278 
279 template <typename T>
282 {
283  mooseError("The invSymm operation calls to LAPACK and only supports plain Real type tensors.");
284 }
285 
286 template <>
289 {
290  unsigned int ntens = N * (N + 1) / 2;
291  int nskip = N - 1;
292 
294  std::vector<PetscScalar> mat;
295  mat.assign(ntens * ntens, 0);
296 
297  // We use the LAPACK matrix inversion routine here. Form the matrix
298  //
299  // mat[0] mat[1] mat[2] mat[3] mat[4] mat[5]
300  // mat[6] mat[7] mat[8] mat[9] mat[10] mat[11]
301  // mat[12] mat[13] mat[14] mat[15] mat[16] mat[17]
302  // mat[18] mat[19] mat[20] mat[21] mat[22] mat[23]
303  // mat[24] mat[25] mat[26] mat[27] mat[28] mat[29]
304  // mat[30] mat[31] mat[32] mat[33] mat[34] mat[35]
305  //
306  // This is filled from the indpendent components of C assuming
307  // the symmetry C_ijkl = C_ijlk = C_jikl.
308  //
309  // If there are two rank-four tensors X and Y then the reason for
310  // this filling becomes apparent if we want to calculate
311  // X_ijkl*Y_klmn = Z_ijmn
312  // For denote the "mat" versions of X, Y and Z by x, y and z.
313  // Then
314  // z_ab = x_ac*y_cb
315  // Eg
316  // z_00 = Z_0000 = X_0000*Y_0000 + X_0011*Y_1111 + X_0022*Y_2200 + 2*X_0001*Y_0100 +
317  // 2*X_0002*Y_0200 + 2*X_0012*Y_1200 (the factors of 2 come from the assumed symmetries)
318  // 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 +
319  // 2*X_0002*2*Y_0201 + 2*X_0012*2*Y_1201
320  // 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 +
321  // 2*X_0102*2*Y_0202 + 2*X_0112*2*Y_1202
322  // Finally, we use LAPACK to find x^-1, and put it back into rank-4 tensor form
323  //
324  // mat[0] = C(0,0,0,0)
325  // mat[1] = C(0,0,1,1)
326  // mat[2] = C(0,0,2,2)
327  // mat[3] = C(0,0,0,1)*2
328  // mat[4] = C(0,0,0,2)*2
329  // mat[5] = C(0,0,1,2)*2
330 
331  // mat[6] = C(1,1,0,0)
332  // mat[7] = C(1,1,1,1)
333  // mat[8] = C(1,1,2,2)
334  // mat[9] = C(1,1,0,1)*2
335  // mat[10] = C(1,1,0,2)*2
336  // mat[11] = C(1,1,1,2)*2
337 
338  // mat[12] = C(2,2,0,0)
339  // mat[13] = C(2,2,1,1)
340  // mat[14] = C(2,2,2,2)
341  // mat[15] = C(2,2,0,1)*2
342  // mat[16] = C(2,2,0,2)*2
343  // mat[17] = C(2,2,1,2)*2
344 
345  // mat[18] = C(0,1,0,0)
346  // mat[19] = C(0,1,1,1)
347  // mat[20] = C(0,1,2,2)
348  // mat[21] = C(0,1,0,1)*2
349  // mat[22] = C(0,1,0,2)*2
350  // mat[23] = C(0,1,1,2)*2
351 
352  // mat[24] = C(0,2,0,0)
353  // mat[25] = C(0,2,1,1)
354  // mat[26] = C(0,2,2,2)
355  // mat[27] = C(0,2,0,1)*2
356  // mat[28] = C(0,2,0,2)*2
357  // mat[29] = C(0,2,1,2)*2
358 
359  // mat[30] = C(1,2,0,0)
360  // mat[31] = C(1,2,1,1)
361  // mat[32] = C(1,2,2,2)
362  // mat[33] = C(1,2,0,1)*2
363  // mat[34] = C(1,2,0,2)*2
364  // mat[35] = C(1,2,1,2)*2
365 
366  unsigned int index = 0;
367  for (auto i : make_range(N))
368  for (auto j : make_range(N))
369  for (auto k : make_range(N))
370  for (auto l : make_range(N))
371  {
372  if (i == j)
373  mat[k == l ? i * ntens + k : i * ntens + k + nskip + l] += _vals[index];
374  else
375  // i!=j
376  mat[k == l ? (nskip + i + j) * ntens + k : (nskip + i + j) * ntens + k + nskip + l] +=
377  _vals[index]; // note the +=, which results in double-counting and is rectified
378  // below
379  index++;
380  }
381 
382  for (unsigned int i = 3; i < ntens; ++i)
383  for (auto j : make_range(ntens))
384  mat[i * ntens + j] /= 2.0; // because of double-counting above
385 
386  // use LAPACK to find the inverse
387  MatrixTools::inverse(mat, ntens);
388 
389  // build the resulting rank-four tensor
390  // using the inverse of the above algorithm
391  index = 0;
392  for (auto i : make_range(N))
393  for (auto j : make_range(N))
394  for (auto k : make_range(N))
395  for (auto l : make_range(N))
396  {
397  if (i == j)
398  result._vals[index] =
399  k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
400  else
401  // i!=j
402  result._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
403  : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
404  index++;
405  }
406 
407  return result;
408 }
409 
410 template <typename T>
411 void
412 RankFourTensorTempl<T>::rotate(const TypeTensor<T> & R)
413 {
414  RankFourTensorTempl<T> old = *this;
415 
416  unsigned int index = 0;
417  for (auto i : make_range(N))
418  for (auto j : make_range(N))
419  for (auto k : make_range(N))
420  for (auto l : make_range(N))
421  {
422  unsigned int index2 = 0;
423  T sum = 0.0;
424  for (auto m : make_range(N))
425  {
426  const T & a = R(i, m);
427  for (auto n : make_range(N))
428  {
429  const T & ab = a * R(j, n);
430  for (auto o : make_range(N))
431  {
432  const T & abc = ab * R(k, o);
433  for (auto p : make_range(N))
434  sum += abc * R(l, p) * old._vals[index2++];
435  }
436  }
437  }
438  _vals[index++] = sum;
439  }
440 }
441 
442 template <typename T>
443 void
444 RankFourTensorTempl<T>::print(std::ostream & stm) const
445 {
446  for (auto i : make_range(N))
447  for (auto j : make_range(N))
448  {
449  stm << "i = " << i << " j = " << j << '\n';
450  for (auto k : make_range(N))
451  {
452  for (auto l : make_range(N))
453  stm << std::setw(15) << (*this)(i, j, k, l) << " ";
454 
455  stm << '\n';
456  }
457  }
458 
459  stm << std::flush;
460 }
461 
462 template <typename T>
463 void
464 RankFourTensorTempl<T>::printReal(std::ostream & stm) const
465 {
466  for (unsigned int i = 0; i < N; ++i)
467  for (unsigned int j = 0; j < N; ++j)
468  {
469  stm << "i = " << i << " j = " << j << '\n';
470  for (unsigned int k = 0; k < N; ++k)
471  {
472  for (unsigned int l = 0; l < N; ++l)
473  stm << std::setw(15) << MetaPhysicL::raw_value((*this)(i, j, k, l)) << " ";
474 
475  stm << '\n';
476  }
477  }
478 
479  stm << std::flush;
480 }
481 
482 template <typename T>
485 {
486  RankFourTensorTempl<T> result;
487 
488  unsigned int index = 0;
489  for (auto i : make_range(N))
490  for (auto j : make_range(N))
491  for (auto k : make_range(N))
492  for (auto l : make_range(N))
493  result._vals[index++] = _vals[k * N3 + i * N + j + l * N2];
494 
495  return result;
496 }
497 
498 template <typename T>
501 {
502  RankFourTensorTempl<T> result;
503 
504  for (auto i : make_range(N))
505  for (auto j : make_range(N))
506  for (auto k : make_range(N))
507  for (auto l : make_range(N))
508  result(i, j, k, l) = (*this)(j, i, k, l);
509 
510  return result;
511 }
512 
513 template <typename T>
516 {
517  RankFourTensorTempl<T> result;
518 
519  for (auto i : make_range(N))
520  for (auto j : make_range(N))
521  for (auto k : make_range(N))
522  for (auto l : make_range(N))
523  result(i, j, k, l) = (*this)(i, j, l, k);
524 
525  return result;
526 }
527 
528 template <typename T>
529 void
531 {
532  zero();
533 
534  if (input.size() == 9)
535  {
536  // then fill from vector C_1111, C_1112, C_1122, C_1212, C_1222, C_1211, C_2211, C_2212, C_2222
537  (*this)(0, 0, 0, 0) = input[0];
538  (*this)(0, 0, 0, 1) = input[1];
539  (*this)(0, 0, 1, 1) = input[2];
540  (*this)(0, 1, 0, 1) = input[3];
541  (*this)(0, 1, 1, 1) = input[4];
542  (*this)(0, 1, 0, 0) = input[5];
543  (*this)(1, 1, 0, 0) = input[6];
544  (*this)(1, 1, 0, 1) = input[7];
545  (*this)(1, 1, 1, 1) = input[8];
546 
547  // fill in remainders from C_ijkl = C_ijlk = C_jikl
548  (*this)(0, 0, 1, 0) = (*this)(0, 0, 0, 1);
549  (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
550  (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
551  (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
552  (*this)(1, 0, 1, 1) = (*this)(0, 1, 1, 1);
553  (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
554  (*this)(1, 1, 1, 0) = (*this)(1, 1, 0, 1);
555  }
556  else if (input.size() == 2)
557  {
558  // only two independent constants, C_1111 and C_1122
559  (*this)(0, 0, 0, 0) = input[0];
560  (*this)(0, 0, 1, 1) = input[1];
561  // use symmetries
562  (*this)(1, 1, 1, 1) = (*this)(0, 0, 0, 0);
563  (*this)(1, 1, 0, 0) = (*this)(0, 0, 1, 1);
564  (*this)(0, 1, 0, 1) = 0.5 * ((*this)(0, 0, 0, 0) - (*this)(0, 0, 1, 1));
565  (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
566  (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
567  (*this)(1, 0, 1, 0) = (*this)(0, 1, 0, 1);
568  }
569  else
570  mooseError("Please provide correct number of inputs for surface RankFourTensorTempl<T> "
571  "initialization.");
572 }
573 
574 template <typename T>
575 void
576 RankFourTensorTempl<T>::fillFromInputVector(const std::vector<T> & input, FillMethod fill_method)
577 {
578 
579  switch (fill_method)
580  {
581  case antisymmetric:
582  fillAntisymmetricFromInputVector(input);
583  break;
584  case symmetric9:
585  fillSymmetric9FromInputVector(input);
586  break;
587  case symmetric21:
588  fillSymmetric21FromInputVector(input);
589  break;
590  case general_isotropic:
591  fillGeneralIsotropicFromInputVector(input);
592  break;
593  case symmetric_isotropic:
594  fillSymmetricIsotropicFromInputVector(input);
595  break;
596  case symmetric_isotropic_E_nu:
597  fillSymmetricIsotropicEandNuFromInputVector(input);
598  break;
599  case antisymmetric_isotropic:
600  fillAntisymmetricIsotropicFromInputVector(input);
601  break;
602  case axisymmetric_rz:
603  fillAxisymmetricRZFromInputVector(input);
604  break;
605  case general:
606  fillGeneralFromInputVector(input);
607  break;
608  case principal:
609  fillPrincipalFromInputVector(input);
610  break;
611  case orthotropic:
612  fillGeneralOrthotropicFromInputVector(input);
613  break;
614  default:
615  mooseError("fillFromInputVector called with unknown fill_method of ", fill_method);
616  }
617 }
618 
619 template <typename T>
620 void
622 {
623  if (input.size() != 6)
624  mooseError(
625  "To use fillAntisymmetricFromInputVector, your input must have size 6. Yours has size ",
626  input.size());
627 
628  zero();
629 
630  (*this)(0, 1, 0, 1) = input[0]; // B1212
631  (*this)(0, 1, 0, 2) = input[1]; // B1213
632  (*this)(0, 1, 1, 2) = input[2]; // B1223
633 
634  (*this)(0, 2, 0, 2) = input[3]; // B1313
635  (*this)(0, 2, 1, 2) = input[4]; // B1323
636 
637  (*this)(1, 2, 1, 2) = input[5]; // B2323
638 
639  // symmetry on the two pairs
640  (*this)(0, 2, 0, 1) = (*this)(0, 1, 0, 2);
641  (*this)(1, 2, 0, 1) = (*this)(0, 1, 1, 2);
642  (*this)(1, 2, 0, 2) = (*this)(0, 2, 1, 2);
643  // have now got the upper parts of vals[0][1], vals[0][2] and vals[1][2]
644 
645  // fill in from antisymmetry relations
646  for (auto i : make_range(N))
647  for (auto j : make_range(N))
648  {
649  (*this)(0, 1, j, i) = -(*this)(0, 1, i, j);
650  (*this)(0, 2, j, i) = -(*this)(0, 2, i, j);
651  (*this)(1, 2, j, i) = -(*this)(1, 2, i, j);
652  }
653  // have now got all of vals[0][1], vals[0][2] and vals[1][2]
654 
655  // fill in from antisymmetry relations
656  for (auto i : make_range(N))
657  for (auto j : make_range(N))
658  {
659  (*this)(1, 0, i, j) = -(*this)(0, 1, i, j);
660  (*this)(2, 0, i, j) = -(*this)(0, 2, i, j);
661  (*this)(2, 1, i, j) = -(*this)(1, 2, i, j);
662  }
663 }
664 
665 template <typename T>
666 void
668 {
669  if (input.size() != 3)
670  mooseError("To use fillGeneralIsotropicFromInputVector, your input must have size 3. Yours "
671  "has size ",
672  input.size());
673 
674  fillGeneralIsotropic(input[0], input[1], input[2]);
675 }
676 
677 template <typename T>
678 void
679 RankFourTensorTempl<T>::fillGeneralIsotropic(const T & i0, const T & i1, const T & i2)
680 {
681  for (auto i : make_range(N))
682  for (auto j : make_range(N))
683  for (auto k : make_range(N))
684  for (auto l : make_range(N))
685  {
686  (*this)(i, j, k, l) = i0 * Real(i == j) * Real(k == l) +
687  i1 * Real(i == k) * Real(j == l) + i1 * Real(i == l) * Real(j == k);
688  for (auto m : make_range(N))
689  (*this)(i, j, k, l) +=
690  i2 * Real(PermutationTensor::eps(i, j, m)) * Real(PermutationTensor::eps(k, l, m));
691  }
692 }
693 
694 template <typename T>
695 void
697 {
698  if (input.size() != 1)
699  mooseError("To use fillAntisymmetricIsotropicFromInputVector, your input must have size 1. "
700  "Yours has size ",
701  input.size());
702 
703  fillGeneralIsotropic(0.0, 0.0, input[0]);
704 }
705 
706 template <typename T>
707 void
709 {
710  fillGeneralIsotropic(0.0, 0.0, i0);
711 }
712 
713 template <typename T>
714 void
716 {
717  mooseAssert(input.size() == 2,
718  "To use fillSymmetricIsotropicFromInputVector, your input must have size 2.");
719  fillSymmetricIsotropic(input[0], input[1]);
720 }
721 
722 template <typename T>
723 void
725 {
726  // clang-format off
727  fillSymmetric21FromInputVector(std::array<T,21>
728  {{lambda + 2.0 * G, lambda, lambda, 0.0, 0.0, 0.0,
729  lambda + 2.0 * G, lambda, 0.0, 0.0, 0.0,
730  lambda + 2.0 * G, 0.0, 0.0, 0.0,
731  G, 0.0, 0.0,
732  G, 0.0,
733  G}});
734  // clang-format on
735 }
736 
737 template <typename T>
738 void
740 {
741  if (input.size() != 2)
742  mooseError(
743  "To use fillSymmetricIsotropicEandNuFromInputVector, your input must have size 2. Yours "
744  "has size ",
745  input.size());
746 
747  fillSymmetricIsotropicEandNu(input[0], input[1]);
748 }
749 
750 template <typename T>
751 void
753 {
754  // Calculate lambda and the shear modulus from the given young's modulus and poisson's ratio
755  const T & lambda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu));
756  const T & G = E / (2.0 * (1.0 + nu));
757 
758  fillSymmetricIsotropic(lambda, G);
759 }
760 
761 template <typename T>
762 void
764 {
765  mooseAssert(input.size() == 5,
766  "To use fillAxisymmetricRZFromInputVector, your input must have size 5.");
767 
768  // C1111 C1122 C1133 0 0 0
769  // C2222 C2233=C1133 0 0 0
770  // C3333 0 0 0
771  // C2323 0 0
772  // C3131=C2323 0
773  // C1212
774  // clang-format off
775  fillSymmetric21FromInputVector(std::array<T,21>
776  {{input[0],input[1],input[2], 0.0, 0.0, 0.0,
777  input[0],input[2], 0.0, 0.0, 0.0,
778  input[3], 0.0, 0.0, 0.0,
779  input[4], 0.0, 0.0,
780  input[4], 0.0,
781  (input[0] - input[1]) * 0.5}});
782  // clang-format on
783 }
784 
785 template <typename T>
786 void
788 {
789  if (input.size() != 81)
790  mooseError("To use fillGeneralFromInputVector, your input must have size 81. Yours has size ",
791  input.size());
792 
793  for (auto i : make_range(N4))
794  _vals[i] = input[i];
795 }
796 
797 template <typename T>
798 void
800 {
801  if (input.size() != 9)
802  mooseError("To use fillPrincipalFromInputVector, your input must have size 9. Yours has size ",
803  input.size());
804 
805  zero();
806 
807  (*this)(0, 0, 0, 0) = input[0];
808  (*this)(0, 0, 1, 1) = input[1];
809  (*this)(0, 0, 2, 2) = input[2];
810  (*this)(1, 1, 0, 0) = input[3];
811  (*this)(1, 1, 1, 1) = input[4];
812  (*this)(1, 1, 2, 2) = input[5];
813  (*this)(2, 2, 0, 0) = input[6];
814  (*this)(2, 2, 1, 1) = input[7];
815  (*this)(2, 2, 2, 2) = input[8];
816 }
817 
818 template <typename T>
819 void
821 {
822  if (input.size() != 12)
823  mooseError("To use fillGeneralOrhotropicFromInputVector, your input must have size 12. Yours "
824  "has size ",
825  input.size());
826 
827  const T & Ea = input[0];
828  const T & Eb = input[1];
829  const T & Ec = input[2];
830  const T & Gab = input[3];
831  const T & Gbc = input[4];
832  const T & Gca = input[5];
833  const T & nuba = input[6];
834  const T & nuca = input[7];
835  const T & nucb = input[8];
836  const T & nuab = input[9];
837  const T & nuac = input[10];
838  const T & nubc = input[11];
839 
840  // Input must satisfy constraints.
841  bool preserve_symmetry = MooseUtils::relativeFuzzyEqual(nuab * Eb, nuba * Ea) &&
842  MooseUtils::relativeFuzzyEqual(nuca * Ea, nuac * Ec) &&
843  MooseUtils::relativeFuzzyEqual(nubc * Ec, nucb * Eb);
844 
845  if (!preserve_symmetry)
846  mooseError("Orthotropic elasticity tensor input is not consistent with symmetry requirements. "
847  "Check input for accuracy");
848 
849  unsigned int ntens = N * (N + 1) / 2;
850 
851  std::vector<T> mat;
852  mat.assign(ntens * ntens, 0);
853 
854  T k = 1 - nuab * nuba - nubc * nucb - nuca * nuac - nuab * nubc * nuca - nuba * nucb * nuac;
855 
856  bool is_positive_definite =
857  (k > 0) && (1 - nubc * nucb) > 0 && (1 - nuac * nuca) > 0 && (1 - nuab * nuba) > 0;
858  if (!is_positive_definite)
859  mooseError("Orthotropic elasticity tensor input is not positive definite. Check input for "
860  "accuracy");
861 
862  mat[0] = Ea * (1 - nubc * nucb) / k;
863  mat[1] = Ea * (nubc * nuca + nuba) / k;
864  mat[2] = Ea * (nuba * nucb + nuca) / k;
865 
866  mat[6] = Eb * (nuac * nucb + nuab) / k;
867  mat[7] = Eb * (1 - nuac * nuca) / k;
868  mat[8] = Eb * (nuab * nuca + nucb) / k;
869 
870  mat[12] = Ec * (nuab * nubc + nuac) / k;
871  mat[13] = Ec * (nuac * nuba + nubc) / k;
872  mat[14] = Ec * (1 - nuab * nuba) / k;
873 
874  mat[21] = 2 * Gab;
875  mat[28] = 2 * Gca;
876  mat[35] = 2 * Gbc;
877 
878  // Switching from Voigt to fourth order tensor
879  // Copied from existing code (invSymm)
880  int nskip = N - 1;
881 
882  unsigned int index = 0;
883  for (auto i : make_range(N))
884  for (auto j : make_range(N))
885  for (auto k : make_range(N))
886  for (auto l : make_range(N))
887  {
888  if (i == j)
889  (*this)._vals[index] =
890  k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
891  else
892  (*this)._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
893  : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
894  index++;
895  }
896 }
897 
898 template <typename T>
901 {
902  RankTwoTensorTempl<T> result;
903 
904  unsigned int index = 0;
905  for (unsigned int ij = 0; ij < N2; ++ij)
906  {
907  T bb = b._coords[ij];
908  for (unsigned int kl = 0; kl < N2; ++kl)
909  result._coords[kl] += _vals[index++] * bb;
910  }
911 
912  return result;
913 }
914 
915 template <typename T>
916 T
918  unsigned int j,
919  const RankTwoTensorTempl<T> & M) const
920 {
921  T val = 0;
922  for (unsigned int k = 0; k < N; k++)
923  for (unsigned int l = 0; l < N; l++)
924  val += (*this)(i, j, k, l) * M(k, l);
925 
926  return val;
927 }
928 
929 template <typename T>
930 T
932  unsigned int l,
933  const RankTwoTensorTempl<T> & M) const
934 {
935  T val = 0;
936  for (unsigned int i = 0; i < N; i++)
937  for (unsigned int j = 0; j < N; j++)
938  val += (*this)(i, j, k, l) * M(i, j);
939 
940  return val;
941 }
942 
943 template <typename T>
944 T
946 {
947  // used in the volumetric locking correction
948  T sum = 0;
949  for (auto i : make_range(N))
950  for (auto j : make_range(N))
951  sum += (*this)(i, i, j, j);
952  return sum;
953 }
954 
955 template <typename T>
958 {
959  // used for volumetric locking correction
961  a(0) = (*this)(0, 0, 0, 0) + (*this)(0, 0, 1, 1) + (*this)(0, 0, 2, 2); // C0000 + C0011 + C0022
962  a(1) = (*this)(1, 1, 0, 0) + (*this)(1, 1, 1, 1) + (*this)(1, 1, 2, 2); // C1100 + C1111 + C1122
963  a(2) = (*this)(2, 2, 0, 0) + (*this)(2, 2, 1, 1) + (*this)(2, 2, 2, 2); // C2200 + C2211 + C2222
964  return a;
965 }
966 
967 template <typename T>
970  const RankTwoTensorTempl<T> & B,
971  const RankTwoTensorTempl<T> & C) const
972 {
974  for (unsigned int i = 0; i < N; i++)
975  for (unsigned int j = 0; j < N; j++)
976  for (unsigned int k = 0; k < N; k++)
977  for (unsigned int l = 0; l < N; l++)
978  for (unsigned int m = 0; m < N; m++)
979  for (unsigned int n = 0; n < N; n++)
980  for (unsigned int t = 0; t < N; t++)
981  R(i, j, k, l) += (*this)(i, m, n, t) * A(j, m) * B(k, n) * C(l, t);
982 
983  return R;
984 }
985 
986 template <typename T>
989  const RankTwoTensorTempl<T> & B,
990  const RankTwoTensorTempl<T> & C) const
991 {
993  for (unsigned int i = 0; i < N; i++)
994  for (unsigned int j = 0; j < N; j++)
995  for (unsigned int k = 0; k < N; k++)
996  for (unsigned int l = 0; l < N; l++)
997  for (unsigned int m = 0; m < N; m++)
998  for (unsigned int n = 0; n < N; n++)
999  for (unsigned int t = 0; t < N; t++)
1000  R(i, j, k, l) += (*this)(m, j, n, t) * A(i, m) * B(k, n) * C(l, t);
1001 
1002  return R;
1003 }
1004 
1005 template <typename T>
1008  const RankTwoTensorTempl<T> & B,
1009  const RankTwoTensorTempl<T> & C) const
1010 {
1012  for (unsigned int i = 0; i < N; i++)
1013  for (unsigned int j = 0; j < N; j++)
1014  for (unsigned int k = 0; k < N; k++)
1015  for (unsigned int l = 0; l < N; l++)
1016  for (unsigned int m = 0; m < N; m++)
1017  for (unsigned int n = 0; n < N; n++)
1018  for (unsigned int t = 0; t < N; t++)
1019  R(i, j, k, l) += (*this)(m, n, k, t) * A(i, m) * B(j, n) * C(l, t);
1020 
1021  return R;
1022 }
1023 
1024 template <typename T>
1027  const RankTwoTensorTempl<T> & B,
1028  const RankTwoTensorTempl<T> & C) const
1029 {
1031  for (unsigned int i = 0; i < N; i++)
1032  for (unsigned int j = 0; j < N; j++)
1033  for (unsigned int k = 0; k < N; k++)
1034  for (unsigned int l = 0; l < N; l++)
1035  for (unsigned int m = 0; m < N; m++)
1036  for (unsigned int n = 0; n < N; n++)
1037  for (unsigned int t = 0; t < N; t++)
1038  R(i, j, k, l) += (*this)(m, n, t, l) * A(i, m) * B(j, n) * C(k, t);
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)(m, j, k, l) * A(i, 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, m, k, l) * A(j, m);
1071 
1072  return R;
1073 }
1074 
1075 template <typename T>
1078 {
1080 
1081  for (unsigned int i = 0; i < N; i++)
1082  for (unsigned int j = 0; j < N; j++)
1083  for (unsigned int k = 0; k < N; k++)
1084  for (unsigned int l = 0; l < N; l++)
1085  for (unsigned int m = 0; m < N; m++)
1086  R(i, j, k, l) += (*this)(i, j, m, l) * A(k, m);
1087 
1088  return R;
1089 }
1090 
1091 template <typename T>
1094 {
1096 
1097  for (unsigned int i = 0; i < N; i++)
1098  for (unsigned int j = 0; j < N; j++)
1099  for (unsigned int k = 0; k < N; k++)
1100  for (unsigned int l = 0; l < N; l++)
1101  for (unsigned int m = 0; m < N; m++)
1102  R(i, j, k, l) += (*this)(i, j, k, m) * A(l, m);
1103 
1104  return R;
1105 }
1106 
1107 template <typename T>
1108 bool
1110 {
1111  for (auto i : make_range(1u, N))
1112  for (auto j : make_range(i))
1113  for (auto k : make_range(1u, N))
1114  for (auto l : make_range(k))
1115  {
1116  // minor symmetries
1117  if ((*this)(i, j, k, l) != (*this)(j, i, k, l) ||
1118  (*this)(i, j, k, l) != (*this)(i, j, l, k))
1119  return false;
1120 
1121  // major symmetry
1122  if ((*this)(i, j, k, l) != (*this)(k, l, i, j))
1123  return false;
1124  }
1125  return true;
1126 }
1127 
1128 template <typename T>
1129 bool
1131 {
1132  // prerequisite is symmetry
1133  if (!isSymmetric())
1134  return false;
1135 
1136  // inspect shear components
1137  const T & mu = (*this)(0, 1, 0, 1);
1138  // ...diagonal
1139  if ((*this)(1, 2, 1, 2) != mu || (*this)(2, 0, 2, 0) != mu)
1140  return false;
1141  // ...off-diagonal
1142  if ((*this)(2, 0, 1, 2) != 0.0 || (*this)(0, 1, 1, 2) != 0.0 || (*this)(0, 1, 2, 0) != 0.0)
1143  return false;
1144 
1145  // off diagonal blocks in Voigt
1146  for (auto i : make_range(N))
1147  for (auto j : make_range(N))
1148  if (_vals[i * (N3 + N2) + ((j + 1) % N) * N + (j + 2) % N] != 0.0)
1149  return false;
1150 
1151  // top left block
1152  const T & K1 = (*this)(0, 0, 0, 0);
1153  const T & K2 = (*this)(0, 0, 1, 1);
1154  if (!MooseUtils::relativeFuzzyEqual(K1 - 4.0 * mu / 3.0, K2 + 2.0 * mu / 3.0))
1155  return false;
1156  if ((*this)(1, 1, 1, 1) != K1 || (*this)(2, 2, 2, 2) != K1)
1157  return false;
1158  for (auto i : make_range(1u, N))
1159  for (auto j : make_range(i))
1160  if ((*this)(i, i, j, j) != K2)
1161  return false;
1162 
1163  return true;
1164 }
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:311
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:54
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