Structured tensors
<!– crosslinks: written by .github/scripts/crosslinks.py from docs/crosslinks.toml; do not edit –>
See also
Theory: Isotropic tensors · The Walpole basis · Transversely isotropic parametrizations · Orthotropy · Cubic symmetry
Manual: Elastic moduli · Parametrizations and conversions · Projection and symmetry detection
Tutorials: Elastic Green's functions · The elastic sphere, solved symbolically · The Walpole basis end to end · Acoustic and Hill tensors · Projection and symmetry detection · A two-point Green tensor between offset frames
<!– /crosslinks –>
The compact symmetry-class types. Each stores a handful of scalars instead of
Storage
Every class is parametrized the same way, by its order and the number N of stored coefficients; the invariance group decides which tensors a shape holds. The last three columns say what a product (⊡ at order 4, ⋅ at order 2) of two tensors of the shape on the same reference, an inverse and a major transpose return; a sum keeps the shape.
| Type | Order | N | Stored | Reference | Group | Product | Inverse | Transpose |
|---|---|---|---|---|---|---|---|---|
TensISO{2,dim} | 2 | 1 | none | same | same | itself | ||
TensISO{4,dim} | 4 | 2 | none | same | same | itself | ||
TensCubic{T,B} | 4 | 3 | cube frame | same | same | itself | ||
TensTI{2,T,2} | 2 | 2 | axis | same | same | itself | ||
TensTI{2,T,3} | 2 | 3 | axis | same | same | |||
TensTI{4,T,5} | 4 | 5 | axis | N=6 | same | itself | ||
TensTI{4,T,6} | 4 | 6 | axis | same | same | |||
TensTI{4,T,8} | 4 | 8 | axis | same | same | |||
TensOrtho{2,T,3} | 2 | 3 | frame | same | same | itself | ||
TensOrtho{4,T,9} | 4 | 9 | frame | N=12 | same | itself | ||
TensOrtho{4,T,12} | 4 | 12 | frame | same | same | normal block transposed |
The major-symmetric shapes are the ones whose product widens (N=5 → 6, N=9 → 12): their product is a product of two symmetric blocks. Tensors.majorsymmetric goes back (N=6, 8 → 5, N=12 → 9). The
using TensND, LinearAlgebra
iso = TensISO{3}(3 * 20.0, 2 * 8.0) # 3k 𝕁 + 2μ 𝕂TensISO{4, 3, Float64, 2}
→ decomposition: (60.0) 𝕁 + (16.0) 𝕂
→ KM: 6×6 Matrix{Float64}:
30.6667 14.6667 14.6667 0.0 0.0 0.0
14.6667 30.6667 14.6667 0.0 0.0 0.0
14.6667 14.6667 30.6667 0.0 0.0 0.0
0.0 0.0 0.0 16.0 0.0 0.0
0.0 0.0 0.0 0.0 16.0 0.0
0.0 0.0 0.0 0.0 0.0 16.0The show method prints the algebraic form, not 81 numbers:
n = (0.0, 0.0, 1.0)
ti = TensTI{4}(12.0, 13.0, 3.5, 7.0, 4.0, n)TensTI{4, Float64, 5}
→ decomposition: (12.0) W₁ˢ + (13.0) W₂ˢ + (3.5) W₃ˢ + (7.0) W₄ˢ + (4.0) W₅ˢ
axis n = (0.0, 0.0, 1.0)
→ KM: 6×6 Matrix{Float64}:
10.0 3.0 2.47487 0.0 0.0 0.0
3.0 10.0 2.47487 0.0 0.0 0.0
2.47487 2.47487 12.0 0.0 0.0 0.0
0.0 0.0 0.0 4.0 0.0 0.0
0.0 0.0 0.0 0.0 4.0 0.0
0.0 0.0 0.0 0.0 0.0 7.0ℬ = CanonicalBasis{3, Float64}()
ort = TensOrtho(10.0, 8.0, 9.0, 3.0, 2.0, 4.0, 2.5, 3.0, 1.5, ℬ)TensOrtho{4, Float64, 9, CanonicalBasis{3, Float64}}
→ decomposition: (10.0) P₁⊗P₁ + (8.0) P₂⊗P₂ + (9.0) P₃⊗P₃
+ (3.0)(P₁⊗P₂+P₂⊗P₁) + (2.0)(P₁⊗P₃+P₃⊗P₁) + (4.0)(P₂⊗P₃+P₃⊗P₂)
+ 2(2.5)(P₂⊠ˢP₃) + 2(3.0)(P₁⊠ˢP₃) + 2(1.5)(P₁⊠ˢP₂)
frame: [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]
→ KM (material frame): 6×6 StaticArraysCore.SMatrix{6, 6, Float64, 36} with indices SOneTo(6)×SOneTo(6):
10.0 3.0 2.0 0.0 0.0 0.0
3.0 8.0 4.0 0.0 0.0 0.0
2.0 4.0 9.0 0.0 0.0 0.0
0.0 0.0 0.0 5.0 0.0 0.0
0.0 0.0 0.0 0.0 6.0 0.0
0.0 0.0 0.0 0.0 0.0 3.0
→ KM (canonical frame): 6×6 Matrix{Float64}:
10.0 3.0 2.0 0.0 0.0 0.0
3.0 8.0 4.0 0.0 0.0 0.0
2.0 4.0 9.0 0.0 0.0 0.0
0.0 0.0 0.0 5.0 0.0 0.0
0.0 0.0 0.0 0.0 6.0 0.0
0.0 0.0 0.0 0.0 0.0 3.0cub = cubic_from_constants(10.0, 4.0, 2.0, ℬ) # (C₁₁, C₁₂, C₄₄)TensCubic{Float64, CanonicalBasis{3, Float64}}
→ decomposition: (18.0) 𝕁 + (6.0) 𝔼 + (4.0) 𝕋
cube frame: [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]
→ KM: 6×6 Matrix{Float64}:
10.0 4.0 4.0 0.0 0.0 0.0
4.0 10.0 4.0 0.0 0.0 0.0
4.0 4.0 10.0 0.0 0.0 0.0
0.0 0.0 0.0 4.0 0.0 0.0
0.0 0.0 0.0 0.0 4.0 0.0
0.0 0.0 0.0 0.0 0.0 4.0Constructors
| Class | Constructors |
|---|---|
| ISO | TensISO, tens_Id2, tens_Id4, tens_J4, tens_K4, iso_projectors |
| TI order 4 | TensTI, ti_from_constants, iso_to_ti, tens_W1…tens_W8, walpole_basis, walpole_basis_sym |
| TI order 2 | TensTI{2}(a, b, n) and (a, b, c, n) |
| CUBIC | TensCubic, cubic_from_constants, iso_to_cubic |
| ORTHO order 4 | TensOrtho{4}(C₁₁, …, C₆₆, frame) (nine constants) and (…, C₂₁, C₃₁, C₃₂, frame) (twelve), TensOrtho(KM, frame[, Val(12)]), iso_to_ortho, walpole_to_ortho, cubic_to_ortho |
| ORTHO order 2 | TensOrtho{2}(a₁, a₂, a₃, frame), iso_to_ortho, walpole_to_ortho |
Accessors
| Function | Applies to | Returns |
|---|---|---|
get_data | all | the stored coefficient tuple |
walpole_coeffs | TensTI{4} | the six classical Walpole coefficients |
walpole_coeffs | TensTI{4} | all eight, zero-padded |
axis | TensTI | the symmetry axis |
frame | TensOrtho, TensCubic | the material or cube frame |
reference | TensTI, TensOrtho, TensCubic | axis or frame, whichever applies |
symmetry | all | the class as a Symbol |
mandel_material | TensOrtho, TensCubic | the sparse matrix in the class's own frame |
cubic_constants | TensCubic | the constants |
cubic_anisotropy | TensCubic | the Zener-type departure from isotropy |
walpole_coeffs(ti), axis(ti)((12.0, 13.0, 3.5, 3.5, 7.0, 4.0), (0.0, 0.0, 1.0))mandel_material(ort)6×6 StaticArraysCore.SMatrix{6, 6, Float64, 36} with indices SOneTo(6)×SOneTo(6):
10.0 3.0 2.0 0.0 0.0 0.0
3.0 8.0 4.0 0.0 0.0 0.0
2.0 4.0 9.0 0.0 0.0 0.0
0.0 0.0 0.0 5.0 0.0 0.0
0.0 0.0 0.0 0.0 6.0 0.0
0.0 0.0 0.0 0.0 0.0 3.0Closure: what an operation returns
This is the table to know. ⊡ between structured types stays structured only while the result stays in the class; otherwise it falls back to the generic route, exactly and without warning.
A ⊡ B | ISO | CUBIC | TI | TI | TI | ORTHO |
|---|---|---|---|---|---|---|
| ISO | ISO | CUBIC | TI | TI | TI | ORTHO |
| CUBIC | CUBIC | CUBIC | generic | generic | generic | ORTHO |
| TI{4,5} | TI | generic | TI | TI | TI | ORTHO |
| TI{4,6} | TI | generic | TI | TI | TI | ORTHO |
| TI{4,8} | TI | generic | TI | TI | TI | generic |
| ORTHO | ORTHO | ORTHO | ORTHO | ORTHO | generic | ORTHO |
The CUBIC row and column need the two frames to describe the same cube, the iso_projectors/TI/ORTHO ones the same axis or frame, and a TI ⊡ ORTHO product a TI axis along one of the frame axes; otherwise the generic route is taken. ORTHO{12} stands for TensOrtho{4, T, 12}, whichever form the operands have.
Two widenings deserve an explanation, and both have the same cause: the major-symmetric tensors of a class do not form a subalgebra.
TI{4,5} ⊡ TI{4,5} → TI{4,6}. The Walpole product is a matrix product, and unless the blocks commute, so the product of two major-symmetric TI tensors generally has .ORTHO ⊡ ORTHO → ORTHO{12}. In the material frame the Kelvin-Mandel matrix is a normal block plus three shear moduli, and the product of two symmetric blocks is not symmetric unless they commute: twelve independent constants where the major-symmetric form stores nine. The product is one matrix product and three scalar ones, and inversion inverts the block.Tensors.majorsymmetricreturns the nine-constant symmetric part.
CUBIC ⊡ CUBIC → CUBIC is the one entry that does not widen, and it is worth knowing why: each irreducible representation of the octahedral group appears with multiplicity one, so the three projectors are orthogonal, any two cubic tensors about the same cube commute, and the product is cubic and still major-symmetric. Cubic symmetry is the only class here that is a genuine commutative subalgebra. See Cubic symmetry.
CUBIC ⊡ TI is generic for the opposite reason: the two classes are incomparable, their intersection being the isotropic class.
At order 2, and between orders, the same rules hold on a shared reference:
| Operation | Result |
|---|---|
ORTHO{2} ⋅ ORTHO{2}, with an ISO{2} or a coaxial TI{2,2} | ORTHO{2} |
ORTHO{4} ⊡ ORTHO{2}, ORTHO{2} ⊡ ORTHO{4}, the order-4 operand iso_projectors, CUBIC or a coaxial TI{4,5}/TI{4,6} | ORTHO{2}: the normal block acting on |
ORTHO{2} ⊗ ORTHO{2} | ORTHO{4,12}; ORTHO{4,9} for |
as TI{4} ⊡ TI{2,2} → TI{2,2} and TI{2,2} ⊗ TI{2,2} → TI{4,6} (TI{4,5} for a self product).
Inversion, by contrast, always stays in the class:
typeof(inv(iso)), typeof(inv(ti)), typeof(inv(ort)), typeof(inv(cub))(TensISO{4, 3, Float64, 2}, TensTI{4, Float64, 5}, TensOrtho{4, Float64, 9, CanonicalBasis{3, Float64}}, TensCubic{Float64, CanonicalBasis{3, Float64}})Blocks: a class as a sum of small matrices
Inside one class, in one frame, the double contraction of two tensors and the inverse of a tensor are computed on a few small matrices, the blocks of the class, each one independently of the others:
| Class | order 4 | order 2 |
|---|---|---|
| ISO | ||
| CUBIC | — | |
| TI | ||
| ORTHO | the normal |
The product of two tensors has, for blocks, the products of their blocks; the inverse has the inverses. symmetry_blocks reads the blocks of a tensor, from_symmetry_blocks builds the tensor back, taking the axis or the frame from a tensor of the class:
bt = symmetry_blocks(ti)([12.0 3.5; 3.5 13.0], [7.0;;], [4.0;;])from_symmetry_blocks(ti, map(inv, bt)) ≈ inv(ti)trueThis is what a computation that works on a whole family of tensors of one class needs. A relaxation operator of aging viscoelasticity, for instance, has a tensor coefficient for each pair of instants: on a grid of
The orthotropic shear blocks are
The same blocks can be read on a matrix rather than on a tensor: a symmetry_basis is the orthonormal basis, adapted to the class and to its axis or frame, in which such a matrix is block diagonal, and symmetry_layout says where each block sits (the isotropic shear modulus five times, the transverse shear symmetry_blocks(A, M) reads the blocks of M in the class and frame of A, and symmetry_matrix builds the matrix back; a matrix that belongs to the class is rebuilt exactly, which is how one checks that it does:
M = mandel(generic_tens(ti))
symmetry_blocks(ti, M)[1], symmetry_matrix(ti, symmetry_blocks(ti, M)) ≈ M([12.0 3.499999999999999; 3.499999999999999 12.999999999999998], true)Exact promotions
Moving up the lattice is exact — an isotropic tensor really is transversely isotropic about every axis:
typeof(iso_to_ti(iso, [0.0, 0.0, 1.0]))TensTI{4, Float64, 5}typeof(iso_to_ortho(iso, ℬ)), typeof(walpole_to_ortho(ti, ℬ, 3))(TensOrtho{4, Float64, 9, CanonicalBasis{3, Float64}}, TensOrtho{4, Float64, 9, CanonicalBasis{3, Float64}})typeof(iso_to_cubic(iso, ℬ)), typeof(cubic_to_ortho(cub))(TensCubic{Float64, CanonicalBasis{3, Float64}}, TensOrtho{4, Float64, 9, CanonicalBasis{3, Float64}})Moving down is approximation, and is Projection.
Same axis, same frame
Addition and subtraction require the two operands to share their orientation — adding two orthotropic tensors expressed in different material frames throws:
julia> ort + TensOrtho(6., 5., 7., 2., 1., 3., 1.2, 2.2, 0.8, Basis(0.3, 0.7, 0.2))
ERROR: ArgumentError: TensOrtho operation requires the same material frameThe assertion is deliberate: silently promoting to a generic tensor would hide a modeling error. Convert explicitly if that is what you mean.
TensCubic is the one class where the test is looser than equality, and legitimately so: the three projectors are invariant under the whole octahedral group, so two frames related by a signed permutation of the axes describe the same tensor with the same coefficients and are accepted. Permuting the axes of an orthotropic tensor permutes
When to use which
| Situation | Type |
|---|---|
| the physics guarantees isotropy | TensISO |
| a stiffness or compliance with an axis of symmetry | TensTI{4,T,5} |
| an object with an axis but no major symmetry (a concentration tensor) | TensTI{4,T,6} or {4,T,8} |
| a cubic crystal, a cubic array, a cube-symmetric shape | TensCubic |
| nine constants and a material frame | TensOrtho{4,T,9} |
| an orthotropic object without major symmetry (a product, a concentration tensor) | TensOrtho{4,T,12} |
| an order-2 tensor diagonal in a material frame | TensOrtho{2,T,3} |
| anything else | a plain Tens |
The measured cost of each is in Performance of the structured types. A tensor of any of these classes, with random coefficients and orientation, is drawn by rand (Random tensors).