forked from Pymol-Scripts/Pymol-script-repo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
elbow_angle.py
257 lines (189 loc) · 7.66 KB
/
elbow_angle.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
'''
More information at: http://www.pymolwiki.org/index.php/elbow_angle
Calculate the elbow angle of an antibody Fab complex and optionally draw a
graphical representation of the vectors used to determine the angle.
NOTE: There is no automatic checking of the validity of limit_l and limit_h
values or of the assignment of light and heavy chain IDs. If these are entered
incorrectly or omitted, the reported angle will likely be incorrect.
As always with these things, your mileage may vary. Use at your own risk!
REQUIREMENTS
numpy, version 1.6
http://numpy.scipy.org
transformations.py, version 2012.01.01
by Christoph Gohlke
www.lfd.uci.edu/~gohlke/code
May also require an edit to transformations.py:
Changes `1e-8` to `1e-7` in lines 357 & 363 to avoid a numerical error.
com.py
by Jason Vertrees
http://www.pymolwiki.org/index.php/com
CHANGELOG
=========
* 0.1.1
- fixed: Print an error message instead of failing silently when a limit
residue doesn't exist.
'''
from __future__ import print_function
__author__ = 'Jared Sampson'
__version__ = '0.1.1'
from pymol import cmd
import pymol
import numpy
try:
import transformations
except ImportError:
from . import transformations
try:
COM = cmd.centerofmass
except AttributeError:
from com import COM
################################################################################
def calc_super_matrix(mobile, static):
'''
DESCRIPTION
Aligns two objects (or selections), returns the transformation matrix,
and resets the matrix of the mobile object.
Uses CEAlign PyMOL function for alignment.
ARGUMENTS
mobile = string: selection describing the mobile object whose rotation
matrix will be reported
static = string: selection describing the static object onto which the
mobile object will be aligned
REQUIRES: numpy
'''
cmd.cealign(static, mobile)
# cmd.super(mobile,static)
T = cmd.get_object_matrix(mobile)
R = numpy.identity(4)
k = 0
for i in range(0, 4):
for j in range(0, 4):
R[i][j] = T[k]
k += 1
return R
################################################################################
def elbow_angle(obj, light='L', heavy='H', limit_l=107, limit_h=113, draw=0):
"""
DESCRIPTION
Calculates the integer elbow angle of an antibody Fab complex and
optionally draws a graphical representation of the vectors used to
determine the angle.
ARGUMENTS
obj = string: object
light/heavy = strings: chain ID of light and heavy chains, respectively
limit_l/limit_h = integers: residue numbers of the last residue in the
light and heavy chain variable domains, respectively
draw = boolean: Choose whether or not to draw the angle visualization
REQUIRES: com.py, transformations.py, numpy (see above)
"""
# store current view
orig_view = cmd.get_view()
limit_l = int(limit_l)
limit_h = int(limit_h)
draw = int(draw)
# for temp object names
tmp_prefix = "tmp_elbow_"
prefix = tmp_prefix + obj + '_'
# names
vl = prefix + 'VL'
vh = prefix + 'VH'
cl = prefix + 'CL'
ch = prefix + 'CH'
# selections
vl_sel = 'polymer and %s and chain %s and resi 1-%i' % (obj, light, limit_l)
vh_sel = 'polymer and %s and chain %s and resi 1-%i' % (obj, heavy, limit_h)
cl_sel = 'polymer and %s and chain %s and not resi 1-%i' % (obj, light, limit_l)
ch_sel = 'polymer and %s and chain %s and not resi 1-%i' % (obj, heavy, limit_h)
v_sel = '((' + vl_sel + ') or (' + vh_sel + '))'
c_sel = '((' + cl_sel + ') or (' + ch_sel + '))'
# create temp objects
cmd.create(vl, vl_sel)
cmd.create(vh, vh_sel)
cmd.create(cl, cl_sel)
cmd.create(ch, ch_sel)
# superimpose vl onto vh, calculate axis and angle
Rv = calc_super_matrix(vl, vh)
angle_v, direction_v, point_v = transformations.rotation_from_matrix(Rv)
# superimpose cl onto ch, calculate axis and angle
Rc = calc_super_matrix(cl, ch)
angle_c, direction_c, point_c = transformations.rotation_from_matrix(Rc)
# delete temporary objects
cmd.delete(vl)
cmd.delete(vh)
cmd.delete(cl)
cmd.delete(ch)
# if dot product is positive, angle is acute
if (numpy.dot(direction_v, direction_c) > 0):
direction_c = direction_c * -1 # ensure angle is > 90 (need to standardize this)
# TODO: make both directions point away from the elbow axis.
elbow = int(numpy.degrees(numpy.arccos(numpy.dot(direction_v, direction_c))))
# while (elbow < 90):
# elbow = 180 - elbow # limit to physically reasonable range
# compare the direction_v and direction_c axes to the vector defined by
# the C-alpha atoms of limit_l and limit_h of the original fab
hinge_l_sel = "%s//%s/%s/CA" % (obj, light, limit_l)
hinge_h_sel = "%s//%s/%s/CA" % (obj, heavy, limit_h)
try:
hinge_l = cmd.get_atom_coords(hinge_l_sel)
hinge_h = cmd.get_atom_coords(hinge_h_sel)
except pymol.CmdException:
# Either hinge_l_sel or hinge_h_sel atom did not exist.
raise pymol.CmdException(
'Unable to calculate elbow angle. Please check '
'your limit and chain selections and try again.'
)
hinge_vec = numpy.array(hinge_h) - numpy.array(hinge_l)
test = numpy.dot(hinge_vec, numpy.cross(direction_v, direction_c))
if (test > 0):
elbow = 360 - elbow
print(" Elbow angle: %i degrees" % elbow)
if (draw == 1):
# there is probably a more elegant way to do this, but
# it works so I'm not going to mess with it for now
pre = obj + '_elbow_'
# draw hinge vector
cmd.pseudoatom(pre + "hinge_l", pos=hinge_l)
cmd.pseudoatom(pre + "hinge_h", pos=hinge_h)
cmd.distance(pre + "hinge_vec", pre + "hinge_l", pre + "hinge_h")
cmd.set("dash_gap", 0)
# draw the variable domain axis
com_v = COM(v_sel)
start_v = [a - 10 * b for a, b in zip(com_v, direction_v)]
end_v = [a + 10 * b for a, b in zip(com_v, direction_v)]
cmd.pseudoatom(pre + "start_v", pos=start_v)
cmd.pseudoatom(pre + "end_v", pos=end_v)
cmd.distance(pre + "v_vec", pre + "start_v", pre + "end_v")
# draw the constant domain axis
com_c = COM(c_sel)
start_c = [a - 10 * b for a, b in zip(com_c, direction_c)]
end_c = [a + 10 * b for a, b in zip(com_c, direction_c)]
cmd.pseudoatom(pre + "start_c", pos=start_c)
cmd.pseudoatom(pre + "end_c", pos=end_c)
cmd.distance(pre + "c_vec", pre + "start_c", pre + "end_c")
# customize appearance
cmd.hide("labels", pre + "hinge_vec")
cmd.hide("labels", pre + "v_vec")
cmd.hide("labels", pre + "c_vec")
cmd.color("green", pre + "hinge_l")
cmd.color("red", pre + "hinge_h")
cmd.color("black", pre + "hinge_vec")
cmd.color("black", pre + "start_v")
cmd.color("black", pre + "end_v")
cmd.color("black", pre + "v_vec")
cmd.color("black", pre + "start_c")
cmd.color("black", pre + "end_c")
cmd.color("black", pre + "c_vec")
# draw spheres
cmd.show("spheres", pre + "hinge_l or " + pre + "hinge_h")
cmd.show("spheres", pre + "start_v or " + pre + "start_c")
cmd.show("spheres", pre + "end_v or " + pre + "end_c")
cmd.set("sphere_scale", 2)
cmd.set("dash_gap", 0, pre + "hinge_vec")
cmd.set("dash_width", 5)
cmd.set("dash_radius", 0.3)
# group drawing objects
cmd.group(pre, pre + "*")
# restore original view
cmd.set_view(orig_view)
return 0
cmd.extend("elbow_angle", elbow_angle)