-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpeptide_pocket_distance.py
More file actions
117 lines (90 loc) · 3.6 KB
/
Copy pathpeptide_pocket_distance.py
File metadata and controls
117 lines (90 loc) · 3.6 KB
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
#!/usr/bin/env python3
"""
Compute center-of-mass distance between a peptide and a protein pocket
over an MD trajectory using MDAnalysis.
USAGE
-----
python com_distance.py \
--gro md_100ns.gro \
--xtc md_100ns.xtc \
--peptide-range 358-370 \
--pocket-range 155-163 \
--out com_distance.dat \
--interval 0.5
ARGUMENTS
---------
--gro Path to topology (.gro)
--xtc Path to trajectory (.xtc)
--peptide-range MDAnalysis resindex range, e.g. 358-370
--pocket-range Protein resid range, e.g. 155-163
--out Output file for distances
--interval Desired sampling interval in ns (approx.)
"""
import argparse
import numpy as np
import MDAnalysis as mda
def parse_args():
p = argparse.ArgumentParser(
description="Compute COM distance between peptide and protein pocket."
)
p.add_argument("--gro", required=True,
help="Topology file (.gro), REQUIRED")
p.add_argument("--xtc", required=True,
help="Trajectory file (.xtc), REQUIRED")
p.add_argument("--peptide-range", required=True,
help="Peptide resindex range, e.g. 358-370 (MDAnalysis indices)")
p.add_argument("--pocket-range", required=True,
help="Pocket resid range, e.g. 155-163 (GROMACS residue IDs)")
p.add_argument("--out", required=True,
help="Output file for COM distances")
p.add_argument("--interval", required=True, type=float,
help="Desired sampling interval between analyzed frames (ns)")
return p.parse_args()
def main():
args = parse_args()
print(f"Loading universe from {args.gro} and {args.xtc} ...")
u = mda.Universe(args.gro, args.xtc)
# --- Build selections ---
peptide_sel = f"resindex {args.peptide_range}"
pocket_sel = f"protein and resid {args.pocket_range}"
peptide = u.select_atoms(peptide_sel)
pocket = u.select_atoms(pocket_sel)
print(f"Peptide selection: '{peptide_sel}' -> {peptide.n_atoms} atoms")
print(f"Pocket selection: '{pocket_sel}' -> {pocket.n_atoms} atoms")
if peptide.n_atoms == 0:
raise RuntimeError("Peptide selection is empty. Check peptide resindex range.")
if pocket.n_atoms == 0:
raise RuntimeError("Pocket selection is empty. Check pocket resid range.")
# --- Determine stride ---
dt_ps = u.trajectory.dt
if dt_ps is None or dt_ps <= 0:
print("Trajectory dt not available; analyzing every frame.")
stride = 1
else:
desired_ps = args.interval * 1000.0
stride = max(1, int(round(desired_ps / dt_ps)))
print(f"Trajectory dt = {dt_ps:.3f} ps -> stride = {stride} "
f"(~{stride * dt_ps / 1000.0:.3f} ns between frames)")
times_ns = []
distances_A = []
print("Starting COM distance calculation ...")
for ts in u.trajectory[::stride]:
t_ns = ts.time / 1000.0
com_pep = peptide.center_of_mass(pbc=True)
com_poc = pocket.center_of_mass(pbc=True)
d = np.linalg.norm(com_pep - com_poc)
times_ns.append(t_ns)
distances_A.append(d)
times_ns = np.array(times_ns)
distances_A = np.array(distances_A)
print(f"Writing distances to {args.out} ...")
with open(args.out, "w") as f:
f.write("# time_ns distance_A\n")
for t, d in zip(times_ns, distances_A):
f.write(f"{t:12.5f} {d:12.5f}\n")
print("Done.")
print(f"Frames analyzed: {len(times_ns)}")
print(f"Distance (Å): min={distances_A.min():.3f}, "
f"max={distances_A.max():.3f}, mean={distances_A.mean():.3f}")
if __name__ == "__main__":
main()