计算化学公社

标题: 求助:分子重组能较小但 RMSD 值偏大,这种情况是否合理? [打印本页]

作者
Author:
gonglijing    时间: 2026-1-31 21:09
标题: 求助:分子重组能较小但 RMSD 值偏大,这种情况是否合理?
本帖最后由 gonglijing 于 2026-2-1 08:24 编辑

想向各位请教一个问题:我计算了分子的重组能,同时通过 VMD 软件计算 RMSD 以表征两个结构间的几何偏差,得到具体数据如下:空穴重组能 = 0.185,对应 RMSD=1.1764;电子重组能 = 0.085,对应 RMSD=4.3345。对应空穴重组能和电子重组能的两个结构的 RMSD 几何偏差图分别见下面左图和右图。
目前发现重组能数值偏小,但对应的 RMSD 值却显著偏大,即两结构间的几何偏差较大,想请教各位该结果是否合理?若存在问题,该从哪些方面排查解决(跑了两边计算,数据提取已核对过,没问题)?恳请指点,谢谢!



作者
Author:
wal    时间: 2026-1-31 21:36
感觉没对齐
作者
Author:
gonglijing    时间: 2026-1-31 22:15
wal 发表于 2026-1-31 21:36
感觉没对齐

您好,点击align了,难道还有其他对其的方式吗?
作者
Author:
snljty2    时间: 2026-1-31 23:58
本帖最后由 snljty2 于 2026-2-1 09:47 编辑
gonglijing 发表于 2026-1-31 22:15
您好,点击align了,难道还有其他对其的方式吗?

VMD的align对齐是考虑原子顺序打乱的,有可能会收敛到一些局部极小值。如果你两个分子的原子顺序完全一致,可以手写一个对齐,比如这个Python小脚本(需要先用VMD把两个结构存成.xyz格式,然后执行下面命令):
  1. python xxx.py structure1.xyz structure2.xyz
复制代码

这里面没考虑只选择一部分原子对齐的功能,需要的话可以自己改一下。
  1. #!/usr/bin/env python3

  2. import os
  3. import sys
  4. import numpy as np
  5. from numpy.typing import NDArray

  6. def K_cross_mat(vec: NDArray[np.double]) -> NDArray[np.double]: # cross matrix of a 3d vector
  7.     return np.array([[0., - vec[2], vec[1]], [vec[2], 0., - vec[0]], [- vec[1], vec[0], 0.]], dtype=np.double)

  8. def L_quat_mat(q: NDArray[np.double]) -> NDArray[np.double]: # left-multiply matrix of a quaternion
  9.     return np.block([[q[0], - q[1:4][np.newaxis, :]], [q[1:4][:, np.newaxis], q[0] * np.identity(3, dtype=np.double) + \
  10.         K_cross_mat(q[1:4])]])

  11. def R_quat_mat(q: NDArray[np.double]) -> NDArray[np.double]: # right-multiply matrix of a quaternion
  12.     return np.block([[q[0], - q[1:4][np.newaxis, :]], [q[1:4][:, np.newaxis], q[0] * np.identity(3, dtype=np.double) - \
  13.         K_cross_mat(q[1:4])]])

  14. def quat_rot_mat(q: NDArray[np.double]) -> NDArray[np.double]: # 4x4 rotate matrix of a quaternion
  15.     return L_quat_mat(q) @ R_quat_mat(q).T

  16. def get_rotation_matrix(coord1: NDArray[np.double], coord2: NDArray[np.double], allow_reflection: bool=False):
  17.     assert coord1.shape == coord2.shape and coord1.shape[1] == 3
  18.     H = coord1.T @ coord2
  19.     U, Sigma, Vt = np.linalg.svd(H)
  20.     R = Vt.T @ U.T
  21.     if not allow_reflection:
  22.         # only half of O(3) is SO(3)
  23.         if np.linalg.det(R) < 0.:
  24.             Vt[-1, :] = - Vt[-1, :]
  25.             R = Vt.T @ U.T
  26.     return R

  27. def get_rot_quaternion(coord1: NDArray[np.double], coord2: NDArray[np.double]) -> NDArray[np.double]:
  28.     assert coord1.shape == coord2.shape and coord1.shape[1] == 3
  29.     H = coord1.T @ coord2

  30.     Delta = np.array([H[1, 2] - H[2, 1], H[2, 0] - H[0, 2], H[0, 1] - H[1, 0]], dtype=np.double)
  31.     tr_H = np.trace(H)

  32.     N = np.block([[tr_H, Delta[np.newaxis, :]], [Delta[:, np.newaxis], H + H.T - tr_H * np.identity(3, dtype=np.double)]])

  33.     eig_val, eig_vec = np.linalg.eigh(N)

  34.     q = eig_vec[:, -1].copy()
  35.     if q[0] < 0.: q = - q # SU(2) has a 2->1 mapping to SO(3)

  36.     return q

  37. def rotate_by_rotation_matrix(coord1: NDArray[np.double], coord2: NDArray[np.double], allow_reflection: bool=False):
  38.     assert coord1.shape == coord2.shape and coord1.shape[1] == 3
  39.     coord1 -= coord1.mean(axis=0)
  40.     coord2 -= coord2.mean(axis=0)
  41.     R = get_rotation_matrix(coord1, coord2, allow_reflection=allow_reflection)
  42.     coord1 @= R.T

  43. def rotate_by_quaternion(coord1: NDArray[np.double], coord2: NDArray[np.double]):
  44.     assert coord1.shape == coord2.shape and coord1.shape[1] == 3
  45.     coord1 -= coord1.mean(axis=0)
  46.     coord2 -= coord2.mean(axis=0)
  47.     q = get_rot_quaternion(coord1, coord2)
  48.     coord1 = ((quat_rot_mat(q) @ (np.hstack([np.zeros((len(coord1), 1), dtype=np.double), coord1])).T).T)[:, 1:]

  49. def align_molecules(ifilename1: str, ifilename2: str, imethod: int=2, allow_reflection: bool=False):
  50.     # 1 for Kabsch, 2 for quaternion
  51.     if imethod == 1:
  52.         align_molecules_Kabsch(ifilename1, ifilename2, allow_reflection=allow_reflection)
  53.     elif imethod == 2:
  54.         if allow_reflection:
  55.             raise ValueError("Quaternion does not allow reflection.")
  56.         align_moleculs_quaternion(ifilename1, ifilename2)
  57.     else:
  58.         raise ValueError(f"Unsupported imethod: {imethod:d}")

  59. def align_molecules_Kabsch(ifilename1: str, ifilename2: str, allow_reflection: bool=False):
  60.     ifilename1_split = os.path.splitext(ifilename1)
  61.     ifilename2_split = os.path.splitext(ifilename2)
  62.     assert ifilename1_split[1] == ".xyz" and ifilename2_split[1] == ".xyz"
  63.     elements = np.loadtxt(ifilename1, unpack=True, usecols=0, skiprows=2, dtype=np.dtype("<U2"))
  64.     coord1 = np.loadtxt(ifilename1, usecols=(1, 2, 3), skiprows=2, dtype=np.double)
  65.     coord2 = np.loadtxt(ifilename2, usecols=(1, 2, 3), skiprows=2, dtype=np.double)
  66.     rotate_by_rotation_matrix(coord1, coord2, allow_reflection=allow_reflection)
  67.     ofilename1 = ifilename1_split[0] + "_aligned.xyz"
  68.     ofilename2 = ifilename2_split[0] + "_aligned.xyz"
  69.     with open(ofilename1, "w") as ofile1, open(ofilename2, "w") as ofile2:
  70.         print(len(elements), file=ofile1)
  71.         print(len(elements), file=ofile2)
  72.         print(f"aligned from {ifilename1:s}", file=ofile1)
  73.         print(f"aligned from {ifilename2:s}", file=ofile2)
  74.         for iatom in range(len(elements)):
  75.             print(f" {elements[iatom]:<2s}" + ("    {:13.8f}" * 3).format(* coord1[iatom]), file=ofile1)
  76.             print(f" {elements[iatom]:<2s}" + ("    {:13.8f}" * 3).format(* coord2[iatom]), file=ofile2)

  77. def align_molecules_quaternion(ifilename1: str, ifilename2: str):
  78.     ifilename1_split = os.path.splitext(ifilename1)
  79.     ifilename2_split = os.path.splitext(ifilename2)
  80.     assert ifilename1_split[1] == ".xyz" and ifilename2_split[1] == ".xyz"
  81.     elements = np.loadtxt(ifilename1, unpack=True, usecols=0, skiprows=2, dtype=np.dtype("<U2"))
  82.     coord1 = np.loadtxt(ifilename1, usecols=(1, 2, 3), skiprows=2, dtype=np.double)
  83.     coord2 = np.loadtxt(ifilename2, usecols=(1, 2, 3), skiprows=2, dtype=np.double)
  84.     rotate_by_quaternion(coord1, coord2)
  85.     ofilename1 = ifilename1_split[0] + "_aligned.xyz"
  86.     ofilename2 = ifilename2_split[0] + "_aligned.xyz"
  87.     with open(ofilename1, "w") as ofile1, open(ofilename2, "w") as ofile2:
  88.         print(len(elements), file=ofile1)
  89.         print(len(elements), file=ofile2)
  90.         print(f"aligned from {ifilename1:s}", file=ofile1)
  91.         print(f"aligned from {ifilename2:s}", file=ofile2)
  92.         for iatom in range(len(elements)):
  93.             print(f" {elements[iatom]:<2s}" + ("    {:13.8f}" * 3).format(* coord1[iatom]), file=ofile1)
  94.             print(f" {elements[iatom]:<2s}" + ("    {:13.8f}" * 3).format(* coord2[iatom]), file=ofile2)

  95. if __name__ == "__main__":
  96.     ifilename1 = sys.argv[1]
  97.     ifilename2 = sys.argv[2]
  98.     # ifilename1 = "1.xyz"
  99.     # ifilename2 = "2.xyz"
  100.     # align_molecules(ifilename1, ifilename2, imethod=1, allow_reflection=True)
  101.     align_molecules(ifilename1, ifilename2)
  102.    
复制代码

作者
Author:
gonglijing    时间: 2026-2-1 08:19
谢谢,是没对齐,已按您们建议调整好了,再次感谢!




欢迎光临 计算化学公社 (http://bbs.keinsci.com/) Powered by Discuz! X3.3