3

I want to rotate a 5-atom crystal defined by X,Y,Z coordinates of atoms by a random angle. My initial idea was to use an external package to generate a rotation matrix (https://github.com/qobilidop/randrot) and then multiplying this matrix by a vector, which defines the coordinates of a single atom. However, that did not work at all and all the atoms got dispersed. Here's a function I wrote for that purpose:

def rotation():
    crystal = []
    rotmat = np.asarray(randrot.generate(3)) #generates 3x3 rotation matrix
    for x,y,z in zip(new_x, new_y, new_z):
        vec = np.array([x,y,z])
        rot = vec.dot(rotmat)
        for elem in rot:
            crystal.append(elem)
    return np.array(crystal).reshape([5,3])

rotated = rotation()
ax.scatter(rotated[0], rotated[1], rotated[2], marker='.', s=100, color='green')

Here's how it looks (red is the initial placement, green is after rotation):

pyplot

1 Answers1

1

Here is an example code that rotates given 3d points about a randomly generated rotation matrix, rotation matrix creation is taken from another answer.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import math


#  taken from https://stackoverflow.com/questions/6802577/rotation-of-3d-vector
def rotation_matrix(axis, theta):
    """
    Return the rotation matrix associated with counterclockwise rotation about
    the given axis by theta radians.
    """
    axis = np.asarray(axis)
    axis = axis / math.sqrt(np.dot(axis, axis))
    a = math.cos(theta / 2.0)
    b, c, d = -axis * math.sin(theta / 2.0)
    aa, bb, cc, dd = a * a, b * b, c * c, d * d
    bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
    return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
                     [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
                     [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])


#  initial xyz coordinates
xs = [0, 1, 1, 1, 1, -1, -1, -1, -1]
ys = [0, 1, 1, -1, -1, 1, 1, -1, -1]
zs = [0, 1, -1, 1, -1, 1, -1, 1, -1]
atoms_initial = np.array([xs, ys, zs]).T

#  specify rotation matrix parameters
#  let us generate a random axis and angle for rotation
rotation_axis = np.random.uniform(low=0, high=1, size=3)  #  three numbers between 0 and 1
rotation_angle = np.random.uniform(low=0, high=2*np.pi, size=1)  #  random number between 0 and 2pi
print("Rotation axis:{}, rotation angle:{} radians".format(rotation_axis, rotation_angle))

#  create our rotation matrix
rotmat = rotation_matrix(rotation_axis, rotation_angle)

#  apply rotation matrix to our points
atoms_rotated = np.dot(atoms_initial, rotmat)

#  draw
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(atoms_initial[:,0], atoms_initial[:,1], atoms_initial[:,2], marker='.', s=100, color='red')
ax.scatter(atoms_rotated[:,0], atoms_rotated[:,1], atoms_rotated[:,2], marker='.', s=100, color="green")
plt.show()
unlut
  • 2,742
  • 2
  • 11
  • 22
  • @RoryDaulton Sorry, I thought I showed how to work with an arbitrary rotation matrix, I can change example to produce a random rotation matrix if that would be better. – unlut Jan 15 '19 at 14:29
  • @unlut that would certainly come in handy :) – user10392573 Jan 15 '19 at 14:31
  • @user10392573 I changed code accordingly, hope it is better now. – unlut Jan 15 '19 at 14:36
  • thanks, it works great, however I'd like to rotate it "in place" so that the center of the crystal remains in the same place. The next step for me is to move it by a random vector but that should be much easier than rotating it by a random angle. – user10392573 Jan 15 '19 at 15:23