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.
Files changed (71) hide show
  1. packmol_memgen/__init__.py +2 -0
  2. packmol_memgen/__version__.py +34 -0
  3. packmol_memgen/data/LICENSE.Apache-2.0 +201 -0
  4. packmol_memgen/data/extra_solvents.lib +789 -0
  5. packmol_memgen/data/frcmod.lipid_ext +97 -0
  6. packmol_memgen/data/frcmod.solvents +129 -0
  7. packmol_memgen/data/insane_lipids.txt +138 -0
  8. packmol_memgen/data/insane_solvents.txt +45 -0
  9. packmol_memgen/data/leaprc.extra_solvents +42 -0
  10. packmol_memgen/data/leaprc.lipid_ext +48 -0
  11. packmol_memgen/data/lipid_ext.lib +12312 -0
  12. packmol_memgen/data/martini_v3.0.0.itp +356605 -0
  13. packmol_memgen/data/memgen.parm +4082 -0
  14. packmol_memgen/data/pdbs.tar.gz +0 -0
  15. packmol_memgen/data/solvent.parm +14 -0
  16. packmol_memgen/example/example.sh +31 -0
  17. packmol_memgen/lib/__init__.py +0 -0
  18. packmol_memgen/lib/amber.py +77 -0
  19. packmol_memgen/lib/charmmlipid2amber/__init__.py +0 -0
  20. packmol_memgen/lib/charmmlipid2amber/charmmlipid2amber.csv +7164 -0
  21. packmol_memgen/lib/charmmlipid2amber/charmmlipid2amber.py +225 -0
  22. packmol_memgen/lib/pdbremix/LICENSE +21 -0
  23. packmol_memgen/lib/pdbremix/__init__.py +0 -0
  24. packmol_memgen/lib/pdbremix/_version.py +1 -0
  25. packmol_memgen/lib/pdbremix/amber.py +1103 -0
  26. packmol_memgen/lib/pdbremix/asa.py +227 -0
  27. packmol_memgen/lib/pdbremix/data/aminoacid.pdb +334 -0
  28. packmol_memgen/lib/pdbremix/data/binaries.json +26 -0
  29. packmol_memgen/lib/pdbremix/data/charmm22.parameter +2250 -0
  30. packmol_memgen/lib/pdbremix/data/charmm22.topology +1635 -0
  31. packmol_memgen/lib/pdbremix/data/color_b.py +682 -0
  32. packmol_memgen/lib/pdbremix/data/hin.lib +130 -0
  33. packmol_memgen/lib/pdbremix/data/hydroxide.lib +88 -0
  34. packmol_memgen/lib/pdbremix/data/make_chi.py +92 -0
  35. packmol_memgen/lib/pdbremix/data/opls.parameter +1108 -0
  36. packmol_memgen/lib/pdbremix/data/opls.topology +1869 -0
  37. packmol_memgen/lib/pdbremix/data/phd.frcmod +82 -0
  38. packmol_memgen/lib/pdbremix/data/phd.leaprc +4 -0
  39. packmol_memgen/lib/pdbremix/data/phd.prepin +35 -0
  40. packmol_memgen/lib/pdbremix/data/template.pdb +334 -0
  41. packmol_memgen/lib/pdbremix/data/znb.frcmod +24 -0
  42. packmol_memgen/lib/pdbremix/data/znb.leaprc +7 -0
  43. packmol_memgen/lib/pdbremix/data/znb.lib +69 -0
  44. packmol_memgen/lib/pdbremix/data.py +264 -0
  45. packmol_memgen/lib/pdbremix/fetch.py +102 -0
  46. packmol_memgen/lib/pdbremix/force.py +627 -0
  47. packmol_memgen/lib/pdbremix/gromacs.py +978 -0
  48. packmol_memgen/lib/pdbremix/lib/__init__.py +0 -0
  49. packmol_memgen/lib/pdbremix/lib/docopt.py +579 -0
  50. packmol_memgen/lib/pdbremix/lib/pyqcprot.py +305 -0
  51. packmol_memgen/lib/pdbremix/namd.py +1078 -0
  52. packmol_memgen/lib/pdbremix/pdbatoms.py +543 -0
  53. packmol_memgen/lib/pdbremix/pdbtext.py +120 -0
  54. packmol_memgen/lib/pdbremix/protein.py +311 -0
  55. packmol_memgen/lib/pdbremix/pymol.py +480 -0
  56. packmol_memgen/lib/pdbremix/rmsd.py +203 -0
  57. packmol_memgen/lib/pdbremix/simulate.py +420 -0
  58. packmol_memgen/lib/pdbremix/spacehash.py +73 -0
  59. packmol_memgen/lib/pdbremix/trajectory.py +286 -0
  60. packmol_memgen/lib/pdbremix/util.py +273 -0
  61. packmol_memgen/lib/pdbremix/v3.py +16 -0
  62. packmol_memgen/lib/pdbremix/v3array.py +482 -0
  63. packmol_memgen/lib/pdbremix/v3numpy.py +350 -0
  64. packmol_memgen/lib/pdbremix/volume.py +155 -0
  65. packmol_memgen/lib/utils.py +1017 -0
  66. packmol_memgen/main.py +2827 -0
  67. packmol_memgen_minimal-1.1.16.dist-info/METADATA +664 -0
  68. packmol_memgen_minimal-1.1.16.dist-info/RECORD +71 -0
  69. packmol_memgen_minimal-1.1.16.dist-info/WHEEL +4 -0
  70. packmol_memgen_minimal-1.1.16.dist-info/entry_points.txt +2 -0
  71. 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
+