"""
This module contains methods to work with first-order tensors (vector) and second-order tensors.
"""
import numpy as np
[docs]def tensor_from_abaqus_notation(array):
"""
Convert a second-order tensor from the abaqus notation into the matrix form.
A tensor `T` in the abaqus notation is ordered:
- with 6 components as `(T11, T22, T33, T12, T13, T23)`
- with 4 components as `(T11, T22, T33, T12)`
**Examples**
The second order tensor can be converted to the abaqus notation
with 6 and 4 components.
By using 4 components, two components `T13` and `T23` are lost.
>>> T = np.array([
... [1.0, 0.2, 0.3],
... [0.2, 2.0, 0.4],
... [0.3, 0.4, 3.],
... ])
>>> T_abaqus_6 = abaqus_notation_from_tensor(T, 6)
>>> T_abaqus_6
array([1. , 2. , 3. , 0.2, 0.3, 0.4])
>>> T_abaqus_4 = abaqus_notation_from_tensor(T, 4)
>>> T_abaqus_4
array([1. , 2. , 3. , 0.2])
For this reason, the tensor is reconstructed correctly,
if 6 components are used, but not if 4 components are used.
>>> tensor_from_abaqus_notation(T_abaqus_6)
array([[1. , 0.2, 0.3],
[0.2, 2. , 0.4],
[0.3, 0.4, 3. ]])
>>> tensor_from_abaqus_notation(T_abaqus_4)
array([[1. , 0.2, 0. ],
[0.2, 2. , 0. ],
[0. , 0. , 3. ]])
:param array: array of shape (..., 4) or (..., 6) in abaqus notation
:return: array of shape (..., 3, 3) containing tensors in their matrix form.
"""
array = np.asarray(array)
dimensions = array.shape
dim_array = dimensions[-1]
dim = dimensions[:-1]
tensor = np.zeros(list(dim) + [3, 3], dtype=float)
if dim_array == 4:
tensor[..., 0, 0] = array[..., 0]
tensor[..., 1, 1] = array[..., 1]
tensor[..., 2, 2] = array[..., 2]
tensor[..., 0, 1] = tensor[..., 1, 0] = array[..., 3]
elif dim_array == 6:
tensor[..., 0, 0] = array[..., 0]
tensor[..., 1, 1] = array[..., 1]
tensor[..., 2, 2] = array[..., 2]
tensor[..., 0, 1] = tensor[..., 1, 0] = array[..., 3]
tensor[..., 0, 2] = tensor[..., 2, 0] = array[..., 4]
tensor[..., 1, 2] = tensor[..., 2, 1] = array[..., 5]
else:
raise NotImplementedError("dim_array=" + str(dim_array))
return tensor
[docs]def abaqus_notation_from_tensor(tensor, dim_vector):
"""
Convert tensors into the abaqus notation.
.. seealso:: :py:func:`tensor_from_abaqus_notation`
:param tensor: array of shape (..., 3, 3) containing tensors in their matrix form
:param dim_vector: int, number of components used in the abaqus notation (4 or 6).
:return: array of shape (..., 4) or (..., 6) containing tensor in the abaqus notation
"""
tensor = np.asarray(tensor)
dimensions = tensor.shape
dim = dimensions[:-2]
vector = np.zeros(list(dim) + [dim_vector], dtype=float)
if dim_vector == 4:
vector[..., 0] = tensor[..., 0, 0]
vector[..., 1] = tensor[..., 1, 1]
vector[..., 2] = tensor[..., 2, 2]
vector[..., 3] = tensor[..., 0, 1]
vector[..., 3] = tensor[..., 1, 0]
elif dim_vector == 6:
vector[..., 0] = tensor[..., 0, 0]
vector[..., 1] = tensor[..., 1, 1]
vector[..., 2] = tensor[..., 2, 2]
vector[..., 3] = tensor[..., 0, 1]
vector[..., 3] = tensor[..., 1, 0]
vector[..., 4] = tensor[..., 0, 2]
vector[..., 4] = tensor[..., 2, 0]
vector[..., 5] = tensor[..., 1, 2]
vector[..., 5] = tensor[..., 2, 1]
else:
raise NotImplementedError("d_vec=" + str(dim_vector))
return vector
[docs]def rotation_matrix_from_quaternion(Q):
"""
Create rotation matrices out of quaternions.
Quaternions have the form
`Q[..., 0]*i + Q[..., 1]*j + Q[..., 2]*k + Q[..., 3]`.
**Examples**
Given the quaternion for a 90-degree rotation along the z-axis,
the corresponding rotation matrix is computed.
>>> Q90z = np.array([ 0, 0, 0.7071068, 0.7071068])
>>> ROT90z = rotation_matrix_from_quaternion(Q90z)
The rotation matrix is used to rotate the x-Vector forward and
backward.
>>> x_vector = np.array([1, 0, 0])
>>> rotated_vector = do_rotation_vector(ROT90z, x_vector)
>>> np.round(rotated_vector, 6)
array([-0., 1., 0.])
>>> np.round(undo_rotation_vector(ROT90z, rotated_vector), 6)
array([1., 0., 0.])
A tensor is rotated using :py:func:`do_rotation_tensor` and :py:func:`undo_rotation_tensor`.
>>> tensor = np.array([
... [1.0, 0.2, 0.3],
... [0.2, 2.0, 0.4],
... [0.3, 0.4, 3.0],
... ])
>>> rotated_tensor = do_rotation_tensor(ROT90z, tensor)
>>> np.round(rotated_tensor, 6)
array([[ 2. , -0.2, -0.4],
[-0.2, 1. , 0.3],
[-0.4, 0.3, 3. ]])
>>> np.round(undo_rotation_tensor(ROT90z, rotated_tensor), 6)
array([[1. , 0.2, 0.3],
[0.2, 2. , 0.4],
[0.3, 0.4, 3. ]])
:param Q: array of shape (..., 4) containing the quaternions
:return: array of shape (..., 3, 3) containing the rotation matrices.
"""
Q = np.asarray(Q)
dim = list(Q.shape[:-1])
q0 = Q[..., 3].reshape(dim + [1, 1])
q1 = Q[..., 0].reshape(dim + [1, 1])
q2 = Q[..., 1].reshape(dim + [1, 1])
q3 = Q[..., 2].reshape(dim + [1, 1])
# First row of the rotation matrix
r00 = 1 - 2*(q2*q2 + q3*q3)
r01 = -2*q0*q3 + 2*q1*q2
r02 = 2*q0*q2+2*q1*q3
r0_ = np.concatenate([r00, r01, r02], axis=-1)
# Second row of the rotation matrix
r10 = 2*q0*q3 + 2*q1*q2
r11 = 1 - 2*(q1*q1 + q3*q3)
r12 = -2*q0*q1 + 2*q2*q3
r1_ = np.concatenate([r10, r11, r12], axis=-1)
# Third row of the rotation matrix
r20 = -2*q0*q2+2*q1*q3
r21 = 2*q0*q1 + 2*q2*q3
r22 = 1 - 2*(q1*q1 + q2*q2)
r2_ = np.concatenate([r20, r21, r22], axis=-1)
# dim X 3 X 3 rotation matrix
ROT = np.concatenate([
r0_,
r1_,
r2_
], axis=-2)
return ROT
[docs]def do_rotation_vector(ROT, vectors):
"""
Rotate vectors using rotation matrices.
:param ROT: array of shape (..., 3, 3) containing rotation matrices
:param vectors: array of shape (..., 3) containing vectors
:return: array of shape (..., 3) containing the rotated vectors
"""
return np.einsum("...ij,...j", ROT, vectors)
[docs]def undo_rotation_vector(ROT, rotated_vectors):
"""
Revert the rotation of vectors done by the given rotation matrices.
:param ROT: array of shape (..., 3, 3) containing rotation matrices
:param rotated_vectors: array of shape (..., 3) containing the rotated vectors
:return: array of shape (..., 3) containing vectors
"""
return np.einsum("...ji,...j", ROT, rotated_vectors)
[docs]def do_rotation_tensor(ROT, tensors):
"""
Rotate second-order tensors using rotation matrices.
:param ROT: array of shape (..., 3, 3) containing rotation matrices
:param tensors: array of shape (..., 3) containing tensors
:return: array of shape (..., 3, 3) containing the rotated tensors
"""
return np.einsum("...ij,...jk,...lk", ROT, tensors, ROT)
[docs]def undo_rotation_tensor(ROT, rotated_tensors):
"""
Revert the rotation of second-order tensors done by rotation matrices.
:param ROT: array of shape (..., 3, 3) containing rotation matrices
:param rotated_tensors: array of shape (..., 3) containing the rotated tensors
:return: array of shape (..., 3, 3) containing tensors
"""
return np.einsum("...ji,...jk,...kl", ROT, rotated_tensors, ROT)