-
Notifications
You must be signed in to change notification settings - Fork 256
/
draw_rotation_axis.py
306 lines (255 loc) · 12.2 KB
/
draw_rotation_axis.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
'''
Calculate and display the rotation axis and the translation vector for a given transformation.
http://pymolwiki.org/index.php/RotationAxis
This is a first version, please use at your own risk!
REQUIREMENTS
numpy (http://numpy.scipy.org) that should be built into the newers versions of Pymol
'''
from __future__ import print_function
__author__ = 'Pablo Guardado Calvo'
__version__ = '0.1'
__email__ = 'pablo.guardado (at) gmail.com'
###########################################################################################################################################################
# USAGE
#
# The idea behind this script is to align two molecules/domains/chains/selections (using cmd.super) and extract the trasformation (TTT) matrix (T).
# From this matrix we obtain the direction of the rotation axis, as well as a point and we create a cgo object representing the axis. The script
# generates two measures: one in the graphical screen (the rotation axis value, and the norm of the tranlation vector along the rotation axis) and
# some basic information in the command-line (the transformation matrix, the rotation angle, distance between centroids, some pml lines you can use
# to reproduce the axis...)
#
# As always with these type of things, you have to use at your own risk. I did not try all the possible combination, but if you find a bug, do
# not hesitate to contact me (pablo.guardado (at) gmail.com) or try to modify the code for yourself to correct it.
#
# To load the script just type:
#
# run path-to-the-script/draw_rotation_axis.py
#
# or if you want something more permanent add the previous line to your .pymolrc file
#
# The script works just typing:
#
# draw_axis('selection1', 'selection2')
#
# Please, pay attention to the apostrophes around the selections, you MUST use them. Also works with chains:
#
# draw_axis('chain A', 'chain B')
#
# Also, you can play a bit with the lenght, width and colour of the axis you are going to generate.
#
# draw_axis('selection1', 'selection2', scale_factor, width, r1, g1, b1, r2, g2, b2)
#
# scale_factor = to control the lenght of the axis, the default is 20
# width = to control the width of the axis. Default is 0.6
# r1, g1, b1 = first RGB colour code. Default is 1, 1, 1
# r2, g2, b2 = second RGB colour code to create the gradient. Default is 1, 0, 0.
# To create a single colour axis, just made r1,g1,b1=r2,g2,b2
#
############################################################################################################################################################
from pymol import cmd, cgo
import math
import numpy
def transf_matrix(chA, chB):
'''
DESCRIPTION
Align two selections/chains, and returns the transformation matrix. I used super to carry out the alignment, likely is possible to use cmd.align and
is going to be a bit faster, but I think is not going to work well with low-sequence-identity alignments.
'''
cmd.create('working', chA)
cmd.super('working', chB)
T = cmd.get_object_matrix('working')
global cmW
cmW = center_of_Mass('working')
cmd.delete('working')
return T
def center_of_Mass(selection):
'''
DESCRIPTION
Calculates the center of mass of a given selection
'''
model= cmd.get_model(selection)
x,y,z=0,0,0
totmass = 0
for a in model.atom:
m = a.get_mass()
x+= a.coord[0]*m
y+= a.coord[1]*m
z+= a.coord[2]*m
totmass += m
cM = numpy.array([x/totmass, y/totmass, z/totmass])
return cM
def direction_cosines(chA, chB):
'''
DESCRIPTION
Calculates the direction cosines of the rotation axis from the transformation matrix.
'''
t=transf_matrix(chA, chB)
a1= (t[6]-t[9])/math.sqrt((t[6]-t[9])**2+(t[8]-t[2])**2+(t[1]-t[4])**2)
b1= (t[8]-t[2])/math.sqrt((t[6]-t[9])**2+(t[8]-t[2])**2+(t[1]-t[4])**2)
c1= (t[1]-t[4])/math.sqrt((t[6]-t[9])**2+(t[8]-t[2])**2+(t[1]-t[4])**2)
axis = numpy.array([a1, b1, c1])
return axis
def angle_axis(chA, chB):
'''
DESCRIPTION
Calculates the rotation angle from the transformation matrix
'''
t=transf_matrix(chA, chB)
angle_rad = math.acos((t[0]+t[5]+t[10]-1)/2)
return angle_rad
def center_of_rot(chA, chB):
'''
DESCRIPTION
Calculates the center of rotation of the axis
'''
cm_Working=center_of_Mass(chA)
cm_Reference=cmW
u=direction_cosines(chA, chB)[0]
u2=u**2
v=direction_cosines(chA, chB)[1]
v2=v**2
w=direction_cosines(chA, chB)[2]
w2=w**2
cos_theta=numpy.cos(angle_axis(chA, chB))
sin_theta=numpy.sin(angle_axis(chA, chB))
fx=cm_Working[0]
fy=cm_Working[1]
fz=cm_Working[2]
x=cm_Reference[0]
y=cm_Reference[1]
z=cm_Reference[2]
T11 = (v2 + w2)*(1-cos_theta)
T12 = (w*sin_theta)-((u*v)*(1-cos_theta))
T13 = -(v*sin_theta)-(u*w*(1-cos_theta))
T14 =fx-((((u2*x)+((u*v)*y)+((u*w)*z))*(1-cos_theta))+(x*cos_theta)+((-(w*y)+(v*z))*sin_theta))
T21 = -(w*sin_theta)-((u*v)*(1-cos_theta))
T22 = (u2 + w2)*(1-cos_theta)
T23 = (u*sin_theta)-(w*v*(1-cos_theta))
T24 =fy-((((v*u*x)+(v2*y)+(v*w*z))*(1-cos_theta))+(y*cos_theta)+(((w*x)-(u*z))*sin_theta))
T31 = (v*sin_theta)-(w*u*(1-cos_theta))
T32 = -(u*sin_theta)-(w*v*(1-cos_theta))
T33 = (u2 + v2)*(1-cos_theta)
T34 =fz-(((((u*x)*w)+((v*y)*w)+(w2*z))*(1-cos_theta))+(z*cos_theta)+((-(v*x)+(u*y))*sin_theta))
term_lig = numpy.array([[T11, T12, T13], [T21, T22, T23], [T31, T32, T33]])
term_ind = numpy.array([T14, T24, T34])
sol_lstsq = numpy.linalg.lstsq(term_lig, term_ind)
sol = sol_lstsq[0]
return sol
def nearest_point_to_axis(chA, chB):
'''
DESCRIPTION
Calculates the nearest point of the axis, I use it to create the cgo object.
'''
cmA=center_of_Mass(chA)
cmB=cmW
cmAver=(cmB+cmA)/2
vector=numpy.array([(cmB[0]-cmA[0]), (cmB[1]-cmA[1]), (cmB[2]-cmA[2])])
moduli_vector=numpy.linalg.norm(vector)
vector_director=numpy.array([(cmB[0]-cmA[0])/moduli_vector, (cmB[1]-cmA[1])/moduli_vector, (cmB[2]-cmA[2])/moduli_vector])
axis1= direction_cosines(chA, chB)
sol=center_of_rot(chA, chB)
term_lig2=numpy.array([[vector_director[0], vector_director[1], vector_director[2], 0], [1, 0, 0, -axis1[0]], [0, 1, 0, -axis1[1]], [0, 0, 1, -axis1[2]]])
term_ind2=numpy.array([(cmAver[0]*(vector_director[0]))+(cmAver[1]*(vector_director[1]))+(cmAver[2]*(vector_director[2])), sol[0], sol[1], sol[2]])
term_j=(cmAver[0]*vector_director[0])+(cmAver[1]*vector_director[1])+(cmAver[2]*vector_director[2])
suma_vect_director=vector_director+axis1
term_ji=(cmAver[0]*suma_vect_director[0])+(cmAver[1]*suma_vect_director[1])+(cmAver[2]*suma_vect_director[2])
if numpy.dot(vector_director, axis1) != 0:
t = ((-numpy.dot(vector_director, sol))+term_j)/numpy.dot(vector_director, axis1)
else:
t = ((-numpy.dot(suma_vect_director, sol))+term_ji)/numpy.dot(suma_vect_director, axis1)
p = [sol[0]+axis1[0]*t, sol[1]+axis1[1]*t, sol[2]+axis1[2]*t]
return p
def proyeccion_centroide(selection, chA, chB):
'''
DESCRIPTION
Calculates the proyection of the mass center for the working molecule before being aligned with the reference molecule. For representation purpuses.
'''
axis1=numpy.array([direction_cosines(chA, chB)[0], direction_cosines(chA, chB)[1], direction_cosines(chA, chB)[2]])
sol=center_of_rot(chA, chB)
cmSel=center_of_Mass(selection)
t_cen=numpy.dot(cmSel, axis1)-numpy.dot(sol, axis1)
proy_cen= [sol[0]+(t_cen*axis1[0]), sol[1]+(t_cen*axis1[1]), sol[2]+(t_cen*axis1[2])]
return proy_cen
def proyeccion_centroide_working(chA, chB):
'''
DESCRIPTION
Calculates the proyection of the mass center for working molecule after being aligned with the reference molecule. For representation purpuses.
'''
axis1=numpy.array([direction_cosines(chA, chB)[0], direction_cosines(chA, chB)[1], direction_cosines(chA, chB)[2]])
sol=center_of_rot(chA, chB)
cmSel=cmW
t_cen=numpy.dot(cmSel, axis1)-numpy.dot(sol, axis1)
proy_cen= [sol[0]+(t_cen*axis1[0]), sol[1]+(t_cen*axis1[1]), sol[2]+(t_cen*axis1[2])]
return proy_cen
def print_information(T, axis1, angle_degrees, moduli_vector, obj, x1, y1, z1, x2, y2, z2, w, r1, g1, b1, r2, g2, b2, modu_tr=0):
'''
DESCRIPTION
Print to basic information to the screen.
'''
print("#################################################################################################")
print("Transformation (TTT) matrix")
print("%8.2f, %8.2f, %8.2f, %8.2f" %(T[0], T[1], T[2], T[3]))
print("%8.2f, %8.2f, %8.2f, %8.2f" %(T[4], T[5], T[6], T[7]))
print("%8.2f, %8.2f, %8.2f, %8.2f" %(T[8], T[9], T[10], T[11]))
print("%8.2f, %8.2f, %8.2f, %8.2f" %(T[12], T[13], T[14], T[15]))
print(".................................................................................................")
print("")
print("The direction cosines of the rotation axis is: %3.2f, %3.2f, %3.2f" %(axis1[0], axis1[1], axis1[2]))
print("The angle of rotation is %3.2f degrees" %(angle_degrees))
print("The lenght of the translation vector along the rotation axis is %3.2f Angstroms" %(modu_tr))
print("The distance between mass centers is %3.2f Angstroms" %(moduli_vector))
print(".................................................................................................")
print("")
print("Lines to be used in a pml script to generate the axis")
print("")
print("CYLINDER, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, %3.2f, 0.0" %(x1, y1, z1, x2, y2, z2, w, r1, g1, b1, r2, g2, b2))
print("cmd.load_cgo(obj, %3.2f)" %(angle_degrees))
print("")
print("#################################################################################################")
def draw_axis(chA, chB, scale_factor=20, w=0.6, r1=1, g1=1, b1=1, r2=1, g2=0, b2=0):
T = transf_matrix(chA, chB)
angle=angle_axis(chA, chB)
angle_degrees=(angle*180)/math.pi
axis1=[direction_cosines(chA, chB)[0], direction_cosines(chA, chB)[1], direction_cosines(chA, chB)[2]]
p = nearest_point_to_axis(chA, chB)
x1, y1, z1 = p[0] + (3*scale_factor*axis1[0]), p[1] + (3*scale_factor*axis1[1]), p[2] + (3*scale_factor*axis1[2])
x2, y2, z2 = p[0] - (3*scale_factor*axis1[0]), p[1] - (3*scale_factor*axis1[1]), p[2] - (3*scale_factor*axis1[2])
obj = [cgo.CYLINDER, x1, y1, z1, x2, y2, z2, w, r1, g1, b1, r2, g2, b2, 0.0]
cmd.load_cgo(obj, angle_degrees)
cmA=center_of_Mass(chA)
cmB=cmW
cmAver=(cmB+cmA)/2
vector=numpy.array([(cmB[0]-cmA[0]), (cmB[1]-cmA[1]), (cmB[2]-cmA[2])])
moduli_vector=numpy.linalg.norm(vector)
vector_director=numpy.array([(cmB[0]-cmA[0])/moduli_vector, (cmB[1]-cmA[1])/moduli_vector, (cmB[2]-cmA[2])/moduli_vector])
pC_A = proyeccion_centroide(chA, chA, chB)
pC_B = proyeccion_centroide_working(chA, chB)
trans_vector = numpy.array([(pC_B[0]-pC_A[0]), (pC_B[1]-pC_A[1]), (pC_B[2]-pC_A[2])])
modu_tr = numpy.linalg.norm(trans_vector)
rota_centroid_rad=numpy.dot(vector_director, axis1)
rota_centroid = (rota_centroid_rad*180)/math.pi
rota_centroid_absol_0= numpy.absolute(rota_centroid)
rota_centroid_absol=round(rota_centroid_absol_0,2)
if rota_centroid_absol == 0.00:
p1 = '_1'
p2 = '_2'
p3 = '_3'
cmd.pseudoatom (pos=[cmA[0], cmA[1], cmA[2]], object=p1)
cmd.pseudoatom (pos=[pC_A[0], pC_A[1], pC_A[2]], object=p2)
cmd.pseudoatom (pos=[cmB[0], cmB[1], cmB[2]], object=p3)
cmd.angle(None, p1, p2, p3)
print_information(T, axis1, angle_degrees, moduli_vector, obj, x1, y1, z1, x2, y2, z2, w, r1, g1, b1, r2, g2, b2)
if rota_centroid_absol != 0:
p1 = '_1'
p2 = '_2'
p3 = '_3'
p4 = '_4'
cmd.pseudoatom (pos=[cmA[0], cmA[1], cmA[2]], object=p1)
cmd.pseudoatom (pos=[pC_A[0], pC_A[1], pC_A[2]], object=p2)
cmd.pseudoatom (pos=[pC_B[0], pC_B[1], pC_B[2]], object=p3)
cmd.pseudoatom (pos=[cmB[0], cmB[1], cmB[2]], object=p4)
cmd.dihedral(None, p1, p2, p3, p4)
cmd.distance(None, p2, p3)
print_information(T, axis1, angle_degrees, moduli_vector, obj, x1, y1, z1, x2, y2, z2, w, r1, g1, b1, r2, g2, b2, modu_tr)
cmd.create('working', chA)
cmd.super('working', chB)