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 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 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 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 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 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 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 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 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 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)
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)