def test_swing_twist(self): from ase.quaternions import Quaternion test_n = 10 for t_i in range(test_n): # Create two quaternions with random rotations theta1, theta2 = np.random.random(2) * 2 * np.pi ax1 = np.random.random(3) ax2 = np.cross(np.random.random(3), ax1) ax1 /= np.linalg.norm(ax1) ax2 /= np.linalg.norm(ax2) q1 = Quaternion([np.cos(theta1 / 2)] + list(ax1 * np.sin(theta1 / 2))) q2 = Quaternion([np.cos(theta2 / 2)] + list(ax2 * np.sin(theta2 / 2))) qT = q1 * q2 # Now decompose qsw, qtw = swing_twist_decomp(qT, ax2) # And check q1.q *= np.sign(q1.q[0]) q2.q *= np.sign(q2.q[0]) qsw.q *= np.sign(qsw.q[0]) qtw.q *= np.sign(qtw.q[0]) self.assertTrue(np.allclose(q1.q, qsw.q)) self.assertTrue(np.allclose(q2.q, qtw.q))
def my_arc(self, gc, fill, j, X, r, n, A, d): if self.images.shapes is not None: rx = (self.images.shapes[j, 0]).round().astype(int) ry = (self.images.shapes[j, 1]).round().astype(int) rz = (self.images.shapes[j, 2]).round().astype(int) circle = rx == ry and ry == rz if not circle: Q = Quaternion(self.images.Q[self.frame][j]) X2d = np.array([X[j][0], X[j][1]]) Ellipsoid = np.array([[1. / (rx * rx), 0, 0], [0, 1. / (ry * ry), 0], [0, 0, 1. / (rz * rz)]]) # Ellipsoid rotated by quaternion as Matrix X' = R X R_transpose El_r = np.dot( Q.rotation_matrix(), np.dot(Ellipsoid, np.transpose(Q.rotation_matrix()))) # Ellipsoid rotated by quaternion and axes as # Matrix X' = R_axes X' R_axes El_v = np.dot(np.transpose(self.axes), np.dot(El_r, self.axes)) # Projection of rotated ellipsoid on xy plane El_p = Ell = np.array( [[ El_v[0][0] - El_v[0][2] * El_v[0][2] / El_v[2][2], El_v[0][1] - El_v[0][2] * El_v[1][2] / El_v[2][2] ], [ El_v[0][1] - El_v[0][2] * El_v[1][2] / El_v[2][2], El_v[1][1] - El_v[1][2] * El_v[1][2] / El_v[2][2] ]]) # diagonal matrix der Ellipse gibt halbachsen El_p_diag = np.linalg.eig(El_p) # Winkel mit dem Ellipse in xy gedreht ist aus # eigenvektor der diagonal matrix phi = atan(El_p_diag[1][0][1] / El_p_diag[1][0][0]) tupl = [] alpha = np.array(range(16)) * 2 * np.pi / 16 El_xy = np.array([ sqrt(1. / (El_p_diag[0][0])) * np.cos(alpha) * np.cos(phi) - sqrt(1. / (El_p_diag[0][1])) * np.sin(alpha) * np.sin(phi), sqrt(1. / (El_p_diag[0][0])) * np.cos(alpha) * np.sin(phi) + sqrt(1. / (El_p_diag[0][1])) * np.sin(alpha) * np.cos(phi) ]) tupl = (El_xy.transpose() * self.scale + X[j][:2]).round().astype(int) # XXX there must be a better way tupl = [tuple(i) for i in tupl] return self.pixmap.draw_polygon(gc, fill, tupl) else: return self.pixmap.draw_arc(gc, fill, A[j, 0], A[j, 1], d[j], d[j], 0, 23040) else: return self.pixmap.draw_arc(gc, fill, A[j, 0], A[j, 1], d[j], d[j], 0, 23040)
def my_arc(self, gc, fill, j, X, r, n, A, d): if self.images.shapes is not None: rx = (self.images.shapes[j, 0]).round().astype(int) ry = (self.images.shapes[j, 1]).round().astype(int) rz = (self.images.shapes[j, 2]).round().astype(int) circle = rx == ry and ry == rz if not circle: Q = Quaternion(self.images.Q[self.frame][j]) X2d = np.array([X[j][0], X[j][1]]) Ellipsoid = np.array([[1. / (rx*rx), 0, 0], [0, 1. / (ry*ry), 0], [0, 0, 1. / (rz*rz)] ]) # Ellipsoid rotated by quaternion as Matrix X' = R X R_transpose El_r = np.dot(Q.rotation_matrix(), np.dot(Ellipsoid, np.transpose(Q.rotation_matrix()))) # Ellipsoid rotated by quaternion and axes as # Matrix X' = R_axes X' R_axes El_v = np.dot(np.transpose(self.axes), np.dot(El_r, self.axes)) # Projection of rotated ellipsoid on xy plane El_p = Ell = np.array([ [El_v[0][0] - El_v[0][2] * El_v[0][2] / El_v[2][2], El_v[0][1] - El_v[0][2] * El_v[1][2] / El_v[2][2]], [El_v[0][1] - El_v[0][2] * El_v[1][2] / El_v[2][2], El_v[1][1] - El_v[1][2] * El_v[1][2] / El_v[2][2]] ]) # diagonal matrix der Ellipse gibt halbachsen El_p_diag = np.linalg.eig(El_p) # Winkel mit dem Ellipse in xy gedreht ist aus # eigenvektor der diagonal matrix phi = atan(El_p_diag[1][0][1] / El_p_diag[1][0][0]) tupl = [] alpha = np.array(range(16)) * 2 * np.pi / 16 El_xy = np.array([sqrt(1. / (El_p_diag[0][0])) * np.cos(alpha)*np.cos(phi) - sqrt(1./(El_p_diag[0][1])) * np.sin(alpha) * np.sin(phi), sqrt(1./(El_p_diag[0][0])) * np.cos(alpha)*np.sin(phi) + sqrt(1./(El_p_diag[0][1])) * np.sin(alpha) * np.cos(phi)]) tupl = (El_xy.transpose() * self.scale + X[j][:2]).round().astype(int) # XXX there must be a better way tupl = [tuple(i) for i in tupl] return self.pixmap.draw_polygon( gc, fill, tupl) else: return self.pixmap.draw_arc(gc, fill, A[j, 0], A[j, 1], d[j], d[j], 0, 23040) else: return self.pixmap.draw_arc(gc, fill, A[j, 0], A[j, 1], d[j], d[j], 0, 23040)
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
def rotation(axis, angle): """ Generate the rotation matrix given the rotation axis and angle """ norm = n.linalg.norm(axis) tol = 2 * n.finfo(n.float).eps q = [n.cos(angle / 2)] for a in axis: q.append(n.sin(angle / 2.0) * a / norm) q = Quaternion(q) matrix = q.rotation_matrix() matrix[abs(matrix) < tol] = 0.0 return matrix
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)))
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_transformprops(self): from ase.quaternions import Quaternion from soprano.selection import AtomSelection from soprano.properties.transform import (Translate, Rotate, Mirror) a = Atoms('CH', positions=[[0, 0, 0], [0.5, 0, 0]]) sel = AtomSelection.from_element(a, 'C') transl = Translate(selection=sel, vector=[0.5, 0, 0]) rot = Rotate(selection=sel, center=[0.25, 0.0, 0.25], quaternion=Quaternion([np.cos(np.pi/4.0), 0, np.sin(np.pi/4.0), 0])) mirr = Mirror(selection=sel, plane=[1, 0, 0, -0.25]) aT = transl(a) aR = rot(a) aM = mirr(a) self.assertAlmostEqual(np.linalg.norm(aT.get_positions()[0]), np.linalg.norm(aT.get_positions()[1])) self.assertAlmostEqual(np.linalg.norm(aR.get_positions()[0]), np.linalg.norm(aR.get_positions()[1])) self.assertAlmostEqual(np.linalg.norm(aM.get_positions()[0]), np.linalg.norm(aM.get_positions()[1]))
def test_diprotavg(self): # Test dipolar rotational averaging eth = io.read(os.path.join(_TESTDATA_DIR, 'ethanol.magres')) from ase.quaternions import Quaternion from soprano.properties.transform import Rotate from soprano.collection import AtomsCollection from soprano.collection.generate import transformGen from soprano.properties.nmr import DipolarTensor N = 30 # Number of averaging steps axis = np.array([1.0, 1.0, 0]) axis /= np.linalg.norm(axis) rot = Rotate(quaternion=Quaternion.from_axis_angle( axis, 2*np.pi/N)) rot_eth = AtomsCollection(transformGen(eth, rot, N)) rot_dip = [D[(0, 1)] for D in DipolarTensor.get(rot_eth, sel_i=[0], sel_j=[1])] dip_avg_num = np.average(rot_dip, axis=0) dip_tens = NMRTensor.make_dipolar(eth, 0, 1, rotation_axis=axis) dip_avg_tens = dip_tens.data self.assertTrue(np.allclose(dip_avg_num, dip_avg_tens)) # Test eigenvectors evecs = dip_tens.eigenvectors self.assertTrue(np.allclose(np.dot(evecs.T, evecs), np.eye(3)))
def extract(s, selection, center, quaternion, scaled): center = np.array(center) if center.shape != (3,): raise ValueError('Invalid center passed to Rotate.') if quaternion is None: quaternion = Quaternion() sT = s.copy() if not scaled: pos = sT.get_positions() else: pos = sT.get_scaled_positions() pos -= center pos[selection.indices] = quaternion \ .rotate(pos[selection.indices].T).T pos += center if not scaled: sT.set_positions(pos) else: sT.set_scaled_positions(pos) return sT
def draw_axes(self): from ase.quaternions import Quaternion q = Quaternion().from_matrix(self.axes) axes_labels = [ "<span foreground=\"red\" weight=\"bold\">X</span>", "<span foreground=\"green\" weight=\"bold\">Y</span>", "<span foreground=\"blue\" weight=\"bold\">Z</span>" ] axes_length = 15 for i in self.axes[:, 2].argsort(): a = 20 b = self.height - 20 c = int(self.axes[i][0] * axes_length + a) d = int(-self.axes[i][1] * axes_length + b) self.pixmap.draw_line(self.foreground_gc, a, b, c, d) # The axes label layout = self.drawing_area.create_pango_layout(axes_labels[i]) layout.set_markup(axes_labels[i]) lox = int(self.axes[i][0] * 20 + 20\ - layout.get_size()[0] / 2. / pango.SCALE) loy = int(self.height - 20 - self.axes[i][1] * 20\ - layout.get_size()[1] / 2. / pango.SCALE) self.pixmap.draw_layout(self.foreground_gc, lox, loy, layout)
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))
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 _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]
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)
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()
def swing_twist_decomp(quat, axis): """Perform a Swing*Twist decomposition of a Quaternion. This splits the quaternion in two: one containing the rotation around axis (Twist), the other containing the rotation around a vector parallel to axis (Swing). Returns two quaternions: Swing, Twist. """ # Current rotation axis ra = quat.q[1:] # Ensure that axis is normalised axis_norm = axis / np.linalg.norm(axis) # Projection of ra along the given axis p = np.dot(ra, axis_norm) * axis_norm # Create Twist qin = [quat.q[0], p[0], p[1], p[2]] twist = Quaternion(qin / np.linalg.norm(qin)) # And Swing swing = quat * twist.conjugate() return swing, twist
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)
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)
def draw_axes(self): from ase.quaternions import Quaternion q = Quaternion().from_matrix(self.axes) L = np.zeros((10, 2, 3)) L[:3, 1] = self.axes * 15 L[3:5] = self.axes[0] * 20 L[5:7] = self.axes[1] * 20 L[7:] = self.axes[2] * 20 L[3:, :, :2] += (((-4, -5), (4, 5)), ((-4, 5), (4, -5)), ((-4, 5), (0, 0)), ((-4, -5), (4, 5)), ((-4, 5), (4, 5)), ((4, 5), (-4, -5)), ((-4, -5), (4, -5))) L = L.round().astype(int) L[:, :, 0] += 20 L[:, :, 1] = self.height - 20 - L[:, :, 1] line = self.pixmap.draw_line colors = ([self.black_gc] * 3 + [self.red] * 2 + [self.green] * 2 + [self.blue] * 3) for i in L[:, 1, 2].argsort(): (a, b), (c, d) = L[i, :, :2] line(colors[i], a, b, c, d)
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)
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)))
def initialize(self, images, filenames=None, init_magmom=False): self.natoms = len(images[0]) self.nimages = len(images) if hasattr(images[0], 'get_shapes'): self.shapes = images[0].get_shapes() self.Q = [] else: self.shapes = None if filenames is None: filenames = [None] * self.nimages self.filenames = filenames self.P = np.empty((self.nimages, self.natoms, 3)) self.V = np.empty((self.nimages, self.natoms, 3)) self.E = np.empty(self.nimages) self.K = np.empty(self.nimages) self.F = np.empty((self.nimages, self.natoms, 3)) self.M = np.empty((self.nimages, self.natoms)) self.T = np.empty((self.nimages, self.natoms), int) self.A = np.empty((self.nimages, 3, 3)) self.Z = images[0].get_atomic_numbers() self.pbc = images[0].get_pbc() self.covalent_radii = covalent_radii config = read_defaults() if config['covalent_radii'] is not None: for data in config['covalent_radii']: self.covalent_radii[data[0]] = data[1] warning = False for i, atoms in enumerate(images): natomsi = len(atoms) if (natomsi != self.natoms or (atoms.get_atomic_numbers() != self.Z).any()): raise RuntimeError('Can not handle different images with ' + 'different numbers of atoms or different ' + 'kinds of atoms!') self.P[i] = atoms.get_positions() self.V[i] = atoms.get_velocities() if hasattr(self, 'Q'): for q in atoms.get_quaternions(): self.Q.append(Quaternion(q)) self.A[i] = atoms.get_cell() if (atoms.get_pbc() != self.pbc).any(): warning = True try: self.E[i] = atoms.get_potential_energy() except RuntimeError: self.E[i] = np.nan self.K[i] = atoms.get_kinetic_energy() try: self.F[i] = atoms.get_forces(apply_constraint=False) except RuntimeError: self.F[i] = np.nan try: if init_magmom: self.M[i] = atoms.get_initial_magnetic_moments() else: self.M[i] = atoms.get_magnetic_moments() except (RuntimeError, AttributeError): self.M[i] = atoms.get_initial_magnetic_moments() # added support for tags try: self.T[i] = atoms.get_tags() except RuntimeError: self.T[i] = 0 if warning: print('WARNING: Not all images have the same bondary conditions!') self.selected = np.zeros(self.natoms, bool) self.selected_ordered = [] self.atoms_to_rotate_0 = np.zeros(self.natoms, bool) self.visible = np.ones(self.natoms, bool) self.nselected = 0 self.set_dynamic(constraints=images[0].constraints) self.repeat = np.ones(3, int) self.set_radii(config['radii_scale'])
def quaternion(self): if self._quat is None: self._quat = _evecs_2_quat([self._evecs])[0] return Quaternion(self._quat.q)
# 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)