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; Tensor2 stays the general 3x3 type (it remains the only one able to hold a non-symmetric tensor such as F, L, R).

API reference

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 Rotation style): the stored numpy array is (6,) for a single tensor and (N, 6) for a batch. The type is a string: "stress", "strain", "generic", or "none"; it selects the shear factors of the Voigt vector (2*e_ij for strain, s_ij for stress) and 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
classmethod concatenate(batches)

Join multiple batches and/or singles (all of the same type) into one batch.

count(item)

Count occurrences of a single tensor in batch.

dev()

Deviatoric part t - tr(t)/3 I.

Returns:

Deviatoric tensor(s), same type tag.

Return type:

Tensor2

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).

  • type_str (str) – One of "stress", "strain", "generic", "none".

Returns:

Batch of N tensors.

Return type:

Tensor2

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", "generic", "none".

Returns:

Tensor(s) of the requested type.

Return type:

Tensor2

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", "generic", "none".

Returns:

Tensor(s) of the requested type.

Return type:

Tensor2

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_ij for strain, s_ij for stress).

  • type_str (str) – One of "stress", "strain", "generic", "none".

Returns:

Tensor(s) of the requested type.

Return type:

Tensor2

classmethod identity(type_str='stress')

Create the single identity tensor (Voigt [1,1,1,0,0,0]) of the given type.

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/generic and the strain definition sqrt(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 metric semantics.

Parameters:
  • F (array_like) – (3,3) deformation gradient, or (N,3,3) batch.

  • metric (bool, optional) – True (default) includes the J = det(F) factor.

Returns:

Transported tensor(s), same type tag.

Return type:

Tensor2

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 with 1/J), strain fully covariant (F^-T e F^-1).

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 the J = det(F) factor (proper Piola transformation); False is pure transport.

Returns:

Transported tensor(s), same type tag.

Return type:

Tensor2

rotate(R, active=True)

Rotate the tensor(s) by a rotation.

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 (active convention Q A Q^T); False rotates the frame (passive).

Returns:

Rotated tensor(s), same type tag.

Return type:

Tensor2

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)
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 (with 2*e_ij shear 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:

Tensor2

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:

Tensor2

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 voigt

(6,) for single, (N,6) for batch. Returns a copy.

Type:

Voigt vector

property voigt_T

(6, N) transposed Voigt array (batch only, for C++ interop).

property vtype

Alias for type (backward compatibility).

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++ tensor4 class uses a lazy mutable Fastor cache that is not thread-safe. However, the Python Tensor4 class creates fresh C++ objects for each operation (via _to_cpp()), so Python-level Tensor4 instances are safe to use from multiple threads (subject to the GIL).

classmethod compliance(data)

Create compliance tensor(s) from an engineering Voigt 6x6.

Parameters:

data (array_like) – (6,6) matrix (e.g. from sim.M_iso) or (N,6,6) batch.

Return type:

Tensor4

classmethod concatenate(batches)

Join multiple batches and/or singles (all of the same type) into one batch.

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.

Parameters:

t (Tensor2) – Single or batch. Single ⊗ batch combinations broadcast; a single Tensor4 contracted with a batch Tensor2 collapses to one BLAS matrix-matrix product (the fast shared-tangent path).

Returns:

Contracted tensor(s) with the inferred type.

Return type:

Tensor2

count(item)

Count occurrences of a single tensor in batch.

classmethod deviatoric(type_str='stiffness')

Create the deviatoric projector K = I - J of the given type.

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-type sqrt(2) congruence factors) or (N,6,6) batch.

  • type_str (str) – One of "stiffness", "compliance", "strain_concentration", "stress_concentration", "generic".

Return type:

Tensor4

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:

Tensor4

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')

Create the identity tensor of the given type (default "stiffness").

For every type the identity contracts to the unchanged field (I : t == t); in Kelvin-Mandel it is eye(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:

Tensor4

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 metric semantics.

Parameters:
  • F (array_like) – (3,3) deformation gradient, or (N,3,3) batch.

  • metric (bool, optional) – True (default) includes the J = det(F) factor.

Returns:

Transported tensor(s), same type tag.

Return type:

Tensor4

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^T with 1/J), compliance fully covariant. Concentration types raise (mixed variance, no pure transport).

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 the J = det(F) factor; False is pure transport.

Returns:

Transported tensor(s), same type tag.

Return type:

Tensor4

rotate(R, active=True)

Rotate the tensor(s) by a rotation (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, False the frame.

Returns:

Rotated tensor(s), same type tag.

Return type:

Tensor4

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. from sim.L_iso) or (N,6,6) batch.

Return type:

Tensor4

classmethod strain_concentration(data)

Create strain concentration tensor(s) (De = A : D maps, e.g. sim.A_R(F)).

Uses the engineering Voigt convention: the identity concentration is eye(6) and A.contract(strain) returns a strain.

Parameters:

data (array_like) – (6,6) matrix or (N,6,6) batch.

Return type:

Tensor4

classmethod stress_concentration(data)

Create stress concentration tensor(s) (s_local = B : s maps).

Parameters:

data (array_like) – (6,6) matrix or (N,6,6) batch.

Return type:

Tensor4

property type

Type string.

property voigt

Alias for mat.

classmethod volumetric(type_str='stiffness')

Create the volumetric (spherical) projector J = 1/3 I⊗I of the given type.

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_kl of two single Tensor2.

Parameters:
Returns:

Stiffness-typed tensor.

Return type:

Tensor4

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)^T of 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_ij of two Tensor2 (single or batch).

Parameters:
  • a (Tensor2) – Single or batch; singles broadcast against batches.

  • b (Tensor2) – Single or batch; singles broadcast against batches.

Returns:

(N,) values ((1,) if both are single).

Return type:

numpy.ndarray