Tensors
The tensor hierarchy, component conversion and the Kelvin-Mandel map. Theory: Tensor algebra; usage: Tensors.
TensND.AbstractTens Type
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:
| Family | Types | Stores |
|---|---|---|
| general | Tens, TensRotated, TensCanonical, TensOrthogonal | a full component array |
| structured | TensISO, TensTI, TensOrtho | a 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
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.TensororSymmetricTensor)a basis of
AbstractBasistype, whose concrete type is the parameterBa tuple of variances (covariant
:covor contravariant:cont) of length equal to theorderof 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> ℬ = 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 1See also components, express, get_var.
TensND.components Function
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> ℬ = 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)/2TensND.canonical_components Function
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
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> ℬ = 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
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> ℬ = 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
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_basis Function
get_basis(t::AbstractTens) → AbstractBasisBasis 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
get_var(t::AbstractTens) → NTuple{order,Symbol}
get_var(t::AbstractTens, i::Integer) → SymbolVariance 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
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
get_array(t::TensTI{4, T}) → Array{T,4}Compute the 3×3×3×3 component array from the Walpole coefficients and axis.
get_array(t::TensTI{2,T,2}) → Array{T,2}Compute the 3×3 component array: a*(δᵢⱼ − nᵢnⱼ) + b*nᵢnⱼ.
Examples
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.0get_array(t::TensOrtho{2,T}) → Array{T,2}The 3×3 component array in the canonical frame, Σₘ aₘ eₘ⊗eₘ.
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
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
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> 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> laplacian(SymFunction("f", real = true)(r)) |> pprint
d
2 2⋅──(f(r))
d dr
───(f(r)) + ──────────
2 r
drTensor 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
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> σ = 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
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
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> Q = mandel_rotation(0.3, 0.5, 0.0);
julia> size(Q)
(6, 6)TensND.diff_with_basis Function
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
tens_basis(ℬ::AbstractBasis, i::Integer, var = :cov) → AbstractTens{1}
tens_basis(ℬ::AbstractBasis, var = :cov) → NTupleThe 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
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
from_tensors(t::Tensors.SymmetricTensor) -> TensThe 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
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
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
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
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
Tensors.majorsymmetric(t::AbstractTens{4}) -> AbstractTens{4}Major-symmetric part 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
Tensors.minorsymmetric(t::AbstractTens{4}) -> AbstractTens{4}Minor-symmetric part 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 minorsymmetric((a ⊗ a) ⊠ˢ N): one product instead of two.
See also Tensors.majorsymmetric.
TensND.SymmetricFirstPair Type
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
Tensors.symmetric(t::AbstractTens{2}) -> AbstractTens{2}Symmetric part 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
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
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
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> rebuild(TensISO{3}(3.0, 2.0), (1.0, 4.0)) == TensISO{3}(1.0, 4.0)
trueTensND.param_norms Function
param_norms(t) → NTupleThe 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
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
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
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
identity_mandel(T = Float64) -> Mandel{T}The second-order identity
TensND.sqnorm_deviator Function
sqnorm_deviator(v::Mandel)The squared norm of the deviator,
TensND.von_mises Function
von_mises(σ::Mandel)The von Mises equivalent stress
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
TensND.equivalent_strain Function
equivalent_strain(ε::Mandel)The equivalent strain von_mises, and equal to
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
MANDEL_IP
MANDEL_OPThe 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
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
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
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
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
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
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
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
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
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
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 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
acoustic_components(C, ζ) → NTuple{6}The six independent entries 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 acoustic_tensor is the same tensor as an object; sym3_inv inverts it from these entries.