diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index aa7115200..e59f7e6bf 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -7,6 +7,7 @@ from damask import Table from damask import _rotation from damask import grid_filters from damask import util +from damask import tensor n = 1000 atol=1.e-4 @@ -20,6 +21,16 @@ def ref_path(ref_path_base): def set_of_rotations(set_of_quaternions): return [Rotation.from_quaternion(s) for s in set_of_quaternions] +@pytest.fixture +def multidim_rotations(set_of_quaternions): + L = len(set_of_quaternions) + i = 0 + while L%(f:=np.random.randint(2,np.sqrt(L).astype(int))) > 0 and i= 0.).all and (v < np.pi+1.e-9).all() - @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}' + r = m.as_matrix() + assert np.allclose(1.,np.linalg.det(r)) - 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) + e = m.as_Euler_angles(degrees=False) + assert (e >= 0.).all and (e < np.pi*np.array([2.,1.,2.])+1.e-9).all() + + c = m.as_cubochoric() + assert (np.linalg.norm(c,ord=np.inf,axis=-1) < np.pi**(2./3.)*0.5+1.e-9).all() + + h = m.as_homochoric() + assert (np.linalg.norm(h,axis=-1) < (3.*np.pi/4.)**(1./3.) + 1.e-9).all() - @pytest.mark.parametrize('P',[1,-1]) - def test_homochoric(self,set_of_rotations,P): - cutoff = np.tan(np.pi*.5*(1.-1e-4)) - for rot in set_of_rotations: - m = rot.as_Rodrigues_vector() - o = Rotation.from_homochoric(rot.as_homochoric()*P*-1,P).as_Rodrigues_vector() - ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) - ok |= np.isclose(m[3],0.0,atol=atol) - assert ok and np.isclose(np.linalg.norm(o[:3]),1.0), f'{m},{o},{rot.as_quaternion()}' - - @pytest.mark.parametrize('P',[1,-1]) - def test_cubochoric(self,set_of_rotations,P): - for rot in set_of_rotations: - m = rot.as_homochoric() - o = Rotation.from_cubochoric(rot.as_cubochoric()*P*-1,P).as_homochoric() - ok = np.allclose(m,o,atol=atol) - assert ok and np.linalg.norm(o) < (3.*np.pi/4.)**(1./3.) + 1.e-9, f'{m},{o},{rot.as_quaternion()}' - - @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,normalize): - 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,normalize,P).as_cubochoric() - ok = np.allclose(m,o,atol=atol) - if np.count_nonzero(np.isclose(np.abs(o),np.pi**(2./3.)*.5)): - ok |= np.allclose(m*-1.,o,atol=atol) - assert ok and o.max() < np.pi**(2./3.)*0.5+1.e-9, f'{m},{o},{rot.as_quaternion()}' + @pytest.mark.parametrize('P',[1,-1]) + def test_quaternion(self,multidim_rotations,accept_homomorph,normalize,P): + c = np.array([1,-P,-P,-P]) * (-1 if accept_homomorph else 1) * (0.9 if normalize else 1.0) + m = multidim_rotations + o = Rotation.from_quaternion(m.as_quaternion()*c, + accept_homomorph=accept_homomorph, + normalize=normalize, + P=P) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + + + @pytest.mark.parametrize('degrees',[True,False]) + def test_Eulers(self,multidim_rotations,degrees): + m = multidim_rotations + o = Rotation.from_Euler_angles(m.as_Euler_angles(degrees), + degrees=degrees) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + + + @pytest.mark.parametrize('degrees',[True,False]) + @pytest.mark.parametrize('normalize',[True,False]) + @pytest.mark.parametrize('P',[1,-1]) + def test_axis_angle(self,multidim_rotations,degrees,normalize,P): + c = np.array([-P,-P,-P,1.]) + c[:3] *= 0.9 if normalize else 1.0 + + m = multidim_rotations + o = Rotation.from_axis_angle(m.as_axis_angle(degrees)*c, + degrees=degrees, + normalize=normalize, + P=P) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + + + def test_matrix(self,multidim_rotations): + m = multidim_rotations + o = Rotation.from_matrix(m.as_matrix()) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + + + def test_parallel(self,multidim_rotations): + m = multidim_rotations + a = np.broadcast_to(np.array([[1.0,0.0,0.0], + [0.0,1.0,0.0]]),m.shape+(2,3)) + assert m.allclose(Rotation.from_parallel(a,m.broadcast_to(m.shape+(2,))@a)) + + + @pytest.mark.parametrize('normalize',[True,False]) + @pytest.mark.parametrize('P',[1,-1]) + def test_Rodrigues(self,multidim_rotations,normalize,P): + c = np.array([-P,-P,-P,1.]) + c[:3] *= 0.9 if normalize else 1.0 + m = multidim_rotations + o = Rotation.from_Rodrigues_vector(m.as_Rodrigues_vector()*c, + normalize=normalize, + P=P) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + + + def test_Rodrigues_compact(self,multidim_rotations): + m = multidim_rotations + c = m.as_Rodrigues_vector(compact=True) + r = m.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,multidim_rotations,P): + m = multidim_rotations + o = Rotation.from_homochoric(m.as_homochoric()*-P, + P=P) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + + + @pytest.mark.parametrize('P',[1,-1]) + def test_cubochoric(self,multidim_rotations,P): + m = multidim_rotations + o = Rotation.from_cubochoric(m.as_cubochoric()*-P, + P=P) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + @pytest.mark.parametrize('reciprocal',[True,False]) - def test_basis(self,set_of_rotations,reciprocal): - for rot in set_of_rotations: - om = rot.as_matrix() + 0.1*np.eye(3) - rot = Rotation.from_basis(om,False,reciprocal=reciprocal) - assert np.isclose(np.linalg.det(rot.as_matrix()),1.0) + def test_basis(self,multidim_rotations,reciprocal): + m = multidim_rotations + r = m.as_matrix() + r = np.linalg.inv(tensor.transpose(r)/np.pi) if reciprocal else r + o = Rotation.from_basis(r, + reciprocal=reciprocal) + f = Rotation(np.where(np.isclose(m.as_quaternion()[...,0],0.0,atol=atol)[...,np.newaxis],~o,o)) + assert np.logical_or(m.isclose(o,atol=atol), + m.isclose(f,atol=atol) + ).all() + @pytest.mark.parametrize('shape',[None,1,(4,4)]) def test_random(self,shape): r = Rotation.from_random(shape) - if shape is None: - assert r.shape == () - elif shape == 1: - assert r.shape == (1,) - else: - assert r.shape == shape + assert r.shape == () if shape is None else (1,) if shape == 1 else shape @pytest.mark.parametrize('shape',[None,5,(4,6)]) def test_equal(self,shape): @@ -822,7 +872,7 @@ class TestRotation: def test_equal_ambiguous(self): qu = np.random.rand(10,4) qu[:,0] = 0. - qu/=np.linalg.norm(qu,axis=1,keepdims=True) + qu /= np.linalg.norm(qu,axis=1,keepdims=True) assert (Rotation(qu) == Rotation(-qu)).all() def test_inversion(self): @@ -947,13 +997,13 @@ class TestRotation: p = np.random.rand(n,3) o = Rotation._get_pyramid_order(p,direction) for i,o_i in enumerate(o): - assert np.all(o_i==Rotation._get_pyramid_order(p[i],direction)) + assert (o_i==Rotation._get_pyramid_order(p[i],direction)).all() def test_pyramid_invariant(self): a = np.random.rand(n,3) f = Rotation._get_pyramid_order(a,'forward') b = Rotation._get_pyramid_order(a,'backward') - assert np.all(np.take_along_axis(np.take_along_axis(a,f,-1),b,-1) == a) + assert (np.take_along_axis(np.take_along_axis(a,f,-1),b,-1) == a).all() @pytest.mark.parametrize('data',[np.random.rand(5,3),