diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index b72cec402..3ce47d50b 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -773,12 +773,12 @@ class Rotation: raise ValueError('P ∉ {-1,1}') qu[...,1:4] *= -P + if accept_homomorph: qu[qu[...,0] < 0.0] *= -1 - else: - if np.any(qu[...,0] < 0.0): - raise ValueError('quaternion with negative first (real) component') - if not np.all(np.isclose(np.linalg.norm(qu,axis=-1), 1.0,rtol=0.0)): + elif np.any(qu[...,0] < 0.0): + raise ValueError('quaternion with negative first (real) component') + if not np.all(np.isclose(np.linalg.norm(qu,axis=-1), 1.0,rtol=1e-8)): raise ValueError('quaternion is not of unit length') return Rotation(qu) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 8b1bd0de8..1423e8ce9 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -708,6 +708,7 @@ class TestRotation: @pytest.mark.parametrize('degrees',[True,False]) def test_axis_angle(self,set_of_rotations,degrees,normalize,P): c = np.array([P*-1,P*-1,P*-1,1.]) + c[:3] *= 0.9 if normalize else 1.0 for rot in set_of_rotations: m = rot.as_Euler_angles() o = Rotation.from_axis_angle(rot.as_axis_angle(degrees)*c,degrees,normalize,P).as_Euler_angles() @@ -730,16 +731,30 @@ class TestRotation: assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) \ and o[3]<=np.pi+1.e-9, f'{m},{o},{rot.as_quaternion()}' + def test_parallel(self,set_of_rotations): + a = np.array([[1.0,0.0,0.0], + [0.0,1.0,0.0]]) + for rot in set_of_rotations: + assert rot.allclose(Rotation.from_parallel(a,rot.broadcast_to((2,))@a)) + @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('normalize',[True,False]) def test_Rodrigues(self,set_of_rotations,normalize,P): c = np.array([P*-1,P*-1,P*-1,1.]) + c[:3] *= 0.9 if normalize else 1.0 for rot in set_of_rotations: m = rot.as_matrix() o = Rotation.from_Rodrigues_vector(rot.as_Rodrigues_vector()*c,normalize,P).as_matrix() ok = np.allclose(m,o,atol=atol) assert ok and np.isclose(np.linalg.det(o),1.0), f'{m},{o}' + def test_Rodrigues_compact(self,set_of_rotations): + for rot in set_of_rotations: + c = rot.as_Rodrigues_vector(compact=True) + r = rot.as_Rodrigues_vector(compact=False) + assert np.allclose(r[:3]*r[3], c, equal_nan=True) + + @pytest.mark.parametrize('P',[1,-1]) def test_homochoric(self,set_of_rotations,P): cutoff = np.tan(np.pi*.5*(1.-1e-4)) @@ -760,8 +775,9 @@ class TestRotation: @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('accept_homomorph',[True,False]) + # @pytest.mark.parametrize('normalize',[True,False]) def test_quaternion(self,set_of_rotations,P,accept_homomorph): - c = np.array([1,P*-1,P*-1,P*-1]) * (-1 if accept_homomorph else 1) + c = np.array([1,P*-1,P*-1,P*-1]) * (-1 if accept_homomorph else 1) #* (0.9 if normalize else 1.0) for rot in set_of_rotations: m = rot.as_cubochoric() o = Rotation.from_quaternion(rot.as_quaternion()*c,accept_homomorph,P).as_cubochoric() @@ -889,6 +905,15 @@ class TestRotation: with pytest.raises(ValueError): fr(eval(f'R.{to}()'),P=-30) + + def test_invalid_multiplication(self): + rot = Rotation.from_random() + with pytest.raises(TypeError): + rot@Rotation.from_random() + with pytest.raises(TypeError): + rot@[1,2,3,4] + + @pytest.mark.parametrize('shape',[None,(3,),(4,2)]) def test_broadcast(self,shape): rot = Rotation.from_random(shape) @@ -899,7 +924,7 @@ class TestRotation: assert np.allclose(rot_broadcast.quaternion[...,i,:], rot.quaternion) - @pytest.mark.parametrize('function,invalid',[(Rotation.from_quaternion, np.array([-1,0,0,0])), + @pytest.mark.parametrize('function,invalid',[(Rotation.from_quaternion, np.array([-1,0,0,0])), (Rotation.from_quaternion, np.array([1,1,1,0])), (Rotation.from_Euler_angles, np.array([1,4,0])), (Rotation.from_axis_angle, np.array([1,0,0,4])),