Esempio n. 1
0
def test_quaternions_rotm(rng):

    # Fifth: test that conversion back to rotation matrices works properly
    for i in range(TEST_N):

        rotm1 = rand_rotm(rng)
        rotm2 = rand_rotm(rng)

        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)))
Esempio n. 2
0
def _evecs_2_quat(evecs):
    """Convert a set of eigenvectors to a Quaternion expressing the
    rotation of the tensor's PAS with respect to the Cartesian axes"""

    # First, guarantee that the eigenvectors express *proper* rotations
    evecs = np.array(evecs) * np.linalg.det(evecs)[:, None, None]

    # Then get the quaternions
    return [Quaternion.from_matrix(evs.T) for evs in evecs]
Esempio n. 3
0
def test_quaternions_overload(rng):

    # Third: test compound rotations and operator overload
    for i in range(TEST_N):

        rotm1 = rand_rotm(rng)
        rotm2 = rand_rotm(rng)

        q1 = Quaternion.from_matrix(rotm1)
        q2 = Quaternion.from_matrix(rotm2)

        # Now test this with a vector
        v = rng.rand(3)

        vrotM = np.dot(rotm2, np.dot(rotm1, v))
        vrotQ = (q2 * q1).rotate(v)

        assert np.allclose(vrotM, vrotQ)
Esempio n. 4
0
def test_quaternions_gimbal(rng):

    # 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()
Esempio n. 5
0
    def extract(s, force_recalc):

        if Molecules.default_name not in s.info or force_recalc:
            Molecules.get(s)

        mol_quat = []
        all_m = s.get_masses()
        all_pos = s.get_positions()

        for mol in s.info[Molecules.default_name]:

            mol_pos = all_pos[mol.indices]
            mol_pos += np.tensordot(mol.get_array('cell_indices'),
                                    s.get_cell(),
                                    axes=(1, 1))
            mol_ms = all_m[mol.indices]

            # We still need to correct the positions with the COM
            mol_com = np.sum(mol_pos * mol_ms[:, None],
                             axis=0) / np.sum(mol_ms)
            mol_pos -= mol_com

            tens_i = np.identity(3)[None, :, :] * \
                np.linalg.norm(mol_pos, axis=1)[:, None, None]**2

            tens_i -= mol_pos[:, None, :] * mol_pos[:, :, None]
            tens_i *= mol_ms[:, None, None]
            tens_i = np.sum(tens_i, axis=0)

            evals, evecs = np.linalg.eigh(tens_i)
            # General ordering convention: we want the component of the
            # longest position to be positive along evecs_0, and the component
            # of the second longest (and non-parallel) position to be positive
            # along evecs_1, and the triple to be right-handed of course.
            mol_pos = sorted(mol_pos, key=lambda x: -np.linalg.norm(x))
            if len(mol_pos) > 1:
                evecs[0] *= np.sign(np.dot(evecs[0], mol_pos[0]))
            e1dirs = np.where(
                np.linalg.norm(np.cross(mol_pos, mol_pos[0])) > 0)[0]
            if len(e1dirs) > 0:
                evecs[1] *= np.sign(np.dot(evecs[1], mol_pos[e1dirs[0]]))
            evecs[2] *= np.sign(np.dot(evecs[2], np.cross(evecs[0], evecs[1])))
            # Evecs must be proper
            evecs /= np.linalg.det(evecs)

            quat = Quaternion()
            quat = quat.from_matrix(evecs.T)
            mol_quat.append(quat)

        return mol_quat
Esempio n. 6
0
def test_quaternions_axang(rng):

    # Sixth: test conversion to axis + angle
    q = Quaternion()
    n, theta = q.axis_angle()
    assert(theta == 0)

    u = np.array([1, 0.5, 1])
    u /= np.linalg.norm(u)
    alpha = 1.25

    q = Quaternion.from_matrix(axang_rotm(u, alpha))
    n, theta = q.axis_angle()

    assert(np.isclose(theta, alpha))
    assert(np.allclose(u, n))
Esempio n. 7
0
def test_quaternions_rotations(rng):

    # First: test that rotations DO work
    for i in range(TEST_N):
        # n random tests

        rotm = rand_rotm(rng)

        q = Quaternion.from_matrix(rotm)

        # Now test this with a vector
        v = rng.rand(3)

        vrotM = np.dot(rotm, v)
        vrotQ = q.rotate(v)

        assert np.allclose(vrotM, vrotQ)
Esempio n. 8
0
    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)
Esempio n. 9
0
    # 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
    
# First: test that rotations DO work
for i in range(10):
    # 10 random tests
        
    rotm = rand_rotm()

    q = Quaternion.from_matrix(rotm)

    # Now test this with a vector
    v = np.random.random(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)
Esempio n. 10
0
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)))