22import numpy as np
33from .pdb2sqlcore import pdb2sql
44from .interface import interface
5+ from .superpose import get_trans_vect , get_rotation_matrix , superpose_selection
6+
57from . import transform
68import os
79import pickle
@@ -134,27 +136,8 @@ def compute_lrmsd_fast(self, lzone=None, method='svd', check=True):
134136 xyz_ref_long , xyz_ref_short = self .get_xyz_zone_backbone (
135137 self .ref , resData , return_not_in_zone = True )
136138
137- # get the translation so that both A chains are centered
138- tr_decoy = self .get_trans_vect (xyz_decoy_long )
139- tr_ref = self .get_trans_vect (xyz_ref_long )
140-
141- # translate everything for 1
142- xyz_decoy_short += tr_decoy
143- xyz_decoy_long += tr_decoy
144-
145- # translate everuthing for 2
146- xyz_ref_short += tr_ref
147- xyz_ref_long += tr_ref
148-
149- # get the ideal rotation matrix
150- # to superimpose the A chains
151- U = self .get_rotation_matrix (
152- xyz_decoy_long , xyz_ref_long , method = method )
153-
154- # rotate the entire fragment
155-
156- xyz_decoy_short = transform .rotate (
157- xyz_decoy_short , U , center = self .origin )
139+ xyz_decoy_short = superpose_selection (
140+ xyz_decoy_short , xyz_decoy_long , xyz_ref_long , method )
158141
159142 # compute the RMSD
160143 return self .get_rmsd (xyz_decoy_short , xyz_ref_short )
@@ -281,24 +264,9 @@ def compute_irmsd_fast(
281264 xyz_contact_decoy = self .get_xyz_zone_backbone (self .decoy , resData )
282265 xyz_contact_ref = self .get_xyz_zone_backbone (self .ref , resData )
283266
284- # get the translation so that both A chains are centered
285- tr_decoy = self .get_trans_vect (xyz_contact_decoy )
286- tr_ref = self .get_trans_vect (xyz_contact_ref )
287-
288- # translate everything
289- xyz_contact_decoy += tr_decoy
290- xyz_contact_ref += tr_ref
291-
292- # get the ideal rotation matrix
293- # to superimpose the A chains
294- U = self .get_rotation_matrix (
295- xyz_contact_decoy ,
296- xyz_contact_ref ,
297- method = method )
298-
299- # rotate the entire fragment
300- xyz_contact_decoy = transform .rotate (
301- xyz_contact_decoy , U , center = self .origin )
267+ # superpose the fragments
268+ xyz_contact_decoy = superpose_selection (xyz_contact_decoy ,
269+ xyz_contact_decoy , xyz_contact_ref , method )
302270
303271 # return the RMSD
304272 return self .get_rmsd (xyz_contact_decoy , xyz_contact_ref )
@@ -559,8 +527,8 @@ def compute_lrmsd_pdb2sql(self, exportpath=None, method='svd'):
559527 xyz_ref_short = xyz_ref_A
560528
561529 # get the translation so that both A chains are centered
562- tr_decoy = self . get_trans_vect (xyz_decoy_long )
563- tr_ref = self . get_trans_vect (xyz_ref_long )
530+ tr_decoy = get_trans_vect (xyz_decoy_long )
531+ tr_ref = get_trans_vect (xyz_ref_long )
564532
565533 # translate everything for 1
566534 xyz_decoy_short += tr_decoy
@@ -572,7 +540,7 @@ def compute_lrmsd_pdb2sql(self, exportpath=None, method='svd'):
572540
573541 # get the ideal rotation matrix
574542 # to superimpose the A chains
575- U = self . get_rotation_matrix (
543+ U = get_rotation_matrix (
576544 xyz_decoy_long , xyz_ref_long , method = method )
577545
578546 # rotate the entire fragment
@@ -756,23 +724,23 @@ def compute_irmsd_pdb2sql(
756724 'Error in i-rmsd: only one chain represented in one chain' )
757725
758726 # get the translation so that both A chains are centered
759- tr_decoy = self . get_trans_vect (xyz_contact_decoy )
760- tr_ref = self . get_trans_vect (xyz_contact_ref )
727+ tr_decoy = get_trans_vect (xyz_contact_decoy )
728+ tr_ref = get_trans_vect (xyz_contact_ref )
761729
762730 # translate everything
763731 xyz_contact_decoy += tr_decoy
764732 xyz_contact_ref += tr_ref
765733
766734 # get the ideql rotation matrix
767735 # to superimpose the A chains
768- U = self . get_rotation_matrix (
769- xyz_contact_decoy ,
770- xyz_contact_ref ,
771- method = method )
736+ rot_mat = get_rotation_matrix (
737+ xyz_contact_decoy ,
738+ xyz_contact_ref ,
739+ method = method )
772740
773741 # rotate the entire fragment
774742 xyz_contact_decoy = transform .rotate (
775- xyz_contact_decoy , U , center = self .origin )
743+ xyz_contact_decoy , rot_mat , center = self .origin )
776744
777745 # compute the RMSD
778746 irmsd = self .get_rmsd (xyz_contact_decoy , xyz_contact_ref )
@@ -1185,7 +1153,7 @@ def compute_clashes(pdb):
11851153
11861154 ##########################################################################
11871155 #
1188- # ROUTINES TO ACTUALY ALIGN THE MOLECULES
1156+ # ROUTINES TO ACTUALY SUPERPOSE THE MOLECULES
11891157 #
11901158 ##########################################################################
11911159
@@ -1205,170 +1173,3 @@ def get_rmsd(P, Q):
12051173 """
12061174 n = len (P )
12071175 return round (np .sqrt (1. / n * np .sum ((P - Q )** 2 )), 3 )
1208-
1209- # compute the translation vector to center a set of points
1210- @staticmethod
1211- def get_trans_vect (P ):
1212- """Get the translationv vector to the origin.
1213-
1214- Args:
1215- P (np.array(nx3)): position of the points in the molecule
1216-
1217- Returns:
1218- float: minus mean value of the xyz columns
1219- """
1220- return - np .mean (P , 0 )
1221-
1222- # main switch for the rotation matrix
1223- # add new methods here if necessary
1224- def get_rotation_matrix (self , P , Q , method = 'svd' ):
1225-
1226- # get the matrix with Kabsh method
1227- if method .lower () == 'svd' :
1228- mat = self .get_rotation_matrix_Kabsh (P , Q )
1229-
1230- # or with the quaternion method
1231- elif method .lower () == 'quaternion' :
1232- mat = self .get_rotation_matrix_quaternion (P , Q )
1233-
1234- else :
1235- raise ValueError (
1236- f'{ method } is not a valid method for rmsd alignement. '
1237- f'Options are svd or quaternions' )
1238-
1239- return mat
1240-
1241- # get the rotation matrix via a SVD
1242- # decomposition of the correlation matrix
1243- @staticmethod
1244- def get_rotation_matrix_Kabsh (P , Q ):
1245- """Get the rotation matrix to aligh two point clouds.
1246-
1247- The method is based on th Kabsh approach
1248- https://cnx.org/contents/HV-RsdwL@23/Molecular-Distance-Measures
1249-
1250- Args:
1251- P (np.array): xyz of the first point cloud
1252- Q (np.array): xyz of the second point cloud
1253-
1254- Returns:
1255- np.array: rotation matrix
1256-
1257- Raises:
1258- ValueError: matrix have different sizes
1259- """
1260- pshape = P .shape
1261- qshape = Q .shape
1262-
1263- if pshape [0 ] == qshape [0 ]:
1264- npts = pshape [0 ]
1265- else :
1266- raise ValueError ("Matrix don't have the same number of points" ,
1267- P .shape , Q .shape )
1268-
1269- p0 , q0 = np .abs (np .mean (P , 0 )), np .abs (np .mean (Q , 0 ))
1270- eps = 1E-6
1271- if any (p0 > eps ) or any (q0 > eps ):
1272- raise ValueError ('You must center the fragment first' , p0 , q0 )
1273-
1274- # form the covariance matrix
1275- A = np .dot (P .T , Q ) / npts
1276-
1277- # SVD the matrix
1278- V , _ , W = np .linalg .svd (A )
1279-
1280- # the W matrix returned here is
1281- # already its transpose
1282- # https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.linalg.svd.html
1283- W = W .T
1284-
1285- # determinant
1286- d = np .linalg .det (np .dot (W , V .T ))
1287-
1288- # form the U matrix
1289- Id = np .eye (3 )
1290- if d < 0 :
1291- Id [2 , 2 ] = - 1
1292-
1293- U = np .dot (W , np .dot (Id , V .T ))
1294-
1295- return U
1296-
1297- # get the rotation amtrix via the quaternion approach
1298- # doesn't work great so far
1299- @staticmethod
1300- def get_rotation_matrix_quaternion (P , Q ):
1301- """Get the rotation matrix to aligh two point clouds.
1302-
1303- The method is based on the quaternion approach
1304- http://www.ams.stonybrook.edu/~coutsias/papers/rmsd17.pdf
1305-
1306- Args:
1307- P (np.array): xyz of the first point cloud
1308- Q (np.array): xyz of the second point cloud
1309-
1310- Returns:
1311- np.array: rotation matrix
1312-
1313- Raises:
1314- ValueError: matrix have different sizes
1315- """
1316- pshape = P .shape
1317- qshape = Q .shape
1318-
1319- if pshape [0 ] != qshape [0 ]:
1320- raise ValueError ("Matrix don't have the same number of points" ,
1321- P .shape , Q .shape )
1322-
1323- p0 , q0 = np .abs (np .mean (P , 0 )), np .abs (np .mean (Q , 0 ))
1324- eps = 1E-6
1325- if any (p0 > eps ) or any (q0 > eps ):
1326- raise ValueError ('You must center the fragment first' , p0 , q0 )
1327-
1328- # form the correlation matrix
1329- R = np .dot (P .T , Q )
1330-
1331- # form the F matrix (eq. 10 of ref[1])
1332- F = np .zeros ((4 , 4 ))
1333-
1334- F [0 , 0 ] = np .trace (R )
1335- F [0 , 1 ] = R [1 , 2 ] - R [2 , 1 ]
1336- F [0 , 2 ] = R [2 , 0 ] - R [0 , 2 ]
1337- F [0 , 3 ] = R [0 , 1 ] - R [1 , 0 ]
1338-
1339- F [1 , 0 ] = R [1 , 2 ] - R [2 , 1 ]
1340- F [1 , 1 ] = R [0 , 0 ] - R [1 , 1 ] - R [2 , 2 ]
1341- F [1 , 2 ] = R [0 , 1 ] + R [1 , 0 ]
1342- F [1 , 3 ] = R [0 , 2 ] + R [2 , 0 ]
1343-
1344- F [2 , 0 ] = R [2 , 0 ] - R [0 , 2 ]
1345- F [2 , 1 ] = R [0 , 1 ] + R [1 , 0 ]
1346- F [2 , 2 ] = - R [0 , 0 ] + R [1 , 1 ] - R [2 , 2 ]
1347- F [2 , 3 ] = R [1 , 2 ] + R [2 , 1 ]
1348-
1349- F [3 , 0 ] = R [0 , 1 ] - R [1 , 0 ]
1350- F [3 , 1 ] = R [0 , 2 ] + R [2 , 0 ]
1351- F [3 , 2 ] = R [1 , 2 ] + R [2 , 1 ]
1352- F [3 , 3 ] = - R [0 , 0 ] - R [1 , 1 ] + R [2 , 2 ]
1353-
1354- # diagonalize it
1355- l , U = np .linalg .eig (F )
1356-
1357- # extract the eigenvect of the highest eigenvalues
1358- indmax = np .argmax (l )
1359- q0 , q1 , q2 , q3 = U [:, indmax ]
1360-
1361- # form the rotation matrix (eq. 33 ref[1])
1362- U = np .zeros ((3 , 3 ))
1363-
1364- U [0 , 0 ] = q0 ** 2 + q1 ** 2 - q2 ** 2 - q3 ** 2
1365- U [0 , 1 ] = 2 * (q1 * q2 - q0 * q3 )
1366- U [0 , 2 ] = 2 * (q1 * q3 + q0 * q2 )
1367- U [1 , 0 ] = 2 * (q1 * q2 + q0 * q3 )
1368- U [1 , 1 ] = q0 ** 2 - q1 ** 2 + q2 ** 2 - q3 ** 2
1369- U [1 , 2 ] = 2 * (q2 * q3 - q0 * q1 )
1370- U [2 , 0 ] = 2 * (q1 * q3 - q0 * q2 )
1371- U [2 , 1 ] = 2 * (q2 * q3 + q0 * q1 )
1372- U [2 , 2 ] = q0 ** 2 - q1 ** 2 - q2 ** 2 + q3 ** 2
1373-
1374- return U
0 commit comments