mutadock 1.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docking/__init__.py +40 -0
- docking/np_docking.py +205 -0
- docking/vina_dock.py +83 -0
- docking/vina_helper.py +340 -0
- mutadock-1.1.dist-info/METADATA +95 -0
- mutadock-1.1.dist-info/RECORD +20 -0
- mutadock-1.1.dist-info/WHEEL +5 -0
- mutadock-1.1.dist-info/entry_points.txt +9 -0
- mutadock-1.1.dist-info/licenses/LICENSE +674 -0
- mutation/Amino.py +559 -0
- mutation/__init__.py +40 -0
- mutation/csv_generator.py +156 -0
- mutation/csv_sort.py +110 -0
- mutation/ddg_calc.py +135 -0
- mutation/ddg_calc_double.py +179 -0
- mutation/ddg_calc_triple.py +204 -0
- mutation/generate_mutants.py +182 -0
- mutation/helpers.py +117 -0
- mutation/np_mutation.py +205 -0
- mutation/predict_ddG.py +70 -0
docking/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# PROJECT INFORMATION #
|
|
3
|
+
# Name: MUTADOCK #
|
|
4
|
+
# Author: Naisarg Patel #
|
|
5
|
+
# #
|
|
6
|
+
# Copyright (C) 2024 Naisarg Patel (https://github.com/naisarg14) #
|
|
7
|
+
# #
|
|
8
|
+
# Project: https://github.com/naisarg14/mutadock #
|
|
9
|
+
# #
|
|
10
|
+
# This program is free software; you can redistribute it and/or modify it #
|
|
11
|
+
# under the terms of the GNU General Public License version 3 as published #
|
|
12
|
+
# by the Free Software Foundation. #
|
|
13
|
+
# #
|
|
14
|
+
# This program is distributed in the hope that it will be useful, but #
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
|
|
16
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License #
|
|
17
|
+
# for more details. #
|
|
18
|
+
################################################################################
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
print("""
|
|
22
|
+
################################################################################
|
|
23
|
+
# PROJECT INFORMATION #
|
|
24
|
+
# Name: MUTADOCK #
|
|
25
|
+
# Author: Naisarg Patel #
|
|
26
|
+
# #
|
|
27
|
+
# Copyright (C) 2024 Naisarg Patel (https://github.com/naisarg14) #
|
|
28
|
+
# #
|
|
29
|
+
# Project: https://github.com/naisarg14/mutadock #
|
|
30
|
+
# #
|
|
31
|
+
# This program is free software; you can redistribute it and/or modify it #
|
|
32
|
+
# under the terms of the GNU General Public License version 3 as published #
|
|
33
|
+
# by the Free Software Foundation. #
|
|
34
|
+
# #
|
|
35
|
+
# This program is distributed in the hope that it will be useful, but #
|
|
36
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
|
|
37
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License #
|
|
38
|
+
# for more details. #
|
|
39
|
+
################################################################################
|
|
40
|
+
""")
|
docking/np_docking.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# PROJECT INFORMATION #
|
|
3
|
+
# Name: MUTADOCK #
|
|
4
|
+
# Author: Naisarg Patel #
|
|
5
|
+
# #
|
|
6
|
+
# Copyright (C) 2024 Naisarg Patel (https://github.com/naisarg14) #
|
|
7
|
+
# #
|
|
8
|
+
# Project: https://github.com/naisarg14/mutadock #
|
|
9
|
+
# #
|
|
10
|
+
# This program is free software; you can redistribute it and/or modify it #
|
|
11
|
+
# under the terms of the GNU General Public License version 3 as published #
|
|
12
|
+
# by the Free Software Foundation. #
|
|
13
|
+
# #
|
|
14
|
+
# This program is distributed in the hope that it will be useful, but #
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
|
|
16
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License #
|
|
17
|
+
# for more details. #
|
|
18
|
+
################################################################################
|
|
19
|
+
|
|
20
|
+
from itertools import product
|
|
21
|
+
import os, time, sys
|
|
22
|
+
import argparse
|
|
23
|
+
from contextlib import contextmanager
|
|
24
|
+
from tqdm import tqdm
|
|
25
|
+
from vina_helper import prepare_receptor, prepare_ligand, vina_split, add_score_to_csv, dock_vina, read_config, calculate_geometric_center, backup
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@contextmanager
|
|
29
|
+
def suppress_stdout():
|
|
30
|
+
with open(os.devnull, "w") as devnull:
|
|
31
|
+
old_stdout = sys.stdout
|
|
32
|
+
sys.stdout = devnull
|
|
33
|
+
try:
|
|
34
|
+
yield
|
|
35
|
+
finally:
|
|
36
|
+
sys.stdout = old_stdout
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def naisarg():
|
|
40
|
+
start_time = time.time()
|
|
41
|
+
receptors, ligands, config, autosite, quiet, completed_name, ignore_existing = prepare_inputs()
|
|
42
|
+
|
|
43
|
+
if config is None and autosite is None:
|
|
44
|
+
print("Both config and autosite not provided. Assuming center as [0,0,0] and box_size as [30,30,30].")
|
|
45
|
+
|
|
46
|
+
if config is not None:
|
|
47
|
+
values = read_config
|
|
48
|
+
values = read_config(config)
|
|
49
|
+
if values[0] is False:
|
|
50
|
+
sys.exit(f"Error while reading the config file {config} \nError: {values[1]}")
|
|
51
|
+
_, center, box_size, exhaustiveness, n_poses, n_poses_write, overwrite = values
|
|
52
|
+
|
|
53
|
+
if autosite is not None:
|
|
54
|
+
site = calculate_geometric_center(autosite)
|
|
55
|
+
if site[0] is not False:
|
|
56
|
+
center = site[0]
|
|
57
|
+
else:
|
|
58
|
+
sys.exit(f"Error while calculating the geometric center of the autosite file {autosite} \nError: {site[1]}")
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
with open(completed_name, "r") as f:
|
|
62
|
+
completed = f.readlines()
|
|
63
|
+
completed = [x.replace("\n", "") for x in completed]
|
|
64
|
+
except FileNotFoundError:
|
|
65
|
+
completed = []
|
|
66
|
+
|
|
67
|
+
if ignore_existing:
|
|
68
|
+
completed = []
|
|
69
|
+
|
|
70
|
+
combinations = list(product(receptors, ligands))
|
|
71
|
+
|
|
72
|
+
combinations = [x for x in combinations if str(x) not in completed]
|
|
73
|
+
|
|
74
|
+
if not quiet and not ignore_existing: print(f"Found {len(completed)} completed receptor-ligand combinations. {len(combinations)} combinations to be docked.")
|
|
75
|
+
|
|
76
|
+
for combination in tqdm(combinations):
|
|
77
|
+
receptor = combination[0]
|
|
78
|
+
ligand = combination[1]
|
|
79
|
+
prepared_receptor = f"{receptor}qt"
|
|
80
|
+
|
|
81
|
+
if ligand.endswith(".sdf"):
|
|
82
|
+
prepared_ligand = f"{ligand.removesuffix(".sdf")}.pdbqt"
|
|
83
|
+
elif ligand.endswith(".mol2"):
|
|
84
|
+
prepared_ligand = f"{ligand.removesuffix(".mol2")}.pdbqt"
|
|
85
|
+
|
|
86
|
+
if not quiet: print(f"Docking the receptor {receptor} to the ligand {ligand}")
|
|
87
|
+
master_folder_receptor, receptor_file = os.path.split(os.path.abspath(receptor))
|
|
88
|
+
master_folder_ligand, ligand_file = os.path.split(os.path.abspath(ligand))
|
|
89
|
+
|
|
90
|
+
output_dir = os.path.join(master_folder_receptor, 'out')
|
|
91
|
+
if not os.path.exists(output_dir):
|
|
92
|
+
os.makedirs(output_dir)
|
|
93
|
+
|
|
94
|
+
out_pdb = os.path.join(output_dir, f'{receptor_file.removesuffix(".pdb")}_{ligand_file.removesuffix(".sdf")}_out.pdb')
|
|
95
|
+
log_file = os.path.join(output_dir, f'{receptor_file.removesuffix(".pdb")}_{ligand_file.removesuffix(".sdf")}_log.txt')
|
|
96
|
+
csv_file = os.path.join(output_dir, "docking_results.csv")
|
|
97
|
+
|
|
98
|
+
backup(out_pdb)
|
|
99
|
+
backup(log_file)
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
if not quiet: print(f"Docking for {ligand} with {receptor}")
|
|
103
|
+
if not quiet: print("Press Ctrl+D (EOFE Error) to skip this receptor-ligand combination.")
|
|
104
|
+
if not os.path.exists(prepared_receptor) or ignore_existing:
|
|
105
|
+
if not quiet: print(f"Preparing receptor {receptor}")
|
|
106
|
+
with suppress_stdout(): rec_out = prepare_receptor(receptor_filename=receptor, outputfilename=prepared_receptor)
|
|
107
|
+
if not rec_out[0]:
|
|
108
|
+
print(f"Error while preparing receptor {receptor} \nError: {rec_out[1]} \nSkipping this receptor-ligand combination.")
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
if not os.path.exists(prepared_ligand) or ignore_existing:
|
|
112
|
+
if not quiet: print(f"Preparing ligand {ligand}")
|
|
113
|
+
with suppress_stdout(): lig_out = prepare_ligand(in_file=ligand, out_file=prepared_ligand)
|
|
114
|
+
if not lig_out[0]:
|
|
115
|
+
print(f"Error while preparing ligand {ligand} \nError: {lig_out[1]} \nSkipping this receptor-ligand combination.")
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
if not quiet: print(f"Starting docking")
|
|
119
|
+
with suppress_stdout(): vina_out = dock_vina(prepared_receptor, prepared_ligand, out_pdb, log_file, center=center, box_size=box_size, exhaustiveness=exhaustiveness, n_poses=n_poses, n_poses_write=n_poses_write, overwrite=overwrite)
|
|
120
|
+
if not vina_out[0]:
|
|
121
|
+
print(f"Error while docking {ligand} to {receptor} \nError: {vina_out[1]} \nSkipping this receptor-ligand combination.")
|
|
122
|
+
continue
|
|
123
|
+
if not quiet: print("Docking Completed, writing log file")
|
|
124
|
+
|
|
125
|
+
if not quiet: print("Getting the ligand 1 after docking")
|
|
126
|
+
ligand_1 = out_pdb.replace('.pdbqt', '_ligand_1.sdf')
|
|
127
|
+
with suppress_stdout(): score, _ = vina_split(input_file=out_pdb, output_file=out_pdb)
|
|
128
|
+
if not quiet: print("Adding affinity to CSV")
|
|
129
|
+
csv_add = add_score_to_csv(out_pdb, csv_file, score)
|
|
130
|
+
if not csv_add[0]:
|
|
131
|
+
print(f"Error while adding affinity to CSV file {csv_file} \nError: {csv_add[1]}")
|
|
132
|
+
|
|
133
|
+
with open(completed_name, "a+") as file: file.write(f"{combination}\n")
|
|
134
|
+
|
|
135
|
+
if not quiet: print(f"Docking completed, log file is {log_file}, ligand_1 is {ligand_1.replace("pdbqt", "sdf")}, docking affinity is {score}. \n")
|
|
136
|
+
|
|
137
|
+
except EOFError:
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
end_time = time.time()
|
|
142
|
+
elapsed_time = (end_time - start_time)/60
|
|
143
|
+
|
|
144
|
+
print(f"All Outputs are saved in the folder: {output_dir}")
|
|
145
|
+
|
|
146
|
+
print(f"Completed in {elapsed_time:.2f} minutes!")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def prepare_inputs():
|
|
150
|
+
parser = argparse.ArgumentParser(prog="np_dock", description=None, epilog="Part of mutadock library. Written by Naisarg Patel (https://github.com/naisarg14)")
|
|
151
|
+
parser.add_argument('receptor_txt', help="Text File with all receptors", metavar="RECEPTOR")
|
|
152
|
+
parser.add_argument("ligand_txt", help="Text File with all ligands", metavar="LIGAND")
|
|
153
|
+
parser.add_argument("--config",default=None, help="Text File with all Vina Configuration Settings", metavar="CONFIG")
|
|
154
|
+
parser.add_argument("--autosite",default=None, help="PDB generated by autosite for binding site of protein.", metavar="AUTOSITE")
|
|
155
|
+
parser.add_argument("--quiet", action="store_true", help="Run the Docking in quiet mode (default: False).")
|
|
156
|
+
parser.add_argument("--ignore_existing", action="store_true", help="Run the Docking while ingoring existing files. All dockings will be performed again. (default: False).")
|
|
157
|
+
|
|
158
|
+
args = parser.parse_args()
|
|
159
|
+
|
|
160
|
+
receptor_txt = args.receptor_txt
|
|
161
|
+
ligand_txt = args.ligand_txt
|
|
162
|
+
config = args.config
|
|
163
|
+
|
|
164
|
+
print("Receptor file:", receptor_txt)
|
|
165
|
+
print("Ligands file:", ligand_txt)
|
|
166
|
+
print("Config file:", config)
|
|
167
|
+
for f in [receptor_txt, ligand_txt]:
|
|
168
|
+
if not os.path.exists(f):
|
|
169
|
+
print(f"{f} not found or cannot be opened.")
|
|
170
|
+
try:
|
|
171
|
+
with open(receptor_txt, "r") as rec:
|
|
172
|
+
receptors = rec.readlines()
|
|
173
|
+
except IOError as err:
|
|
174
|
+
sys.exit(f"Error reading the file {receptor_txt}: ".format(receptor_txt, err))
|
|
175
|
+
try:
|
|
176
|
+
with open(ligand_txt, "r") as lig:
|
|
177
|
+
ligands = lig.readlines()
|
|
178
|
+
except IOError as err:
|
|
179
|
+
sys.exit(f"Error reading the file {ligand_txt}: ".format(ligand_txt, err))
|
|
180
|
+
|
|
181
|
+
for i in range(len(receptors)):
|
|
182
|
+
receptors[i] = receptors[i].replace("\n", "")
|
|
183
|
+
if not os.path.isabs(receptors[i]):
|
|
184
|
+
receptors[i] = os.path.join(os.getcwd(), receptors[i])
|
|
185
|
+
for i in range(len(ligands)):
|
|
186
|
+
ligands[i] = ligands[i].replace("\n", "")
|
|
187
|
+
if not os.path.isabs(ligands[i]):
|
|
188
|
+
ligands[i] = os.path.join(os.getcwd(), ligands[i])
|
|
189
|
+
|
|
190
|
+
receptor_basename = os.path.basename(receptor_txt)
|
|
191
|
+
ligand_basename = os.path.basename(ligand_txt)
|
|
192
|
+
|
|
193
|
+
if os.path.isabs(receptor_txt):
|
|
194
|
+
rec_folder = os.path.dirname(receptor_txt)
|
|
195
|
+
else:
|
|
196
|
+
rec_folder = os.path.dirname(os.path.abspath(receptor_txt))
|
|
197
|
+
|
|
198
|
+
completed_file_name = f"{receptor_basename.removesuffix(".txt")}_{ligand_basename.removesuffix(".txt")}_completed.txt"
|
|
199
|
+
completed_name = os.path.join(rec_folder, completed_file_name)
|
|
200
|
+
|
|
201
|
+
return receptors, ligands, config, args.autosite, args.quiet, completed_name, args.ignore_existing
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
naisarg()
|
docking/vina_dock.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# PROJECT INFORMATION #
|
|
3
|
+
# Name: MUTADOCK #
|
|
4
|
+
# Author: Naisarg Patel #
|
|
5
|
+
# #
|
|
6
|
+
# Copyright (C) 2024 Naisarg Patel (https://github.com/naisarg14) #
|
|
7
|
+
# #
|
|
8
|
+
# Project: https://github.com/naisarg14/mutadock #
|
|
9
|
+
# #
|
|
10
|
+
# This program is free software; you can redistribute it and/or modify it #
|
|
11
|
+
# under the terms of the GNU General Public License version 3 as published #
|
|
12
|
+
# by the Free Software Foundation. #
|
|
13
|
+
# #
|
|
14
|
+
# This program is distributed in the hope that it will be useful, but #
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
|
|
16
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License #
|
|
17
|
+
# for more details. #
|
|
18
|
+
################################################################################
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import sys, argparse
|
|
22
|
+
|
|
23
|
+
def vina_dock(receptor, ligand, output, center=[0, 0, 0], box_size=[30, 30, 30], exhaustiveness=32, n_poses=20, n_poses_write=5, overwrite=True):
|
|
24
|
+
try:
|
|
25
|
+
from vina import Vina
|
|
26
|
+
except ModuleNotFoundError:
|
|
27
|
+
msg = "Error with importing modules for docking using Vina.\n"
|
|
28
|
+
msg += "Easiest way to fix this is to install vina using the following command:\n\n"
|
|
29
|
+
msg += "python -m pip install vina\n"
|
|
30
|
+
msg += "If you already have vina installed, please check the installation.\n"
|
|
31
|
+
msg += "If the problem persists, please create a github issue or contact developer at naisarg.patel14@hotmail.com"
|
|
32
|
+
print(msg)
|
|
33
|
+
sys.exit(2)
|
|
34
|
+
try:
|
|
35
|
+
print("Docking done using mutadock library developed by Naisarg Patel (Github:@naisarg14)")
|
|
36
|
+
v = Vina(sf_name='vina')
|
|
37
|
+
|
|
38
|
+
v.set_receptor(receptor)
|
|
39
|
+
v.set_ligand_from_file(ligand)
|
|
40
|
+
print(v)
|
|
41
|
+
v.compute_vina_maps(center=center, box_size=box_size)
|
|
42
|
+
|
|
43
|
+
v.dock(exhaustiveness=exhaustiveness, n_poses=n_poses)
|
|
44
|
+
v.write_poses(output, n_poses=n_poses_write, overwrite=overwrite)
|
|
45
|
+
|
|
46
|
+
except Exception as e:
|
|
47
|
+
return (False, e)
|
|
48
|
+
return (True, "")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
parser = argparse.ArgumentParser(description="Run docking using AutoDock vina Python bindings.")
|
|
53
|
+
|
|
54
|
+
parser.add_argument("--receptor", type=str, help="Path to the receptor file.")
|
|
55
|
+
parser.add_argument("--ligand", type=str, help="Path to the ligand file.")
|
|
56
|
+
parser.add_argument("--output", type=str, help="Path for saving the output file.")
|
|
57
|
+
parser.add_argument("--log_file", type=str, help="Path for saving the log file.")
|
|
58
|
+
|
|
59
|
+
parser.add_argument("--center", nargs=3, type=float, default=[0, 0, 0], help="X-dimension of the center of search box (default: [0, 0, 0]).")
|
|
60
|
+
|
|
61
|
+
parser.add_argument("--box_size", nargs=3, type=float, default=[30, 30, 30], help="Size of the search box (default: [30, 30, 30]).")
|
|
62
|
+
parser.add_argument("--exhaustiveness", type=int, default=32, help="Exhaustiveness of the search (default: 32).")
|
|
63
|
+
parser.add_argument("--n_poses", type=int, default=20, help="Number of poses to generate (default: 20).")
|
|
64
|
+
parser.add_argument("--n_poses_write", type=int, default=5, help="Number of poses to write to the output (default: 5).")
|
|
65
|
+
parser.add_argument("--nooverwrite", action="store_false", default=True, help="Do not overwrite existing files.")
|
|
66
|
+
|
|
67
|
+
args = parser.parse_args()
|
|
68
|
+
|
|
69
|
+
vina_dock(
|
|
70
|
+
receptor=args.receptor,
|
|
71
|
+
ligand=args.ligand,
|
|
72
|
+
output=args.output,
|
|
73
|
+
center=args.center,
|
|
74
|
+
box_size=args.box_size,
|
|
75
|
+
exhaustiveness=args.exhaustiveness,
|
|
76
|
+
n_poses=args.n_poses,
|
|
77
|
+
n_poses_write=args.n_poses_write,
|
|
78
|
+
overwrite=args.nooverwrite,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
main()
|
docking/vina_helper.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# PROJECT INFORMATION #
|
|
3
|
+
# Name: MUTADOCK #
|
|
4
|
+
# Author: Naisarg Patel #
|
|
5
|
+
# #
|
|
6
|
+
# Copyright (C) 2024 Naisarg Patel (https://github.com/naisarg14) #
|
|
7
|
+
# #
|
|
8
|
+
# Project: https://github.com/naisarg14/mutadock #
|
|
9
|
+
# #
|
|
10
|
+
# This program is free software; you can redistribute it and/or modify it #
|
|
11
|
+
# under the terms of the GNU General Public License version 3 as published #
|
|
12
|
+
# by the Free Software Foundation. #
|
|
13
|
+
# #
|
|
14
|
+
# This program is distributed in the hope that it will be useful, but #
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
|
|
16
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License #
|
|
17
|
+
# for more details. #
|
|
18
|
+
################################################################################
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def backup(file_path):
|
|
22
|
+
import os
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
|
|
25
|
+
if not os.path.exists(file_path):
|
|
26
|
+
return False
|
|
27
|
+
master_folder, file = os.path.split(os.path.abspath(file_path))
|
|
28
|
+
target_directory = os.path.join(master_folder, 'backups')
|
|
29
|
+
if not os.path.exists(target_directory):
|
|
30
|
+
os.makedirs(target_directory)
|
|
31
|
+
modified_time = os.path.getmtime(file_path)
|
|
32
|
+
timestamp = datetime.fromtimestamp(modified_time).strftime("%b-%d-%Y_%H.%M")
|
|
33
|
+
name, ext = os.path.splitext(file)
|
|
34
|
+
target_file = os.path.join(target_directory, f'{name}_{timestamp}{ext}')
|
|
35
|
+
os.rename(file_path, target_file)
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
def read_pdb_file(file_path):
|
|
39
|
+
import re
|
|
40
|
+
try:
|
|
41
|
+
atoms = []
|
|
42
|
+
pattern = r'^ATOM\s+(\d+)\s+([A-Z]+)\s+([A-Z]{2,3})\s+([A-Z]?)\s*(\d+)\s+(-?\d+\.\d{3})\s+(-?\d+\.\d{3})\s+(-?\d+\.\d{3})\s+(\d+\.\d{1,2})\s+(\d+\.\d{1,3})(?:\s+(\d+\.\d{1,3}))?\s+[A-Z]$'
|
|
43
|
+
with open(file_path, 'r') as file:
|
|
44
|
+
for line in file:
|
|
45
|
+
if line.startswith(('ATOM', 'HETATM')):
|
|
46
|
+
match = re.match(pattern, line)
|
|
47
|
+
if match:
|
|
48
|
+
parts = {
|
|
49
|
+
"atom_serial_number": int(match.group(1)), # Atom Serial Number
|
|
50
|
+
"atom_name": match.group(2).strip(), # Atom Name
|
|
51
|
+
"residue_name": match.group(3).strip(), # Residue Name
|
|
52
|
+
"chain_id": match.group(4).strip() or None, # Chain Identifier (optional)
|
|
53
|
+
"residue_sequence_number": int(match.group(5)), # Residue Sequence Number
|
|
54
|
+
"x": float(match.group(6)), # X Coordinate
|
|
55
|
+
"y": float(match.group(7)), # Y Coordinate
|
|
56
|
+
"z": float(match.group(8)), # Z Coordinate
|
|
57
|
+
"occupancy": float(match.group(9)), # Occupancy
|
|
58
|
+
"temp_factor": float(match.group(10)), # Temperature Factor
|
|
59
|
+
"extra_factor": float(match.group(11)) if match.group(11) else None, # Extra Factor (optional)
|
|
60
|
+
}
|
|
61
|
+
atoms.append(parts)
|
|
62
|
+
return (atoms)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
return (False, e)
|
|
65
|
+
|
|
66
|
+
def calculate_geometric_center(pdb_file):
|
|
67
|
+
atoms = read_pdb_file(pdb_file)
|
|
68
|
+
num_atoms = len(atoms)
|
|
69
|
+
x_sum = sum(atom['x'] for atom in atoms)
|
|
70
|
+
y_sum = sum(atom['y'] for atom in atoms)
|
|
71
|
+
z_sum = sum(atom['z'] for atom in atoms)
|
|
72
|
+
|
|
73
|
+
return (x_sum / num_atoms, y_sum / num_atoms, z_sum / num_atoms)
|
|
74
|
+
|
|
75
|
+
def calculate_radius(pdb_file):
|
|
76
|
+
import math
|
|
77
|
+
atoms = read_pdb_file(pdb_file)
|
|
78
|
+
if isinstance(atoms, tuple):
|
|
79
|
+
return atoms
|
|
80
|
+
|
|
81
|
+
center = calculate_geometric_center(pdb_file)
|
|
82
|
+
max_distance = 0
|
|
83
|
+
|
|
84
|
+
for atom in atoms:
|
|
85
|
+
distance = math.sqrt(
|
|
86
|
+
(atom['x'] - center[0]) ** 2 +
|
|
87
|
+
(atom['y'] - center[1]) ** 2 +
|
|
88
|
+
(atom['z'] - center[2]) ** 2
|
|
89
|
+
)
|
|
90
|
+
if distance > max_distance:
|
|
91
|
+
max_distance = distance
|
|
92
|
+
|
|
93
|
+
return max_distance
|
|
94
|
+
|
|
95
|
+
def vina_split(input_file, output_file=None):
|
|
96
|
+
try:
|
|
97
|
+
import sys
|
|
98
|
+
from meeko import PDBQTMolecule, RDKitMolCreate
|
|
99
|
+
except ModuleNotFoundError:
|
|
100
|
+
msg = "Error with importing modules for preparing ligand files for Docking.\n"
|
|
101
|
+
msg += "Easaies way to fix this is to install meeko using the following command:\n\n"
|
|
102
|
+
msg += "python -m pip install meeko\n"
|
|
103
|
+
msg += "If you already have meeko installed, please check the installation.\n"
|
|
104
|
+
msg += "If the problem persists, please create a github issue or contact developer at naisarg.patel14@hotmail.com"
|
|
105
|
+
print(msg)
|
|
106
|
+
sys.exit(2)
|
|
107
|
+
|
|
108
|
+
if output_file is None:
|
|
109
|
+
output_file = input_file.replace('.pdbqt', '_ligand_1.sdf')
|
|
110
|
+
|
|
111
|
+
pdbqt_string = ""
|
|
112
|
+
with open(input_file, 'r') as infile:
|
|
113
|
+
for line in infile:
|
|
114
|
+
if "vina result" in line.lower():
|
|
115
|
+
score = [float(x) for x in line.split() if x.replace('.', '', 1).replace('-', '', 1).isdigit()][0]
|
|
116
|
+
pdbqt_string += line
|
|
117
|
+
if line.startswith('ENDMDL'):
|
|
118
|
+
break
|
|
119
|
+
molecule = PDBQTMolecule(pdbqt_string)
|
|
120
|
+
sdf_string, failures = RDKitMolCreate.write_sd_string(molecule)
|
|
121
|
+
|
|
122
|
+
if len(failures) > 0:
|
|
123
|
+
msg = "\nCould not convert to RDKit. Maybe this library was not used for preparing\n"
|
|
124
|
+
msg += "the input PDBQT for docking, and the SMILES string is missing?\n"
|
|
125
|
+
msg += "Except for standard protein sidechains, all ligands and flexible residues\n"
|
|
126
|
+
msg += "require a REMARK SMILES line in the PDBQT, which is added automatically by meeko."
|
|
127
|
+
raise RuntimeError(msg)
|
|
128
|
+
|
|
129
|
+
footer_string = f"> <Docking Score>\n{score}\n> <Credits>\nCreated using a script in mutadock library written by Naisarg Patel (https://github.com/naisarg14/mutadock).\n$$$$\n"
|
|
130
|
+
with open(output_file, 'w') as outfile:
|
|
131
|
+
outfile.write(sdf_string.replace('$$$$', footer_string))
|
|
132
|
+
|
|
133
|
+
return (score, output_file)
|
|
134
|
+
|
|
135
|
+
def prepare_ligand(in_file, out_file=None):
|
|
136
|
+
try:
|
|
137
|
+
import sys
|
|
138
|
+
from meeko import MoleculePreparation, PDBQTWriterLegacy
|
|
139
|
+
from rdkit import Chem
|
|
140
|
+
except ModuleNotFoundError:
|
|
141
|
+
msg = "Error with importing modules for preparing ligand files for Docking.\n"
|
|
142
|
+
msg += "Easaies way to fix this is to install meeko and rdkit using the following command:\n\n"
|
|
143
|
+
msg += "python -m pip install meeko rdkit\n"
|
|
144
|
+
msg += "If you already have meeko and rdkit installed, please check the installation.\n"
|
|
145
|
+
msg += "If the problem persists, please create a github issue or contact developer at naisarg.patel14@hotmail.com"
|
|
146
|
+
print(msg)
|
|
147
|
+
sys.exit(2)
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
#Add support for PDB files
|
|
151
|
+
if out_file is None:
|
|
152
|
+
if in_file.endswith(".sdf"):
|
|
153
|
+
out_file = f"{in_file.removesuffix(".sdf")}.pdbqt"
|
|
154
|
+
elif in_file.endswith(".mol2"):
|
|
155
|
+
out_file = f"{in_file.removesuffix(".mol2")}.pdbqt"
|
|
156
|
+
else:
|
|
157
|
+
return (False, "Input file is not in SDF or MOL2 format.")
|
|
158
|
+
|
|
159
|
+
if in_file.endswith(".sdf"):
|
|
160
|
+
mol = Chem.SDMolSupplier(in_file)[0]
|
|
161
|
+
if in_file.endswith(".mol2"):
|
|
162
|
+
mol = Chem.MolFromMol2File(in_file)
|
|
163
|
+
|
|
164
|
+
mol = Chem.AddHs(mol)
|
|
165
|
+
mp = MoleculePreparation()
|
|
166
|
+
molecule_setups = mp.prepare(mol)
|
|
167
|
+
pdbqt_string, success, error_msg = PDBQTWriterLegacy.write_string(molecule_setups[0])
|
|
168
|
+
|
|
169
|
+
if not success:
|
|
170
|
+
raise RuntimeError(f"Could not convert to PDBQT: {error_msg}")
|
|
171
|
+
with open(out_file, "w") as output_file:
|
|
172
|
+
output_file.write(pdbqt_string)
|
|
173
|
+
except Exception as e:
|
|
174
|
+
return (False, e)
|
|
175
|
+
|
|
176
|
+
return (True, pdbqt_string)
|
|
177
|
+
|
|
178
|
+
def prepare_receptor(receptor_filename, outputfilename="None"):
|
|
179
|
+
try:
|
|
180
|
+
import sys, os
|
|
181
|
+
from MolKit import Read
|
|
182
|
+
from AutoDockTools.MoleculePreparation import AD4ReceptorPreparation
|
|
183
|
+
except ModuleNotFoundError:
|
|
184
|
+
msg = "Error with importing modules for preparing receptor files for Docking.\n"
|
|
185
|
+
msg += "Easaies way to fix this is to install AutoDockTools_py3 using the following command:\n\n"
|
|
186
|
+
msg += "python -m pip install git+https://github.com/Valdes-Tresanco-MS/AutoDockTools_py3\n"
|
|
187
|
+
msg += "If you already have AutoDockTools_py3 installed, please check the installation.\n"
|
|
188
|
+
msg += "If the problem persists, please create a github issue or contact developer at naisarg.patel14@hotmail.com"
|
|
189
|
+
print(msg)
|
|
190
|
+
sys.exit(2)
|
|
191
|
+
finally:
|
|
192
|
+
original_stdout = os.dup(1)
|
|
193
|
+
original_stderr = os.dup(2)
|
|
194
|
+
with open(os.devnull, 'w') as fnull:
|
|
195
|
+
os.dup2(fnull.fileno(), 1)
|
|
196
|
+
os.dup2(fnull.fileno(), 2)
|
|
197
|
+
|
|
198
|
+
if outputfilename is None:
|
|
199
|
+
outputfilename = f"{receptor_filename}qt"
|
|
200
|
+
|
|
201
|
+
# initialize required parameters
|
|
202
|
+
repairs = 'hydrogens'
|
|
203
|
+
charges_to_add = 'gasteiger'
|
|
204
|
+
cleanup = "waters"
|
|
205
|
+
|
|
206
|
+
mode = 'automatic'
|
|
207
|
+
delete_single_nonstd_residues = None
|
|
208
|
+
dictionary = None
|
|
209
|
+
unique_atom_names = False
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
mols = Read(receptor_filename)
|
|
213
|
+
mol = mols[0]
|
|
214
|
+
if unique_atom_names:
|
|
215
|
+
for at in mol.allAtoms:
|
|
216
|
+
if mol.allAtoms.get(at.name) >1:
|
|
217
|
+
at.name = at.name + str(at._uniqIndex +1)
|
|
218
|
+
|
|
219
|
+
if len(mols)>1:
|
|
220
|
+
#use the molecule with the most atoms
|
|
221
|
+
ctr = 1
|
|
222
|
+
for m in mols[1:]:
|
|
223
|
+
ctr += 1
|
|
224
|
+
if len(m.allAtoms)>len(mol.allAtoms):
|
|
225
|
+
mol = m
|
|
226
|
+
|
|
227
|
+
mol.buildBondsByDistance()
|
|
228
|
+
alt_loc_ats = mol.allAtoms.get(lambda x: "@" in x.name)
|
|
229
|
+
len_alt_loc_ats = len(alt_loc_ats)
|
|
230
|
+
if len_alt_loc_ats:
|
|
231
|
+
print("WARNING!", mol.name, "has",len_alt_loc_ats, ' alternate location atoms!\nUse prepare_pdb_split_alt_confs.py to create pdb files containing a single conformation.\n')
|
|
232
|
+
|
|
233
|
+
RPO = AD4ReceptorPreparation(mol, mode, repairs, charges_to_add,
|
|
234
|
+
cleanup, outputfilename=outputfilename,
|
|
235
|
+
delete_single_nonstd_residues=delete_single_nonstd_residues,
|
|
236
|
+
dict=dictionary)
|
|
237
|
+
except Exception as e:
|
|
238
|
+
os.dup2(original_stdout, 1)
|
|
239
|
+
os.dup2(original_stderr, 2)
|
|
240
|
+
os.close(original_stdout)
|
|
241
|
+
os.close(original_stderr)
|
|
242
|
+
return (False, e)
|
|
243
|
+
finally:
|
|
244
|
+
os.dup2(original_stdout, 1)
|
|
245
|
+
os.dup2(original_stderr, 2)
|
|
246
|
+
os.close(original_stdout)
|
|
247
|
+
os.close(original_stderr)
|
|
248
|
+
|
|
249
|
+
return (True, "")
|
|
250
|
+
|
|
251
|
+
def add_score_to_csv(out_pdb, csv_file, score):
|
|
252
|
+
import csv, os
|
|
253
|
+
try:
|
|
254
|
+
with open(csv_file, "r") as lc:
|
|
255
|
+
final_line = lc.readlines()[-1]
|
|
256
|
+
count = int(final_line.split(",")[0]) + 1
|
|
257
|
+
except FileNotFoundError:
|
|
258
|
+
count = 1
|
|
259
|
+
except ValueError:
|
|
260
|
+
count = 1
|
|
261
|
+
except Exception as e:
|
|
262
|
+
return (False, e)
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
name = f"{os.path.basename(out_pdb).removesuffix('_out.pdb')}"
|
|
266
|
+
with open(csv_file, "a+") as out:
|
|
267
|
+
writer = csv.DictWriter(out, fieldnames=["sr", "name", "affinity"])
|
|
268
|
+
writer.writerow({"sr": count, "name": name, "affinity": score})
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
return (False, e)
|
|
272
|
+
|
|
273
|
+
return (True, name)
|
|
274
|
+
|
|
275
|
+
def read_config(config_file):
|
|
276
|
+
try:
|
|
277
|
+
config = {}
|
|
278
|
+
with open(config_file, 'r') as file:
|
|
279
|
+
for line in file:
|
|
280
|
+
line = line.strip()
|
|
281
|
+
if line and not line.startswith('#') and "=" in line:
|
|
282
|
+
key, value = line.split('=')
|
|
283
|
+
config[key.strip()] = value.strip()
|
|
284
|
+
|
|
285
|
+
center_x = config.get('center_x', '0.0')
|
|
286
|
+
center_y = config.get('center_y', '0.0')
|
|
287
|
+
center_z = config.get('center_z', '0.0')
|
|
288
|
+
size_x = config.get('size_x', '30.0')
|
|
289
|
+
size_y = config.get('size_y', '30.0')
|
|
290
|
+
size_z = config.get('size_z', '30.0')
|
|
291
|
+
exhaustiveness = config.get('exhaustiveness', '32')
|
|
292
|
+
n_poses = config.get('n_poses', '20')
|
|
293
|
+
n_poses_write = config.get('n_poses_write', '5')
|
|
294
|
+
overwrite = config.get('overwrite', 'True')
|
|
295
|
+
|
|
296
|
+
center = (center_x, center_y, center_z)
|
|
297
|
+
box_size = (size_x, size_y, size_z)
|
|
298
|
+
|
|
299
|
+
return (True, center, box_size, exhaustiveness, n_poses, n_poses_write, overwrite)
|
|
300
|
+
|
|
301
|
+
except Exception as e:
|
|
302
|
+
return (False, e)
|
|
303
|
+
|
|
304
|
+
def dock_vina(receptor, ligand, output, log_file, config=None, autosite=None, center=[0, 0, 0], box_size=[30, 30, 30], exhaustiveness=32, n_poses=20, n_poses_write=5, overwrite=True):
|
|
305
|
+
if config is not None:
|
|
306
|
+
values = read_config(config)
|
|
307
|
+
if values[0] is False:
|
|
308
|
+
return values
|
|
309
|
+
_, center, box_size, exhaustiveness, n_poses, n_poses_write, overwrite = values
|
|
310
|
+
|
|
311
|
+
if autosite is not None:
|
|
312
|
+
center = calculate_geometric_center(autosite)
|
|
313
|
+
|
|
314
|
+
import subprocess, os
|
|
315
|
+
|
|
316
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
317
|
+
vina_dock_script = os.path.join(current_dir, 'vina_dock.py')
|
|
318
|
+
|
|
319
|
+
commands = [
|
|
320
|
+
"python3", vina_dock_script,
|
|
321
|
+
"--receptor", receptor,
|
|
322
|
+
"--ligand", ligand,
|
|
323
|
+
"--output", output,
|
|
324
|
+
"--center", str(center[0]), str(center[1]), str(center[2]),
|
|
325
|
+
"--box_size", str(box_size[0]), str(box_size[1]), str(box_size[2]),
|
|
326
|
+
"--exhaustiveness", str(exhaustiveness),
|
|
327
|
+
"--n_poses", str(n_poses),
|
|
328
|
+
"--n_poses_write", str(n_poses_write),
|
|
329
|
+
]
|
|
330
|
+
if not overwrite: commands.append("--nooverwrite")
|
|
331
|
+
with open(log_file, 'w+') as lfile:
|
|
332
|
+
result = subprocess.run(commands, stdout=lfile, stderr=lfile, text=True)
|
|
333
|
+
|
|
334
|
+
if result.returncode != 0:
|
|
335
|
+
return (False, f"Check the error in {log_file}")
|
|
336
|
+
|
|
337
|
+
return (True, "")
|
|
338
|
+
|
|
339
|
+
if __name__ == "__main__":
|
|
340
|
+
print("This is a dependency file for mutadock (https://github.com/naisarg14/mutadock) library's docking module.")
|