Typed Tensors (Tensor2 / Tensor4)
simcoon.Tensor2 and simcoon.Tensor4 are unified typed wrappers for 2nd-
and 4th-order tensors. A single object transparently represents either one tensor
or a batch (scipy Rotation style): shape (6,) / (6,6) is a single
tensor, (N,6) / (N,6,6) a batch. Tensor4 stores its data internally
in the Kelvin-Mandel convention, so the double contraction, inverse and
composition of 4th-order tensors become ordinary 6x6 linear algebra; the
engineering Voigt form is recovered on demand via .mat / .voigt.
Objects are built through typed factories, e.g. Tensor2.stress(v),
Tensor2.strain(v), Tensor4.stiffness(m), Tensor4.compliance(m),
Tensor4.from_voigt(v, type_str).
Batch operations
Batch contract, rotate, push_forward, pull_back and inverse
dispatch to vectorised kernels over the (N, ...) data.
Tip
Shared-tangent contraction is ~20x faster — pass a single Tensor4.
When the same 4th-order tensor is contracted against many 2nd-order tensors
(a shared elastic tangent applied at every integration point), pass a
single Tensor4 and contract it with a batch Tensor2:
L = Tensor4.stiffness(L6x6) # single (6,6)
eps = Tensor2.strain(eps_Nx6) # batch (N,6)
sig = L.contract(eps) # one BLAS GEMM -> ~20x
This collapses to a single L @ X matrix-matrix product (BLAS dgemm):
measured ~3.7 ns/point vs ~74 ns/point for the per-point path (N = 1e5).
The C++ batch_contract implements the same fast path when the tensor4
cube has a single slice (N4 == 1).
Anti-pattern: materialising the shared tangent as a tiled (N,6,6)
batch of identical slices — that forces N independent 6x6 matrix-vector
products and the GEMM speed-up is lost. Tile the tangent only when it
genuinely differs per point (a distinct consistent tangent, e.g. plasticity);
there a GEMM collapse is not possible and per-point evaluation is correct.
Note
A dedicated symmetric symtensor2 (Kelvin-Mandel 6-vector storage)
was benchmarked as a way to speed the general contraction path and
rejected: it yields only ~1.1x on a single contraction and ~1.07x on
realistic distinct-tangent batch work, and Armadillo fixed-size containers
negate the 6-vs-9-double storage saving. The real ~20x lever is the
shared-tangent GEMM path above, which needs no new type; the C++ tensor2
stays the general 3x3 type (the only one able to hold a non-symmetric tensor
such as F, L, R). The Python Tensor2 stores the 6 Voigt
components for the symmetric types ("stress", "strain",
"symmetric") and the 9 components of any 3x3 for the type "none" (no
Voigt convention); a non-symmetric matrix given to a
symmetric type is refused rather than silently symmetrised.
Basis: the reference system of the components
Objectivity implies that the physical laws are independent of the choice of reference frame.
The components of a tensor only mean something together with the basis they are
written in. By default that basis is the fixed orthonormal laboratory basis
\(\mathbf{e}_i\): t.basis is None and nothing is stored. A
simcoon.Basis makes another choice explicit. It holds the basis vectors
\(\mathbf{g}_i\) through the matrix \(\mathbf{A}\) whose columns are
their laboratory frame components, and comes in two kinds:
orthonormal, built from a rotation, \(\mathbf{g}_i = \mathbf{R}\,\mathbf{e}_i\) (a material frame, a corotational frame). Its metric is the identity, so every formula is the one of the lab.
natural, any three independent vectors, typically the convected basis \(\mathbf{g}_i = \mathbf{F}\,\mathbf{G}_i\) (
Basis.from_F). Its metric \(g_{ij} = \mathbf{g}_i \cdot \mathbf{g}_j\), i.e. \(\mathbf{g} = \mathbf{A}^T \mathbf{A}\), enters the invariants. ‘Natural’ definition come from the convected basis of a material point.
The type tag gives the variance of the components: a stress is contravariant, \(\boldsymbol{\sigma} = \sigma^{ij}\,\mathbf{g}_i \otimes \mathbf{g}_j\), a strain covariant, \(\boldsymbol{\varepsilon} = \varepsilon_{ij}\,\mathbf{g}^i \otimes \mathbf{g}^j\); a stiffness is contravariant on its four indices, a compliance covariant, the concentration tensors mixed. The lab components follow:
Type |
Components |
Transport by \(\mathbf{F}\) ( |
|---|---|---|
|
\(\sigma^{ij}\) (contravariant) |
\(\mathbf{F}\,\boldsymbol{\sigma}\,\mathbf{F}^T\), weight \(1/J\) with |
|
\(\varepsilon_{ij}\) (covariant) |
\(\mathbf{F}^{-T}\,\boldsymbol{\varepsilon}\,\mathbf{F}^{-1}\) |
|
lab or orthonormal-frame components only |
none: no variance, hence no natural basis and no transport |
|
\(C^{ijkl}\) |
\(\mathbf{F}\) on the four indices, weight \(1/J\) |
|
\(M_{ijkl}\) |
\(\mathbf{F}^{-T}\) on the four indices, weight \(J\) |
|
mixed, \(A_{ij}{}^{kl}\) / \(B^{ij}{}_{kl}\) |
pair-wise: \(\mathbf{F}^{-T}\) on the covariant pair, \(\mathbf{F}\) on the contravariant one |
The variance is a tag of the components, t.variance, carried next to the type
and the basis: "contravariant" or "covariant" for a Tensor2, a pair
(output, input) for a Tensor4. The table gives its default; it is read,
not the type, by the transports, by to_basis and by the metric invariants.
Two things the default is not:
It is not the only possible representation. Variance is a choice made for the components, not a property of the tensor: with the metric any tensor can be given contravariant or covariant components (
detandeigvalsuse the mixed ones \(T^i{}_j = T^{ik} g_{kj}\) internally). The default is the one under which the conjugate pairs \(\mathbf{S} : \mathbf{E}\) and \(\boldsymbol{\tau} : \mathbf{e}\) contract without a metric and the convected components stay constant under transport.t.to_variance(v)gives the same tensor with components of the other variance: a retag in the lab or an orthonormal basis (the numbers do not change), a contraction with the metric in a natural basis, \(\sigma_{ij} = g_{ik}\,\sigma^{kl}\,g_{lj}\) (or its inverse), pair by pair for aTensor4. The Voigt shear factors stay those of the type. Mixed components of a single pair are not representable in 6 components (they are not symmetric), hence a tag per pair. The variance is part of what a transport means: a covariantly tagged stiffness pushes forward with \(\mathbf{F}^{-T}\) on its four indices, which is another tensor than the push-forward of the contravariant one. A"symmetric"Tensor2has no default variance:to_variancedeclares it before any transport or natural basis; type"none"has none.It is not a statement about every strain measure. The covariant transport is that of the Green-Lagrange / Almansi pair. A logarithmic strain is a tensor function of a stretch, not the transport of anything:
push_forwardon a strain-typed \(\ln \mathbf{V}\) is accepted by the type tag and has no meaning.
Two-point tensors have no basis
The deformation gradient \(\mathbf{F} = F^i{}_J\,\mathbf{g}_i \otimes
\mathbf{G}^J\) has one index in each configuration, so there is no single basis
to attach it to; in the convected pair \((\mathbf{G}_i, \mathbf{g}_i =
\mathbf{F}\,\mathbf{G}_i)\) its mixed components are \(\delta^i_J\): it is
the relation between the two bases. This is why F is always a plain array of
lab components in the API, consumed by Basis.from_F and by the transports,
never wrapped. The same holds for the rotation \(\mathbf{R}\) of the polar
decomposition, the rotation increment \(\Delta\mathbf{R}\) and the first
Piola-Kirchhoff stress. They can be held in a Tensor2 of type "none" (no
Voigt convention: 9 components, lab-lab). Such a tensor has no variance, hence no
transport and no natural basis (re-expressing its components would need the
variance of each leg); it does accept an orthonormal Basis, both legs being
read in that frame, since \(\mathbf{Q}^T \mathbf{X}\,\mathbf{Q}\) is the same
for every variance. It offers the trace, determinant, norm and eigenvalues, but
no Voigt vector.
Three operations, three meanings:
Call |
Meaning |
|---|---|
|
The same tensor, its components re-expressed in |
|
The same components in another basis: a transport, hence another tensor. No arithmetic, no copy. |
|
The same tensor in the frame turned by |
Tensors written in different bases cannot be combined: +, -, @,
% and the dyadic products raise ValueError("Mixed basis"). A name
given to a basis is a label for repr and error messages; two bases are the
same when their vectors are.
import numpy as np
import simcoon as sim
material = sim.Basis(rotation=sim.Rotation.from_euler("z", 30, degrees=True),
name="material")
sigma = sim.Tensor2.stress(np.array([100., 0., 0., 0., 0., 0.]))
sigma_m = sigma.to_basis(material) # material-frame components
sigma + sigma_m # ValueError: Mixed basis
sigma_m.to_basis(None) # lab components again
One basis can serve a whole batch (it is stored once and shared by reference by every tensor derived from it), or a batch of N bases can give one per tensor.
A basis also knows its reciprocal vectors \(\mathbf{g}^i\) (reciprocal,
the columns of \(\mathbf{A}^{-T}\), on which covariant components live), its
right stretch \(\mathbf{U} = \sqrt{\mathbf{g}}\) (stretch) and the
orthonormal basis closest to it, the rotation of the polar decomposition
\(\mathbf{A} = \mathbf{R}\,\mathbf{U}\) (polar). For a convected basis
these are the right stretch and the rotation of \(\mathbf{F}\); polar is
the rotated orthonormal frame a convected tensor is often read in.
Push-forward and pull-back carry the basis along
A push-forward is a transport, not a change of basis: the result is another tensor, living in the current configuration. But in the convected basis its components are those the original tensor had in the reference basis:
with \(\boldsymbol{\tau}\) the Kirchhoff stress, \(\mathbf{S}\) the second Piola-Kirchhoff stress, \(\mathbf{e}\) the Almansi and \(\mathbf{E}\) the Green-Lagrange strain. Pushing forward carries the basis \(\mathbf{G}_i\) to \(\mathbf{g}_i = \mathbf{F}\,\mathbf{G}_i\) and keeps the components; the familiar \(\mathbf{F}\,\mathbf{S}\,\mathbf{F}^T\) is that statement followed by “write the result in the lab”. Accordingly:
on a lab tensor,
push_forward(F)returns lab components, as before;on a tensor that has its own basis, it keeps the components and returns the convected basis \(\mathbf{F}\,\mathbf{A}\) (
pull_back: \(\mathbf{F}^{-1}\mathbf{A}\)). Only the weight \(1/J\) ofmetric=True(Kirchhoff to Cauchy, a density) touches the numbers.
F = np.array([[1., 0.5, 0.], [0., 1., 0.], [0., 0., 1.]]) # simple shear
S = sim.Tensor2.stress(np.array([0., 80., 0., 0., 0., 0.])) # PK2 components
tau = S.with_basis(sim.Basis.from_F(F)) # Kirchhoff stress, same numbers
tau.to_basis(None) # == S.push_forward(F, metric=False)
tau.trace() # 100.0 = g_22 * 80, not 80
The same holds for an active rotation: a lab tensor is rotated in the lab, a tensor with its own basis keeps its components while its basis vectors turn (\(\mathbf{A} \to \mathbf{Q}\,\mathbf{A}\)), which leaves the metric and every invariant unchanged.
Invariants with the metric
Contractions between dual variances need no metric and act on the components in any basis: \(\boldsymbol{\sigma} : \boldsymbol{\varepsilon} = \sigma^{ij} \varepsilon_{ij}\), \(\mathbb{L} : \boldsymbol{\varepsilon}\), the inverse of a stiffness. Contracting two indices of the same variance does. For contravariant components (covariant ones exchange \(\mathbf{g}\) and \(\mathbf{g}^{-1}\)):
trace, dev, mises, norm, det, eigvals and % use these
forms in a natural basis and the usual ones otherwise. The identity tensor is the
metric itself: Tensor2.identity("stress", basis=b) has components
\(g^{ij}\), Tensor2.identity("strain", basis=b) has \(g_{ij}\). For
fourth-order tensors, Tensor4.identity, volumetric and deviatoric
take the same basis argument; the stiffness-type identity becomes
\(\tfrac12 (g^{ik} g^{jl} + g^{il} g^{jk})\) and the volumetric projector
\(\tfrac13\,g^{ij} g^{kl}\).
Note
The Kelvin-Mandel form is an isometry only in an orthonormal basis. In a
natural basis .mandel and .voigt still return the stored components,
but eye(6) is no longer the stiffness-type identity and the plain
Frobenius norm of the 6x6 is not the norm of the tensor.
Cost: with basis=None nothing changes. A basis costs one reference per
tensor object, not per point; with_basis and the transports of a tensor with
its own basis are free; to_basis costs one transport (the same kernel as
push_forward); the metric of a natural basis is computed on first use and
cached, and its invariants cost about one 3x3 product per point more than in the
lab.
API reference
- class simcoon.Basis(rotation=None, vectors=None, name=None)
The basis the components of a Tensor2 / Tensor4 are written in.
A tensor whose
basisisNoneis expressed in the fixed orthonormal lab basis \(\mathbf{e}_i\) (the default, at no cost). ABasisholds the basis vectors \(\mathbf{g}_i\) through the matrix \(\mathbf{A}\) whose columns are their lab components. Two kinds:orthonormal – built from a rotation, \(\mathbf{g}_i = \mathbf{R}\,\mathbf{e}_i\). The metric is the identity: every formula of the tensor classes holds as in the lab.
natural – any three independent vectors, typically the convected basis \(\mathbf{g}_i = \mathbf{F}\,\mathbf{G}_i\) (from_F). The metric \(g_{ij} = \mathbf{g}_i \cdot \mathbf{g}_j\), i.e. \(\mathbf{g} = \mathbf{A}^T \mathbf{A}\), enters the trace, the deviator, the norms and the invariants.
The variance of the components follows the tensor type: stress is contravariant (\(\boldsymbol{\sigma} = \sigma^{ij}\,\mathbf{g}_i \otimes \mathbf{g}_j\)), strain covariant (\(\boldsymbol{\varepsilon} = \varepsilon_{ij}\,\mathbf{g}^i \otimes \mathbf{g}^j\)), stiffness and compliance likewise on their four indices, concentration tensors mixed. Lab components are \(\mathbf{A}\,\hat{\boldsymbol{\sigma}}\, \mathbf{A}^T\) and \(\mathbf{A}^{-T}\,\hat{\boldsymbol{\varepsilon}}\, \mathbf{A}^{-1}\).
One basis is either single (shared by every tensor of a batch, stored once) or a batch of N bases, one per tensor. A basis is immutable; tensors derived from one another share it by reference.
The variance is a convention of the components, not a property of the tensor: it is a tag (
t.variance) defaulted from the type, andt.to_variance(v)gives the components of the other variance (with the metric in a natural basis). Two-point tensors such as \(\mathbf{F}\) have one index in each configuration and no single basis:Fis always a plain array of lab components, consumed by from_F and by the transports.- Parameters:
rotation (simcoon.Rotation or scipy.spatial.transform.Rotation, optional) – Orthonormal basis \(\mathbf{g}_i = \mathbf{R}\,\mathbf{e}_i\) (single or batch).
vectors (array_like, optional) – Natural basis:
(3,3)matrix (or(N,3,3)batch) whose columns are the lab components of the basis vectors. The array is kept by reference, not copied: do not modify it afterwards.name (str, optional) – A label shown in
reprand in error messages. It is never used to compare two bases.
Examples
import numpy as np import simcoon as sim material = sim.Basis(rotation=sim.Rotation.from_euler('z', 30, degrees=True), name="material") sigma = sim.Tensor2.stress(np.array([100., 0., 0., 0., 0., 0.])) sigma_m = sigma.to_basis(material) # same tensor, material components sigma_m.to_basis(None) # back to lab components F = np.array([[1., 0.5, 0.], [0., 1., 0.], [0., 0., 1.]]) S = sim.Tensor2.stress(np.array([0., 80., 0., 0., 0., 0.])) # PK2 tau = S.with_basis(sim.Basis.from_F(F)) # Kirchhoff: same components, convected basis tau.trace() # uses the metric
- property det
float or
(N,).- Type:
Volume of the basis, \(\det \mathbf{A} = \sqrt{\det \mathbf{g}}\)
- equals(other, tol=1e-12)
True if
otheris the same basis withintol(the name is ignored).
- classmethod from_F(F, name=None)
Convected basis \(\mathbf{g}_i = \mathbf{F}\,\mathbf{e}_i\) of a deformation gradient.
- Parameters:
F (array_like) –
(3,3)deformation gradient or(N,3,3)batch, kept by reference. Its metric is the right Cauchy-Green tensor \(\mathbf{C} = \mathbf{F}^T \mathbf{F}\).name (str, optional) – Label of the basis.
- Returns:
Natural basis.
- Return type:
- property inverse_metric
(3,3)or(N,3,3).- Type:
Contravariant metric \(g^{ij}\)
- property matrix
Basis matrix \(\mathbf{A}\) (columns = lab components of the basis vectors):
(3,3)or(N,3,3). Returns a copy.
- property metric
(3,3)or(N,3,3).- Type:
Covariant metric \(g_{ij} = \mathbf{g}_i \cdot \mathbf{g}_j\)
- property name
Label of the basis (
Noneif unnamed).
- property orthonormal
True for a basis built from a rotation (identity metric).
- property polar
math:mathbf{R} = mathbf{A},mathbf{U}^{-1} of the polar decomposition \(\mathbf{A} = \mathbf{R}\, \mathbf{U}\), as a Basis (
selfwhen already orthonormal). Forfrom_Fit is the frame turned by the rotation of \(\mathbf{F}\).- Type:
The orthonormal basis closest to this one
- Type:
the rotation
- property reciprocal
mathbf{g}^i cdot mathbf{g}_j = delta^i_j, as the columns of \(\mathbf{A}^{-T}\):
(3,3)or(N,3,3). Covariant components live on them. Returns a copy.- Type:
Reciprocal (dual) basis vectors \(\mathbf{g}^i\),
- Type:
math
- property rotation
The rotation of an orthonormal basis,
Nonefor a natural basis.
- property single
True for one basis, False for a batch of N bases.
- property stretch
Right stretch of the basis, \(\mathbf{U} = \sqrt{\mathbf{g}}\) (the identity for an orthonormal basis):
(3,3)or(N,3,3). Forfrom_Fit is the right stretch of \(\mathbf{F}\). Returns a copy.
- class simcoon.Tensor2(data)
A 2nd-order tensor with a type tag driving the Voigt convention and rotation dispatch.
A single object transparently represents either one tensor or a batch (scipy
Rotationstyle): the stored numpy array is(6,)for a single tensor and(N, 6)for a batch. The type is a string:"stress","strain"or"symmetric"for a symmetric tensor stored as a Voigt vector (shear factors2*e_ijfor strain,s_ijotherwise), or"none"– no Voigt convention – for any 3x3 (F,R,L,PK1) stored as its 9 row-major components, with no Voigt vector, no variance and no transport; it accepts an orthonormal basis (both legs read in that frame), not a natural one. The type also selects the rotation/transport rules.Construct through the typed factories (stress, strain, from_mat, from_voigt, from_mandel), never through
Tensor2(array).Examples
import numpy as np import simcoon as sim sigma = sim.Tensor2.stress(np.array([100., 50., 75., 0., 0., 0.])) sigma.mises() # von Mises equivalent stress eps = sim.Tensor2.strain(np.random.randn(1000, 6) * 0.01) # batch len(eps), eps[0] # 1000, single Tensor2
- property basis
The Basis the components are written in;
Nonefor the lab basis.
- classmethod concatenate(batches)
Join multiple batches and/or singles (all of the same type) into one batch.
Parts sharing one basis keep it; parts in different bases give a per-tensor basis; lab and non-lab parts cannot be mixed.
- count(item)
Count occurrences of a single tensor in batch.
- det()
Determinant of the tensor (of its mixed components \(T^i{}_j\)).
In a natural basis \(\det \mathbf{T} = \det \hat{\mathbf{T}}\, \det \mathbf{g}\) for contravariant components and \(\det \hat{\mathbf{T}} / \det \mathbf{g}\) for covariant ones.
- Returns:
Scalar for a single tensor,
(N,)for a batch.- Return type:
float or numpy.ndarray
- dev()
Deviatoric part
t - tr(t)/3 I.- Returns:
Deviatoric tensor(s), same type tag.
- Return type:
- eigvals()
Eigenvalues (principal values), in ascending order.
They are those of the mixed components: in a natural basis the generalized problem \(\det(\hat{\mathbf{T}} - \lambda\, \mathbf{g}^{-1}) = 0\) (contravariant) or \(\det(\hat{\mathbf{T}} - \lambda\,\mathbf{g}) = 0\) (covariant), solved in symmetric form through a Cholesky factor of the metric.
- Returns:
(3,)for a single tensor,(N,3)for a batch.- Return type:
numpy.ndarray
- classmethod from_columns(arr, type_str)
Create a batch from a column-major array (C++ interop).
- Parameters:
arr (array_like) –
(6, N)array, one Voigt vector per column (the simcoon C++ batch convention);(9, N)for type"none".type_str (str) – One of
"stress","strain","symmetric","none".
- Returns:
Batch of N tensors.
- Return type:
- classmethod from_list(tensors)
Stack a list of single tensors (all of the same type) into a batch.
- classmethod from_mandel(v, type_str)
Create from a Kelvin-Mandel 6-vector (inverse of the mandel property).
- Parameters:
v (array_like) –
(6,)Kelvin-Mandel vector (sqrt(2)factor on shear terms, identical for stress and strain) or(N,6)batch.type_str (str) – One of
"stress","strain","symmetric"(type"none"has no Mandel vector).
- Returns:
Tensor(s) of the requested type.
- Return type:
- classmethod from_mat(m, type_str)
Create from a 3x3 matrix representation with an explicit type.
- Parameters:
m (array_like) –
(3,3)matrix or(N,3,3)batch of matrices.type_str (str) – One of
"stress","strain","symmetric"(symmetric, 6 components) or"none"(any 3x3, 9 components).
- Returns:
Tensor(s) of the requested type.
- Return type:
- classmethod from_tensor(t, n)
Broadcast a single tensor to a batch of size
n(copies the data).
- classmethod from_voigt(v, type_str)
Create from a Voigt 6-vector with an explicit type.
- Parameters:
v (array_like) –
(6,)Voigt vector or(N,6)batch. Shear components follow the type convention (2*e_ijfor strain,s_ijfor stress).type_str (str) – One of
"stress","strain","symmetric"(type"none"has no Voigt vector).
- Returns:
Tensor(s) of the requested type.
- Return type:
- classmethod identity(type_str='stress', basis=None)
Create the identity tensor of the given type.
In the lab or an orthonormal basis its Voigt components are
[1,1,1,0,0,0]. In a natural basis the identity is the metric: \(g^{ij}\) for a stress-typed tensor, \(g_{ij}\) for a strain-typed one.- Parameters:
type_str (str, optional) – Tensor type (default
"stress").basis (Basis or None, optional) – Basis of the result (default: lab). A batch basis gives a batch.
- is_symmetric(tol=1e-12)
Check symmetry of the 3x3 matrix representation (single only).
- Parameters:
tol (float, optional) – Absolute tolerance on the off-diagonal differences (default 1e-12).
- Return type:
bool
- property mandel
Kelvin-Mandel vector (sqrt2 on shear, identical for stress/strain): (6,) for single, (N,6) for batch. Returns a copy.
- property mat
(3,3) for single, (N,3,3) for batch.
- Type:
Matrix
- mises()
Von Mises equivalent (type-aware).
Uses the stress definition
sqrt(3/2 s_dev:s_dev)for stress/symmetric and the strain definitionsqrt(2/3 e_dev:e_dev)for strain.- Returns:
Scalar for a single tensor,
(N,)for a batch.- Return type:
float or numpy.ndarray
- norm()
Frobenius norm
sqrt(t_ij t_ij)(type-aware shear factors).- Returns:
Scalar for a single tensor,
(N,)for a batch.- Return type:
float or numpy.ndarray
- pull_back(F, metric=True)
Pull-back (current to reference configuration) via the deformation gradient.
Inverse of push_forward; same type-dependent transport and
metricsemantics.- Parameters:
F (array_like) –
(3,3)deformation gradient, or(N,3,3)batch.metric (bool, optional) –
True(default) includes theJ = det(F)factor.
- Returns:
Transported tensor(s), same type tag.
- Return type:
- push_forward(F, metric=True)
Push-forward (reference to current configuration) via the deformation gradient.
Type-dependent transport: stress is fully contravariant (
F s F^T, Piola with1/J), strain fully covariant (F^-T e F^-1).A push-forward is a transport that carries the basis along: the transported tensor has, in the convected basis \(\mathbf{g}_i = \mathbf{F}\,\mathbf{G}_i\), the components the original had in \(\mathbf{G}_i\). A lab tensor is returned in lab components (the transport is carried out); a tensor with its own basis keeps its components and gets the convected basis \(\mathbf{F}\,\mathbf{A}\) (only the
1/Jweight ofmetric=Truetouches the numbers).The variance is read from the type tag: the covariant transport is the one of the Green-Lagrange / Almansi pair (and the contravariant one of the second Piola-Kirchhoff / Kirchhoff pair). A logarithmic strain is not the transport of anything: on a strain-typed \(\ln \mathbf{V}\) the result has no meaning.
Fis a two-point tensor and is always given as lab components.- Parameters:
F (array_like) –
(3,3)deformation gradient, or(N,3,3)batch (a single F is broadcast over a tensor batch).metric (bool, optional) –
True(default) includes theJ = det(F)factor (proper Piola transformation);Falseis pure transport.
- Returns:
Transported tensor(s), same type tag.
- Return type:
- rotate(R, active=True)
Rotate the tensor(s) (active) or the frame they are written in (passive).
- Parameters:
R (simcoon.Rotation or scipy.spatial.transform.Rotation) – Rotation(s) to apply. On a batch, a single rotation is broadcast to all N tensors; a batch of N rotations is applied slice-wise.
active (bool, optional) –
True(default) rotates the tensor: a new tensor \(\mathbf{Q}\,\mathbf{X}\,\mathbf{Q}^T\),Rbeing a lab rotation. A lab tensor stays written in the lab; a tensor with its own basis keeps its components and has its basis vectors turned (\(\mathbf{A} \to \mathbf{Q}\,\mathbf{A}\)).Falserotates the frame: the same tensor, with components \(\mathbf{Q}^T \hat{\mathbf{X}}\,\mathbf{Q}\) in the basis turned byR(\(\mathbf{A} \to \mathbf{A}\,\mathbf{Q}\),Rread in the current basis). The result carries that basis.
- Returns:
Same type tag; basis as described above.
- Return type:
Examples
import numpy as np import simcoon as sim from scipy.spatial.transform import Rotation sigma = sim.Tensor2.stress(np.array([100., 0., 0., 0., 0., 0.])) R = Rotation.from_euler('z', 45, degrees=True) sigma_rot = sigma.rotate(R) # another tensor, lab basis sigma_45 = sigma.rotate(R, active=False) # same tensor, basis turned by R sigma_45.to_basis(None) == sigma # True up to round-off
- property single
True if this is a single tensor, False if batch.
- classmethod strain(data)
Create strain tensor(s).
- Parameters:
data (array_like) –
(6,)Voigt vector (with2*e_ijshear terms),(3,3)matrix,(N,6)Voigt batch, or(N,3,3)matrix batch.- Returns:
Strain-typed tensor (single or batch, matching the input shape).
- Return type:
- classmethod stress(data)
Create stress tensor(s).
- Parameters:
data (array_like) –
(6,)Voigt vector,(3,3)matrix,(N,6)Voigt batch, or(N,3,3)matrix batch.- Returns:
Stress-typed tensor (single or batch, matching the input shape).
- Return type:
- to_basis(basis=None)
The same tensor, its components re-expressed in another basis.
With \(\mathbf{M} = \mathbf{A}_{new}^{-1} \mathbf{A}_{old}\), contravariant components become \(\mathbf{M}\,\hat{\mathbf{T}}\, \mathbf{M}^T\) and covariant ones \(\mathbf{M}^{-T}\, \hat{\mathbf{T}}\,\mathbf{M}^{-1}\), on each pair of indices.
- to_variance(variance)
The same tensor with components of another variance (index raising / lowering).
In the lab or an orthonormal basis the numbers do not change: the tag does. In a natural basis one pair of indices is lowered with the metric, \(T_{ij} = g_{ik}\,T^{kl}\,g_{lj}\), or raised with its inverse. The Voigt shear factors stay those of the type. On a
"symmetric"Tensor2 whose variance is not set yet, this declares it.
- trace()
Trace
t_kk.- Returns:
Scalar for a single tensor,
(N,)for a batch.- Return type:
float or numpy.ndarray
- property type
Type string.
- property variance
"contravariant"or"covariant"for a Tensor2 (Nonewhen the type has none:"symmetric"until declared,"none"), a pair(output, input)for a Tensor4. Defaults to the one of the type.- Type:
Variance of the components
- property voigt
(6,) for single, (N,6) for batch. Returns a copy. Type
"none"(any 3x3) has no Voigt vector: use mat.- Type:
Voigt vector
- property voigt_T
(6, N) transposed Voigt array (batch only, for C++ interop).
- property vtype
Alias for type (backward compatibility).
- with_basis(basis)
The tensor with the same components and another basis (a transport).
The numbers are kept and the basis vectors replaced: the result is a different tensor. Giving the convected basis of
Fto second Piola-Kirchhoff components yields the Kirchhoff stress, \(\tau^{ij} = S^{IJ}\); likewise \(e_{ij} = E_{IJ}\) for the Green-Lagrange / Almansi pair. No arithmetic, no copy.
- classmethod zeros(type_str='stress')
Create a single zero tensor of the given type (default
"stress").
- class simcoon.Tensor4(data)
A 4th-order tensor with a type tag driving rotation dispatch and Voigt factors.
A single object transparently represents either one tensor or a batch: the stored numpy array is
(6,6)(engineering Voigt) for a single tensor and(N,6,6)for a batch. The type is a string:"stiffness","compliance","strain_concentration","stress_concentration", or"generic"; it selects the Kelvin-Mandel congruence factors and the rotation/transport rules, and is propagated through operations (e.g.stiffness.inverse()is a compliance).The C++ backend works internally in the Kelvin-Mandel convention, so contraction, inverse and composition are plain 6x6 linear algebra; the engineering Voigt form is what mat / voigt expose.
Construct through the typed factories (stiffness, compliance, strain_concentration, stress_concentration, from_mat, from_mandel), never through
Tensor4(array).Examples
import numpy as np import simcoon as sim L = sim.Tensor4.stiffness(sim.L_iso([70000, 0.3], 'Enu')) eps = sim.Tensor2.strain(np.array([0.01, -0.003, -0.003, 0.005, 0., 0.])) sigma = L @ eps # contraction -> stress Tensor2 M = L.inverse() # compliance Tensor4
Note
The underlying C++
tensor4class uses a lazy mutable Fastor cache that is not thread-safe. However, the PythonTensor4class creates fresh C++ objects for each operation (via_to_cpp()), so Python-levelTensor4instances are safe to use from multiple threads (subject to the GIL).- property basis
The Basis the components are written in;
Nonefor the lab basis.
- classmethod compliance(data)
Create compliance tensor(s) from an engineering Voigt 6x6.
- Parameters:
data (array_like) –
(6,6)matrix (e.g. fromsim.M_iso) or(N,6,6)batch.- Return type:
- classmethod concatenate(batches)
Join multiple batches and/or singles (all of the same type) into one batch.
Parts sharing one basis keep it; parts in different bases give a per-tensor basis; lab and non-lab parts cannot be mixed.
- contract(t)
Double contraction with a Tensor2 (also available as
L @ t).The output type follows the tensor4 type: stiffness and stress_concentration produce a stress, compliance and strain_concentration produce a strain.
- count(item)
Count occurrences of a single tensor in batch.
- classmethod deviatoric(type_str='stiffness', basis=None)
Create the deviatoric projector
K = I - Jof the given type (inbasis, default lab).
- classmethod from_list(tensors)
Stack a list of single tensors (all of the same type) into a batch.
- classmethod from_mandel(m, type_str)
Create from a Kelvin-Mandel 6x6 (inverse of the mandel property).
- Parameters:
m (array_like) –
(6,6)Kelvin-Mandel matrix (per-typesqrt(2)congruence factors) or(N,6,6)batch.type_str (str) – One of
"stiffness","compliance","strain_concentration","stress_concentration","generic".
- Return type:
- classmethod from_mat(m, type_str)
Create from an engineering Voigt 6x6 with an explicit type.
- Parameters:
m (array_like) –
(6,6)matrix or(N,6,6)batch.type_str (str) – One of
"stiffness","compliance","strain_concentration","stress_concentration","generic".
- Return type:
- classmethod from_tensor(t, n)
Broadcast a single tensor to a batch of size
n(copies the data).
- classmethod from_voigt(v, type_str)
Alias for from_mat (explicit counterpart of from_mandel).
- classmethod identity(type_str='stiffness', basis=None)
Create the identity tensor of the given type (default
"stiffness").For every type the identity contracts to the unchanged field (
I : t == t); in the lab or an orthonormal basis its Kelvin-Mandel form iseye(6). In a natural basis (basis=) the stiffness-type identity is \(\tfrac12 (g^{ik} g^{jl} + g^{il} g^{jk})\) and the compliance-type one the same with \(g_{ij}\); only the concentration (mixed) types keepeye(6).
- inverse()
Invert the tensor (plain 6x6 inverse in Kelvin-Mandel).
The type follows the algebra: stiffness ↔ compliance, concentration types keep their type, generic stays generic.
- Returns:
Inverse tensor(s) with the inferred type.
- Return type:
- property mandel
Kelvin-Mandel 6x6 (per-type sqrt2 congruence; identity = eye(6) for every type): (6,6) single, (N,6,6) batch. Returns a copy.
- property mat
(6,6) single, (N,6,6) batch. Returns a copy.
- Type:
6x6 matrix
- pull_back(F, metric=True)
Pull-back (current to reference configuration) via the deformation gradient.
Inverse of push_forward; same type-dependent transport and
metricsemantics.- Parameters:
F (array_like) –
(3,3)deformation gradient, or(N,3,3)batch.metric (bool, optional) –
True(default) includes theJ = det(F)factor.
- Returns:
Transported tensor(s), same type tag.
- Return type:
- push_forward(F, metric=True)
Push-forward (reference to current configuration) via the deformation gradient.
Type-dependent transport: stiffness is fully contravariant (
F⊗F : L : F^T⊗F^Twith1/J), compliance fully covariant. For a lab tensor, concentration types raise (mixed variance: no transport implemented on lab components). A tensor with its own basis (any type) keeps its components and gets the convected basisF.A, as for Tensor2.push_forward.- Parameters:
F (array_like) –
(3,3)deformation gradient, or(N,3,3)batch (a single F is broadcast over a tensor batch).metric (bool, optional) –
True(default) includes theJ = det(F)factor;Falseis pure transport.
- Returns:
Transported tensor(s), same type tag.
- Return type:
- rotate(R, active=True)
Rotate the tensor(s) (active) or the frame they are written in (passive).
Same rules as Tensor2.rotate, with the type-dependent Voigt congruence.
- Parameters:
R (simcoon.Rotation or scipy.spatial.transform.Rotation) – Rotation(s) to apply. On a batch, a single rotation is broadcast; a batch of N rotations is applied slice-wise.
active (bool, optional) –
True(default) rotates the tensor;Falsethe frame, and the result carries the turned basis.
- Returns:
Same type tag.
- Return type:
- property single
True if this is a single tensor, False if batch.
- classmethod stiffness(data)
Create stiffness tensor(s) from an engineering Voigt 6x6.
- Parameters:
data (array_like) –
(6,6)matrix (e.g. fromsim.L_iso) or(N,6,6)batch.- Return type:
- classmethod strain_concentration(data)
Create strain concentration tensor(s) (
De = A : Dmaps, e.g.sim.A_R(F)).Uses the engineering Voigt convention: the identity concentration is
eye(6)andA.contract(strain)returns a strain.- Parameters:
data (array_like) –
(6,6)matrix or(N,6,6)batch.- Return type:
- classmethod stress_concentration(data)
Create stress concentration tensor(s) (
s_local = B : smaps).- Parameters:
data (array_like) –
(6,6)matrix or(N,6,6)batch.- Return type:
- to_basis(basis=None)
The same tensor, its components re-expressed in another basis.
With \(\mathbf{M} = \mathbf{A}_{new}^{-1} \mathbf{A}_{old}\), contravariant components become \(\mathbf{M}\,\hat{\mathbf{T}}\, \mathbf{M}^T\) and covariant ones \(\mathbf{M}^{-T}\, \hat{\mathbf{T}}\,\mathbf{M}^{-1}\), on each pair of indices.
- to_variance(variance)
The same tensor with components of another variance (index raising / lowering).
In the lab or an orthonormal basis the numbers do not change: the tag does. In a natural basis one pair of indices is lowered with the metric, \(T_{ij} = g_{ik}\,T^{kl}\,g_{lj}\), or raised with its inverse. The Voigt shear factors stay those of the type. On a
"symmetric"Tensor2 whose variance is not set yet, this declares it.
- property type
Type string.
- property variance
"contravariant"or"covariant"for a Tensor2 (Nonewhen the type has none:"symmetric"until declared,"none"), a pair(output, input)for a Tensor4. Defaults to the one of the type.- Type:
Variance of the components
- property voigt
Alias for mat.
- classmethod volumetric(type_str='stiffness', basis=None)
Create the volumetric (spherical) projector
J = 1/3 I⊗Iof the given type.In a natural basis (
basis=) the stiffness-type projector is \(\tfrac13 g^{ij} g^{kl}\).
- with_basis(basis)
The tensor with the same components and another basis (a transport).
The numbers are kept and the basis vectors replaced: the result is a different tensor. Giving the convected basis of
Fto second Piola-Kirchhoff components yields the Kirchhoff stress, \(\tau^{ij} = S^{IJ}\); likewise \(e_{ij} = E_{IJ}\) for the Green-Lagrange / Almansi pair. No arithmetic, no copy.
- classmethod zeros(type_str='stiffness')
Create a single zero tensor of the given type (default
"stiffness").
- simcoon.dyadic(a, b)
Dyadic (outer) product
C_ijkl = a_ij b_klof two single Tensor2.
- simcoon.auto_dyadic(a)
Dyadic product of a single Tensor2 with itself:
C_ijkl = a_ij a_kl-> stiffness Tensor4.
- simcoon.sym_dyadic(a, b)
Symmetric Voigt dyadic product
C = v(a) v(b)^Tof two single Tensor2 -> stiffness Tensor4.
- simcoon.auto_sym_dyadic(a)
Symmetric Voigt dyadic product of a single Tensor2 with itself -> stiffness Tensor4.
- simcoon.double_contract(a, b)
Double contraction
A_ij B_ijof two Tensor2 (single or batch).