return rotation type (ensures proper rotation)
This commit is contained in:
parent
a4b5c2a537
commit
a87596cefc
|
@ -956,9 +956,9 @@ class Orientation(Rotation):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if self.family is None or other.family is None:
|
if self.family is None or other.family is None:
|
||||||
raise ValueError('Missing crystal symmetry')
|
raise ValueError('missing crystal symmetry')
|
||||||
if self.family != other.family:
|
if self.family != other.family:
|
||||||
raise NotImplementedError('Disorientation between different crystal families not supported yet.')
|
raise NotImplementedError('disorientation between different crystal families')
|
||||||
|
|
||||||
blend = util.shapeblender(self.shape,other.shape)
|
blend = util.shapeblender(self.shape,other.shape)
|
||||||
s = self.equivalent
|
s = self.equivalent
|
||||||
|
|
|
@ -944,7 +944,7 @@ class Result:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _add_rotational(F):
|
def _add_rotational(F):
|
||||||
return {
|
return {
|
||||||
'data': mechanics.rotational(F['data']),
|
'data': mechanics.rotational(F['data']).as_matrix(),
|
||||||
'label': f"R({F['label']})",
|
'label': f"R({F['label']})",
|
||||||
'meta': {
|
'meta': {
|
||||||
'Unit': F['meta']['Unit'],
|
'Unit': F['meta']['Unit'],
|
||||||
|
|
|
@ -61,12 +61,15 @@ class Rotation:
|
||||||
elif np.array(rotation).shape[-1] == 4:
|
elif np.array(rotation).shape[-1] == 4:
|
||||||
self.quaternion = np.array(rotation)
|
self.quaternion = np.array(rotation)
|
||||||
else:
|
else:
|
||||||
raise ValueError('"rotation" is neither a Rotation nor a quaternion')
|
raise TypeError('"rotation" is neither a Rotation nor a quaternion')
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Represent rotation as unit quaternion, rotation matrix, and Bunge-Euler angles."""
|
"""Represent rotation as unit quaternion, rotation matrix, and Bunge-Euler angles."""
|
||||||
return 'As quaternions:\n'+str(self.quaternion) \
|
if self == Rotation():
|
||||||
|
return 'Rotation()'
|
||||||
|
else:
|
||||||
|
return f'Quaternions {self.shape}:\n'+str(self.quaternion) \
|
||||||
if self.quaternion.shape != (4,) else \
|
if self.quaternion.shape != (4,) else \
|
||||||
'\n'.join([
|
'\n'.join([
|
||||||
'Quaternion: (real={:.3f}, imag=<{:+.3f}, {:+.3f}, {:+.3f}>)'.format(*(self.quaternion)),
|
'Quaternion: (real={:.3f}, imag=<{:+.3f}, {:+.3f}, {:+.3f}>)'.format(*(self.quaternion)),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Finite-strain continuum mechanics."""
|
"""Finite-strain continuum mechanics."""
|
||||||
|
|
||||||
from . import tensor as _tensor
|
from . import tensor as _tensor
|
||||||
|
from . import _rotation
|
||||||
|
|
||||||
import numpy as _np
|
import numpy as _np
|
||||||
|
|
||||||
|
@ -107,11 +108,11 @@ def rotational(T):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
R : numpy.ndarray of shape (...,3,3)
|
R : damask.Rotation of shape (...)
|
||||||
Rotational part.
|
Rotational part of the vector.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return _polar_decomposition(T,'R')[0]
|
return _rotation.Rotation.from_matrix(_polar_decomposition(T,'R')[0])
|
||||||
|
|
||||||
|
|
||||||
def strain(F,t,m):
|
def strain(F,t,m):
|
||||||
|
@ -260,7 +261,7 @@ def _polar_decomposition(T,requested):
|
||||||
output.append(_np.einsum('...ji,...jk',R,T))
|
output.append(_np.einsum('...ji,...jk',R,T))
|
||||||
|
|
||||||
if len(output) == 0:
|
if len(output) == 0:
|
||||||
raise ValueError('Output needs to be out of V,R,U')
|
raise ValueError('output needs to be out of V, R, U')
|
||||||
|
|
||||||
return tuple(output)
|
return tuple(output)
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,7 @@ class TestResult:
|
||||||
default.add_rotational('F')
|
default.add_rotational('F')
|
||||||
loc = {'F': default.get_dataset_location('F'),
|
loc = {'F': default.get_dataset_location('F'),
|
||||||
'R(F)': default.get_dataset_location('R(F)')}
|
'R(F)': default.get_dataset_location('R(F)')}
|
||||||
in_memory = mechanics.rotational(default.read_dataset(loc['F'],0))
|
in_memory = mechanics.rotational(default.read_dataset(loc['F'],0)).as_matrix()
|
||||||
in_file = default.read_dataset(loc['R(F)'],0)
|
in_file = default.read_dataset(loc['R(F)'],0)
|
||||||
assert np.allclose(in_memory,in_file)
|
assert np.allclose(in_memory,in_file)
|
||||||
|
|
||||||
|
|
|
@ -682,6 +682,9 @@ class TestRotation:
|
||||||
for v,o in zip(vec,ori):
|
for v,o in zip(vec,ori):
|
||||||
assert np.allclose(func(v,normalize=True).as_quaternion(),o.as_quaternion())
|
assert np.allclose(func(v,normalize=True).as_quaternion(),o.as_quaternion())
|
||||||
|
|
||||||
|
def test_invalid_init(self):
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
Rotation(np.ones(3))
|
||||||
|
|
||||||
@pytest.mark.parametrize('degrees',[True,False])
|
@pytest.mark.parametrize('degrees',[True,False])
|
||||||
def test_Eulers(self,set_of_rotations,degrees):
|
def test_Eulers(self,set_of_rotations,degrees):
|
||||||
|
@ -831,6 +834,13 @@ class TestRotation:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
function(invalid_shape)
|
function(invalid_shape)
|
||||||
|
|
||||||
|
def test_invalid_shape_parallel(self):
|
||||||
|
invalid_a = np.random.random(np.random.randint(8,32,(3)))
|
||||||
|
invalid_b = np.random.random(np.random.randint(8,32,(3)))
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
Rotation.from_parallel(invalid_a,invalid_b)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('fr,to',[(Rotation.from_quaternion,'as_quaternion'),
|
@pytest.mark.parametrize('fr,to',[(Rotation.from_quaternion,'as_quaternion'),
|
||||||
(Rotation.from_axis_angle,'as_axis_angle'),
|
(Rotation.from_axis_angle,'as_axis_angle'),
|
||||||
(Rotation.from_Rodrigues_vector, 'as_Rodrigues_vector'),
|
(Rotation.from_Rodrigues_vector, 'as_Rodrigues_vector'),
|
||||||
|
|
|
@ -3,6 +3,7 @@ import numpy as np
|
||||||
|
|
||||||
from damask import tensor
|
from damask import tensor
|
||||||
from damask import mechanics
|
from damask import mechanics
|
||||||
|
from damask import Rotation
|
||||||
|
|
||||||
def stress_Cauchy(P,F):
|
def stress_Cauchy(P,F):
|
||||||
sigma = 1.0/np.linalg.det(F) * np.dot(P,F.T)
|
sigma = 1.0/np.linalg.det(F) * np.dot(P,F.T)
|
||||||
|
@ -96,7 +97,6 @@ class TestMechanics:
|
||||||
@pytest.mark.parametrize('vectorized,single',[(mechanics.maximum_shear, maximum_shear),
|
@pytest.mark.parametrize('vectorized,single',[(mechanics.maximum_shear, maximum_shear),
|
||||||
(mechanics.equivalent_stress_Mises, equivalent_stress_Mises),
|
(mechanics.equivalent_stress_Mises, equivalent_stress_Mises),
|
||||||
(mechanics.equivalent_strain_Mises, equivalent_strain_Mises),
|
(mechanics.equivalent_strain_Mises, equivalent_strain_Mises),
|
||||||
(mechanics.rotational, rotational),
|
|
||||||
(mechanics.stretch_left, stretch_left),
|
(mechanics.stretch_left, stretch_left),
|
||||||
(mechanics.stretch_right, stretch_right),
|
(mechanics.stretch_right, stretch_right),
|
||||||
])
|
])
|
||||||
|
@ -106,6 +106,14 @@ class TestMechanics:
|
||||||
for i,v in enumerate(np.reshape(vectorized(epsilon_vec),vectorized(epsilon).shape)):
|
for i,v in enumerate(np.reshape(vectorized(epsilon_vec),vectorized(epsilon).shape)):
|
||||||
assert np.allclose(single(epsilon[i]),v)
|
assert np.allclose(single(epsilon[i]),v)
|
||||||
|
|
||||||
|
def test_vectorize_rotation(self):
|
||||||
|
epsilon = Rotation.from_random(self.n).as_matrix()
|
||||||
|
epsilon_vec = np.reshape(epsilon,(self.n//10,10,3,3))
|
||||||
|
for i,v in enumerate(np.reshape(mechanics.rotational(epsilon_vec).as_matrix(),
|
||||||
|
mechanics.rotational(epsilon).as_matrix().shape)):
|
||||||
|
assert np.allclose(rotational(epsilon[i]),v)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('vectorized,single',[(mechanics.stress_Cauchy, stress_Cauchy),
|
@pytest.mark.parametrize('vectorized,single',[(mechanics.stress_Cauchy, stress_Cauchy),
|
||||||
(mechanics.stress_second_Piola_Kirchhoff, stress_second_Piola_Kirchhoff)
|
(mechanics.stress_second_Piola_Kirchhoff, stress_second_Piola_Kirchhoff)
|
||||||
])
|
])
|
||||||
|
@ -138,7 +146,7 @@ class TestMechanics:
|
||||||
def test_polar_decomposition(self):
|
def test_polar_decomposition(self):
|
||||||
"""F = RU = VR."""
|
"""F = RU = VR."""
|
||||||
F = np.broadcast_to(np.eye(3),[self.n,3,3])*np.random.rand(self.n,3,3)
|
F = np.broadcast_to(np.eye(3),[self.n,3,3])*np.random.rand(self.n,3,3)
|
||||||
R = mechanics.rotational(F)
|
R = mechanics.rotational(F).as_matrix()
|
||||||
V = mechanics.stretch_left(F)
|
V = mechanics.stretch_left(F)
|
||||||
U = mechanics.stretch_right(F)
|
U = mechanics.stretch_right(F)
|
||||||
assert np.allclose(np.matmul(R,U),
|
assert np.allclose(np.matmul(R,U),
|
||||||
|
@ -162,7 +170,7 @@ class TestMechanics:
|
||||||
@pytest.mark.parametrize('t',['V','U'])
|
@pytest.mark.parametrize('t',['V','U'])
|
||||||
def test_strain_rotation(self,m,t):
|
def test_strain_rotation(self,m,t):
|
||||||
"""Ensure that pure rotation results in no strain."""
|
"""Ensure that pure rotation results in no strain."""
|
||||||
F = mechanics.rotational(np.random.rand(self.n,3,3))
|
F = Rotation.from_random(self.n).as_matrix()
|
||||||
assert np.allclose(mechanics.strain(F,t,m),
|
assert np.allclose(mechanics.strain(F,t,m),
|
||||||
0.0)
|
0.0)
|
||||||
|
|
||||||
|
@ -173,7 +181,7 @@ class TestMechanics:
|
||||||
Should be +1, but random F might contain a reflection.
|
Should be +1, but random F might contain a reflection.
|
||||||
"""
|
"""
|
||||||
x = np.random.rand(self.n,3,3)
|
x = np.random.rand(self.n,3,3)
|
||||||
assert np.allclose(np.abs(np.linalg.det(mechanics.rotational(x))),
|
assert np.allclose(np.abs(np.linalg.det(mechanics._polar_decomposition(x,'R')[0])),
|
||||||
1.0)
|
1.0)
|
||||||
|
|
||||||
def test_deviatoric_Mises(self):
|
def test_deviatoric_Mises(self):
|
||||||
|
@ -203,3 +211,7 @@ class TestMechanics:
|
||||||
"""Ensure that sherical stress has max shear of 0.0."""
|
"""Ensure that sherical stress has max shear of 0.0."""
|
||||||
A = tensor.spherical(tensor.symmetric(np.random.rand(self.n,3,3)),True)
|
A = tensor.spherical(tensor.symmetric(np.random.rand(self.n,3,3)),True)
|
||||||
assert np.allclose(mechanics.maximum_shear(A),0.0)
|
assert np.allclose(mechanics.maximum_shear(A),0.0)
|
||||||
|
|
||||||
|
def test_invalid_decomposition(self):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
mechanics._polar_decomposition(np.random.rand(10,3,3),'A')
|
||||||
|
|
Loading…
Reference in New Issue