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,1078 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
__doc__ = """
|
|
4
|
+
|
|
5
|
+
Interface to the NAMD 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 sys
|
|
19
|
+
import copy
|
|
20
|
+
import struct
|
|
21
|
+
import shutil
|
|
22
|
+
|
|
23
|
+
from . import util
|
|
24
|
+
from . import data
|
|
25
|
+
from . import pdbatoms
|
|
26
|
+
from . import v3
|
|
27
|
+
from . import pdbtext
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ##########################################################
|
|
31
|
+
|
|
32
|
+
# 1. Reading and writing restart files
|
|
33
|
+
|
|
34
|
+
# In PDBREMIX, restart files for NAMD are assumed to
|
|
35
|
+
# have the naming scheme, as chosen by namd2 restart
|
|
36
|
+
# conventions:
|
|
37
|
+
|
|
38
|
+
# 1. topology file: sim.psf
|
|
39
|
+
# 2. coordinate file: sim.coor
|
|
40
|
+
# 2. velocity file: sim.vel
|
|
41
|
+
|
|
42
|
+
# Parsers have been written to read .psf. The .coor and .vel
|
|
43
|
+
# files are simply PDB files. These are the same file formats
|
|
44
|
+
# used by CHARMM.
|
|
45
|
+
|
|
46
|
+
# Standard units used in NAMD are:
|
|
47
|
+
# - positions: angstroms
|
|
48
|
+
# - velocities: angstroms/picosecond
|
|
49
|
+
# - mass: Da
|
|
50
|
+
# - charge: electron-charge
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def soup_from_psf(psf):
|
|
54
|
+
"""
|
|
55
|
+
Returns a Soup from a .psf file
|
|
56
|
+
"""
|
|
57
|
+
soup = pdbatoms.Soup()
|
|
58
|
+
curr_res_num = None
|
|
59
|
+
is_header = True
|
|
60
|
+
for line in open(psf):
|
|
61
|
+
if is_header:
|
|
62
|
+
if "NATOM" in line:
|
|
63
|
+
n_atom = int(line.split()[0])
|
|
64
|
+
is_header = False
|
|
65
|
+
continue
|
|
66
|
+
words = line.split()
|
|
67
|
+
atom_num = int(words[0])
|
|
68
|
+
chain_id = words[1]
|
|
69
|
+
res_num = int(words[2])
|
|
70
|
+
res_type = words[3]
|
|
71
|
+
atom_type = words[4]
|
|
72
|
+
charge = float(words[6])
|
|
73
|
+
mass = float(words[7])
|
|
74
|
+
if chain_id.startswith('WT') or chain_id.startswith('ION'):
|
|
75
|
+
is_hetatm = True
|
|
76
|
+
chain_id = " "
|
|
77
|
+
else:
|
|
78
|
+
is_hetatm = False
|
|
79
|
+
chain_id = chain_id[0]
|
|
80
|
+
if curr_res_num != res_num:
|
|
81
|
+
res = pdbatoms.Residue(res_type, chain_id, res_num)
|
|
82
|
+
soup.append_residue(res)
|
|
83
|
+
curr_res_num = res_num
|
|
84
|
+
atom = pdbatoms.Atom()
|
|
85
|
+
atom.vel = v3.vector()
|
|
86
|
+
atom.chain_id = chain_id
|
|
87
|
+
atom.is_hetatm = is_hetatm
|
|
88
|
+
atom.num = atom_num
|
|
89
|
+
atom.res_num = res_num
|
|
90
|
+
atom.res_type = res_type
|
|
91
|
+
atom.type = atom_type
|
|
92
|
+
atom.mass = mass
|
|
93
|
+
atom.charge = charge
|
|
94
|
+
atom.element = data.guess_element(res_type, atom_type)
|
|
95
|
+
soup.insert_atom(-1, atom)
|
|
96
|
+
if len(soup.atoms()) == n_atom:
|
|
97
|
+
break
|
|
98
|
+
convert_to_pdb_atom_names(soup)
|
|
99
|
+
return soup
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def convert_to_pdb_atom_names(soup):
|
|
103
|
+
"""
|
|
104
|
+
For the soup structure, converts residue
|
|
105
|
+
atom names peculiar to .coord into standard PDB names
|
|
106
|
+
for interacting with other systems.
|
|
107
|
+
"""
|
|
108
|
+
for res in soup.residues():
|
|
109
|
+
if res.type == "ILE":
|
|
110
|
+
if res.has_atom('CD'):
|
|
111
|
+
res.change_atom_type('CD', 'CD1')
|
|
112
|
+
if res.has_atom('OT2'):
|
|
113
|
+
res.change_atom_type('OT2', 'OXT')
|
|
114
|
+
if res.has_atom('OT1'):
|
|
115
|
+
res.change_atom_type('OT1', 'O')
|
|
116
|
+
if res.type == "TIP3":
|
|
117
|
+
res.set_type("HOH")
|
|
118
|
+
res.set_chain_id(" ")
|
|
119
|
+
if res.type == "HSE":
|
|
120
|
+
res.set_type("HIS")
|
|
121
|
+
for atom in res.atoms():
|
|
122
|
+
if atom.type[-1].isdigit() and atom.type[0] == "H":
|
|
123
|
+
new_atom_type = atom.type[-1] + atom.type[:-1]
|
|
124
|
+
res.change_atom_type(atom.type, new_atom_type)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def convert_to_namd_atom_names(soup):
|
|
128
|
+
"""
|
|
129
|
+
For writing into .coor files, converts PDB atom names
|
|
130
|
+
into NAMD specific atom names.
|
|
131
|
+
"""
|
|
132
|
+
for res in soup.residues():
|
|
133
|
+
if res.type == "ILE" and res.has_atom('CD1'):
|
|
134
|
+
res.change_atom_type('CD1', 'CD')
|
|
135
|
+
if res.has_atom('OXT'):
|
|
136
|
+
res.change_atom_type('OXT', 'OT2')
|
|
137
|
+
if res.has_atom('O'):
|
|
138
|
+
res.change_atom_type('O', 'OT1')
|
|
139
|
+
for atom in res.atoms():
|
|
140
|
+
if atom.type[0].isdigit() and atom.type[1] == "H":
|
|
141
|
+
new_atom_type = atom.type[1:] + atom.type[0]
|
|
142
|
+
res.change_atom_type(atom.type, new_atom_type)
|
|
143
|
+
if res.type == "HOH":
|
|
144
|
+
res.set_type("TIP3")
|
|
145
|
+
res.set_chain_id("W")
|
|
146
|
+
if res.type == "HIS":
|
|
147
|
+
res.set_type("HSE")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# The following functions wrap the above functions into a
|
|
151
|
+
# standard API that does not explicitly reference GROMACS
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def expand_restart_files(basename):
|
|
155
|
+
"""
|
|
156
|
+
Returns expanded restart files based on basename
|
|
157
|
+
"""
|
|
158
|
+
psf = os.path.abspath(basename + '.psf')
|
|
159
|
+
coor = os.path.abspath(basename + '.coor')
|
|
160
|
+
vel = os.path.abspath(basename + '.vel')
|
|
161
|
+
return psf, coor, vel
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def get_restart_files(basename):
|
|
165
|
+
"""
|
|
166
|
+
Returns restart files only if they exist
|
|
167
|
+
"""
|
|
168
|
+
psf, coor, vel = expand_restart_files(basename)
|
|
169
|
+
util.check_files(psf, coor)
|
|
170
|
+
if not os.path.isfile(vel):
|
|
171
|
+
vel = ''
|
|
172
|
+
return psf, coor, vel
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def soup_from_restart_files(psf, in_coor, in_vel='', skip_solvent=False):
|
|
176
|
+
"""
|
|
177
|
+
Reads a Soup from restart files.
|
|
178
|
+
"""
|
|
179
|
+
soup = soup_from_psf(psf)
|
|
180
|
+
coord_soup = pdbatoms.Soup(in_coor)
|
|
181
|
+
for atom, coord_atom in zip(soup.atoms(), coord_soup.atoms()):
|
|
182
|
+
p = coord_atom.pos
|
|
183
|
+
v3.set_vector(atom.pos, p[0], p[1], p[2])
|
|
184
|
+
if in_vel:
|
|
185
|
+
vel_soup = pdbatoms.Soup(in_vel)
|
|
186
|
+
for atom, vel_atom in zip(soup.atoms(), vel_soup.atoms()):
|
|
187
|
+
v = vel_atom.pos
|
|
188
|
+
v3.set_vector(atom.vel, v[0], v[1], v[2])
|
|
189
|
+
return soup
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def write_soup_to_crds_and_vels(in_soup, basename):
|
|
193
|
+
"""
|
|
194
|
+
From soup, writes out the coordinate/velocities, used for pulsing
|
|
195
|
+
"""
|
|
196
|
+
soup = in_soup.copy()
|
|
197
|
+
convert_to_namd_atom_names(soup)
|
|
198
|
+
coor = basename + '.coor'
|
|
199
|
+
soup.write_pdb(coor)
|
|
200
|
+
for atom in soup.atoms():
|
|
201
|
+
v3.set_vector(atom.pos, atom.vel[0], atom.vel[1], atom.vel[2])
|
|
202
|
+
vel = basename + '.vel'
|
|
203
|
+
soup.write_pdb(vel)
|
|
204
|
+
return coor, vel
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def convert_restart_to_pdb(basename, pdb):
|
|
208
|
+
"""
|
|
209
|
+
Converts restart files with basename into PDB file.
|
|
210
|
+
"""
|
|
211
|
+
psf, coor, vel = get_restart_files(basename)
|
|
212
|
+
soup = soup_from_restart_files(psf, coor, vel)
|
|
213
|
+
soup.write_pdb(pdb)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# ##########################################################
|
|
218
|
+
|
|
219
|
+
# # 2. Generate restart files from PDB
|
|
220
|
+
|
|
221
|
+
# The restart files used for PDBREMIX assumes a consistent file
|
|
222
|
+
# naming. For a given basename `sim`, the files are:
|
|
223
|
+
|
|
224
|
+
# 1. topology file: sim.psf
|
|
225
|
+
# 2. coordinate file: sim.coor
|
|
226
|
+
# 3. optional velocity: sim.vel
|
|
227
|
+
# 4. optional box: sim.xsc
|
|
228
|
+
|
|
229
|
+
# To generate a topology file from the PDB file:
|
|
230
|
+
|
|
231
|
+
# - handles multiple protein chains
|
|
232
|
+
# - hydrogens are removed and then regenerated by GROMACS
|
|
233
|
+
# - disulfide bonds are identified and written into scripts
|
|
234
|
+
# - charged residue protonation states are auto-detected
|
|
235
|
+
# - explicit water in cubic box with 10.0 angstrom buffer
|
|
236
|
+
# - counterions to neutralize the system
|
|
237
|
+
# - use the CHARM22 topology/parameters included in this library
|
|
238
|
+
|
|
239
|
+
# Binaries used to generate restart files:
|
|
240
|
+
|
|
241
|
+
# 1. psfgen - create topologies for atoms in PDB
|
|
242
|
+
# 2. vmd - to add waters and counterions
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
module_load_script = """
|
|
246
|
+
package require psfgen
|
|
247
|
+
topology %(topology)s
|
|
248
|
+
alias residue HIS HSE
|
|
249
|
+
alias atom ILE CD1 CD
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
fixed_waters_script = """
|
|
253
|
+
pdbalias residue HOH TIP3
|
|
254
|
+
pdbalias residue WAT TIP3
|
|
255
|
+
segment wat {
|
|
256
|
+
auto none
|
|
257
|
+
pdb %(water_pdb)s
|
|
258
|
+
}
|
|
259
|
+
pdbalias atom HOH O OH2
|
|
260
|
+
pdbalias atom WAT O OH2
|
|
261
|
+
coordpdb %(water_pdb)s wat
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
protein_chain_script = """
|
|
265
|
+
segment %(chain_id)s {
|
|
266
|
+
pdb %(chain_pdb)s
|
|
267
|
+
}
|
|
268
|
+
coordpdb %(chain_pdb)s %(chain_id)s
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
write_script = """
|
|
272
|
+
guesscoord
|
|
273
|
+
writepdb %(out_pdb)s
|
|
274
|
+
writepsf %(out_psf)s
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def make_chain_loading_script(pdb, basename):
|
|
279
|
+
script = ""
|
|
280
|
+
soup = pdbatoms.Soup(pdb)
|
|
281
|
+
for chain_id in soup.chain_ids():
|
|
282
|
+
if chain_id == ' ':
|
|
283
|
+
chain_id = 'A'
|
|
284
|
+
chain_pdb = '%s.chain.%s.pdb' % (basename, chain_id)
|
|
285
|
+
chain = soup.extract_chain(chain_id).write_pdb(chain_pdb)
|
|
286
|
+
script += protein_chain_script % {
|
|
287
|
+
'chain_id': chain_id, 'chain_pdb': chain_pdb }
|
|
288
|
+
return script
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def make_disulfide_script(pdb):
|
|
292
|
+
"""
|
|
293
|
+
Returns the psfgen script for disulfide bonds.
|
|
294
|
+
|
|
295
|
+
This function opens in_pdb in a soup object, and searches for
|
|
296
|
+
CYS residues where the SG-SG distance < 3 angs. These residues
|
|
297
|
+
are then renamed to CYX and written to out_pdb. The disulfide bonds
|
|
298
|
+
are then returned in a .tleap script fragment.
|
|
299
|
+
"""
|
|
300
|
+
soup = pdbatoms.Soup(pdb)
|
|
301
|
+
n = len(soup.residues())
|
|
302
|
+
|
|
303
|
+
# First generate the residue names recognized by psfgen
|
|
304
|
+
res_names = []
|
|
305
|
+
chain_id = None
|
|
306
|
+
i_res = None
|
|
307
|
+
for i in range(n):
|
|
308
|
+
res = soup.residue(i)
|
|
309
|
+
if res.chain_id != chain_id:
|
|
310
|
+
chain_id = res.chain_id
|
|
311
|
+
i_res = 1
|
|
312
|
+
res_names.append("%s:%s" % (chain_id, i_res))
|
|
313
|
+
i_res += 1
|
|
314
|
+
|
|
315
|
+
# Then search through for all CYS-CYS pairs and identify disulfide bonds
|
|
316
|
+
script = ""
|
|
317
|
+
for i in range(n):
|
|
318
|
+
for j in range(i+1, n):
|
|
319
|
+
if soup.residue(i).type in 'CYS' and soup.residue(j).type in 'CYS':
|
|
320
|
+
sg1 = soup.residue(i).atom('SG')
|
|
321
|
+
sg2 = soup.residue(j).atom('SG')
|
|
322
|
+
if v3.distance(sg1.pos, sg2.pos) < 3.0:
|
|
323
|
+
script += "patch DISU %s %s\n" % (res_names[i], res_names[j])
|
|
324
|
+
if script:
|
|
325
|
+
script = "# disulfide bonds\n" + script + "\n"
|
|
326
|
+
|
|
327
|
+
return script
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
solvate_vmd_script = """
|
|
331
|
+
# Solvate system
|
|
332
|
+
|
|
333
|
+
# Set minimum padding
|
|
334
|
+
set pad %(solvent_buffer)s
|
|
335
|
+
|
|
336
|
+
# Run solvate with automatic padding option
|
|
337
|
+
package require solvate
|
|
338
|
+
resetpsf
|
|
339
|
+
solvate %(in_psf)s %(in_pdb)s -o %(name)s.vmd -rotate -rotinc 5 -t $pad
|
|
340
|
+
|
|
341
|
+
# Find the periodic box size.
|
|
342
|
+
mol load psf %(name)s.vmd.psf
|
|
343
|
+
set mol1 [molinfo top get id]
|
|
344
|
+
animate read pdb %(name)s.vmd.pdb $mol1
|
|
345
|
+
|
|
346
|
+
set sel1 [atomselect $mol1 all]
|
|
347
|
+
set cent1 [measure center $sel1]
|
|
348
|
+
set size1 [measure minmax $sel1]
|
|
349
|
+
mol delete $mol1
|
|
350
|
+
|
|
351
|
+
set sizemin [lindex $size1 0]
|
|
352
|
+
set sizemax [lindex $size1 1]
|
|
353
|
+
|
|
354
|
+
set xsize [expr [lindex $sizemax 0]-[lindex $sizemin 0]]
|
|
355
|
+
set ysize [expr [lindex $sizemax 1]-[lindex $sizemin 1]]
|
|
356
|
+
set zsize [expr [lindex $sizemax 2]-[lindex $sizemin 2]]
|
|
357
|
+
|
|
358
|
+
# Find the padding in each direction to make the box cubic
|
|
359
|
+
set lmax $xsize
|
|
360
|
+
if {$ysize > $lmax} {
|
|
361
|
+
set lmax $ysize
|
|
362
|
+
}
|
|
363
|
+
if {$zsize > $lmax} {
|
|
364
|
+
set lmax $zsize
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
# I like my boxe size to be a nice even number
|
|
368
|
+
set maxsize [expr int(ceil($lmax))]
|
|
369
|
+
if {$maxsize%%2 != 0} { set maxsize [expr $maxsize +1] }
|
|
370
|
+
|
|
371
|
+
# Calculate additional padding
|
|
372
|
+
set xpad [expr {$pad+0.5*($maxsize-$xsize)}]
|
|
373
|
+
set ypad [expr {$pad+0.5*($maxsize-$ysize)}]
|
|
374
|
+
set zpad [expr {$pad+0.5*($maxsize-$zsize)}]
|
|
375
|
+
|
|
376
|
+
puts ":Padding: $xpad $ypad $zpad"
|
|
377
|
+
puts ":Box size: $lmax"
|
|
378
|
+
|
|
379
|
+
# Adjust padding for nonzero center of mass. These are used to manually set the padding in each direction (both + and -)
|
|
380
|
+
set xplus [expr $xpad - [lindex $cent1 0]]
|
|
381
|
+
set xmin [expr $xpad + [lindex $cent1 0]]
|
|
382
|
+
set yplus [expr $ypad - [lindex $cent1 1]]
|
|
383
|
+
set ymin [expr $ypad + [lindex $cent1 1]]
|
|
384
|
+
set zplus [expr $zpad - [lindex $cent1 2]]
|
|
385
|
+
set zmin [expr $zpad + [lindex $cent1 2]]
|
|
386
|
+
|
|
387
|
+
# Rerun solvate on the original structure using calculated padding to make the box cubic
|
|
388
|
+
resetpsf
|
|
389
|
+
solvate %(name)s.psf %(name)s.pdb -o %(name)s.vmd -rotate -rotinc 5 -x $xmin +x $xplus -y $ymin +y $yplus -z $zmin +z $zplus
|
|
390
|
+
|
|
391
|
+
# Check that it worked
|
|
392
|
+
mol delete all
|
|
393
|
+
mol load psf %(name)s.vmd.psf
|
|
394
|
+
set mol1 [molinfo top get id]
|
|
395
|
+
animate read pdb %(name)s.vmd.pdb $mol1
|
|
396
|
+
|
|
397
|
+
set sel1 [atomselect $mol1 all]
|
|
398
|
+
set cent1 [measure center $sel1]
|
|
399
|
+
set size1 [measure minmax $sel1]
|
|
400
|
+
|
|
401
|
+
set sizemin [lindex $size1 0]
|
|
402
|
+
set sizemax [lindex $size1 1]
|
|
403
|
+
|
|
404
|
+
set xsize [expr [lindex $sizemax 0]-[lindex $sizemin 0]]
|
|
405
|
+
set ysize [expr [lindex $sizemax 1]-[lindex $sizemin 1]]
|
|
406
|
+
set zsize [expr [lindex $sizemax 2]-[lindex $sizemin 2]]
|
|
407
|
+
|
|
408
|
+
puts ":Final size: $xsize, $ysize, $zsize"
|
|
409
|
+
puts ":Length: $sizemax"
|
|
410
|
+
puts ":Center: $cent1"
|
|
411
|
+
puts ":Size: $size1"
|
|
412
|
+
|
|
413
|
+
# Ionize
|
|
414
|
+
package require autoionize
|
|
415
|
+
# Find original charge
|
|
416
|
+
set all [atomselect $mol1 all]
|
|
417
|
+
set q [measure sumweights $all weight charge]
|
|
418
|
+
|
|
419
|
+
# Determine the number and type of ions to use
|
|
420
|
+
set natom [expr round($q)]
|
|
421
|
+
if {$natom < 0} {
|
|
422
|
+
set natom [expr abs($natom)]
|
|
423
|
+
autoionize -psf %(name)s.vmd.psf -pdb %(name)s.vmd.pdb -nna $natom -ncl 0 -o %(name)s
|
|
424
|
+
} elseif {$natom > 0} {
|
|
425
|
+
autoionize -psf %(name)s.vmd.psf -pdb %(name)s.vmd.pdb -nna 0 -ncl $natom -o %(name)s
|
|
426
|
+
} elseif {$natom == 0} {
|
|
427
|
+
exec cp %(name)s.vmd.psf %(name)s.psf
|
|
428
|
+
exec cp %(name)s.vmd.pdb %(name)s.pdb
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
# Check that it worked
|
|
432
|
+
mol delete all
|
|
433
|
+
mol load psf %(name)s.psf
|
|
434
|
+
set mol1 [molinfo top get id]
|
|
435
|
+
animate read pdb %(name)s.pdb $mol1
|
|
436
|
+
set all [atomselect $mol1 all]
|
|
437
|
+
puts ":Old charge: $q, New charge: [measure sumweights $all weight charge]"
|
|
438
|
+
mol delete all
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
def solvate_psf(in_psf, in_pdb, basename, solvent_buffer=10.0):
|
|
442
|
+
"""
|
|
443
|
+
Uses VMD to add explicit waters to a .psf topology file
|
|
444
|
+
"""
|
|
445
|
+
parms = {
|
|
446
|
+
'in_psf': in_psf,
|
|
447
|
+
'in_pdb': in_pdb,
|
|
448
|
+
'name': basename,
|
|
449
|
+
'solvent_buffer': solvent_buffer,
|
|
450
|
+
}
|
|
451
|
+
tcl = basename + '.vmd.tcl'
|
|
452
|
+
open(tcl, 'w').write(solvate_vmd_script % parms)
|
|
453
|
+
data.binary('vmd', '-dispdev text -eofexit', basename+'.vmd', tcl)
|
|
454
|
+
util.check_output(basename+'.vmd.pdb')
|
|
455
|
+
util.check_output(basename+'.pdb')
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def pdb_to_top_and_crds(force_field, pdb, basename, solvent_buffer=10.0):
|
|
459
|
+
"""
|
|
460
|
+
Creates CHARMM .coor and .psf file for NAMD simulation.
|
|
461
|
+
"""
|
|
462
|
+
solv_dir = basename + '.solvate'
|
|
463
|
+
save_dir = os.getcwd()
|
|
464
|
+
|
|
465
|
+
pdb = os.path.abspath(pdb)
|
|
466
|
+
|
|
467
|
+
util.goto_dir(solv_dir)
|
|
468
|
+
|
|
469
|
+
# Remove all but protein heavy atoms in a single clean conformation
|
|
470
|
+
stripped_pdb = basename + '.clean.pdb'
|
|
471
|
+
pdbtext.clean_pdb(pdb, stripped_pdb)
|
|
472
|
+
|
|
473
|
+
# Make input script for psfgen
|
|
474
|
+
psfgen_psf = basename+'.psfgen.psf'
|
|
475
|
+
psfgen_pdb = basename+'.psfgen.pdb'
|
|
476
|
+
script = module_load_script
|
|
477
|
+
script += make_chain_loading_script(stripped_pdb, basename)
|
|
478
|
+
script += make_disulfide_script(stripped_pdb)
|
|
479
|
+
script += write_script
|
|
480
|
+
script = script % {
|
|
481
|
+
# load the included CHARMM2 atom topologies
|
|
482
|
+
'topology': os.path.join(data.data_dir, 'charmm22.topology'),
|
|
483
|
+
'out_pdb': psfgen_pdb,
|
|
484
|
+
'out_psf': psfgen_psf
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
psfgen_in = basename+".psfgen.in"
|
|
488
|
+
open(psfgen_in, "w").write(script)
|
|
489
|
+
|
|
490
|
+
data.binary('psfgen', psfgen_in, basename+'.psfgen')
|
|
491
|
+
util.check_output(psfgen_psf)
|
|
492
|
+
util.check_output(psfgen_pdb)
|
|
493
|
+
|
|
494
|
+
solvate_psf(psfgen_psf, psfgen_pdb, basename, solvent_buffer)
|
|
495
|
+
|
|
496
|
+
psf = basename+'.psf'
|
|
497
|
+
coor = basename+'.coor'
|
|
498
|
+
pdb = basename+'.pdb'
|
|
499
|
+
os.rename(pdb, coor)
|
|
500
|
+
convert_restart_to_pdb(basename, pdb)
|
|
501
|
+
shutil.copy(psf, save_dir)
|
|
502
|
+
shutil.copy(coor, save_dir)
|
|
503
|
+
shutil.copy(pdb, save_dir)
|
|
504
|
+
|
|
505
|
+
os.chdir(save_dir)
|
|
506
|
+
|
|
507
|
+
return psf, coor
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
# ##########################################################
|
|
511
|
+
|
|
512
|
+
# # 3. Run simulations from restart files
|
|
513
|
+
|
|
514
|
+
# Simulation approach for implicit solvent:
|
|
515
|
+
# - optional positional constraints: 100 kcal/mol/angs**2
|
|
516
|
+
# - Langevin thermostat for constant temperature
|
|
517
|
+
|
|
518
|
+
# Simulation approach for explict water:
|
|
519
|
+
# - periodic box with PME electrostatics
|
|
520
|
+
# - optional positional restraints: 100 kcal/mol/angs**2
|
|
521
|
+
# - Langevin thermostat for constant temperature
|
|
522
|
+
# - Nose-Hoover barometer with flexible periodic box size
|
|
523
|
+
|
|
524
|
+
# Binaries used:
|
|
525
|
+
# 1. namd2
|
|
526
|
+
|
|
527
|
+
# Files for trajectories:
|
|
528
|
+
# 1. coordinate trajectory: md.dcd
|
|
529
|
+
# 2. velocitiy trajectory: md.vel.dcd
|
|
530
|
+
# 3. restart coordinate: md.coor
|
|
531
|
+
# 4. restart velocity: md.vel
|
|
532
|
+
# 5. restart box: md.xsc
|
|
533
|
+
|
|
534
|
+
minimization_parms = {
|
|
535
|
+
'topology' : 'in.psf',
|
|
536
|
+
'input_crds' : 'in.pdb',
|
|
537
|
+
'output_basename' : 'min',
|
|
538
|
+
'force_field': 'NAMD',
|
|
539
|
+
'restraint_pdb': '',
|
|
540
|
+
'restraint_force': 100.0,
|
|
541
|
+
'n_step_minimization' : 100,
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
constant_energy_parms = {
|
|
545
|
+
'topology' : 'in.top',
|
|
546
|
+
'input_crds': 'in.coor',
|
|
547
|
+
'input_vels': 'in.vel',
|
|
548
|
+
'output_basename' : 'md',
|
|
549
|
+
'random_seed' : 2342,
|
|
550
|
+
'force_field': 'NAMD',
|
|
551
|
+
'n_step_per_snapshot' : 5,
|
|
552
|
+
'n_step_dynamics' : 1000,
|
|
553
|
+
'restraint_pdb': '',
|
|
554
|
+
'restraint_force': 100.0,
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
langevin_thermometer_parms = {
|
|
558
|
+
'topology' : 'in.top',
|
|
559
|
+
'input_crds': 'in.coor',
|
|
560
|
+
'input_vels': '',
|
|
561
|
+
'temperature_thermometer' : 300.0,
|
|
562
|
+
'force_field': 'NAMD',
|
|
563
|
+
'output_basename' : 'md',
|
|
564
|
+
'random_seed' : 2342,
|
|
565
|
+
'n_step_per_snapshot' : 5,
|
|
566
|
+
'n_step_dynamics' : 1000,
|
|
567
|
+
'restraint_pdb': '',
|
|
568
|
+
'restraint_force': 100.0,
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
io_script = """
|
|
573
|
+
# load topology and initial coordinates
|
|
574
|
+
structure %(topology)s
|
|
575
|
+
coordinates %(input_crds)s
|
|
576
|
+
|
|
577
|
+
# set output basenames
|
|
578
|
+
outputName %(output_basename)s
|
|
579
|
+
binaryOutput no
|
|
580
|
+
"""
|
|
581
|
+
|
|
582
|
+
simulation_parameters_script = """
|
|
583
|
+
# forcefield parameters
|
|
584
|
+
%(psf_type)s
|
|
585
|
+
parameters %(parameter)s
|
|
586
|
+
|
|
587
|
+
# switching parameters
|
|
588
|
+
exclude scaled1-4
|
|
589
|
+
1-4scaling 1.0
|
|
590
|
+
dielectric 1.0
|
|
591
|
+
switching on
|
|
592
|
+
switchDist 8.0
|
|
593
|
+
cutoff 12.0
|
|
594
|
+
pairListDist 15.0
|
|
595
|
+
molly off
|
|
596
|
+
|
|
597
|
+
# Bond constraints using SHAKE
|
|
598
|
+
rigidBonds water
|
|
599
|
+
|
|
600
|
+
# Periodic Boundary Conditions and PME Electrostatics
|
|
601
|
+
wrapWater on
|
|
602
|
+
wrapAll on
|
|
603
|
+
wrapNearest on
|
|
604
|
+
PME yes
|
|
605
|
+
PMEGridSpacing 1.0
|
|
606
|
+
"""
|
|
607
|
+
|
|
608
|
+
restraint_script="""
|
|
609
|
+
# restraints, called constraints here
|
|
610
|
+
constraints on
|
|
611
|
+
consExp 2
|
|
612
|
+
consKFile %(restraint_pdb)s
|
|
613
|
+
consKCol B
|
|
614
|
+
consRef %(restraint_pdb)s
|
|
615
|
+
constraintScaling %(restraint_force)s
|
|
616
|
+
"""
|
|
617
|
+
|
|
618
|
+
molecular_dynamics_script = """
|
|
619
|
+
# DCD output
|
|
620
|
+
dcdFile %(output_basename)s.dcd
|
|
621
|
+
dcdFreq %(n_step_per_snapshot)s
|
|
622
|
+
velDcdFile %(output_basename)s.vel.dcd
|
|
623
|
+
velDcdFreq %(n_step_per_snapshot)s
|
|
624
|
+
|
|
625
|
+
# Molecular Dynamics Timesteps
|
|
626
|
+
numSteps %(n_step_dynamics)s
|
|
627
|
+
timeStep 1 # in fs
|
|
628
|
+
firstTimeStep 0
|
|
629
|
+
stepsPerCycle 20
|
|
630
|
+
nonBondedFreq 1
|
|
631
|
+
fullElectFrequency 2
|
|
632
|
+
"""
|
|
633
|
+
|
|
634
|
+
extended_periodic_box_script = """
|
|
635
|
+
# Periodic Box from file
|
|
636
|
+
extendedSystem %(xsc)s
|
|
637
|
+
"""
|
|
638
|
+
|
|
639
|
+
new_periodic_box_script = """
|
|
640
|
+
# Periodic Box Definition
|
|
641
|
+
cellBasisVector1 %(len_x)s 0.0 0.0
|
|
642
|
+
cellBasisVector2 0.0 %(len_y)s 0.0
|
|
643
|
+
cellBasisVector3 0.0 0.0 %(len_z)s
|
|
644
|
+
cellOrigin %(x_origin)s %(y_origin)s %(z_origin)s
|
|
645
|
+
"""
|
|
646
|
+
|
|
647
|
+
def calculate_periodic_box_script(parms):
|
|
648
|
+
"""
|
|
649
|
+
Returns namd2 input fragment to parameterize the periodic
|
|
650
|
+
box for the protein. The requires loading the protein
|
|
651
|
+
and directly calculating a good bounding box.
|
|
652
|
+
"""
|
|
653
|
+
script = new_periodic_box_script
|
|
654
|
+
p = pdbatoms.Soup(parms['input_crds'])
|
|
655
|
+
atoms = p.atoms()
|
|
656
|
+
parms = {}
|
|
657
|
+
for i_axis, axis in enumerate(['x', 'y', 'z']):
|
|
658
|
+
vals = [a.pos[i_axis] for a in atoms]
|
|
659
|
+
axis_min, axis_max = min(vals), max(vals)
|
|
660
|
+
parms["len_"+axis] = axis_max - axis_min + 0.5
|
|
661
|
+
parms[axis+"_origin"] = sum(vals)/float(len(vals))
|
|
662
|
+
return script % parms
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
import_velocities_script = """
|
|
666
|
+
# Import velocities
|
|
667
|
+
velocities %(input_vels)s
|
|
668
|
+
"""
|
|
669
|
+
|
|
670
|
+
generate_velocities_script = """
|
|
671
|
+
# Generate velocities from temperature
|
|
672
|
+
temperature %(temperature_initial_velocities)s
|
|
673
|
+
seed %(random_seed)s
|
|
674
|
+
"""
|
|
675
|
+
|
|
676
|
+
temperature_coupling_script = """
|
|
677
|
+
# Temperature Coupling with Langevin Thermometer
|
|
678
|
+
langevin on
|
|
679
|
+
langevinHydrogen off
|
|
680
|
+
langevinTemp %(temperature_thermometer)s
|
|
681
|
+
langevinDamping 5.0
|
|
682
|
+
"""
|
|
683
|
+
|
|
684
|
+
constant_pressure_script = """
|
|
685
|
+
# Constant Pressure Control (variable volume)
|
|
686
|
+
useGroupPressure yes # yes needed for rigidBonds
|
|
687
|
+
useFlexibleCell no
|
|
688
|
+
useConstantArea no
|
|
689
|
+
|
|
690
|
+
# Nose-Hoover Langevin barometer
|
|
691
|
+
langevinPiston on
|
|
692
|
+
langevinPistonTarget 1.01325 # in bar -> 1 atm
|
|
693
|
+
langevinPistonPeriod 200.0
|
|
694
|
+
langevinPistonDecay 100.0
|
|
695
|
+
langevinPistonTemp %(temperature_thermometer)s
|
|
696
|
+
"""
|
|
697
|
+
|
|
698
|
+
minimization_script = """
|
|
699
|
+
# Minimization Parameters
|
|
700
|
+
minimization on
|
|
701
|
+
numsteps %(n_step_minimization)s
|
|
702
|
+
"""
|
|
703
|
+
|
|
704
|
+
def make_namd_input_file(parms):
|
|
705
|
+
"""
|
|
706
|
+
Make namd2 input script to run simulations.
|
|
707
|
+
"""
|
|
708
|
+
script = io_script % parms
|
|
709
|
+
script += simulation_parameters_script % parms
|
|
710
|
+
if parms['restraint_pdb']:
|
|
711
|
+
script += restraint_script % parms
|
|
712
|
+
if parms['xsc']:
|
|
713
|
+
script += extended_periodic_box_script % parms
|
|
714
|
+
else:
|
|
715
|
+
script += calculate_periodic_box_script(parms)
|
|
716
|
+
if 'n_step_dynamics' in parms:
|
|
717
|
+
script += molecular_dynamics_script % parms
|
|
718
|
+
if parms['input_vels']:
|
|
719
|
+
script += import_velocities_script % parms
|
|
720
|
+
elif 'temperature_initial_velocities' in parms and parms['temperature_initial_velocities'] > 0.0:
|
|
721
|
+
script += generate_velocities_script % parms
|
|
722
|
+
else:
|
|
723
|
+
raise IndexError("No initial velocity information for dynamics run")
|
|
724
|
+
if 'temperature_thermometer' in parms:
|
|
725
|
+
script += temperature_coupling_script % parms
|
|
726
|
+
script += constant_pressure_script % parms
|
|
727
|
+
else:
|
|
728
|
+
script += minimization_script % parms
|
|
729
|
+
return script
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
def run(in_parms):
|
|
733
|
+
"""
|
|
734
|
+
Runs a NAMD simulation using the PDBREMIX in_parms dictionary.
|
|
735
|
+
"""
|
|
736
|
+
parms = copy.deepcopy(in_parms)
|
|
737
|
+
name = parms['output_basename']
|
|
738
|
+
|
|
739
|
+
# load the included CHARMM2 energy parameters
|
|
740
|
+
parms['parameter'] = os.path.join(data.data_dir, 'charmm22.parameter')
|
|
741
|
+
parms['psf_type'] = 'paraTypeCharmm on'
|
|
742
|
+
|
|
743
|
+
# copy over input xsc and topology files (same basename)
|
|
744
|
+
xsc = parms['topology'].replace('.psf', '.xsc')
|
|
745
|
+
if os.path.isfile(xsc):
|
|
746
|
+
shutil.copy(xsc, name + '.in.xsc')
|
|
747
|
+
parms['xsc'] = name + '.in.xsc'
|
|
748
|
+
else:
|
|
749
|
+
parms['xsc'] = ''
|
|
750
|
+
shutil.copy(parms['topology'], name + '.psf')
|
|
751
|
+
parms['topology'] = name + '.psf'
|
|
752
|
+
|
|
753
|
+
# copy over coordinates
|
|
754
|
+
shutil.copy(parms['input_crds'], name + '.in.coor')
|
|
755
|
+
parms['input_crds'] = name + '.in.coor'
|
|
756
|
+
|
|
757
|
+
# copy over velocities
|
|
758
|
+
if 'input_vels' in parms and parms['input_vels']:
|
|
759
|
+
shutil.copy(parms['input_vels'], name + '.in.vel')
|
|
760
|
+
parms['input_vels'] = name + '.in.vel'
|
|
761
|
+
else:
|
|
762
|
+
parms['input_vels'] = ''
|
|
763
|
+
|
|
764
|
+
# copy over restraint coordinates
|
|
765
|
+
if 'restraint_pdb' in parms and parms['restraint_pdb']:
|
|
766
|
+
shutil.copy(parms['restraint_pdb'], name + '.restraint.coor')
|
|
767
|
+
parms['restraint_pdb'] = name + '.restraint.coor'
|
|
768
|
+
else:
|
|
769
|
+
parms['restraint_pdb'] = ''
|
|
770
|
+
|
|
771
|
+
namd_in = name + ".namd2.in"
|
|
772
|
+
open(namd_in, "w").write(make_namd_input_file(parms))
|
|
773
|
+
|
|
774
|
+
data.binary('namd2', namd_in, name + '.namd2')
|
|
775
|
+
|
|
776
|
+
top, crds, vels = get_restart_files(name)
|
|
777
|
+
util.check_output(top)
|
|
778
|
+
util.check_output(crds)
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
# ##########################################################
|
|
783
|
+
|
|
784
|
+
# # 4. Read trajectories with some post-processing
|
|
785
|
+
|
|
786
|
+
# The units used in these files are:
|
|
787
|
+
# - positions: angstroms
|
|
788
|
+
# - velocities: angs/ps
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def check_dcd_byte_order(dcd):
|
|
792
|
+
"""
|
|
793
|
+
Uses flipdcd to DCD trajectories have matching OS endianess.
|
|
794
|
+
"""
|
|
795
|
+
if sys.byteorder in 'big':
|
|
796
|
+
option = '-B'
|
|
797
|
+
elif sys.byteorder in 'little':
|
|
798
|
+
option = '-L'
|
|
799
|
+
else:
|
|
800
|
+
raise Exception("Couldn't figure out system byte order %s" % sys.byteorder)
|
|
801
|
+
data.binary('flipdcd', '%s %s' % (option, dcd), dcd+'.flipdcd')
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
class DcdReader:
|
|
805
|
+
"""
|
|
806
|
+
Class to read CHARMM .dcd files.
|
|
807
|
+
|
|
808
|
+
Attributes:
|
|
809
|
+
dcd (str) - name of .dcd file
|
|
810
|
+
n_frame (int) - number of frames in trajectory
|
|
811
|
+
remarks (list) - title of file
|
|
812
|
+
pos_after_header (int) - position of frames in file
|
|
813
|
+
size_frame (int) - the size of the frame in bytes
|
|
814
|
+
n_atom (int) - number of atoms simulated
|
|
815
|
+
n_frame (int) - number of frames in trajectory
|
|
816
|
+
i_frame (int) - index of current frame
|
|
817
|
+
frame (int) - container of coordinates of current frame
|
|
818
|
+
|
|
819
|
+
Methods:
|
|
820
|
+
__init__ - open dcd, read header and load first frame
|
|
821
|
+
load_frame(i) - loads the coordinates of the i'th frame
|
|
822
|
+
__getitme__ - return a frame as a list (3xn) of floats.
|
|
823
|
+
"""
|
|
824
|
+
|
|
825
|
+
def __init__(self, dcd):
|
|
826
|
+
self.dcd = dcd
|
|
827
|
+
|
|
828
|
+
check_dcd_byte_order(self.dcd)
|
|
829
|
+
|
|
830
|
+
self.file = open(dcd, 'rb')
|
|
831
|
+
|
|
832
|
+
# Read heading block
|
|
833
|
+
if self._read_fmt_val('i') != 84:
|
|
834
|
+
raise Exception("DCD: first int of heading is not 84")
|
|
835
|
+
if self._read_fmt_vals('4c') != ('C', 'O', 'R', 'D') :
|
|
836
|
+
raise Exception("DCD: Missing 'CORD' tag")
|
|
837
|
+
self.n_frame = self._read_fmt_val('i')
|
|
838
|
+
self.i_start = self._read_fmt_val('i')
|
|
839
|
+
self.n_step_save = self._read_fmt_val('i')
|
|
840
|
+
self._read_fmt_val('5i')
|
|
841
|
+
self.n_fixed_atom = self._read_fmt_val('i')
|
|
842
|
+
if self.n_fixed_atom > 0:
|
|
843
|
+
raise Exception("DCD: this reader doesn't handle fixed atoms")
|
|
844
|
+
self.timeStep = self._read_fmt_val('d')
|
|
845
|
+
self._read_fmt_vals('9i')
|
|
846
|
+
if self._read_fmt_val('i') != 84 :
|
|
847
|
+
raise Exception("DCD: couldn't find ending 84 of the heading")
|
|
848
|
+
|
|
849
|
+
# Read title block
|
|
850
|
+
size = self._read_fmt_val('i')
|
|
851
|
+
if (size - 4) % 80 != 0 :
|
|
852
|
+
raise Exception("DCD: title block size is wrong")
|
|
853
|
+
self.remarks = []
|
|
854
|
+
n_line = self._read_fmt_val('i')
|
|
855
|
+
for i in range(0, n_line):
|
|
856
|
+
s = "".join(self._read_fmt_vals('80c'))
|
|
857
|
+
self.remarks.append(s.strip())
|
|
858
|
+
if self._read_fmt_val('i') != size:
|
|
859
|
+
raise Exception("DCD: title block end != block start")
|
|
860
|
+
|
|
861
|
+
# Read n_atom block
|
|
862
|
+
if self._read_fmt_val('i') != 4 :
|
|
863
|
+
raise Exception("DCD: n_atom field start != 4")
|
|
864
|
+
self.n_atom = self._read_fmt_val('i')
|
|
865
|
+
if self._read_fmt_val('i') != 4 :
|
|
866
|
+
raise Exception("DCD: n_atom field end != 4")
|
|
867
|
+
|
|
868
|
+
# Store end of header position
|
|
869
|
+
self.pos_after_header = self.file.tell()
|
|
870
|
+
|
|
871
|
+
# Calculate size_frame from the size of the rest of file
|
|
872
|
+
n_free_atom = self.n_atom - self.n_fixed_atom
|
|
873
|
+
self.size_frame = struct.calcsize('%df6i' % (3 * n_free_atom))
|
|
874
|
+
self.file.seek(0, 2)
|
|
875
|
+
last_pos = self.file.tell()
|
|
876
|
+
size_rest_of_file = last_pos - self.pos_after_header
|
|
877
|
+
implied_size_frame = size_rest_of_file / self.n_frame
|
|
878
|
+
self.extra_block_size = implied_size_frame - self.size_frame
|
|
879
|
+
if self.extra_block_size > 0:
|
|
880
|
+
self.size_frame += self.extra_block_size
|
|
881
|
+
|
|
882
|
+
self.i_frame = None
|
|
883
|
+
self.load_frame(0)
|
|
884
|
+
|
|
885
|
+
def _read_fmt_vals(self, fmt):
|
|
886
|
+
return struct.unpack(fmt, self.file.read(struct.calcsize(fmt)))
|
|
887
|
+
|
|
888
|
+
def _read_fmt_val(self, fmt):
|
|
889
|
+
return self._read_fmt_vals(fmt)[0]
|
|
890
|
+
|
|
891
|
+
def load_frame(self, i):
|
|
892
|
+
"""
|
|
893
|
+
Reads self.frame from loaded trajectory file. The frame
|
|
894
|
+
is (x_vals, y_vals, z_vals) for each atom.
|
|
895
|
+
"""
|
|
896
|
+
if i < - 1*self.n_frame or i >= self.n_frame:
|
|
897
|
+
raise IndexError
|
|
898
|
+
if i < 0:
|
|
899
|
+
i = self.n_frame + i
|
|
900
|
+
if i == self.i_frame:
|
|
901
|
+
return
|
|
902
|
+
|
|
903
|
+
frame_coords_pos = self.pos_after_header + i*self.size_frame
|
|
904
|
+
frame_coords_pos += self.extra_block_size
|
|
905
|
+
self.file.seek(frame_coords_pos)
|
|
906
|
+
|
|
907
|
+
coords_size_fmt = "%df" % self.n_atom
|
|
908
|
+
size = struct.calcsize(coords_size_fmt)
|
|
909
|
+
if size != self._read_fmt_val('i'):
|
|
910
|
+
raise Exception("DCD: x_vals block start != size")
|
|
911
|
+
x_vals = self._read_fmt_vals(coords_size_fmt)
|
|
912
|
+
if size != self._read_fmt_val('i'):
|
|
913
|
+
raise Exception("DCD: x_vals block end != size")
|
|
914
|
+
if size != self._read_fmt_val('i'):
|
|
915
|
+
raise Exception("DCD: y_vals block start != size")
|
|
916
|
+
y_vals = self._read_fmt_vals(coords_size_fmt)
|
|
917
|
+
if size != self._read_fmt_val('i'):
|
|
918
|
+
raise Exception("DCD: y_vals block end != size")
|
|
919
|
+
if size != self._read_fmt_val('i'):
|
|
920
|
+
raise Exception("DCD: z_vals block start != size")
|
|
921
|
+
z_vals = self._read_fmt_vals(coords_size_fmt)
|
|
922
|
+
if size != self._read_fmt_val('i'):
|
|
923
|
+
raise Exception("DCD: z_vals block end != size")
|
|
924
|
+
self.frame = (x_vals, y_vals, z_vals)
|
|
925
|
+
self.i_frame = i
|
|
926
|
+
|
|
927
|
+
def __getitem__(self, i):
|
|
928
|
+
self.load_frame(i)
|
|
929
|
+
return self.frame
|
|
930
|
+
|
|
931
|
+
def __repr__(self):
|
|
932
|
+
return "< DCD %s with %d frames of %d atoms >" % \
|
|
933
|
+
(self.dcd, self.n_frame, self.n_atom)
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
class SoupTrajectory:
|
|
937
|
+
"""
|
|
938
|
+
Class to interact with an CHARMM/NAMD DCD trajctory using soup.
|
|
939
|
+
|
|
940
|
+
Attributes:
|
|
941
|
+
basename (str) - basename used to guess all required files
|
|
942
|
+
psf (str) - topology file of trajectory
|
|
943
|
+
dcd (str) - coordinate trajectory file
|
|
944
|
+
vel_dcd (str) - velocity trajectory file
|
|
945
|
+
coor_dcd_reader (DcdReader) - the reader of the coordinates
|
|
946
|
+
vel_dcd_reader (DcdReader) - the reader of the velocitiies
|
|
947
|
+
n_frame (int) - number of frames in trajectory
|
|
948
|
+
i_frame (int) - index of current frame
|
|
949
|
+
soup (Soup) - Soup object holding current coordinates/velocities
|
|
950
|
+
|
|
951
|
+
Methods:
|
|
952
|
+
__init__ - load coordinate and velocity trajectories and build soup
|
|
953
|
+
load_frame - loads new frame into soup
|
|
954
|
+
"""
|
|
955
|
+
|
|
956
|
+
def __init__(self, soup, dcd, vel_dcd=''):
|
|
957
|
+
self.soup = soup
|
|
958
|
+
self.dcd = dcd
|
|
959
|
+
self.coor_dcd_reader = DcdReader(self.dcd)
|
|
960
|
+
self.vel_dcd = vel_dcd
|
|
961
|
+
if vel_dcd:
|
|
962
|
+
self.vel_dcd_reader = DcdReader(self.vel_dcd)
|
|
963
|
+
else:
|
|
964
|
+
self.vel_dcd_reader = None
|
|
965
|
+
self.n_frame = self.coor_dcd_reader.n_frame
|
|
966
|
+
self.i_frame = 0
|
|
967
|
+
self.load_frame(0)
|
|
968
|
+
|
|
969
|
+
def load_frame(self, i_frame):
|
|
970
|
+
x, y, z = self.coor_dcd_reader[i_frame]
|
|
971
|
+
atoms = self.soup.atoms()
|
|
972
|
+
for i in range(len(atoms)):
|
|
973
|
+
v3.set_vector(atoms[i].pos, x[i], y[i], z[i])
|
|
974
|
+
|
|
975
|
+
if self.vel_dcd_reader is not None:
|
|
976
|
+
x, y, z = self.vel_dcd_reader[i_frame]
|
|
977
|
+
for i in range(len(atoms)):
|
|
978
|
+
v3.set_vector(atoms[i].vel, x[i], y[i], z[i])
|
|
979
|
+
|
|
980
|
+
self.i_frame = self.coor_dcd_reader.i_frame
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
class Trajectory:
|
|
984
|
+
"""
|
|
985
|
+
Class to interact with an CHARMM/NAMD DCD trajctory using soup.
|
|
986
|
+
|
|
987
|
+
Attributes:
|
|
988
|
+
basename (str) - basename used to guess all required files
|
|
989
|
+
psf (str) - topology file of trajectory
|
|
990
|
+
dcd (str) - coordinate trajectory file
|
|
991
|
+
vel_dcd (str) - velocity trajectory file
|
|
992
|
+
coor_dcd_reader (DcdReader) - the reader of the coordinates
|
|
993
|
+
vel_dcd_reader (DcdReader) - the reader of the velocitiies
|
|
994
|
+
n_frame (int) - number of frames in trajectory
|
|
995
|
+
i_frame (int) - index of current frame
|
|
996
|
+
soup (Soup) - Soup object holding current coordinates/velocities
|
|
997
|
+
|
|
998
|
+
Methods:
|
|
999
|
+
__init__ - load coordinate and velocity trajectories and build soup
|
|
1000
|
+
load_frame - loads new frame into soup
|
|
1001
|
+
"""
|
|
1002
|
+
|
|
1003
|
+
def __init__(self, basename):
|
|
1004
|
+
self.basename = basename
|
|
1005
|
+
self.psf = basename + '.psf'
|
|
1006
|
+
self.soup = soup_from_psf(self.psf)
|
|
1007
|
+
self.dcd = basename + '.dcd'
|
|
1008
|
+
self.vel_dcd = basename + '.vel.dcd'
|
|
1009
|
+
if not os.path.isfile(self.vel_dcd):
|
|
1010
|
+
self.vel_dcd = ''
|
|
1011
|
+
self.soup_trj = SoupTrajectory(self.soup, self.dcd, self.vel_dcd)
|
|
1012
|
+
self.n_frame = self.soup_trj.n_frame
|
|
1013
|
+
self.i_frame = 0
|
|
1014
|
+
self.load_frame(0)
|
|
1015
|
+
|
|
1016
|
+
def load_frame(self, i_frame):
|
|
1017
|
+
self.soup_trj.load_frame(i_frame)
|
|
1018
|
+
self.i_frame = self.soup_trj.i_frame
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
def merge_dcds(psf, dcds, out_dcd):
|
|
1022
|
+
"""
|
|
1023
|
+
Given a list of traj filenames (trajs), merges them into one complete
|
|
1024
|
+
trajectory (out_traj) using top to work out the number of atoms, and
|
|
1025
|
+
hence the size of the frame of the trajectory.
|
|
1026
|
+
"""
|
|
1027
|
+
dcd_reader = DcdReader(dcds[0])
|
|
1028
|
+
pos_after_header = dcd_reader.pos_after_header
|
|
1029
|
+
size_frame = dcd_reader.size_frame
|
|
1030
|
+
del dcd_reader
|
|
1031
|
+
|
|
1032
|
+
shutil.copy(dcds[0], out_dcd)
|
|
1033
|
+
|
|
1034
|
+
merge_dcd_file = open(out_dcd, "ab+")
|
|
1035
|
+
for dcd in dcds[1:]:
|
|
1036
|
+
dcd_file = open(dcd, "rb")
|
|
1037
|
+
dcd_file.seek(-1, 2)
|
|
1038
|
+
eof = dcd_file.tell()
|
|
1039
|
+
|
|
1040
|
+
dcd_file.seek(pos_after_header)
|
|
1041
|
+
while dcd_file.tell() < eof:
|
|
1042
|
+
merge_dcd_file.write(dcd_file.read(size_frame))
|
|
1043
|
+
|
|
1044
|
+
dcd_file.close()
|
|
1045
|
+
merge_dcd_file.close()
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
def merge_trajectories(basename, traj_basenames):
|
|
1049
|
+
"""
|
|
1050
|
+
Splices together a bunch of simulations, all with the same
|
|
1051
|
+
basename, into one large simulation in the current directory.
|
|
1052
|
+
"""
|
|
1053
|
+
for ext in ['.psf', '.coor', '.vel', '.xsc']:
|
|
1054
|
+
f = traj_basenames[-1] + ext
|
|
1055
|
+
g = basename + ext
|
|
1056
|
+
shutil.copy(f, g)
|
|
1057
|
+
trajs = [b + '.dcd' for b in traj_basenames]
|
|
1058
|
+
merge_dcds(basename + '.psf', trajs, basename + '.dcd')
|
|
1059
|
+
vels = [b + '.vel.dcd' for b in traj_basenames]
|
|
1060
|
+
merge_dcds(basename + '.psf', vels, basename + '.vel.dcd')
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
# def merge_simulations(basename, pulses):
|
|
1064
|
+
# """
|
|
1065
|
+
# Splices together a bunch of simulations, all with the same
|
|
1066
|
+
# basename, into one large simulation in the current directory.
|
|
1067
|
+
# """
|
|
1068
|
+
# for ext in ['.psf', '.coor', '.vel', '.xsc']:
|
|
1069
|
+
# fname = '%s%s' % (basename, ext)
|
|
1070
|
+
# shutil.copy('%s/%s' % (pulses[-1], fname), fname)
|
|
1071
|
+
# trajs = [os.path.join(pulse, basename + '.dcd') for pulse in pulses]
|
|
1072
|
+
# merge_trajectories(basename + '.psf', trajs, basename + '.dcd')
|
|
1073
|
+
# vels = [os.path.join(pulse, basename + '.vel.dcd') for pulse in pulses]
|
|
1074
|
+
# merge_trajectories(basename + '.psf', vels, basename + '.vel.dcd')
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
|