65 lines
2.4 KiB
Python
65 lines
2.4 KiB
Python
import numpy as np
|
|
|
|
def quadrupole_tensor(x, y, z, m=1):
|
|
Qxx = np.sum(m*x**2)
|
|
Qyy = np.sum(m*y**2)
|
|
Qzz = np.sum(m*z**2)
|
|
Qxy = Qyx = np.sum(m*x*y)
|
|
Qxz = Qzx = np.sum(m*x*z)
|
|
Qzy = Qyz = np.sum(m*z*y)
|
|
return np.array([[Qxx, Qxy, Qxz],
|
|
[Qyx, Qyy, Qyz],
|
|
[Qzx, Qzy, Qzz]])
|
|
|
|
def rotation_matrix_from_eigenvectors(eigenvectors, eigenvalues):
|
|
"""
|
|
Calculates a rotation matrix such that the eigenvector with the largest
|
|
eigenvalue points in the x-direction, the eigenvector with the middle
|
|
eigenvalue points in the y-direction, and so on.
|
|
"""
|
|
return np.array([eigenvectors[:,i] for i in np.argsort(eigenvalues)[::-1]])
|
|
|
|
def generate_ellipsoid(n, power, a, b, c, euler_1, euler_2, euler_3, seed=None):
|
|
from scipy.spatial.transform import Rotation
|
|
if not (a >= b >= c):
|
|
raise ValueError('Requires a >= b >= c')
|
|
b_over_a = b/a
|
|
c_over_a = c/a
|
|
np.random.seed(seed)
|
|
|
|
# Create a sphere of particles with radius a.
|
|
r = (np.random.rand(n)**power)*a
|
|
theta = np.arccos(np.random.rand(n)*2-1)
|
|
phi = np.random.rand(n)*2*np.pi
|
|
x = r*np.cos(phi)*np.sin(theta)
|
|
y = r*np.sin(phi)*np.sin(theta)
|
|
z = r* np.cos(theta)
|
|
|
|
# Scale the intermediate and minor axes
|
|
y *= b_over_a
|
|
z *= c_over_a
|
|
|
|
# Rotate
|
|
rotation = Rotation.from_euler('zxz', [euler_1, euler_2, euler_3])
|
|
return rotation.apply(np.stack((x,y,z), axis=1))
|
|
|
|
if __name__ == '__main__': # Test if the above routines
|
|
n = 65536
|
|
power = 1.5
|
|
a, b, c = 2.3, 0.92, 0.345 # or sorted(np.random.rand(3))[::-1]
|
|
angles = 0.43, 1.81, 0.32 # or np.random.rand(3)*2*np.pi
|
|
|
|
X = generate_ellipsoid(n, power, a, b, c, *angles, seed=None)
|
|
Q = quadrupole_tensor(*X.T)
|
|
eigenvalues, eigenvectors = np.linalg.eig(Q)
|
|
i = np.argsort(eigenvalues)
|
|
print('measured b/a = %.4f (expected %.4f)' % (np.sqrt(eigenvalues[i[1]]/eigenvalues[i[2]]), b/a))
|
|
print('measured c/a = %.4f (expected %.4f)' % (np.sqrt(eigenvalues[i[0]]/eigenvalues[i[2]]), c/a))
|
|
R = rotation_matrix_from_eigenvectors(eigenvectors, eigenvalues)
|
|
print('Rotating vectors and recalculating quadrupole tensor...')
|
|
X_new = (R @ X.T).T
|
|
Q_new = quadrupole_tensor(*X_new.T)
|
|
print('%11.4e %11.4e %11.4e' % tuple(Q_new[0,:]))
|
|
print('%11.4e %11.4e %11.4e' % tuple(Q_new[1,:]))
|
|
print('%11.4e %11.4e %11.4e' % tuple(Q_new[2,:]))
|
|
print('... it should be almost diagonal.')
|