Other operators

Transpose-dot

The dot product between the transpose of a tensor with itself. Results in a symmetric tensor.

\[\mathbf{A} = \mathbf{B}^\text{T} \cdot \mathbf{B} \Leftrightarrow A_{ij} = B_{ki}^\text{T} B_{kj} = B_{ik} B_{kj}\]

\[\mathbf{A} = \mathbf{B} \cdot \mathbf{B}^\text{T} \Leftrightarrow A_{ij} = B_{ik} B_{jk}^\text{T} = B_{ik} B_{kj}\]

Tensors.tdotFunction
tdot(A::SecondOrderTensor)

Compute the transpose-dot product of A with itself, i.e. dot(A', A). Return a SymmetricTensor.

Examples

julia> A = rand(Tensor{2,3})
3×3 Tensor{2, 3, Float64, 9}:
 0.590845  0.460085  0.200586
 0.766797  0.794026  0.298614
 0.566237  0.854147  0.246837

julia> tdot(A)
3×3 SymmetricTensor{2, 3, Float64, 6}:
 1.2577   1.36435   0.48726
 1.36435  1.57172   0.540229
 0.48726  0.540229  0.190334
source
Tensors.dottFunction
dott(A::SecondOrderTensor)

Compute the dot-transpose product of A with itself, i.e. dot(A, A'). Return a SymmetricTensor.

Examples

julia> A = rand(Tensor{2,3})
3×3 Tensor{2, 3, Float64, 9}:
 0.590845  0.460085  0.200586
 0.766797  0.794026  0.298614
 0.566237  0.854147  0.246837

julia> dott(A)
3×3 SymmetricTensor{2, 3, Float64, 6}:
 0.601011  0.878275  0.777051
 0.878275  1.30763   1.18611
 0.777051  1.18611   1.11112
source

Norm

The (2)-norm of a tensor is defined for a vector, second order tensor and fourth order tensor as

\[\|\mathbf{a}\| = \sqrt{\mathbf{a} \cdot \mathbf{a}} \Leftrightarrow \|a_i\| = \sqrt{a_i a_i},\]

\[\|\mathbf{A}\| = \sqrt{\mathbf{A} : \mathbf{A}} \Leftrightarrow \|A_{ij}\| = \sqrt{A_{ij} A_{ij}},\]

\[\|\mathsf{A}\| = \sqrt{\mathsf{A} :: \mathsf{A}} \Leftrightarrow \|A_{ijkl}\| = \sqrt{A_{ijkl} A_{ijkl}}.\]

LinearAlgebra.normFunction
norm(::Vec)
norm(::SecondOrderTensor)
norm(::FourthOrderTensor)

Computes the norm of a tensor.

Examples

julia> A = rand(Tensor{2,3})
3×3 Tensor{2, 3, Float64, 9}:
 0.590845  0.460085  0.200586
 0.766797  0.794026  0.298614
 0.566237  0.854147  0.246837

julia> norm(A)
1.7377443667834922
source

Trace

The trace for a second order tensor is defined as the sum of the diagonal elements. This can be written as

\[\text{tr}(\mathbf{A}) = \mathbf{I} : \mathbf{A} \Leftrightarrow \text{tr}(A_{ij}) = A_{ii}.\]

LinearAlgebra.trFunction
tr(::SecondOrderTensor)

Computes the trace of a second order tensor.

Examples

julia> A = rand(SymmetricTensor{2,3})
3×3 SymmetricTensor{2, 3, Float64, 6}:
 0.590845  0.766797  0.566237
 0.766797  0.460085  0.794026
 0.566237  0.794026  0.854147

julia> tr(A)
1.9050765715072775
source

Determinant

Determinant for a second order tensor.

LinearAlgebra.detFunction
det(::SecondOrderTensor)

Computes the determinant of a second order tensor.

Examples

julia> A = rand(SymmetricTensor{2,3})
3×3 SymmetricTensor{2, 3, Float64, 6}:
 0.590845  0.766797  0.566237
 0.766797  0.460085  0.794026
 0.566237  0.794026  0.854147

julia> det(A)
-0.1005427219925894
source

Inverse

Inverse of a second order tensor such that

\[\mathbf{A}^{-1} \cdot \mathbf{A} = \mathbf{I}\]

where $\mathbf{I}$ is the second order identitiy tensor.

Base.invFunction
inv(::SecondOrderTensor)

Computes the inverse of a second order tensor.

Examples

julia> A = rand(Tensor{2,3})
3×3 Tensor{2, 3, Float64, 9}:
 0.590845  0.460085  0.200586
 0.766797  0.794026  0.298614
 0.566237  0.854147  0.246837

julia> inv(A)
3×3 Tensor{2, 3, Float64, 9}:
  19.7146   -19.2802    7.30384
   6.73809  -10.7687    7.55198
 -68.541     81.4917  -38.8361
source

Transpose

Transpose of tensors is defined by changing the order of the tensor's "legs". The transpose of a vector/symmetric tensor is the vector/tensor itself. The transpose of a second order tensor can be written as:

\[A_{ij}^\text{T} = A_{ji}\]

and for a fourth order tensor the minor transpose can be written as

\[A_{ijkl}^\text{t} = A_{jilk}\]

and the major transpose as

\[A_{ijkl}^\text{T} = A_{klij}.\]

Base.transposeFunction
transpose(::Vec)
transpose(::SecondOrderTensor)
transpose(::FourthOrderTensor)

Compute the transpose of a tensor. For a fourth order tensor, the transpose is the minor transpose.

Examples

julia> A = rand(Tensor{2,2})
2×2 Tensor{2, 2, Float64, 4}:
 0.590845  0.566237
 0.766797  0.460085

julia> A'
2×2 Tensor{2, 2, Float64, 4}:
 0.590845  0.766797
 0.566237  0.460085
source

Symmetric

The symmetric part of a second order tensor is defined by:

\[\mathbf{A}^\text{sym} = \frac{1}{2}(\mathbf{A} + \mathbf{A}^\text{T}) \Leftrightarrow A_{ij}^\text{sym} = \frac{1}{2}(A_{ij} + A_{ji}),\]

The major symmetric part of a fourth order tensor is defined by

\[\mathsf{A}^\text{majsym} = \frac{1}{2}(\mathsf{A} + \mathsf{A}^\text{T}) \Leftrightarrow A_{ijkl}^\text{majsym} = \frac{1}{2}(A_{ijkl} + A_{klij}).\]

The minor symmetric part of a fourth order tensor is defined by

\[A_{ijkl}^\text{minsym} = \frac{1}{4}(A_{ijkl} + A_{ijlk} + A_{jikl} + A_{jilk}).\]

Tensors.symmetricFunction
symmetric(::SecondOrderTensor)
symmetric(::FourthOrderTensor)

Computes the (minor) symmetric part of a second or fourth order tensor. Return a SymmetricTensor.

Examples

julia> A = rand(Tensor{2,2})
2×2 Tensor{2, 2, Float64, 4}:
 0.590845  0.566237
 0.766797  0.460085

julia> symmetric(A)
2×2 SymmetricTensor{2, 2, Float64, 3}:
 0.590845  0.666517
 0.666517  0.460085
source
Tensors.minorsymmetricFunction
minorsymmetric(::FourthOrderTensor)

Compute the minor symmetric part of a fourth order tensor, return a SymmetricTensor{4}.

source

Skew symmetric

The skew symmetric part of a second order tensor is defined by

\[\mathbf{A}^\text{skw} = \frac{1}{2}(\mathbf{A} - \mathbf{A}^\text{T}) \Leftrightarrow A^\text{skw}_{ij} = \frac{1}{2}(A_{ij} - A_{ji}).\]

The skew symmetric part of a symmetric tensor is zero.

Tensors.skewFunction
skew(::SecondOrderTensor)

Computes the skew-symmetric (anti-symmetric) part of a second order tensor, returns a Tensor{2}.

source

Deviatoric tensor

The deviatoric part of a second order tensor is defined by

\[\mathbf{A}^\text{dev} = \mathbf{A} - \frac{1}{3} \mathrm{tr}[\mathbf{A}] \mathbf{I} \Leftrightarrow A_{ij}^\text{dev} = A_{ij} - \frac{1}{3}A_{kk}\delta_{ij}.\]

Tensors.devFunction
dev(::SecondOrderTensor)

Computes the deviatoric part of a second order tensor.

Examples

julia> A = rand(Tensor{2, 3});

julia> dev(A)
3×3 Tensor{2, 3, Float64, 9}:
 0.0469421  0.460085   0.200586
 0.766797   0.250123   0.298614
 0.566237   0.854147  -0.297065

julia> tr(dev(A))
0.0
source

Volumetric tensor

The volumetric part of a second order tensor is defined by

\[\mathbf{A}^\text{vol} = \frac{1}{3} \mathrm{tr}[\mathbf{A}] \mathbf{I} \Leftrightarrow A_{ij}^\text{vol} = \frac{1}{3}A_{kk}\delta_{ij}.\]

Tensors.volFunction
vol(::SecondOrderTensor)

Computes the volumetric part of a second order tensor based on the additive decomposition.

Examples

julia> A = rand(SymmetricTensor{2,3})
3×3 SymmetricTensor{2, 3, Float64, 6}:
 0.590845  0.766797  0.566237
 0.766797  0.460085  0.794026
 0.566237  0.794026  0.854147

julia> vol(A)
3×3 SymmetricTensor{2, 3, Float64, 6}:
 0.635026  0.0       0.0
 0.0       0.635026  0.0
 0.0       0.0       0.635026

julia> vol(A) + dev(A) ≈ A
true
source

Cross product

The cross product between two vectors is defined as

\[\mathbf{a} = \mathbf{b} \times \mathbf{c} \Leftrightarrow a_i = \epsilon_{ijk} b_j c_k\]

LinearAlgebra.crossFunction
cross(::Vec, ::Vec)

Computes the cross product between two Vec vectors, returns a Vec{3}. For dimensions 1 and 2 the Vec's are expanded to 3D first. The infix operator × (written \times) can also be used.

Examples

julia> a = rand(Vec{3})
3-element Vec{3, Float64}:
 0.5908446386657102
 0.7667970365022592
 0.5662374165061859

julia> b = rand(Vec{3})
3-element Vec{3, Float64}:
 0.4600853424625171
 0.7940257103317943
 0.8541465903790502

julia> a × b
3-element Vec{3, Float64}:
  0.20535000738340053
 -0.24415039787171888
  0.11635375677388776
source

Eigenvalues and eigenvectors

The eigenvalues and eigenvectors of a (symmetric) second order tensor, $\mathbf{A}$ can be solved from the eigenvalue problem

\[\mathbf{A} \cdot \mathbf{v}_i = \lambda_i \mathbf{v}_i \qquad i = 1, \dots, \text{dim}\]

where $\lambda_i$ are the eigenvalues and $\mathbf{v}_i$ are the corresponding eigenvectors. For a symmetric fourth order tensor, $\mathsf{A}$ the second order eigentensors and eigenvalues can be solved from

\[\mathsf{A} : \mathbf{V}_i = \lambda_i \mathbf{V}_i \qquad i = 1, \dots, \text{dim}\]

where $\lambda_i$ are the eigenvalues and $\mathbf{V}_i$ the corresponding eigentensors.

LinearAlgebra.eigenFunction
eigen(A::SymmetricTensor{2})

Compute the eigenvalues and eigenvectors of a symmetric second order tensor and return an Eigen object. The eigenvalues are stored in a Vec, sorted in ascending order. The corresponding eigenvectors are stored as the columns of a Tensor.

See eigvals and eigvecs.

Examples

julia> A = rand(SymmetricTensor{2, 2});

julia> E = eigen(A);

julia> E.values
2-element Vec{2, Float64}:
 -0.1883547111127678
  1.345436766284664

julia> E.vectors
2×2 Tensor{2, 2, Float64, 4}:
 -0.701412  0.712756
  0.712756  0.701412
source
eigen(A::SymmetricTensor{4})

Compute the eigenvalues and second order eigentensors of a symmetric fourth order tensor and return an FourthOrderEigen object. The eigenvalues and eigentensors are sorted in ascending order of the eigenvalues.

See also eigvals and eigvecs.

source
LinearAlgebra.eigvalsFunction
eigvals(::SymmetricTensor)

Compute the eigenvalues of a symmetric tensor.

source
eigvals(::Union{Eigen,FourthOrderEigen})

Extract eigenvalues from an Eigen or FourthOrderEigen object, returned by eigen.

source
LinearAlgebra.eigvecsFunction
eigvecs(::SymmetricTensor)

Compute the eigenvectors of a symmetric tensor.

source
eigvecs(::Union{Eigen,FourthOrderEigen})

Extract eigenvectors from an Eigen or FourthOrderEigen object, returned by eigen.

source

Tensor square root

Square root of a symmetric positive definite second order tensor $S$, defined such that

\[\sqrt{\mathbf{S}} \cdot \sqrt{\mathbf{S}} = S.\]

Base.sqrtFunction
sqrt(S::SymmetricTensor{2})

Calculate the square root of the positive definite symmetric second order tensor S, such that √S ⋅ √S == S.

Examples

julia> S = rand(SymmetricTensor{2,2}); S = tdot(S)
2×2 SymmetricTensor{2, 2, Float64, 3}:
 0.937075  0.887247
 0.887247  0.908603

julia> sqrt(S)
2×2 SymmetricTensor{2, 2, Float64, 3}:
 0.776178  0.578467
 0.578467  0.757614

julia> √S ⋅ √S ≈ S
true
source

Rotations

Tensors.rotateFunction
rotate(x::AbstractTensor{3}, u::Vec{3}, θ::Number)

Rotate a three dimensional tensor x around the vector u a total of θ radians.

Examples

julia> x = Vec{3}((0.0, 0.0, 1.0));

julia> u = Vec{3}((0.0, 1.0, 0.0));

julia> rotate(x, u, π/2)
3-element Vec{3, Float64}:
 1.0
 0.0
 6.123233995736766e-17
source
rotate(x::AbstractTensor{2}, θ::Number)

Rotate a two dimensional tensor x θ radians around the out-of-plane axis.

Examples

julia> x = Vec{2}((0.0, 1.0));

julia> rotate(x, π/4)
2-element Vec{2, Float64}:
 -0.7071067811865475
  0.7071067811865476
source
Tensors.rotation_tensorFunction
rotation_tensor(θ::Number)

Return the two-dimensional rotation matrix corresponding to rotation of θ radians around the out-of-plane axis (i.e. around (0, 0, 1)).

source
rotation_tensor(ψ::Number, θ::Number, ϕ::Number)

Return the three-dimensional rotation matrix corresponding to the rotation described by the three Euler angles $ψ$, $θ$, $ϕ$.

\[R(ψ,θ,ϕ) = R_x(ψ)R_y(θ)R_z(ϕ)\]

see e.g. http://eecs.qmul.ac.uk/~gslabaugh/publications/euler.pdf for a complete description.

Note that the gimbal lock phenomena can occur when using this rotation tensor parametrization.

source
rotation_tensor(u::Vec{3}, θ::Number)

Return the three-dimensional rotation matrix corresponding to rotation of θ radians around the vector u.

source

Special operations

For computing a special dot product between two vectors $\mathbf{a}$ and $\mathbf{b}$ with a fourth order symmetric tensor $\mathbf{C}$ such that $a_k C_{ikjl} b_l$ there is dotdot(a, C, b). This function is useful because it is the expression for the tangent matrix in continuum mechanics when the displacements are approximated by scalar shape functions.

Tensors.dotdotFunction
dotdot(::Vec, ::SymmetricFourthOrderTensor, ::Vec)

Computes a special dot product between two vectors and a symmetric fourth order tensor such that $a_k C_{ikjl} b_l$.

source

Voigt format

For some operations it is convenient to easily switch to the so called "Voigt"-format. For example when solving a local problem in a plasticity model. To simplify the conversion between tensors and Voigt format, see tovoigt, tovoigt! and fromvoigt documented below. Care must be exercised when combined with differentiation, see Differentiation of Voigt format further down.

Tensors.tovoigtFunction
tovoigt([type::Type{<:AbstractArray}, ]A::Union{SecondOrderTensor, FourthOrderTensor}; kwargs...)

Converts a tensor to "Voigt"-format.

Optional argument:

  • type: determines the returned Array type. Possible types are Array and SArray (see StaticArrays).

Keyword arguments:

  • offdiagscale: determines the scaling factor for the offdiagonal elements. This argument is only applicable for SymmetricTensors. tomandel can also be used for the "Mandel"-format which sets offdiagscale = √2 for SymmetricTensors, and is equivalent to tovoigt for Tensors.
  • order: matrix of the linear indices determining the Voigt order. The default index order is [11, 22, 33, 23, 13, 12, 32, 31, 21], corresponding to order = [1 6 5; 9 2 4; 8 7 3].

See also tovoigt! and fromvoigt.

julia> tovoigt(Tensor{2,3}(1:9))
9-element Vector{Int64}:
 1
 5
 9
 8
 7
 4
 6
 3
 2

julia> tovoigt(SymmetricTensor{2,3}(1:6); offdiagscale = 2)
6-element Vector{Int64}:
  1
  4
  6
 10
  6
  4

julia> tovoigt(Tensor{4,2}(1:16))
4×4 Matrix{Int64}:
 1  13   9  5
 4  16  12  8
 3  15  11  7
 2  14  10  6

julia> tovoigt(SMatrix, Tensor{4,2}(1:16))
4×4 SMatrix{4, 4, Int64, 16} with indices SOneTo(4)×SOneTo(4):
 1  13   9  5
 4  16  12  8
 3  15  11  7
 2  14  10  6
source
Tensors.tovoigt!Function
tovoigt!(v::AbstractArray, A::Union{SecondOrderTensor, FourthOrderTensor}; kwargs...)

Converts a tensor to "Voigt"-format using the following index order: [11, 22, 33, 23, 13, 12, 32, 31, 21].

Keyword arguments:

  • offset: offset index for where in the array A the tensor should be stored. For 4th order tensors the keyword arguments are offset_i and offset_j, respectively. Defaults to 0.
  • offdiagscale: determines the scaling factor for the offdiagonal elements. This argument is only applicable for SymmetricTensors. frommandel! can also be used for the "Mandel"-format which sets offdiagscale = √2 for SymmetricTensors, and is equivalent to fromvoigt! for Tensors.
  • order: matrix of the linear indices determining the Voigt order. The default index order is [11, 22, 33, 23, 13, 12, 32, 31, 21].

See also tovoigt and fromvoigt.

julia> T = rand(Tensor{2,2})
2×2 Tensor{2, 2, Float64, 4}:
 0.590845  0.566237
 0.766797  0.460085

julia> x = zeros(4);

julia> tovoigt!(x, T)
4-element Vector{Float64}:
 0.5908446386657102
 0.4600853424625171
 0.5662374165061859
 0.7667970365022592

julia> x = zeros(5);

julia> tovoigt!(x, T; offset=1)
5-element Vector{Float64}:
 0.0
 0.5908446386657102
 0.4600853424625171
 0.5662374165061859
 0.7667970365022592
source
Tensors.fromvoigtFunction
fromvoigt(S::Type{<:AbstractTensor}, A::AbstractArray{T}; kwargs...)

Converts an array A stored in Voigt format to a Tensor of type S.

Keyword arguments:

  • offset: offset index for where in the array A the tensor starts. For 4th order tensors the keyword arguments are offset_i and offset_j, respectively. Defaults to 0.
  • offdiagscale: determines the scaling factor for the offdiagonal elements. This argument is only applicable for SymmetricTensors. frommandel can also be used for the "Mandel"-format which sets offdiagscale = √2 for SymmetricTensors, and is equivalent to fromvoigt for Tensors.
  • order: matrix of the linear indices determining the Voigt order. The default index order is [11, 22, 33, 23, 13, 12, 32, 31, 21], corresponding to order = [1 6 5; 9 2 4; 8 7 3].

See also tovoigt.

julia> fromvoigt(Tensor{2,3}, 1.0:1.0:9.0)
3×3 Tensor{2, 3, Float64, 9}:
 1.0  6.0  5.0
 9.0  2.0  4.0
 8.0  7.0  3.0
source

Differentiation of Voigt format

Differentiating with a Voigt representation of a symmetric tensor may lead to incorrect results when converted back to tensors. The tomandel, tomandel!, and frommandel versions of tovoigt, tovoigt!, and fromvoigt can then be used. As illustrated by the following example, this will give the correct result. In general, however, direct differentiation of Tensors is faster (see Automatic Differentiation).

julia> using Tensors, ForwardDiff

julia> fun(X::SymmetricTensor{2}) = X;

julia> A = rand(SymmetricTensor{2,2});

# Differentiation of a tensor directly (correct)
julia> tovoigt(gradient(fun, A))
3×3 Matrix{Float64}:
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  0.5

# Converting to Voigt format, perform differentiation, convert back (WRONG!)
julia> ForwardDiff.jacobian(
           v -> tovoigt(fun(fromvoigt(SymmetricTensor{2,2}, v))),
           tovoigt(A)
       )
3×3 Matrix{Float64}:
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  1.0

# Converting to Mandel format, perform differentiation, convert back (correct)
julia> tovoigt(
           frommandel(SymmetricTensor{4,2},
                ForwardDiff.jacobian(
                    v -> tomandel(fun(frommandel(SymmetricTensor{2,2}, v))),
                    tomandel(A)
                )
           )
       )
3×3 Matrix{Float64}:
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  0.5