def align_geometries_from_files(self, structure, reference_structure): self.structure_atoms, self.structure = kb.get_coordinates(structure) self.reference_structure_atoms, self.reference_structure = kb.get_coordinates(reference_structure) self.structure_centroid = kb.centroid(self.structure) self.reference_structure_centroid = kb.centroid(self.reference_structure) self.structure -= self.structure_centroid self.reference_structure -= self.reference_structure_centroid self.structure_aligned, self.rmsd_error = kb.kabsch(self.structure, self.reference_structure, output=True) self.structure_aligned += self.reference_structure_centroid
def align(current: np.ndarray, reference: np.ndarray): """Align two geometries using Kabsch algorithm.""" # Getting structures to be fitted geo = current.copy() ref_geo = reference.copy() # Computation of the centroids geo_centroid = kb.centroid(geo) ref_centroid = kb.centroid(ref_geo) # Translation of structures geo -= geo_centroid ref_geo -= ref_centroid aligned, rmsd_error = kb.kabsch(geo, ref_geo, output=True) return aligned, rmsd_error
def align_one(work_frag, ref_frag, save_matrix=False, mat_path=None, save_center=False, frameid=None): """Align only one geometry to a reference geometry. ref_frag : dict/Fragment Information of the reference fragment used for the aligning. save_matrices : bool Weather to save the `rot_matrices` and the `centers` to files. mat_path : str Path where to save the matrices when `save_matrices` is set to True. """ if isinstance(work_frag, Fragment): atoms = work_frag.atoms work_geo = work_frag.coords elif isinstance(work_frag, dict): is_ensemble = False atoms = work_frag['atoms'] work_geo = work_frag['coords'] else: raise TypeError( """Geometries to be aligned must be given either as a dict(atoms, geos)""" """ or as an instance of the Ensemble object.""") # Check ref_frag types if isinstance(ref_frag, Fragment): ref_atoms = ref_frag.atoms ref_geo = ref_frag.coords elif isinstance(ref_frag, dict): ref_atoms = ref_frag['atoms'] ref_geo = ref_frag['coords'] if isinstance(ref_geo, list): tmp = np.array(ref_geo) if tmp.shape[1] != 3: raise ValueError( 'Expecting one single geometry, with shape (natoms, 3).') ref_center, rot_matrix = perform_kabsch(ref_geo, work_geo, centered=False) work_center = centroid(work_geo) new_geo = _core_align(rot_matrix, ref_center, work_geo, work_center) # Replace values if not is_ensemble: work_frag['coords'][:] = new_geo.copy() else: work_frag.coords[:] = new_geo.copy() # Save matrices if save_matrix: if mat_path is None: mat_path = os.getcwd() rot_path = os.path.join(mat_path, 'rot_matrices') if not os.path.isdir(rot_path): os.mkdir(rot_path) if frameid is None: np.savetxt(os.path.join(rot_path, 'rot_matri.txt'), rot_matrix) else: np.savetxt(os.path.join(rot_path, 'rot_matrix_%d.txt' % frameid), rot_matrix) if save_center: np.savetxt(os.path.join(mat_path, 'ref_center.txt'), ref_center)
def centroid_distance(ref_geo, work_geo): """Compute the distance between the centroids of two geometries. Parameters ---------- ref_geo : np.ndarray Geometry of the reference fragment/molecule. work_geo : np.ndarray Geometry of the working fragment/molecule. Returns ------- distance : float Distance between the two centroids. """ ref_centroid = centroid(ref_geo) work_centroid = centroid(work_geo) return np.linalg.norm(ref_centroid - work_centroid)
def _align_from_matrices(geos_ensemble, mat_path=None): """ Parameters ---------- geos_ensemble : dict/Ensemble Collection of frames with atoms, coordinates (and charges) of a fragment. mat_path : str Path where to save the matrices when `save_matrices` is set to True. """ # Check path if mat_path is None: mat_path = os.getcwd() rot_path = os.path.join(mat_path, 'rot_matrices') if not os.path.isdir(rot_path): raise ValueError('Missing `rot_matrices` folder') # Check geos_ensemble types if isinstance(geos_ensemble, Ensemble): is_ensemble = True latoms = list() lcoords = list() lframeids = list() for iframe in range(geos_ensemble.nframes): ifrag = geos_ensemble.fragments[iframe] latoms.append(ifrag.atoms) lcoords.append(ifrag.coords) lframeids.append(ifrag.frameid) elif isinstance(geos_ensemble, dict): is_ensemble = False latoms = geos_ensemble['atoms'] lcoords = geos_ensemble['coords'] lframeids = geos_ensemble['frameids'] else: raise TypeError( """Geometries to be aligned must be given either as a dict(atoms, geos)""" """ or as an instance of the Ensemble object.""") # Loop over frames nframes = len(latoms) if nframes != len(lcoords): raise ValueError('Number of atoms and coordinates does not match.') ref_center = np.loadtxt(os.path.join(mat_path, 'ref_center.txt')) for iframe in range(nframes): # Read matrices frameid = lframeids[iframe] work_geo = lcoords[iframe] work_center = centroid(work_geo) rot_matrix = np.loadtxt( os.path.join(rot_path, 'rot_matrix_%d.txt' % frameid)) new_geo = _core_align(rot_matrix, ref_center, work_geo, work_center) # Replace values if not is_ensemble: geos_ensemble['coords'][iframe][:] = new_geo.copy() else: geos_ensemble.fragments[iframe].coords[:] = new_geo.copy()
def align(self, structure, reference_structure): """The Kabsch algorithm http://en.wikipedia.org/wiki/Kabsch_algorithm The algorithm starts with two sets of paired points of structure P and reference_structure Q. It returns a rotation matrix U prividing the best fit between the two structures, a measure of that fit RMSD and a new geometry of structure P aligned to reference_structure Q. Each vector set P and Q is represented as an NxD matrix, where D is the dimension of the space and N is the number of atoms The algorithm works in three steps: - a translation of P and Q - the computation of a covariance matrix C - computation of the optimal rotation matrix U The optimal rotation matrix U is then used to rotate P unto Q so the RMSD can be caculated from a straight forward fashion. Arguments ---------- structure : ndarray 2D ndarray [Number_of_atoms, XYZ coordinates] contains a geometry of structure. reference_structure : ndarray 2D ndarray [Number_of_atoms, XYZ coordinates] contains a geometry of reference_structure. Returns ---------- structure_aligned : ndarray 2D ndarray shape(Number_of_atoms, XYZ coordinates) contains a transformed coordinates of 'structure' aligned to 'reference_structure' U : ndarray 3D ndarray the ebst rotation matrix. rmsd : float64 RMSD between 'structure_aligned' and 'reference_structure'. structure_centroid : ndarray 1D numpy array containing the coordinates of the 'structure' centroid. reference_structure_centroid : ndarray 1D array containing the coordinates of the 'reference_structure' centroid. """ # Getting structures to be fitted self.structure = structure.copy() self.reference_structure = reference_structure.copy() # Computation of the centroids self.structure_centroid = kb.centroid(self.structure) self.reference_structure_centroid = kb.centroid(self.reference_structure) # Translation of structures self.structure -= self.structure_centroid self.reference_structure -= self.reference_structure_centroid # Get initial RMSD # Computation of the covariance matrix self.covariation_matrix = np.dot(np.transpose(self.structure), self.reference_structure) # Computation of the optimal rotation matrix # This can be done using singular value decomposition (SVD) # Getting the sign of the det(V)*(W) to decide # whether we need to correct our rotation matrix to ensure a # right-handed coordinate system. # And finally calculating the optimal rotation matrix U # see http://en.wikipedia.org/wiki/Kabsch_algorithm V, S, W = np.linalg.svd(self.covariation_matrix) if (np.linalg.det(V) * np.linalg.det(W)) < 0.0: S[-1] = -S[-1] V[:, -1] = -V[:, -1] # Create Rotation matrix U U = np.dot(V, W) # Rotate P self.structure_aligned = np.dot(self.structure, U) return np.asarray([self.structure_aligned, U, self.rmsd(self.structure_aligned, self.reference_structure), self.structure_centroid, self.reference_structure_centroid])
def _align_from_scratch(geos_ensemble, ref_frag, save_matrices=False, mat_path=None): """Align a set of geometries comparing to a reference geometry. Parameters ---------- geos_ensemble : dict/Ensemble Collection of frames with atoms, coordinates (and charges) of a fragment. ref_frag : dict/Fragment Information of the reference fragment used for the aligning. save_matrices : bool Weather to save the `rot_matrices` and the `centers` to files. mat_path : str Path where to save the matrices when `save_matrices` is set to True. """ # Check geos_ensemble types if isinstance(geos_ensemble, Ensemble): is_ensemble = True latoms = list() lcoords = list() for iframe in range(geos_ensemble.nframes): ifrag = geos_ensemble.fragments[iframe] latoms.append(ifrag.atoms) lcoords.append(ifrag.coords) elif isinstance(geos_ensemble, dict): is_ensemble = False latoms = geos_ensemble['atoms'] lcoords = geos_ensemble['coords'] else: raise TypeError( """Geometries to be aligned must be given either as a dict(atoms, geos)""" """ or as an instance of the Ensemble object.""") # Check ref_frag types if isinstance(ref_frag, Fragment): ref_atoms = ref_frag.atoms ref_geo = ref_frag.coords elif isinstance(ref_frag, dict): ref_atoms = np.array(ref_frag['atoms']) ref_geo = ref_frag['coords'] if isinstance(ref_geo, list): tmp = np.array(ref_geo) if tmp.shape[1] != 3: raise ValueError( 'Expecting one single geometry, with shape (natoms, 3).') # Loop over frames nframes = len(latoms) if nframes != len(lcoords): raise ValueError('Number of atoms and coordinates does not match.') for iframe in range(nframes): # Check with respect to the ref_fragment if not (ref_atoms == latoms[iframe]).all(): raise ValueError( 'Atoms of frame %d do not correspond to the reference geometry' % iframe) work_geo = lcoords[iframe] ref_center, rot_matrix = perform_kabsch(ref_geo, work_geo, centered=False) work_center = centroid(work_geo) new_geo = _core_align(rot_matrix, ref_center, work_geo, work_center) # Save matrices if save_matrices: if mat_path is None: mat_path = os.getcwd() rot_path = os.path.join(mat_path, 'rot_matrices') if not os.path.isdir(rot_path): os.mkdir(rot_path) if iframe == 0: np.savetxt(os.path.join(mat_path, 'ref_center.txt'), ref_center) np.savetxt(os.path.join(rot_path, 'rot_matrix_%d.txt' % iframe), rot_matrix) # Replace values if not is_ensemble: geos_ensemble['coords'][iframe][:] = new_geo.copy() else: geos_ensemble.frag[iframe].coords[:] = new_geo.copy()