Skip to content

Commit efb14bc

Browse files
committed
added creating box around ligand
1 parent b8b4e05 commit efb14bc

File tree

3 files changed

+91
-47
lines changed

3 files changed

+91
-47
lines changed

meeko/gridbox.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def box_to_pdb_string(box_center, npts, spacing=0.375):
6161
|5....|./6
6262
|.____|/
6363
1 2
64-
64+
6565
"""
6666

6767
step_x = int(npts[0] / 2.0) * spacing
@@ -77,7 +77,7 @@ def box_to_pdb_string(box_center, npts, spacing=0.375):
7777
corners.append([center_x + step_x, center_y - step_y, center_z + step_z] ) # 6
7878
corners.append([center_x + step_x, center_y + step_y, center_z + step_z] ) # 7
7979
corners.append([center_x - step_x, center_y + step_y, center_z + step_z] ) # 8
80-
80+
8181
count = 1
8282
res = "BOX"
8383
chain = "X"
@@ -89,10 +89,10 @@ def box_to_pdb_string(box_center, npts, spacing=0.375):
8989
z = corners[idx][2]
9090
pdb_out += line % (count, "Ne", res, chain, idx+1, x, y, z, "Ne")
9191
count += 1
92-
92+
9393
# center
9494
pdb_out += line % (count+1, "Xe", res, chain, idx+1, center_x, center_y, center_z, "Xe")
95-
95+
9696
pdb_out += "CONECT 1 2" + os_linesep
9797
pdb_out += "CONECT 1 4" + os_linesep
9898
pdb_out += "CONECT 1 5" + os_linesep
@@ -122,5 +122,35 @@ def is_point_outside_box(point, center, npts, spacing=0.375):
122122
is_outside |= z >= maxcorner[2] or z <= mincorner[2]
123123
return is_outside
124124

125+
def calc_box(fname, padding):
126+
"""Crude PDBQT parsing is used here because it allows input of autosite pdbqts"""
127+
padding = float(padding)
128+
x_min = float('inf')
129+
x_min = float('inf')
130+
y_min = float('inf')
131+
z_min = float('inf')
132+
x_max = float('-inf')
133+
y_max = float('-inf')
134+
z_max = float('-inf')
135+
with open(fname) as f:
136+
for line in f:
137+
if line.startswith('ATOM') or line.startswith('HETATM'):
138+
x = float(line[30:38])
139+
y = float(line[38:46])
140+
z = float(line[46:54])
141+
x_max = max(x, x_max)
142+
y_max = max(y, y_max)
143+
z_max = max(z, z_max)
144+
x_min = min(x, x_min)
145+
y_min = min(y, y_min)
146+
z_min = min(z, z_min)
147+
center_x = (x_min + x_max) / 2.0
148+
center_y = (y_min + y_max) / 2.0
149+
center_z = (z_min + z_max) / 2.0
150+
size_x = x_max - x_min + 2 * padding
151+
size_y = y_max - y_min + 2 * padding
152+
size_z = z_max - z_min + 2 * padding
153+
return (center_x, center_y, center_z), (size_x, size_y, size_z)
154+
125155
boron_silicon_atompar = "atom_par Si 4.10 0.200 35.8235 -0.00143 0.0 0.0 0 -1 -1 6" + os_linesep
126156
boron_silicon_atompar += "atom_par B 3.84 0.155 29.6478 -0.00152 0.0 0.0 0 -1 -1 0" + os_linesep

meeko/receptor_pdbqt.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import json
99
from os import linesep as os_linesep
1010
import pathlib
11+
import sys
1112

1213
import numpy as np
1314
from scipy import spatial
@@ -86,7 +87,7 @@ def _read_receptor_pdbqt_string(pdbqt_string, skip_typing=False):
8687
except:
8788
temp_factor = None
8889
record_type = line[0:6].strip()
89-
90+
9091
if skip_typing:
9192
atoms.append((idx, serial, name, resid, resname, chainid, xyz, partial_charges, atom_type,
9293
alt_id, in_code, occupancy, temp_factor, record_type))
@@ -135,7 +136,7 @@ def __init__(self, pdbqt_filename, skip_typing=False):
135136
self._KDTree = None
136137

137138
with open(pdbqt_filename) as f:
138-
pdbqt_string = f.read()
139+
pdbqt_string = f.read()
139140

140141
self._atoms, self._atom_annotations = _read_receptor_pdbqt_string(pdbqt_string, skip_typing)
141142
# We add to the KDTree only the rigid part of the receptor
@@ -150,7 +151,7 @@ def __repr__(self):
150151
def get_atom_indices_by_residue(atoms):
151152
""" return a dictionary where residues are keys and
152153
values are lists of atom indices
153-
154+
154155
>>> atom_idx_by_res = {("A", "LYS", 417): [0, 1, 2, 3, ..., 8]}
155156
"""
156157

@@ -184,18 +185,18 @@ def get_params_for_residue(resname, atom_names, residue_params=residue_params):
184185
if not is_matched:
185186
ok = False
186187
return atom_params, ok, err
187-
188+
188189
for atom_name in atom_names:
189190
name_index = residue_params[r_id]["atom_names"].index(atom_name)
190191
for param in residue_params[r_id].keys():
191192
if param in excluded_params:
192193
continue
193194
if param not in atom_params:
194-
atom_params[param] = [None] * atom_counter
195+
atom_params[param] = [None] * atom_counter
195196
value = residue_params[r_id][param][name_index]
196197
atom_params[param].append(value)
197198
atom_counter += 1
198-
199+
199200
return atom_params, ok, err
200201

201202
def assign_types_charges(self, residue_params=residue_params):
@@ -215,8 +216,8 @@ def assign_types_charges(self, residue_params=residue_params):
215216
for key in wanted_params:
216217
atom_params[key].extend(params_this_res[key])
217218
if ok:
218-
self._atoms["partial_charges"] = atom_params["gasteiger"]
219-
self._atoms["atom_type"] = atom_params["atom_types"]
219+
self._atoms["partial_charges"] = atom_params["gasteiger"]
220+
self._atoms["atom_type"] = atom_params["atom_types"]
220221
return ok, err
221222

222223
def write_flexres_from_template(self, res_id, atom_index=0):
@@ -259,7 +260,7 @@ def write_flexres_from_template(self, res_id, atom_index=0):
259260
for i in range(len(template["is_atom"])):
260261
if template["is_atom"][i]:
261262
ref_atoms.add(template["atom_name"][i])
262-
if got_atoms != ref_atoms:
263+
if got_atoms != ref_atoms:
263264
success = False
264265
error_msg += "mismatch in atom names for residue %s" % str(res_id) + os_linesep
265266
error_msg += "names found but not in template: %s" % str(got_atoms.difference(ref_atoms)) + os_linesep
@@ -273,7 +274,7 @@ def write_flexres_from_template(self, res_id, atom_index=0):
273274
atom_index += 1
274275
name = template['atom_name'][i]
275276
atom = atoms_by_name[name]
276-
if atom["atom_type"] not in self.skip_types:
277+
if atom["atom_type"] not in self.skip_types:
277278
atom["serial"] = atom_index
278279
output["pdbqt"] += self.write_pdbqt_line(atom)
279280
else:
@@ -319,7 +320,7 @@ def write_pdbqt_string(self, flexres=()):
319320
for i, atom in enumerate(self._atoms):
320321
if i not in pdbqt["flex_indices"] and atom["atom_type"] not in self.skip_types:
321322
pdbqt["rigid"] += self.write_pdbqt_line(atom)
322-
323+
323324
return pdbqt, ok, err
324325

325326
@staticmethod
@@ -343,7 +344,7 @@ def get_neigh(idx, bonds):
343344
two_bond_away.add(j)
344345
names_1bond = [atom_names[i] for i in one_bond_away]
345346
names_2bond = [atom_names[i] for i in two_bond_away]
346-
new_pdbqt_str = ""
347+
new_pdbqt_str = ""
347348
for i, line in enumerate(pdbqtstr.split(os_linesep)[:-1]):
348349
if line.startswith("ATOM") or line.startswith("HETATM"):
349350
name = line[12:16].strip()
@@ -394,13 +395,13 @@ def positions(self, atom_idx=None):
394395
return np.atleast_2d(self.atoms(atom_idx)['xyz'])
395396

396397
def closest_atoms_from_positions(self, xyz, radius, atom_properties=None, ignore=None):
397-
"""Retrieve indices of the closest atoms around a positions/coordinates
398+
"""Retrieve indices of the closest atoms around a positions/coordinates
398399
at a certain radius.
399400
400401
Args:
401402
xyz (np.ndarray): array of 3D coordinates
402403
raidus (float): radius
403-
atom_properties (str): property of the atoms to retrieve
404+
atom_properties (str): property of the atoms to retrieve
404405
(properties: ligand, flexible_residue, vdw, hb_don, hb_acc, metal, water, reactive, glue)
405406
ignore (int or list): ignore atom for the search using atom id (0-based)
406407
@@ -442,13 +443,13 @@ def closest_atoms_from_positions(self, xyz, radius, atom_properties=None, ignore
442443
return atoms
443444

444445
def closest_atoms(self, atom_idx, radius, atom_properties=None):
445-
"""Retrieve indices of the closest atoms around a positions/coordinates
446+
"""Retrieve indices of the closest atoms around a positions/coordinates
446447
at a certain radius.
447448
448449
Args:
449450
atom_idx (int, list): index of one or multiple atoms (0-based)
450451
raidus (float): radius
451-
atom_properties (str or list): property of the atoms to retrieve
452+
atom_properties (str or list): property of the atoms to retrieve
452453
(properties: ligand, flexible_residue, vdw, hb_don, hb_acc, metal, water, reactive, glue)
453454
454455
Returns:

scripts/mk_prepare_receptor.py

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def parse_residue_string(string):
5151
err += "resnum could not be converted to integer, it was '%s' in '%s'" % (resnum, string) + os_linesep
5252
if ok:
5353
res_id = (chain, resname, resnum)
54-
return res_id, ok, err
54+
return res_id, ok, err
5555

5656
def parse_residue_string_and_name(string):
5757
"""
@@ -127,6 +127,8 @@ def get_args():
127127
parser.add_argument('--box_size', help="size of grid box (x, y, z) in Angstrom", nargs=3, type=float)
128128
parser.add_argument('--box_center', help="center of grid box (x, y, z) in Angstrom", nargs=3, type=float)
129129
parser.add_argument('--box_center_on_reactive_res', help="project center of grid box along CA-CB bond 5 A away from CB", action="store_true")
130+
parser.add_argument('--ligand', help="PDBQT of reference ligand")
131+
parser.add_argument('--padding', help="padding around reference ligand [A]", type=float)
130132
parser.add_argument('--skip_gpf', help="do not write a GPF file for autogrid", action="store_true")
131133
parser.add_argument('--r_eq_12', default=1.8, type=float, help="r_eq for reactive atoms (1-2 interaction)")
132134
parser.add_argument('--eps_12', default=2.5, type=float, help="epsilon for reactive atoms (1-2 interaction)")
@@ -142,21 +144,28 @@ def get_args():
142144
msg = "can't use both --box_center and --box_center_on_reactive_res"
143145
print("Command line error: " + msg, file=sys.stderr)
144146
sys.exit(2)
145-
got_center = (args.box_center is not None) or args.box_center_on_reactive_res
146-
if not args.skip_gpf and (args.box_size is None) == got_center:
147-
msg = "missing center or size of grid box to write .gpf file for autogrid4" + os_linesep
148-
msg += "use --box_size and either --box_center or --box_center_on_reactive_res" + os_linesep
149-
msg += "Exactly one reactive residue required for --box_center_on_reactive_res" + os_linesep
150-
msg += "If a GPF file is not needed (e.g. docking with Vina scoring function) use option --skip_gpf"
151-
print("Command line error: " + msg, file=sys.stderr)
152-
sys.exit(2)
153-
if (args.box_size is None) and not args.skip_gpf:
154-
msg = "grid box information is needed to dock with the AD4 scoring function." + os_linesep
155-
msg += "The grid box center and size will be used to write a GPF file for autogrid" + os_linesep
156-
msg += "If a GPF file is not needed (e.g. docking with Vina scoring function) use option --skip_gpf"
147+
got_center = (args.box_center is not None) or args.box_center_on_reactive_res or (args.ligand is not None)
148+
if not args.skip_gpf:
149+
if not got_center:
150+
msg = "missing center or size of grid box to write .gpf file for autogrid4" + os_linesep
151+
msg += "use --box_size and either --box_center or --box_center_on_reactive_res" + os_linesep
152+
msg += "or --ligand and --padding" + os_linesep
153+
msg += "Exactly one reactive residue required for --box_center_on_reactive_res" + os_linesep
154+
msg += "If a GPF file is not needed (e.g. docking with Vina scoring function) use option --skip_gpf"
155+
print("Command line error: " + msg, file=sys.stderr)
156+
sys.exit(2)
157+
if (args.box_size is None) and (args.padding is None):
158+
msg = "grid box information is needed to dock with the AD4 scoring function." + os_linesep
159+
msg += "The grid box center and size will be used to write a GPF file for autogrid" + os_linesep
160+
msg += "If a GPF file is not needed (e.g. docking with Vina scoring function) use option --skip_gpf"
161+
print("Command line error: " + msg, file=sys.stderr)
162+
sys.exit(2)
163+
164+
if (args.box_center is not None) + (args.ligand is not None) + args.box_center_on_reactive_res > 1:
165+
msg = "--box_center, --box_center_on_reactive_res, and --ligand are mutually exclusive options"
157166
print("Command line error: " + msg, file=sys.stderr)
158167
sys.exit(2)
159-
168+
160169
return args
161170

162171
args = get_args()
@@ -183,34 +192,34 @@ def get_args():
183192
all_ok = True
184193
all_err = ""
185194
for resid_string in args.reactive_flexres:
186-
res_id, ok, err = parse_residue_string(resid_string)
195+
res_id, ok, err = parse_residue_string(resid_string)
187196
if ok:
188197
resname = res_id[1]
189198
if resname in reactive_atom:
190199
reactive_flexres[res_id] = reactive_atom[resname]
191200
else:
192201
all_ok = False
193-
all_err += "no default reactive name for %s, " % resname
202+
all_err += "no default reactive name for %s, " % resname
194203
all_err += "use --reactive_name or --reactive_name_specific" + os_linesep
195204
all_ok &= ok
196205
all_err += err
197206

198207
for string in args.reactive_name_specific:
199-
out, ok, err = parse_residue_string_and_name(string)
208+
out, ok, err = parse_residue_string_and_name(string)
200209
if ok:
201210
# override name if res_id was also passed to --reactive_flexres
202211
reactive_flexres[out["res_id"]] = out["name"]
203212
all_ok &= ok
204213
all_err += err
205214

206-
if len(reactive_flexres) > 8:
215+
if len(reactive_flexres) > 8:
207216
msg = "got %d reactive_flexres but maximum is 8." % (len(args.reactive_flexres))
208217
print("Command line error: " + msg, file=sys.stderr)
209218
sys.exit(2)
210219

211220
all_flexres = set()
212221
for resid_string in args.flexres:
213-
res_id, ok, err = parse_residue_string(resid_string)
222+
res_id, ok, err = parse_residue_string(resid_string)
214223
all_ok &= ok
215224
all_err += err
216225
if ok:
@@ -247,7 +256,7 @@ def get_args():
247256
msg += "residue, but %d reactive residues are set" % len(reactive_flexres)
248257
print("Command line error:" + msg, file=sys.stderr)
249258
sys.exit(2)
250-
259+
251260
if args.pdb is not None:
252261
receptor = PDBQTReceptor(args.pdb, skip_typing=True)
253262
ok, err = receptor.assign_types_charges()
@@ -257,7 +266,7 @@ def get_args():
257266

258267
any_lig_base_types = ["HD", "C", "A", "N", "NA", "OA", "F", "P", "SA",
259268
"S", "Cl", "CL", "Br", "BR", "I", "Si", "B"]
260-
269+
261270
pdbqt, ok, err = receptor.write_pdbqt_string(flexres=all_flexres)
262271
check(ok, err)
263272

@@ -283,7 +292,7 @@ def get_args():
283292

284293
suffix = outpath.suffix
285294
if outpath.suffix == "":
286-
suffix = ".pdbqt"
295+
suffix = ".pdbqt"
287296
rigid_fn = str(outpath.with_suffix("")) + "_rigid" + suffix
288297
flex_fn = str(outpath.with_suffix("")) + "_flex" + suffix
289298

@@ -295,16 +304,17 @@ def get_args():
295304
written_files_log["filename"].append(rigid_fn)
296305
written_files_log["description"].append("static (i.e., rigid) receptor input file")
297306
with open(rigid_fn, "w") as f:
298-
f.write(pdbqt["rigid"])
307+
f.write(pdbqt["rigid"])
299308

300309
# GPF for autogrid4
301310
if not args.skip_gpf:
302311
if args.box_center is not None:
303312
box_center = args.box_center
313+
box_size = args.box_size
304314
elif args.box_center_on_reactive_res:
305315
# we have only one reactive residue and will set the box center
306316
# to be 5 Angstromg away from CB along the CA->CB vector
307-
idxs = receptor.atom_idxs_by_res[list(reactive_flexres.keys())[0]]
317+
idxs = receptor.atom_idxs_by_res[list(reactive_flexres.keys())[0]]
308318
ca = None
309319
cb = None
310320
for atom in receptor.atoms(idxs):
@@ -317,6 +327,9 @@ def get_args():
317327
v = (cb - ca)
318328
v /= math.sqrt(v[0]**2 + v[1]**2 + v[2]**2) + 1e-8
319329
box_center = ca + 5 * v
330+
box_size = args.box_size
331+
elif args.ligand is not None:
332+
box_center, box_size = gridbox.calc_box(args.ligand, args.padding)
320333
else:
321334
print("Error: No box center specified.", file=sys.stderr)
322335
sys.exit(2)
@@ -328,7 +341,7 @@ def get_args():
328341
with open(ff_fn, "w") as f:
329342
f.write(gridbox.boron_silicon_atompar)
330343
rec_types = set(t for (i, t) in enumerate(receptor.atoms()["atom_type"]) if i not in pdbqt["flex_indices"])
331-
gpf_string, npts = gridbox.get_gpf_string(box_center, args.box_size, rigid_fn, rec_types, any_lig_base_types,
344+
gpf_string, npts = gridbox.get_gpf_string(box_center, box_size, rigid_fn, rec_types, any_lig_base_types,
332345
ff_param_fname=ff_fn.name)
333346
# write GPF
334347
gpf_fn = pathlib.Path(rigid_fn).with_suffix(".gpf")
@@ -351,7 +364,7 @@ def get_args():
351364
print("WARNING: Flexible residue outside box." + os_linesep, file=sys.stderr)
352365
print("WARNING: Strongly recommended to use a box that encompasses flexible residues." + os_linesep, file=sys.stderr)
353366
break # only need to warn once
354-
367+
355368
# configuration info for AutoDock-GPU reactive docking
356369
if len(reactive_flexres) > 0:
357370
any_lig_reac_types = []
@@ -363,7 +376,7 @@ def get_args():
363376
for line in all_flex_pdbqt.split(os_linesep):
364377
if line.startswith("ATOM") or line.startswith("HETATM"):
365378
atype = line[77:].strip()
366-
basetype, _ = reactive_typer.get_basetype_and_order(atype)
379+
basetype, _ = reactive_typer.get_basetype_and_order(atype)
367380
if basetype is not None: # is None if not reactive
368381
rec_reac_types.append(line[77:].strip())
369382

0 commit comments

Comments
 (0)