def _displacement_mic(xyz, pairs, box_vectors, orthogonal): """Displacement vector between pairs of points in each frame under the minimum image convention for periodic boundary conditions. The computation follows scheme B.9 in Tukerman, M. "Statistical Mechanics: Theory and Molecular Simulation", 2010. This is a very slow pure python implementation, mostly for testing. """ out = np.empty((xyz.shape[0], pairs.shape[0], 3), dtype=np.float32) for i in range(len(xyz)): bv1, bv2, bv3 = _reduce_box_vectors(box_vectors[i].T) hinv = np.linalg.inv(np.array([bv1, bv2, bv3]).T) for j, (a,b) in enumerate(pairs): r12 = xyz[i,b,:] - xyz[i,a,:] r12 -= bv3*round(r12[2]/bv3[2]); r12 -= bv2*round(r12[1]/bv2[1]); r12 -= bv1*round(r12[0]/bv1[0]); min_disp = r12 dist2 = (r12*r12).sum() if not orthogonal: for ii in range(-1, 2): v1 = bv1*ii for jj in range(-1, 2): v12 = bv2*jj+v1 for kk in range(-1, 2): tmp = r12 + v12 + bv3*kk new_dist2 = (tmp*tmp).sum() if new_dist2 < dist2: dist2 = new_dist2 min_disp = tmp out[i, j] = min_disp return out
def _distance_mic(xyz, pairs, box_vectors, orthogonal): """Distance between pairs of points in each frame under the minimum image convention for periodic boundary conditions. The computation follows scheme B.9 in Tukerman, M. "Statistical Mechanics: Theory and Molecular Simulation", 2010. This is a slow pure python implementation, mostly for testing. """ out = np.empty((xyz.shape[0], pairs.shape[0]), dtype=np.float32) for i in range(len(xyz)): bv1, bv2, bv3 = _reduce_box_vectors(box_vectors[i].T) for j, (a,b) in enumerate(pairs): r12 = xyz[i,b,:] - xyz[i,a,:] r12 -= bv3*round(r12[2]/bv3[2]); r12 -= bv2*round(r12[1]/bv2[1]); r12 -= bv1*round(r12[0]/bv1[0]); dist = np.linalg.norm(r12) if not orthogonal: for ii in range(-1, 2): v1 = bv1*ii for jj in range(-1, 2): v12 = bv2*jj + v1 for kk in range(-1, 2): new_r12 = r12 + v12 + bv3*kk dist = min(dist, np.linalg.norm(new_r12)) out[i, j] = dist return out
def _displacement_mic(xyz, pairs, box_vectors, orthogonal): """Displacement vector between pairs of points in each frame under the minimum image convention for periodic boundary conditions. The computation follows scheme B.9 in Tukerman, M. "Statistical Mechanics: Theory and Molecular Simulation", 2010. This is a very slow pure python implementation, mostly for testing. """ out = np.empty((xyz.shape[0], pairs.shape[0], 3), dtype=np.float32) for i in range(len(xyz)): hinv = np.linalg.inv(box_vectors[i]) bv1, bv2, bv3 = box_vectors[i].T for j, (a,b) in enumerate(pairs): s1 = np.dot(hinv, xyz[i,a,:]) s2 = np.dot(hinv, xyz[i,b,:]) s12 = s2 - s1 s12 = s12 - np.round(s12) disp = np.dot(box_vectors[i], s12) min_disp = disp dist2 = (disp*disp).sum() if not orthogonal: for ii in range(-1, 2): v1 = bv1*ii for jj in range(-1, 2): v12 = bv2*jj+v1 for kk in range(-1, 2): tmp = disp + v12 + bv3*kk new_dist2 = (tmp*tmp).sum() if new_dist2 < dist2: dist2 = new_dist2 min_disp = tmp out[i, j] = min_disp return out
def _displacement_mic(xyz, pairs, box_vectors, orthogonal): """Displacement vector between pairs of points in each frame under the minimum image convention for periodic boundary conditions. The computation follows scheme B.9 in Tukerman, M. "Statistical Mechanics: Theory and Molecular Simulation", 2010. This is a very slow pure python implementation, mostly for testing. """ out = np.empty((xyz.shape[0], pairs.shape[0], 3), dtype=np.float32) for i in range(len(xyz)): hinv = np.linalg.inv(box_vectors[i]) bv1, bv2, bv3 = box_vectors[i].T for j, (a, b) in enumerate(pairs): s1 = np.dot(hinv, xyz[i, a, :]) s2 = np.dot(hinv, xyz[i, b, :]) s12 = s2 - s1 s12 = s12 - np.round(s12) disp = np.dot(box_vectors[i], s12) min_disp = disp dist2 = (disp * disp).sum() if not orthogonal: for ii in range(-1, 2): v1 = bv1 * ii for jj in range(-1, 2): v12 = bv2 * jj + v1 for kk in range(-1, 2): tmp = disp + v12 + bv3 * kk new_dist2 = (tmp * tmp).sum() if new_dist2 < dist2: dist2 = new_dist2 min_disp = tmp out[i, j] = min_disp return out
def compute_distances(traj, atom_pairs, periodic=True, opt=True): """Compute the distances between pairs of atoms in each frame. Parameters ---------- traj : Trajectory An mtraj trajectory. atom_pairs : np.ndarray, shape=(num_pairs, 2), dtype=int Each row gives the indices of two atoms involved in the interaction. periodic : bool, default=True If `periodic` is True and the trajectory contains unitcell information, we will compute distances under the minimum image convention. opt : bool, default=True Use an optimized native library to calculate distances. Our optimized SSE minimum image convention calculation implementation is over 1000x faster than the naive numpy implementation. Returns ------- distances : np.ndarray, shape=(n_frames, num_pairs), dtype=float The distance, in each frame, between each pair of atoms. """ xyz = ensure_type(traj.xyz, dtype=np.float32, ndim=3, name='traj.xyz', shape=(None, None, 3), warn_on_cast=False) pairs = ensure_type(atom_pairs, dtype=np.int32, ndim=2, name='atom_pairs', shape=(None, 2), warn_on_cast=False) if not np.all(np.logical_and(pairs < traj.n_atoms, pairs >= 0)): raise ValueError('atom_pairs must be between 0 and %d' % traj.n_atoms) if len(pairs) == 0: return np.zeros((len(xyz), 0), dtype=np.float32) if periodic and traj._have_unitcell: out = np.empty((xyz.shape[0], pairs.shape[0]), dtype=np.float32) a = traj.unitcell_lengths[0][0] * 10.0 b = traj.unitcell_lengths[0][1] * 10.0 c = traj.unitcell_lengths[0][2] * 10.0 cosalpha = math.cos(math.radians(traj.unitcell_angles[0][0])) cosbeta = math.cos(math.radians(traj.unitcell_angles[0][1])) cosgamma = math.cos(math.radians(traj.unitcell_angles[0][2])) for i in range(len(xyz)): for j, (ta, tb) in enumerate(pairs): x1 = xyz[0, ta, 0] * 10.0 y1 = xyz[0, ta, 1] * 10.0 z1 = xyz[0, ta, 2] * 10.0 x2 = xyz[0, tb, 0] * 10.0 y2 = xyz[0, tb, 1] * 10.0 z2 = xyz[0, tb, 2] * 10.0 #get min dist from all images dist = cal_dist(x1, y1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma) dist = min( dist, cal_dist(x1 + 1, y1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) dist = min( dist, cal_dist(x1 - 1, y1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) dist = min( dist, cal_dist(x1, y1 + 1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) dist = min( dist, cal_dist(x1, y1 - 1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) dist = min( dist, cal_dist(x1 + 1, y1 + 1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) dist = min( dist, cal_dist(x1 + 1, y1 - 1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) dist = min( dist, cal_dist(x1 - 1, y1 + 1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) dist = min( dist, cal_dist(x1 - 1, y1 - 1, z1, x2, y2, z2, a, b, c, cosalpha, cosbeta, cosgamma)) #print("pair:", ta, tb) #print("final dist:",dist) out[i, j] = dist return out else: out = np.empty((xyz.shape[0], pairs.shape[0]), dtype=np.float32) return out