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

Generated by: LCOV version 1.14