LCOV - code coverage report
Current view: top level - include/utils - RankTwoTensor.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 96 113 85.0 %
Date: 2026-05-29 20:35:17 Functions: 37 71 52.1 %
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 "Moose.h"
      13             : #include "MooseTypes.h"
      14             : #include "ADRankTwoTensorForward.h"
      15             : #include "ADRankFourTensorForward.h"
      16             : #include "ADRankThreeTensorForward.h"
      17             : #include "ADSymmetricRankTwoTensorForward.h"
      18             : #include "MooseUtils.h"
      19             : #include "MathUtils.h"
      20             : 
      21             : // Any requisite includes here
      22             : #include "libmesh/libmesh.h"
      23             : #include "libmesh/tensor_value.h"
      24             : #include "libmesh/vector_value.h"
      25             : #include "libmesh/int_range.h"
      26             : 
      27             : #include "metaphysicl/raw_type.h"
      28             : 
      29             : #include <petscsys.h>
      30             : #include <vector>
      31             : 
      32             : // Forward declarations
      33             : class MooseEnum;
      34             : template <typename T>
      35             : class MooseArray;
      36             : typedef MooseArray<Real> VariableValue;
      37             : template <typename>
      38             : class ColumnMajorMatrixTempl;
      39             : namespace libMesh
      40             : {
      41             : template <typename>
      42             : class TypeVector;
      43             : template <typename>
      44             : class TypeTensor;
      45             : template <typename>
      46             : class TensorValue;
      47             : namespace TensorTools
      48             : {
      49             : template <>
      50             : struct IncrementRank<RankTwoTensor>
      51             : {
      52             :   typedef RankThreeTensor type;
      53             : };
      54             : }
      55             : }
      56             : 
      57             : namespace MathUtils
      58             : {
      59             : template <typename T>
      60             : void mooseSetToZero(T & v);
      61             : 
      62             : /**
      63             :  * Helper function template specialization to set an object to zero.
      64             :  * Needed by DerivativeMaterialInterface
      65             :  */
      66             : template <>
      67             : void mooseSetToZero<RankTwoTensor>(RankTwoTensor & v);
      68             : 
      69             : /**
      70             :  * Helper function template specialization to set an object to zero.
      71             :  * Needed by DerivativeMaterialInterface
      72             :  */
      73             : template <>
      74             : void mooseSetToZero<ADRankTwoTensor>(ADRankTwoTensor & v);
      75             : }
      76             : 
      77             : /**
      78             :  * RankTwoTensorTempl is designed to handle the Stress or Strain Tensor for a fully anisotropic
      79             :  * material. It is designed to allow for maximum clarity of the mathematics and ease of use.
      80             :  * Original class authors: A. M. Jokisaari, O. Heinonen, M. R. Tonks
      81             :  *
      82             :  * RankTwoTensorTempl holds the 9 separate Sigma_ij or Epsilon_ij entries.
      83             :  * The entries are accessed by index, with i, j equal to 1, 2, or 3, or
      84             :  * internally i, j = 0, 1, 2.
      85             :  */
      86             : template <typename T>
      87             : class RankTwoTensorTempl : public libMesh::TensorValue<T>
      88             : {
      89             : public:
      90             :   /// @{
      91             : 
      92             :   /**
      93             :    * @brief Tensor dimension, i.e. number of rows/columns of the second order tensor
      94             :    */
      95             :   static constexpr unsigned int N = Moose::dim;
      96             : 
      97             :   /**
      98             :    * @brief The square of the tensor dimension.
      99             :    */
     100             :   static constexpr unsigned int N2 = N * N;
     101             : 
     102             :   /**
     103             :    * @brief The initialization method.
     104             :    * @see fillFromInputVector()
     105             :    * @see RankTwoTensorTempl(const std::vector<T> &)
     106             :    */
     107             :   enum InitMethod
     108             :   {
     109             :     initNone,
     110             :     initIdentity
     111             :   };
     112             : 
     113             :   /**
     114             :    * To fill up the 9 entries in the 2nd-order tensor, fillFromInputVector
     115             :    * is called with one of the following fill_methods.
     116             :    * See the fill*FromInputVector functions for more details
     117             :    */
     118             :   enum FillMethod
     119             :   {
     120             :     autodetect = 0,
     121             :     isotropic1 = 1,
     122             :     diagonal3 = 3,
     123             :     symmetric6 = 6,
     124             :     general = 9
     125             :   };
     126             : 
     127             :   /**
     128             :    * @brief Initialize the random seed based on an unsigned integer.
     129             :    * Deprecated in favor of MooseRandom::seed().
     130             :    */
     131             :   static void initRandom(unsigned int);
     132             : 
     133             :   /**
     134             :    * @brief Get the available `FillMethod` options.
     135             :    *
     136             :    * This method is useful in validParams().
     137             :    */
     138             :   [[nodiscard]] static MooseEnum fillMethodEnum();
     139             : 
     140             :   /**
     141             :    * @brief Fill a `libMesh::TensorValue<T>` from this second order tensor
     142             :    */
     143             :   void fillRealTensor(libMesh::TensorValue<T> &);
     144             : 
     145             :   /// Print the rank two tensor
     146             :   void print(std::ostream & stm = Moose::out) const;
     147             : 
     148             :   /// Print the Real part of the RankTwoTensorTempl<ADReal>
     149             :   void printReal(std::ostream & stm = Moose::out) const;
     150             : 
     151             :   /// Print the Real part of the RankTwoTensorTempl<ADReal> along with its first nDual dual numbers
     152             :   void printADReal(unsigned int nDual, std::ostream & stm = Moose::out) const;
     153             : 
     154             :   /// @}
     155             : 
     156             :   /// @{
     157             : 
     158             :   /**
     159             :    * @brief Empty constructor; fills to zero
     160             :    *
     161             :    * \f$ A_{ij} = 0 \f$
     162             :    *
     163             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     164             :    * RankTwoTensor A;
     165             :    * // A = [ 0 0 0
     166             :    * //       0 0 0
     167             :    * //       0 0 0 ]
     168             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     169             :    */
     170             :   RankTwoTensorTempl();
     171             : 
     172             :   /**
     173             :    * @brief Select specific initialization pattern.
     174             :    *
     175             :    * `initNone` initializes an empty second order tensor.
     176             :    *
     177             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     178             :    * RankTwoTensor A(RankTwoTensor::initNone);
     179             :    * // A = [ 0 0 0
     180             :    * //       0 0 0
     181             :    * //       0 0 0 ]
     182             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     183             :    *
     184             :    * `initIdentity` initializes a second order identity tensor.
     185             :    *
     186             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     187             :    * RankTwoTensor A(RankTwoTensor::initIdentity);
     188             :    * // A = [ 1 0 0
     189             :    * //       0 1 0
     190             :    * //       0 0 1 ]
     191             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     192             :    */
     193             :   RankTwoTensorTempl(const InitMethod);
     194             : 
     195             :   /**
     196             :    * @brief Initialize from row vectors.
     197             :    * @see initializeFromRows()
     198             :    *
     199             :    * Deprecated in favor of initializeFromRows()
     200             :    *
     201             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     202             :    * RealVectorValue row1(1, 2, 3);
     203             :    * RealVectorValue row2(4, 5, 6);
     204             :    * RealVectorValue row3(7, 8, 9);
     205             :    * RankTwoTensor A(row1, row2, row3);
     206             :    * // A = [ 1 2 3
     207             :    * //       4 5 6
     208             :    * //       7 8 9 ]
     209             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     210             :    */
     211             :   RankTwoTensorTempl(const libMesh::TypeVector<T> & row1,
     212             :                      const libMesh::TypeVector<T> & row2,
     213             :                      const libMesh::TypeVector<T> & row3);
     214             : 
     215             :   /**
     216             :    * @brief Constructor that proxies the fillFromInputVector() method
     217             :    * @see fillFromInputVector()
     218             :    */
     219          18 :   RankTwoTensorTempl(const std::vector<T> & input) { this->fillFromInputVector(input); };
     220             : 
     221             :   /**
     222             :    * @brief Initialize a symmetric second order tensor using the 6 arguments.
     223             :    *
     224             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     225             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6);
     226             :    * // A = [ 1 6 5
     227             :    * //       6 2 4
     228             :    * //       5 4 3 ]
     229             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     230             :    */
     231             :   RankTwoTensorTempl(
     232             :       const T & S11, const T & S22, const T & S33, const T & S23, const T & S13, const T & S12);
     233             : 
     234             :   /**
     235             :    * @brief Initialize a second order tensor using the 9 arguments in a column-major fashion.
     236             :    *
     237             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     238             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     239             :    * // A = [ 1 4 7
     240             :    * //       2 5 8
     241             :    * //       3 6 9 ]
     242             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     243             :    */
     244             :   RankTwoTensorTempl(const T & S11,
     245             :                      const T & S21,
     246             :                      const T & S31,
     247             :                      const T & S12,
     248             :                      const T & S22,
     249             :                      const T & S32,
     250             :                      const T & S13,
     251             :                      const T & S23,
     252             :                      const T & S33);
     253             : 
     254             :   /**
     255             :    * @brief The copy constructor
     256             :    *
     257             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     258             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     259             :    * RankTwoTensor B = A;
     260             :    * // A = B = [ 1 4 7
     261             :    * //           2 5 8
     262             :    * //           3 6 9 ]
     263             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     264             :    */
     265          44 :   RankTwoTensorTempl(const RankTwoTensorTempl<T> & a) = default;
     266             : 
     267             :   /**
     268             :    * @brief The conversion operator from a `libMesh::TensorValue`
     269             :    */
     270      274992 :   RankTwoTensorTempl(const libMesh::TensorValue<T> & a) : libMesh::TensorValue<T>(a) {}
     271             : 
     272             :   /**
     273             :    * @brief The conversion operator from a `libMesh::TypeTensor`
     274             :    */
     275      802938 :   RankTwoTensorTempl(const libMesh::TypeTensor<T> & a) : libMesh::TensorValue<T>(a) {}
     276             : 
     277             :   /**
     278             :    * @brief The conversion operator from a `SymmetricRankTwoTensorTempl`
     279             :    */
     280             :   template <typename T2>
     281           4 :   RankTwoTensorTempl(const SymmetricRankTwoTensorTempl<T2> & a)
     282           0 :     : RankTwoTensorTempl<T>(a(0),
     283           4 :                             a(1),
     284           4 :                             a(2),
     285           4 :                             a(3) / MathUtils::sqrt2,
     286           4 :                             a(4) / MathUtils::sqrt2,
     287           8 :                             a(5) / MathUtils::sqrt2)
     288             :   {
     289           4 :   }
     290             : 
     291             :   /**
     292             :    * @brief The conversion operator from `RankTwoTensorTempl<T2>` to `RankTwoTensorTempl<T>` where
     293             :    * `T2` is convertible to `T`.
     294             :    */
     295             :   template <typename T2>
     296          39 :   RankTwoTensorTempl(const RankTwoTensorTempl<T2> & a) : libMesh::TensorValue<T>(a)
     297             :   {
     298          39 :   }
     299             :   /// @} end Constructor
     300             : 
     301             :   /// @{
     302             : 
     303             :   /**
     304             :    * @brief Named constructor for initializing symmetrically.
     305             :    *
     306             :    * The supplied vectors are used as row and column vectors to construct two tensors respectively,
     307             :    * that are averaged to create a symmetric tensor.
     308             :    *
     309             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     310             :    * RealVectorValue row1(1, 2, 3);
     311             :    * RealVectorValue row2(4, 5, 6);
     312             :    * RealVectorValue row3(7, 8, 9);
     313             :    * auto A = RankTwoTensor::initializeSymmetric(row1, row2, row3);
     314             :    * // A = [ 1 3 5
     315             :    * //       3 5 7
     316             :    * //       5 7 9 ]
     317             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     318             :    */
     319             :   [[nodiscard]] static RankTwoTensorTempl initializeSymmetric(const libMesh::TypeVector<T> & v0,
     320             :                                                               const libMesh::TypeVector<T> & v1,
     321             :                                                               const libMesh::TypeVector<T> & v2);
     322             : 
     323             :   /**
     324             :    * @brief Named constructor for initializing from row vectors.
     325             :    *
     326             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     327             :    * RealVectorValue row1(1, 2, 3);
     328             :    * RealVectorValue row2(4, 5, 6);
     329             :    * RealVectorValue row3(7, 8, 9);
     330             :    * auto A = RankTwoTensor::initializeFromRows(row1, row2, row3);
     331             :    * // A = [ 1 2 3
     332             :    * //       4 5 6
     333             :    * //       7 8 9 ]
     334             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     335             :    */
     336             :   [[nodiscard]] static RankTwoTensorTempl initializeFromRows(const libMesh::TypeVector<T> & row0,
     337             :                                                              const libMesh::TypeVector<T> & row1,
     338             :                                                              const libMesh::TypeVector<T> & row2);
     339             : 
     340             :   /**
     341             :    * @brief Named constructor for initializing from row vectors.
     342             :    *
     343             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     344             :    * RealVectorValue col1(1, 2, 3);
     345             :    * RealVectorValue col2(4, 5, 6);
     346             :    * RealVectorValue col3(7, 8, 9);
     347             :    * auto A = RankTwoTensor::initializeFromColumns(col1, col2, col3);
     348             :    * // A = [ 1 4 7
     349             :    * //       2 5 8
     350             :    * //       3 6 9 ]
     351             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     352             :    */
     353             :   [[nodiscard]] static RankTwoTensorTempl
     354             :   initializeFromColumns(const libMesh::TypeVector<T> & col0,
     355             :                         const libMesh::TypeVector<T> & col1,
     356             :                         const libMesh::TypeVector<T> & col2);
     357             : 
     358             :   /**
     359             :    * @brief Initialize a second order identity tensor.
     360             :    *
     361             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     362             :    * auto A = RankTwoTensor::Identity();
     363             :    * // A = [ 1 0 0
     364             :    * //       0 1 0
     365             :    * //       0 0 1 ]
     366             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     367             :    */
     368           0 :   [[nodiscard]] static RankTwoTensorTempl Identity() { return RankTwoTensorTempl(initIdentity); }
     369             : 
     370             :   /**
     371             :    * @brief Initialize a second order tensor with expression \f$ B_{ij} = F_{ij} F_{ji} \f$.
     372             :    *
     373             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     374             :    * RankTwoTensor F(1, 2, 3, 4, 5, 6, 7, 8, 9);
     375             :    * // F = [ 1 4 7
     376             :    * //       2 5 8
     377             :    * //       3 6 9 ]
     378             :    * auto B = RankTwoTensor::timesTranspose(F);
     379             :    * // B = [ 66 78  90
     380             :    * //       78 93  108
     381             :    * //       90 108 126 ]
     382             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     383             :    */
     384             :   [[nodiscard]] static RankTwoTensorTempl<T> timesTranspose(const RankTwoTensorTempl<T> &);
     385             : 
     386             :   /**
     387             :    * @brief Initialize a second order tensor with expression \f$ C_{ij} = F_{ji} F_{ij} \f$.
     388             :    *
     389             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     390             :    * RankTwoTensor F(1, 2, 3, 4, 5, 6, 7, 8, 9);
     391             :    * // F = [ 1 4 7
     392             :    * //       2 5 8
     393             :    * //       3 6 9 ]
     394             :    * auto C = RankTwoTensor::transposeTimes(F);
     395             :    * // C = [ 14 32  50
     396             :    * //       32 77  122
     397             :    * //       50 122 194 ]
     398             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     399             :    */
     400             :   [[nodiscard]] static RankTwoTensorTempl<T> transposeTimes(const RankTwoTensorTempl<T> &);
     401             : 
     402             :   /**
     403             :    * @brief Initialize a second order tensor with expression \f$ E_{ij} = C_{ij} + C_{ji} \f$.
     404             :    *
     405             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     406             :    * RankTwoTensor C(1, 2, 3, 4, 5, 6, 7, 8, 9);
     407             :    * // C = [ 1 4 7
     408             :    * //       2 5 8
     409             :    * //       3 6 9 ]
     410             :    * auto E = RankTwoTensor::plusTranspose(C);
     411             :    * // E = [ 2  6  10
     412             :    * //       6  10 14
     413             :    * //       10 14 18 ]
     414             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     415             :    */
     416             :   [[nodiscard]] static RankTwoTensorTempl<T> plusTranspose(const RankTwoTensorTempl<T> &);
     417             : 
     418             :   /**
     419             :    * @brief Initialize a second order tensor as the outer product of two vectors, i.e. \f$ A_{ij} =
     420             :    * a_i b_j \f$.
     421             :    *
     422             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     423             :    * RealVectorValue a(1, 2, 3);
     424             :    * RealVectorValue b(4, 5, 6);
     425             :    * auto A = RankTwoTensor::outerProduct(a, b);
     426             :    * // A = [ 4  5  6
     427             :    * //       8  10 12
     428             :    * //       12 15 18 ]
     429             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     430             :    */
     431             :   [[nodiscard]] static RankTwoTensorTempl<T> outerProduct(const libMesh::TypeVector<T> &,
     432             :                                                           const libMesh::TypeVector<T> &);
     433             : 
     434             :   /**
     435             :    * @brief Initialize a second order tensor as the outer product of a vector with itself, i.e. \f$
     436             :    * A_{ij} = a_i a_j \f$.
     437             :    *
     438             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     439             :    * RealVectorValue a(1, 2, 3);
     440             :    * auto A = RankTwoTensor::selfOuterProduct(a);
     441             :    * // A = [ 1 2 3
     442             :    * //       2 4 6
     443             :    * //       3 6 9 ]
     444             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     445             :    */
     446             :   [[nodiscard]] static RankTwoTensorTempl<T> selfOuterProduct(const libMesh::TypeVector<T> &);
     447             : 
     448             :   /**
     449             :    * @brief Generate a random second order tensor with all 9 components treated as independent
     450             :    * random variables following a Gaussian distribution.
     451             :    *
     452             :    * @param stddev The standard deviation of the Gaussian distribution
     453             :    * @param mean   The mean of the Gaussian distribution
     454             :    */
     455             :   [[nodiscard]] static RankTwoTensorTempl<T> genRandomTensor(T stddev, T mean);
     456             : 
     457             :   /**
     458             :    * @brief Generate a random symmetric second order tensor with the 6 upper-triangular components
     459             :    * treated as independent random variables following a Gaussian distribution.
     460             :    *
     461             :    * @param stddev The standard deviation of the Gaussian distribution
     462             :    * @param mean   The mean of the Gaussian distribution
     463             :    */
     464             :   [[nodiscard]] static RankTwoTensorTempl<T> genRandomSymmTensor(T stddev, T mean);
     465             : 
     466             :   /// @}
     467             : 
     468             :   /// @{
     469             : 
     470             :   /**
     471             :    * @brief Get the i-th column of the second order tensor.
     472             :    *
     473             :    * @param i The column number, i = 0, 1, 2
     474             :    * @return The i-th column of the second order tensor.
     475             :    *
     476             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     477             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     478             :    * RealVectorValue col = A.column(1);
     479             :    * // col = [ 4
     480             :    * //         5
     481             :    * //         6 ]
     482             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     483             :    */
     484             :   libMesh::VectorValue<T> column(const unsigned int i) const;
     485             : 
     486             :   /// @}
     487             : 
     488             :   /// @{
     489             : 
     490             :   /**
     491             :    * @brief Assignment operator
     492             :    *
     493             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     494             :    * RankTwoTensor A;
     495             :    * // A = [ 0 0 0
     496             :    * //       0 0 0
     497             :    * //       0 0 0 ]
     498             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
     499             :    * // B = [ 9 6 3
     500             :    * //       8 5 2
     501             :    * //       7 4 1 ]
     502             :    * A = B;
     503             :    * // A = [ 9 6 3
     504             :    * //       8 5 2
     505             :    * //       7 4 1 ]
     506             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     507             :    */
     508      100472 :   RankTwoTensorTempl<T> & operator=(const RankTwoTensorTempl<T> & a) = default;
     509             : 
     510             :   /**
     511             :    * @brief Assignment operator (from a ColumnMajorMatrixTempl<T>)
     512             :    *
     513             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     514             :    * RankTwoTensor A;
     515             :    * // A = [ 0 0 0
     516             :    * //       0 0 0
     517             :    * //       0 0 0 ]
     518             :    * RealVectorValue col1(1, 2, 3);
     519             :    * RealVectorValue col2(4, 5, 6);
     520             :    * RealVectorValue col3(7, 8, 9);
     521             :    * ColumnMajorMatrix B(col1, col2, col3);
     522             :    * // B = [ 1 4 7
     523             :    * //       2 5 8
     524             :    * //       3 6 9 ]
     525             :    * A = B;
     526             :    * // A = [ 1 4 7
     527             :    * //       2 5 8
     528             :    * //       3 6 9 ]
     529             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     530             :    */
     531             :   RankTwoTensorTempl<T> & operator=(const ColumnMajorMatrixTempl<T> & a);
     532             : 
     533             :   /**
     534             :    * @brief Assignment-from-scalar operator.  Used only to zero out the tensor.
     535             :    *
     536             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     537             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     538             :    * // A = [ 1 2 3
     539             :    * //       2 4 6
     540             :    * //       3 6 9 ]
     541             :    * A = 0;
     542             :    * // A = [ 0 0 0
     543             :    * //       0 0 0
     544             :    * //       0 0 0 ]
     545             :    * A = 1;
     546             :    * // This triggers an assertion failure.
     547             :    * A = 0.0;
     548             :    * // This triggers an assertion failure.
     549             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     550             :    */
     551             :   template <typename Scalar>
     552             :   typename std::enable_if<libMesh::ScalarTraits<Scalar>::value, RankTwoTensorTempl &>::type
     553             :   operator=(const Scalar & libmesh_dbg_var(p))
     554             :   {
     555             :     libmesh_assert_equal_to(p, Scalar(0));
     556             :     this->zero();
     557             :     return *this;
     558             :   }
     559             : 
     560             :   /**
     561             :    * Add another second order tensor to this one
     562             :    *
     563             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     564             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     565             :    * // A = [ 1 2 3
     566             :    * //       2 4 6
     567             :    * //       3 6 9 ]
     568             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
     569             :    * // B = [ 9 6 3
     570             :    * //       8 5 2
     571             :    * //       7 4 1 ]
     572             :    * A += B;
     573             :    * // A = [ 10 8  6
     574             :    * //       10 9  8
     575             :    * //       10 10 10 ]
     576             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     577             :    */
     578             :   RankTwoTensorTempl<T> & operator+=(const RankTwoTensorTempl<T> & a);
     579             : 
     580             :   /**
     581             :    * Subtract another second order tensor from this one
     582             :    *
     583             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     584             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     585             :    * // A = [ 1 2 3
     586             :    * //       2 4 6
     587             :    * //       3 6 9 ]
     588             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
     589             :    * // B = [ 9 6 3
     590             :    * //       8 5 2
     591             :    * //       7 4 1 ]
     592             :    * A -= B;
     593             :    * // A = [ -8 -4 0
     594             :    * //       -6 -1 4
     595             :    * //       -4  2 8 ]
     596             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     597             :    */
     598             :   RankTwoTensorTempl<T> & operator-=(const RankTwoTensorTempl<T> & a);
     599             : 
     600             :   /**
     601             :    * Multiply this tensor by a scalar (component-wise)
     602             :    *
     603             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     604             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     605             :    * // A = [ 1 2 3
     606             :    * //       2 4 6
     607             :    * //       3 6 9 ]
     608             :    * A *= 2;
     609             :    * // A = [ 2 4  6
     610             :    * //       4 8  12
     611             :    * //       6 12 18 ]
     612             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     613             :    */
     614             :   RankTwoTensorTempl<T> & operator*=(const T & a);
     615             : 
     616             :   /**
     617             :    * Divide this tensor by a scalar (component-wise)
     618             :    *
     619             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     620             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     621             :    * // A = [ 1 2 3
     622             :    * //       2 4 6
     623             :    * //       3 6 9 ]
     624             :    * A /= 2;
     625             :    * // A = [ 0.5 1.0 1.5
     626             :    * //       1.0 2.0 3.0
     627             :    * //       1.5 3.0 4.5 ]
     628             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     629             :    */
     630             :   RankTwoTensorTempl<T> & operator/=(const T & a);
     631             : 
     632             :   /**
     633             :    * Multiplication with another second order tensor
     634             :    *
     635             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     636             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     637             :    * // A = [ 1 2 3
     638             :    * //       2 4 6
     639             :    * //       3 6 9 ]
     640             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
     641             :    * // B = [ 9 6 3
     642             :    * //       8 5 2
     643             :    * //       7 4 1 ]
     644             :    * A *= B;
     645             :    * // A = [ 90  54 18
     646             :    * //       114 69 24
     647             :    * //       138 84 30 ]
     648             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     649             :    */
     650             :   RankTwoTensorTempl<T> & operator*=(const libMesh::TypeTensor<T> & a);
     651             : 
     652             :   /**
     653             :    * @brief The smart mutator that determines how to fill the second order tensor based on the size
     654             :    * of the input vector.
     655             :    *
     656             :    * @param input The input vector, can be of size 1, 3, 6, or 9
     657             :    * @param fill_method The fill method, default to autodetect.
     658             :    *
     659             :    * When `input.size() == 1`, the vector value is used to fill the diagonal components of the
     660             :    * second order tensor:
     661             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     662             :    * RankTwoTensor A;
     663             :    * A.fillFromInputVector({1.5});
     664             :    * // A = [ 1.5 0   0
     665             :    * //       0   1.5 0
     666             :    * //       0   0   1.5 ]
     667             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     668             :    *
     669             :    * When `input.size() == 3`, the vector values are used to fill the diagonal components of the
     670             :    * second order tensor:
     671             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     672             :    * RankTwoTensor A;
     673             :    * A.fillFromInputVector({1, 2, 3});
     674             :    * // A = [ 1 0 0
     675             :    * //       0 2 0
     676             :    * //       0 0 3 ]
     677             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     678             :    *
     679             :    * When `input.size() == 6`, the second order tensor is filled symmetrically using the Voigt
     680             :    * notation:
     681             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     682             :    * RankTwoTensor A;
     683             :    * A.fillFromInputVector({1, 2, 3, 4, 5, 6});
     684             :    * // A = [ 1 6 5
     685             :    * //       6 2 4
     686             :    * //       5 4 3 ]
     687             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     688             :    *
     689             :    * When `input.size() == 9`, all components of the second order tensor are filled in a
     690             :    * column-major fashion:
     691             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     692             :    * RankTwoTensor A;
     693             :    * A.fillFromInputVector({1, 2, 3, 4, 5, 6, 7, 8, 9});
     694             :    * // A = [ 1 4 7
     695             :    * //       2 5 8
     696             :    * //       3 6 9 ]
     697             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     698             :    */
     699             :   void fillFromInputVector(const std::vector<T> & input, FillMethod fill_method = autodetect);
     700             : 
     701             :   /**
     702             :    * @brief The smart mutator that determines how to fill the second order tensor based on the order
     703             :    * of the scalar_variable.
     704             :    *
     705             :    * @param scalar_variable The input scalar variable. Supported orders are FIRST, THIRD, and SIXTH.
     706             :    *
     707             :    * When `scalar_variable.size() == 1`, the scalar value is used to fill the very first component
     708             :    * of the second order tensor:
     709             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     710             :    & // Suppose v[0] = 1
     711             :    * RankTwoTensor A;
     712             :    * A.fillFromScalarVariable(v);
     713             :    * // A = [ 1 0 0
     714             :    * //       0 0 0
     715             :    * //       0 0 0 ]
     716             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     717             :    *
     718             :    * When `scalar_variable.size() == 3`, the scalar values are used to fill the in-plane components
     719             :    * of the second order tensor using the Voigt notation:
     720             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     721             :    * // Suppose v[0] = 1
     722             :    * //         v[1] = 2
     723             :    * //         v[2] = 3
     724             :    * RankTwoTensor A;
     725             :    * A.fillFromScalarVariable(v);
     726             :    * // A = [ 1 3 0
     727             :    * //       3 2 0
     728             :    * //       0 0 0 ]
     729             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     730             :    *
     731             :    * When `scalar_variable.size() == 6`, the second order tensor is filled symmetrically using the
     732             :    * Voigt notation:
     733             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     734             :    * // Suppose v[0] = 1
     735             :    * //         v[1] = 2
     736             :    * //         v[2] = 3
     737             :    * //         v[3] = 4
     738             :    * //         v[4] = 5
     739             :    * //         v[5] = 6
     740             :    * RankTwoTensor A;
     741             :    * A.fillFromScalarVariable(v);
     742             :    * // A = [ 1 6 5
     743             :    * //       6 2 4
     744             :    * //       5 4 3 ]
     745             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     746             :    */
     747             :   void fillFromScalarVariable(const VariableValue & scalar_variable);
     748             : 
     749             :   /**
     750             :    * sets _coords[0][0], _coords[0][1], _coords[1][0], _coords[1][1] to input,
     751             :    * and the remainder to zero
     752             :    */
     753             :   void surfaceFillFromInputVector(const std::vector<T> & input);
     754             : 
     755             :   /**
     756             :    * @brief Set the values of the second order tensor to be the outer product of two vectors, i.e.
     757             :    * \f$ A_{ij} = a_i b_j \f$.
     758             :    *
     759             :    * Deprecated in favor of outerProduct()
     760             :    *
     761             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     762             :    * RealVectorValue a(1, 2, 3);
     763             :    * RealVectorValue b(4, 5, 6);
     764             :    * RankTwoTensor A;
     765             :    * A.vectorOuterProduct(a, b);
     766             :    * // A = [ 4  5  6
     767             :    * //       8  10 12
     768             :    * //       12 15 18 ]
     769             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     770             :    */
     771             :   void vectorOuterProduct(const libMesh::TypeVector<T> &, const libMesh::TypeVector<T> &);
     772             : 
     773             :   /**
     774             :    * @brief Assign values to a specific row of the second order tensor.
     775             :    *
     776             :    * @param r The row number, r = 0, 1, 2
     777             :    * @param v The values to be set
     778             :    *
     779             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     780             :    * RealVectorValue a(1, 2, 3);
     781             :    * RankTwoTensor A;
     782             :    * // A = [ 0 0 0
     783             :    * //       0 0 0
     784             :    * //       0 0 0 ]
     785             :    * A.fillRow(1, a);
     786             :    * // A = [ 0 0 0
     787             :    * //       1 2 3
     788             :    * //       0 0 0 ]
     789             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     790             :    */
     791             :   void fillRow(unsigned int r, const libMesh::TypeVector<T> & v);
     792             : 
     793             :   /**
     794             :    * @brief Assign values to a specific column of the second order tensor.
     795             :    *
     796             :    * @param c The column number, c = 0, 1, 2
     797             :    * @param v The values to be set
     798             :    *
     799             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     800             :    * RealVectorValue a(1, 2, 3);
     801             :    * RankTwoTensor A;
     802             :    * // A = [ 0 0 0
     803             :    * //       0 0 0
     804             :    * //       0 0 0 ]
     805             :    * A.fillColumn(1, a);
     806             :    * // A = [ 0 1 0
     807             :    * //       0 2 0
     808             :    * //       0 3 0 ]
     809             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     810             :    */
     811             :   void fillColumn(unsigned int c, const libMesh::TypeVector<T> & v);
     812             : 
     813             :   /**
     814             :    * @brief Set the tensor to identity.
     815             :    *
     816             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     817             :    * RankTwoTensor A;
     818             :    * // A = [ 0 0 0
     819             :    * //       0 0 0
     820             :    * //       0 0 0 ]
     821             :    * A.setToIdentity();
     822             :    * // A = [ 1 0 0
     823             :    * //       0 1 0
     824             :    * //       0 0 1 ]
     825             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     826             :    */
     827             :   void setToIdentity();
     828             : 
     829             :   /// Add identity times a to _coords
     830             :   /**
     831             :    * @brief Add a scalar to diagonal components \f$ A_{ij} + a\delta_{ij} \f$
     832             :    *
     833             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     834             :    * RankTwoTensor A;
     835             :    * // A = [ 0 0 0
     836             :    * //       0 0 0
     837             :    * //       0 0 0 ]
     838             :    * A.addIa(1.5);
     839             :    * // A = [ 1.5 0   0
     840             :    * //       0   1.5 0
     841             :    * //       0   0   1.5 ]
     842             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     843             :    */
     844             :   void addIa(const T & a);
     845             : 
     846             :   /**
     847             :    * Rotate the tensor in-place given a rotation tensor
     848             :    * \f$ A_{ij} \leftarrow R_{ij} A_{jk} R_{jk} \f$
     849             :    * @param R The rotation tensor
     850             :    *
     851             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     852             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     853             :    * // A = [ 1 4 7
     854             :    * //       2 5 8
     855             :    * //       3 6 9 ]
     856             :    * RankTwoTensor R(0, 1, 0, 1, 0, 0, 0, 0, 1);
     857             :    * // R = [ 0 1 0
     858             :    * //       1 0 0
     859             :    * //       0 0 1 ]
     860             :    * A.rotate(R);
     861             :    * // A = [ 5 2 8
     862             :    * //       4 1 7
     863             :    * //       6 3 9 ]
     864             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     865             :    */
     866             :   void rotate(const RankTwoTensorTempl<T> & R);
     867             : 
     868             :   /// @}
     869             : 
     870             :   /// @{
     871             : 
     872             :   /**
     873             :    * @brief Return \f$ A_{ij} = A_{ik}A_{kj} \f$
     874             :    *
     875             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     876             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     877             :    * // A = [ 1 4 7
     878             :    * //       2 5 8
     879             :    * //       3 6 9 ]
     880             :    * RankTwoTensor B = A.square();
     881             :    * // B = [ 30 66 102
     882             :    * //       36 81 126
     883             :    * //       42 96 150 ]
     884             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     885             :    */
     886             :   RankTwoTensorTempl<T> square() const;
     887             : 
     888             :   /**
     889             :    * Return the rotated tensor given a rotation tensor
     890             :    * \f$ A'_{ij} = R_{ij} A_{jk} R_{jk} \f$
     891             :    * @param R The rotation tensor
     892             :    *
     893             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     894             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     895             :    * // A = [ 1 4 7
     896             :    * //       2 5 8
     897             :    * //       3 6 9 ]
     898             :    * RankTwoTensor R(0, 1, 0, 1, 0, 0, 0, 0, 1);
     899             :    * // R = [ 0 1 0
     900             :    * //       1 0 0
     901             :    * //       0 0 1 ]
     902             :    * RankTwoTensor A_rotated = A.rotated(R);
     903             :    * // A_rotated = [ 5 2 8
     904             :    * //               4 1 7
     905             :    * //               6 3 9 ]
     906             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     907             :    */
     908             :   RankTwoTensorTempl<T> rotated(const RankTwoTensorTempl<T> & R) const;
     909             : 
     910             :   /**
     911             :    * Rotate the tensor about the z-axis
     912             :    * @param a The rotation angle in radians
     913             :    */
     914             :   RankTwoTensorTempl<T> rotateXyPlane(T a);
     915             : 
     916             :   /**
     917             :    * Return the tensor transposed
     918             :    *
     919             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     920             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     921             :    * // A = [ 1 4 7
     922             :    * //       2 5 8
     923             :    * //       3 6 9 ]
     924             :    * RankTwoTensor At = A.transpose();
     925             :    * // At = [ 1 2 3
     926             :    * //        4 5 6
     927             :    * //        7 8 9 ]
     928             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     929             :    */
     930             :   RankTwoTensorTempl<T> transpose() const;
     931             : 
     932             :   /**
     933             :    * Return the sum of two second order tensors
     934             :    *
     935             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     936             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     937             :    * // A = [ 1 2 3
     938             :    * //       2 4 6
     939             :    * //       3 6 9 ]
     940             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
     941             :    * // B = [ 9 6 3
     942             :    * //       8 5 2
     943             :    * //       7 4 1 ]
     944             :    * RankTwoTensor C = A + B;
     945             :    * // C = [ 10 8  6
     946             :    * //       10 9  8
     947             :    * //       10 10 10 ]
     948             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     949             :    */
     950             :   template <typename T2>
     951             :   RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
     952             :   operator+(const libMesh::TypeTensor<T2> & a) const;
     953             : 
     954             :   /**
     955             :    * Return the subtraction of two second order tensors
     956             :    *
     957             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     958             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     959             :    * // A = [ 1 2 3
     960             :    * //       2 4 6
     961             :    * //       3 6 9 ]
     962             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
     963             :    * // B = [ 9 6 3
     964             :    * //       8 5 2
     965             :    * //       7 4 1 ]
     966             :    * RankTwoTensor C = A - B;
     967             :    * // C = [ -8 -4 0
     968             :    * //       -6 -1 4
     969             :    * //       -4  2 8 ]
     970             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     971             :    */
     972             :   template <typename T2>
     973             :   RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
     974             :   operator-(const libMesh::TypeTensor<T2> & a) const;
     975             : 
     976             :   /**
     977             :    * Return the negation of this tensor
     978             :    *
     979             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     980             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     981             :    * // A = [ 1 2 3
     982             :    * //       2 4 6
     983             :    * //       3 6 9 ]
     984             :    * RankTwoTensor B = -A;
     985             :    * // B = [ -1 -2 -3
     986             :    * //       -2 -4 -6
     987             :    * //       -3 -6 -9 ]
     988             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     989             :    */
     990             :   RankTwoTensorTempl<T> operator-() const;
     991             : 
     992             :   /**
     993             :    * Return this tensor multiplied by a scalar (component-wise)
     994             :    *
     995             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
     996             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
     997             :    * // A = [ 1 2 3
     998             :    * //       2 4 6
     999             :    * //       3 6 9 ]
    1000             :    * RankTwoTensor B = A * 2;
    1001             :    * // B = [ 2 4  6
    1002             :    * //       4 8  12
    1003             :    * //       6 12 18 ]
    1004             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1005             :    */
    1006             :   template <typename T2, typename std::enable_if<libMesh::ScalarTraits<T2>::value, int>::type = 0>
    1007             :   RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1008             :   operator*(const T2 & a) const;
    1009             : 
    1010             :   /**
    1011             :    * Return this tensor divided by a scalar (component-wise)
    1012             :    *
    1013             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1014             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
    1015             :    * // A = [ 1 2 3
    1016             :    * //       2 4 6
    1017             :    * //       3 6 9 ]
    1018             :    * RankTwoTensor B = A / 2;
    1019             :    * // B = [ 0.5 1.0 1.5
    1020             :    * //       1.0 2.0 3.0
    1021             :    * //       1.5 3.0 4.5 ]
    1022             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1023             :    */
    1024             :   template <typename T2, typename std::enable_if<libMesh::ScalarTraits<T2>::value, int>::type = 0>
    1025             :   RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1026             :   operator/(const T2 & a) const;
    1027             : 
    1028             :   /**
    1029             :    * Return this tensor multiplied by a vector. \f$ b_i = A_{ij} a_j \f$
    1030             :    *
    1031             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1032             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
    1033             :    * // A = [ 1 2 3
    1034             :    * //       2 4 6
    1035             :    * //       3 6 9 ]
    1036             :    * RealVectorValue a(1, 2, 3);
    1037             :    * // a = [ 1
    1038             :    * //       2
    1039             :    * //       3 ]
    1040             :    * RealVectorValue b = A * a;
    1041             :    * // b = [ 30
    1042             :    * //       36
    1043             :    * //       42 ]
    1044             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1045             :    */
    1046             :   template <typename T2>
    1047             :   libMesh::TypeVector<typename libMesh::CompareTypes<T, T2>::supertype>
    1048             :   operator*(const libMesh::TypeVector<T2> & a) const;
    1049             : 
    1050             :   /**
    1051             :    * Multiplication with another second order tensor
    1052             :    *
    1053             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1054             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
    1055             :    * // A = [ 1 2 3
    1056             :    * //       2 4 6
    1057             :    * //       3 6 9 ]
    1058             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
    1059             :    * // B = [ 9 6 3
    1060             :    * //       8 5 2
    1061             :    * //       7 4 1 ]
    1062             :    * RankTwoTensor C = A * B;
    1063             :    * // C = [ 90  54 18
    1064             :    * //       114 69 24
    1065             :    * //       138 84 30 ]
    1066             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1067             :    */
    1068             :   template <typename T2>
    1069             :   RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1070             :   operator*(const libMesh::TypeTensor<T2> & a) const;
    1071             : 
    1072             :   /**
    1073             :    * Return the double contraction with another second order tensor \f$ A_{ij} B_{ij} \f$
    1074             :    *
    1075             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1076             :    * RankTwoTensor A(1, 2, 3, 4, 5, 6, 7, 8, 9);
    1077             :    * // A = [ 1 2 3
    1078             :    * //       2 4 6
    1079             :    * //       3 6 9 ]
    1080             :    * RankTwoTensor B(9, 8, 7, 6, 5, 4, 3, 2, 1);
    1081             :    * // B = [ 9 6 3
    1082             :    * //       8 5 2
    1083             :    * //       7 4 1 ]
    1084             :    * Real result = A.doubleContraction(B);
    1085             :    * // result = 143
    1086             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1087             :    */
    1088             : 
    1089             :   T doubleContraction(const RankTwoTensorTempl<T> & a) const;
    1090             : 
    1091             :   /**
    1092             :    * Return the general tensor product of this second order tensor and another second order tensor
    1093             :    * defined as \f$ C_{ijkl} = A_{\mathcal{M}(n)\mathcal{M}(o)} B_{\mathcal{M}(p)\mathcal{M}(q)} \f$
    1094             :    * where the multiplication order is defined by the index map \f$ \mathcal{M}: \{n,o,p,q\} \to
    1095             :    * \{i,j,k,l\} \f$. The index map is specified using the template parameters. See examples below
    1096             :    * for detailed explanation.
    1097             :    *
    1098             :    * Suppose we have two second order tensors A and B, and we denote the output indices as i, j, k,
    1099             :    * l:
    1100             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1101             :    * usingTensorIndices(i, j, k, l);
    1102             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1103             :    *
    1104             :    * The outer product of A and B is defined as \f$ A_{ij} B_{kl} \f$, hence the template
    1105             :    * specialization should be `times<i, j, k, l>`
    1106             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1107             :    * RankFourTensor C = A.times<i, j, k, l>(B);
    1108             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1109             :    *
    1110             :    * The tensor product of A and B, i.e. \f$ A_{ik} B_{jl} \f$ can be expressed using the template
    1111             :    * specialization `times<i, k, j, l>`
    1112             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1113             :    * RankFourTensor C = A.times<i, k, j, l>(B);
    1114             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1115             :    *
    1116             :    * Similarly, another tensor product of A and B, i.e. \f$ A_{il} B_{jk} \f$ can be expressed using
    1117             :    * the template specialization `times<i, l, j, k>`
    1118             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1119             :    * RankFourTensor C = A.times<i, l, j, k>(B);
    1120             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1121             :    *
    1122             :    * The combination goes on...
    1123             :    */
    1124             :   template <int n, int o, int p, int q>
    1125             :   RankFourTensorTempl<T> times(const RankTwoTensorTempl<T> & b) const;
    1126             : 
    1127             :   /**
    1128             :    * Return the single contraction of this second order tensor with a fourth order tensor defined as
    1129             :    * \f$ C_{ijkl} = A_{\mathcal{M}(m)\mathcal{M}(n)}
    1130             :    * B_{\mathcal{M}(p)\mathcal{M}(q)\mathcal{M}(r)\mathcal{M}(s)} \f$ where the multiplication order
    1131             :    * is defined by the index map \f$ \mathcal{M}: \{m,n,p,q,r,s\} \to \{i,j,k,l\} \f$. The index map
    1132             :    * is specified using the template parameters. See examples below for detailed explanation.
    1133             :    *
    1134             :    * Suppose we have a second order tensors A and a fourth order tensor B, and we denote the indices
    1135             :    * (four output indices and a dummy index to be contracted) as i, j, k, l, m:
    1136             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1137             :    * usingTensorIndices(i, j, k, l, m);
    1138             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1139             :    *
    1140             :    * The single contraction of A and B defined as \f$ A_{im} B_{mjkl} \f$ can be expressed using the
    1141             :    * template specialization `times<i, m, m, j, k, l>`
    1142             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1143             :    * RankFourTensor C = A.times<i, m, m, j, k, l>(B);
    1144             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1145             :    *
    1146             :    * Similarly, another single contraction of A and B, i.e. \f$ A_{m, i} A_{j, k, m, l} \f$ can be
    1147             :    * expressed using the template specialization `times<m, i, j, k, m, l>`
    1148             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    1149             :    * RankFourTensor C = A.times<m, i, j, k, m, l>(B);
    1150             :    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1151             :    *
    1152             :    * The combination goes on. Note that this method assumes exactly one repeated index.
    1153             :    */
    1154             :   template <int n, int o, int p, int q, int r, int s>
    1155             :   RankFourTensorTempl<T> times(const RankFourTensorTempl<T> & b) const;
    1156             : 
    1157             :   /**
    1158             :    * @brief Return the outer product \f$ C_{ijkl} = A_{ij} B_{kl} \f$
    1159             :    */
    1160          38 :   RankFourTensorTempl<T> outerProduct(const RankTwoTensorTempl<T> & b) const
    1161             :   {
    1162             :     usingTensorIndices(i_, j_, k_, l_);
    1163          38 :     return times<i_, j_, k_, l_>(b);
    1164             :   }
    1165             : 
    1166             :   /**
    1167             :    * @brief Return the single contraction of this second order tensor with a third order tensor \f$
    1168             :    * C_{ikl} = A_{ij} B_{jkl} \f$
    1169             :    */
    1170             :   RankThreeTensorTempl<T> contraction(const RankThreeTensorTempl<T> & b) const;
    1171             : 
    1172             :   /**
    1173             :    * @brief Return the tensor product of this second order tensor with a vector \f$ C_{ijk} = A_{jk}
    1174             :    * b_{i} \f$
    1175             :    */
    1176             :   RankThreeTensorTempl<T> mixedProductJkI(const libMesh::VectorValue<T> & b) const;
    1177             : 
    1178             :   /**
    1179             :    * @brief Return the positive projection tensor
    1180             :    *
    1181             :    * Consider the eigenvalue decomposition of this second order tensor \f$ A = V D V^T \f$, the part
    1182             :    * of this tensor that lies on the positive spectrum is defined as \f$ A_+ = V \left<D\right> V^T
    1183             :    * \f$ where the angled brackets are the Macaulay brackets. The positive projection tensor is the
    1184             :    * linear projection from the full spectrum to the positive spectrum, i.e. \f$ A_+ = P A \f$. The
    1185             :    * derivation of this positive projection tensor can be found in C. Miehe and M. Lambrecht,
    1186             :    * Commun. Numer. Meth. Engng 2001; 17:337~353
    1187             :    *
    1188             :    * @param eigvals The three eigenvalues of this second order tensor will be filled into this
    1189             :    * vector.
    1190             :    * @param eigvecs The three eigenvectors of this second order tensor will be filled into this
    1191             :    * tensor.
    1192             :    * @return The fourth order positive projection tensor.
    1193             :    */
    1194             :   RankFourTensorTempl<T> positiveProjectionEigenDecomposition(std::vector<T> &,
    1195             :                                                               RankTwoTensorTempl<T> &) const;
    1196             : 
    1197             :   /**
    1198             :    * @brief Return the deviatoric part of this tensor \f$ A_{ij} - \frac{1}{3} A_{kk} \delta_{ij}
    1199             :    * \f$
    1200             :    */
    1201             :   RankTwoTensorTempl<T> deviatoric() const;
    1202             : 
    1203             :   /**
    1204             :    * @brief A wrapper for tr()
    1205             :    * @see tr()
    1206             :    */
    1207             :   T trace() const;
    1208             : 
    1209             :   /**
    1210             :    * Return the derivative of the trace w.r.t. this second order tensor itself \f$ \frac{\partial
    1211             :    * A_{kk}}{\partial A_{ij}} = \delta_{ij} \f$
    1212             :    */
    1213             :   RankTwoTensorTempl<T> dtrace() const;
    1214             : 
    1215             :   /**
    1216             :    * @brief Return the inverse of this second order tensor
    1217             :    */
    1218             :   RankTwoTensorTempl<T> inverse() const;
    1219             : 
    1220             :   /**
    1221             :    * @brief Return the principal second invariant of this second order tensor
    1222             :    *
    1223             :    * \f$ I_2 = \frac{1}{2} \left( A_{kk}^2 - A_{ij}A_{ij} \right) \f$
    1224             :    */
    1225             :   T generalSecondInvariant() const;
    1226             : 
    1227             :   /**
    1228             :    * @brief Return the main second invariant of this second order tensor
    1229             :    *
    1230             :    * \f$ J_2 = \frac{1}{2} \left( S_{ij}S_{ij} \right) \f$, where \f$ S_{ij} = A_{ij} -
    1231             :    * \frac{1}{3}A_{kk}\delta_{ij} \f$
    1232             :    */
    1233             :   T secondInvariant() const;
    1234             : 
    1235             :   /**
    1236             :    * Return the derivative of the main second invariant w.r.t. this second order tensor itself \f$
    1237             :    * \frac{\partial J_2}{\partial A_{ij}} = S_{ij} = A_{ij} -
    1238             :    * \frac{1}{3}A_{kk}\delta_{ij} \f$
    1239             :    */
    1240             :   RankTwoTensorTempl<T> dsecondInvariant() const;
    1241             : 
    1242             :   /**
    1243             :    * Return the second derivative of the main second invariant w.r.t. this second order tensor
    1244             :    * itself \f$ \frac{\partial^2 J_2}{\partial A_{ij}A_{kl}} = \delta_{ik}\delta_{jl} -
    1245             :    * \frac{1}{3}\delta_{ij}\delta_{kl} \f$
    1246             :    */
    1247             :   RankFourTensorTempl<T> d2secondInvariant() const;
    1248             : 
    1249             :   /**
    1250             :    * @brief Sin(3*Lode_angle)
    1251             :    *
    1252             :    * If secondInvariant() <= r0 then return r0_value
    1253             :    * This is to gaurd against precision-loss errors.
    1254             :    * Note that sin(3*Lode_angle) is not defined for secondInvariant() = 0
    1255             :    */
    1256             :   T sin3Lode(const T & r0, const T & r0_value) const;
    1257             : 
    1258             :   /**
    1259             :    * d(sin3Lode)/dA_ij
    1260             :    * If secondInvariant() <= r0 then return zero
    1261             :    * This is to gaurd against precision-loss errors.
    1262             :    * Note that sin(3*Lode_angle) is not defined for secondInvariant() = 0
    1263             :    */
    1264             :   RankTwoTensorTempl<T> dsin3Lode(const T & r0) const;
    1265             : 
    1266             :   /**
    1267             :    * d^2(sin3Lode)/dA_ij/dA_kl
    1268             :    * If secondInvariant() <= r0 then return zero
    1269             :    * This is to gaurd against precision-loss errors.
    1270             :    * Note that sin(3*Lode_angle) is not defined for secondInvariant() = 0
    1271             :    */
    1272             :   RankFourTensorTempl<T> d2sin3Lode(const T & r0) const;
    1273             : 
    1274             :   /**
    1275             :    * Denote the _coords[i][j] by A_ij, then
    1276             :    * S_ij = A_ij - de_ij*tr(A)/3
    1277             :    * Then this returns det(S + S.transpose())/2
    1278             :    * Note the explicit symmeterisation
    1279             :    */
    1280             :   T thirdInvariant() const;
    1281             : 
    1282             :   /**
    1283             :    * Denote the _coords[i][j] by A_ij, then
    1284             :    * this returns d(thirdInvariant()/dA_ij
    1285             :    */
    1286             :   RankTwoTensorTempl<T> dthirdInvariant() const;
    1287             : 
    1288             :   /**
    1289             :    * Denote the _coords[i][j] by A_ij, then this returns
    1290             :    * d^2(thirdInvariant)/dA_ij/dA_kl
    1291             :    */
    1292             :   RankFourTensorTempl<T> d2thirdInvariant() const;
    1293             : 
    1294             :   /**
    1295             :    * Denote the _coords[i][j] by A_ij, then this returns
    1296             :    * d(det)/dA_ij
    1297             :    */
    1298             :   RankTwoTensorTempl<T> ddet() const;
    1299             : 
    1300             :   /// Sqrt(_coords[i][j]*_coords[i][j])
    1301             :   T L2norm() const;
    1302             : 
    1303             :   /**
    1304             :    * computes eigenvalues, assuming tens is symmetric, and places them
    1305             :    * in ascending order in eigvals
    1306             :    */
    1307             :   void symmetricEigenvalues(std::vector<T> & eigvals) const;
    1308             : 
    1309             :   /**
    1310             :    * computes eigenvalues and eigenvectors, assuming tens is symmetric, and places them
    1311             :    * in ascending order in eigvals.  eigvecs is a matrix with the first column
    1312             :    * being the first eigenvector, the second column being the second, etc.
    1313             :    */
    1314             :   void symmetricEigenvaluesEigenvectors(std::vector<T> & eigvals,
    1315             :                                         RankTwoTensorTempl<T> & eigvecs) const;
    1316             : 
    1317             :   /**
    1318             :    * computes eigenvalues, and their symmetric derivatives wrt vals,
    1319             :    * assuming tens is symmetric
    1320             :    * @param eigvals are the eigenvalues of the matrix, in ascending order
    1321             :    * @param deigvals Here digvals[i](j,k) = (1/2)*(d(eigvals[i])/dA_jk + d(eigvals[i]/dA_kj))
    1322             :    * Note the explicit symmeterisation here.
    1323             :    * For equal eigenvalues, these derivatives are not gauranteed to
    1324             :    * be the ones you expect, since the derivatives in this case are
    1325             :    * often defined by continuation from the un-equal case, and that is
    1326             :    * too sophisticated for this routine.
    1327             :    */
    1328             :   void dsymmetricEigenvalues(std::vector<T> & eigvals,
    1329             :                              std::vector<RankTwoTensorTempl<T>> & deigvals) const;
    1330             : 
    1331             :   /**
    1332             :    * Computes second derivatives of Eigenvalues of a rank two tensor
    1333             :    * @param deriv store second derivative of the current tensor in here
    1334             :    */
    1335             :   void d2symmetricEigenvalues(std::vector<RankFourTensorTempl<T>> & deriv) const;
    1336             : 
    1337             :   /**
    1338             :    * Uses the petscblaslapack.h LAPACKsyev_ routine to perform RU decomposition and obtain the
    1339             :    * rotation tensor.
    1340             :    */
    1341             :   void getRUDecompositionRotation(RankTwoTensorTempl<T> & rot) const;
    1342             : 
    1343             :   /// returns this_ij * b_ijkl
    1344             :   RankTwoTensorTempl<T> initialContraction(const RankFourTensorTempl<T> & b) const;
    1345             : 
    1346             :   /// @}
    1347             : 
    1348             :   /// @{
    1349             : 
    1350             :   /// Defines logical equality with another RankTwoTensorTempl<T>
    1351             :   bool operator==(const RankTwoTensorTempl<T> & a) const;
    1352             : 
    1353             :   /// Test for symmetry
    1354             :   bool isSymmetric() const;
    1355             : 
    1356             :   /// @}
    1357             : 
    1358             : protected:
    1359             :   /**
    1360             :    * Uses the petscblaslapack.h LAPACKsyev_ routine to find, for symmetric _coords:
    1361             :    *  (1) the eigenvalues (if calculation_type == "N")
    1362             :    *  (2) the eigenvalues and eigenvectors (if calculation_type == "V")
    1363             :    * @param calculation_type If "N" then calculation eigenvalues only
    1364             :    * @param eigvals Eigenvalues are placed in this array, in ascending order
    1365             :    * @param a Eigenvectors are placed in this array if calculation_type == "V".
    1366             :    * See code in dsymmetricEigenvalues for extracting eigenvectors from the a output.
    1367             :    */
    1368             :   void syev(const char * calculation_type, std::vector<T> & eigvals, std::vector<T> & a) const;
    1369             : 
    1370             : private:
    1371             :   static constexpr Real identityCoords[N2] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
    1372             : 
    1373             :   template <class T2>
    1374             :   friend void dataStore(std::ostream &, RankTwoTensorTempl<T2> &, void *);
    1375             : 
    1376             :   using libMesh::TensorValue<T>::_coords;
    1377             : 
    1378             :   template <class T2>
    1379             :   friend void dataLoad(std::istream &, RankTwoTensorTempl<T2> &, void *);
    1380             :   template <class T2>
    1381             :   friend class RankFourTensorTempl;
    1382             :   template <class T2>
    1383             :   friend class RankThreeTensorTempl;
    1384             : };
    1385             : 
    1386             : namespace MetaPhysicL
    1387             : {
    1388             : template <typename T>
    1389             : struct RawType<RankTwoTensorTempl<T>>
    1390             : {
    1391             :   typedef RankTwoTensorTempl<typename RawType<T>::value_type> value_type;
    1392             : 
    1393          52 :   static value_type value(const RankTwoTensorTempl<T> & in)
    1394             :   {
    1395          52 :     value_type ret;
    1396         208 :     for (auto i : libMesh::make_range(RankTwoTensorTempl<T>::N))
    1397         624 :       for (auto j : libMesh::make_range(RankTwoTensorTempl<T>::N))
    1398         468 :         ret(i, j) = raw_value(in(i, j));
    1399             : 
    1400          52 :     return ret;
    1401           0 :   }
    1402             : };
    1403             : }
    1404             : 
    1405             : template <typename T>
    1406             : template <typename T2>
    1407             : RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1408        2696 : RankTwoTensorTempl<T>::operator+(const libMesh::TypeTensor<T2> & b) const
    1409             : {
    1410        2696 :   return libMesh::TensorValue<T>::operator+(b);
    1411             : }
    1412             : 
    1413             : template <typename T>
    1414             : template <typename T2>
    1415             : RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1416      793312 : RankTwoTensorTempl<T>::operator-(const libMesh::TypeTensor<T2> & b) const
    1417             : {
    1418      793312 :   return libMesh::TensorValue<T>::operator-(b);
    1419             : }
    1420             : 
    1421             : template <typename T>
    1422             : template <typename T2, typename std::enable_if<libMesh::ScalarTraits<T2>::value, int>::type>
    1423             : RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1424        2682 : RankTwoTensorTempl<T>::operator*(const T2 & b) const
    1425             : {
    1426        2682 :   return libMesh::TensorValue<T>::operator*(b);
    1427             : }
    1428             : 
    1429             : template <typename T>
    1430             : template <typename T2>
    1431             : libMesh::TypeVector<typename libMesh::CompareTypes<T, T2>::supertype>
    1432          28 : RankTwoTensorTempl<T>::operator*(const libMesh::TypeVector<T2> & b) const
    1433             : {
    1434          28 :   return libMesh::TensorValue<T>::operator*(b);
    1435             : }
    1436             : 
    1437             : template <typename T>
    1438             : template <typename T2>
    1439             : RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1440          50 : RankTwoTensorTempl<T>::operator*(const libMesh::TypeTensor<T2> & b) const
    1441             : {
    1442          50 :   return libMesh::TensorValue<T>::operator*(b);
    1443             : }
    1444             : 
    1445             : template <typename T>
    1446             : template <typename T2, typename std::enable_if<libMesh::ScalarTraits<T2>::value, int>::type>
    1447             : RankTwoTensorTempl<typename libMesh::CompareTypes<T, T2>::supertype>
    1448         508 : RankTwoTensorTempl<T>::operator/(const T2 & b) const
    1449             : {
    1450         508 :   return libMesh::TensorValue<T>::operator/(b);
    1451             : }
    1452             : 
    1453             : template <typename T>
    1454             : RankFourTensorTempl<T>
    1455           2 : RankTwoTensorTempl<T>::positiveProjectionEigenDecomposition(std::vector<T> & eigval,
    1456             :                                                             RankTwoTensorTempl<T> & eigvec) const
    1457             : {
    1458             :   using std::abs;
    1459             :   if constexpr (MooseUtils::IsLikeReal<T>::value)
    1460             :   {
    1461             :     // Compute eigenvectors and eigenvalues of this tensor
    1462           2 :     this->symmetricEigenvaluesEigenvectors(eigval, eigvec);
    1463             : 
    1464             :     // Separate out positive and negative eigen values
    1465           0 :     std::array<T, N> epos;
    1466           0 :     std::array<T, N> d;
    1467           8 :     for (auto i : libMesh::make_range(N))
    1468             :     {
    1469           6 :       epos[i] = (abs(eigval[i]) + eigval[i]) / 2.0;
    1470           6 :       d[i] = 0 < eigval[i] ? 1.0 : 0.0;
    1471             :     }
    1472             : 
    1473             :     // projection tensor
    1474           2 :     RankFourTensorTempl<T> proj_pos;
    1475           2 :     RankFourTensorTempl<T> Gab, Gba;
    1476             : 
    1477           8 :     for (auto a : libMesh::make_range(N))
    1478             :     {
    1479           6 :       const auto Ma = RankTwoTensorTempl<T>::selfOuterProduct(eigvec.column(a));
    1480           6 :       proj_pos += d[a] * Ma.outerProduct(Ma);
    1481             :     }
    1482             : 
    1483             :     usingTensorIndices(i_, j_, k_, l_);
    1484           8 :     for (const auto a : libMesh::make_range(N))
    1485          12 :       for (const auto b : libMesh::make_range(a))
    1486             :       {
    1487           6 :         const auto Ma = RankTwoTensorTempl<T>::selfOuterProduct(eigvec.column(a));
    1488           6 :         const auto Mb = RankTwoTensorTempl<T>::selfOuterProduct(eigvec.column(b));
    1489             : 
    1490           6 :         Gab = Ma.template times<i_, k_, j_, l_>(Mb) + Ma.template times<i_, l_, j_, k_>(Mb);
    1491           6 :         Gba = Mb.template times<i_, k_, j_, l_>(Ma) + Mb.template times<i_, l_, j_, k_>(Ma);
    1492             : 
    1493           0 :         T theta_ab;
    1494           6 :         if (!MooseUtils::absoluteFuzzyEqual(eigval[a], eigval[b]))
    1495           6 :           theta_ab = 0.5 * (epos[a] - epos[b]) / (eigval[a] - eigval[b]);
    1496             :         else
    1497           0 :           theta_ab = 0.25 * (d[a] + d[b]);
    1498             : 
    1499           6 :         proj_pos += theta_ab * (Gab + Gba);
    1500             :       }
    1501           2 :     return proj_pos;
    1502           0 :   }
    1503             :   else
    1504             :     mooseError("positiveProjectionEigenDecomposition is only available for ordered tensor "
    1505             :                "component types");
    1506             : }
    1507             : 
    1508             : template <typename T>
    1509             : T
    1510          64 : RankTwoTensorTempl<T>::sin3Lode(const T & r0, const T & r0_value) const
    1511             : {
    1512             :   using std::pow, std::max, std::sqrt, std::min;
    1513             :   if constexpr (MooseUtils::IsLikeReal<T>::value)
    1514             :   {
    1515          64 :     T bar = secondInvariant();
    1516          64 :     if (bar <= r0)
    1517             :       // in this case the Lode angle is not defined
    1518           0 :       return r0_value;
    1519             :     else
    1520             :       // the min and max here gaurd against precision-loss when bar is tiny but nonzero.
    1521          64 :       return max(min(-1.5 * sqrt(3.0) * thirdInvariant() / pow(bar, 1.5), 1.0), -1.0);
    1522           0 :   }
    1523             :   else
    1524             :     mooseError("sin3Lode is only available for ordered tensor component types");
    1525             : }
    1526             : 
    1527             : template <typename T>
    1528             : RankTwoTensorTempl<T>
    1529         496 : RankTwoTensorTempl<T>::dsin3Lode(const T & r0) const
    1530             : {
    1531             :   using std::sqrt, std::pow;
    1532             :   if constexpr (MooseUtils::IsLikeReal<T>::value)
    1533             :   {
    1534         496 :     T bar = secondInvariant();
    1535         496 :     if (bar <= r0)
    1536           0 :       return RankTwoTensorTempl<T>();
    1537             :     else
    1538           0 :       return -1.5 * sqrt(3.0) *
    1539           0 :              (dthirdInvariant() / pow(bar, 1.5) -
    1540         496 :               1.5 * dsecondInvariant() * thirdInvariant() / pow(bar, 2.5));
    1541           0 :   }
    1542             :   else
    1543             :     mooseError("dsin3Lode is only available for ordered tensor component types");
    1544             : }
    1545             : 
    1546             : template <typename T>
    1547             : RankFourTensorTempl<T>
    1548           4 : RankTwoTensorTempl<T>::d2sin3Lode(const T & r0) const
    1549             : {
    1550             :   using std::pow, std::sqrt;
    1551             :   if constexpr (MooseUtils::IsLikeReal<T>::value)
    1552             :   {
    1553           4 :     T bar = secondInvariant();
    1554           4 :     if (bar <= r0)
    1555           0 :       return RankFourTensorTempl<T>();
    1556             : 
    1557           4 :     T J3 = thirdInvariant();
    1558           4 :     RankTwoTensorTempl<T> dII = dsecondInvariant();
    1559           4 :     RankTwoTensorTempl<T> dIII = dthirdInvariant();
    1560           0 :     RankFourTensorTempl<T> deriv =
    1561           4 :         d2thirdInvariant() / pow(bar, 1.5) - 1.5 * d2secondInvariant() * J3 / pow(bar, 2.5);
    1562             : 
    1563          16 :     for (unsigned i = 0; i < N; ++i)
    1564          48 :       for (unsigned j = 0; j < N; ++j)
    1565         144 :         for (unsigned k = 0; k < N; ++k)
    1566         432 :           for (unsigned l = 0; l < N; ++l)
    1567         324 :             deriv(i, j, k, l) +=
    1568         324 :                 (-1.5 * dII(i, j) * dIII(k, l) - 1.5 * dIII(i, j) * dII(k, l)) / pow(bar, 2.5) +
    1569         324 :                 1.5 * 2.5 * dII(i, j) * dII(k, l) * J3 / pow(bar, 3.5);
    1570             : 
    1571           4 :     deriv *= -1.5 * sqrt(3.0);
    1572           4 :     return deriv;
    1573           4 :   }
    1574             :   else
    1575             :     mooseError("d2sin3Lode is only available for ordered tensor component types");
    1576             : }
    1577             : 
    1578             : template <typename T>
    1579             : template <int n, int o, int p, int q>
    1580             : RankFourTensorTempl<T>
    1581         182 : RankTwoTensorTempl<T>::times(const RankTwoTensorTempl<T> & b) const
    1582             : {
    1583         182 :   RankFourTensorTempl<T> result;
    1584             :   std::size_t x[4];
    1585         728 :   for (x[0] = 0; x[0] < N; ++x[0])
    1586        2184 :     for (x[1] = 0; x[1] < N; ++x[1])
    1587        6552 :       for (x[2] = 0; x[2] < N; ++x[2])
    1588       19656 :         for (x[3] = 0; x[3] < N; ++x[3])
    1589       14742 :           result(x[0], x[1], x[2], x[3]) = (*this)(x[n], x[o]) * b(x[p], x[q]);
    1590             : 
    1591         364 :   return result;
    1592           0 : }
    1593             : 
    1594             : template <typename T>
    1595             : template <int n, int o, int p, int q, int r, int s>
    1596             : RankFourTensorTempl<T>
    1597           6 : RankTwoTensorTempl<T>::times(const RankFourTensorTempl<T> & b) const
    1598             : {
    1599           6 :   RankFourTensorTempl<T> result;
    1600             :   std::size_t x[5];
    1601          24 :   for (x[0] = 0; x[0] < N; ++x[0])
    1602          72 :     for (x[1] = 0; x[1] < N; ++x[1])
    1603         216 :       for (x[2] = 0; x[2] < N; ++x[2])
    1604         648 :         for (x[3] = 0; x[3] < N; ++x[3])
    1605        1944 :           for (x[4] = 0; x[4] < N; ++x[4])
    1606        1458 :             result(x[0], x[1], x[2], x[3]) += (*this)(x[n], x[o]) * b(x[p], x[q], x[r], x[s]);
    1607             : 
    1608          12 :   return result;
    1609             : }

Generated by: LCOV version 1.14