1 """ @package forcebalance.tinkerio TINKER input/output. 3 This serves as a good template for writing future force matching I/O 4 modules for other programs because it's so simple. 9 from __future__
import division
10 from __future__
import print_function
12 from builtins
import str
13 from builtins
import zip
14 from builtins
import range
16 from re
import match, sub
22 from copy
import deepcopy
23 from forcebalance
import BaseReader
24 from subprocess
import Popen, PIPE
25 from forcebalance.engine
import Engine
34 from collections
import OrderedDict
38 allp = [
'atom',
'vdw',
'vdw14',
'vdwpr',
'hbond',
'bond',
'bond5',
'bond4',
39 'bond3',
'electneg',
'angle',
'angle5',
'angle4',
'angle3',
'anglef',
40 'strbnd',
'ureybrad',
'angang',
'opbend',
'opdist',
'improper',
'imptors',
41 'torsion',
'torsion5',
'torsion4',
'pitors',
'strtors',
'tortors',
'charge',
42 'dipole',
'dipole5',
'dipole4',
'dipole3',
'multipole',
'polarize',
'piatom',
43 'pibond',
'pibond5',
'pibond4',
'metal',
'biotype',
'mmffvdw',
'mmffbond',
44 'mmffbonder',
'mmffangle',
'mmffstrbnd',
'mmffopbend',
'mmfftorsion',
'mmffbci',
45 'mmffpbci',
'mmffequiv',
'mmffdefstbn',
'mmffcovrad',
'mmffprop',
'mmffarom']
48 eckeys = [
'Angle-Angle',
'Angle Bending',
'Atomic Multipoles',
'Bond Stretching',
'Charge-Charge',
49 'Charge-Dipole',
'Dipole-Dipole',
'Extra Energy Terms',
'Geometric Restraints',
'Implicit Solvation',
50 'Improper Dihedral',
'Improper Torsion',
'Metal Ligand Field',
'Out-of-Plane Bend',
'Out-of-Plane Distance',
51 'Pi-Orbital Torsion',
'Polarization',
'Reaction Field',
'Stretch-Bend',
'Stretch-Torsion',
52 'Torsional Angle',
'Torsion-Torsion',
'Urey-Bradley',
'Van der Waals']
54 from forcebalance.output
import getLogger
55 logger = getLogger(__name__)
57 pdict = {
'VDW' : {
'Atom':[1], 2:
'S',3:
'T',4:
'D'},
58 'BOND' : {
'Atom':[1,2], 3:
'K',4:
'B'},
59 'ANGLE' : {
'Atom':[1,2,3], 4:
'K',5:
'B'},
60 'STRBND' : {
'Atom':[1,2,3], 4:
'K1',5:
'K2'},
61 'OPBEND' : {
'Atom':[1,2,3,4], 5:
'K'},
62 'UREYBRAD' : {
'Atom':[1,2,3], 4:
'K',5:
'B'},
63 'TORSION' : ({
'Atom':[1,2,3,4], 5:
'1K', 6:
'1B',
64 8:
'2K', 9:
'2B', 11:
'3K', 12:
'3B'}),
65 'PITORS' : {
'Atom':[1,2], 3:
'K'},
66 'CHARGE' : {
'Atom':[1], 2:
''},
68 'MCHARGE' : {
'Atom':[1,2,3], 4:
''},
69 'DIPOLE' : {0:
'X',1:
'Y',2:
'Z'},
71 'QUADY' : {0:
'X',1:
'Y'},
72 'QUADZ' : {0:
'X',1:
'Y',2:
'Z'},
73 'POLARIZE' : {
'Atom':[1], 2:
'A',3:
'T'},
74 'BOND-CUBIC' : {
'Atom':[], 0:
''},
75 'BOND-QUARTIC' : {
'Atom':[], 0:
''},
76 'ANGLE-CUBIC' : {
'Atom':[], 0:
''},
77 'ANGLE-QUARTIC': {
'Atom':[], 0:
''},
78 'ANGLE-PENTIC' : {
'Atom':[], 0:
''},
79 'ANGLE-SEXTIC' : {
'Atom':[], 0:
''},
80 'DIELECTRIC' : {
'Atom':[], 0:
''},
81 'POLAR-SOR' : {
'Atom':[], 0:
''}
87 """Finite state machine for parsing TINKER force field files. 89 This class is instantiated when we begin to read in a file. The 90 feed(line) method updates the state of the machine, informing it 91 of the current interaction type. Using this information we can 92 look up the interaction type and parameter type for building the 98 super(Tinker_Reader,self).
__init__(fnm)
105 """ Given a line, determine the interaction type and the atoms involved (the suffix). 107 TINKER generally has stuff like this: 111 bond-quartic 3.793125 114 vdw 2 2.6550 0.0135 0.910 # PRM 4 116 multipole 2 1 2 0.25983 117 -0.03859 0.00000 -0.05818 120 -0.00203 0.00000 0.14412 123 The '#PRM 4' has no effect on TINKER but it indicates that we 124 are tuning the fourth field on the line (the 0.910 value). 126 @todo Put the rescaling factors for TINKER parameters in here. 127 Currently we're using the initial value to determine the 128 rescaling factor which is not very good. 130 Every parameter line is prefaced by the interaction type 131 except for 'multipole' which is on multiple lines. Because 132 the lines that come after 'multipole' are predictable, we just 133 determine the current line using the previous line. 135 Random note: Unit of force is kcal / mole / angstrom squared. 141 if len(s) == 0
or match(
'^#',line):
return None,
None 144 if s[0].upper()
in pdict:
145 self.
itype = s[0].upper()
148 elif s[0].upper() ==
'MULTIPOLE':
149 self.
itype =
'MCHARGE' 150 elif self.
itype ==
'MCHARGE':
151 self.
itype =
'DIPOLE' 152 elif self.
itype ==
'DIPOLE':
154 elif self.
itype ==
'QUADX':
156 elif self.
itype ==
'QUADY':
161 if self.
itype in pdict:
162 if 'Atom' in pdict[self.
itype]:
164 self.
atom = [s[i]
for i
in pdict[self.
itype][
'Atom']]
169 def write_key(fout, options, fin=None, defaults={}, verbose=False, prmfnm=None, chk=[]):
171 Create or edit a TINKER .key file. 172 @param[in] fout Output file name, can be the same as input file name. 173 @param[in] options Dictionary containing .key options. Existing options are replaced, new options are added at the end. 174 Passing None causes options to be deleted. To pass an option without an argument, use ''. 175 @param[in] fin Input file name. 176 @param[in] defaults Default options to add to the mdp only if they don't already exist. 177 @param[in] verbose Print out all modifications to the file. 178 @param[in] prmfnm TINKER parameter file name. 179 @param[in] chk Crash if the key file does NOT have these options by the end. 182 options = OrderedDict([(key.lower(), str(val)
if val
is not None else None)
for key, val
in options.items()])
183 if 'parameters' in options
and prmfnm
is not None:
184 logger.error(
"Please pass prmfnm or 'parameters':'filename.prm' in options but not both.\n")
186 elif 'parameters' in options:
187 prmfnm = options[
'parameters']
189 if prmfnm
is not None:
192 if prms[0].endswith(
'.prm'):
193 prms.append(prmfnm[:-4])
205 if fin
is not None and os.path.isfile(fin):
206 for line0
in open(fin).readlines():
207 line1 = line0.replace(
'\n',
'').expandtabs()
208 line = line0.strip().expandtabs()
216 if len(line) == 0
or set(line).issubset([
' ']):
220 if line.startswith(
"#"):
224 if s[0].lower()
in allp:
227 if s[0].lower() ==
"multipole":
231 s = line.split(
'#',1)
233 comms = s[1]
if len(s) > 1
else None 235 ds = data.split(
' ',1)
237 valf = ds[1]
if len(ds) > 1
else '' 238 key = keyf.strip().lower()
240 if key ==
'parameters':
246 if prmfnm
is None or val0
in prms
or val0[:-4]
in prms:
250 logger.info(line +
'\n')
251 warn_press_key(
"The above line was found in %s, but we expected something like 'parameters %s'; replacing." % (line,prmfnm))
252 options[
'parameters'] = prmfnm
257 if key
in clashes
and val != val0:
258 logger.error(
"write_key tried to set %s = %s but its original value was %s = %s\n" % (key, val, key, val0))
263 if len(val) < len(valf):
264 valf =
' ' + val +
' '*(len(valf) - len(val)-1)
266 valf =
' ' + val +
' ' 267 lout = [keyf,
' ', valf]
268 if comms
is not None:
270 out.append(
''.join(lout))
274 for key, val
in options.items():
276 if val
is None:
continue 277 if key
not in haveopts:
279 out.append(
"%-20s %s" % (key, val))
281 for key, val
in defaults.items():
284 if key
not in haveopts:
286 out.append(
"%-20s %s" % (key, val))
288 if not prmflag
and prmfnm
is not None:
289 out.insert(0,
"parameters %s" % prmfnm)
290 options[
"parameters"] = prmfnm
292 if not os.path.exists(
'%s.prm' % os.path.splitext(fout)[0]):
293 logger.error(
'No parameter file detected, this will cause TINKER to crash\n')
296 if i
not in haveopts:
297 logger.error(
'%s is expected to be in the .key file, but not found\n' % i)
300 file_out =
wopen(fout)
302 print(line, file=file_out)
309 """ Engine for carrying out general purpose TINKER calculations. """ 311 def __init__(self, name="tinker", **kwargs):
313 self.
valkwd = [
'tinker_key',
'tinkerpath',
'tinker_prm']
315 super(TINKER,self).
__init__(name=name, **kwargs)
319 """ Called by __init__ ; Set TINKER-specific options. """ 322 if 'tinkerpath' in kwargs:
324 if not os.path.exists(os.path.join(self.
tinkerpath,
"dynamic")):
325 warn_press_key(
"The 'dynamic' executable indicated by %s doesn't exist! (Check tinkerpath)" \
328 warn_once(
"The 'tinkerpath' option was not specified; using default.")
330 warn_press_key(
"Please add TINKER executables to the PATH or specify tinkerpath.")
335 """ Called by __init__ ; read files from the source directory. """ 337 self.
key =
onefile(kwargs.get(
'tinker_key'),
'key')
338 self.
prm =
onefile(kwargs.get(
'tinker_prm'),
'prm')
340 self.
mol = kwargs[
'mol']
342 crdfile =
onefile(kwargs.get(
'coords'),
'arc', err=
True)
343 self.
mol = Molecule(crdfile)
345 def calltinker(self, command, stdin=None, print_to_screen=False, print_command=False, **kwargs):
347 """ Call TINKER; prepend the tinkerpath to calling the TINKER program. """ 349 csplit = command.split()
351 if "%s.key" % self.name
in csplit
and not os.path.exists(
"%s.key" % self.name):
353 prog = os.path.join(self.
tinkerpath, csplit[0])
355 o = _exec(
' '.join(csplit), stdin=stdin, print_to_screen=print_to_screen, print_command=print_command, rbytes=1024, **kwargs)
358 if "Version" in line:
360 if len(vw.split(
'.')) <= 2:
363 vn = float(vw.split(
'.')[:2])
368 warn_press_key(
"ForceBalance requires TINKER %.1f - unexpected behavior with older versions!" % vn_need)
371 logger.error(
"Unable to determine TINKER version number!\n")
375 if "TINKER is Unable to Continue" in line:
377 logger.error(
"%s\n" % l)
379 logger.error(
"TINKER may have crashed! (See above output)\nThe command was: %s\nThe directory was: %s\n" % (
' '.join(csplit), os.getcwd()))
384 logger.info(line+
'\n')
385 warn_press_key(
"TINKER returned a very large floating point number! (See above line; will give error on parse)")
388 def prepare(self, pbc=False, **kwargs):
390 """ Called by __init__ ; prepare the temp directory and figure out the topology. """ 393 o = self.
calltinker(
"dynamic", persist=1, print_error=
False)
399 tk_opts = OrderedDict([(
"digits",
"10"), (
"archive",
"")])
400 tk_defs = OrderedDict()
404 if hasattr(self,
'FF'):
405 if not os.path.exists(self.FF.tinkerprm):
409 self.FF.make(np.zeros(self.FF.np))
410 if self.FF.rigid_water:
411 tk_opts[
"rattle"] =
"water" 413 if self.FF.amoeba_pol ==
'mutual':
414 tk_opts[
'polarization'] =
'mutual' 415 if self.FF.amoeba_eps
is not None:
416 tk_opts[
'polar-eps'] = str(self.FF.amoeba_eps)
418 tk_defs[
'polar-eps'] =
'1e-6' 419 elif self.FF.amoeba_pol ==
'direct':
420 tk_opts[
'polarization'] =
'direct' 422 warn_press_key(
"Using TINKER without explicitly specifying AMOEBA settings. Are you sure?")
423 self.
prm = self.FF.tinkerprm
424 prmfnm = self.FF.tinkerprm
434 for line
in open(os.path.join(self.srcdir, self.
key)).readlines():
436 if len(s) > 0
and s[0].lower() ==
'a-axis':
439 if len(s) > 0
and s[0].lower() ==
'b-axis' and float(s[1]) < minbox:
441 if len(s) > 0
and s[0].lower() ==
'c-axis' and float(s[1]) < minbox:
443 if keypbc
and (
not pbc):
444 warn_once(
"Deleting PBC options from the .key file.")
445 tk_opts[
'a-axis'] =
None 446 tk_opts[
'b-axis'] =
None 447 tk_opts[
'c-axis'] =
None 448 tk_opts[
'alpha'] =
None 449 tk_opts[
'beta'] =
None 450 tk_opts[
'gamma'] =
None 452 if 'boxes' in self.
mol.Data:
453 minbox = min([self.
mol.boxes[0].a, self.
mol.boxes[0].b, self.
mol.boxes[0].c])
454 if (
not keypbc)
and 'boxes' not in self.
mol.Data:
455 logger.error(
"Periodic boundary conditions require either (1) a-axis to be in the .key file or (b) boxes to be in the coordinate file.\n")
459 tk_opts[
'ewald'] =
'' 461 warn_press_key(
"Periodic box is set to less than 10 Angstroms across")
463 rpme = 0.5*(float(int(minbox - 1)))
if minbox <= 15
else 7.0
464 if 'nonbonded_cutoff' in kwargs:
465 rpme = kwargs[
'nonbonded_cutoff']
466 if rpme > 0.5*(float(int(minbox - 1))):
467 warn_press_key(
"nonbonded_cutoff = %.1f should be smaller than half the box size = %.1f Angstrom" % (rpme, minbox))
468 tk_defs[
'ewald-cutoff'] =
"%f" % rpme
470 rvdw = 0.5*(float(int(minbox - 1)))
if minbox <= 19
else 9.0
471 if 'nonbonded_cutoff' in kwargs
and 'vdw_cutoff' not in kwargs:
472 warn_press_key(
'AMOEBA detected and nonbonded_cutoff is set, but not vdw_cutoff (so it will be set equal to nonbonded_cutoff)')
473 rvdw = kwargs[
'nonbonded_cutoff']
474 if 'vdw_cutoff' in kwargs:
475 rvdw = kwargs[
'vdw_cutoff']
476 if rvdw > 0.5*(float(int(minbox - 1))):
477 warn_press_key(
"vdw_cutoff = %.1f should be smaller than half the box size = %.1f Angstrom" % (rvdw, minbox))
478 tk_defs[
'vdw-cutoff'] =
"%f" % rvdw
479 if (minbox*0.5 - rpme) > 2.5
and (minbox*0.5 - rvdw) > 2.5:
480 tk_defs[
'neighbor-list'] =
'' 481 elif (minbox*0.5 - rpme) > 2.5:
482 tk_defs[
'mpole-list'] =
'' 484 if 'nonbonded_cutoff' in kwargs
or 'vdw_cutoff' in kwargs:
485 warn_press_key(
'No periodic boundary conditions, your provided nonbonded_cutoff and vdw_cutoff will not be used')
486 tk_opts[
'ewald'] =
None 487 tk_opts[
'ewald-cutoff'] =
None 488 tk_opts[
'vdw-cutoff'] =
None 492 write_key(
"%s.key" % self.name, tk_opts, os.path.join(self.srcdir, self.
key)
if self.
key else None, tk_defs, verbose=
False, prmfnm=prmfnm)
493 self.
abskey = os.path.abspath(
"%s.key")
495 self.
mol[0].write(os.path.join(
"%s.xyz" % self.name), ftype=
"tinker")
498 self.
mol.require(
'tinkersuf')
501 o = self.
calltinker(
"analyze %s.xyz P,C" % (self.name), stdin=
"ALL")
507 ptype_dict = {
'atom':
'A',
'vsite':
'D'}
511 if len(s) == 0:
continue 513 if "Atom Type Definition Parameters" in line
or "Atom Definition Parameters" in line:
516 if isint(s[0]): mode = 2
519 G.add_node(int(s[0]))
524 self.
AtomLists[
'ParticleType'].append(
'D')
526 self.
AtomLists[
'ParticleType'].append(
'A')
530 if "List of 1-2 Connected Atomic Interactions" in line:
533 if isint(s[0]): mode = 4
541 if len(list(G.nodes())) > 0:
543 gs = [G.subgraph(c).copy()
for c
in nx.connected_components(G)]
546 tmols = [gs[i]
for i
in np.argsort(np.array([min(list(g.nodes()))
for g
in gs]))]
547 mnodes = [list(m.nodes())
for m
in tmols]
548 self.
AtomLists[
'MoleculeNumber'] = [[i+1
in m
for m
in mnodes].index(1)
for i
in range(self.
mol.na)]
550 grouped = [i.L()
for i
in self.
mol.molecules]
551 self.
AtomLists[
'MoleculeNumber'] = [[i
in g
for g
in grouped].index(1)
for i
in range(self.
mol.na)]
553 for f
in self.FF.fnms:
556 def optimize(self, shot, method="newton", align=True, crit=1e-4):
558 """ Optimize the geometry and align the optimized geometry to the starting geometry. """ 560 if os.path.exists(
'%s.xyz_2' % self.name):
561 os.unlink(
'%s.xyz_2' % self.name)
563 self.
mol[shot].write(
'%s.xyz' % self.name, ftype=
"tinker")
565 if method ==
"newton":
566 if self.
rigid: optprog =
"optrigid" 567 else: optprog =
"optimize" 568 elif method ==
"bfgs":
569 if self.
rigid: optprog =
"minrigid" 570 else: optprog =
"minimize" 572 raise RuntimeError(
"Incorrect choice of method for TINKER.optimize()")
575 o = self.
calltinker(
"%s %s.xyz %f" % (optprog, self.name, crit))
599 M12 = Molecule(
"%s.xyz" % self.name, ftype=
"tinker") + Molecule(
"%s.xyz_2" % self.name, ftype=
"tinker")
601 M12.align(center=
False)
602 M12[1].write(
"%s.xyz_2" % self.name, ftype=
"tinker")
605 rmsd = M12.ref_rmsd(0)[1]
610 if len(s) == 0:
continue 611 if "Optimally Conditioned Variable Metric Optimization" in line: mode = 1
612 if "Limited Memory BFGS Quasi-Newton Optimization" in line: mode = 1
613 if mode == 1
and isint(s[0]): mode = 2
615 if isint(s[0]): E = float(s[1])
617 if "Normal Termination" in line:
621 logger.info(str(line) +
'\n')
622 logger.info(
"The minimization did not converge in the geometry optimization - printout is above.\n")
623 return E, rmsd, M12[1]
625 def evaluate_(self, xyzin, force=False, dipole=False):
628 Utility function for computing energy, and (optionally) forces and dipoles using TINKER. 631 xyzin: TINKER .xyz file name. 632 force: Switch for calculating the force. 633 dipole: Switch for calculating the dipole. 636 Result: Dictionary containing energies, forces and/or dipoles. 639 Result = OrderedDict()
641 if dipole
or (
not force):
642 oanl = self.
calltinker(
"analyze %s -k %s" % (xyzin, self.name), stdin=
"G,E,M", print_to_screen=
False)
648 if 'Total Potential Energy : ' in line:
649 eanl.append(float(s[4]) * 4.184)
651 if 'Dipole X,Y,Z-Components :' in line:
652 dip.append([float(s[i])
for i
in range(-3,0)])
653 Result[
"Energy"] = np.array(eanl)
654 Result[
"Dipole"] = np.array(dip)
660 o = self.
calltinker(
"testgrad %s -k %s y n n" % (xyzin, self.name))
665 if "Total Potential Energy" in line:
666 E.append(float(s[4]) * 4.184)
667 if "Cartesian Gradient Breakdown over Individual Atoms" in line:
672 Fi += [-1 * float(j) * 4.184 * 10
for j
in s[2:5]]
674 if ReadFrc == 2
and len(s) < 6:
679 Result[
"Energy"] = np.array(E)
680 Result[
"Force"] = np.array(F)
684 logger.error(
'TINKER engine does not have get_charges (should be easy to implement however.)')
685 raise NotImplementedError
689 """ Computes the energy and force using TINKER for one snapshot. """ 691 self.
mol[shot].write(
"%s.xyz" % self.name, ftype=
"tinker")
692 Result = self.
evaluate_(
"%s.xyz" % self.name, force=
True)
693 return np.hstack((Result[
"Energy"].reshape(-1,1), Result[
"Force"]))
697 """ Computes the energy using TINKER over a trajectory. """ 699 if hasattr(self,
'md_trajectory') :
702 x =
"%s.xyz" % self.name
703 self.
mol.write(x, ftype=
"tinker")
708 """ Computes the energy and force using TINKER over a trajectory. """ 710 if hasattr(self,
'md_trajectory') :
713 x =
"%s.xyz" % self.name
714 self.
mol.write(x, ftype=
"tinker")
716 return np.hstack((Result[
"Energy"].reshape(-1,1), Result[
"Force"]))
720 """ Computes the energy and dipole using TINKER over a trajectory. """ 722 if hasattr(self,
'md_trajectory') :
725 x =
"%s.xyz" % self.name
726 self.
mol.write(x, ftype=
"tinker")
728 return np.hstack((Result[
"Energy"].reshape(-1,1), Result[
"Dipole"]))
736 warn_once(
"Asking for normal modes without geometry optimization?")
737 self.
mol[shot].write(
'%s.xyz' % self.name, ftype=
"tinker")
738 o = self.
calltinker(
"vibrate %s.xyz a" % (self.name))
746 if "Vibrational Normal Mode" in line:
749 calc_eigvals.append(freq)
750 calc_eigvecs.append([])
751 elif "Atom" in line
and "Delta X" in line:
754 calc_eigvecs[-1].append([float(i)
for i
in s[1:]])
755 calc_eigvals = np.array(calc_eigvals)
756 calc_eigvecs = np.array(calc_eigvecs)
758 calc_eigvecs = calc_eigvecs[np.argsort(np.abs(calc_eigvals))][6:]
759 calc_eigvals = calc_eigvals[np.argsort(np.abs(calc_eigvals))][6:]
761 calc_eigvecs = calc_eigvecs[np.argsort(calc_eigvals)]
762 calc_eigvals = calc_eigvals[np.argsort(calc_eigvals)]
763 os.system(
"rm -rf *.xyz_* *.[0-9][0-9][0-9]")
764 return calc_eigvals, calc_eigvecs
768 """ Return the multipole moments of the 1st snapshot in Debye and Buckingham units. """ 773 o = self.
calltinker(
"analyze %s.xyz_2 M" % (self.name))
775 self.
mol[shot].write(
'%s.xyz' % self.name, ftype=
"tinker")
776 o = self.
calltinker(
"analyze %s.xyz M" % (self.name))
782 if "Dipole X,Y,Z-Components" in line:
783 dipole_dict = OrderedDict(zip([
'x',
'y',
'z'], [float(i)
for i
in s[-3:]]))
784 elif "Quadrupole Moment Tensor" in line:
786 quadrupole_dict = OrderedDict([(
'xx',float(s[-3]))])
787 elif qn > 0
and ln == qn + 1:
788 quadrupole_dict[
'xy'] = float(s[-3])
789 quadrupole_dict[
'yy'] = float(s[-2])
790 elif qn > 0
and ln == qn + 2:
791 quadrupole_dict[
'xz'] = float(s[-3])
792 quadrupole_dict[
'yz'] = float(s[-2])
793 quadrupole_dict[
'zz'] = float(s[-1])
796 calc_moments = OrderedDict([(
'dipole', dipole_dict), (
'quadrupole', quadrupole_dict)])
800 o = self.
calltinker(
"polarize %s.xyz_2" % (self.name))
802 o = self.
calltinker(
"polarize %s.xyz" % (self.name))
806 polarizability_dict = OrderedDict()
809 if "Molecular Polarizability Tensor" in line:
811 elif pn > 0
and ln == pn + 2:
812 polarizability_dict[
'xx'] = float(s[-3])
813 polarizability_dict[
'yx'] = float(s[-2])
814 polarizability_dict[
'zx'] = float(s[-1])
815 elif pn > 0
and ln == pn + 3:
816 polarizability_dict[
'xy'] = float(s[-3])
817 polarizability_dict[
'yy'] = float(s[-2])
818 polarizability_dict[
'zy'] = float(s[-1])
819 elif pn > 0
and ln == pn + 4:
820 polarizability_dict[
'xz'] = float(s[-3])
821 polarizability_dict[
'yz'] = float(s[-2])
822 polarizability_dict[
'zz'] = float(s[-1])
824 calc_moments[
'polarizability'] = polarizability_dict
825 os.system(
"rm -rf *.xyz_* *.[0-9][0-9][0-9]")
830 """ Calculate energy of the selected structure (optionally minimize and return the minimized energy and RMSD). In kcal/mol. """ 837 o = self.
calltinker(
"analyze %s.xyz_2 E" % self.name)
851 os.system(
"rm %s.xyz_2" % self.name)
853 o = self.
calltinker(
"analyze %s.xyz E" % self.name)
857 if "Total Potential Energy" in line:
858 E = float(line.split()[-2].replace(
'D',
'e'))
860 logger.error(
"Total potential energy wasn't encountered when calling analyze!\n")
862 if optimize
and abs(E-E_) > 0.1:
863 warn_press_key(
"Energy from optimize and analyze aren't the same (%.3f vs. %.3f)" % (E, E_))
868 """ Calculate the interaction energy for two fragments. """ 870 self.
A =
TINKER(name=
"A", mol=self.
mol.atom_select(fraga), tinker_key=
"%s.key" % self.name, tinkerpath=self.
tinkerpath)
871 self.
B =
TINKER(name=
"B", mol=self.
mol.atom_select(fragb), tinker_key=
"%s.key" % self.name, tinkerpath=self.
tinkerpath)
876 def molecular_dynamics(self, nsteps, timestep, temperature=None, pressure=None, nequil=0, nsave=1000, minimize=True, anisotropic=False, threads=1, verbose=False, **kwargs):
879 Method for running a molecular dynamics simulation. 882 nsteps = (int) Number of total time steps 883 timestep = (float) Time step in FEMTOSECONDS 884 temperature = (float) Temperature control (Kelvin) 885 pressure = (float) Pressure control (atmospheres) 886 nequil = (int) Number of additional time steps at the beginning for equilibration 887 nsave = (int) Step interval for saving and printing data 888 minimize = (bool) Perform an energy minimization prior to dynamics 889 threads = (int) Specify how many OpenMP threads to use 891 Returns simulation data: 892 Rhos = (array) Density in kilogram m^-3 893 Potentials = (array) Potential energies 894 Kinetics = (array) Kinetic energies 895 Volumes = (array) Box volumes 896 Dips = (3xN array) Dipole moments 897 EComps = (dict) Energy components 900 md_defs = OrderedDict()
901 md_opts = OrderedDict()
903 md_opts[
"printout"] = nsave
904 md_opts[
"openmp-threads"] = threads
906 if temperature
is not None:
907 md_defs[
"integrator"] =
"stochastic" 909 md_defs[
"integrator"] =
"beeman" 910 md_opts[
"thermostat"] =
None 913 md_opts[
"vdw-correction"] =
'' 914 if temperature
is not None and pressure
is not None:
915 md_defs[
"integrator"] =
"beeman" 916 md_defs[
"thermostat"] =
"bussi" 917 md_defs[
"barostat"] =
"montecarlo" 919 md_opts[
"aniso-pressure"] =
'' 920 elif pressure
is not None:
921 warn_once(
"Pressure is ignored because temperature is turned off.")
923 if pressure
is not None:
924 warn_once(
"Pressure is ignored because pbc is set to False.")
928 md_opts[
"barostat"] =
None 930 eq_opts = deepcopy(md_opts)
931 if self.
pbc and temperature
is not None and pressure
is not None:
932 eq_opts[
"integrator"] =
"beeman" 933 eq_opts[
"thermostat"] =
"bussi" 934 eq_opts[
"barostat"] =
"berendsen" 937 if verbose: logger.info(
"Minimizing the energy...")
938 self.
optimize(0, method=
"bfgs", crit=1)
939 os.system(
"mv %s.xyz_2 %s.xyz" % (self.name, self.name))
940 if verbose: logger.info(
"Done\n")
944 write_key(
"%s-eq.key" % self.name, eq_opts,
"%s.key" % self.name, md_defs)
945 if verbose:
printcool(
"Running equilibration dynamics", color=0)
946 if self.
pbc and pressure
is not None:
947 self.
calltinker(
"dynamic %s -k %s-eq %i %f %f 4 %f %f" % (self.name, self.name, nequil, timestep, float(nsave*timestep)/1000,
948 temperature, pressure), print_to_screen=verbose)
950 self.
calltinker(
"dynamic %s -k %s-eq %i %f %f 2 %f" % (self.name, self.name, nequil, timestep, float(nsave*timestep)/1000,
951 temperature), print_to_screen=verbose)
952 os.system(
"rm -f %s.arc" % (self.name))
955 if verbose:
printcool(
"Running production dynamics", color=0)
956 write_key(
"%s-md.key" % self.name, md_opts,
"%s.key" % self.name, md_defs)
957 if self.
pbc and pressure
is not None:
958 odyn = self.
calltinker(
"dynamic %s -k %s-md %i %f %f 4 %f %f" % (self.name, self.name, nsteps, timestep, float(nsave*timestep/1000),
959 temperature, pressure), print_to_screen=verbose)
961 odyn = self.
calltinker(
"dynamic %s -k %s-md %i %f %f 2 %f" % (self.name, self.name, nsteps, timestep, float(nsave*timestep/1000),
962 temperature), print_to_screen=verbose)
965 os.system(
"mv %s.arc %s-md.arc" % (self.name, self.name))
972 if 'Current Potential' in line:
973 edyn.append(float(s[2]))
974 if 'Current Kinetic' in line:
975 kdyn.append(float(s[2]))
976 if len(s) > 0
and s[0] ==
'Temperature' and s[2] ==
'Kelvin':
977 temps.append(float(s[1]))
980 edyn = np.array(edyn) * 4.184
981 kdyn = np.array(kdyn) * 4.184
982 temps = np.array(temps)
984 if verbose: logger.info(
"Post-processing to get the dipole moments\n")
985 oanl = self.
calltinker(
"analyze %s-md.arc" % self.name, stdin=
"G,E,M", print_to_screen=
False)
991 ecomp = OrderedDict()
994 for ln, line
in enumerate(oanl):
997 if 'Total System Mass' in line:
999 if 'Total Potential Energy : ' in line:
1000 eanl.append(float(s[4]))
1001 if 'Dipole X,Y,Z-Components :' in line:
1002 dip.append([float(s[i])
for i
in range(-3,0)])
1005 if strip.startswith(key):
1007 ecomp[key].append(float(s[-2])*4.184)
1009 ecomp[key] = [float(s[-2])*4.184]
1014 for key
in havekeys:
1015 if strip.startswith(key):
1017 ecomp[key].append(float(s[-2])*4.184)
1019 ecomp[key] = [float(s[-2])*4.184]
1021 ecomp[key] = np.array(ecomp[key])
1022 ecomp[
"Potential Energy"] = edyn
1023 ecomp[
"Kinetic Energy"] = kdyn
1024 ecomp[
"Temperature"] = temps
1025 ecomp[
"Total Energy"] = edyn+kdyn
1028 eanl = np.array(eanl) * 4.184
1035 conv = 1.6605387831627252
1038 for line
in open(
"%s-md.arc" % self.name).readlines() \
1039 if (len(line.split()) == 6
and isfloat(line.split()[1]) \
1040 and all([
isfloat(i)
for i
in line.split()[:6]]))]) / 1000
1041 rho = conv * mass / vol
1045 prop_return = OrderedDict()
1046 prop_return.update({
'Rhos': rho,
'Potentials': edyn,
'Kinetics': kdyn,
'Volumes': vol,
'Dips': dip,
'Ecomps': ecomp})
1050 """ Condensed phase property matching using TINKER. """ 1051 def __init__(self,options,tgt_opts,forcefield):
1053 self.set_option(tgt_opts,
'md_threads')
1055 self.set_option(options,
'tinkerpath')
1057 self.set_option(tgt_opts,
'liquid_coords',default=
'liquid.xyz',forceprint=
True)
1059 self.set_option(tgt_opts,
'gas_coords',default=
'gas.xyz',forceprint=
True)
1061 self.engine_ = TINKER
1063 self.engname =
"tinker" 1067 self.nptfiles = [
'%s.key' % os.path.splitext(f)[0]
for f
in [self.liquid_coords, self.gas_coords]]
1069 self.gas_engine_args = {
'tinker_key' :
'%s.key' % os.path.splitext(self.gas_coords)[0]}
1073 super(Liquid_TINKER,self).
__init__(options,tgt_opts,forcefield)
1076 if not os.path.exists(os.path.join(self.root, self.tgtdir, i)):
1077 logger.error(
'Please provide %s; it is needed to proceed.\n' % i)
1081 if self.save_traj > 0:
1087 self.post_init(options)
1090 """ Submit a NPT simulation to the Work Queue. """ 1093 if (temperature, pressure)
in self.
DynDict:
1094 dynsrc = self.
DynDict[(temperature, pressure)]
1095 dyndest = os.path.join(os.getcwd(),
'liquid.dyn')
1096 logger.info(
"Copying .dyn file: %s to %s\n" % (dynsrc, dyndest))
1097 shutil.copy2(dynsrc,dyndest)
1099 self.
DynDict_New[(temperature, pressure)] = os.path.join(os.getcwd(),
'liquid.dyn')
1104 """ Subclass of Target for force and energy matching using TINKER. """ 1107 self.set_option(tgt_opts,
'coords',default=
"all.arc")
1108 self.set_option(tgt_opts,
'tinker_key',default=
"shot.key")
1111 super(AbInitio_TINKER,self).
__init__(options,tgt_opts,forcefield)
1114 """ Binding energy matching using TINKER. """ 1115 def __init__(self,options,tgt_opts,forcefield):
1118 super(BindingEnergy_TINKER,self).
__init__(options,tgt_opts,forcefield)
1121 """ Subclass of Target for interaction matching using TINKER. """ 1124 self.set_option(tgt_opts,
'coords',default=
"all.arc")
1125 self.set_option(tgt_opts,
'tinker_key',default=
"shot.key")
1128 super(Interaction_TINKER,self).
__init__(options,tgt_opts,forcefield)
1131 """ Subclass of Target for multipole moment matching using TINKER. """ 1132 def __init__(self,options,tgt_opts,forcefield):
1134 self.set_option(tgt_opts,
'coords',default=
"input.xyz")
1135 self.set_option(tgt_opts,
'tinker_key',default=
"input.key")
1138 super(Moments_TINKER,self).
__init__(options,tgt_opts,forcefield)
1141 """ Vibrational frequency matching using TINKER. """ 1142 def __init__(self,options,tgt_opts,forcefield):
1144 self.set_option(tgt_opts,
'coords',default=
"input.xyz")
1145 self.set_option(tgt_opts,
'tinker_key',default=
"input.key")
1148 super(Vibration_TINKER,self).
__init__(options,tgt_opts,forcefield)
def energy_rmsd(self, shot=0, optimize=True)
Calculate energy of the selected structure (optionally minimize and return the minimized energy and R...
Engine for carrying out general purpose TINKER calculations.
def readsrc(self, kwargs)
Called by init ; read files from the source directory.
def energy_dipole(self)
Computes the energy and dipole using TINKER over a trajectory.
Vibrational mode fitting module.
Nifty functions, intended to be imported by any module within ForceBalance.
def BuildLatticeFromLengthsAngles(a, b, c, alpha, beta, gamma)
This function takes in three lattice lengths and three lattice angles, and tries to return a complete...
Ab-initio fitting module (energies, forces, resp).
def energy_force(self)
Computes the energy and force using TINKER over a trajectory.
def write_key(fout, options, fin=None, defaults={}, verbose=False, prmfnm=None, chk=[])
Create or edit a TINKER .key file.
def LinkFile(src, dest, nosrcok=False)
valkwd
Keyword args that aren't in this list are filtered out.
def molecular_dynamics(self, nsteps, timestep, temperature=None, pressure=None, nequil=0, nsave=1000, minimize=True, anisotropic=False, threads=1, verbose=False, kwargs)
Method for running a molecular dynamics simulation.
def isint(word)
ONLY matches integers! If you have a decimal point? None shall pass!
def optimize(self, shot, method="newton", align=True, crit=1e-4)
Optimize the geometry and align the optimized geometry to the starting geometry.
def __init__(self, options, tgt_opts, forcefield)
def npt_simulation(self, temperature, pressure, simnum)
Submit a NPT simulation to the Work Queue.
def energy_force_one(self, shot)
Computes the energy and force using TINKER for one snapshot.
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.
Subclass of Target for force and energy matching using TINKER.
Subclass of Target for interaction matching using TINKER.
tinkerpath
The directory containing TINKER executables (e.g.
def calltinker(self, command, stdin=None, print_to_screen=False, print_command=False, kwargs)
Call TINKER; prepend the tinkerpath to calling the TINKER program.
AtomMask
If the coordinates do not come with TINKER suffixes then throw an error.
atom
The atom numbers in the interaction (stored in the TINKER parser)
def energy(self)
Computes the energy using TINKER over a trajectory.
Vibrational frequency matching using TINKER.
engine_
Default file names for coordinates and key file.
def __init__(self, options, tgt_opts, forcefield)
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 __init__(self, options, tgt_opts, forcefield)
Subclass of Target for multipole moment matching using TINKER.
def printcool(text, sym="#", bold=False, color=2, ansi=None, bottom='-', minwidth=50, center=True, sym2="=")
Cool-looking printout for slick formatting of output.
def feed(self, line)
Given a line, determine the interaction type and the atoms involved (the suffix). ...
Interaction energy fitting module.
def onefile(fnm=None, ext=None, err=False)
rigid
Attempt to set some TINKER options.
Finite state machine for parsing TINKER force field files.
Binding energy matching using TINKER.
def prepare(self, pbc=False, kwargs)
Called by init ; prepare the temp directory and figure out the topology.
def warn_once(warning, warnhash=None)
Prints a warning but will only do so once in a given run.
pdict
The parameter dictionary (defined in this file)
def interaction_energy(self, fraga, fragb)
Calculate the interaction energy for two fragments.
engine_
Default file names for coordinates and key file.
def normal_modes(self, shot=0, optimize=True)
Multipole moment fitting module.
def evaluate_(self, xyzin, force=False, dipole=False)
Utility function for computing energy, and (optionally) forces and dipoles using TINKER.
def isfloat(word)
Matches ANY number; it can be a decimal, scientific notation, integer, or what have you...
def setopts(self, kwargs)
Called by init ; Set TINKER-specific options.
def __init__(self, name="tinker", kwargs)
def wopen(dest, binary=False)
If trying to write to a symbolic link, remove it first.
Condensed phase property matching using TINKER.
Matching of liquid bulk properties.
def __init__(self, options, tgt_opts, forcefield)