def setUp(self): self.x = array([[51.65, -1.90, 50.07], [50.40, -1.23, 50.65], [50.68, -0.04, 51.54], [50.22, -0.02, 52.85]]) self.y = array([[51.30, -2.99, 46.54], [51.09, -1.88, 47.58], [52.36, -1.20, 48.03], [52.71, -1.18, 49.38]]) self.sup = QCPSuperimposer() self.sup.set(self.x, self.y)
def align(self, structure, transform=True): """Align the input structure onto the reference structure. Parameters ---------- transform: bool, optional If True (default), apply the rotation/translation that minimizes the RMSD between the two structures to the input structure. If False, the structure is not modified but the optimal RMSD will still be calculated. """ self.rms = None # clear before aligning coord = self.get_guide_coord_from_structure(structure) if len(coord) < self.window_size * 2: n_atoms = len(coord) msg = (f"Too few atoms in the mobile structure ({n_atoms}). " "Try reducing the window_size parameter.") raise PDBException(msg) # Run CEAlign # CEAlign returns the best N paths, where each path is a pair of lists # with aligned atom indices. Paths are not guaranteed to be unique. paths = run_cealign(self.refcoord, coord, self.window_size, self.max_gap) unique_paths = {(tuple(pA), tuple(pB)) for pA, pB in paths} # Iterate over unique paths and find the one that gives the lowest # corresponding RMSD. Use QCP to align the molecules. best_rmsd, best_u = 1e6, None for u_path in unique_paths: idxA, idxB = u_path coordsA = np.array([self.refcoord[i] for i in idxA]) coordsB = np.array([coord[i] for i in idxB]) aln = QCPSuperimposer() aln.set(coordsA, coordsB) aln.run() if aln.rms < best_rmsd: best_rmsd = aln.rms best_u = (aln.rot, aln.tran) if best_u is None: raise RuntimeError("Failed to find a suitable alignment.") if transform: # Transform all atoms rotmtx, trvec = best_u for chain in structure.get_chains(): for resid in chain.get_unpacked_list(): for atom in resid.get_unpacked_list(): atom.transform(rotmtx, trvec) self.rms = best_rmsd
def calc_rmsd(self, source_atoms, target_atoms): from math import sqrt import numpy as np from Bio.PDB.QCPSuperimposer import QCPSuperimposer if len(source_atoms) != len(target_atoms): return -1 source_arr = [] for atom in source_atoms: xyz = [atom.coord[0], atom.coord[1], atom.coord[2]] source_arr.append(xyz) source_arr = np.array(source_arr) target_arr = [] for atom in target_atoms: xyz = [atom.coord[0], atom.coord[1], atom.coord[2]] target_arr.append(xyz) target_arr = np.array(target_arr) sup = QCPSuperimposer() sup.set(source_arr, target_arr) sup.run() return sup.get_rms()
"C module in Bio.QCPSuperimposer not compiled") # start with two coordinate sets (Nx3 arrays - Float0) x = array([[51.65, -1.90, 50.07], [50.40, -1.23, 50.65], [50.68, -0.04, 51.54], [50.22, -0.02, 52.85]], 'f') y = array([[51.30, -2.99, 46.54], [51.09, -1.88, 47.58], [52.36, -1.20, 48.03], [52.71, -1.18, 49.38]], 'f') sup = QCPSuperimposer() # set the coords # y will be rotated and translated on x sup.set(x, y) # do the qcp fit sup.run() # get the rmsd rms = sup.get_rms() # get rotation (right multiplying!) and the translation rot, tran = sup.get_rotran() # rotate y on x manually
"Install NumPy if you want to use Bio.QCPSuperimposer.") from Bio import BiopythonExperimentalWarning with warnings.catch_warnings(): warnings.simplefilter('ignore', BiopythonExperimentalWarning) from Bio.PDB.QCPSuperimposer import QCPSuperimposer # start with two coordinate sets (Nx3 arrays - Float0) x = array([[51.65, -1.90, 50.07], [50.40, -1.23, 50.65], [50.68, -0.04, 51.54], [50.22, -0.02, 52.85]], 'f') y = array([[51.30, -2.99, 46.54], [51.09, -1.88, 47.58], [52.36, -1.20, 48.03], [52.71, -1.18, 49.38]], 'f') sup = QCPSuperimposer() # set the coords # y will be rotated and translated on x sup.set(x, y) # do the qcp fit sup.run() # get the rmsd rms = sup.get_rms() # get rotation (right multiplying!) and the translation rot, tran = sup.get_rotran() # rotate y on x manually
class QCPSuperimposerTest(unittest.TestCase): def setUp(self): self.x = array([[51.65, -1.90, 50.07], [50.40, -1.23, 50.65], [50.68, -0.04, 51.54], [50.22, -0.02, 52.85]]) self.y = array([[51.30, -2.99, 46.54], [51.09, -1.88, 47.58], [52.36, -1.20, 48.03], [52.71, -1.18, 49.38]]) self.sup = QCPSuperimposer() self.sup.set(self.x, self.y) # Public methods def test_set(self): self.assertTrue( array_equal(around(self.sup.reference_coords, decimals=3), around(self.x, decimals=3))) self.assertTrue( array_equal(around(self.sup.coords, decimals=3), around(self.y, decimals=3))) self.assertIsNone(self.sup.transformed_coords) self.assertIsNone(self.sup.rot) self.assertIsNone(self.sup.tran) self.assertIsNone(self.sup.rms) self.assertIsNone(self.sup.init_rms) def test_run(self): self.sup.run() self.assertTrue( array_equal(around(self.sup.reference_coords, decimals=3), around(self.x, decimals=3))) self.assertTrue( array_equal(around(self.sup.coords, decimals=3), around(self.y, decimals=3))) self.assertIsNone(self.sup.transformed_coords) calc_rot = array([[0.68304939, -0.5227742, -0.51004967], [0.53664482, 0.83293151, -0.13504605], [0.49543503, -0.18147239, 0.84947743]]) self.assertTrue( array_equal(around(self.sup.rot, decimals=3), around(calc_rot, decimals=3))) calc_tran = array([-7.43885125, 36.51522275, 36.81135533]) self.assertTrue( array_equal(around(self.sup.tran, decimals=3), around(calc_tran, decimals=3))) calc_rms = 0.003 self.assertEqual(float('%.3f' % self.sup.rms), calc_rms) self.assertIsNone(self.sup.init_rms) def test_get_transformed(self): self.sup.run() transformed_coords = array([[49.05456082, -1.23928381, 50.58427566], [50.02204971, -0.39367917, 51.42494181], [51.47738545, -0.57287128, 51.06760944], [52.39602307, -0.98417088, 52.03318837]]) self.assertTrue( array_equal(around(self.sup.get_transformed(), decimals=3), around(transformed_coords, decimals=3))) def test_get_init_rms(self): x = array([[1.1, 1.2, 1.3], [1.4, 1.5, 1.6], [1.7, 1.8, 1.9]]) y = array([[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) self.sup.set(x, y) self.assertIsNone(self.sup.init_rms) init_rms = array([0.81, 0.9, 0.98]) self.assertTrue( array_equal(around(self.sup.get_init_rms(), decimals=2), around(init_rms, decimals=2))) def test_get_rotran(self): self.sup.run() calc_rot = array([[0.68304939, -0.5227742, -0.51004967], [0.53664482, 0.83293151, -0.13504605], [0.49543503, -0.18147239, 0.84947743]]) calc_tran = array([-7.43885125, 36.51522275, 36.81135533]) rot, tran = self.sup.get_rotran() self.assertTrue( array_equal(around(rot, decimals=3), around(calc_rot, decimals=3))) self.assertTrue( array_equal(around(tran, decimals=3), around(calc_tran, decimals=3))) def test_get_rms(self): self.sup.run() calc_rms = 0.003 self.assertEqual(float('%.3f' % self.sup.get_rms()), calc_rms) # Old test from Bio/PDB/QCPSuperimposer/__init__.py def test_oldTest(self): x = array([[-2.803, -15.373, 24.556], [0.893, -16.062, 25.147], [1.368, -12.371, 25.885], [-1.651, -12.153, 28.177], [-0.440, -15.218, 30.068], [2.551, -13.273, 31.372], [0.105, -11.330, 33.567]]) y = array([[-14.739, -18.673, 15.040], [-12.473, -15.810, 16.074], [-14.802, -13.307, 14.408], [-17.782, -14.852, 16.171], [-16.124, -14.617, 19.584], [-15.029, -11.037, 18.902], [-18.577, -10.001, 17.996]]) self.sup.set(x, y) self.assertTrue( array_equal(around(self.sup.reference_coords, decimals=3), around(x, decimals=3))) self.assertTrue( array_equal(around(self.sup.coords, decimals=3), around(y, decimals=3))) self.sup.run() rot = array([[0.72216358, 0.69118937, -0.0271479], [-0.52038257, 0.51700833, -0.67963547], [-0.45572112, 0.50493528, 0.73304748]]) tran = array([11.68878393, -4.13245037, 6.05208344]) rms = 0.7191064509622271 self.assertTrue( array_equal(around(self.sup.rot, decimals=3), around(rot, decimals=3))) self.assertTrue( array_equal(around(self.sup.tran, decimals=3), around(tran, decimals=3))) self.assertEqual(float('%.3f' % self.sup.rms), around(rms, decimals=3)) rms_get = self.sup.get_rms() self.assertTrue( array_equal(around(rms_get, decimals=3), around(rms, decimals=3))) rot_get, tran_get = self.sup.get_rotran() self.assertTrue( array_equal(around(rot_get, decimals=3), around(rot, decimals=3))) self.assertTrue( array_equal(around(tran_get, decimals=3), around(tran, decimals=3))) y_on_x1 = dot(y, rot) + tran y_x_solution = array([[3.90787281, -16.37976032, 30.16808376], [3.58322456, -12.81122728, 28.91874135], [1.3580194, -13.96815768, 26.05958411], [-0.79347336, -15.93647897, 28.48288439], [-1.27379224, -12.9456459, 30.78004989], [-2.03519089, -10.6822696, 27.81728956], [-4.72366028, -13.05646024, 26.54536695]]) self.assertTrue( array_equal(around(y_on_x1, decimals=3), around(y_x_solution, decimals=3))) y_on_x2 = self.sup.get_transformed() self.assertTrue( array_equal(around(y_on_x2, decimals=3), around(y_x_solution, decimals=3)))
from Bio.PDB.QCPSuperimposer import QCPSuperimposer sup = QCPSuperimposer() # set the coords # y will be rotated and translated on x sup.set(x, y) # do the lsq fit sup.run() # get the rmsd rms = sup.get_rms()