File size: 4,856 Bytes
9d6a2a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import argparse
import os
import pickle

import numpy as np
import pandas as pd
from joblib import Parallel, delayed

from _common import add_model_args, formatter, runtime_kwargs, score_ligand_file


def parse_args():
    parser = argparse.ArgumentParser(
        description="Run GenScore CASF-2016 docking benchmark.",
        formatter_class=formatter(),
    )
    add_model_args(parser)
    parser.add_argument("--casf-dir", required=True, help="CASF-2016 root directory.")
    parser.add_argument("--pdbbind-dir", required=True, help="PDBbind root directory.")
    parser.add_argument(
        "--native-ligand-dir",
        default=None,
        help="Directory containing <pdbid>_ligand.mol2 files.",
    )
    parser.add_argument("--outdir", required=True, help="Directory for CASF docking score .dat files.")
    parser.add_argument("--decoys-subdir", default="decoys_docking")
    parser.add_argument("--coreset-subdir", default="coreset")
    parser.add_argument("--refined-subdir", default="v2020-refined")
    parser.add_argument("--other-pl-subdir", default="v2020-other-PL")
    parser.add_argument("--parallel", action="store_true", default=False)
    return parser.parse_args()


def _complex_ids(path):
    return [name for name in os.listdir(path) if os.path.isdir(os.path.join(path, name))]


def _pocket_path(pdbbind_dir, subset, pdbid, cutoff):
    return os.path.join(
        pdbbind_dir,
        subset,
        pdbid,
        f"{pdbid}_prot",
        f"{pdbid}_p_pocket_{cutoff}.pdb",
    )


def _native_ligand_path(pdbbind_dir, native_ligand_dir, subset, pdbid):
    candidates = []
    if native_ligand_dir:
        candidates.append(
            os.path.join(native_ligand_dir, f"{pdbid}_ligand.mol2")
        )
    candidates.extend([
        os.path.join(pdbbind_dir, "mol2", f"{pdbid}_ligand.mol2"),
        os.path.join(
            pdbbind_dir, subset, pdbid, f"{pdbid}_prot",
            f"{pdbid}_l.mol2",
        ),
        os.path.join(
            pdbbind_dir, subset, pdbid, f"{pdbid}_prot",
            f"{pdbid}_l.sdf",
        ),
    ])
    for candidate in candidates:
        if os.path.isfile(candidate):
            return candidate
    raise FileNotFoundError(
        f"No native ligand found for {pdbid}. Checked: "
        + ", ".join(candidates)
    )


def _decoys_path(casf_dir, decoys_subdir, pdbid):
    root = os.path.join(casf_dir, decoys_subdir)
    return _ligand_path(root, f"{pdbid}_decoys")


def _ligand_path(root, stem):
    candidates = [
        os.path.join(root, f"{stem}.{extension}")
        for extension in ("sdf", "mol2")
    ]
    for candidate in candidates:
        if os.path.isfile(candidate):
            return candidate
    raise FileNotFoundError(
        f"No ligand file found for {stem}. Checked: {', '.join(candidates)}"
    )


def score_compound(pdbid, subset, args):
    prot = _pocket_path(args.pdbbind_dir, subset, pdbid, args.cutoff)
    decoys = _decoys_path(args.casf_dir, args.decoys_subdir, pdbid)
    ids_decoy, scores_decoy = score_ligand_file(prot, decoys, args, parallel=True)

    native = _native_ligand_path(
        args.pdbbind_dir,
        args.native_ligand_dir,
        subset,
        pdbid,
    )
    ids_native, scores_native = score_ligand_file(prot, native, args, parallel=False)
    ids_native = list(ids_native)
    ids_native.pop(-1)
    ids_native.append(f"{pdbid}_ligand")
    return ids_decoy + ids_native, np.append(scores_decoy, scores_native)


def run_for_subset(pdbids, subset, args):
    if args.parallel and runtime_kwargs(args)["device"] == "cpu":
        return Parallel(n_jobs=-1, backend="threading")(
            delayed(score_compound)(pdbid, subset, args) for pdbid in pdbids
        )
    return [score_compound(pdbid, subset, args) for pdbid in pdbids]


def main():
    args = parse_args()
    coreset = _complex_ids(os.path.join(args.casf_dir, args.coreset_subdir))
    refined_ids = set(_complex_ids(os.path.join(args.pdbbind_dir, args.refined_subdir)))
    other_ids = set(_complex_ids(os.path.join(args.pdbbind_dir, args.other_pl_subdir)))

    ids_refined = [pdbid for pdbid in coreset if pdbid in refined_ids]
    ids_other = [pdbid for pdbid in coreset if pdbid in other_ids]

    results = run_for_subset(ids_refined, args.refined_subdir, args)
    results += run_for_subset(ids_other, args.other_pl_subdir, args)

    os.makedirs(args.outdir, exist_ok=True)
    for ids, scores in results:
        pdbid = ids[0].split("_")[0]
        df = pd.DataFrame(zip(ids, scores), columns=["#code", "score"])
        df["#code"] = df["#code"].str.split("-").apply(lambda item: item[0])
        df.to_csv(os.path.join(args.outdir, f"{pdbid}_score.dat"), index=False, sep="\t")

    with open(f"{args.outprefix}_docking.pkl", "wb") as handle:
        pickle.dump(results, handle)


if __name__ == "__main__":
    main()