packmol-memgen-minimal 1.1.16__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.
- packmol_memgen/__init__.py +2 -0
- packmol_memgen/__version__.py +34 -0
- packmol_memgen/data/LICENSE.Apache-2.0 +201 -0
- packmol_memgen/data/extra_solvents.lib +789 -0
- packmol_memgen/data/frcmod.lipid_ext +97 -0
- packmol_memgen/data/frcmod.solvents +129 -0
- packmol_memgen/data/insane_lipids.txt +138 -0
- packmol_memgen/data/insane_solvents.txt +45 -0
- packmol_memgen/data/leaprc.extra_solvents +42 -0
- packmol_memgen/data/leaprc.lipid_ext +48 -0
- packmol_memgen/data/lipid_ext.lib +12312 -0
- packmol_memgen/data/martini_v3.0.0.itp +356605 -0
- packmol_memgen/data/memgen.parm +4082 -0
- packmol_memgen/data/pdbs.tar.gz +0 -0
- packmol_memgen/data/solvent.parm +14 -0
- packmol_memgen/example/example.sh +31 -0
- packmol_memgen/lib/__init__.py +0 -0
- packmol_memgen/lib/amber.py +77 -0
- packmol_memgen/lib/charmmlipid2amber/__init__.py +0 -0
- packmol_memgen/lib/charmmlipid2amber/charmmlipid2amber.csv +7164 -0
- packmol_memgen/lib/charmmlipid2amber/charmmlipid2amber.py +225 -0
- packmol_memgen/lib/pdbremix/LICENSE +21 -0
- packmol_memgen/lib/pdbremix/__init__.py +0 -0
- packmol_memgen/lib/pdbremix/_version.py +1 -0
- packmol_memgen/lib/pdbremix/amber.py +1103 -0
- packmol_memgen/lib/pdbremix/asa.py +227 -0
- packmol_memgen/lib/pdbremix/data/aminoacid.pdb +334 -0
- packmol_memgen/lib/pdbremix/data/binaries.json +26 -0
- packmol_memgen/lib/pdbremix/data/charmm22.parameter +2250 -0
- packmol_memgen/lib/pdbremix/data/charmm22.topology +1635 -0
- packmol_memgen/lib/pdbremix/data/color_b.py +682 -0
- packmol_memgen/lib/pdbremix/data/hin.lib +130 -0
- packmol_memgen/lib/pdbremix/data/hydroxide.lib +88 -0
- packmol_memgen/lib/pdbremix/data/make_chi.py +92 -0
- packmol_memgen/lib/pdbremix/data/opls.parameter +1108 -0
- packmol_memgen/lib/pdbremix/data/opls.topology +1869 -0
- packmol_memgen/lib/pdbremix/data/phd.frcmod +82 -0
- packmol_memgen/lib/pdbremix/data/phd.leaprc +4 -0
- packmol_memgen/lib/pdbremix/data/phd.prepin +35 -0
- packmol_memgen/lib/pdbremix/data/template.pdb +334 -0
- packmol_memgen/lib/pdbremix/data/znb.frcmod +24 -0
- packmol_memgen/lib/pdbremix/data/znb.leaprc +7 -0
- packmol_memgen/lib/pdbremix/data/znb.lib +69 -0
- packmol_memgen/lib/pdbremix/data.py +264 -0
- packmol_memgen/lib/pdbremix/fetch.py +102 -0
- packmol_memgen/lib/pdbremix/force.py +627 -0
- packmol_memgen/lib/pdbremix/gromacs.py +978 -0
- packmol_memgen/lib/pdbremix/lib/__init__.py +0 -0
- packmol_memgen/lib/pdbremix/lib/docopt.py +579 -0
- packmol_memgen/lib/pdbremix/lib/pyqcprot.py +305 -0
- packmol_memgen/lib/pdbremix/namd.py +1078 -0
- packmol_memgen/lib/pdbremix/pdbatoms.py +543 -0
- packmol_memgen/lib/pdbremix/pdbtext.py +120 -0
- packmol_memgen/lib/pdbremix/protein.py +311 -0
- packmol_memgen/lib/pdbremix/pymol.py +480 -0
- packmol_memgen/lib/pdbremix/rmsd.py +203 -0
- packmol_memgen/lib/pdbremix/simulate.py +420 -0
- packmol_memgen/lib/pdbremix/spacehash.py +73 -0
- packmol_memgen/lib/pdbremix/trajectory.py +286 -0
- packmol_memgen/lib/pdbremix/util.py +273 -0
- packmol_memgen/lib/pdbremix/v3.py +16 -0
- packmol_memgen/lib/pdbremix/v3array.py +482 -0
- packmol_memgen/lib/pdbremix/v3numpy.py +350 -0
- packmol_memgen/lib/pdbremix/volume.py +155 -0
- packmol_memgen/lib/utils.py +1017 -0
- packmol_memgen/main.py +2827 -0
- packmol_memgen_minimal-1.1.16.dist-info/METADATA +664 -0
- packmol_memgen_minimal-1.1.16.dist-info/RECORD +71 -0
- packmol_memgen_minimal-1.1.16.dist-info/WHEEL +4 -0
- packmol_memgen_minimal-1.1.16.dist-info/entry_points.txt +2 -0
- packmol_memgen_minimal-1.1.16.dist-info/licenses/LICENSE +338 -0
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
__doc__ = """
|
|
4
|
+
|
|
5
|
+
Interface to the GROMACS molecular-dynamics package.
|
|
6
|
+
|
|
7
|
+
The library is split into three sections:
|
|
8
|
+
|
|
9
|
+
1. Read and write restart files
|
|
10
|
+
2. Generate restart files from PDB
|
|
11
|
+
3. Run simulations from restart files
|
|
12
|
+
4. Read trajectories with some post-processing
|
|
13
|
+
|
|
14
|
+
Copyright (C) 2009, 2014, Bosco K. Ho
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import shutil
|
|
19
|
+
import copy
|
|
20
|
+
import glob
|
|
21
|
+
import xdrlib
|
|
22
|
+
|
|
23
|
+
from . import util
|
|
24
|
+
from . import pdbatoms
|
|
25
|
+
from . import v3
|
|
26
|
+
from . import data
|
|
27
|
+
from . import pdbtext
|
|
28
|
+
from . import protein
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ##########################################################
|
|
32
|
+
|
|
33
|
+
# 1. Reading and writing restart files
|
|
34
|
+
|
|
35
|
+
# In PDBREMIX, restart files for GROMACS are assumed to
|
|
36
|
+
# have the naming scheme:
|
|
37
|
+
|
|
38
|
+
# 1. topology file: sim.top
|
|
39
|
+
# 2. coordinate/velocity file: sim.gro
|
|
40
|
+
|
|
41
|
+
# Parsers have been written to read .top and .gro files into
|
|
42
|
+
# Python structures, and to write these back into .top and .gro
|
|
43
|
+
# files, and to convert them into .pdb files
|
|
44
|
+
|
|
45
|
+
# The units used in GROMACS are:
|
|
46
|
+
# - positions: nanometers
|
|
47
|
+
# - velocities: nanometers/picosecond
|
|
48
|
+
# - force constant: kJ/mol/nm^2
|
|
49
|
+
|
|
50
|
+
# which will be converted into
|
|
51
|
+
# - angstroms
|
|
52
|
+
# - picoseconds
|
|
53
|
+
# - kilocalories
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_top(top):
|
|
57
|
+
"""
|
|
58
|
+
Returns a list of (mass, charge, chain_id) for the atoms
|
|
59
|
+
in the topology file.
|
|
60
|
+
"""
|
|
61
|
+
util.check_output(top)
|
|
62
|
+
lines = open(top).readlines()
|
|
63
|
+
|
|
64
|
+
atoms = []
|
|
65
|
+
is_chain_topologies = False
|
|
66
|
+
chain=" "
|
|
67
|
+
top_dir = os.path.dirname(top)
|
|
68
|
+
for l in lines:
|
|
69
|
+
if not is_chain_topologies:
|
|
70
|
+
if 'chain topologies' in l:
|
|
71
|
+
is_chain_topologies = True
|
|
72
|
+
continue
|
|
73
|
+
if l.startswith("#include"):
|
|
74
|
+
itp = l.split()[1][1:-1]
|
|
75
|
+
itp = os.path.join(top_dir, itp)
|
|
76
|
+
if os.path.isfile(itp):
|
|
77
|
+
full_chain_name = os.path.splitext(itp)[0]
|
|
78
|
+
chain = full_chain_name.split('_')[-1]
|
|
79
|
+
these_atoms = read_top(itp, chain)
|
|
80
|
+
atoms.extend(these_atoms)
|
|
81
|
+
if l.startswith(";"):
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
is_atoms = False
|
|
85
|
+
qtot = None
|
|
86
|
+
for l in lines:
|
|
87
|
+
if not is_atoms:
|
|
88
|
+
if '[ atoms ]' in l:
|
|
89
|
+
is_atoms = True
|
|
90
|
+
continue
|
|
91
|
+
if l.startswith('['):
|
|
92
|
+
break
|
|
93
|
+
if l.startswith(";"):
|
|
94
|
+
continue
|
|
95
|
+
if not l.strip():
|
|
96
|
+
continue
|
|
97
|
+
words = l.split()
|
|
98
|
+
n = int(words[0])
|
|
99
|
+
res_num = int(words[2])
|
|
100
|
+
res_type = words[3]
|
|
101
|
+
q = float(words[6])
|
|
102
|
+
mass = float(words[7])
|
|
103
|
+
atoms.append((mass, q, chain))
|
|
104
|
+
|
|
105
|
+
return atoms
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def AtomFromGroLine(line):
|
|
109
|
+
"""
|
|
110
|
+
Returns an Atom object from a .gro atom line.
|
|
111
|
+
"""
|
|
112
|
+
atom = pdbatoms.Atom()
|
|
113
|
+
atom.res_num = int(line[0:5])
|
|
114
|
+
atom.res_type = line[5:8].strip()
|
|
115
|
+
atom.type = line[10:15].strip(" ")
|
|
116
|
+
atom.element = data.guess_element(
|
|
117
|
+
atom.res_type, line[12:15])
|
|
118
|
+
atom.num = int(line[15:20])
|
|
119
|
+
# 10 x multiplier converts from nm to angstroms
|
|
120
|
+
x = 10.0*float(line[20:28])
|
|
121
|
+
y = 10.0*float(line[28:36])
|
|
122
|
+
z = 10.0*float(line[36:44])
|
|
123
|
+
v3.set_vector(atom.pos, x, y, z)
|
|
124
|
+
if len(line) > 62:
|
|
125
|
+
# 10 x multiplier converts from nm to angstroms
|
|
126
|
+
x = 10.0*float(line[44:52])
|
|
127
|
+
y = 10.0*float(line[52:60])
|
|
128
|
+
z = 10.0*float(line[60:68])
|
|
129
|
+
v3.set_vector(atom.vel, x, y, z)
|
|
130
|
+
return atom
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def convert_to_pdb_atom_names(soup):
|
|
134
|
+
"""
|
|
135
|
+
For the soup structure, converts residue
|
|
136
|
+
atom names peculiar to .gro into standard PDB names
|
|
137
|
+
for interacting with other systems.
|
|
138
|
+
"""
|
|
139
|
+
for res in soup.residues():
|
|
140
|
+
if res.type == "ILE":
|
|
141
|
+
if res.has_atom("CD"):
|
|
142
|
+
res.change_atom_type("CD", "CD1")
|
|
143
|
+
if res.has_atom("OC2"):
|
|
144
|
+
res.change_atom_type("OC2", "OXT")
|
|
145
|
+
if res.has_atom("OC1"):
|
|
146
|
+
res.change_atom_type("OC1", "O")
|
|
147
|
+
if res.type == "SOL":
|
|
148
|
+
res.set_type("HOH")
|
|
149
|
+
res.change_atom_type("HW1", "1H")
|
|
150
|
+
res.change_atom_type("HW2", "2H")
|
|
151
|
+
res.change_atom_type("OW", "O")
|
|
152
|
+
if res.type in data.solvent_res_types:
|
|
153
|
+
for a in res.atoms():
|
|
154
|
+
a.is_hetatm = True
|
|
155
|
+
for atom in res.atoms():
|
|
156
|
+
if atom.type[-1].isdigit() and atom.type[0] == "H":
|
|
157
|
+
new_atom_type = atom.type[-1] + atom.type[:-1]
|
|
158
|
+
res.change_atom_type(atom.type, new_atom_type)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def convert_to_gromacs_atom_names(soup):
|
|
162
|
+
"""
|
|
163
|
+
For writing into .gro files, converts PDB atom names
|
|
164
|
+
into GROMACS specific atom names.
|
|
165
|
+
"""
|
|
166
|
+
for res in soup.residues():
|
|
167
|
+
if res.type == "ILE":
|
|
168
|
+
if res.has_atom("CD1"):
|
|
169
|
+
res.change_atom_type("CD1", "CD")
|
|
170
|
+
if res.has_atom("OXT"):
|
|
171
|
+
res.change_atom_type("OXT", "OC2")
|
|
172
|
+
if res.has_atom("O"):
|
|
173
|
+
res.change_atom_type("O", "OC1")
|
|
174
|
+
if res.type == "HOH":
|
|
175
|
+
res.set_type("SOL")
|
|
176
|
+
res.change_atom_type("1H", "HW1")
|
|
177
|
+
res.change_atom_type("2H", "HW2")
|
|
178
|
+
res.change_atom_type("O", "OW")
|
|
179
|
+
if res.type in data.solvent_res_types:
|
|
180
|
+
for a in res.atoms():
|
|
181
|
+
a.is_hetatm = False
|
|
182
|
+
for atom in res.atoms():
|
|
183
|
+
if atom.type[0].isdigit() and atom.type[1] == "H":
|
|
184
|
+
new_atom_type = atom.type[1:] + atom.type[0]
|
|
185
|
+
res.change_atom_type(atom.type, new_atom_type)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def soup_from_top_gro(top, gro, skip_solvent=False):
|
|
189
|
+
"""
|
|
190
|
+
Returns a Soup built from GROMACS restart files.
|
|
191
|
+
If skip_solvent=True, will skip all solvent molecules.
|
|
192
|
+
"""
|
|
193
|
+
util.check_output(top)
|
|
194
|
+
util.check_output(gro)
|
|
195
|
+
|
|
196
|
+
soup = pdbatoms.Soup()
|
|
197
|
+
soup.remaining_text = ""
|
|
198
|
+
soup.n_remaining_text = 0
|
|
199
|
+
|
|
200
|
+
atoms = []
|
|
201
|
+
|
|
202
|
+
# Read from .gro because .top does not contain water
|
|
203
|
+
# residue information, which is "inferred"
|
|
204
|
+
lines = open(gro, 'r').readlines()
|
|
205
|
+
for i_line, line in enumerate(lines[2:-1]):
|
|
206
|
+
atom = AtomFromGroLine(line)
|
|
207
|
+
if skip_solvent and atom.res_type == "SOL":
|
|
208
|
+
soup.remaining_text = "".join(lines[i_line+2:-1])
|
|
209
|
+
soup.n_remaining_text = len(lines[i_line+2:-1])
|
|
210
|
+
break
|
|
211
|
+
atoms.append(atom)
|
|
212
|
+
soup.box = [float(w) for w in lines[-1].split()]
|
|
213
|
+
|
|
214
|
+
for atom, (mass, q, chain_id) in zip(atoms, read_top(top)):
|
|
215
|
+
atom.mass = mass
|
|
216
|
+
atom.charge = q
|
|
217
|
+
|
|
218
|
+
curr_res_num = -1
|
|
219
|
+
for a in atoms:
|
|
220
|
+
if curr_res_num != a.res_num:
|
|
221
|
+
res = pdbatoms.Residue(
|
|
222
|
+
a.res_type, a.chain_id, a.res_num)
|
|
223
|
+
soup.append_residue(res.copy())
|
|
224
|
+
curr_res_num = a.res_num
|
|
225
|
+
soup.insert_atom(-1, a)
|
|
226
|
+
|
|
227
|
+
convert_to_pdb_atom_names(soup)
|
|
228
|
+
protein.find_chains(soup)
|
|
229
|
+
|
|
230
|
+
return soup
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def write_soup_to_gro(in_soup, gro):
|
|
234
|
+
soup = in_soup.copy()
|
|
235
|
+
convert_to_gromacs_atom_names(soup)
|
|
236
|
+
f = open(gro, 'w')
|
|
237
|
+
f.write("Generated by gromacs.py\n")
|
|
238
|
+
atoms = soup.atoms()
|
|
239
|
+
n_atom = len(atoms) + soup.n_remaining_text
|
|
240
|
+
f.write(str(n_atom) + '\n')
|
|
241
|
+
for a in atoms:
|
|
242
|
+
# GRO doesn't care about numbering so wrap when full
|
|
243
|
+
res_num = a.res_num % 100000
|
|
244
|
+
atom_num = a.num % 100000
|
|
245
|
+
# 0.1 x multiplier converts from angs back to nm
|
|
246
|
+
s = "%5d%-5s%5s%5d%8.3f%8.3f%8.3f%8.4f%8.4f%8.4f\n" % \
|
|
247
|
+
(res_num, a.res_type, a.type, atom_num,
|
|
248
|
+
a.pos[0]*0.1, a.pos[1]*0.1, a.pos[2]*0.1,
|
|
249
|
+
a.vel[0]*0.1, a.vel[1]*0.1, a.vel[2]*0.1)
|
|
250
|
+
f.write(s)
|
|
251
|
+
if soup.remaining_text:
|
|
252
|
+
f.write(soup.remaining_text)
|
|
253
|
+
f.write("%10.5f%10.5f%10.5f\n" % (soup.box[0], soup.box[1], soup.box[2]))
|
|
254
|
+
f.close()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# The following functions wrap the above functions into a
|
|
258
|
+
# standard API that does not explicitly reference GROMACS
|
|
259
|
+
|
|
260
|
+
def expand_restart_files(basename):
|
|
261
|
+
"""Returns expanded restart files based on basename"""
|
|
262
|
+
top = os.path.abspath(basename + '.top')
|
|
263
|
+
crds = os.path.abspath(basename + '.gro')
|
|
264
|
+
vels = '' # dummy file for cross-package interface
|
|
265
|
+
return top, crds, vels
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def get_restart_files(basename):
|
|
269
|
+
"""Returns restart files only if they exist"""
|
|
270
|
+
top, crds, vels = expand_restart_files(basename)
|
|
271
|
+
util.check_files(top, crds)
|
|
272
|
+
return top, crds, vels
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def soup_from_restart_files(top, crds, vels, skip_solvent=False):
|
|
276
|
+
"""Reads pdbatoms.Soup object from restart files."""
|
|
277
|
+
return soup_from_top_gro(top, crds, skip_solvent)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def write_soup_to_crds_and_vels(soup, basename):
|
|
281
|
+
"""From soup, writes out the coordinate/velocities, used for pulsing"""
|
|
282
|
+
write_soup_to_gro(soup, basename + '.gro')
|
|
283
|
+
return basename + '.gro', ''
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def convert_restart_to_pdb(basename, pdb):
|
|
287
|
+
"""Converts restart files with basename into PDB file"""
|
|
288
|
+
top, crds, vels = get_restart_files(basename)
|
|
289
|
+
soup = soup_from_restart_files(top, crds, vels)
|
|
290
|
+
soup.write_pdb(pdb)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# ##########################################################
|
|
294
|
+
|
|
295
|
+
# # 2. Generate restart files from PDB
|
|
296
|
+
|
|
297
|
+
# The restart files used for PDBREMIX assumes a consistent file naming.
|
|
298
|
+
# For a given basename `sim`, the files are:
|
|
299
|
+
# 1. topology file: sim.top
|
|
300
|
+
# 2. coordinate/velocity file: sim.gro
|
|
301
|
+
# 3. restraint file: sim.pos_re.itp
|
|
302
|
+
|
|
303
|
+
# To generate a topology file from the PDB file:
|
|
304
|
+
# - handles multiple protein chains
|
|
305
|
+
# - hydrogens are removed and then regenerated by GROMACS
|
|
306
|
+
# - disulfide bonds are auto-detected
|
|
307
|
+
# - charged residue protonation states are auto-detected
|
|
308
|
+
# - explicit water in cubic box with 10.0 angstrom buffer
|
|
309
|
+
# - counterions to neutralize the system
|
|
310
|
+
# - GROMACS4.5: AMBER99 force-field
|
|
311
|
+
# - GROMACS4.0: GROMOS96 force-field
|
|
312
|
+
|
|
313
|
+
# Binaries used to generate restart files:
|
|
314
|
+
# 1. pdb2gmx - create topologies for atoms in PDB
|
|
315
|
+
# 2. editconf - define cubic box
|
|
316
|
+
# 3. genbox - add explicit water molecules
|
|
317
|
+
# 4. grompp - build .tpr file for energy terms
|
|
318
|
+
# 5. genion - add counterions based on .tpr file
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def delete_backup_files(tag):
|
|
322
|
+
util.clean_fname(*util.re_glob('*', '^#' + tag))
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
force_field_mdp = """
|
|
326
|
+
; Bond parameters
|
|
327
|
+
constraints = hbonds ; bonds from heavy atom to H, constrained
|
|
328
|
+
continuation = yes ; first dynamics run
|
|
329
|
+
; constraints = all-bonds ; all bonds (even heavy atom-H bonds) constrained
|
|
330
|
+
; constraint-algorithm = lincs ; holonomic constraints
|
|
331
|
+
; lincs_iter = 1 ; accuracy of LINCS
|
|
332
|
+
; lincs_order = 4 ; also related to accuracy
|
|
333
|
+
|
|
334
|
+
; Neighborsearching
|
|
335
|
+
ns_type = grid ; search neighboring grid cels
|
|
336
|
+
nstlist = 5 ; 10 fs
|
|
337
|
+
rlist = 1.0 ; short-range neighborlist cutoff (in nm)
|
|
338
|
+
|
|
339
|
+
; Periodic boundary conditions
|
|
340
|
+
pbc = xyz ; 3-dimensional periodic boundary conditions (xyz|no)
|
|
341
|
+
|
|
342
|
+
; Electrostatics
|
|
343
|
+
coulombtype = PME ; Particle Mesh Ewald for long-range electrostatics
|
|
344
|
+
pme_order = 4 ; cubic interpolation
|
|
345
|
+
fourierspacing = 0.16 ; grid spacing for FFT
|
|
346
|
+
rcoulomb = 1.0 ; short-range electrostatic cutoff (in nm)
|
|
347
|
+
rvdw = 1.0 ; short-range van der Waals cutoff (in nm)
|
|
348
|
+
|
|
349
|
+
; Add also refcoord-scaling to work with position restraints and pressure coupling
|
|
350
|
+
refcoord-scaling = all
|
|
351
|
+
|
|
352
|
+
; Dispersion correction
|
|
353
|
+
DispCorr = EnerPres ; account for cut-off vdW scheme
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
ions_mdp = """
|
|
358
|
+
; ions.mdp - used as input into grompp to generate ions.tpr
|
|
359
|
+
; Parameters describing what to do, when to stop and what to save
|
|
360
|
+
integrator = steep ; Algorithm (steep = steepest descent minimization)
|
|
361
|
+
emtol = 1000.0 ; Stop minimization when the maximum force < 1000.0 kJ/mol/nm
|
|
362
|
+
emstep = 0.01 ; Energy step size
|
|
363
|
+
nsteps = 50000 ; Maximum number of (minimization) steps to perform
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def neutralize_system_with_salt(
|
|
368
|
+
in_top, in_gro, basename, force_field):
|
|
369
|
+
"""
|
|
370
|
+
Takes a .top file and adds counterions to neutralize the overall
|
|
371
|
+
charge of system, and saves to `basename.gro`.
|
|
372
|
+
"""
|
|
373
|
+
# Calculate overall charege in the .top file
|
|
374
|
+
qtot = sum([q for mass, q, chain in read_top(in_top)])
|
|
375
|
+
counter_ion_charge = -int(round(qtot))
|
|
376
|
+
if counter_ion_charge == 0:
|
|
377
|
+
shutil.copy(in_gro, basename + '.gro')
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
# Create a .tpr paramater file for genion to find low-energy sites
|
|
381
|
+
in_mdp = basename + '.salt.grompp.mdp'
|
|
382
|
+
open(in_mdp, 'w').write(ions_mdp + force_field_mdp)
|
|
383
|
+
top = basename + '.top'
|
|
384
|
+
if in_top != top:
|
|
385
|
+
shutil.copy(in_top, top)
|
|
386
|
+
tpr = basename + '.salt.tpr'
|
|
387
|
+
out_mdp = basename + '.mdp'
|
|
388
|
+
data.binary(
|
|
389
|
+
'grompp',
|
|
390
|
+
'-f %s -po %s -c %s -p %s -o %s' \
|
|
391
|
+
% (in_mdp, out_mdp, in_gro, top, tpr),
|
|
392
|
+
basename + '.salt.grompp')
|
|
393
|
+
util.check_files(tpr)
|
|
394
|
+
|
|
395
|
+
# Use genion to generate a gro of system with counterions
|
|
396
|
+
gro = basename + '.gro'
|
|
397
|
+
# Genion requires user input "SOL" to choose solvent for replacement
|
|
398
|
+
input_fname = basename + '.salt.genion.in'
|
|
399
|
+
open(input_fname, 'w').write('SOL')
|
|
400
|
+
# Different versions of Gromacs use different counterions
|
|
401
|
+
charge_str = ""
|
|
402
|
+
if 'GROMACS4.5' in force_field:
|
|
403
|
+
charge_str = " -pname NA -nname CL "
|
|
404
|
+
elif 'GROMACS4.0' in force_field:
|
|
405
|
+
charge_str = " -pname NA+ -nname CL- "
|
|
406
|
+
else:
|
|
407
|
+
raise ValueError("Cannot recognize force_field " + force_field)
|
|
408
|
+
if counter_ion_charge > 0:
|
|
409
|
+
charge_str += " -np %d " % counter_ion_charge
|
|
410
|
+
else:
|
|
411
|
+
charge_str += " -nn %d " % abs(counter_ion_charge)
|
|
412
|
+
log = basename + '.salt.genion.log'
|
|
413
|
+
data.binary(
|
|
414
|
+
'genion',
|
|
415
|
+
'-g %s -s %s -o %s -p %s -neutral %s' % \
|
|
416
|
+
(log, tpr, gro, top, charge_str),
|
|
417
|
+
basename + '.salt.genion',
|
|
418
|
+
input_fname)
|
|
419
|
+
util.check_files(gro)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def pdb_to_top_and_crds(force_field, pdb, basename, solvent_buffer=10):
|
|
423
|
+
"""
|
|
424
|
+
Converts a PDB file into GROMACS topology and coordinate files,
|
|
425
|
+
and fully converted PDB file. These constitute the restart files
|
|
426
|
+
of a GROMACS simulation.
|
|
427
|
+
"""
|
|
428
|
+
util.check_files(pdb)
|
|
429
|
+
full_pdb = os.path.abspath(pdb)
|
|
430
|
+
save_dir = os.getcwd()
|
|
431
|
+
|
|
432
|
+
# All intermediate files placed into a subdirectory
|
|
433
|
+
util.goto_dir(basename + '.solvate')
|
|
434
|
+
|
|
435
|
+
# Remove all but protein heavy atoms in a single clean conformation
|
|
436
|
+
pdb = basename + '.clean.pdb'
|
|
437
|
+
pdbtext.clean_pdb(full_pdb, pdb)
|
|
438
|
+
|
|
439
|
+
# Generate protein topology in pdb2gmx_gro using pdb2gmx
|
|
440
|
+
pdb2gmx_gro = basename + '.pdb2gmx.gro'
|
|
441
|
+
top = basename + '.top'
|
|
442
|
+
itp = basename + '_posre.itp'
|
|
443
|
+
# Choose force field based on GROMACS version
|
|
444
|
+
if 'GROMACS4.5' in force_field:
|
|
445
|
+
ff = 'amber99'
|
|
446
|
+
elif 'GROMACS4.0' in force_field:
|
|
447
|
+
ff = 'G43a1'
|
|
448
|
+
else:
|
|
449
|
+
raise ValueError("Couldn't work out pdb2gmx for " + force_field)
|
|
450
|
+
args = '-ignh -ff %s -water spc -missing -f %s -o %s -p %s -i %s -chainsep id_or_ter -merge all' \
|
|
451
|
+
% (ff, pdb, pdb2gmx_gro, top, itp)
|
|
452
|
+
data.binary('pdb2gmx', args, basename+'.pdb2gmx')
|
|
453
|
+
util.check_files(pdb2gmx_gro)
|
|
454
|
+
|
|
455
|
+
# Now add a box with editconf
|
|
456
|
+
box_gro = basename + '.box.gro'
|
|
457
|
+
solvent_buffer_in_nm = solvent_buffer/10.0
|
|
458
|
+
data.binary(
|
|
459
|
+
'editconf',
|
|
460
|
+
'-f %s -o %s -c -d %f -bt cubic' \
|
|
461
|
+
% (pdb2gmx_gro, box_gro, solvent_buffer_in_nm),
|
|
462
|
+
basename+'.box')
|
|
463
|
+
util.check_files(box_gro)
|
|
464
|
+
|
|
465
|
+
# Given box dimensions, can now populate with explict waters
|
|
466
|
+
solvated_gro = basename + '.solvated.gro'
|
|
467
|
+
data.binary(
|
|
468
|
+
'genbox',
|
|
469
|
+
'-cp %s -cs spc216.gro -o %s -p %s' \
|
|
470
|
+
% (box_gro, solvated_gro, top),
|
|
471
|
+
'%s.solvated' % basename)
|
|
472
|
+
util.check_files(solvated_gro)
|
|
473
|
+
|
|
474
|
+
# Neutralize with counterions using genion to place ions
|
|
475
|
+
# based on energy parameters processed by grompp
|
|
476
|
+
gro = basename + '.gro'
|
|
477
|
+
neutralize_system_with_salt(top, solvated_gro, basename, force_field)
|
|
478
|
+
util.check_files(gro)
|
|
479
|
+
|
|
480
|
+
# Make a reference PDB file from restart files for viewing and restraints
|
|
481
|
+
convert_restart_to_pdb(basename, basename+'.pdb')
|
|
482
|
+
|
|
483
|
+
# Copy finished restart files back into original directory
|
|
484
|
+
fnames = util.re_glob(
|
|
485
|
+
'*', os.path.basename(basename) + r'[^\.]*\.(pdb|itp|gro|mdp|top)$')
|
|
486
|
+
for fname in fnames:
|
|
487
|
+
shutil.copy(fname, save_dir)
|
|
488
|
+
|
|
489
|
+
# Cleanup
|
|
490
|
+
delete_backup_files(basename)
|
|
491
|
+
os.chdir(save_dir)
|
|
492
|
+
|
|
493
|
+
return top, gro
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
# ##########################################################
|
|
497
|
+
|
|
498
|
+
# # 3. Run simulations from restart files
|
|
499
|
+
|
|
500
|
+
# Simulation approach:
|
|
501
|
+
# - cubic periodic box
|
|
502
|
+
# - optional positional restraints: 100 kcal/mol/angs**2
|
|
503
|
+
# - PME electrostatics on the periodic box
|
|
504
|
+
# - Langevin thermostat for constant temperature
|
|
505
|
+
# - Nose-Hoover barometer with flexible periodic box size
|
|
506
|
+
# - constraints on hydrogen atoms bonded to heavy atoms
|
|
507
|
+
|
|
508
|
+
# Binaries used:
|
|
509
|
+
# 1. grompp - process topology files for .tpr file
|
|
510
|
+
# 2. mdrun - run MD using .tpr on the .gro file
|
|
511
|
+
# mpi version - mpiexec -np 8 /...dir.../gromacs-4.0.7/bin/mdrun
|
|
512
|
+
|
|
513
|
+
# Files for trajectories:
|
|
514
|
+
# 1. coordinate trajectory: md.trr
|
|
515
|
+
# 2. restart coordinate/velocity: md.gro
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
minimization_parms = {
|
|
519
|
+
'topology' : 'in.top',
|
|
520
|
+
'input_crds' : 'in.gro',
|
|
521
|
+
'output_basename' : 'min',
|
|
522
|
+
'force_field': 'GROMACS',
|
|
523
|
+
'restraint_pdb': '',
|
|
524
|
+
'restraint_force': 100.0,
|
|
525
|
+
'n_step_minimization' : 100,
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
constant_energy_parms = {
|
|
529
|
+
'topology' : 'in.top',
|
|
530
|
+
'input_crds' : 'in.gro',
|
|
531
|
+
'output_basename' : 'md',
|
|
532
|
+
'force_field': 'GROMACS',
|
|
533
|
+
'solvent_state': 2,
|
|
534
|
+
'surface_area': 1,
|
|
535
|
+
'restraint_pdb': '',
|
|
536
|
+
'restraint_force': 100.0,
|
|
537
|
+
'n_step_per_snapshot' : 50,
|
|
538
|
+
'n_step_dynamics' : 1000,
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
langevin_thermometer_parms = {
|
|
542
|
+
'topology' : 'in.top',
|
|
543
|
+
'input_crds' : 'in.gro',
|
|
544
|
+
'output_basename' : 'md',
|
|
545
|
+
'force_field': 'GROMACS',
|
|
546
|
+
'restraint_pdb': '',
|
|
547
|
+
'restraint_force': 100.0,
|
|
548
|
+
'random_seed' : 2342,
|
|
549
|
+
'temperature_thermometer' : 300.0,
|
|
550
|
+
'temperature_initial_velocities': 0.0, # ignored if it is 0.0
|
|
551
|
+
'n_step_per_snapshot' : 50,
|
|
552
|
+
'n_step_dynamics' : 1000,
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
minimization_mdp = """
|
|
556
|
+
; template .mdp file used as input into grompp to generate energy minimization for mdrun
|
|
557
|
+
|
|
558
|
+
; Parameters describing what to do, when to stop and what to save
|
|
559
|
+
integrator = steep ; Algorithm (steep = steepest descent minimization)
|
|
560
|
+
emtol = 1000.0 ; Stop minimization when the maximum force < 1000.0 kJ/mol/nm
|
|
561
|
+
emstep = 0.01 ; Energy step size
|
|
562
|
+
nsteps = %(n_step_minimization)s ; Maximum number of (minimization) steps to perform
|
|
563
|
+
"""
|
|
564
|
+
|
|
565
|
+
dynamics_mdp = """
|
|
566
|
+
title = Template for constant temperature/pressure
|
|
567
|
+
|
|
568
|
+
; Run parameters
|
|
569
|
+
integrator = md ; leap-frog integrator
|
|
570
|
+
nsteps = %(n_step_dynamics)s ; time = n_step_dynamics*dt
|
|
571
|
+
dt = 0.001 ; time-step in fs
|
|
572
|
+
|
|
573
|
+
; Output control
|
|
574
|
+
nstxout = %(n_step_per_snapshot)s ; save coordinates every 0.05 ps
|
|
575
|
+
nstvout = %(n_step_per_snapshot)s ; save velocities every 0.05 ps
|
|
576
|
+
nstenergy = %(n_step_per_snapshot)s ; save energies every 0.05 ps
|
|
577
|
+
nstlog = %(n_step_per_snapshot)s ; update log file every 0.05 ps
|
|
578
|
+
|
|
579
|
+
; Pressure coupling is on
|
|
580
|
+
pcoupl = Parrinello-Rahman ; Pressure coupling on in NPT
|
|
581
|
+
pcoupltype = isotropic ; uniform scaling of box vectors
|
|
582
|
+
tau_p = 2.0 ; time constant, in ps
|
|
583
|
+
ref_p = 1.0 ; reference pressure, in bar
|
|
584
|
+
compressibility = 4.5e-5 ; isothermal compressibility of water, bar^-1
|
|
585
|
+
"""
|
|
586
|
+
|
|
587
|
+
temp_mdp = """
|
|
588
|
+
; Temperature coupling is on
|
|
589
|
+
tcoupl = V-rescale ; modified Berendsen thermostat
|
|
590
|
+
tc-grps = Protein Non-Protein ; two coupling groups - more accurate
|
|
591
|
+
tau_t = 0.1 0.1 ; time constant, in ps
|
|
592
|
+
ref_t = %(temperature_thermometer)s %(temperature_thermometer)s ; reference temperature, one for each group, in K
|
|
593
|
+
ld-seed = %(random_seed)s
|
|
594
|
+
"""
|
|
595
|
+
|
|
596
|
+
vel_mdp = """
|
|
597
|
+
; Velocity generation
|
|
598
|
+
gen_vel = yes ; assign velocities from Maxwell distribution
|
|
599
|
+
gen_temp = %(temperature_initial_velocities)s ; temperature for Maxwell distribution
|
|
600
|
+
gen_seed = -1 ; generate a random seed
|
|
601
|
+
"""
|
|
602
|
+
|
|
603
|
+
restraint_mdp = """
|
|
604
|
+
; Restaints turned on
|
|
605
|
+
define = -DPOSRES ; position restrain the protein
|
|
606
|
+
"""
|
|
607
|
+
|
|
608
|
+
def make_mdp(parms):
|
|
609
|
+
"""
|
|
610
|
+
Using GROMACS, we will always solvate, use periodic
|
|
611
|
+
boundaries and Particle Ewald Mesh.
|
|
612
|
+
"""
|
|
613
|
+
if 'n_step_minimization' in parms:
|
|
614
|
+
mdp = minimization_mdp
|
|
615
|
+
else:
|
|
616
|
+
mdp = dynamics_mdp
|
|
617
|
+
if 'temperature_thermometer' in parms:
|
|
618
|
+
mdp += temp_mdp
|
|
619
|
+
else:
|
|
620
|
+
mdp += "; Temperature coupling is off\n"
|
|
621
|
+
mdp += "tcoupl = no\n"
|
|
622
|
+
if 'temperature_initial_velocities' in parms and parms['temperature_initial_velocities'] > 0.0:
|
|
623
|
+
mdp += vel_mdp
|
|
624
|
+
else:
|
|
625
|
+
mdp += "; Velocity generation\n"
|
|
626
|
+
mdp += "gen_vel = no ; Velocity generation is off \n"
|
|
627
|
+
if 'restraint_pdb' in parms and parms['restraint_pdb']:
|
|
628
|
+
mdp += restraint_mdp
|
|
629
|
+
mdp += force_field_mdp
|
|
630
|
+
return mdp % parms
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def replace_include_file(chain, r1, r2):
|
|
634
|
+
lines = open(chain, 'r').readlines()
|
|
635
|
+
new_lines = []
|
|
636
|
+
for l in lines:
|
|
637
|
+
if l.startswith('#include'):
|
|
638
|
+
new_lines.append(l.replace(r1, r2))
|
|
639
|
+
else:
|
|
640
|
+
new_lines.append(l)
|
|
641
|
+
open(chain, 'w').write(''.join(new_lines))
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
restraint_header = """
|
|
645
|
+
; In this topology include file, you will find position restraint
|
|
646
|
+
; entries for all the heavy atoms in your original pdb file.
|
|
647
|
+
; This means that all the protons which were added by pdb2gmx are
|
|
648
|
+
; not restrained.
|
|
649
|
+
|
|
650
|
+
[ position_restraints ]
|
|
651
|
+
; atom type fx fy fz
|
|
652
|
+
"""
|
|
653
|
+
|
|
654
|
+
def make_restraint_itp(restraint_pdb, force):
|
|
655
|
+
txt = restraint_header
|
|
656
|
+
atoms = pdbatoms.Soup(restraint_pdb).atoms()
|
|
657
|
+
for i, atom in enumerate(atoms):
|
|
658
|
+
if atom.bfactor > 0.0:
|
|
659
|
+
txt += "%6s 1 %5.f %5.f %5.f\n" % (i+1, force, force, force)
|
|
660
|
+
return txt
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def run(in_parms):
|
|
664
|
+
"""
|
|
665
|
+
Run a GROMACS simulations using the PDBREMIX parms dictionary.
|
|
666
|
+
"""
|
|
667
|
+
parms = copy.deepcopy(in_parms)
|
|
668
|
+
basename = parms['output_basename']
|
|
669
|
+
|
|
670
|
+
# Copies across topology and related *.itp files, with appropriate
|
|
671
|
+
# filename renaming in #includes
|
|
672
|
+
top = basename + '.top'
|
|
673
|
+
in_top = parms['topology']
|
|
674
|
+
shutil.copy(in_top, top)
|
|
675
|
+
in_name = os.path.basename(in_top).replace('.top', '')
|
|
676
|
+
in_dir = os.path.dirname(in_top)
|
|
677
|
+
file_tag = "%s/%s_*itp" % (in_dir, in_name)
|
|
678
|
+
new_files = [top]
|
|
679
|
+
for f in glob.glob(file_tag):
|
|
680
|
+
new_f = os.path.basename(f)
|
|
681
|
+
new_f = new_f.replace(in_name, basename)
|
|
682
|
+
shutil.copy(f, new_f)
|
|
683
|
+
new_files.append(new_f)
|
|
684
|
+
for f in new_files:
|
|
685
|
+
replace_include_file(f, in_name + "_", basename + "_")
|
|
686
|
+
|
|
687
|
+
# Copy over input coordinates/velocities
|
|
688
|
+
in_gro = basename + '.in.gro'
|
|
689
|
+
shutil.copy(parms['input_crds'], in_gro)
|
|
690
|
+
|
|
691
|
+
# Generates a postiional-restraint topology file
|
|
692
|
+
if parms['restraint_pdb']:
|
|
693
|
+
# 1kcal*mol*A**-2 = 4.184 kJ*mol*(0.1 nm)**-2
|
|
694
|
+
kcalmolang2_to_kJmolnm2 = 400.184
|
|
695
|
+
open(basename + '_posre.itp', 'w').write(
|
|
696
|
+
make_restraint_itp(
|
|
697
|
+
parms['restraint_pdb'],
|
|
698
|
+
parms['restraint_force'] * kcalmolang2_to_kJmolnm2))
|
|
699
|
+
|
|
700
|
+
# Generate .mdp file based on parms
|
|
701
|
+
in_mdp = basename + '.grompp.mdp'
|
|
702
|
+
open(in_mdp, 'w').write(make_mdp(parms))
|
|
703
|
+
|
|
704
|
+
# Now run .grompp to generate this .tpr file
|
|
705
|
+
tpr = basename + '.tpr'
|
|
706
|
+
# .mdp to save complete set of parameters
|
|
707
|
+
mdp = basename + '.mdrun.mdp'
|
|
708
|
+
data.binary(
|
|
709
|
+
'grompp',
|
|
710
|
+
'-f %s -po %s -c %s -p %s -o %s' \
|
|
711
|
+
% (in_mdp, mdp, in_gro, top, tpr),
|
|
712
|
+
basename + '.grompp')
|
|
713
|
+
util.check_files(tpr)
|
|
714
|
+
|
|
715
|
+
# Run simulation with the .tpr file
|
|
716
|
+
data.binary(
|
|
717
|
+
'mdrun',
|
|
718
|
+
'-v -deffnm %s' % (basename),
|
|
719
|
+
basename + '.mdrun')
|
|
720
|
+
top, crds, vels = get_restart_files(basename)
|
|
721
|
+
util.check_output(top)
|
|
722
|
+
util.check_output(crds)
|
|
723
|
+
|
|
724
|
+
# Cleanup
|
|
725
|
+
delete_backup_files(basename)
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
# ##########################################################
|
|
729
|
+
|
|
730
|
+
# # 4. Read trajectories with some post-processing
|
|
731
|
+
|
|
732
|
+
# The units used in these files are:
|
|
733
|
+
|
|
734
|
+
# - positions: nanometers
|
|
735
|
+
# - velocities: nanometers/picosecond
|
|
736
|
+
# - force constant: kJ/mol/nm^2
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
n_dim = 3
|
|
740
|
+
|
|
741
|
+
class TrrReader:
|
|
742
|
+
"""
|
|
743
|
+
Class to read the coordinates of a GROMACS .trr file.
|
|
744
|
+
|
|
745
|
+
Attributes:
|
|
746
|
+
trr (str) - name of trajectory file
|
|
747
|
+
file (file) - file object to trajectory
|
|
748
|
+
n_atom (int) - number of atoms simulated
|
|
749
|
+
n_frame (int) - number of frames in trajectory
|
|
750
|
+
i_frame (int) - index of current frame
|
|
751
|
+
frame (array) - container of coordinates of current frame
|
|
752
|
+
|
|
753
|
+
Methods:
|
|
754
|
+
__init__ - initializes trajectory and loads 1st frame
|
|
755
|
+
load_frame(i) - loads the i'th frame
|
|
756
|
+
__getitem__ - returns the frame
|
|
757
|
+
save_to_crd - save current frame to a .crd file
|
|
758
|
+
__repr__ - string representation
|
|
759
|
+
"""
|
|
760
|
+
|
|
761
|
+
def __init__(self, trr):
|
|
762
|
+
self.trr = trr
|
|
763
|
+
self.file = open(self.trr, 'r')
|
|
764
|
+
self.read_header()
|
|
765
|
+
self.calc_precision()
|
|
766
|
+
self.calc_frame_info()
|
|
767
|
+
self.i_frame = None
|
|
768
|
+
self.load_frame(0)
|
|
769
|
+
|
|
770
|
+
def read_header(self):
|
|
771
|
+
self.u = xdrlib.Unpacker(self.file.read(200))
|
|
772
|
+
self.magic = self.u.unpack_int()
|
|
773
|
+
self.version = self.u.unpack_string()
|
|
774
|
+
self.size_ir = self.u.unpack_int()
|
|
775
|
+
self.size_e = self.u.unpack_int()
|
|
776
|
+
self.size_box = self.u.unpack_int()
|
|
777
|
+
self.size_vir = self.u.unpack_int()
|
|
778
|
+
self.size_pres = self.u.unpack_int()
|
|
779
|
+
self.size_top = self.u.unpack_int()
|
|
780
|
+
self.size_sym = self.u.unpack_int()
|
|
781
|
+
self.size_x = self.u.unpack_int()
|
|
782
|
+
self.size_v = self.u.unpack_int()
|
|
783
|
+
self.size_f = self.u.unpack_int()
|
|
784
|
+
self.n_atom = self.u.unpack_int()
|
|
785
|
+
self.step = self.u.unpack_int()
|
|
786
|
+
self.nre = self.u.unpack_int()
|
|
787
|
+
self.t = self.u.unpack_float()
|
|
788
|
+
self.lamb = self.u.unpack_float()
|
|
789
|
+
self.pos_after_header = self.u.get_position()
|
|
790
|
+
|
|
791
|
+
def calc_precision(self):
|
|
792
|
+
"Returns 4 for single precision, and 8 for double precision"
|
|
793
|
+
if self.size_box:
|
|
794
|
+
self.precision = self.size_box/n_dim/n_dim
|
|
795
|
+
elif self.size_x:
|
|
796
|
+
self.precision = self.size_x/n_dim
|
|
797
|
+
elif self.size_v:
|
|
798
|
+
self.precision = self.size_v/n_dim
|
|
799
|
+
elif self.size_f:
|
|
800
|
+
self.precision = self.size_f/n_dim
|
|
801
|
+
else:
|
|
802
|
+
raise ValueError("Cannot determine precision")
|
|
803
|
+
if self.precision not in [4, 8]:
|
|
804
|
+
raise ValueError("Precision not single or double!")
|
|
805
|
+
|
|
806
|
+
def calc_frame_info(self):
|
|
807
|
+
"""Given header info and precision, can calculate frame & n_frame"""
|
|
808
|
+
n_vec = 0
|
|
809
|
+
if self.size_box: n_vec += n_dim
|
|
810
|
+
if self.size_vir: n_vec += n_dim
|
|
811
|
+
if self.size_pres: n_vec += n_dim
|
|
812
|
+
if self.size_x: n_vec += self.n_atom
|
|
813
|
+
if self.size_v: n_vec += self.n_atom
|
|
814
|
+
if self.size_f: n_vec += self.n_atom
|
|
815
|
+
self.size_frame = n_vec*n_dim*self.precision + self.pos_after_header
|
|
816
|
+
|
|
817
|
+
# Calculates n_frame from end of file
|
|
818
|
+
self.file.seek(0, 2)
|
|
819
|
+
size_file = self.file.tell()
|
|
820
|
+
self.n_frame = size_file / self.size_frame
|
|
821
|
+
|
|
822
|
+
def __repr__(self):
|
|
823
|
+
return "< Gromacs TRR Coord file %s with %d frames of %d atoms >" % \
|
|
824
|
+
(self.trj, self.n_frame, self.n_atom)
|
|
825
|
+
|
|
826
|
+
def next_3_reals(self):
|
|
827
|
+
if self.precision == 4:
|
|
828
|
+
return [self.u.unpack_float() for i in range(3)]
|
|
829
|
+
if self.precision == 8:
|
|
830
|
+
return [self.u.unpack_double() for i in range(3)]
|
|
831
|
+
|
|
832
|
+
def load_frame(self, i_frame):
|
|
833
|
+
if i_frame < - 1*self.n_frame or i_frame >= self.n_frame:
|
|
834
|
+
raise IndexError
|
|
835
|
+
if i_frame < 0:
|
|
836
|
+
i_frame = self.n_frame + i_frame
|
|
837
|
+
if i_frame == self.i_frame:
|
|
838
|
+
return
|
|
839
|
+
self.file.seek(self.pos_after_header + i_frame*self.size_frame)
|
|
840
|
+
self.u = xdrlib.Unpacker(self.file.read(self.size_frame))
|
|
841
|
+
box, positions, velocities, forces = None, None, None, None
|
|
842
|
+
if self.size_box:
|
|
843
|
+
box = [self.next_3_reals() for i in range(n_dim)]
|
|
844
|
+
if self.size_vir:
|
|
845
|
+
dummy = [self.next_3_reals() for i in range(n_dim)]
|
|
846
|
+
if self.size_pres:
|
|
847
|
+
dummy = [self.next_3_reals() for i in range(n_dim)]
|
|
848
|
+
if self.size_x:
|
|
849
|
+
positions = [self.next_3_reals() for i in range(self.n_atom)]
|
|
850
|
+
if self.size_v:
|
|
851
|
+
velocities = [self.next_3_reals() for i in range(self.n_atom)]
|
|
852
|
+
if self.size_f:
|
|
853
|
+
forces = [self.next_3_reals() for i in range(self.n_atom)]
|
|
854
|
+
self.frame = box, positions, velocities, forces
|
|
855
|
+
self.i_frame = i_frame
|
|
856
|
+
|
|
857
|
+
def __getitem__(self, i_frame):
|
|
858
|
+
self.load_frame(i_frame)
|
|
859
|
+
return self.frame
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
class SoupTrajectory():
|
|
863
|
+
"""
|
|
864
|
+
Class to interact with an GROMACS trajctory using soup.
|
|
865
|
+
|
|
866
|
+
Attributes:
|
|
867
|
+
soup (Soup) - Soup object holding current coordinates/velocities
|
|
868
|
+
trr (str) - coordinate/velocity trajectory file
|
|
869
|
+
trr_reader (TrrReader) - the reader of the frames
|
|
870
|
+
n_frame (int) - number of frames in trajectory
|
|
871
|
+
i_frame (int) - index of current frame
|
|
872
|
+
|
|
873
|
+
Methods:
|
|
874
|
+
__init__ - load coordinate and velocity trajectories and build soup
|
|
875
|
+
load_frame - loads new frame into soup
|
|
876
|
+
"""
|
|
877
|
+
|
|
878
|
+
def __init__(self, soup, trr):
|
|
879
|
+
self.soup = soup
|
|
880
|
+
self.trr = trr
|
|
881
|
+
self.trr_reader = TrrReader(self.trr)
|
|
882
|
+
self.n_frame = self.trr_reader.n_frame
|
|
883
|
+
self.atoms = self.soup.atoms()
|
|
884
|
+
self.load_frame(0)
|
|
885
|
+
|
|
886
|
+
def __repr__(self):
|
|
887
|
+
return "< Gromacs Trajectory object with %d frames of %d atoms >" % \
|
|
888
|
+
(self.trj, self.n_frame, self.n_atom)
|
|
889
|
+
|
|
890
|
+
def load_frame(self, i_frame):
|
|
891
|
+
box, positions, velocities, forces = self.trr_reader[i_frame]
|
|
892
|
+
for i, atom in enumerate(self.atoms):
|
|
893
|
+
v3.set_vector(
|
|
894
|
+
atom.pos,
|
|
895
|
+
positions[i][0]*10,
|
|
896
|
+
positions[i][1]*10,
|
|
897
|
+
positions[i][2]*10)
|
|
898
|
+
v3.set_vector(
|
|
899
|
+
atom.vel,
|
|
900
|
+
velocities[i][0]*10,
|
|
901
|
+
velocities[i][1]*10,
|
|
902
|
+
velocities[i][2]*10)
|
|
903
|
+
self.i_frame = self.trr_reader.i_frame
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
class Trajectory(object):
|
|
907
|
+
"""
|
|
908
|
+
Class to interact with an GROMACS trajctory using soup.
|
|
909
|
+
|
|
910
|
+
Attributes:
|
|
911
|
+
basename (str) - basename used to guess all required files
|
|
912
|
+
top (str) - topology file of trajectory
|
|
913
|
+
gro (str) - coordinate/velocity restart file
|
|
914
|
+
trr (str) - coordinate/velocity trajectory file
|
|
915
|
+
trr_reader (TrrReader) - the reader of the frames
|
|
916
|
+
n_frame (int) - number of frames in trajectory
|
|
917
|
+
i_frame (int) - index of current frame
|
|
918
|
+
soup (Soup) - Soup object holding current coordinates/velocities
|
|
919
|
+
|
|
920
|
+
Methods:
|
|
921
|
+
__init__ - load coordinate and velocity trajectories and build soup
|
|
922
|
+
load_frame - loads new frame into soup
|
|
923
|
+
"""
|
|
924
|
+
|
|
925
|
+
def __init__(self, basename):
|
|
926
|
+
self.basename = basename
|
|
927
|
+
self.top = basename + '.top'
|
|
928
|
+
self.gro = basename + '.gro'
|
|
929
|
+
self.soup = soup_from_top_gro(self.top, self.gro)
|
|
930
|
+
self.trr = basename + '.trr'
|
|
931
|
+
self.soup_trj = SoupTrajectory(self.soup, self.trr)
|
|
932
|
+
self.n_frame = self.soup_trj.n_frame
|
|
933
|
+
self.i_frame = None
|
|
934
|
+
self.load_frame(0)
|
|
935
|
+
|
|
936
|
+
def load_frame(self, i):
|
|
937
|
+
self.soup_trj.load_frame(i)
|
|
938
|
+
self.i_frame = self.soup_trj.i_frame
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
def merge_trajectories(basename, traj_basenames):
|
|
942
|
+
"""
|
|
943
|
+
Given a bunch of directories with consecutive trajectories, all
|
|
944
|
+
with the same basename, the function will splice them into a
|
|
945
|
+
single trajctory with basename in the current directory.
|
|
946
|
+
"""
|
|
947
|
+
save_dir = os.getcwd()
|
|
948
|
+
|
|
949
|
+
trr_fname = basename + '.trr'
|
|
950
|
+
trr_list = [b + '.trr' for b in traj_basenames]
|
|
951
|
+
util.check_files(*trr_list)
|
|
952
|
+
|
|
953
|
+
f = open(trr_fname, 'w')
|
|
954
|
+
for trr in trr_list:
|
|
955
|
+
trr = TrrReader(trr)
|
|
956
|
+
for i_frame in range(trr.n_frame-1):
|
|
957
|
+
trr.file.seek(trr.size_frame*i_frame)
|
|
958
|
+
txt = trr.file.read(trr.size_frame)
|
|
959
|
+
f.write(txt)
|
|
960
|
+
f.close()
|
|
961
|
+
|
|
962
|
+
# Copy parameters of last pulse into current directory
|
|
963
|
+
traj_basename = traj_basenames[-1]
|
|
964
|
+
for ext in ['.top', '.itp', '.tpr', '.mdrun.mdp',
|
|
965
|
+
'.grompp.mdp', '.gro']:
|
|
966
|
+
for f in glob.glob('%s*%s' % (traj_basename, ext)):
|
|
967
|
+
g = f.replace(traj_basename, basename)
|
|
968
|
+
shutil.copy(f, g)
|
|
969
|
+
if g.endswith('.top'):
|
|
970
|
+
replace_include_file(g, traj_basename + "_", basename + "_")
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
os.chdir(save_dir)
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
|