在笛卡尔坐标中对齐两个3D对象

时间:2019-07-17 13:13:56

标签: python

我有两个与.xyz文件相同分子的副本。这意味着每个原子具有X,Y和Z坐标。但是,您可以旋转分子并为每个原子获得不同的坐标,尽管相对位置相同并且分子保持相同。我想使用三个原子作为参考点对齐两个分子。但是,我正在努力使两个分子完全对齐。

首先,我通过翻译将两个分子排列成一个原子。然后,我使用elsewhere中所述的旋转矩阵进行了两次后续旋转。由于某些原因,我需要取两个向量的叉积的负值,并使用正弦曲线而不是余弦曲线来使两个结构完全对齐(我经过反复试验发现了这一点)。

对于第二个旋转,我将两个要对齐的向量投影到由旋转向量定义的平面上。这是必要的,因为我不想沿着两个向量的叉积旋转以对齐,因为那样会使分子的其余部分偏离。相反,我沿着两个已经对齐的向量旋转。该项目使我能够找到两个向量之间的平面角度,从而找到必要的旋转方向。

但是,此代码无法正确对齐两个分子。

“ group1 [0]”包含要在列表中对齐的三个原子的XYZ坐标。同样对于“ group2 [0]”和结构2。

#Point 1: align the functional groups to the origin

O1 = np.array(coords1[group1[0][0]])
O2 = np.array(coords2[group2[0][0]])

mat_2 = np.zeros((len(atoms2), 3))

for ind, c in enumerate(coords1):
    coords1[ind] = np.array(c) - O1

for ind, c in enumerate(coords2):
    coords2[ind] = np.array(c) - O2
    mat_2[ind] = coords2[ind]

#Point 2: align according to a first vector
v1 = np.array(coords1[group1[0][1]])#Since atom 1 is the origin, the coordinates is the vector already
v2 = np.array(coords2[group2[0][1]])#Since atom 1 is the origin, the coordinates is the vector already

v1 = v1/np.linalg.norm(v1)
v2 = v2/np.linalg.norm(v2)

#Let v be the axis of rotation
v = -np.cross(v1, v2)#why do I need a minus here?

if np.linalg.norm(v) != 0:
    a = np.arccos(np.dot(v1, v2)/(np.linalg.norm(v1)*np.linalg.norm(v2)))
    #c = np.dot(v1, v2)*np.cos(a)

    c = np.dot(v1, v2)*np.sin(a)#The internet says cos, but this works perfectly

    vx = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])

    rot_mat = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + vx + vx.dot(vx)*(1-c)/(1-c**2)
    mat_2 = np.array(mat_2)

    R_mat_rot = np.matmul(rot_mat, mat_2.T).T
else:
    exit(0) 

coords3 = R_mat_rot.copy()

#I get exactly what I want up until here

#Point 3: Rotate along atom2-atom1 (v1) to align the third atom
v = -v1.copy()

v2 = np.array(coords3[group2[0][2]]) - np.array(coords3[group2[0][0]]) #Since atom 1 is the origin, the coordinates is the vector already
v2 = v2/np.linalg.norm(v2)

v1 = np.array(coords1[group1[0][2]]) - np.array(coords1[group1[0][0]]) #Since atom 1 is the origin, the coordinates is the vector already
v1 = v1/np.linalg.norm(v1)

if np.linalg.norm(v) != 0:
    #consider v to be the vector normal to a plane
    #we want the projection of v1 and v2 unto that plane
    vp1 = np.cross(v, np.cross(v1, v)) - np.array(coords1[group1[0][0]])
    vp1 = vp1/np.linalg.norm(vp1)

    vp2 = np.cross(v, np.cross(v2, v)) - np.array(coords3[group2[0][0]])
    vp2 = vp2/np.linalg.norm(vp2)

    #we find the angle between those vectors on the plane
    a = np.arccos(np.dot(vp1, vp2))/(np.linalg.norm(vp1)*np.linalg.norm(vp2))

    #rotation of that amount
    c = np.dot(v1, v2)*np.cos(a)
    vx = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])

    rot_mat = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + vx + np.dot(vx, vx)*(1-c)/(1-c**2)

    R_mat_rot = np.matmul(rot_mat, coords3.T).T

coords4 = R_mat_rot.copy()#Final coordinates

0 个答案:

没有答案