Fast-HInt-ppi 0.1.0__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.
Fast_HInt/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Fast_HInt"""
@@ -0,0 +1,340 @@
1
+ #!/usr/bin/env python3
2
+ #Adapted from get_good_inter_pae.py (https://github.com/KosinskiLab/AlphaPulldown/blob/main/alphapulldown/analysis_pipeline/alpha_analysis_jax0.4.def)
3
+
4
+ import sys
5
+ import os
6
+ sys.path.append(os.path.join(os.path.dirname(__file__), "script_pi_score"))
7
+ import pickle
8
+ import json
9
+ import shutil
10
+ import gemmi
11
+ import logging
12
+ import subprocess
13
+ import gzip
14
+ import numpy as np
15
+ import pandas as pd
16
+ from pathlib import Path
17
+ from calculate_mpdockq import *
18
+
19
+
20
+ def examine_inter_pae(pae_mtx, lenght, cutoff, type_int) :
21
+ """Check inter-chain PAE only between the last chain and the others"""
22
+ pae = pae_mtx.copy()
23
+ if type_int == "PPI" or type_int == "Compounds" :
24
+ start_last = sum(lenght[:-1])
25
+ # mask all
26
+ pae[:] = 50
27
+
28
+ # restore only inter-PAE with prey
29
+ pae[start_last:, :start_last] = pae_mtx[start_last:, :start_last]
30
+ pae[:start_last, start_last:] = pae_mtx[:start_last, start_last:]
31
+ check = np.where(pae < cutoff)[0].size != 0
32
+ if type_int == "homo" :
33
+ start = 0
34
+
35
+ for l in lenght:
36
+ end = start + l
37
+ pae[start:end, start:end] = 50 # masque intra-chaîne
38
+ start = end
39
+
40
+ check = np.any(pae < cutoff)
41
+ return check
42
+
43
+
44
+ def obtain_chain_coord(work_dir,pkl_dict=None) :
45
+ """Returns chain_coords,chain_CB_inds,plddt_per_chain,best_plddt,pdb_path"""
46
+ interaction = work_dir.split("/")[-1]
47
+ pdb_path = os.path.join(work_dir,f'{interaction}_ranked_0.pdb')
48
+ pdb_chains, chain_coords, chain_CA_inds, chain_CB_inds = read_pdb(pdb_path)
49
+ if pkl_dict == None :
50
+ best_plddt = extract_plddt_from_pdb(pdb_path)
51
+ else :
52
+ best_plddt = pkl_dict['plddt']
53
+ plddt_per_chain = read_plddt(best_plddt,chain_CA_inds)
54
+ return chain_coords,chain_CB_inds,plddt_per_chain,best_plddt,pdb_path
55
+
56
+ def obtain_mpdockq2(chain_coords,chain_CB_inds,plddt_per_chain,best_plddt,pdb_path) :
57
+ """Returns mpDockQ if more than two chains otherwise return pDockQ"""
58
+ complex_score,num_chains = score_complex(chain_coords,chain_CB_inds,plddt_per_chain)
59
+ if complex_score is not None and num_chains>2:
60
+ mpDockq_or_pdockq = calculate_mpDockQ(complex_score)
61
+ elif complex_score is not None and num_chains==2:
62
+ chain_coords,plddt_per_chain = read_pdb_pdockq(pdb_path)
63
+ mpDockq_or_pdockq = calc_pdockq(chain_coords,plddt_per_chain,t=8)
64
+ else:
65
+ mpDockq_or_pdockq = "None"
66
+ return mpDockq_or_pdockq
67
+
68
+
69
+ def extract_plddt_from_pdb(pdb_file) :
70
+ """
71
+ Extract plddt from b-factor in pdb
72
+ """
73
+ plddt_values = []
74
+ with open(pdb_file, "r") as f :
75
+ for line in f:
76
+ if line.startswith("ATOM") and line[12:16].strip() == "CA" :
77
+ try :
78
+ b_factor = float(line[60:66].strip())
79
+ plddt_values.append(b_factor)
80
+ except ValueError :
81
+ pass
82
+ return np.array(plddt_values, dtype=float)
83
+
84
+ def run_and_summarise_pi_score(jobs, surface_thres, ccp4_setup) :
85
+ """
86
+ A function to calculate all predicted models' pi_scores and make a pandas df of the results.
87
+ Instrumented to log timing per major step.
88
+ """
89
+ output_df = pd.DataFrame()
90
+ for t in ["micromamba", "mamba", "conda"] :
91
+ if shutil.which(t) :
92
+ tool = t
93
+ output_df = pd.DataFrame()
94
+ for job in jobs :
95
+ direc = os.path.dirname(job)
96
+ file_pdb = job.split("/")[-1]
97
+ name_job = f"{file_pdb.split('.pdb')[0]}"
98
+ if os.path.isdir("/scratch") :
99
+ tmp_dir = f"/scratch/tmp/{name_job}"
100
+ else :
101
+ tmp_dir = f"/tmp/{name_job}"
102
+
103
+ logging.info(f"Creating temporary directory {tmp_dir} for pi_score outputs")
104
+ subprocess.run(f"rm -rf {tmp_dir} && mkdir -p {tmp_dir}/pi_score_outputs", shell=True, executable="/bin/bash", check=True)
105
+ pi_score_outputs = os.path.join(tmp_dir, "pi_score_outputs")
106
+
107
+ cwd = os.path.dirname(os.path.abspath(__file__))
108
+
109
+ if not os.path.isfile(os.path.join(direc, f"{file_pdb}")) :
110
+ logging.error(f"{job} failed. Cannot find {file_pdb} in {direc}")
111
+ sys.exit()
112
+
113
+
114
+ output_dir = os.path.join(pi_score_outputs)
115
+
116
+ cmd = (
117
+ f"source {ccp4_setup}/bin/ccp4.setup-sh && "
118
+ f"{tool} run -n pi_score python {cwd}/script_pi_score/run_piscore_wc.py "
119
+ f"-p {job} -o {output_dir} -s {surface_thres} -ps 10"
120
+ )
121
+
122
+ proc = subprocess.Popen(cmd, shell=True, executable="/bin/bash", close_fds=True)
123
+ proc.wait()
124
+
125
+
126
+
127
+ subdir = os.path.join(pi_score_outputs)
128
+ csv_files = [f for f in os.listdir(subdir) if 'filter_intf_features' in f]
129
+ pi_score_files = [f for f in os.listdir(subdir) if 'pi_score_' in f]
130
+
131
+ if not csv_files or not pi_score_files :
132
+ logging.info(f"Warning: missing CSV or pi_score files for {name_job}")
133
+ continue
134
+ for csv_f in csv_files:
135
+ filtered_df = pd.read_csv(os.path.join(subdir, csv_f))
136
+
137
+ if filtered_df.shape[0] == 0 :
138
+ for column in filtered_df.columns :
139
+ filtered_df[column] = ["None"]
140
+ filtered_df['jobs'] = str(name_job)
141
+ filtered_df['pi_score'] = "No interface detected"
142
+ output_df = pd.concat([output_df, filtered_df])
143
+ continue
144
+
145
+ interface_id = csv_f.split("filter_intf_features_")[-1].replace(".csv", "")
146
+
147
+ pi_f = [f for f in pi_score_files if interface_id in f]
148
+ if not pi_f :
149
+ logging.info(f"Warning: no pi_score file for interface {interface_id}")
150
+ continue
151
+
152
+ pi_score = pd.read_csv(os.path.join(subdir, pi_f[0]))
153
+ pi_score['jobs'] = str(name_job)
154
+
155
+
156
+ if 'chains' in pi_score.columns :
157
+ pi_score['interface'] = pi_score['chains']
158
+ else:
159
+ pi_score['interface'] = interface_id
160
+
161
+ filtered_df['jobs'] = str(name_job)
162
+ last_chain = get_last_chain_from_pdb(os.path.join(job))
163
+
164
+ filtered_df = filtered_df[filtered_df['interface'].apply(lambda x: last_chain in x)]
165
+ pi_score = pi_score.drop(columns=["#PDB", "pdb", "pvalue", "chains", "predicted_class"],errors="ignore")
166
+
167
+ filtered_df = pd.merge(
168
+ filtered_df,
169
+ pi_score,
170
+ on=['jobs', 'interface'],
171
+ how='left'
172
+ )
173
+ output_df = pd.concat([output_df, filtered_df])
174
+
175
+ subprocess.run(f"rm -rf {tmp_dir}", shell=True, executable='/bin/bash')
176
+ return output_df
177
+
178
+ def get_last_chain_from_pdb(pdb_file) :
179
+ """
180
+ Get the last chain identifier from a PDB file. This is used to filter pi_score results for the last chain only.
181
+ """
182
+ last_chain = None
183
+ with open(pdb_file, 'r') as f :
184
+ for line in f :
185
+ if line.startswith(('ATOM', 'HETATM')):
186
+ chain = line[21] # col 22 (index 21)
187
+ last_chain = chain
188
+ return last_chain
189
+
190
+
191
+ def main(job, cutoff, surface_thres, save_file, AF_version, ccp4_setup, multi_scoring) :
192
+ """
193
+ Main function to check inter-PAE and run pi_score for good models. Returns a pandas df with all scores.
194
+ structure confidence (ipTM)
195
+ geometry confidence (PAE)
196
+ interface quality (mpDockQ)
197
+ physico-chemical scoring (PI-score)
198
+
199
+ Parameters :
200
+ ----------
201
+ job : str
202
+ cutoff : int
203
+ surface_thres : int
204
+ save_file : File_proteins object
205
+ AF_version : str
206
+ ccp4_setup : str
207
+ multi_scoring : bool
208
+ """
209
+ seq_no_SP = save_file.get_proteins_sequence_no_SP()
210
+ prot_lenght = save_file.get_lenght_prot()
211
+ good_jobs = []
212
+ iptm_ptm = list()
213
+ iptm = list()
214
+ name_jobs = list()
215
+ mpDockq_scores = list()
216
+ logging.info(f"Scoring {job}")
217
+ result_subdir = os.path.join(job)
218
+ interaction = job.split("/")[-1]
219
+ lenght = list()
220
+ if "_and_" in interaction and "PPI" in job :
221
+ type_int = "PPI"
222
+ for prot in interaction.split("_and_") :
223
+ if "-" in prot and prot.split("_")[0] in seq_no_SP.keys() :
224
+ prot = prot.split("_")[0]
225
+ lenght.append(prot_lenght[prot])
226
+ if "_homo_" in interaction :
227
+ type_int = "homo"
228
+ prot = interaction.split("_homo_")[0]
229
+ nbr = int(interaction.split("_homo_")[1].replace("er",""))
230
+ for i in range(0,nbr) :
231
+ if "-" in prot and prot.split("_")[0] in seq_no_SP.keys() :
232
+ prot = prot.split("_")[0]
233
+ lenght.append(prot_lenght[prot])
234
+ if "Compounds" in job :
235
+ type_int = "Compounds"
236
+ for prot in interaction.split("_and_") :
237
+ if "-" in prot and prot.split("_")[0] in seq_no_SP.keys() :
238
+ prot = prot.split("_")[0]
239
+ if prot == interaction.split("_and_")[-1] :
240
+ l = 1
241
+ else :
242
+ l = prot_lenght[prot]
243
+ lenght.append(l)
244
+
245
+
246
+
247
+ ### AlphaFold3 ###
248
+ if AF_version == "3" :
249
+ cif_files = list(Path(result_subdir).glob("*model.cif"))
250
+ pdb = "ranked_0.pdb"
251
+ if os.path.isfile(os.path.join(result_subdir,f'{interaction}_ranked_0.pdb')) == False : #create ranked_0.pdb for AF3
252
+ doc = gemmi.cif.read_file(str(cif_files[0]))
253
+ block = doc.sole_block()
254
+ structure = gemmi.make_structure_from_block(block)
255
+ structure.write_pdb(os.path.join(result_subdir, f'{interaction}_ranked_0.pdb'))
256
+
257
+ int_AF3 = str(cif_files[0]).split("_model")[0]
258
+ if os.path.isfile(int_AF3+'_summary_confidences.json') :
259
+ with open(int_AF3+'_summary_confidences.json','rb') as json_sum_f :
260
+ json_sum = json.load(json_sum_f)
261
+ if "iptm" in json_sum.keys() and "ptm" in json_sum.keys() :
262
+ iptm_score = json_sum['iptm']
263
+ ptm_score = json_sum['ptm']
264
+ iptm_ptm_score = 0.8 * iptm_score + 0.2 * ptm_score
265
+ with open(int_AF3+'_confidences.json','rb') as json_f :
266
+ json_data = json.load(json_f)
267
+ pae_mtx = np.array(json_data['pae'])
268
+ chain_coords,chain_CB_inds,plddt_per_chain,best_plddt,pdb_path = obtain_chain_coord(os.path.join(job))
269
+ check = examine_inter_pae(pae_mtx,lenght,cutoff=cutoff,type_int=type_int)
270
+ mpDockq_score = obtain_mpdockq2(chain_coords,chain_CB_inds,plddt_per_chain,best_plddt,pdb_path)
271
+ if check:
272
+ good_jobs.append(str(job)+"/"+f"{job.split('/')[-1]}_ranked_0.pdb")
273
+ iptm_ptm.append(iptm_ptm_score)
274
+ iptm.append(iptm_score)
275
+ mpDockq_scores.append(mpDockq_score)
276
+ name_jobs.append(f"{job.split('/')[-1]}_{pdb.split('.')[0]}")
277
+ else :
278
+ logging.info(f"Cannot find summary_confidences.json for {job}, skipping.")
279
+
280
+
281
+ ### AlphaFold2 ###
282
+ if os.path.isfile(os.path.join(job,'ranking_debug.json')) :
283
+ mod_index = -1
284
+ os.system(f"cp {job}/ranked_0.pdb {job}/{interaction}_ranked_0.pdb") #rename pdb file with explicit name
285
+ all_models = ["ranked_0.pdb"]
286
+ if multi_scoring == True :
287
+ all_models = ["ranked_0.pdb", "ranked_1.pdb", "ranked_2.pdb", "ranked_3.pdb", "ranked_4.pdb"]
288
+ with open(os.path.join(result_subdir,'ranking_debug.json'),'rb') as json_f :
289
+ data = json.load(json_f)
290
+ best_model = data['order']
291
+ for pdb in all_models :
292
+ mod_index += 1
293
+ rank_model = best_model[mod_index]
294
+ if "iptm" in data.keys() or "iptm+ptm" in data.keys() :
295
+ iptm_ptm_score = data['iptm+ptm'][rank_model]
296
+ if os.path.exists(os.path.join(result_subdir, f"result_{rank_model}.pkl")) :
297
+ pkl_path = os.path.join(result_subdir, f"result_{rank_model}.pkl")
298
+ with open(pkl_path, 'rb') as pkl :
299
+ check_dict = pickle.load(pkl)
300
+ elif os.path.exists(os.path.join(result_subdir, f"result_{rank_model}.pkl.gz")) :
301
+ logging.info("Result pickle for the best model not found. Now search for zipped pickle.")
302
+ pkl_path = os.path.join(result_subdir, f"result_{rank_model}.pkl.gz")
303
+ with gzip.open(pkl_path, 'rb') as pkl :
304
+ check_dict = pickle.load(pkl)
305
+ else :
306
+ logging.info(f"Cannot find result pickle for {job}, skipping.")
307
+ iptm_score = check_dict['iptm']
308
+ pae_mtx = np.array(check_dict['predicted_aligned_error'])
309
+ chain_coords,chain_CB_inds,plddt_per_chain,best_plddt,pdb_path = obtain_chain_coord(os.path.join(job),check_dict)
310
+ if pdb == "ranked_0.pdb" :
311
+ check = examine_inter_pae(pae_mtx,lenght,cutoff=cutoff,type_int=type_int) #only check PAE for best model
312
+ mpDockq_score = obtain_mpdockq2(chain_coords,chain_CB_inds,plddt_per_chain,best_plddt,pdb_path)
313
+ if check :
314
+ good_jobs.append(str(f"{job}/{job.split('/')[-1]}_{pdb}"))
315
+ iptm_ptm.append(iptm_ptm_score)
316
+ iptm.append(iptm_score)
317
+ mpDockq_scores.append(mpDockq_score)
318
+ name_jobs.append(f"{job.split('/')[-1]}_{pdb.split('.')[0]}")
319
+ del data
320
+
321
+ other_measurements_df=pd.DataFrame.from_dict({
322
+ "jobs":name_jobs,
323
+ "iptm_ptm":iptm_ptm,
324
+ "iptm":iptm,
325
+ "mpDockQ/pDockQ":mpDockq_scores})
326
+
327
+ if good_jobs!=[] :
328
+ pi_score_df = run_and_summarise_pi_score(good_jobs, surface_thres, ccp4_setup)
329
+ pi_score_df = pd.merge(pi_score_df, other_measurements_df, on="jobs")
330
+ columns = list(pi_score_df.columns.values)
331
+ columns.pop(columns.index('jobs'))
332
+ pi_score_df = pi_score_df[['jobs'] + columns]
333
+ pi_score_df = pi_score_df.sort_values(by='iptm',ascending=False)
334
+
335
+ return pi_score_df
336
+ else :
337
+ return None
338
+
339
+
340
+
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: Fast_HInt-ppi
3
+ Version: 0.1.0
4
+ Summary: A tool to find homologous interactions and speed up AlphaFold-based structural modeling.
5
+ Home-page: https://github.com/Qrouger/HInt
6
+ Author: Quentin Rouger
7
+ Author-email: quentin.rouger@univ-rennes.fr
8
+ License: GPL-3.0 license
9
+ Requires-Dist: alphapulldown
10
+ Requires-Dist: matplotlib
11
+ Requires-Dist: nvidia-ml-py
12
+ Requires-Dist: ihm
13
+ Requires-Dist: scipy==1.16.0
14
+ Requires-Dist: jax[cuda12]==0.5.3
15
+ Requires-Dist: numpy==1.26.4
16
+ Requires-Dist: pandas
17
+ Requires-Dist: pydantic
18
+ Requires-Dist: packaging
19
+ Requires-Dist: opt_einsum
20
+ Requires-Dist: rdkit==2024.3.5
21
+ Requires-Dist: zstandard==0.23.0
22
+ Requires-Dist: jaxtyping==0.2.34
23
+ Requires-Dist: typeguard==2.13.3
24
+ Requires-Dist: jax_triton==0.2.0
25
+ Requires-Dist: triton==3.1.0
26
+ Requires-Dist: torch>=1.6
27
+ Requires-Dist: gemmi
28
+ Dynamic: author
29
+ Dynamic: author-email
30
+ Dynamic: home-page
31
+ Dynamic: license
32
+ Dynamic: requires-dist
33
+ Dynamic: summary
@@ -0,0 +1,11 @@
1
+ Fast_HInt/File_proteins.py,sha256=PTuez57LBYLojQp2z7FlI6wCgTqWXggmQwRSjJfIddI,28958
2
+ Fast_HInt/HInt.py,sha256=qi7GCsKNa_SScX2HsNOeBg0qbReQhE-KHEdb0VRolIo,10916
3
+ Fast_HInt/Scoring_HInt.py,sha256=3R6oIHslhv7oqtTi7mlTEBMZAPPNpb3REk3KhfcfzG4,49068
4
+ Fast_HInt/Utils_HInt.py,sha256=PHXwnmAv1Kdl7CQ0TUtFEBSGgg9RXpe0JlYVRE_0Lws,55681
5
+ Fast_HInt/__init__.py,sha256=h3PwrodWYBJhu3gtpU3l3j_arrskdp3T92s-DeJtw8c,16
6
+ Fast_HInt/get_good_inter_pae.py,sha256=eOp92maZ9keBE4DOeV2Kg4sleqgrIHZIn1sA9V6Qa3s,14030
7
+ fast_hint_ppi-0.1.0.dist-info/METADATA,sha256=mGZ-UdohSgPl5EPbkkDNEuQKWJHkCPBMiT5KpYotzYQ,934
8
+ fast_hint_ppi-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ fast_hint_ppi-0.1.0.dist-info/entry_points.txt,sha256=4XMuRhcHLBN8jDvo2rUo2D5l6PuECepmMdbM31tvV0Q,40
10
+ fast_hint_ppi-0.1.0.dist-info/top_level.txt,sha256=Y0JGcaXOtb7bsZzM5_KHyUdizPVB912yydU9-vtuhqs,10
11
+ fast_hint_ppi-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ HInt = HInt.HInt:main
@@ -0,0 +1 @@
1
+ Fast_HInt