def test_efg(self): eth = io.read(os.path.join(_TESTDATA_DIR, 'ethanol.magres')) # Load the data calculated with MagresView with open(os.path.join(_TESTDATA_DIR, 'ethanol_efg.dat')) as f: data = f.readlines()[8:] asymm = EFGAsymmetry.get(eth) qprop = EFGQuadrupolarConstant(isotopes={'H': 2}) qcnst = qprop(eth) quats = EFGQuaternion.get(eth) for i, d in enumerate(data): vals = [float(x) for x in d.split()[1:]] if len(vals) != 8: continue # And check... # The quadrupolar constant has some imprecision due to values # of quadrupole moment, so we only ask 2 places in kHz self.assertAlmostEqual(qcnst[i] * 1e-3, vals[0] * 1e-3, places=2) self.assertAlmostEqual(asymm[i], vals[1]) vq = Quaternion.from_euler_angles(*(np.array(vals[-3:]) * np.pi / 180.0)) # Product to see if they go back to the origin # The datafile contains conjugate quaternions pq = vq * quats[i] cosphi = np.clip(pq.q[0], -1, 1) phi = np.arccos(cosphi) # 180 degrees rotations are possible (signs are not fixed) self.assertTrue( np.isclose((phi * 2) % np.pi, 0) or np.isclose( (phi * 2) % np.pi, np.pi))
def test_quaternions_euler(rng): # Fourth: test Euler angles for mode in ['zyz', 'zxz']: for i in range(TEST_N): abc = rng.rand(3) * 2 * np.pi q_eul = Quaternion.from_euler_angles(*abc, mode=mode) rot_eul = eulang_rotm(*abc, mode=mode) assert(np.allclose(rot_eul, q_eul.rotation_matrix())) # Test conversion back and forth abc_2 = q_eul.euler_angles(mode=mode) q_eul_2 = Quaternion.from_euler_angles(*abc_2, mode=mode) assert(np.allclose(q_eul_2.q, q_eul.q))
def test_quaternions_euler(rng): # Fourth: test Euler angles for mode in ['zyz', 'zxz']: for i in range(TEST_N): abc = rng.rand(3) * 2 * np.pi v2 = rng.rand(2, 3) # Two random vectors to rotate rigidly q_eul = Quaternion.from_euler_angles(*abc, mode=mode) rot_eul = eulang_rotm(*abc, mode=mode) v2_q = np.array([q_eul.rotate(v) for v in v2]) v2_m = np.array([np.dot(rot_eul, v) for v in v2]) assert np.allclose(v2_q, v2_m)
# Now test this with a vector v = rndstate.rand(3) vrotM = np.dot(rotm2, np.dot(rotm1, v)) vrotQ = (q2 * q1).rotate(v) assert np.allclose(vrotM, vrotQ) # Fourth: test Euler angles for mode in ['zyz', 'zxz']: for i in range(test_n): abc = rndstate.rand(3)*2*np.pi v2 = rndstate.rand(2, 3) # Two random vectors to rotate rigidly q_eul = Quaternion.from_euler_angles(*abc, mode=mode) rot_eul = eulang_rotm(*abc, mode=mode) v2_q = np.array([q_eul.rotate(v) for v in v2]) v2_m = np.array([np.dot(rot_eul, v) for v in v2]) assert np.allclose(v2_q, v2_m) # Fifth: test that conversion back to rotation matrices works properly for i in range(test_n): rotm1 = rand_rotm(rndstate) rotm2 = rand_rotm(rndstate) q1 = Quaternion.from_matrix(rotm1) q2 = Quaternion.from_matrix(rotm2)
def test_quaternions(): import numpy as np from ase.quaternions import Quaternion def axang_rotm(u, theta): u = np.array(u, float) u /= np.linalg.norm(u) # Cross product matrix for u ucpm = np.array([[0, -u[2], u[1]], [u[2], 0, -u[0]], [-u[1], u[0], 0]]) # Rotation matrix rotm = (np.cos(theta) * np.identity(3) + np.sin(theta) * ucpm + (1 - np.cos(theta)) * np.kron(u[:, None], u[None, :])) return rotm def rand_rotm(rndstate=np.random.RandomState(0)): """Axis & angle rotations.""" u = rndstate.rand(3) theta = rndstate.rand() * np.pi * 2 return axang_rotm(u, theta) def eulang_rotm(a, b, c, mode='zyz'): rota = axang_rotm([0, 0, 1], a) rotc = axang_rotm([0, 0, 1], c) if mode == 'zyz': rotb = axang_rotm([0, 1, 0], b) elif mode == 'zxz': rotb = axang_rotm([1, 0, 0], b) return np.dot(rotc, np.dot(rotb, rota)) # Random state for testing rndstate = np.random.RandomState(0) test_n = 200 # First: test that rotations DO work for i in range(test_n): # n random tests rotm = rand_rotm(rndstate) q = Quaternion.from_matrix(rotm) # Now test this with a vector v = rndstate.rand(3) vrotM = np.dot(rotm, v) vrotQ = q.rotate(v) assert np.allclose(vrotM, vrotQ) # Second: test the special case of a PI rotation rotm = np.identity(3) rotm[:2, :2] *= -1 # Rotate PI around z axis q = Quaternion.from_matrix(rotm) assert not np.isnan(q.q).any() # Third: test compound rotations and operator overload for i in range(test_n): rotm1 = rand_rotm(rndstate) rotm2 = rand_rotm(rndstate) q1 = Quaternion.from_matrix(rotm1) q2 = Quaternion.from_matrix(rotm2) # Now test this with a vector v = rndstate.rand(3) vrotM = np.dot(rotm2, np.dot(rotm1, v)) vrotQ = (q2 * q1).rotate(v) assert np.allclose(vrotM, vrotQ) # Fourth: test Euler angles for mode in ['zyz', 'zxz']: for i in range(test_n): abc = rndstate.rand(3) * 2 * np.pi v2 = rndstate.rand(2, 3) # Two random vectors to rotate rigidly q_eul = Quaternion.from_euler_angles(*abc, mode=mode) rot_eul = eulang_rotm(*abc, mode=mode) v2_q = np.array([q_eul.rotate(v) for v in v2]) v2_m = np.array([np.dot(rot_eul, v) for v in v2]) assert np.allclose(v2_q, v2_m) # Fifth: test that conversion back to rotation matrices works properly for i in range(test_n): rotm1 = rand_rotm(rndstate) rotm2 = rand_rotm(rndstate) q1 = Quaternion.from_matrix(rotm1) q2 = Quaternion.from_matrix(rotm2) assert (np.allclose(q1.rotation_matrix(), rotm1)) assert (np.allclose(q2.rotation_matrix(), rotm2)) assert (np.allclose((q1 * q2).rotation_matrix(), np.dot(rotm1, rotm2)))