Skip to content

Tensors ​

The tensor hierarchy, component conversion and the Kelvin-Mandel map. Theory: Tensor algebra; usage: Tensors.

TensND.AbstractTens Type
julia
AbstractTens{order,dim,T}

Supertype of every tensor in TensND: a tensor of a given order and dim, with element type T.

A tensor is not an array. It is an array of components together with the basis those components refer to and the variance of each index, which is what lets tensors expressed in different frames be combined, and what makes comparing raw component arrays meaningless. See Bases and variance.

The concrete subtypes fall in two families:

FamilyTypesStores
generalTens, TensRotated, TensCanonical, TensOrthogonala full component array
structuredTensISO, TensTI, TensOrthoa few scalars plus an orientation

The type follows the basis: only the non-orthonormal cases carry a variance tuple. Accessors common to all of them are get_order, get_dim, get_basis, get_var, get_data and get_array.

TensND.Tens Type
julia
Tens{order,dim,T,A<:AbstractArray,B<:Basis}

Tensor type of any order defined by

  • a multidata of components (of any type inheriting from AbstractArray, e.g. Tensor or SymmetricTensor)

  • a basis of AbstractBasis type, whose concrete type is the parameter B

  • a tuple of variances (covariant :cov or contravariant :cont) of length equal to the order of the tensor

The variance tuple only matters when the basis is not orthonormal; on an orthonormal basis the covariant and contravariant components coincide, which is why TensCanonical, TensRotated and TensOrthonormal do not carry one. See Bases and variance.

Examples

Storing the covariant metric of a non-orthogonal basis as a twice-covariant tensor, and raising one index — which necessarily returns the identity, since gⁱᵏ gₖⱼ = δⁱⱼ:

julia
julia> ℬ = Basis(Sym[1 0 0; 0 1 0; 0 1 1]) ;

julia> T = Tens(metric(ℬ, :cov), ℬ, (:cov, :cov))
3×3 Tens{2, 3, Sym, SymmetricTensor{2, 3, Sym, 6}}:
 1  0  0
 0  2  1
 0  1  1

julia> components(T, (:cont, :cov))
3×3 Matrix{Sym}:
 1  0  0
 0  1  0
 0  0  1

See also components, express, get_var.

TensND.components Function
julia
components(t::AbstractTens{order,dim,T},ℬ::AbstractBasis{dim},var::NTuple{order,Symbol})
components(t::AbstractTens{order,dim,T},ℬ::AbstractBasis{dim})
components(t::AbstractTens{order,dim,T},var::NTuple{order,Symbol})

Extract the components of a tensor for new variances and/or in a new basis

Examples

julia
julia> ℬ = Basis(Sym[0 1 1; 1 0 1; 1 1 0]) ;

julia> TV = Tens(Tensor{1,3}(i->symbols("v$i",real=true)))
TensND.TensCanonical{1, 3, Sym, Vec{3, Sym}}
# data: 3-element Vec{3, Sym}:
 v₁
 v₂
 v₃
# basis: 3×3 TensND.LazyIdentity{3, Sym}:
 1  0  0
 0  1  0
 0  0  1
# var: (:cont,)

julia> factor.(components(TV, ℬ, (:cont,)))
3-element Vector{Sym}:
 -(v1 - v2 - v3)/2
  (v1 - v2 + v3)/2
  (v1 + v2 - v3)/2

julia> components(TV, ℬ, (:cov,))
3-element Vector{Sym}:
 v₂ + v₃
 v₁ + v₃
 v₁ + v₂

julia> simplify.(components(TV, normalize(ℬ), (:cov,)))
3-element Vector{Sym}:
 sqrt(2)*(v2 + v3)/2
 sqrt(2)*(v1 + v3)/2
 sqrt(2)*(v1 + v2)/2

julia> TT = Tens(Tensor{2,3}((i,j)->symbols("t$i$j",real=true)))
TensND.TensCanonical{2, 3, Sym, Tensor{2, 3, Sym, 9}}
# data: 3×3 Tensor{2, 3, Sym, 9}:
 t₁₁  t₁₂  t₁₃
 t₂₁  t₂₂  t₂₃
 t₃₁  t₃₂  t₃₃
# basis: 3×3 TensND.LazyIdentity{3, Sym}:
 1  0  0
 0  1  0
 0  0  1
# var: (:cont, :cont)

julia> components(TT, ℬ, (:cov,:cov))
3×3 Matrix{Sym}:
 t₂₂ + t₂₃ + t₃₂ + t₃₃  t₂₁ + t₂₃ + t₃₁ + t₃₃  t₂₁ + t₂₂ + t₃₁ + t₃₂
 t₁₂ + t₁₃ + t₃₂ + t₃₃  t₁₁ + t₁₃ + t₃₁ + t₃₃  t₁₁ + t₁₂ + t₃₁ + t₃₂
 t₁₂ + t₁₃ + t₂₂ + t₂₃  t₁₁ + t₁₃ + t₂₁ + t₂₃  t₁₁ + t₁₂ + t₂₁ + t₂₂

julia> factor.(components(TT, ℬ, (:cont,:cov)))
3×3 Matrix{Sym}:
 -(t12 + t13 - t22 - t23 - t32 - t33)/2  …  -(t11 + t12 - t21 - t22 - t31 - t32)/2
  (t12 + t13 - t22 - t23 + t32 + t33)/2      (t11 + t12 - t21 - t22 + t31 + t32)/2
  (t12 + t13 + t22 + t23 - t32 - t33)/2      (t11 + t12 + t21 + t22 - t31 - t32)/2
TensND.canonical_components Function
julia
canonical_components(t::AbstractTens)

The components of t in the canonical basis, with its variances: the array components(t, ℬ) gives in a basis ℬ, for the canonical one. The tensor itself, re-expressed in that basis, is canonical(t).

TensND.express Function
julia
express(t::AbstractTens{order,dim,T},ℬ::AbstractBasis{dim},var::NTuple{order,Symbol})
express(t::AbstractTens{order,dim,T},ℬ::AbstractBasis{dim})
express(t::AbstractTens{order,dim,T},var::NTuple{order,Symbol})

The same tensor, expressed in the basis ℬ and/or with the variances var: its components are those components gives there, and the result is again a tensor. canonical(t) expresses it in the canonical basis.

julia
julia> ℬ = Basis(Sym[0 1 1; 1 0 1; 1 1 0]) ;

julia> TV = Tens(Tensor{1,3}(i->symbols("v$i",real=true)))
TensND.TensCanonical{1, 3, Sym, Vec{3, Sym}}
# data: 3-element Vec{3, Sym}:
 v₁
 v₂
 v₃
# basis: 3×3 TensND.LazyIdentity{3, Sym}:
 1  0  0
 0  1  0
 0  0  1
# var: (:cont,)

julia> factor.(components(TV, ℬ, (:cont,)))
3-element Vector{Sym}:
 -(v1 - v2 - v3)/2
  (v1 - v2 + v3)/2
  (v1 + v2 - v3)/2

julia> ℬ₀ = Basis(Sym[0 1 1; 1 0 1; 1 1 1]) ;

julia> TV0 = express(TV, ℬ₀)
Tens{1, 3, Sym, Vec{3, Sym}}
# data: 3-element Vec{3, Sym}:
     -v₁ + v₃
     -v₂ + v₃
 v₁ + v₂ - v₃
# basis: 3×3 Tensor{2, 3, Sym, 9}:
 0  1  1
 1  0  1
 1  1  1
# var: (:cont,)
TensND.canonical Function
julia
canonical(t::AbstractTens)

The same tensor, expressed in the canonical basis: express(t, ℬ) for the canonical ℬ, its components being canonical_components(t). A TensISO, TensTI, TensOrtho or TensCubic has canonical components already and is returned as it is, in its class.

julia
julia> ℬ = Basis(Sym[0 1 1; 1 0 1; 1 1 0]) ;

julia> TV = Tens(Tensor{1,3}(i->symbols("v$i",real=true)), ℬ)
Tens{1, 3, Sym, Vec{3, Sym}}
# data: 3-element Vec{3, Sym}:
 v₁
 v₂
 v₃
# basis: 3×3 Tensor{2, 3, Sym, 9}:
 0  1  1
 1  0  1
 1  1  1
# var: (:cont,)

julia> TV0 = canonical(TV)
TensND.TensCanonical{1, 3, Sym, Vec{3, Sym}}
# data: 3-element Vec{3, Sym}:
      v₂ + v₃
      v₁ + v₃
 v₁ + v₂ + v₃
# basis: 3×3 TensND.LazyIdentity{3, Sym}:
 1  0  0
 0  1  0
 0  0  1
# var: (:cont,)
TensND.generic_tens Function
julia
generic_tens(t::AbstractTens)

The tensor t as a generic Tens of its canonical components: a structured tensor (TensISO, TensTI, TensOrtho, TensCubic) forgets its class, so that it accepts any update — zero(generic_tens(t)) is a zero of the same order, dimension and element type that a non-isotropic term can be added to, where zero(t) of a TensTI is a TensISO.

TensND.get_order Function
julia
get_order(t::AbstractTens) → Int

Order (number of indices) of t.

TensND.get_basis Function
julia
get_basis(t::AbstractTens) → AbstractBasis

Basis the components of t refer to. Structured types report the canonical basis, their orientation being carried separately by axis or frame.

TensND.get_var Function
julia
get_var(t::AbstractTens) → NTuple{order,Symbol}
get_var(t::AbstractTens, i::Integer) → Symbol

Variance of each index of t, :cov (covariant) or :cont (contravariant).

On an orthonormal basis the two coincide, so tensors stored on one report a constant tuple and the distinction may be ignored. See Bases and variance.

TensND.get_data Function
julia
get_data(t::AbstractTens)

The stored data: the coefficient tuple for a structured tensor (TensISO, TensTI, TensOrtho), the component array otherwise. Use get_array when the full component array is wanted in every case.

TensND.get_array Function
julia
get_array(t::TensTI{4, T}) → Array{T,4}

Compute the 3×3×3×3 component array from the Walpole coefficients and axis.

julia
get_array(t::TensTI{2,T,2}) → Array{T,2}

Compute the 3×3 component array: a*(δᵢⱼ − nᵢnⱼ) + b*nᵢnⱼ.

Examples

julia
julia> A = TensTI{2}(5.0, 8.0, [0., 0., 1.]);

julia> get_array(A)
3×3 Matrix{Float64}:
 5.0  0.0  0.0
 0.0  5.0  0.0
 0.0  0.0  8.0
julia
get_array(t::TensOrtho{2,T}) → Array{T,2}

The 3×3 component array in the canonical frame, Σₘ aₘ eₘ⊗eₘ.

julia
get_array(t::TensCubic{T}) -> Array{T,4}

The 3×3×3×3 component array in the canonical frame.

Evaluated through _ortho_entry with C₁₁ = C₂₂ = C₃₃, C₁₂ = C₁₃ = C₂₃ and C₄₄ = C₅₅ = C₆₆, which is what a cubic tensor is as an orthotropic one — no second copy of that algebra.

TensND.sqnorm Function
julia
sqnorm(t::AbstractTens)

The square of norm(t), the self-contraction uᵢuⁱ, tᵢⱼtⁱʲ or tᵢⱼₖₗtⁱʲᵏˡ, computed without the square root: smooth at zero, where the derivative of norm is not, and exact for symbolic components. On complex components it is Hermitian, t̄ ⋅ t: the contractions do not conjugate (they are contractions), so the self-contraction of a complex tensor is not its squared norm. Real element types, duals and symbols included, keep the plain self-contraction, with no abs in a symbolic result.

TensND.pprint Function
julia
pprint(t::AbstractTens; vec = '𝐞', coords = 1:dim)
pprint(t::AbstractTens, CS::AbstractCoorSystem; vec = '𝐞')

Print t expanded on its basis — as a sum of basis products such as (σʳʳ)𝐞ʳ⊗𝐞ʳ + (σᶿᶿ)𝐞ᶿ⊗𝐞ᶿ — instead of as an array of components.

This is a display helper: it writes to stdout and returns nothing. For symbolic results it is far more readable than the component array, and it is the natural way to report a field computed in a curvilinear frame. Zero components are omitted, and a vanishing tensor prints as 0.

Passing a coordinate system names the basis vectors after its coordinates; @set_coorsys makes that the default, so pprint(t) alone then prints 𝐞ʳ, 𝐞ᶿ, … rather than 𝐞₁, 𝐞₂, ….

Formerly intrinsic, then print_tensor

intrinsic was misleading: what is printed is explicitly basis dependent — it changes with the chart — whereas "intrinsic" denotes the basis-free form. print_tensor was misleading in turn, because the fallback below accepts a scalar, and pprint is the name MPCM already uses for a generic pretty-printer (ChemistryLab.pprint). Both former names were removed in 0.11.

Examples

julia
julia> Spherical = coorsys_spherical() ; 𝐞ᶿ, 𝐞ᵠ, 𝐞ʳ = unitvec(Spherical) ;

julia> @set_coorsys Spherical

julia> pprint(𝐞ʳ ⊗ 𝐞ʳ + 2 * 𝐞ᶿ ⊗ 𝐞ᶿ)   # spherical coordinates are ordered (θ, ϕ, r)
(2)𝐞ᶿ⊗𝐞ᶿ + 𝐞ʳ⊗𝐞ʳ

Applied to anything that is not a tensor — a scalar field, typically, so that laplacian(f(r)) |> pprint works in a pipeline — it falls back to the rich text/plain display, which for a symbolic expression is SymPy's two-dimensional form:

julia
julia> laplacian(SymFunction("f", real = true)(r)) |> pprint
              d
 2          2⋅──(f(r))
d             dr
───(f(r)) + ──────────
  2             r
dr

Tensor coefficients are deliberately not printed that way: a multi-line coefficient interpolated into (…)𝐞ʳ⊗𝐞ʳ + (…)𝐞ᶿ⊗𝐞ᶿ would destroy the layout that makes the expanded form readable in the first place.

See also get_array, components.

TensND.mandel Function
julia
mandel(t; symmetrize = false, kwargs...)
mandel(t, b::AbstractBasis; symmetrize = false, kwargs...)
mandel(t, b::AbstractBasis, var::NTuple{order,Symbol}; symmetrize = false, kwargs...)

Write the components of a second or fourth order tensor in Kelvin-Mandel notation, in its own basis or in the basis b (with the variances var, in the order of components). t is a TensND tensor, a Tensors.jl one, or an array of components. A TensISO, TensTI, TensOrtho or TensCubic is written from its coefficients, in the canonical basis.

The size follows the symmetries of the storage: a matrix 6×6 (3×3 in 2D) for a fourth-order tensor stored with its minor symmetries, 9×9 (4×4) otherwise, and a vector of 6 (3) or 9 (4) components at order 2. With symmetrize = true the result is the Kelvin-Mandel matrix or vector of the minor-symmetric part of the components, whatever the storage, with no assumption of major symmetry: the form a concentration tensor is handled in. On components that have the minor symmetries it reads them exactly, to the last bit.

The other keyword arguments go to Tensors.tomandel.

Examples

julia
julia> σ = Tens(SymmetricTensor{2,3}((i, j) -> symbols("σ$i$j", real = true))) ;

julia> mandel(σ)
6-element Vector{Sym}:
         σ11
         σ22
         σ33
      √2⋅σ32
      √2⋅σ31
      √2⋅σ21

julia> C = Tens(SymmetricTensor{4,3}((i, j, k, l) -> symbols("C$i$j$k$l", real = true))) ;

julia> mandel(C)
6×6 Matrix{Sym}:
         C₁₁₁₁     C₁₁₂₂     C₁₁₃₃  √2⋅C₁₁₃₂  √2⋅C₁₁₃₁  √2⋅C₁₁₂₁
         C₂₂₁₁     C₂₂₂₂     C₂₂₃₃  √2⋅C₂₂₃₂  √2⋅C₂₂₃₁  √2⋅C₂₂₂₁
         C₃₃₁₁     C₃₃₂₂     C₃₃₃₃  √2⋅C₃₃₃₂  √2⋅C₃₃₃₁  √2⋅C₃₃₂₁
      √2⋅C₃₂₁₁  √2⋅C₃₂₂₂  √2⋅C₃₂₃₃   2⋅C₃₂₃₂   2⋅C₃₂₃₁   2⋅C₃₂₂₁
      √2⋅C₃₁₁₁  √2⋅C₃₁₂₂  √2⋅C₃₁₃₃   2⋅C₃₁₃₂   2⋅C₃₁₃₁   2⋅C₃₁₂₁
      √2⋅C₂₁₁₁  √2⋅C₂₁₂₂  √2⋅C₂₁₃₃   2⋅C₂₁₃₂   2⋅C₂₁₃₁   2⋅C₂₁₂₁
TensND.from_mandel Function
julia
from_mandel(v::AbstractVecOrMat[, b::AbstractBasis]; kwargs...)
from_mandel(TT::Type{<:Tensors.AllTensors}, v[, b]; kwargs...)

The tensor whose Kelvin-Mandel vector or matrix is v, in the basis b (the canonical one by default). The size of v gives the Tensors.jl type TT — inferred when v is a static array — unless it is given.

TensND.mandel_rotation Function
julia
mandel_rotation(θ, ϕ, ψ) → SMatrix{6,6}
mandel_rotation(R::AbstractMatrix) → SMatrix{6,6}

6×6 Kelvin-Mandel (Bond) rotation matrix, from ZYZ Euler angles (θ, ϕ, ψ) or directly from a 3×3 rotation matrix R whose columns are the new basis vectors.

Transforms a 6×6 KM matrix as C' = Q' · C · Q with Q = mandel_rotation(…); Q is orthogonal.

Built entirely with SMatrix / ntuple, so it is compatible with ForwardDiff.Dual and with non-isbits element types such as SymPyPythonCall.Sym or Symbolics.Num, for which an MMatrix{6,6,T}(undef) cannot even be constructed.

Examples

julia
julia> Q = mandel_rotation(0.3, 0.5, 0.0);

julia> size(Q)
(6, 6)
TensND.diff_with_basis Function
julia
diff_with_basis(t::AbstractTens, args...; kwargs...)

Differentiate t including its basis, then re-express the result on the original basis and variance.

This differs from ∂ applied componentwise: when the basis itself depends on the differentiation variable — as a rotated or curvilinear frame does — the derivative of the basis vectors contributes, and dropping it is the classic way to lose the Christoffel terms.

TensND.tens_basis Function
julia
tens_basis(ℬ::AbstractBasis, i::Integer, var = :cov) → AbstractTens{1}
tens_basis(ℬ::AbstractBasis, var = :cov) → NTuple

The i-th basis vector of ℬ as a first-order tensor, or all of them as a tuple. var = :cov gives the basis vectors 𝐞ᵢ, var = :cont the dual vectors 𝐞ⁱ.

Tensors.jl and the Voigt forms ​

Components in the canonical basis, whatever basis a tensor is written in: the form a finite-element code (Ferrite among others) and an Abaqus-style interface expect.

TensND.to_tensors Function
julia
to_tensors(t::AbstractTens{2,dim}) -> Tensors.SymmetricTensor{2,dim}
to_tensors(t::AbstractTens{4,dim}) -> Tensors.SymmetricTensor{4,dim}

The Tensors.jl counterpart of t, in the canonical basis: the components a finite-element code (Ferrite among others) contracts with its shape-function gradients.

A second-order tensor is symmetrized. A fourth-order one is assumed to have the minor symmetries, as stiffnesses, compliances and concentration tensors do; the major symmetry is not assumed, SymmetricTensor{4,3} holding 36 independent components.

Not get_array

get_array(t) and t[i, j, k, l] are the components of t in its own basis. For a tensor written in a rotated basis they are not the canonical ones; to_tensors performs the change of basis.

TensND.from_tensors Function
julia
from_tensors(t::Tensors.SymmetricTensor) -> Tens

The TensND tensor whose components in the canonical basis are those of t: the inverse of to_tensors, for a strain or a stress coming out of a finite-element code.

TensND.voigt_stress Function
julia
voigt_stress(σ::AbstractTens{2,3}) -> NTuple{6}
voigt_strain(ε::AbstractTens{2,3}) -> NTuple{6}

Canonical components of a symmetric second-order tensor in the Voigt order (11, 22, 33, 12, 13, 23) of Abaqus-style interfaces.

The two differ by the engineering shears: voigt_strain doubles the off-diagonal terms ( ), voigt_stress does not. Two functions rather than one with a flag, because a single voigt is the classic way to lose a factor of two between the strain that goes in and the stress that comes out. Voigt is not an isometry, unlike the Kelvin–Mandel form of mandel, and belongs only at the boundary with codes that demand it.

TensND.voigt_strain Function
julia
voigt_stress(σ::AbstractTens{2,3}) -> NTuple{6}
voigt_strain(ε::AbstractTens{2,3}) -> NTuple{6}

Canonical components of a symmetric second-order tensor in the Voigt order (11, 22, 33, 12, 13, 23) of Abaqus-style interfaces.

The two differ by the engineering shears: voigt_strain doubles the off-diagonal terms ( ), voigt_stress does not. Two functions rather than one with a flag, because a single voigt is the classic way to lose a factor of two between the strain that goes in and the stress that comes out. Voigt is not an isometry, unlike the Kelvin–Mandel form of mandel, and belongs only at the boundary with codes that demand it.

TensND.stress_from_voigt Function
julia
stress_from_voigt(v) -> Tens{2,3}
strain_from_voigt(v) -> Tens{2,3}

Inverses of voigt_stress and voigt_strain: the tensor, in the canonical basis, whose Voigt components (11, 22, 33, 12, 13, 23) are v, the engineering shears of a strain being halved.

TensND.strain_from_voigt Function
julia
stress_from_voigt(v) -> Tens{2,3}
strain_from_voigt(v) -> Tens{2,3}

Inverses of voigt_stress and voigt_strain: the tensor, in the canonical basis, whose Voigt components (11, 22, 33, 12, 13, 23) are v, the engineering shears of a strain being halved.

Symmetric parts ​

Methods of the Tensors.jl functions, which keep the symmetry class of a structured tensor where the class allows it.

Tensors.majorsymmetric Method
julia
Tensors.majorsymmetric(t::AbstractTens{4}) -> AbstractTens{4}

Major-symmetric part   of a fourth-order tensor, a method of the Tensors.jl function.

The symmetry class is kept: an isotropic, cubic or nine-constant orthotropic tensor is its own major-symmetric part, a twelve-constant orthotropic one (a product of orthotropic tensors lands there) returns the nine-constant form on the same frame, and a transversely isotropic tensor in the six- or eight-parameter Walpole form (a product of TI tensors lands there) returns the five-parameter form about the same axis. A general tensor stays in its basis; in a basis that is not orthonormal, the transpose pairs components of the same variance, so they are first written with the variance of the first pair on the second as well.

See also Tensors.symmetric for a second-order tensor.

Tensors.minorsymmetric Method
julia
Tensors.minorsymmetric(t::AbstractTens{4}) -> AbstractTens{4}

Minor-symmetric part     of a fourth-order tensor, a method of the Tensors.jl function. A structured tensor (isotropic, transversely isotropic, orthotropic, cubic) is its own minor-symmetric part; a general tensor stays in its basis, stored symmetric. In a basis that is not orthonormal, the transposed indices are first given the same variance, that of the first index of each pair.

For instance, the half sum      , that is    for a symmetric , is minorsymmetric((a ⊗ a) ⊠ˢ N): one product instead of two.

See also Tensors.majorsymmetric.

TensND.SymmetricFirstPair Type
julia
SymmetricFirstPair{dim, T} <: AbstractArray{T, 3}

A static third-order array symmetric in its first two indices, the result of a vector ⊗ˢ a second-order tensor. It carries the symmetry in its type, so that ⊗ˢ with a vector next gives a SymmetricTensor{4} directly.

Tensors.symmetric Method
julia
Tensors.symmetric(t::AbstractTens{2}) -> AbstractTens{2}

Symmetric part   of a second-order tensor, a method of the Tensors.jl function. An isotropic or transversely isotropic tensor keeps its class (the skew part c·w of a three-parameter TensTI{2} goes); a general one stays in its basis, its components written with one variance before transposing.

Real and imaginary parts ​

Base.real Method
julia
real(t::AbstractTens)
imag(t::AbstractTens)

The real or imaginary part of the tensor t, in its class: a TensISO, TensTI, TensOrtho or TensCubic stays one (its coefficients are linear in the tensor, its axis or frame a real geometry), a tensor in a real basis stays in that basis. In a complex basis the real part of the components is not the components of the real part, so such a tensor comes back in the canonical basis.

Base.conj Method
julia
conj(t::AbstractTens)

The complex conjugate of the tensor t, in its class, as real and imag are.

Coefficients and flat vectors ​

TensND.rebuild Function
julia
rebuild(t, data)

The tensor of the class of t — same order, same axis or frame — whose coefficients (get_data, in the same order) are data, a tuple or a vector. The coefficients are promoted to a common type; a transversely isotropic tensor also promotes its axis, which shares their type. A TensTI given six or eight coefficients becomes the six- or eight-parameter form.

julia
julia> rebuild(TensISO{3}(3.0, 2.0), (1.0, 4.0)) == TensISO{3}(1.0, 4.0)
true
TensND.param_norms Function
julia
param_norms(t) → NTuple

The Frobenius norms of the tensors the coefficients of t multiply, ‖rebuild(t, eᵢ)‖: (1, √5) for the isotropic (𝕁, 𝕂) in 3-D, the norms of the Walpole, orthotropic or cubic basis tensors for the other classes. These bases are orthogonal, so norm(t) == norm(param_norms(t) .* get_data(t)): the weights that make the Euclidean norm of the coefficients the norm of the tensor. For a generic tensor, whose to_vector coordinates are orthonormal Kelvin-Mandel components, they are all one.

TensND.to_vector Function
julia
to_vector(t) → Vector
from_vector(prototype, v)

A tensor as a flat vector of real coordinates, and back: its coefficients (get_data) for a TensISO, TensTI, TensOrtho or TensCubic, the Kelvin-Mandel components in the canonical basis otherwise (6 or 9 at order 2, 36 or 81 at order 4). from_vector rebuilds a tensor of the class of prototype, with its axis or frame; a generic tensor comes back in the canonical basis. Both are linear, for an ODE state, a nonlinear solver or a sequence accelerator.

TensND.from_vector Function
julia
from_vector(prototype, v)

The tensor whose flat vector (to_vector) is v: of the class of prototype, with its axis or frame, or in the canonical basis for a generic one.

Symmetric tensors as Kelvin-Mandel vectors ​

A symmetric second-order tensor of dimension 3 as the static vector of its six Kelvin-Mandel components, with the operations a constitutive law needs on it.

TensND.Mandel Type
julia
Mandel{T}

A symmetric second-order tensor of dimension 3 in Kelvin–Mandel components, an SVector{6, T} ordered

the order of mandel and of Tensors.jl's tomandel. The basis is orthonormal:     (dot(σ, ε)), the norm of the tensor is norm(v), and a fourth-order tensor with the minor symmetries is an SMatrix{6, 6} acting by the matrix product.

See also identity_mandel, trace_mandel, deviator, sqnorm_deviator, von_mises, equivalent_strain.

TensND.identity_mandel Function
julia
identity_mandel(T = Float64) -> Mandel{T}

The second-order identity , in Kelvin–Mandel components.

TensND.trace_mandel Function
julia
trace_mandel(v::Mandel)

The trace   .

TensND.deviator Function
julia
deviator(v::Mandel) -> Mandel

The deviatoric part   .

TensND.sqnorm_deviator Function
julia
sqnorm_deviator(v::Mandel)

The squared norm of the deviator,  , without a square root: the form a law differentiates where the deviator vanishes (   ). It is formed from the differences of the normal components, so that it is exactly zero for a change of volume, whatever the rounding, where subtracting the mean would leave a few ulps (and a law with a power of the deviatoric strain would take them for a deviatoric strain, or produce 0 × ∞ in its derivatives).

TensND.von_mises Function
julia
von_mises(σ::Mandel)

The von Mises equivalent stress  , being the deviator of : equal to in uniaxial tension.

Not differentiable at zero

The square root has no derivative at a vanishing deviator. A law whose response must be differentiated there is written with instead.

TensND.equivalent_strain Function
julia
equivalent_strain(ε::Mandel)

The equivalent strain  , being the deviator of : work-conjugate to von_mises, and equal to for an incompressible uniaxial strain.

Kelvin-Mandel blocks about a normal ​

In a frame whose third axis is a normal n, the in-plane and out-of-plane symmetric tensors split the Kelvin-Mandel slots; the maps below work on that partition, with no pivoting and no SVD (duals and symbols go through).

TensND.MANDEL_IP Constant
julia
MANDEL_IP
MANDEL_OP

The Kelvin-Mandel slots of the in-plane symmetric tensors, (1, 2, 6) for (ℓ⊗ℓ, m⊗m, √2 ℓ⊗ˢm), and of the out-of-plane ones, (3, 4, 5) for (n⊗n, √2 m⊗ˢn, √2 ℓ⊗ˢn), in an orthonormal frame (ℓ, m, n) whose third axis is the normal n. Across the interfaces of a laminate normal to n, the in-plane strain and the out-of-plane stress are continuous.

TensND.MANDEL_OP Constant

The Kelvin-Mandel slots of the out-of-plane symmetric tensors; see MANDEL_IP.

TensND.mandel_ip_block Function
julia
mandel_ip_block(M) → SMatrix{3,3}
mandel_op_block(M) → SMatrix{3,3}

The in-plane or out-of-plane block of a 6×6 Kelvin-Mandel matrix, in slot order (MANDEL_IP, MANDEL_OP), with its Mandel weights: no rescaling and no permutation (acoustic_tensor reads the physical acoustic tensor).

TensND.mandel_op_block Function

The out-of-plane block of a 6×6 Kelvin-Mandel matrix; see mandel_ip_block.

TensND.mandel_ip_embed Function
julia
mandel_ip_embed(B) → SMatrix{6,6}
mandel_op_embed(B) → SMatrix{6,6}

A slot-ordered 3×3 block scattered into a 6×6 Kelvin-Mandel matrix, zero everywhere else: the right inverse of mandel_ip_block or mandel_op_block.

TensND.mandel_op_embed Function

An out-of-plane block scattered into a 6×6 Kelvin-Mandel matrix; see mandel_ip_embed.

TensND.mandel_from_blocks Function
julia
mandel_from_blocks(X_II, X_IO, X_OI, X_OO) → SMatrix{6,6}

The 6×6 Kelvin-Mandel matrix of its four slot-ordered 3×3 blocks on the in-plane / out-of-plane partition (MANDEL_IP, MANDEL_OP).

TensND.mandel_inv_schur Function
julia
mandel_inv_schur(M) → SMatrix{6,6}

The inverse of a 6×6 Kelvin-Mandel matrix by block elimination on the in-plane / out-of-plane partition: two 3×3 inverses by cofactors and a Schur complement, with no pivoting and no factorization, so that it holds for duals and for symbols, where an LU factorization pivots on values. Needs the out-of-plane block and the in-plane Schur complement invertible, as they are for any stiffness whose acoustic tensor about n is positive definite.

TensND.plane_pinv Function
julia
plane_pinv(M) → SMatrix{6,6}
plane_pinv2(K) → SMatrix{3,3}

The Moore-Penrose pseudo-inverse of the out-of-plane part of a 6×6 Kelvin-Mandel matrix (plane_pinv): the inverse of its mandel_op_block, embedded back. For the Kelvin-Mandel matrix of a stiffness ℂ it is the Hill tensor of a flat inclusion of normal n, n ⊗ˢ (n⋅ℂ⋅n)⁻¹ ⊗ˢ n; for a matrix supported on the out-of-plane subspace it is its pseudo-inverse. plane_pinv2 is the order-2 counterpart for a 3×3 matrix written in the frame (ℓ, m, n): (1/K₃₃) n⊗n. No SVD, so duals and symbols go through.

TensND.plane_pinv2 Function

The order-2 out-of-plane pseudo-inverse (1/K₃₃) n⊗n; see plane_pinv.

TensND.deviatoric_pinv Function
julia
deviatoric_pinv(M) → SMatrix{6,6}

The Moore-Penrose pseudo-inverse of a 6×6 Kelvin-Mandel matrix supported on the deviators (𝕁 M = M 𝕁 = 0): its inverse on the deviatoric subspace, zero on volume changes. For the deviatoric stiffness of an incompressible medium it is the compliance on deviators, and conversely. Written (M + 𝕁)⁻¹ − 𝕁, the volume changes taking the identity while the matrix is inverted: M + 𝕁 is invertible whenever M is on deviators, and is inverted by mandel_inv_schur, so that duals and symbols go through (no SVD, no pivoting). That M acts on deviators only is assumed, not checked: the check would be a value test.

TensND.acoustic_tensor Function
julia
acoustic_tensor(M::AbstractMatrix) → SMatrix{3,3}
acoustic_tensor(ℂ::AbstractTens{4,3}, n) → Tens{2,3}

The acoustic tensor K = n⋅ℂ⋅n, Kᵢⱼ = nₖ ℂₖᵢₗⱼ nₗ: read off the out-of-plane block of a 6×6 Kelvin-Mandel matrix written in a frame whose third axis is n (its Mandel weights and the (3, 2, 1) order of its slots undone), or computed from a tensor and a direction.

TensND.compliance_op_block Function
julia
compliance_op_block(X) → SMatrix{3,3}

The slot-ordered out-of-plane block (mandel_op_block) of the fourth-order compliance X ⊗ˢ n ⊗ˢ n — the strain (X⋅(σ⋅n)) ⊗ˢ n, which is also n ⊗ˢ X ⊗ˢ n on symmetric stresses — from the 3×3 matrix X written in the frame (ℓ, m, n) (symmetric for an interface, not necessarily otherwise): the map acoustic_tensor inverts on the compliance side, Ŝₐᵦ = X_{π(a)π(b)} / (sₐ sᵦ), π = (3, 2, 1), s = (1, √2, √2). For a spring interface of compliance X, mandel_op_embed(compliance_op_block(X)) is its Kelvin-Mandel matrix.

Small matrices by cofactors ​

Closed-form inverses of 3×3 matrices, and the acoustic tensor as the six numbers an integral over the directions needs: no pivot, no allocation, duals and symbols through.

TensND.inv3 Function
julia
inv3(K) → SMatrix{3,3}

The inverse of a 3×3 matrix by its cofactors and one determinant, as a static matrix: no pivot, no allocation, for duals as for floats, and on symbols an expression of the size of the cofactors (the generic inverse of StaticArrays swells symbolic results by orders of magnitude). Singular matrices are not detected: the determinant is inverted as it is.

TensND.sym3_inv Function
julia
sym3_inv(K11, K22, K33, K12, K13, K23) → NTuple{9}

The inverse of a symmetric 3×3 matrix from its six entries, by its adjugate and determinant, flattened in column-major order (iK[i + 3(j - 1)] is ): the symmetric case of inv3, with the cofactors a symmetric matrix shares computed once, for the kernels of integrals over directions where it is evaluated millions of times. Singular matrices are not detected.

TensND.acoustic_components Function
julia
acoustic_components(C, ζ) → NTuple{6}

The six independent entries of the acoustic tensor     of a fourth-order array of canonical components C with the minor symmetries, along a direction ζ of any length: the kernel of an integral over the directions, with no tensor object and no allocation (a recurrence over the nine pairs accumulating the six sums at once). acoustic_tensor is the same tensor as an object; sym3_inv inverts it from these entries.