1 """ @package forcebalance.gmxio GROMACS input/output. 3 @todo Even more stuff from forcefield.py needs to go into here. 8 from __future__
import division
9 from __future__
import print_function
11 from builtins
import zip
12 from builtins
import str
13 from builtins
import range
18 from forcebalance
import BaseReader
19 from forcebalance.engine
import Engine
28 from forcebalance.thermo
import Thermo
29 from copy
import deepcopy
32 from collections
import defaultdict, OrderedDict
37 from forcebalance.output
import getLogger
38 logger = getLogger(__name__)
40 def edit_mdp(fin=None, fout=None, options={}, defaults={}, verbose=False):
42 Read, create or edit a Gromacs MDP file. The MDP file contains GROMACS run parameters. 43 If the input file exists, it is parsed and options are replaced where "options" overrides them. 44 If the "options" dictionary contains more options, they are added at the end. 45 If the "defaults" dictionary contains more options, they are added at the end. 46 Keys are standardized to lower-case strings where all dashes are replaced by underscores. 47 The output file contains the same comments and "dressing" as the input. 48 Also returns a dictionary with the final key/value pairs. 53 Input .mdp file name containing options that are more important than "defaults", but less important than "options" 55 Output .mdp file name. 56 options : dict, optional 57 Dictionary containing mdp options. Existing options are replaced, new options are added at the end, None values are deleted from output mdp. 58 defaults : dict, optional 59 defaults Dictionary containing "default" mdp options, added only if they don't already exist. 60 verbose : bool, optional 61 Print out additional information 66 Key-value pairs combined from the input .mdp and the supplied options/defaults and equivalent to what's printed in the output mdp. 70 options = OrderedDict([(key.lower().replace(
'-',
'_'), str(val)
if val
is not None else None)
for key, val
in options.items()])
76 all_options = OrderedDict()
77 if fin
is not None and os.path.isfile(fin):
78 for line
in open(fin).readlines():
79 line = line.strip().expandtabs()
88 comms = s[1]
if len(s) > 1
else None 90 if set(data).issubset([
' ']):
94 keyf, valf = data.split(
'=',1)
95 key = keyf.strip().lower().replace(
'-',
'_')
100 if key
in clashes
and val != val0:
101 logger.error(
"edit_mdp tried to set %s = %s but its original value was %s = %s\n" % (key, val, key, val0))
104 if val
is None:
continue 105 if len(val) < len(valf):
106 valf =
' ' + val +
' '*(len(valf) - len(val)-1)
108 valf =
' ' + val +
' ' 109 lout = [keyf,
'=', valf]
110 if comms
is not None:
112 out.append(
''.join(lout))
116 all_options[key] = val
117 for key, val
in options.items():
118 key = key.lower().replace(
'-',
'_')
119 if key
not in haveopts:
121 out.append(
"%-20s = %s" % (key, val))
122 all_options[key] = val
124 for key, val
in defaults.items():
125 key = key.lower().replace(
'-',
'_')
127 if key
not in haveopts:
128 out.append(
"%-20s = %s" % (key, val))
129 all_options[key] = val
131 file_out =
wopen(fout)
133 print(line, file=file_out)
141 Create or edit a Gromacs ndx file. 142 @param[in] fout Output file name, can be the same as input file name. 143 @param[in] grps Dictionary containing key : atom selections. 144 @param[in] fin Input file name. 146 ndxgrps = OrderedDict()
149 if fin
is not None and os.path.isfile(fin):
150 for line
in open(fin):
152 if len(s) == 0:
continue 153 if line.startswith(
'['):
157 ndxgrps[grp] += [int(i)
for i
in s]
160 for name, nums
in ndxgrps.items():
161 print(
'[ %s ]' % name, file=outf)
162 for subl
in list(
grouper(nums, 15)):
163 print(
' '.join([
"%4i" % i
for i
in subl]) +
' ', file=outf)
167 nftypes = [
None,
'VDW',
'VDW_BHAM']
169 pftypes = [
None,
'VPAIR',
'VPAIR_BHAM']
171 bftypes = [
None,
'BONDS',
'G96BONDS',
'MORSE']
173 aftypes = [
None,
'ANGLES',
'G96ANGLES',
'CROSS_BOND_BOND',
174 'CROSS_BOND_ANGLE',
'UREY_BRADLEY',
'QANGLES']
176 dftypes = [
None,
'PDIHS',
'IDIHS',
'RBDIHS',
'PIMPDIHS',
'FOURDIHS',
None,
None,
'TABDIHS',
'PDIHMULS']
185 'atomtypes' : nftypes,
186 'nonbond_params': pftypes,
187 'pairtypes' : pftypes,
189 'bondtypes' : bftypes,
191 'angletypes' : aftypes,
192 'dihedrals' : dftypes,
193 'dihedraltypes' : dftypes,
194 'virtual_sites2': [
'NONE',
'VSITE2'],
195 'virtual_sites3': [
'NONE',
'VSITE3',
'VSITE3FD',
'VSITE3FAD',
'VSITE3OUT'],
196 'virtual_sites4': [
'NONE',
'VSITE4FD',
'VSITE4FDN']
208 pdict = {
'BONDS':{3:
'B', 4:
'K'},
210 'MORSE':{3:
'B', 4:
'C', 5:
'E'},
211 'ANGLES':{4:
'B', 5:
'K'},
213 'CROSS_BOND_BOND':{4:
'1', 5:
'2', 6:
'K'},
214 'CROSS_BOND_ANGLE':{4:
'1', 5:
'2', 6:
'3', 7:
'K'},
215 'QANGLES':{4:
'B', 5:
'K0', 6:
'K1', 7:
'K2', 8:
'K3', 9:
'K4'},
216 'UREY_BRADLEY':{4:
'T', 5:
'K1', 6:
'B', 7:
'K2'},
217 'PDIHS1':{5:
'B', 6:
'K'},
'PDIHS2':{5:
'B', 6:
'K'},
'PDIHS3':{5:
'B', 6:
'K'},
218 'PDIHS4':{5:
'B', 6:
'K'},
'PDIHS5':{5:
'B', 6:
'K'},
'PDIHS6':{5:
'B', 6:
'K'},
219 'PIMPDIHS1':{5:
'B', 6:
'K'},
'PIMPDIHS2':{5:
'B', 6:
'K'},
'PIMPDIHS3':{5:
'B', 6:
'K'},
220 'PIMPDIHS4':{5:
'B', 6:
'K'},
'PIMPDIHS5':{5:
'B', 6:
'K'},
'PIMPDIHS6':{5:
'B', 6:
'K'},
221 'FOURDIHS1':{5:
'B', 6:
'K'},
'FOURDIHS2':{5:
'B', 6:
'K'},
'FOURDIHS3':{5:
'B', 6:
'K'},
222 'FOURDIHS4':{5:
'B', 6:
'K'},
'FOURDIHS5':{5:
'B', 6:
'K'},
'FOURDIHS6':{5:
'B', 6:
'K'},
223 'PDIHMULS1':{5:
'B', 6:
'K'},
'PDIHMULS2':{5:
'B', 6:
'K'},
'PDIHMULS3':{5:
'B', 6:
'K'},
224 'PDIHMULS4':{5:
'B', 6:
'K'},
'PDIHMULS5':{5:
'B', 6:
'K'},
'PDIHMULS6':{5:
'B', 6:
'K'},
225 'IDIHS':{5:
'B', 6:
'K'},
226 'VDW':{4:
'S', 5:
'T'},
227 'VPAIR':{3:
'S', 4:
'T'},
229 'RBDIHS':{6:
'K1', 7:
'K2', 8:
'K3', 9:
'K4', 10:
'K5'},
230 'VDW_BHAM':{4:
'A', 5:
'B', 6:
'C'},
231 'VPAIR_BHAM':{3:
'A', 4:
'B', 5:
'C'},
232 'QTPIE':{1:
'C', 2:
'H', 3:
'A'},
234 'VSITE3':{5:
'A',6:
'B'},
235 'VSITE3FD':{5:
'A',6:
'D'},
236 'VSITE3FAD':{5:
'T',6:
'D'},
237 'VSITE3OUT':{5:
'A',6:
'B',7:
'C'},
238 'VSITE4FD':{6:
'A',7:
'B',8:
'D'},
239 'VSITE4FDN':{6:
'A',7:
'B',8:
'C'},
240 'DEF':{3:
'FLJ',4:
'FQQ'},
242 'DEFINE':dict([(i,
'')
for i
in range(100)])
246 """ Parses the 'atomtype' line. 248 Parses lines like this:\n 249 <tt> opls_135 CT 6 12.0107 0.0000 A 3.5000e-01 2.7614e-01\n 250 C 12.0107 0.0000 A 3.7500e-01 4.3932e-01\n 251 Na 11 22.9897 0.0000 A 6.068128070229e+03 2.662662556402e+01 0.0000e+00 ; PRM 5 6\n </tt> 252 Look at all the variety! 254 @param[in] line Input line. 255 @return answer Dictionary containing:\n 257 bonded atom type (if any)\n 258 atomic number (if any)\n 262 force field parameters\n 263 number of optional fields 266 sline = line.split(
';')[0].split()
274 atomtype = sline[wrd]
275 batomtype = sline[wrd]
279 if re.match(
'[A-Za-z]',sline[wrd]):
280 batomtype = sline[wrd]
286 if isint(sline[wrd]):
287 atomicnum = int(sline[wrd])
291 mass = float(sline[wrd])
294 chg = float(sline[wrd])
299 param = [float(i)
for i
in sline[wrd:]]
300 answer = {
'atomtype':atomtype,
'batomtype':batomtype,
'atomicnum':atomicnum,
'mass':mass,
'chg':chg,
'ptp':ptp,
'param':param,
'bonus':bonus}
305 """Finite state machine for parsing GROMACS force field files. 307 We open the force field file and read all of its lines. As we loop 308 through the force field file, we look for two types of tags: (1) section 309 markers, in GMX indicated by [ section_name ], which allows us to determine 310 the section, and (2) parameter tags, indicated by the 'PRM' or 'RPT' keywords. 312 As we go through the file, we figure out the atoms involved in the interaction 313 described on each line. 315 When a 'PRM' keyword is indicated, it is followed by a number which is the field 316 in the line to be modified, starting with zero. Based on the field number and the 317 section name, we can figure out the parameter type. With the parameter type 318 and the atoms in hand, we construct a 'parameter identifier' or pid which uniquely 319 identifies that parameter. We also store the physical parameter value in an array 320 called 'pvals0' and the precise location of that parameter (by filename, line number, 321 and field number) in a list called 'pfields'. 323 An example: Suppose in 'my_ff.itp' I encounter the following on lines 146 and 147: 327 CA CB O 1 109.47 350.00 ; PRM 4 5 330 From reading <tt>[ angletypes ]</tt> I know I'm in the 'angletypes' section. 332 On the next line, I notice two parameters on fields 4 and 5. 334 From the atom types, section type and field number I know the parameter IDs are <tt>'ANGLESBCACBO'</tt> and <tt>'ANGLESKCACBO'</tt>. 336 After building <tt>map={'ANGLESBCACBO':1,'ANGLESKCACBO':2}</tt>, I store the values in 337 an array: <tt>pvals0=array([109.47,350.00])</tt>, and I put the parameter locations in 338 pfields: <tt>pfields=[['my_ff.itp',147,4,1.0],['my_ff.itp',146,5,1.0]]</tt>. The 1.0 339 is a 'multiplier' and I will explain it below. 341 Note that in the creation of parameter IDs, we run into the issue that the atoms 342 involved in the interaction may be labeled in reverse order (e.g. <tt>OCACB</tt>). Thus, 343 we store both the normal and the reversed parameter ID in the map. 345 Parameter repetition and multiplier: 347 If <tt>'RPT'</tt> is encountered in the line, it is always in the syntax: 348 <tt>'RPT 4 ANGLESBCACAH 5 MINUS_ANGLESKCACAH /RPT'</tt>. In this case, field 4 is replaced by 349 the stored parameter value corresponding to <tt>ANGLESBCACAH</tt> and field 5 is replaced by 350 -1 times the stored value of <tt>ANGLESKCACAH</tt>. Now I just picked this as an example, 351 I don't think people actually want a negative angle force constant .. :) the <tt>MINUS</tt> 352 keyword does come in handy for assigning atomic charges and virtual site positions. 353 In order to achieve this, a multiplier of -1.0 is stored into pfields instead of 1.0. 355 @todo Note that I can also create the opposite virtual site position by changing the atom 360 def __init__(self,fnm):
378 def feed(self, line):
379 """ Given a line, determine the interaction type and the atoms involved (the suffix). 381 For example, we want \n 382 <tt> H O H 5 1.231258497536e+02 4.269161426840e+02 -1.033397697685e-02 1.304674117410e+04 ; PRM 4 5 6 7 </tt> \n 383 to give us itype = 'UREY_BRADLEY' and suffix = 'HOH' 385 If we are in a TypeSection, it returns a list of atom types; \n 386 If we are in a TopolSection, it returns a list of atom names. 388 The section is essentially a case statement that picks out the 389 appropriate interaction type and makes a list of the atoms 392 Note that we can call gmxdump for this as well, but I 393 prefer to read the force field file directly. 395 ToDo: [ atoms ] section might need to be more flexible to accommodate optional fields 403 if len(s) == 0
or re.match(
'^ *;',line):
return None,
None 405 if re.match(
'^#', line):
408 elif hasattr(self,
'overpfx'):
409 delattr(self,
'overpfx')
410 delattr(self,
'oversfx')
412 if re.match(
'^ *\[.*\]',line):
414 self.
sec = re.sub(
'[\[\] \n]',
'',line.strip())
415 elif self.
sec ==
'defaults':
418 elif self.
sec ==
'moleculetype':
420 elif self.
sec ==
'atomtypes':
425 if atype[
'bonus'] > 0:
426 pdict[
'VDW'] = {4+atype[
'bonus']:
'S',5+atype[
'bonus']:
'T'}
427 pdict[
'VDW_BHAM'] = {4+atype[
'bonus']:
'A', 5+atype[
'bonus']:
'B', 6+atype[
'bonus']:
'C'}
428 atom = atype[
'atomtype']
431 self.AtomTypes[atype[
'atomtype']] = {
'AtomClass' : atype[
'batomtype'],
432 'AtomicNumber' : atype[
'atomicnum'],
433 'Mass' : atype[
'mass'],
434 'Charge' : atype[
'chg'],
435 'ParticleType' : atype[
'ptp']}
436 elif self.
sec ==
'nonbond_params':
439 elif self.
sec ==
'pairtypes':
442 elif self.
sec ==
'atoms':
450 self.adict.setdefault(self.
mol,[]).append(int(s[0]))
451 ffAtom = {
'AtomType' : s[1],
'ResidueNumber' : int(s[2]),
'ResidueName' : s[3],
'AtomName' : s[4],
'ChargeGroupNumber' : int(s[5]),
'Charge' : float(s[6])}
452 self.Molecules.setdefault(self.
mol,[]).append(ffAtom)
453 elif self.
sec ==
'polarization':
456 elif self.
sec ==
'qtpie':
460 elif self.
sec ==
'bonds':
461 atom = [self.adict[self.
mol][int(i)-1]
for i
in s[:2]]
462 self.
itype = fdict[self.
sec][int(s[2])]
463 elif self.
sec ==
'bondtypes':
465 self.
itype = fdict[self.
sec][int(s[2])]
466 elif self.
sec ==
'angles':
467 atom = [self.adict[self.
mol][int(i)-1]
for i
in s[:3]]
468 self.
itype = fdict[self.
sec][int(s[3])]
469 elif self.
sec ==
'angletypes':
470 atom = [s[0], s[1], s[2]]
471 self.
itype = fdict[self.
sec][int(s[3])]
472 elif self.
sec ==
'dihedrals':
473 atom = [self.adict[self.
mol][int(i)-1]
for i
in s[:4]]
474 self.
itype = fdict[self.
sec][int(s[4])]
475 if self.
itype in [
'PDIHS',
'PIMPDIHS',
'FOURDIHS',
'PDIHMULS']
and len(s) >= 7:
478 elif self.
sec ==
'dihedraltypes':
487 atom = [s[0], s[1], s[2], s[3]]
488 self.
itype = fdict[self.
sec][int(s[4])]
489 if self.
itype in [
'PDIHS',
'PIMPDIHS',
'FOURDIHS',
'PDIHMULS']
and len(s) >= 7:
491 elif self.
sec ==
'virtual_sites2':
492 atom = [self.adict[self.
mol][int(i)-1]
for i
in s[:1]]
494 self.
itype = fdict[self.
sec][int(s[3])]
495 elif self.
sec ==
'virtual_sites3':
496 atom = [self.adict[self.
mol][int(i)-1]
for i
in s[:1]]
499 self.
itype = fdict[self.
sec][int(s[4])]
500 elif self.
sec ==
'virtual_sites4':
501 atom = [self.adict[self.
mol][int(i)-1]
for i
in s[:1]]
504 self.
itype = fdict[self.
sec][int(s[5])]
507 if type(atom)
is list
and (len(atom) > 1
and atom[0] > atom[-1]):
511 self.
suffix =
':' +
''.join([
"%s" % i
for i
in atom])
512 elif self.
sec ==
'qtpie':
513 self.
suffix =
':' +
'.'.join([
"%s" % i
for i
in atom])
515 self.
suffix =
':' +
'-'.join([self.
mol,
'.'.join([
"%s" % i
for i
in atom])])
516 self.
molatom = (self.
mol, atom
if type(atom)
is list
else [atom])
520 for root, dirs, files
in os.walk(dir):
522 if re.match(
'^#',file):
527 """ Derived from Engine object for carrying out general purpose GROMACS calculations. """ 529 def __init__(self, name="gmx", **kwargs):
531 self.valkwd = [
'gmxsuffix',
'gmxpath',
'gmx_top',
'gmx_mdp',
'gmx_ndx',
'gmx_eq_barostat',
'gmx_pme_order']
532 super(GMX,self).__init__(name=name, **kwargs)
536 """ Called by __init__ ; Set GROMACS-specific options. """ 539 os.environ[
"GMX_MAXBACKUP"] =
"-1" 540 os.environ[
"GMX_NO_SOLV_OPT"] =
"TRUE" 541 os.environ[
"GMX_NO_ALLVSALL"] =
"TRUE" 544 if 'gmxsuffix' in kwargs:
547 warn_once(
"The 'gmxsuffix' option were not provided; using default.")
551 if 'gmx_eq_barostat' in kwargs:
556 if 'gmxpath' in kwargs:
557 self.
gmxpath = kwargs[
'gmxpath']
568 warn_press_key(
"The mdrun executable indicated by %s doesn't exist! (Check gmxpath and gmxsuffix)" \
572 warn_once(
"The 'gmxpath' option was not specified; using default.")
582 warn_press_key(
"Please add GROMACS executables to the PATH or specify gmxpath.")
583 logger.error(
"Cannot find the GROMACS executables!\n")
587 """ Called by __init__ ; read files from the source directory. """ 590 self.
top =
onefile(kwargs.get(
'gmx_top'),
'top')
591 self.
mdp =
onefile(kwargs.get(
'gmx_mdp'),
'mdp')
593 self.
mol = kwargs[
'mol']
595 self.
mol = Molecule(
onefile(kwargs.get(
'coords'),
'gro', err=
True))
597 def prepare(self, pbc=False, **kwargs):
598 """ Called by __init__ ; prepare the temp directory and figure out the topology. """ 600 self.
gmx_defs = OrderedDict([(
"integrator",
"md"), (
"dt",
"0.001"), (
"nsteps",
"0"),
601 (
"nstxout",
"0"), (
"nstfout",
"0"), (
"nstenergy",
"1"),
602 (
"nstxtcout",
"0"), (
"constraints",
"none"), (
"cutoff-scheme",
"group")])
603 gmx_opts = OrderedDict([])
608 minbox = min([self.
mol.boxes[0].a, self.
mol.boxes[0].b, self.
mol.boxes[0].c])
614 rlist = 0.05*(float(int(minbox - 1)))
619 if 'nonbonded_cutoff' in kwargs:
620 rlist = kwargs[
'nonbonded_cutoff'] / 10
623 if rlist > 0.05*(float(int(minbox - 1))):
624 warn_press_key(
"nonbonded_cutoff = %.1f should be smaller than half the box size = %.1f Angstrom" % (rlist*10, minbox))
626 if 'vdw_cutoff' in kwargs:
627 rvdw = kwargs[
'vdw_cutoff'] / 10
628 if rvdw > 0.05*(float(int(minbox - 1))):
629 warn_press_key(
"vdw_cutoff = %.1f should be smaller than half the box size = %.1f Angstrom" % (rvdw*10, minbox))
630 rvdw_switch = rvdw-0.05
631 gmx_opts[
"pbc"] =
"xyz" 634 self.
gmx_defs[
"rlist"] =
"%.2f" % rlist
635 self.
gmx_defs[
"coulombtype"] =
"pme" 636 self.
gmx_defs[
"rcoulomb"] =
"%.2f" % rlist
641 self.
gmx_defs[
"rvdw"] =
"%.2f" % rvdw
642 self.
gmx_defs[
"rvdw_switch"] =
"%.2f" % rvdw_switch
643 self.
gmx_defs[
"DispCorr"] =
"EnerPres" 645 if 'nonbonded_cutoff' in kwargs:
646 warn_press_key(
"Not using PBC, your provided nonbonded_cutoff will not be used")
647 if 'vdw_cutoff' in kwargs:
648 warn_press_key(
"Not using PBC, your provided vdw_cutoff will not be used")
649 gmx_opts[
"pbc"] =
"no" 653 self.
gmx_defs[
"coulombtype"] =
"cut-off" 655 self.
gmx_defs[
"vdwtype"] =
"cut-off" 659 if self.
top is not None:
660 LinkFile(os.path.join(self.srcdir, self.
top), self.
top, nosrcok=
True)
661 if self.
mdp is not None:
662 LinkFile(os.path.join(self.srcdir, self.
mdp), self.
mdp, nosrcok=
True)
666 if 'constraints' in mdp_dict.keys()
and mdp_dict[
'constraints'].replace(
'-',
'_').lower()
in [
'h_bonds',
'all_bonds',
'h_angles',
'all_angles']:
672 if hasattr(self,
'FF'):
677 if self.FF.rigid_water:
679 if not all([os.path.exists(f)
for f
in self.FF.fnms]):
683 self.FF.make(np.zeros(self.FF.np))
687 if self.
top is not None and os.path.exists(self.
top):
688 if self.
top not in self.FF.fnms
and (
not any([any([fnm
in line
for fnm
in self.FF.fnms])
for line
in open(self.
top)])):
689 logger.warning(
'Force field file is not referenced in the .top file\nAssuming the first .itp file is to be included\n')
690 for itpfnm
in self.FF.fnms:
691 if itpfnm.endswith(
".itp"):
693 topol = open(self.
top).readlines()
695 print(
"#include \"%s\"" % itpfnm, file=f)
698 print(line, end=
' ', file=f)
703 if hasattr(self,
'target')
and hasattr(self.target,
'shots'):
704 self.
mol.write(
"%s-all.gro" % self.name, selection=range(self.target.shots))
706 self.
mol.write(
"%s-all.gro" % self.name)
707 self.
mol[0].write(
'%s.gro' % self.name)
712 if self.
top is not None and os.path.exists(self.
top):
715 logger.error(
"No .top file found, cannot continue.\n")
717 edit_mdp(fin=self.
mdp, fout=
"%s.mdp" % self.name, options=gmx_opts, defaults=self.
gmx_defs)
720 o = self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s.mdp -o %s.tpr" % (self.name, self.name, self.name, self.name), warnings=warnings)
723 if 'double precision' in line:
725 o = self.
callgmx(
"gmxdump -s %s.tpr -sys" % self.name, copy_stderr=
True)
728 ptype_dict = {
'atom':
'A',
'vsite':
'D',
'shell':
'S'}
732 line = line.replace(
"=",
"= ")
735 ptype = s[s.index(
"ptype=")+1].replace(
',',
'').lower()
736 resind = int(s[s.index(
"resind=")+1].replace(
',',
'').lower())
737 mass = float(s[s.index(
"m=")+1].replace(
',',
'').lower())
738 charge = float(s[s.index(
"q=")+1].replace(
',',
'').lower())
741 self.
AtomLists[
'ResidueNumber'].append(resind)
742 self.
AtomLists[
'ParticleType'].append(ptype_dict[ptype])
746 ai = [int(i)
for i
in line.split(
"{")[1].split(
"}")[0].split(
"..")]
747 cg = int(line.split(
'[')[1].split(
']')[0])
748 for i
in range(ai[1]-ai[0]+1) : self.
AtomLists[
'ChargeGroupNumber'].append(cg)
750 ai = [int(i)
for i
in line.split(
"{")[1].split(
"}")[0].split(
"..")]
751 mn = int(line.split(
'[')[1].split(
']')[0])
752 for i
in range(ai[1]-ai[0]+1) : self.
AtomLists[
'MoleculeNumber'].append(mn)
753 os.unlink(
'mdout.mdp')
754 os.unlink(
'%s.tpr' % self.name)
755 if hasattr(self,
'FF')
and itptmp:
756 for f
in self.FF.fnms:
761 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s.mdp -o %s.tpr" % (self.name, self.name, self.name, self.name))
762 o = self.
callgmx(
"gmxdump -s %s.tpr -sys" % self.name, copy_stderr=
True)
766 line = line.replace(
"=",
"= ")
770 charge = float(s[s.index(
"q=")+1].replace(
',',
'').lower())
771 charges.append(charge)
772 os.unlink(
'%s.tpr' % self.name)
773 return np.array(charges)
776 topfile =
onefile(
'%s.top' % self.name,
'top', err=
True)
777 LinkFile(topfile,
"%s.top" % self.name)
778 mdpfile =
onefile(
'%s.mdp' % self.name,
'mdp', err=
True)
779 LinkFile(mdpfile,
"%s.mdp" % self.name, nosrcok=
True)
781 def callgmx(self, command, stdin=None, print_to_screen=False, print_command=False, **kwargs):
783 """ Call GROMACS; prepend the gmxpath to the call to the GROMACS program. """ 791 csplit = command.split()
792 prog = os.path.join(self.
gmxpath, csplit[0])
794 csplit[0] = csplit[0].replace(
'g_',
'').replace(
'gmxdump',
'dump')
795 csplit = [
'gmx' + self.
gmxsuffix] + csplit
799 raise RuntimeError(
'gmxversion can only be 4 or 5')
800 return _exec(
' '.join(csplit), stdin=stdin, print_to_screen=print_to_screen, print_command=print_command, **kwargs)
802 def warngmx(self, command, warnings=[], maxwarn=1, **kwargs):
804 """ Call gromacs and allow for certain expected warnings. """ 809 csplit = command.split()
810 if '-maxwarn' in csplit:
811 csplit[csplit.index(
'-maxwarn')+1] =
'%i' % maxwarn
813 csplit += [
'-maxwarn',
'%i' % maxwarn]
814 command =
' '.join(csplit)
815 o = self.
callgmx(command, persist=
True, copy_stderr=
True, print_error=
False, **kwargs)
821 warnthis.append(line)
823 if 'Fatal error' in line:
825 if 'WARNING' in line:
827 if fatal
and all([any([a
in w
for a
in warnings])
for w
in warnthis]):
828 maxwarn = len(warnthis)
829 csplit = command.split()
830 if '-maxwarn' in csplit:
831 csplit[csplit.index(
'-maxwarn')+1] =
'%i' % maxwarn
833 csplit += [
'-maxwarn',
'%i' % maxwarn]
834 command =
' '.join(csplit)
835 o = self.
callgmx(command, **kwargs)
838 logger.error(line+
'\n')
839 logger.error(
'grompp encountered a fatal error!\n')
845 """ Get a list of energy term names from the .edr file by parsing a system call to g_energy. """ 848 edrfile =
"%s.edr" % self.name
849 if not os.path.exists(edrfile):
850 logger.error(
'Cannot determine energy term names without an .edr file\n')
853 o = self.
callgmx(
"g_energy -f %s -xvg no" % (edrfile), stdin=
"Total-Energy\n", copy_stdout=
False, copy_stderr=
True)
855 energyterms = OrderedDict()
858 if "Select the terms you want from the following list" in line:
861 if len(s) > 0
and all([
isint(i)
for i
in s[::2]]):
867 for j
in range(len(s))[::2]:
870 energyterms[name] = num
874 def optimize(self, shot, crit=1e-4, align=True, **kwargs):
876 """ Optimize the geometry and align the optimized geometry to the starting geometry. """ 879 self.
mol[shot].write(
"%s.gro" % self.name)
881 if "min_opts" in kwargs:
882 min_opts = kwargs[
"min_opts"]
890 min_opts = {
"integrator" : algorithm,
"emtol" : crit,
"nstxout" : 0,
"nstfout" : 0,
"nsteps" : 10000,
"nstenergy" : 1}
892 edit_mdp(fin=
"%s.mdp" % self.name, fout=
"%s-min.mdp" % self.name, options=min_opts)
894 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s-min.mdp -o %s-min.tpr" % (self.name, self.name, self.name, self.name))
895 self.
callgmx(
"mdrun -deffnm %s-min -nt 1" % self.name)
896 self.
callgmx(
"trjconv -f %s-min.trr -s %s-min.tpr -o %s-min.gro -ndec 9" % (self.name, self.name, self.name), stdin=
"System")
897 self.
callgmx(
"g_energy -xvg no -f %s-min.edr -o %s-min-e.xvg" % (self.name, self.name), stdin=
'Potential')
899 E = float(open(
"%s-min-e.xvg" % self.name).readlines()[-1].split()[1])
900 M = Molecule(
"%s.gro" % self.name, build_topology=
False) + Molecule(
"%s-min.gro" % self.name, build_topology=
False)
902 M.align(center=
False)
903 rmsd = M.ref_rmsd(0)[1]
904 M[1].write(
"%s-min.gro" % self.name)
906 return E / 4.184, rmsd, M[1]
908 def evaluate_(self, force=False, dipole=False, traj=None):
911 Utility function for computing energy, and (optionally) forces and dipoles using GROMACS. 914 force: Switch for calculating the force. 915 dipole: Switch for calculating the dipole. 916 traj: Trajectory file name. If present, will loop over these snapshots. 917 Otherwise will do a single point evaluation at the current geometry. 920 Result: Dictionary containing energies, forces and/or dipoles. 923 shot_opts = OrderedDict([(
"nsteps", 0), (
"nstxout", 0), (
"nstxtcout", 0), (
"nstenergy", 1)])
924 shot_opts[
"nstfout"] = 1
if force
else 0
925 edit_mdp(fin=
"%s.mdp" % self.name, fout=
"%s-1.mdp" % self.name, options=shot_opts)
928 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s-1.mdp -o %s.tpr" % (self.name, self.name, self.name, self.name))
929 self.
callgmx(
"mdrun -deffnm %s -nt 1 -rerunvsite %s" % (self.name,
"-rerun %s" % traj
if traj
else ''))
932 Result = OrderedDict()
935 self.
callgmx(
"g_energy -xvg no -f %s.edr -o %s-e.xvg" % (self.name, self.name), stdin=
'Potential')
936 Efile = open(
"%s-e.xvg" % self.name).readlines()
937 Result[
"Energy"] = np.array([float(Eline.split()[1])
for Eline
in Efile])
941 self.
callgmx(
"g_traj -xvg no -s %s.tpr -f %s.trr -of %s-f.xvg -fp" % (self.name, self.name, self.name), stdin=
'System')
942 Result[
"Force"] = np.array([[float(j)
for i, j
in enumerate(line.split()[1:])
if self.
AtomMask[int(i/3)]] \
943 for line
in open(
"%s-f.xvg" % self.name).readlines()])
946 self.
callgmx(
"g_dipoles -s %s.tpr -f %s -o %s-d.xvg -xvg no" % (self.name, traj
if traj
else '%s.gro' % self.name, self.name), stdin=
"System\n")
947 Result[
"Dipole"] = np.array([[float(i)
for i
in line.split()[1:4]]
for line
in open(
"%s-d.xvg" % self.name)])
953 """ Evaluate variables (energies, force and/or dipole) using GROMACS for a single snapshot. """ 956 self.
mol[shot].write(
"%s.gro" % self.name)
961 """ Evaluate variables (energies, force and/or dipole) using GROMACS over a trajectory. """ 964 if hasattr(self,
'mdtraj'):
967 traj =
"%s-all.gro" % self.name
968 self.
mol[0].write(
"%s.gro" % self.name)
969 return self.
evaluate_(force, dipole, traj)
972 """ Return the MD trajectory as a Molecule object. """ 974 fout =
"%s-mdtraj.gro" % self.name
975 if not hasattr(self,
'mdtraj'):
976 raise RuntimeError(
'Engine does not have an associated trajectory.')
977 self.
callgmx(
"trjconv -f %s -o %s -ndec 9 -pbc mol -novel -noforce" % (self.
mdtraj, fout), stdin=
'System')
982 """ Compute the energy using GROMACS for a snapshot. """ 988 """ Compute the energy and force using GROMACS for a single snapshot; interfaces with AbInitio target. """ 991 return np.hstack((Result[
"Energy"].reshape(-1,1), Result[
"Force"]))
993 def energy(self, traj=None):
995 """ Compute the energy using GROMACS over a trajectory. """ 1001 """ Compute the energy and force using GROMACS over a trajectory. """ 1004 return np.hstack((Result[
"Energy"].reshape(-1,1), Result[
"Force"]))
1008 return np.hstack((Result[
"Energy"].reshape(-1,1), Result[
"Dipole"]))
1012 """ Calculate energy of the selected structure (optionally minimize and return the minimized energy and RMSD). In kcal/mol. """ 1017 self.
mol[shot].write(
"%s.gro" % self.name)
1018 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s.mdp -o %s-1.tpr" % (self.name, self.name, self.name, self.name))
1019 self.
callgmx(
"mdrun -deffnm %s-1 -nt 1 -rerunvsite" % (self.name, self.name))
1020 self.
callgmx(
"g_energy -xvg no -f %s-1.edr -o %s-1-e.xvg" % (self.name, self.name), stdin=
'Potential')
1021 E = float(open(
"%s-1-e.xvg" % self.name).readlines()[0].split()[1])
1026 """ Computes the interaction energy between two fragments over a trajectory. """ 1028 self.
mol[0].write(
"%s.gro" % self.name)
1031 write_ndx(
'%s.ndx' % self.name, OrderedDict([(
'A',[i+1
for i
in fraga]),(
'B',[i+1
for i
in fragb])]))
1034 edit_mdp(fin=
'%s.mdp' % self.name, fout=
'%s-i.mdp' % self.name, options={
'xtc_grps':
'A B',
'energygrps':
'A B'})
1035 edit_mdp(fin=
'%s.mdp' % self.name, fout=
'%s-x.mdp' % self.name, options={
'xtc_grps':
'A B',
'energygrps':
'A B',
'energygrp-excl':
'A B'})
1038 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s-i.mdp -n %s.ndx -o %s-i.tpr" % \
1039 (self.name, self.name, self.name, self.name, self.name))
1040 self.
callgmx(
"mdrun -deffnm %s-i -nt 1 -rerunvsite -rerun %s-all.gro" % (self.name, self.name))
1041 self.
callgmx(
"g_energy -f %s-i.edr -o %s-i-e.xvg -xvg no" % (self.name, self.name), stdin=
'Potential\n')
1043 for line
in open(
'%s-i-e.xvg' % self.name):
1044 I.append(sum([float(i)
for i
in line.split()[1:]]))
1048 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s-x.mdp -n %s.ndx -o %s-x.tpr" % \
1049 (self.name, self.name, self.name, self.name, self.name))
1050 self.
callgmx(
"mdrun -deffnm %s-x -nt 1 -rerunvsite -rerun %s-all.gro" % (self.name, self.name))
1051 self.
callgmx(
"g_energy -f %s-x.edr -o %s-x-e.xvg -xvg no" % (self.name, self.name), stdin=
'Potential\n')
1053 for line
in open(
'%s-x-e.xvg' % self.name):
1054 X.append(sum([float(i)
for i
in line.split()[1:]]))
1057 return (I - X) / 4.184
1061 """ Return the multipole moments of the 1st snapshot in Debye and Buckingham units. """ 1064 warn_once(
"Single-precision GROMACS detected - recommend that you use double precision build.")
1067 raise NotImplementedError
1071 M = Molecule(
"%s-min.gro" % self.name)
1073 self.
mol[shot].write(
"%s.gro" % self.name)
1074 M = Molecule(
"%s.gro" % self.name)
1083 ea_debye = 4.803204255928332
1085 x = M.xyzs[0] - M.center_of_mass()[0]
1087 xx, xy, xz, yy, yz, zz = (x[:,i]*x[:,j]
for i, j
in [(0,0),(0,1),(0,2),(1,1),(1,2),(2,2)])
1089 dip = ea_debye * np.sum(x*q.reshape(-1,1),axis=0)
1093 qxx = 1.5 * ea_debye * np.sum(q*xx)
1094 qxy = 1.5 * ea_debye * np.sum(q*xy)
1095 qxz = 1.5 * ea_debye * np.sum(q*xz)
1096 qyy = 1.5 * ea_debye * np.sum(q*yy)
1097 qyz = 1.5 * ea_debye * np.sum(q*yz)
1098 qzz = 1.5 * ea_debye * np.sum(q*zz)
1104 moments = [dx,dy,dz,qxx,qxy,qyy,qxz,qyz,qzz]
1105 dipole_dict = OrderedDict(zip([
'x',
'y',
'z'], moments[:3]))
1106 quadrupole_dict = OrderedDict(zip([
'xx',
'xy',
'yy',
'xz',
'yz',
'zz'], moments[3:10]))
1107 calc_moments = OrderedDict([(
'dipole', dipole_dict), (
'quadrupole', quadrupole_dict)])
1114 warn_once(
"Single-precision GROMACS detected - recommend that you use double precision build.")
1116 edit_mdp(fin=
'%s.mdp' % self.name, fout=
'%s-nm.mdp' % self.name, options={
'integrator':
'nm'})
1120 self.
warngmx(
"grompp -c %s-min.gro -p %s.top -f %s-nm.mdp -o %s-nm.tpr" % (self.name, self.name, self.name, self.name))
1122 warn_once(
"Asking for normal modes without geometry optimization?")
1123 self.
mol[shot].write(
"%s.gro" % self.name)
1124 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s-nm.mdp -o %s-nm.tpr" % (self.name, self.name, self.name, self.name))
1126 self.
callgmx(
"mdrun -deffnm %s-nm -nt 1 -mtx %s-nm.mtx -v" % (self.name, self.name))
1127 self.
callgmx(
"g_nmeig -s %s-nm.tpr -f %s-nm.mtx -of %s-nm.xvg -v %s-nm.trr -last 10000 -xvg no" % \
1128 (self.name, self.name, self.name, self.name))
1129 self.
callgmx(
"trjconv -s %s-nm.tpr -f %s-nm.trr -o %s-nm.gro -ndec 9" % (self.name, self.name, self.name), stdin=
"System")
1130 NM = Molecule(
"%s-nm.gro" % self.name, build_topology=
False)
1132 calc_eigvals = np.array([float(line.split()[1])
for line
in open(
"%s-nm.xvg" % self.name).readlines()])
1133 calc_eigvecs = NM.xyzs[1:]
1135 calc_eigvals = np.array(calc_eigvals)
1136 calc_eigvecs = np.array(calc_eigvecs)
1138 calc_eigvecs = calc_eigvecs[np.argsort(np.abs(calc_eigvals))][6:]
1139 calc_eigvals = calc_eigvals[np.argsort(np.abs(calc_eigvals))][6:]
1141 calc_eigvecs = calc_eigvecs[np.argsort(calc_eigvals)]
1142 calc_eigvals = calc_eigvals[np.argsort(calc_eigvals)]
1143 for i
in range(len(calc_eigvecs)):
1144 calc_eigvecs[i] /= np.linalg.norm(calc_eigvecs[i])
1145 return calc_eigvals, calc_eigvecs
1149 self.
warngmx(
"grompp -c %s.gro -p %s.top -f %s.mdp -o %s.tpr" % (self.name, self.name, self.name, self.name))
1150 self.
callgmx(
"mdrun -deffnm %s -nt 1 -rerunvsite -rerun %s-all.gro" % (self.name, self.name))
1151 self.
callgmx(
"trjconv -f %s.trr -o %s-out.gro -ndec 9 -novel -noforce" % (self.name, self.name), stdin=
'System')
1152 NewMol = Molecule(
"%s-out.gro" % self.name)
1155 def n_snaps(self, nsteps, step_interval, timestep):
1156 return int((nsteps * 1.0 / step_interval) * timestep)
1158 def scd_persnap(self, ndx, timestep, final_frame):
1160 for snap
in range(0, final_frame + 1):
1161 self.
callgmx(
"g_order -s %s-md.tpr -f %s-md.trr -n %s-%s.ndx -od %s-%s.xvg -b %i -e %i -xvg no" % (self.name, self.name, self.name, ndx, self.name, ndx, snap, snap))
1163 for line
in open(
"%s-%s.xvg" % (self.name, ndx)):
1164 s = [float(i)
for i
in line.split()]
1165 Scd_snap.append(s[-1])
1166 Scd.append(Scd_snap)
1169 def calc_scd(self, n_snap, timestep):
1172 sn1_ndx = [
'a C15',
'a C17',
'a C18',
'a C19',
'a C20',
'a C21',
'a C22',
'a C23',
'a C24',
'a C25',
'a C26',
'a C27',
'a C28',
'a C29',
'a C30',
'a C31',
'del 0-5',
'q',
'']
1173 sn2_ndx = [
'a C34',
'a C36',
'a C37',
'a C38',
'a C39',
'a C40',
'a C41',
'a C42',
'a C43',
'a C44',
'a C45',
'a C46',
'a C47',
'a C48',
'a C49',
'a C50',
'del 0-5',
'q',
'']
1174 self.
callgmx(
"make_ndx -f %s-md.tpr -o %s-sn1.ndx" % (self.name, self.name), stdin=
"\n".join(sn1_ndx))
1175 self.
callgmx(
"make_ndx -f %s-md.tpr -o %s-sn2.ndx" % (self.name, self.name), stdin=
"\n".join(sn2_ndx))
1181 for i
in range(0, n_snap + 1):
1182 sn1[i].extend(sn2[i])
1183 Scds = np.abs(np.array(sn1))
1187 mol = Molecule(structure_file)
1188 n_mol = len(mol.molecules)
1189 n_sol = len([i
for i
in mol.Data[
'resname']
if i ==
'SOL']) * 1.0 / 3
1190 return n_mol - n_sol
1192 def molecular_dynamics(self, nsteps, timestep, temperature=None, pressure=None, nequil=0, nsave=0, minimize=True, threads=None, verbose=False, bilayer=False, **kwargs):
1195 Method for running a molecular dynamics simulation. 1198 nsteps = (int) Number of total time steps 1199 timestep = (float) Time step in FEMTOSECONDS 1200 temperature = (float) Temperature control (Kelvin) 1201 pressure = (float) Pressure control (atmospheres) 1202 nequil = (int) Number of additional time steps at the beginning for equilibration 1203 nsave = (int) Step interval for saving data 1204 minimize = (bool) Perform an energy minimization prior to dynamics 1205 threads = (int) Number of MPI-threads 1207 Returns simulation data: 1208 Rhos = (array) Density in kilogram m^-3 1209 Potentials = (array) Potential energies 1210 Kinetics = (array) Kinetic energies 1211 Volumes = (array) Box volumes 1212 Dips = (3xN array) Dipole moments 1213 EComps = (dict) Energy components 1214 Als = (array) Average area per lipid in nm^2 1215 Scds = (Nx28 array) Deuterium order parameter 1218 if verbose: logger.info(
"Molecular dynamics simulation with GROMACS engine.\n")
1222 if "OMP_NUM_THREADS" in os.environ:
1223 threads = int(os.environ[
"OMP_NUM_THREADS"])
1228 md_opts = OrderedDict([(
"integrator",
"md"), (
"nsteps", nsteps), (
"dt", timestep / 1000)])
1230 md_defs = OrderedDict()
1235 mdp_opts =
edit_mdp(fin=
'%s.mdp' % self.name)
1236 if temperature
is not None:
1237 ref_t_str =
' '.join([
"%f" % temperature]*len(mdp_opts.get(
'tc_grps',
'System').split()))
1238 if pressure
is not None:
1239 ref_p_str =
' '.join([
"%f" % pressure]*len(mdp_opts.get(
'tc_grps',
'System').split()))
1242 if temperature
is not None:
1243 md_opts[
"ref_t"] = ref_t_str
1244 md_opts[
"gen_vel"] =
"no" 1245 md_defs[
"tc_grps"] =
"System" 1246 md_defs[
"tcoupl"] =
"v-rescale" 1247 md_defs[
"tau_t"] = 1.0
1249 md_opts[
"comm_mode"] =
"linear" 1250 if pressure
is not None:
1251 md_opts[
"ref_p"] = ref_p_str
1252 md_defs[
"pcoupl"] =
"parrinello-rahman" 1253 md_defs[
"tau_p"] = 1.5
1255 md_opts[
"comm_mode"] =
"None" 1256 md_opts[
"nstcomm"] = 0
1260 md_defs[
"cutoff-scheme"] =
'group' 1261 md_opts[
"nstenergy"] = nsave
1262 md_opts[
"nstcalcenergy"] = nsave
1263 md_opts[
"nstxout"] = nsave
1264 md_opts[
"nstvout"] = nsave
1265 md_opts[
"nstfout"] = 0
1266 md_opts[
"nstxtcout"] = 0
1270 min_opts = OrderedDict([(
"integrator",
"steep"), (
"emtol", 10.0), (
"nsteps", 10000)])
1271 if verbose: logger.info(
"Minimizing energy... ")
1272 self.
optimize(0, min_opts=min_opts)
1273 if verbose: logger.info(
"Done\n")
1274 gro1=
"%s-min.gro" % self.name
1276 gro1=
"%s.gro" % self.name
1277 self.
mol[0].write(gro1)
1281 if verbose: logger.info(
"Equilibrating...\n")
1282 eq_opts = deepcopy(md_opts)
1283 eq_opts.update({
"nsteps" : nequil,
"nstenergy" : 0,
"nstxout" : 0,
1284 "gen-vel":
"yes",
"gen-temp" : temperature,
"gen-seed" : random.randrange(100000,999999)})
1285 eq_defs = deepcopy(md_defs)
1286 if "pcoupl" in eq_defs
and hasattr(self,
'gmx_eq_barostat'):
1288 edit_mdp(fin=
'%s.mdp' % self.name, fout=
"%s-eq.mdp" % self.name, options=eq_opts, defaults=eq_defs)
1289 self.
warngmx(
"grompp -c %s -p %s.top -f %s-eq.mdp -o %s-eq.tpr" % (gro1, self.name, self.name, self.name), warnings=warnings, print_command=verbose)
1290 self.
callgmx(
"mdrun -v -deffnm %s-eq -nt %i -stepout %i" % (self.name, threads, nsave), print_command=verbose, print_to_screen=verbose)
1291 gro2=
"%s-eq.gro" % self.name
1296 if verbose: logger.info(
"Production run...\n")
1297 edit_mdp(fin=
"%s.mdp" % self.name, fout=
"%s-md.mdp" % self.name, options=md_opts, defaults=md_defs)
1298 self.
warngmx(
"grompp -c %s -p %s.top -f %s-md.mdp -o %s-md.tpr" % (gro2, self.name, self.name, self.name), warnings=warnings, print_command=verbose)
1299 self.
callgmx(
"mdrun -v -deffnm %s-md -nt %i -stepout %i" % (self.name, threads, nsave), print_command=verbose, print_to_screen=verbose)
1301 self.
mdtraj =
'%s-md.trr' % self.name
1303 if verbose: logger.info(
"Production run finished, calculating properties...\n")
1305 self.
callgmx(
"g_dipoles -s %s-md.tpr -f %s-md.trr -o %s-md-dip.xvg -xvg no" % (self.name, self.name, self.name), stdin=
"System\n")
1309 ekeep = [k
for k,v
in energyterms.items()
if v <= energyterms[
'Total-Energy']]
1310 ekeep += [
'Temperature',
'Volume',
'Density']
1315 n_lip = self.
n_nonwater(
'%s.gro' % self.name)
1316 n_snap = self.
n_snaps(nsteps, 1000, timestep)
1317 Scds = self.
calc_scd(n_snap, timestep)
1318 al_vars = [
'Box-Y',
'Box-X']
1319 self.
callgmx(
"g_energy -f %s-md.edr -o %s-md-energy-xy.xvg -xvg no" % (self.name, self.name), stdin=
"\n".join(al_vars))
1322 for line
in open(
"%s-md-energy-xy.xvg" % self.name):
1323 s = [float(i)
for i
in line.split()]
1328 Als = 2 * (Xs * Ys) / n_lip
1334 self.
callgmx(
"g_energy -f %s-md.edr -o %s-md-energy.xvg -xvg no" % (self.name, self.name), stdin=
"\n".join(ekeep))
1335 ecomp = OrderedDict()
1340 for line
in open(
"%s-md-energy.xvg" % self.name):
1341 s = [float(i)
for i
in line.split()]
1342 for i
in range(len(ekeep) - 2):
1344 if ekeep[i]
in ecomp:
1345 ecomp[ekeep[i]].append(val)
1347 ecomp[ekeep[i]] = [val]
1349 Volumes.append(s[-2])
1350 Rhos = np.array(Rhos)
1351 Volumes = np.array(Volumes)
1352 Potentials = np.array(ecomp[
'Potential'])
1353 Kinetics = np.array(ecomp[
'Kinetic-En.'])
1354 Dips = np.array([[float(i)
for i
in line.split()[1:4]]
for line
in open(
"%s-md-dip.xvg" % self.name)])
1355 Ecomps = OrderedDict([(key, np.array(val))
for key, val
in ecomp.items()])
1357 prop_return = OrderedDict()
1358 prop_return.update({
'Rhos': Rhos,
'Potentials': Potentials,
'Kinetics': Kinetics,
'Volumes': Volumes,
'Dips': Dips,
'Ecomps': Ecomps,
'Als': Als,
'Scds': Scds})
1359 if verbose: logger.info(
"Finished!\n")
1362 def md(self, nsteps=0, nequil=0, verbose=False, deffnm=None, **kwargs):
1365 Method for running a molecular dynamics simulation. A little different than molecular_dynamics (for thermo.py) 1369 nsteps = (int) Number of total time steps 1371 nequil = (int) Number of additional time steps at the beginning 1374 verbose = (bool) Be loud and noisy 1376 deffnm = (string) default names for simulation output files 1378 The simulation data is written to the working directory. 1383 logger.info(
"Molecular dynamics simulation with GROMACS engine.\n")
1386 md_opts = OrderedDict()
1388 md_defs = OrderedDict(**kwargs)
1391 md_opts[
"nsteps"] = nsteps
1395 if "gmx_ndx" in kwargs:
1396 ndx_flag =
"-n " + kwargs[
"gmx_ndx"]
1400 gro1 =
"%s.gro" % self.name
1405 logger.info(
"Equilibrating...\n")
1406 eq_opts = deepcopy(md_opts)
1407 eq_opts.update({
"nsteps" : nequil,
"nstenergy" : 0,
"nstxout" : 0})
1408 eq_defs = deepcopy(md_defs)
1410 fout=
"%s-eq.mdp" % self.name,
1416 "-f %s-eq.mdp " % self.name +
1417 "-p %s.top " % self.name +
1419 "-o %s-eq.tpr" % self.name),
1421 print_command=verbose)
1423 "-deffnm %s-eq" % self.name),
1424 print_command=verbose,
1425 print_to_screen=verbose)
1427 gro2 =
"%s-eq.gro" % self.name
1431 self.
mdtraj =
'%s-md.trr' % self.name
1432 self.
mdene =
'%s-md.edr' % self.name
1436 logger.info(
"Production run...\n")
1438 fout=
"%s-md.mdp" % self.name,
1443 "-f %s-md.mdp " % self.name +
1444 "-p %s.top " % self.name +
1446 "-o %s-md.tpr" % self.name),
1448 print_command=verbose)
1450 "-deffnm %s-md " % self.name),
1451 print_command=verbose,
1452 print_to_screen=verbose)
1454 self.
mdtraj =
'%s-md.trr' % self.name
1457 logger.info(
"Production run finished...\n")
1460 def __init__(self,options,tgt_opts,forcefield):
1462 self.set_option(options,
'gmxpath')
1464 self.set_option(options,
'gmxsuffix')
1466 self.set_option(tgt_opts,
'md_threads')
1468 self.set_option(tgt_opts,
'liquid_coords',default=
'liquid.gro',forceprint=
True)
1470 self.set_option(tgt_opts,
'gas_coords',default=
'gas.gro',forceprint=
True)
1472 self.set_option(tgt_opts,
'gmx_eq_barostat',forceprint=
True)
1476 self.engname =
"gromacs" 1478 self.nptpfx =
"bash rungmx.sh" 1479 if tgt_opts[
'remote_backup']:
1480 self.nptpfx +=
" -b" 1482 self.nptfiles = [
'%s.mdp' % os.path.splitext(f)[0]
for f
in [self.liquid_coords, self.gas_coords]]
1483 self.nptfiles += [
'%s.top' % os.path.splitext(f)[0]
for f
in [self.liquid_coords, self.gas_coords]]
1485 self.gas_engine_args = {
'gmx_top' :
'%s.top' % os.path.splitext(self.gas_coords)[0],
1486 'gmx_mdp' :
'%s.mdp' % os.path.splitext(self.gas_coords)[0]}
1490 super(Liquid_GMX,self).
__init__(options,tgt_opts,forcefield)
1493 if not os.path.exists(os.path.join(self.root, self.tgtdir, i)):
1494 logger.error(
'Please provide %s; it is needed to proceed.\n' % i)
1499 if self.save_traj > 0:
1502 self.
LfDict = OrderedDict()
1505 self.post_init(options)
1508 """ Submit a NPT simulation to the Work Queue. """ 1510 self.
LfDict[(temperature, pressure)] = self.
LfDict_New[(temperature, pressure)]
1511 if (temperature, pressure)
in self.
LfDict:
1513 lfdest = os.path.join(os.getcwd(),
'liquid.gro')
1514 logger.info(
"Copying previous iteration final geometry .gro file: %s to %s\n" % (lfsrc, lfdest))
1515 shutil.copy2(lfsrc,lfdest)
1517 self.
LfDict_New[(temperature, pressure)] = os.path.join(os.getcwd(),
'liquid-md.gro')
1518 super(Liquid_GMX, self).
npt_simulation(temperature, pressure, simnum)
1522 def __init__(self,options,tgt_opts,forcefield):
1524 self.set_option(options,
'gmxpath')
1526 self.set_option(options,
'gmxsuffix')
1528 self.set_option(tgt_opts,
'md_threads')
1530 self.set_option(tgt_opts,
'lipid_coords',default=
'lipid.gro',forceprint=
True)
1532 self.set_option(tgt_opts,
'gmx_eq_barostat',forceprint=
True)
1538 self.
nptpfx =
"bash rungmx.sh" 1539 if tgt_opts[
'remote_backup']:
1542 self.
nptfiles = [
'%s.mdp' % os.path.splitext(f)[0]
for f
in [self.lipid_coords]]
1543 self.
nptfiles += [
'%s.top' % os.path.splitext(f)[0]
for f
in [self.lipid_coords]]
1547 super(Lipid_GMX,self).
__init__(options,tgt_opts,forcefield)
1550 if not os.path.exists(os.path.join(self.root, self.tgtdir, i)):
1551 logger.error(
'Please provide %s; it is needed to proceed.\n' % i)
1556 if self.save_traj > 0:
1559 self.
LfDict = OrderedDict()
1563 """ Submit a NPT simulation to the Work Queue. """ 1564 if "n_ic" in self.RefData:
1566 phase_reorder = list(zip(*self.PhasePoints))
1567 t_index = [i
for i, x
in enumerate(phase_reorder[0])
if x == temperature]
1568 p_index = [i
for i, x
in enumerate(phase_reorder[1])
if x == pressure]
1569 p_u = phase_reorder[-1][list(set(t_index) & set(p_index))[0]]
1570 PT_vals = (temperature, pressure, p_u)
1572 if not PT_vals
in self.lipid_mols_new:
1573 self.lipid_mols_new[PT_vals] = [os.path.join(os.getcwd(),
'lipid-md.gro')]
1575 self.lipid_mols_new[PT_vals].append(os.path.join(os.getcwd(),
'lipid-md.gro'))
1577 super(Lipid_GMX, self).
npt_simulation(temperature, pressure, simnum)
1579 if len(self.lipid_mols_new[PT_vals]) == int(self.RefData[
'n_ic'][PT_vals]):
1580 self.lipid_mols[PT_vals] = self.lipid_mols_new[PT_vals]
1581 self.lipid_mols_new.pop(PT_vals)
1583 if self.goodstep
and (temperature, pressure)
in self.
LfDict_New:
1584 self.
LfDict[(temperature, pressure)] = self.
LfDict_New[(temperature, pressure)]
1585 if (temperature, pressure)
in self.
LfDict:
1586 lfsrc = self.
LfDict[(temperature, pressure)]
1587 lfdest = os.path.join(os.getcwd(),
'lipid.gro')
1588 logger.info(
"Copying previous iteration final geometry .gro file: %s to %s\n" % (lfsrc, lfdest))
1589 shutil.copy2(lfsrc,lfdest)
1591 self.
LfDict_New[(temperature, pressure)] = os.path.join(os.getcwd(),
'lipid-md.gro')
1596 """ Subclass of AbInitio for force and energy matching using GROMACS. """ 1597 def __init__(self,options,tgt_opts,forcefield):
1599 self.set_option(tgt_opts,
'coords',default=
"all.gro")
1600 self.set_option(tgt_opts,
'gmx_top',default=
"topol.top")
1601 self.set_option(tgt_opts,
'gmx_mdp',default=
"shot.mdp")
1604 super(AbInitio_GMX,self).
__init__(options,tgt_opts,forcefield)
1607 """ Binding energy matching using Gromacs. """ 1608 def __init__(self,options,tgt_opts,forcefield):
1611 super(BindingEnergy_GMX,self).
__init__(options,tgt_opts,forcefield)
1614 """ Interaction energy matching using GROMACS. """ 1615 def __init__(self,options,tgt_opts,forcefield):
1617 self.set_option(tgt_opts,
'coords',default=
"all.gro")
1618 self.set_option(tgt_opts,
'gmx_top',default=
"topol.top")
1619 self.set_option(tgt_opts,
'gmx_mdp',default=
"shot.mdp")
1622 super(Interaction_GMX,self).
__init__(options,tgt_opts,forcefield)
1625 """ Multipole moment matching using GROMACS. """ 1628 self.set_option(tgt_opts,
'coords',default=
"conf.gro")
1629 self.set_option(tgt_opts,
'gmx_top',default=
"topol.top")
1630 self.set_option(tgt_opts,
'gmx_mdp',default=
"shot.mdp")
1633 super(Moments_GMX,self).
__init__(options,tgt_opts,forcefield)
1636 """ Vibrational frequency matching using GROMACS. """ 1637 def __init__(self,options,tgt_opts,forcefield):
1639 self.set_option(tgt_opts,
'coords',default=
"conf.gro")
1642 super(Vibration_GMX,self).
__init__(options,tgt_opts,forcefield)
1645 """ Thermodynamical property matching using GROMACS. """ 1648 self.set_option(options,
'gmxpath')
1650 self.set_option(options,
'gmxsuffix')
1653 self.engname =
"gromacs" 1655 self.mdpfx =
"bash gmxprefix.bash" 1657 self.scripts = [
'gmxprefix.bash',
'md_chain.py']
1659 super(Thermo_GMX,self).
__init__(options,tgt_opts,forcefield)
def edit_mdp(fin=None, fout=None, options={}, defaults={}, verbose=False)
Read, create or edit a Gromacs MDP file.
def md(self, nsteps=0, nequil=0, verbose=False, deffnm=None, kwargs)
Method for running a molecular dynamics simulation.
Q-Chem input file parser.
atomtype_to_mass
A dictionary of atomic masses.
def npt_simulation(self, temperature, pressure, simnum)
Submit a NPT simulation to the Work Queue.
def energy_force_one(self, shot)
Compute the energy and force using GROMACS for a single snapshot; interfaces with AbInitio target...
atomtypes
Listing of all atom types in the file, (probably unnecessary)
Subclass of AbInitio for force and energy matching using GROMACS.
def energy_rmsd(self, shot, optimize=True)
Calculate energy of the selected structure (optionally minimize and return the minimized energy and R...
Vibrational mode fitting module.
def __init__(self, options, tgt_opts, forcefield)
def evaluate_(self, force=False, dipole=False, traj=None)
Utility function for computing energy, and (optionally) forces and dipoles using GROMACS.
def feed(self, line)
Given a line, determine the interaction type and the atoms involved (the suffix). ...
Nifty functions, intended to be imported by any module within ForceBalance.
Ab-initio fitting module (energies, forces, resp).
def parse_atomtype_line(line)
Parses the 'atomtype' line.
Multipole moment matching using GROMACS.
sec
The current section that we're in.
def __init__(self, name="gmx", kwargs)
def optimize(self, shot, crit=1e-4, align=True, kwargs)
Optimize the geometry and align the optimized geometry to the starting geometry.
Thermodynamical property matching using GROMACS.
def LinkFile(src, dest, nosrcok=False)
def evaluate_trajectory(self, force=False, dipole=False, traj=None)
Evaluate variables (energies, force and/or dipole) using GROMACS over a trajectory.
def setopts(self, kwargs)
Called by init ; Set GROMACS-specific options.
def energy_force(self, force=True, traj=None)
Compute the energy and force using GROMACS over a trajectory.
def energy(self, traj=None)
Compute the energy using GROMACS over a trajectory.
def npt_simulation(self, temperature, pressure, simnum)
Submit a NPT simulation to the Work Queue.
def callgmx(self, command, stdin=None, print_to_screen=False, print_command=False, kwargs)
Call GROMACS; prepend the gmxpath to the call to the GROMACS program.
def isint(word)
ONLY matches integers! If you have a decimal point? None shall pass!
def scd_persnap(self, ndx, timestep, final_frame)
def warngmx(self, command, warnings=[], maxwarn=1, kwargs)
Call gromacs and allow for certain expected warnings.
def __init__(self, options, tgt_opts, forcefield)
engine_
Default file names for coordinates, top and mdp files.
gmxsuffix
Disable some optimizations.
def multipole_moments(self, shot=0, optimize=True, polarizability=False)
Return the multipole moments of the 1st snapshot in Debye and Buckingham units.
Binding energy fitting module.
def n_snaps(self, nsteps, step_interval, timestep)
def write_ndx(fout, grps, fin=None)
Create or edit a Gromacs ndx file.
Interaction energy matching using GROMACS.
gmxversion
Figure out the GROMACS version - it will determine how programs are called.
mol
The current molecule (set by the moleculetype keyword)
pdict
The parameter dictionary (defined in this file)
engine_
Default file names for coordinates, top and mdp files.
def __init__(self, options, tgt_opts, forcefield)
have_constraints
Link files into the temp directory.
Binding energy matching using Gromacs.
def make_gro_trajectory(self, fout=None)
Return the MD trajectory as a Molecule object.
def generate_positions(self)
def energy_one(self, shot)
Compute the energy using GROMACS for a snapshot.
Derived from Engine object for carrying out general purpose GROMACS calculations. ...
def warn_press_key(warning, timeout=10)
def printcool_dictionary(Dict, title="Dictionary Keys : Values", bold=False, color=2, keywidth=25, topwidth=50, center=True, leftpad=0)
See documentation for printcool; this is a nice way to print out keys/values in a dictionary...
def readsrc(self, kwargs)
Called by init ; read files from the source directory.
def __init__(self, options, tgt_opts, forcefield)
Finite state machine for parsing GROMACS force field files.
def prepare(self, pbc=False, kwargs)
Called by init ; prepare the temp directory and figure out the topology.
Interaction energy fitting module.
def n_nonwater(self, structure_file)
def onefile(fnm=None, ext=None, err=False)
def __init__(self, options, tgt_opts, forcefield)
double
Write out the trajectory coordinates to a .gro file.
def evaluate_snapshot(self, shot, force=False, dipole=False)
Evaluate variables (energies, force and/or dipole) using GROMACS for a single snapshot.
def interaction_energy(self, fraga, fragb)
Computes the interaction energy between two fragments over a trajectory.
def warn_once(warning, warnhash=None)
Prints a warning but will only do so once in a given run.
Matching of lipid bulk properties.
def __init__(self, options, tgt_opts, forcefield)
top
Attempt to determine file names of .gro, .top, and .mdp files.
gmxpath
The directory containing GROMACS executables (e.g.
Multipole moment fitting module.
def molecular_dynamics(self, nsteps, timestep, temperature=None, pressure=None, nequil=0, nsave=0, minimize=True, threads=None, verbose=False, bilayer=False, kwargs)
Method for running a molecular dynamics simulation.
def wopen(dest, binary=False)
If trying to write to a symbolic link, remove it first.
def energy_termnames(self, edrfile=None)
Get a list of energy term names from the .edr file by parsing a system call to g_energy.
gmx_eq_barostat
Barostat keyword for equilibration.
Matching of liquid bulk properties.
Vibrational frequency matching using GROMACS.
def grouper(n, iterable)
Groups a big long iterable into groups of ten or what have you.
def energy_dipole(self, traj=None)
def calc_scd(self, n_snap, timestep)
atomnames
Listing of all atom names in the file, (probably unnecessary)
def normal_modes(self, shot=0, optimize=True)