-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathelectrostatics.py
executable file
·368 lines (287 loc) · 10.4 KB
/
electrostatics.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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
'''
Electrostatics (simple alternative to the APBS Tools Plugin)
(c) 2012 Thomas Holder
License: BSD-2-Clause
'''
from __future__ import print_function
from pymol import cmd, CmdException
template_apbs_in = '''
read
mol pqr "{pqrfile}"
end
elec
mg-auto
mol 1
fgcent {fgcent} # fine grid center
cgcent mol 1 # coarse grid center
fglen {fglen}
cglen {cglen}
dime {dime}
lpbe # l=linear, n=non-linear Poisson-Boltzmann equation
bcfl sdh # "Single Debye-Hueckel" boundary condition
pdie 2.0 # protein dielectric
sdie 78.0 # solvent dielectric
chgm spl2 # Cubic B-spline discretization of point charges on grid
srfm smol # smoothed surface for dielectric and ion-accessibility coefficients
swin 0.3
temp 310.0 # temperature
sdens 10.0
calcenergy no
calcforce no
srad {srad} # solvent radius
ion charge +1 conc 0.15 radius 2.0
ion charge -1 conc 0.15 radius 1.8
write pot dx "{mapfile}"
end
quit
'''
def validate_apbs_exe(exe):
'''Get and validate apbs executable.
Raise CmdException if not found or broken.'''
import os, subprocess
if exe:
exe = cmd.exp_path(exe)
else:
try:
import freemol.apbs
exe = freemol.apbs.get_exe_path()
except:
pass
if not exe:
exe = cmd.exp_path('$SCHRODINGER/utilities/apbs')
if not os.path.exists(exe):
exe = "apbs"
try:
r = subprocess.call([exe, "--version"],
stdout=open(os.devnull, "w"), stderr=subprocess.STDOUT)
if r < 0:
raise CmdException("Broken executable: " + exe)
except OSError:
raise CmdException("Cannot execute: " + exe)
return exe
def map_new_apbs(name, selection='all', grid=0.5, buffer=10.0, state=1,
preserve=0, exe='', assign=-1, focus='', quiet=1, _template=''):
'''
DESCRIPTION
Create electrostatic potential map with APBS.
For more control over parameters and a graphical user interface I
recommend to use the APBS Tools Plugin instead.
In case of missing atoms or residues I recommend to remodel the input
structure with modeller before calculating the electrostatic potential.
If selection has no charges and radii, they will be automatically assigned
with PyMOL (not with pdb2pqr).
SEE ALSO
apbs_surface, map_new (coulomb), APBS Tools Plugin
'''
import tempfile, os, shutil, glob, subprocess
from pymol.util import protein_assign_charges_and_radii
from .modelling import add_missing_atoms
selection = '(%s) and not solvent' % (selection)
grid, buffer, state = float(grid), float(buffer), int(state)
preserve, assign, quiet = int(preserve), int(assign), int(quiet)
# path to apbs executable
exe = validate_apbs_exe(exe)
# temporary directory
tempdir = tempfile.mkdtemp()
if not quiet:
print(' Tempdir:', tempdir)
# filenames
pqrfile = os.path.join(tempdir, 'mol.pqr')
infile = os.path.join(tempdir, 'apbs.in')
stem = os.path.join(tempdir, 'map')
# temporary object
tmpname = cmd.get_unused_name('mol' if preserve else '_')
cmd.create(tmpname, selection, state, 1)
# partial charges
assign = [assign]
if assign[0] == -1:
# auto detect if selection has charges and radii
cmd.iterate(tmpname,
'assign[0] *= (elec_radius * partial_charge) == 0.0',
space=locals())
if assign[0]:
cmd.remove('hydro and model ' + tmpname)
add_missing_atoms(tmpname, quiet=quiet)
protein_assign_charges_and_radii(tmpname)
elif not quiet:
print(' Notice: using exsiting charges and radii')
cmd.save(pqrfile, tmpname, 1, format='pqr', quiet=quiet)
# grid dimensions
extent = cmd.get_extent(tmpname)
extentfocus = cmd.get_extent(focus) if focus else extent
fglen = [(emax-emin + 2*buffer) for (emin, emax) in zip(*extentfocus)]
cglen = [(emax-emin + 4*buffer) for (emin, emax) in zip(*extent)]
if not preserve:
cmd.delete(tmpname)
apbs_in = {
'pqrfile': pqrfile,
'fgcent': 'mol 1',
'fglen': '%f %f %f' % tuple(fglen),
'cglen': '%f %f %f' % tuple(cglen),
'srad': cmd.get('solvent_radius'),
'mapfile': stem,
}
if focus:
apbs_in['fgcent'] = '%f %f %f' % tuple((emax + emin) / 2.0
for (emin, emax) in zip(*extentfocus))
try:
# apbs will fail if grid does not fit into memory
# -> on failure repeat with larger grid spacing
for _ in range(3):
dime = [1 + max(64, n / grid) for n in fglen]
apbs_in['dime'] = '%d %d %d' % tuple(dime)
# apbs input file
with open(infile, 'w') as f:
f.write((_template or template_apbs_in).format(**apbs_in))
# run apbs
r = subprocess.call([exe, infile], cwd=tempdir)
if r == 0:
break
if r in (-6, -9):
grid *= 2.0
if not quiet:
print(' Warning: retry with grid =', grid)
continue
raise CmdException('apbs failed with code ' + str(r))
dx_list = glob.glob(stem + '*.dx')
if len(dx_list) != 1:
raise CmdException('dx file missing')
# load map
cmd.load(dx_list[0], name, quiet=quiet)
except OSError:
raise CmdException('Cannot execute "%s"' % (exe))
finally:
if not preserve:
shutil.rmtree(tempdir)
elif not quiet:
print(' Notice: not deleting %s' % (tempdir))
def apbs_surface(selection='all', maximum=None, minimum=None, map_name=None,
ramp_name=None, grid=0.5, quiet=1):
'''
DESCRIPTION
Show electrostatic potential on surface (calculated with APBS).
Important: surface_color is a object property, so when calculating
surface potential for different selections and visualize them all
together, you should first split them into separate objects.
USAGE
apbs_surface [ selection [, maximum [, minimum ]]]
EXAMPLE
fetch 2x19, bsync=0
split_chains
apbs_surface 2x19_A, 10
apbs_surface 2x19_B, 10
SEE ALSO
map_new_apbs, APBS Tools Plugin, isosurface, gradient,
util.protein_vacuum_esp
'''
quiet = int(quiet)
if ramp_name is None:
ramp_name = cmd.get_unused_name('ramp')
if map_name is None:
map_name = cmd.get_unused_name('map')
map_new_apbs(map_name, selection, float(grid), quiet=quiet)
if maximum is not None:
maximum = float(maximum)
minimum = -maximum if minimum is None else float(minimum)
kwargs = {'range': [minimum, (minimum+maximum)*0.5, maximum]}
else:
kwargs = {'selection': selection}
cmd.ramp_new(ramp_name, map_name, **kwargs)
object_names = cmd.get_object_list('(' + selection + ')')
for name in object_names:
cmd.set('surface_color', ramp_name, name)
cmd.show('surface', selection)
cmd.set('surface_solvent', 0)
cmd.set('surface_ramp_above_mode', 1)
def volume_esp(name, map, stops=[0.1, 1.0], neg='red', pos='blue',
opacity=0.2, quiet=1):
'''
DESCRIPTION
Create a volume object from a map object with default coloring
for electrostatic potential (similar to positive and negative
isosurface).
ARGUMENTS
name = string: name for the new volume object
map = string: name of the map object to use
stops = list of floats: 2 or 3 values in standard deviations for creating
the volume ramp {default: [0.1, 1.0]}
neg = string: color for negative volume {default: red}
pos = string: color for positive volume {default: blue}
opacity = float: maximum opacity in volume ramp {default: 0.2}
SEE ALSO
volume
'''
from .setting import set_temporary
opacity, quiet = float(opacity), int(quiet)
if isinstance(stops, str):
stops = cmd.safe_list_eval(stops)
try:
from pymol.colorramping import ColorRamp
except ImportError:
print(' Warning: volume_esp is deprecated')
stdevD = cmd.get_volume_histogram(map, 0)[3]
stops = [s * stdevD for s in stops]
ramp = [
-stops[1], neg, opacity,
-stops[0], neg, 0.0,
stops[0], pos, 0.0,
stops[1], pos, opacity,
]
if len(stops) == 3:
ramp = [-stops[2], neg, opacity] + ramp + [stops[2], pos, opacity]
cmd.volume(name, map, ramp, quiet=quiet)
return
c_neg = cmd.get_color_tuple(neg)
c_pos = cmd.get_color_tuple(pos)
c_pos_0 = c_pos + (0.0,)
c_pos_1 = c_pos + (opacity,)
c_neg_0 = c_neg + (0.0,)
c_neg_1 = c_neg + (opacity,)
if len(stops) == 2:
cstops = [(c_neg_1, -999), (c_neg_1, -stops[1]), (c_neg_0, -stops[0]),
(c_pos_0, stops[0]), (c_pos_1, stops[1]), (c_pos_1, 999)]
elif len(stops) == 3:
cstops = [(c_neg_0, -999), (c_neg_0, -stops[2]), (c_neg_1, -stops[1]),
(c_neg_0, -stops[0]), (c_pos_0, stops[0]), (c_pos_1, stops[1]),
(c_pos_0, stops[2]), (c_pos_0, 999)]
else:
print(' Error: need 2 or 3 stops')
raise CmdException
cmd.volume(name, map, quiet=quiet)
# get_volume_histogram returns zeros without refresh
with set_temporary(suspend_updates='off'):
cmd.refresh()
minD, maxD, meanD, stdevD = cmd.get_volume_histogram(name)[:4]
v_ramp = []
c_ramp = ColorRamp(360)
for c, s in cstops:
i = int(360 * ((s * stdevD) - minD) / (maxD - minD))
i = min(max(i, 0), 359)
v_ramp.append(i)
v_ramp.extend(c)
c_ramp.addColor(i, c)
cmd.set_volume_ramp(name, v_ramp)
cmd.volume_color(name, c_ramp.getRamp())
cmd.recolor(name)
def volume_fofc(name, map, stops=[2.5, 3.0, 4.0], neg='red', pos='green',
opacity=0.4, quiet=1):
'''
DESCRIPTION
Create a difference density volume object.
SEE ALSO
volume_esp
'''
return volume_esp(**locals())
cmd.extend('map_new_apbs', map_new_apbs)
cmd.extend('apbs_surface', apbs_surface)
cmd.extend('volume_esp', volume_esp)
cmd.extend('volume_fofc', volume_fofc)
cmd.auto_arg[0].update([
('apbs_surface', cmd.auto_arg[0]['zoom']),
])
cmd.auto_arg[1].update([
('map_new_apbs', cmd.auto_arg[0]['zoom']),
('volume_esp', cmd.auto_arg[1]['volume']),
('volume_fofc', cmd.auto_arg[1]['volume']),
])
# vi:expandtab:smarttab