LCOV - code coverage report
Current view: top level - include/utils - RankFourTensorImplementation.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33390 (250e9c) with base 846a5c Lines: 324 610 53.1 %
Date: 2026-07-31 18:15:22 Functions: 35 187 18.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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>
      43             : MooseEnum
      44           0 : RankFourTensorTempl<T>::fillMethodEnum()
      45             : {
      46             :   return MooseEnum("antisymmetric symmetric9 symmetric21 general_isotropic symmetric_isotropic "
      47             :                    "symmetric_isotropic_E_nu antisymmetric_isotropic axisymmetric_rz general "
      48           0 :                    "principal orthotropic");
      49             : }
      50             : 
      51             : template <typename T>
      52     2484396 : RankFourTensorTempl<T>::RankFourTensorTempl()
      53             : {
      54             :   mooseAssert(N == 3, "RankFourTensorTempl<T> is currently only tested for 3 dimensions.");
      55             : 
      56   196341210 :   for (auto i : make_range(N4))
      57   193946805 :     _vals[i] = 0.0;
      58     2394405 : }
      59             : 
      60             : template <typename T>
      61          28 : RankFourTensorTempl<T>::RankFourTensorTempl(const InitMethod init)
      62             : {
      63          28 :   unsigned int index = 0;
      64          28 :   switch (init)
      65             :   {
      66           0 :     case initNone:
      67           0 :       break;
      68             : 
      69           2 :     case initIdentity:
      70           2 :       zero();
      71           8 :       for (auto i : make_range(N))
      72           6 :         (*this)(i, i, i, i) = 1.0;
      73           2 :       break;
      74             : 
      75          20 :     case initIdentityFour:
      76          80 :       for (auto i : make_range(N))
      77         240 :         for (auto j : make_range(N))
      78         720 :           for (auto k : make_range(N))
      79        2160 :             for (auto l : make_range(N))
      80        1620 :               _vals[index++] = Real(i == k && j == l);
      81          20 :       break;
      82             : 
      83           6 :     case initIdentitySymmetricFour:
      84          24 :       for (auto i : make_range(N))
      85          72 :         for (auto j : make_range(N))
      86         216 :           for (auto k : make_range(N))
      87         648 :             for (auto l : make_range(N))
      88         486 :               _vals[index++] = 0.5 * Real(i == k && j == l) + 0.5 * Real(i == l && j == k);
      89           6 :       break;
      90             : 
      91           0 :     case initIdentityDeviatoric:
      92           0 :       for (unsigned int i = 0; i < N; ++i)
      93           0 :         for (unsigned int j = 0; j < N; ++j)
      94           0 :           for (unsigned int k = 0; k < N; ++k)
      95           0 :             for (unsigned int l = 0; l < N; ++l)
      96             :             {
      97           0 :               _vals[index] = Real(i == k && j == l);
      98           0 :               if ((i == j) && (k == l))
      99           0 :                 _vals[index] -= 1.0 / 3.0;
     100           0 :               index++;
     101             :             }
     102           0 :       break;
     103             : 
     104           0 :     default:
     105           0 :       mooseError("Unknown RankFourTensorTempl<T> initialization pattern.");
     106             :   }
     107          28 : }
     108             : 
     109             : template <typename T>
     110          34 : RankFourTensorTempl<T>::RankFourTensorTempl(const std::vector<T> & input, FillMethod fill_method)
     111             : {
     112          34 :   fillFromInputVector(input, fill_method);
     113          34 : }
     114             : 
     115             : template <typename T>
     116             : void
     117           8 : RankFourTensorTempl<T>::zero()
     118             : {
     119         656 :   for (auto i : make_range(N4))
     120         648 :     _vals[i] = 0.0;
     121           8 : }
     122             : 
     123             : template <typename T>
     124             : template <template <typename> class Tensor, typename T2>
     125             : auto
     126           4 : RankFourTensorTempl<T>::operator*(const Tensor<T2> & b) const ->
     127             :     typename std::enable_if<TwoTensorMultTraits<Tensor, T2>::value,
     128             :                             RankTwoTensorTempl<decltype(T() * T2())>>::type
     129             : {
     130             :   typedef decltype(T() * T2()) ValueType;
     131           4 :   RankTwoTensorTempl<ValueType> result;
     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           4 :     Eigen::Matrix<Real, N2, 1> bvec;
     139          40 :     for (unsigned int kl = 0; kl < N2; ++kl)
     140          36 :       bvec(kl) = b(kl / N, kl % N);
     141           4 :     const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> a(_vals);
     142           4 :     Eigen::Map<Eigen::Matrix<Real, N2, 1>> r(result._coords);
     143           4 :     r.noalias() = a * bvec;
     144           8 :     return result;
     145             :   }
     146             :   else
     147             :   {
     148           0 :     unsigned int index = 0;
     149           0 :     for (unsigned int ij = 0; ij < N2; ++ij)
     150             :     {
     151           0 :       ValueType tmp = 0;
     152           0 :       for (unsigned int kl = 0; kl < N2; ++kl)
     153           0 :         tmp += _vals[index++] * b(kl / LIBMESH_DIM, kl % LIBMESH_DIM);
     154           0 :       result._coords[ij] = tmp;
     155             :     }
     156             : 
     157           0 :     return result;
     158             :   }
     159           0 : }
     160             : 
     161             : template <typename T>
     162             : RankFourTensorTempl<T> &
     163           6 : RankFourTensorTempl<T>::operator*=(const T & a)
     164             : {
     165         492 :   for (auto i : make_range(N4))
     166         486 :     _vals[i] *= a;
     167           6 :   return *this;
     168             : }
     169             : 
     170             : template <typename T>
     171             : RankFourTensorTempl<T> &
     172           2 : RankFourTensorTempl<T>::operator/=(const T & a)
     173             : {
     174         164 :   for (auto i : make_range(N4))
     175         162 :     _vals[i] /= a;
     176           2 :   return *this;
     177             : }
     178             : 
     179             : template <typename T>
     180             : RankFourTensorTempl<T> &
     181          74 : RankFourTensorTempl<T>::operator+=(const RankFourTensorTempl<T> & a)
     182             : {
     183        6068 :   for (auto i : make_range(N4))
     184        5994 :     _vals[i] += a._vals[i];
     185          74 :   return *this;
     186             : }
     187             : 
     188             : template <typename T>
     189             : template <typename T2>
     190             : auto
     191         110 : RankFourTensorTempl<T>::operator+(const RankFourTensorTempl<T2> & b) const
     192             :     -> RankFourTensorTempl<decltype(T() + T2())>
     193             : {
     194         110 :   RankFourTensorTempl<decltype(T() + T2())> result;
     195        9020 :   for (auto i : make_range(N4))
     196        8910 :     result._vals[i] = _vals[i] + b._vals[i];
     197         110 :   return result;
     198           0 : }
     199             : 
     200             : template <typename T>
     201             : RankFourTensorTempl<T> &
     202           2 : RankFourTensorTempl<T>::operator-=(const RankFourTensorTempl<T> & a)
     203             : {
     204         164 :   for (auto i : make_range(N4))
     205         162 :     _vals[i] -= a._vals[i];
     206           2 :   return *this;
     207             : }
     208             : 
     209             : template <typename T>
     210             : template <typename T2>
     211             : auto
     212      792722 : RankFourTensorTempl<T>::operator-(const RankFourTensorTempl<T2> & b) const
     213             :     -> RankFourTensorTempl<decltype(T() - T2())>
     214             : {
     215      792722 :   RankFourTensorTempl<decltype(T() - T2())> result;
     216    65003204 :   for (auto i : make_range(N4))
     217    64210482 :     result._vals[i] = _vals[i] - b._vals[i];
     218      792722 :   return result;
     219           0 : }
     220             : 
     221             : template <typename T>
     222             : RankFourTensorTempl<T>
     223           2 : RankFourTensorTempl<T>::operator-() const
     224             : {
     225           2 :   RankFourTensorTempl<T> result;
     226         164 :   for (auto i : make_range(N4))
     227         162 :     result._vals[i] = -_vals[i];
     228           2 :   return result;
     229           0 : }
     230             : 
     231             : template <typename T>
     232             : template <typename T2>
     233             : auto
     234           8 : RankFourTensorTempl<T>::operator*(const RankFourTensorTempl<T2> & b) const
     235             :     -> RankFourTensorTempl<decltype(T() * T2())>
     236             : {
     237             :   typedef decltype(T() * T2()) ValueType;
     238           8 :   RankFourTensorTempl<ValueType> result;
     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           8 :     const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> a(_vals);
     247           8 :     const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> bmat(b._vals);
     248           8 :     Eigen::Map<Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> r(result._vals);
     249           8 :     r.noalias() = a * bmat;
     250          16 :     return result;
     251             :   }
     252             :   else
     253             :   {
     254           0 :     for (auto i : make_range(N))
     255           0 :       for (auto j : make_range(N))
     256           0 :         for (auto k : make_range(N))
     257           0 :           for (auto l : make_range(N))
     258           0 :             for (auto p : make_range(N))
     259           0 :               for (auto q : make_range(N))
     260           0 :                 result(i, j, k, l) += (*this)(i, j, p, q) * b(p, q, k, l);
     261             : 
     262           0 :     return result;
     263             :   }
     264           0 : }
     265             : 
     266             : template <typename T>
     267             : T
     268      792722 : RankFourTensorTempl<T>::L2norm() const
     269             : {
     270      792722 :   T l2 = 0;
     271             : 
     272    65003204 :   for (auto i : make_range(N4))
     273    64210482 :     l2 += Utility::pow<2>(_vals[i]);
     274             : 
     275             :   using std::sqrt;
     276      792722 :   return sqrt(l2);
     277           0 : }
     278             : 
     279             : template <typename T>
     280             : RankFourTensorTempl<T>
     281           0 : RankFourTensorTempl<T>::invSymm() const
     282             : {
     283           0 :   mooseError("The invSymm operation calls to LAPACK and only supports plain Real type tensors.");
     284             : }
     285             : 
     286             : template <>
     287             : RankFourTensorTempl<Real>
     288           6 : RankFourTensorTempl<Real>::invSymm() const
     289             : {
     290           6 :   unsigned int ntens = N * (N + 1) / 2;
     291           6 :   int nskip = N - 1;
     292             : 
     293           6 :   RankFourTensorTempl<Real> result;
     294           6 :   std::vector<PetscScalar> mat;
     295           6 :   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           6 :   unsigned int index = 0;
     367          24 :   for (auto i : make_range(N))
     368          72 :     for (auto j : make_range(N))
     369         216 :       for (auto k : make_range(N))
     370         648 :         for (auto l : make_range(N))
     371             :         {
     372         486 :           if (i == j)
     373         162 :             mat[k == l ? i * ntens + k : i * ntens + k + nskip + l] += _vals[index];
     374             :           else
     375             :             // i!=j
     376         324 :             mat[k == l ? (nskip + i + j) * ntens + k : (nskip + i + j) * ntens + k + nskip + l] +=
     377         324 :                 _vals[index]; // note the +=, which results in double-counting and is rectified
     378             :                               // below
     379         486 :           index++;
     380             :         }
     381             : 
     382          24 :   for (unsigned int i = 3; i < ntens; ++i)
     383         126 :     for (auto j : make_range(ntens))
     384         108 :       mat[i * ntens + j] /= 2.0; // because of double-counting above
     385             : 
     386             :   // use LAPACK to find the inverse
     387           6 :   MatrixTools::inverse(mat, ntens);
     388             : 
     389             :   // build the resulting rank-four tensor
     390             :   // using the inverse of the above algorithm
     391           6 :   index = 0;
     392          24 :   for (auto i : make_range(N))
     393          72 :     for (auto j : make_range(N))
     394         216 :       for (auto k : make_range(N))
     395         648 :         for (auto l : make_range(N))
     396             :         {
     397         486 :           if (i == j)
     398         162 :             result._vals[index] =
     399         162 :                 k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
     400             :           else
     401             :             // i!=j
     402         540 :             result._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
     403         216 :                                          : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
     404         486 :           index++;
     405             :         }
     406             : 
     407          12 :   return result;
     408           6 : }
     409             : 
     410             : template <typename T>
     411             : void
     412           2 : RankFourTensorTempl<T>::rotate(const TypeTensor<T> & R)
     413             : {
     414           2 :   RankFourTensorTempl<T> old = *this;
     415             : 
     416           2 :   unsigned int index = 0;
     417           8 :   for (auto i : make_range(N))
     418          24 :     for (auto j : make_range(N))
     419          72 :       for (auto k : make_range(N))
     420         216 :         for (auto l : make_range(N))
     421             :         {
     422         162 :           unsigned int index2 = 0;
     423         162 :           T sum = 0.0;
     424         648 :           for (auto m : make_range(N))
     425             :           {
     426         486 :             const T & a = R(i, m);
     427        1944 :             for (auto n : make_range(N))
     428             :             {
     429        1458 :               const T & ab = a * R(j, n);
     430        5832 :               for (auto o : make_range(N))
     431             :               {
     432        4374 :                 const T & abc = ab * R(k, o);
     433       17496 :                 for (auto p : make_range(N))
     434       13122 :                   sum += abc * R(l, p) * old._vals[index2++];
     435             :               }
     436             :             }
     437             :           }
     438         162 :           _vals[index++] = sum;
     439             :         }
     440           2 : }
     441             : 
     442             : template <typename T>
     443             : void
     444           0 : RankFourTensorTempl<T>::print(std::ostream & stm) const
     445             : {
     446           0 :   for (auto i : make_range(N))
     447           0 :     for (auto j : make_range(N))
     448             :     {
     449           0 :       stm << "i = " << i << " j = " << j << '\n';
     450           0 :       for (auto k : make_range(N))
     451             :       {
     452           0 :         for (auto l : make_range(N))
     453           0 :           stm << std::setw(15) << (*this)(i, j, k, l) << " ";
     454             : 
     455           0 :         stm << '\n';
     456             :       }
     457             :     }
     458             : 
     459           0 :   stm << std::flush;
     460           0 : }
     461             : 
     462             : template <typename T>
     463             : void
     464           2 : RankFourTensorTempl<T>::printReal(std::ostream & stm) const
     465             : {
     466           8 :   for (unsigned int i = 0; i < N; ++i)
     467          24 :     for (unsigned int j = 0; j < N; ++j)
     468             :     {
     469          18 :       stm << "i = " << i << " j = " << j << '\n';
     470          72 :       for (unsigned int k = 0; k < N; ++k)
     471             :       {
     472         216 :         for (unsigned int l = 0; l < N; ++l)
     473         162 :           stm << std::setw(15) << MetaPhysicL::raw_value((*this)(i, j, k, l)) << " ";
     474             : 
     475          54 :         stm << '\n';
     476             :       }
     477             :     }
     478             : 
     479           2 :   stm << std::flush;
     480           2 : }
     481             : 
     482             : template <typename T>
     483             : RankFourTensorTempl<T>
     484           2 : RankFourTensorTempl<T>::transposeMajor() const
     485             : {
     486           2 :   RankFourTensorTempl<T> result;
     487             : 
     488           2 :   unsigned int index = 0;
     489           8 :   for (auto i : make_range(N))
     490          24 :     for (auto j : make_range(N))
     491          72 :       for (auto k : make_range(N))
     492         216 :         for (auto l : make_range(N))
     493         162 :           result._vals[index++] = _vals[k * N3 + i * N + j + l * N2];
     494             : 
     495           2 :   return result;
     496           0 : }
     497             : 
     498             : template <typename T>
     499             : RankFourTensorTempl<T>
     500           2 : RankFourTensorTempl<T>::transposeIj() const
     501             : {
     502           2 :   RankFourTensorTempl<T> result;
     503             : 
     504           8 :   for (auto i : make_range(N))
     505          24 :     for (auto j : make_range(N))
     506          72 :       for (auto k : make_range(N))
     507         216 :         for (auto l : make_range(N))
     508         162 :           result(i, j, k, l) = (*this)(j, i, k, l);
     509             : 
     510           2 :   return result;
     511           0 : }
     512             : 
     513             : template <typename T>
     514             : RankFourTensorTempl<T>
     515           0 : RankFourTensorTempl<T>::transposeKl() const
     516             : {
     517           0 :   RankFourTensorTempl<T> result;
     518             : 
     519           0 :   for (auto i : make_range(N))
     520           0 :     for (auto j : make_range(N))
     521           0 :       for (auto k : make_range(N))
     522           0 :         for (auto l : make_range(N))
     523           0 :           result(i, j, k, l) = (*this)(i, j, l, k);
     524             : 
     525           0 :   return result;
     526           0 : }
     527             : 
     528             : template <typename T>
     529             : void
     530           0 : RankFourTensorTempl<T>::surfaceFillFromInputVector(const std::vector<T> & input)
     531             : {
     532           0 :   zero();
     533             : 
     534           0 :   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           0 :     (*this)(0, 0, 0, 0) = input[0];
     538           0 :     (*this)(0, 0, 0, 1) = input[1];
     539           0 :     (*this)(0, 0, 1, 1) = input[2];
     540           0 :     (*this)(0, 1, 0, 1) = input[3];
     541           0 :     (*this)(0, 1, 1, 1) = input[4];
     542           0 :     (*this)(0, 1, 0, 0) = input[5];
     543           0 :     (*this)(1, 1, 0, 0) = input[6];
     544           0 :     (*this)(1, 1, 0, 1) = input[7];
     545           0 :     (*this)(1, 1, 1, 1) = input[8];
     546             : 
     547             :     // fill in remainders from C_ijkl = C_ijlk = C_jikl
     548           0 :     (*this)(0, 0, 1, 0) = (*this)(0, 0, 0, 1);
     549           0 :     (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
     550           0 :     (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
     551           0 :     (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
     552           0 :     (*this)(1, 0, 1, 1) = (*this)(0, 1, 1, 1);
     553           0 :     (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
     554           0 :     (*this)(1, 1, 1, 0) = (*this)(1, 1, 0, 1);
     555             :   }
     556           0 :   else if (input.size() == 2)
     557             :   {
     558             :     // only two independent constants, C_1111 and C_1122
     559           0 :     (*this)(0, 0, 0, 0) = input[0];
     560           0 :     (*this)(0, 0, 1, 1) = input[1];
     561             :     // use symmetries
     562           0 :     (*this)(1, 1, 1, 1) = (*this)(0, 0, 0, 0);
     563           0 :     (*this)(1, 1, 0, 0) = (*this)(0, 0, 1, 1);
     564           0 :     (*this)(0, 1, 0, 1) = 0.5 * ((*this)(0, 0, 0, 0) - (*this)(0, 0, 1, 1));
     565           0 :     (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
     566           0 :     (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
     567           0 :     (*this)(1, 0, 1, 0) = (*this)(0, 1, 0, 1);
     568             :   }
     569             :   else
     570           0 :     mooseError("Please provide correct number of inputs for surface RankFourTensorTempl<T> "
     571             :                "initialization.");
     572           0 : }
     573             : 
     574             : template <typename T>
     575             : void
     576         138 : RankFourTensorTempl<T>::fillFromInputVector(const std::vector<T> & input, FillMethod fill_method)
     577             : {
     578             : 
     579         138 :   switch (fill_method)
     580             :   {
     581           0 :     case antisymmetric:
     582           0 :       fillAntisymmetricFromInputVector(input);
     583           0 :       break;
     584           4 :     case symmetric9:
     585           4 :       fillSymmetric9FromInputVector(input);
     586           4 :       break;
     587           4 :     case symmetric21:
     588           4 :       fillSymmetric21FromInputVector(input);
     589           4 :       break;
     590           0 :     case general_isotropic:
     591           0 :       fillGeneralIsotropicFromInputVector(input);
     592           0 :       break;
     593           4 :     case symmetric_isotropic:
     594           4 :       fillSymmetricIsotropicFromInputVector(input);
     595           4 :       break;
     596           2 :     case symmetric_isotropic_E_nu:
     597           2 :       fillSymmetricIsotropicEandNuFromInputVector(input);
     598           2 :       break;
     599           0 :     case antisymmetric_isotropic:
     600           0 :       fillAntisymmetricIsotropicFromInputVector(input);
     601           0 :       break;
     602           2 :     case axisymmetric_rz:
     603           2 :       fillAxisymmetricRZFromInputVector(input);
     604           2 :       break;
     605         118 :     case general:
     606         118 :       fillGeneralFromInputVector(input);
     607         118 :       break;
     608           2 :     case principal:
     609           2 :       fillPrincipalFromInputVector(input);
     610           2 :       break;
     611           2 :     case orthotropic:
     612           2 :       fillGeneralOrthotropicFromInputVector(input);
     613           2 :       break;
     614           0 :     default:
     615           0 :       mooseError("fillFromInputVector called with unknown fill_method of ", fill_method);
     616             :   }
     617         138 : }
     618             : 
     619             : template <typename T>
     620             : void
     621           0 : RankFourTensorTempl<T>::fillAntisymmetricFromInputVector(const std::vector<T> & input)
     622             : {
     623           0 :   if (input.size() != 6)
     624           0 :     mooseError(
     625             :         "To use fillAntisymmetricFromInputVector, your input must have size 6.  Yours has size ",
     626           0 :         input.size());
     627             : 
     628           0 :   zero();
     629             : 
     630           0 :   (*this)(0, 1, 0, 1) = input[0]; // B1212
     631           0 :   (*this)(0, 1, 0, 2) = input[1]; // B1213
     632           0 :   (*this)(0, 1, 1, 2) = input[2]; // B1223
     633             : 
     634           0 :   (*this)(0, 2, 0, 2) = input[3]; // B1313
     635           0 :   (*this)(0, 2, 1, 2) = input[4]; // B1323
     636             : 
     637           0 :   (*this)(1, 2, 1, 2) = input[5]; // B2323
     638             : 
     639             :   // symmetry on the two pairs
     640           0 :   (*this)(0, 2, 0, 1) = (*this)(0, 1, 0, 2);
     641           0 :   (*this)(1, 2, 0, 1) = (*this)(0, 1, 1, 2);
     642           0 :   (*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           0 :   for (auto i : make_range(N))
     647           0 :     for (auto j : make_range(N))
     648             :     {
     649           0 :       (*this)(0, 1, j, i) = -(*this)(0, 1, i, j);
     650           0 :       (*this)(0, 2, j, i) = -(*this)(0, 2, i, j);
     651           0 :       (*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           0 :   for (auto i : make_range(N))
     657           0 :     for (auto j : make_range(N))
     658             :     {
     659           0 :       (*this)(1, 0, i, j) = -(*this)(0, 1, i, j);
     660           0 :       (*this)(2, 0, i, j) = -(*this)(0, 2, i, j);
     661           0 :       (*this)(2, 1, i, j) = -(*this)(1, 2, i, j);
     662             :     }
     663           0 : }
     664             : 
     665             : template <typename T>
     666             : void
     667           0 : RankFourTensorTempl<T>::fillGeneralIsotropicFromInputVector(const std::vector<T> & input)
     668             : {
     669           0 :   if (input.size() != 3)
     670           0 :     mooseError("To use fillGeneralIsotropicFromInputVector, your input must have size 3.  Yours "
     671             :                "has size ",
     672           0 :                input.size());
     673             : 
     674           0 :   fillGeneralIsotropic(input[0], input[1], input[2]);
     675           0 : }
     676             : 
     677             : template <typename T>
     678             : void
     679           0 : RankFourTensorTempl<T>::fillGeneralIsotropic(const T & i0, const T & i1, const T & i2)
     680             : {
     681           0 :   for (auto i : make_range(N))
     682           0 :     for (auto j : make_range(N))
     683           0 :       for (auto k : make_range(N))
     684           0 :         for (auto l : make_range(N))
     685             :         {
     686           0 :           (*this)(i, j, k, l) = i0 * Real(i == j) * Real(k == l) +
     687           0 :                                 i1 * Real(i == k) * Real(j == l) + i1 * Real(i == l) * Real(j == k);
     688           0 :           for (auto m : make_range(N))
     689           0 :             (*this)(i, j, k, l) +=
     690           0 :                 i2 * Real(PermutationTensor::eps(i, j, m)) * Real(PermutationTensor::eps(k, l, m));
     691             :         }
     692           0 : }
     693             : 
     694             : template <typename T>
     695             : void
     696           0 : RankFourTensorTempl<T>::fillAntisymmetricIsotropicFromInputVector(const std::vector<T> & input)
     697             : {
     698           0 :   if (input.size() != 1)
     699           0 :     mooseError("To use fillAntisymmetricIsotropicFromInputVector, your input must have size 1. "
     700             :                "Yours has size ",
     701           0 :                input.size());
     702             : 
     703           0 :   fillGeneralIsotropic(0.0, 0.0, input[0]);
     704           0 : }
     705             : 
     706             : template <typename T>
     707             : void
     708           0 : RankFourTensorTempl<T>::fillAntisymmetricIsotropic(const T & i0)
     709             : {
     710           0 :   fillGeneralIsotropic(0.0, 0.0, i0);
     711           0 : }
     712             : 
     713             : template <typename T>
     714             : void
     715           4 : RankFourTensorTempl<T>::fillSymmetricIsotropicFromInputVector(const std::vector<T> & input)
     716             : {
     717             :   mooseAssert(input.size() == 2,
     718             :               "To use fillSymmetricIsotropicFromInputVector, your input must have size 2.");
     719           4 :   fillSymmetricIsotropic(input[0], input[1]);
     720           4 : }
     721             : 
     722             : template <typename T>
     723             : void
     724           6 : RankFourTensorTempl<T>::fillSymmetricIsotropic(const T & lambda, const T & G)
     725             : {
     726             :   // clang-format off
     727          12 :   fillSymmetric21FromInputVector(std::array<T,21>
     728           6 :   {{lambda + 2.0 * G, lambda,           lambda,           0.0, 0.0, 0.0,
     729           6 :                       lambda + 2.0 * G, lambda,           0.0, 0.0, 0.0,
     730           6 :                                         lambda + 2.0 * G, 0.0, 0.0, 0.0,
     731           0 :                                                             G, 0.0, 0.0,
     732           0 :                                                                  G, 0.0,
     733             :                                                                       G}});
     734             :   // clang-format on
     735           6 : }
     736             : 
     737             : template <typename T>
     738             : void
     739           2 : RankFourTensorTempl<T>::fillSymmetricIsotropicEandNuFromInputVector(const std::vector<T> & input)
     740             : {
     741           2 :   if (input.size() != 2)
     742           0 :     mooseError(
     743             :         "To use fillSymmetricIsotropicEandNuFromInputVector, your input must have size 2. Yours "
     744             :         "has size ",
     745           0 :         input.size());
     746             : 
     747           2 :   fillSymmetricIsotropicEandNu(input[0], input[1]);
     748           2 : }
     749             : 
     750             : template <typename T>
     751             : void
     752           2 : RankFourTensorTempl<T>::fillSymmetricIsotropicEandNu(const T & E, const T & nu)
     753             : {
     754             :   // Calculate lambda and the shear modulus from the given young's modulus and poisson's ratio
     755           2 :   const T & lambda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu));
     756           2 :   const T & G = E / (2.0 * (1.0 + nu));
     757             : 
     758           2 :   fillSymmetricIsotropic(lambda, G);
     759           2 : }
     760             : 
     761             : template <typename T>
     762             : void
     763           2 : RankFourTensorTempl<T>::fillAxisymmetricRZFromInputVector(const std::vector<T> & input)
     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          20 :   fillSymmetric21FromInputVector(std::array<T,21>
     776           2 :   {{input[0],input[1],input[2],      0.0,      0.0, 0.0,
     777           2 :              input[0],input[2],      0.0,      0.0, 0.0,
     778           2 :                       input[3],      0.0,      0.0, 0.0,
     779           2 :                                 input[4],      0.0, 0.0,
     780           2 :                                           input[4], 0.0,
     781           2 :                                                     (input[0] - input[1]) * 0.5}});
     782             :   // clang-format on
     783           2 : }
     784             : 
     785             : template <typename T>
     786             : void
     787         118 : RankFourTensorTempl<T>::fillGeneralFromInputVector(const std::vector<T> & input)
     788             : {
     789         118 :   if (input.size() != 81)
     790           0 :     mooseError("To use fillGeneralFromInputVector, your input must have size 81. Yours has size ",
     791           0 :                input.size());
     792             : 
     793        9676 :   for (auto i : make_range(N4))
     794        9558 :     _vals[i] = input[i];
     795         118 : }
     796             : 
     797             : template <typename T>
     798             : void
     799           2 : RankFourTensorTempl<T>::fillPrincipalFromInputVector(const std::vector<T> & input)
     800             : {
     801           2 :   if (input.size() != 9)
     802           0 :     mooseError("To use fillPrincipalFromInputVector, your input must have size 9. Yours has size ",
     803           0 :                input.size());
     804             : 
     805           2 :   zero();
     806             : 
     807           2 :   (*this)(0, 0, 0, 0) = input[0];
     808           2 :   (*this)(0, 0, 1, 1) = input[1];
     809           2 :   (*this)(0, 0, 2, 2) = input[2];
     810           2 :   (*this)(1, 1, 0, 0) = input[3];
     811           2 :   (*this)(1, 1, 1, 1) = input[4];
     812           2 :   (*this)(1, 1, 2, 2) = input[5];
     813           2 :   (*this)(2, 2, 0, 0) = input[6];
     814           2 :   (*this)(2, 2, 1, 1) = input[7];
     815           2 :   (*this)(2, 2, 2, 2) = input[8];
     816           2 : }
     817             : 
     818             : template <typename T>
     819             : void
     820           2 : RankFourTensorTempl<T>::fillGeneralOrthotropicFromInputVector(const std::vector<T> & input)
     821             : {
     822           2 :   if (input.size() != 12)
     823           0 :     mooseError("To use fillGeneralOrhotropicFromInputVector, your input must have size 12. Yours "
     824             :                "has size ",
     825           0 :                input.size());
     826             : 
     827           2 :   const T & Ea = input[0];
     828           2 :   const T & Eb = input[1];
     829           2 :   const T & Ec = input[2];
     830           2 :   const T & Gab = input[3];
     831           2 :   const T & Gbc = input[4];
     832           2 :   const T & Gca = input[5];
     833           2 :   const T & nuba = input[6];
     834           2 :   const T & nuca = input[7];
     835           2 :   const T & nucb = input[8];
     836           2 :   const T & nuab = input[9];
     837           2 :   const T & nuac = input[10];
     838           2 :   const T & nubc = input[11];
     839             : 
     840             :   // Input must satisfy constraints.
     841           2 :   bool preserve_symmetry = MooseUtils::relativeFuzzyEqual(nuab * Eb, nuba * Ea) &&
     842           4 :                            MooseUtils::relativeFuzzyEqual(nuca * Ea, nuac * Ec) &&
     843           4 :                            MooseUtils::relativeFuzzyEqual(nubc * Ec, nucb * Eb);
     844             : 
     845           2 :   if (!preserve_symmetry)
     846           0 :     mooseError("Orthotropic elasticity tensor input is not consistent with symmetry requirements. "
     847             :                "Check input for accuracy");
     848             : 
     849           2 :   unsigned int ntens = N * (N + 1) / 2;
     850             : 
     851           2 :   std::vector<T> mat;
     852           2 :   mat.assign(ntens * ntens, 0);
     853             : 
     854           2 :   T k = 1 - nuab * nuba - nubc * nucb - nuca * nuac - nuab * nubc * nuca - nuba * nucb * nuac;
     855             : 
     856           2 :   bool is_positive_definite =
     857           2 :       (k > 0) && (1 - nubc * nucb) > 0 && (1 - nuac * nuca) > 0 && (1 - nuab * nuba) > 0;
     858           2 :   if (!is_positive_definite)
     859           0 :     mooseError("Orthotropic elasticity tensor input is not positive definite. Check input for "
     860             :                "accuracy");
     861             : 
     862           2 :   mat[0] = Ea * (1 - nubc * nucb) / k;
     863           2 :   mat[1] = Ea * (nubc * nuca + nuba) / k;
     864           2 :   mat[2] = Ea * (nuba * nucb + nuca) / k;
     865             : 
     866           2 :   mat[6] = Eb * (nuac * nucb + nuab) / k;
     867           2 :   mat[7] = Eb * (1 - nuac * nuca) / k;
     868           2 :   mat[8] = Eb * (nuab * nuca + nucb) / k;
     869             : 
     870           2 :   mat[12] = Ec * (nuab * nubc + nuac) / k;
     871           2 :   mat[13] = Ec * (nuac * nuba + nubc) / k;
     872           2 :   mat[14] = Ec * (1 - nuab * nuba) / k;
     873             : 
     874           2 :   mat[21] = 2 * Gab;
     875           2 :   mat[28] = 2 * Gca;
     876           2 :   mat[35] = 2 * Gbc;
     877             : 
     878             :   // Switching from Voigt to fourth order tensor
     879             :   // Copied from existing code (invSymm)
     880           2 :   int nskip = N - 1;
     881             : 
     882           2 :   unsigned int index = 0;
     883           8 :   for (auto i : make_range(N))
     884          24 :     for (auto j : make_range(N))
     885          72 :       for (auto k : make_range(N))
     886         216 :         for (auto l : make_range(N))
     887             :         {
     888         162 :           if (i == j)
     889          54 :             (*this)._vals[index] =
     890          54 :                 k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
     891             :           else
     892         180 :             (*this)._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
     893          72 :                                           : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
     894         162 :           index++;
     895             :         }
     896           2 : }
     897             : 
     898             : template <typename T>
     899             : RankTwoTensorTempl<T>
     900           0 : RankFourTensorTempl<T>::innerProductTranspose(const RankTwoTensorTempl<T> & b) const
     901             : {
     902           0 :   RankTwoTensorTempl<T> result;
     903             : 
     904           0 :   unsigned int index = 0;
     905           0 :   for (unsigned int ij = 0; ij < N2; ++ij)
     906             :   {
     907           0 :     T bb = b._coords[ij];
     908           0 :     for (unsigned int kl = 0; kl < N2; ++kl)
     909           0 :       result._coords[kl] += _vals[index++] * bb;
     910             :   }
     911             : 
     912           0 :   return result;
     913           0 : }
     914             : 
     915             : template <typename T>
     916             : T
     917           0 : RankFourTensorTempl<T>::contractionIj(unsigned int i,
     918             :                                       unsigned int j,
     919             :                                       const RankTwoTensorTempl<T> & M) const
     920             : {
     921           0 :   T val = 0;
     922           0 :   for (unsigned int k = 0; k < N; k++)
     923           0 :     for (unsigned int l = 0; l < N; l++)
     924           0 :       val += (*this)(i, j, k, l) * M(k, l);
     925             : 
     926           0 :   return val;
     927           0 : }
     928             : 
     929             : template <typename T>
     930             : T
     931           0 : RankFourTensorTempl<T>::contractionKl(unsigned int k,
     932             :                                       unsigned int l,
     933             :                                       const RankTwoTensorTempl<T> & M) const
     934             : {
     935           0 :   T val = 0;
     936           0 :   for (unsigned int i = 0; i < N; i++)
     937           0 :     for (unsigned int j = 0; j < N; j++)
     938           0 :       val += (*this)(i, j, k, l) * M(i, j);
     939             : 
     940           0 :   return val;
     941           0 : }
     942             : 
     943             : template <typename T>
     944             : T
     945           2 : RankFourTensorTempl<T>::sum3x3() const
     946             : {
     947             :   // used in the volumetric locking correction
     948           2 :   T sum = 0;
     949           8 :   for (auto i : make_range(N))
     950          24 :     for (auto j : make_range(N))
     951          18 :       sum += (*this)(i, i, j, j);
     952           2 :   return sum;
     953           0 : }
     954             : 
     955             : template <typename T>
     956             : libMesh::VectorValue<T>
     957           2 : RankFourTensorTempl<T>::sum3x1() const
     958             : {
     959             :   // used for volumetric locking correction
     960           2 :   libMesh::VectorValue<T> a(3);
     961           2 :   a(0) = (*this)(0, 0, 0, 0) + (*this)(0, 0, 1, 1) + (*this)(0, 0, 2, 2); // C0000 + C0011 + C0022
     962           2 :   a(1) = (*this)(1, 1, 0, 0) + (*this)(1, 1, 1, 1) + (*this)(1, 1, 2, 2); // C1100 + C1111 + C1122
     963           2 :   a(2) = (*this)(2, 2, 0, 0) + (*this)(2, 2, 1, 1) + (*this)(2, 2, 2, 2); // C2200 + C2211 + C2222
     964           2 :   return a;
     965           0 : }
     966             : 
     967             : template <typename T>
     968             : RankFourTensorTempl<T>
     969           0 : RankFourTensorTempl<T>::tripleProductJkl(const RankTwoTensorTempl<T> & A,
     970             :                                          const RankTwoTensorTempl<T> & B,
     971             :                                          const RankTwoTensorTempl<T> & C) const
     972             : {
     973           0 :   RankFourTensorTempl<T> R;
     974           0 :   for (unsigned int i = 0; i < N; i++)
     975           0 :     for (unsigned int j = 0; j < N; j++)
     976           0 :       for (unsigned int k = 0; k < N; k++)
     977           0 :         for (unsigned int l = 0; l < N; l++)
     978           0 :           for (unsigned int m = 0; m < N; m++)
     979           0 :             for (unsigned int n = 0; n < N; n++)
     980           0 :               for (unsigned int t = 0; t < N; t++)
     981           0 :                 R(i, j, k, l) += (*this)(i, m, n, t) * A(j, m) * B(k, n) * C(l, t);
     982             : 
     983           0 :   return R;
     984           0 : }
     985             : 
     986             : template <typename T>
     987             : RankFourTensorTempl<T>
     988           0 : RankFourTensorTempl<T>::tripleProductIkl(const RankTwoTensorTempl<T> & A,
     989             :                                          const RankTwoTensorTempl<T> & B,
     990             :                                          const RankTwoTensorTempl<T> & C) const
     991             : {
     992           0 :   RankFourTensorTempl<T> R;
     993           0 :   for (unsigned int i = 0; i < N; i++)
     994           0 :     for (unsigned int j = 0; j < N; j++)
     995           0 :       for (unsigned int k = 0; k < N; k++)
     996           0 :         for (unsigned int l = 0; l < N; l++)
     997           0 :           for (unsigned int m = 0; m < N; m++)
     998           0 :             for (unsigned int n = 0; n < N; n++)
     999           0 :               for (unsigned int t = 0; t < N; t++)
    1000           0 :                 R(i, j, k, l) += (*this)(m, j, n, t) * A(i, m) * B(k, n) * C(l, t);
    1001             : 
    1002           0 :   return R;
    1003           0 : }
    1004             : 
    1005             : template <typename T>
    1006             : RankFourTensorTempl<T>
    1007           0 : RankFourTensorTempl<T>::tripleProductIjl(const RankTwoTensorTempl<T> & A,
    1008             :                                          const RankTwoTensorTempl<T> & B,
    1009             :                                          const RankTwoTensorTempl<T> & C) const
    1010             : {
    1011           0 :   RankFourTensorTempl<T> R;
    1012           0 :   for (unsigned int i = 0; i < N; i++)
    1013           0 :     for (unsigned int j = 0; j < N; j++)
    1014           0 :       for (unsigned int k = 0; k < N; k++)
    1015           0 :         for (unsigned int l = 0; l < N; l++)
    1016           0 :           for (unsigned int m = 0; m < N; m++)
    1017           0 :             for (unsigned int n = 0; n < N; n++)
    1018           0 :               for (unsigned int t = 0; t < N; t++)
    1019           0 :                 R(i, j, k, l) += (*this)(m, n, k, t) * A(i, m) * B(j, n) * C(l, t);
    1020             : 
    1021           0 :   return R;
    1022           0 : }
    1023             : 
    1024             : template <typename T>
    1025             : RankFourTensorTempl<T>
    1026           0 : RankFourTensorTempl<T>::tripleProductIjk(const RankTwoTensorTempl<T> & A,
    1027             :                                          const RankTwoTensorTempl<T> & B,
    1028             :                                          const RankTwoTensorTempl<T> & C) const
    1029             : {
    1030           0 :   RankFourTensorTempl<T> R;
    1031           0 :   for (unsigned int i = 0; i < N; i++)
    1032           0 :     for (unsigned int j = 0; j < N; j++)
    1033           0 :       for (unsigned int k = 0; k < N; k++)
    1034           0 :         for (unsigned int l = 0; l < N; l++)
    1035           0 :           for (unsigned int m = 0; m < N; m++)
    1036           0 :             for (unsigned int n = 0; n < N; n++)
    1037           0 :               for (unsigned int t = 0; t < N; t++)
    1038           0 :                 R(i, j, k, l) += (*this)(m, n, t, l) * A(i, m) * B(j, n) * C(k, t);
    1039             : 
    1040           0 :   return R;
    1041           0 : }
    1042             : 
    1043             : template <typename T>
    1044             : RankFourTensorTempl<T>
    1045           0 : RankFourTensorTempl<T>::singleProductI(const RankTwoTensorTempl<T> & A) const
    1046             : {
    1047           0 :   RankFourTensorTempl<T> R;
    1048             : 
    1049           0 :   for (unsigned int i = 0; i < N; i++)
    1050           0 :     for (unsigned int j = 0; j < N; j++)
    1051           0 :       for (unsigned int k = 0; k < N; k++)
    1052           0 :         for (unsigned int l = 0; l < N; l++)
    1053           0 :           for (unsigned int m = 0; m < N; m++)
    1054           0 :             R(i, j, k, l) += (*this)(m, j, k, l) * A(i, m);
    1055             : 
    1056           0 :   return R;
    1057           0 : }
    1058             : 
    1059             : template <typename T>
    1060             : RankFourTensorTempl<T>
    1061           0 : RankFourTensorTempl<T>::singleProductJ(const RankTwoTensorTempl<T> & A) const
    1062             : {
    1063           0 :   RankFourTensorTempl<T> R;
    1064             : 
    1065           0 :   for (unsigned int i = 0; i < N; i++)
    1066           0 :     for (unsigned int j = 0; j < N; j++)
    1067           0 :       for (unsigned int k = 0; k < N; k++)
    1068           0 :         for (unsigned int l = 0; l < N; l++)
    1069           0 :           for (unsigned int m = 0; m < N; m++)
    1070           0 :             R(i, j, k, l) += (*this)(i, m, k, l) * A(j, m);
    1071             : 
    1072           0 :   return R;
    1073           0 : }
    1074             : 
    1075             : template <typename T>
    1076             : RankFourTensorTempl<T>
    1077           0 : RankFourTensorTempl<T>::singleProductK(const RankTwoTensorTempl<T> & A) const
    1078             : {
    1079           0 :   RankFourTensorTempl<T> R;
    1080             : 
    1081           0 :   for (unsigned int i = 0; i < N; i++)
    1082           0 :     for (unsigned int j = 0; j < N; j++)
    1083           0 :       for (unsigned int k = 0; k < N; k++)
    1084           0 :         for (unsigned int l = 0; l < N; l++)
    1085           0 :           for (unsigned int m = 0; m < N; m++)
    1086           0 :             R(i, j, k, l) += (*this)(i, j, m, l) * A(k, m);
    1087             : 
    1088           0 :   return R;
    1089           0 : }
    1090             : 
    1091             : template <typename T>
    1092             : RankFourTensorTempl<T>
    1093           0 : RankFourTensorTempl<T>::singleProductL(const RankTwoTensorTempl<T> & A) const
    1094             : {
    1095           0 :   RankFourTensorTempl<T> R;
    1096             : 
    1097           0 :   for (unsigned int i = 0; i < N; i++)
    1098           0 :     for (unsigned int j = 0; j < N; j++)
    1099           0 :       for (unsigned int k = 0; k < N; k++)
    1100           0 :         for (unsigned int l = 0; l < N; l++)
    1101           0 :           for (unsigned int m = 0; m < N; m++)
    1102           0 :             R(i, j, k, l) += (*this)(i, j, k, m) * A(l, m);
    1103             : 
    1104           0 :   return R;
    1105           0 : }
    1106             : 
    1107             : template <typename T>
    1108             : bool
    1109           2 : RankFourTensorTempl<T>::isSymmetric() const
    1110             : {
    1111           6 :   for (auto i : make_range(1u, N))
    1112          10 :     for (auto j : make_range(i))
    1113          18 :       for (auto k : make_range(1u, N))
    1114          30 :         for (auto l : make_range(k))
    1115             :         {
    1116             :           // minor symmetries
    1117          36 :           if ((*this)(i, j, k, l) != (*this)(j, i, k, l) ||
    1118          18 :               (*this)(i, j, k, l) != (*this)(i, j, l, k))
    1119           0 :             return false;
    1120             : 
    1121             :           // major symmetry
    1122          18 :           if ((*this)(i, j, k, l) != (*this)(k, l, i, j))
    1123           0 :             return false;
    1124             :         }
    1125           2 :   return true;
    1126             : }
    1127             : 
    1128             : template <typename T>
    1129             : bool
    1130           2 : RankFourTensorTempl<T>::isIsotropic() const
    1131             : {
    1132             :   // prerequisite is symmetry
    1133           2 :   if (!isSymmetric())
    1134           0 :     return false;
    1135             : 
    1136             :   // inspect shear components
    1137           2 :   const T & mu = (*this)(0, 1, 0, 1);
    1138             :   // ...diagonal
    1139           2 :   if ((*this)(1, 2, 1, 2) != mu || (*this)(2, 0, 2, 0) != mu)
    1140           0 :     return false;
    1141             :   // ...off-diagonal
    1142           2 :   if ((*this)(2, 0, 1, 2) != 0.0 || (*this)(0, 1, 1, 2) != 0.0 || (*this)(0, 1, 2, 0) != 0.0)
    1143           0 :     return false;
    1144             : 
    1145             :   // off diagonal blocks in Voigt
    1146           8 :   for (auto i : make_range(N))
    1147          24 :     for (auto j : make_range(N))
    1148          18 :       if (_vals[i * (N3 + N2) + ((j + 1) % N) * N + (j + 2) % N] != 0.0)
    1149           0 :         return false;
    1150             : 
    1151             :   // top left block
    1152           2 :   const T & K1 = (*this)(0, 0, 0, 0);
    1153           2 :   const T & K2 = (*this)(0, 0, 1, 1);
    1154           2 :   if (!MooseUtils::relativeFuzzyEqual(K1 - 4.0 * mu / 3.0, K2 + 2.0 * mu / 3.0))
    1155           0 :     return false;
    1156           2 :   if ((*this)(1, 1, 1, 1) != K1 || (*this)(2, 2, 2, 2) != K1)
    1157           0 :     return false;
    1158           6 :   for (auto i : make_range(1u, N))
    1159          10 :     for (auto j : make_range(i))
    1160           6 :       if ((*this)(i, i, j, j) != K2)
    1161           0 :         return false;
    1162             : 
    1163           2 :   return true;
    1164             : }

Generated by: LCOV version 1.14