ForceBalance API  1.3
Automated optimization of force fields and empirical potentials
amberio.py
Go to the documentation of this file.
1 """ @package forcebalance.amberio AMBER force field input/output.
2 
3 This serves as a good template for writing future force matching I/O
4 modules for other programs because it's so simple.
5 
6 @author Lee-Ping Wang
7 @date 01/2012
8 """
9 from __future__ import division
10 from __future__ import print_function
11 
12 from builtins import str
13 from builtins import zip
14 from builtins import range
15 from builtins import object
16 import os, sys, re
17 import copy
18 from re import match, sub, split, findall
19 import networkx as nx
20 from forcebalance.nifty import isint, isfloat, _exec, LinkFile, warn_once, which, onefile, listfiles, warn_press_key, wopen, printcool, printcool_dictionary
21 import numpy as np
22 from forcebalance import BaseReader
23 from forcebalance.engine import Engine
24 from forcebalance.liquid import Liquid
25 from forcebalance.abinitio import AbInitio
26 from forcebalance.interaction import Interaction
27 from forcebalance.vibration import Vibration
28 from forcebalance.molecule import Molecule
29 from collections import OrderedDict, defaultdict, namedtuple
30 # Rudimentary NetCDF file usage
31 from scipy.io.netcdf import netcdf_file
32 try:
33  # Some functions require the Python API to sander "pysander"
34  import sander
35 except:
36  pass
37 
38 from forcebalance.output import getLogger
39 logger = getLogger(__name__)
40 
41 # Boltzmann's constant
42 kb_kcal = 0.0019872041
43 
44 mol2_pdict = {'COUL':{'Atom':[1], 8:''}}
45 
46 frcmod_pdict = {'BONDS': {'Atom':[0], 1:'K', 2:'B'},
47  'ANGLES':{'Atom':[0], 1:'K', 2:'B'},
48  'PDIHS1':{'Atom':[0], 2:'K', 3:'B'},
49  'PDIHS2':{'Atom':[0], 2:'K', 3:'B'},
50  'PDIHS3':{'Atom':[0], 2:'K', 3:'B'},
51  'PDIHS4':{'Atom':[0], 2:'K', 3:'B'},
52  'PDIHS5':{'Atom':[0], 2:'K', 3:'B'},
53  'PDIHS6':{'Atom':[0], 2:'K', 3:'B'},
54  'IDIHS' :{'Atom':[0], 1:'K', 2:'B'},
55  'VDW':{'Atom':[0], 1:'S', 2:'T'}
56  }
57 
58 def is_mol2_atom(line):
59  s = line.split()
60  if len(s) < 9:
61  return False
62  return all([isint(s[0]), isfloat(s[2]), isfloat(s[3]), isfloat(s[4]), isfloat(s[8])])
63 
64 def write_leap(fnm, mol2=[], frcmod=[], pdb=None, prefix='amber', spath = [], delcheck=False):
65  """ Parse and edit an AMBER LEaP input file. Output file is written to inputfile_ (with trailing underscore.) """
66  have_fmod = []
67  have_mol2 = []
68  # The lines that will be printed out to actually run tleap
69  line_out = []
70  aload = ['loadamberparams', 'source', 'loadoff']
71  aload_eq = ['loadmol2']
72  spath.append('.')
73  # Default name for the "unit" that is written to prmtop/inpcrd
74  ambername = 'amber'
75  for line in open(fnm):
76  # Skip comment lines
77  if line.strip().startswith('#') : continue
78  line = line.split('#')[0]
79  s = line.split()
80  ll = line.lower()
81  ls = line.lower().split()
82  # Check to see if all files being loaded are in the search path
83  if '=' in line:
84  if ll.split('=')[1].split()[0] in aload_eq:
85  if not any([os.path.exists(os.path.join(d, s[-1])) for d in spath]):
86  logger.error("The file in this line cannot be loaded : " + line.strip())
87  raise RuntimeError
88  elif len(ls) > 0 and ls[0] in aload:
89  if not any([os.path.exists(os.path.join(d, s[-1])) for d in spath]):
90  logger.error("The file in this line cannot be loaded : " + line.strip())
91  raise RuntimeError
92  if len(s) >= 2 and ls[0] == 'loadamberparams':
93  have_fmod.append(s[1])
94  if len(s) >= 2 and 'loadmol2' in ll:
95  # Adopt the AMBER molecule name from the loadpdb line.
96  ambername = line.split('=')[0].strip()
97  have_mol2.append(s[-1])
98  if len(s) >= 2 and 'loadpdb' in ll:
99  # Adopt the AMBER molecule name from the loadpdb line.
100  ambername = line.split('=')[0].strip()
101  # If we pass in our own PDB, then this line is replaced.
102  if pdb is not None:
103  line = '%s = loadpdb %s\n' % (ambername, pdb)
104  if len(s) >= 1 and ls[0] == 'check' and delcheck:
105  # Skip over check steps if so decreed
106  line = "# " + line
107  if 'saveamberparm' in ll:
108  # We'll write the saveamberparm line ourselves
109  continue
110  if len(s) >= 1 and ls[0] == 'quit':
111  # Don't write the quit line.
112  break
113  if not line.endswith('\n') : line += '\n'
114  line_out.append(line)
115  # Sanity checks: If frcmod and mol2 files are provided to this function,
116  # they should be in the leap.cmd file as well. There should be exactly
117  # one PDB file being loaded.
118  for i in frcmod:
119  if i not in have_fmod:
120  warn_press_key("WARNING: %s is not being loaded in %s" % (i, fnm))
121  for i in mol2:
122  if i not in have_mol2:
123  warn_press_key("WARNING: %s is not being loaded in %s" % (i, fnm))
124 
125  fout = fnm+'_'
126  line_out.append('saveamberparm %s %s.prmtop %s.inpcrd\n' % (ambername, prefix, prefix))
127  line_out.append('quit\n')
128  if os.path.exists(fout): os.remove(fout)
129  with wopen(fout) as f: print(''.join(line_out), file=f)
130 
131 def splitComment(mystr, debug=False):
132  """
133  Remove the comment from a line in an AMBER namelist. Had to write a separate
134  function because I couldn't get regex to work
135 
136  Parameters
137  ----------
138  mystr : str
139  Input string such as:
140  restraintmask='!:WAT,NA&!@H=', ! Restraint mask for non-water, non-ions
141 
142  Returns
143  -------
144  str
145  Output string with comment removed (but keeping leading and trailing whitespace) such as:
146  restraintmask='!:WAT,NA&!@H=',
147  """
148  inStr = False
149  commi = 0
150  headStr = False
151  for i in range(len(mystr)):
152  deactiv=False
153  if inStr:
154  if mystr[i] == '\'':
155  if i < (len(mystr)-1) and mystr[i+1] != '\'' and i > 0 and mystr[i-1] != '\'':
156  deactiv=True
157  if headStr and i > 0 and mystr[i-1] == '\'':
158  deactiv=True
159  headStr = False
160  elif mystr[i]=='\'':
161  # if i < (len(mystr)-1) and mystr[i+1] == '\'':
162  # raise IOError('A string expression should not start with double quotes')
163  inStr=True
164  headStr=True
165  if debug:
166  if inStr:
167  print("\x1b[91m%s\x1b[0m" % mystr[i],end="")
168  else:
169  print(mystr[i],end="")
170  if deactiv:
171  inStr=False
172  if not inStr:
173  if mystr[i] == '!':
174  commi = i
175  break
176  if debug: print()
177  if commi != 0:
178  return mystr[:i]
179  else:
180  return mystr
181 
182 def parse_amber_namelist(fin):
183  """
184  Parse a file containing an AMBER namelist
185  (only significantly tested for sander input).
186 
187  Parameters
188  ----------
189  fin : str
190  Name of file containing the namelist
191 
192  Returns
193  -------
194  comments (list) of lines)
195  List of lines containing comments before first namelist
196  names (list)
197  List of names of each namelist (e.g. cntrl, ewald)
198  block_dicts (list)
199  List of ordered dictionaries containing variable names and values for each namelist
200  suffixes (list)
201  List of list of lines coming after the "slash" for each suffix
202  """
203  # Are we in the leading comments?
204  in_comment = True
205  # Are we inside an input block?
206  in_block = False
207  fobj = open(fin)
208  lines = fobj.readlines()
209  fobj.close()
210  comments = []
211  suffixes = []
212  names = []
213  blocks = []
214  for i in range(len(lines)):
215  line = lines[i]
216  strip = line.strip()
217  # Does the line start with &?
218  if not in_block:
219  if strip.startswith('&'):
220  in_block = True
221  in_comment = False
222  names.append(strip[1:].lower())
223  block_lines = []
224  suffixes.append([])
225  continue
226  if in_comment:
227  comments.append(line.replace('\n',''))
228  else:
229  suffixes[-1].append(line.replace('\n',''))
230  else:
231  if strip in ['/','&end']:
232  in_block = False
233  blocks.append(block_lines[:])
234  elif strip.startswith('&'):
235  raise RuntimeError('Cannot start a namelist within a namelist')
236  else:
237  block_lines.append(line.replace('\n',''))
238 
239  block_dicts = []
240  for name, block in zip(names, blocks):
241  block_string = ' '.join([splitComment(line) for line in block])
242  # Matches the following:
243  # variable name (may include alphanumeric characters or underscore)
244  #
245  block_split = re.findall("[A-Za-z0-9_]+ *= *(?:\'[^']*\'|[+-]?[0-9]+\.?[0-9]*),", block_string)
246  #block_split = re.findall("[A-Za-z0-9_ ]+= *(?:(?:\'.*\')*[+-]?[0-9]+\.*[0-9]*,)+", block_string)
247  #block_split = re.findall("[A-Za-z0-9_ ]+= *(?:(?:\'.*\')*[^ ]*,)+", block_string)
248  # print(block_string)
249  # print(block_split)
250  block_dict = OrderedDict()
251  for word in block_split:
252  field1, field2 = word.split("=", 1)
253  key = field1.strip().lower()
254  val = re.sub(',$','',field2).strip()
255  block_dict[key] = val
256  block_dicts.append(block_dict)
257 
258  return comments, names, block_dicts, suffixes
259 
260 def write_mdin(calctype, fout=None, nsteps=None, timestep=None, nsave=None, pbc=False, temperature=None, pressure=None, mdin_orig=None):
261  """
262  Write an AMBER .mdin file to carry out a calculation using sander or pmemd.cuda.
263 
264  Parameters
265  ----------
266  calctype : str
267  The type of calculation being performed
268  'min' : minimization
269  'eq' : equilibration
270  'md' : (production) MD
271  'sp' : Single-point calculation
272 
273  fout : str
274  If provided, file name that the .mdin file should be written to.
275  Each variable within a namelist will occupy one line.
276  Comments within namelist are not written to output.
277 
278  timestep : float
279  Time step in picoseconds. For minimizations or
280  single-point calculations, this is not needed
281 
282  nsteps : int
283  How many MD or minimization steps to take
284  For single-point calculations, this is not needed
285 
286  nsave : int
287  How often to write trajectory and velocity frames
288  (only production MD writes velocities)
289  For single-point calculations, this is not needed
290 
291  pbc : bool
292  Whether to use periodic boundary conditions
293 
294  temperature : float
295  If not None, the simulation temperature
296 
297  pressure : float
298  If not None, the simulation pressure
299 
300  mdin_orig : str, optional
301  Custom mdin file provided by the user.
302  Non-&cntrl blocks will be written to output.
303  Top-of-file comments will be written to output.
304 
305  Returns
306  -------
307  OrderedDict
308  key : value pairs in the &cntrl namelist,
309  useful for passing to cpptraj or sander/cpptraj
310  Python APIs in the future.
311  """
312  if calctype not in ['min', 'eq', 'md', 'sp']:
313  raise RuntimeError("Invalid calctype")
314  if calctype in ['eq', 'md']:
315  if timestep is None:
316  raise RuntimeError("eq and md requires timestep")
317  if nsteps is None:
318  raise RuntimeError("eq and md requires nsteps")
319  if nsave is None:
320  raise RuntimeError("eq and md requires nsave")
321  if calctype == 'min':
322  # This value is never used but needed
323  # to prevent an error in string formatting
324  timestep = 0.0
325  if nsteps is None:
326  nsteps = 500
327  if nsave is None:
328  nsave = 10
329  if calctype == 'sp':
330  # This value is never used but needed
331  # to prevent an error in string formatting
332  nsteps = 0
333  timestep = 0.0
334  nsave = 0
335 
336  # cntrl_vars is an OrderedDict of namedtuples.
337  # Keys are variable names to be printed to mdin_orig.
338  # Values are namedtuples representing values, their properties are:
339  # 1) Name of the variable
340  # 2) Value of the variable, should be a dictionary with three keys 'min', 'eq', 'md'.
341  # When writing the variable for a run type, it will only be printed if the value exists in the dictionary.
342  # (e.g. 'maxcyc' should only be printed out for minimization jobs.)
343  # If the value looked up is equal to None, it will throw an error.
344  # 3) Comment to be printed out with the variable
345  # 4) Priority level of the variable
346  # 1: The variable will always be set in the ForceBalance code at runtime (The highest)
347  # 2: The variable is set by the user in the ForceBalance input file
348  # 3: User may provide variable in custom mdin_orig; if not provided, default value will be used
349  # 4: User may provide variable in custom mdin_orig; if not provided, it will not be printed
350  cntrl_vars = OrderedDict()
351  cntrl_var = namedtuple("cntrl_var", ["name", "value", "comment", "priority"])
352  cntrl_vars["imin"] = cntrl_var(name="imin", value={"min":"1","eq":"0","md":"0","sp":"5"}, comment="0 = MD; 1 = Minimize; 5 = Trajectory analysis", priority=1)
353  # Options pertaining to minimization
354  cntrl_vars["ntmin"] = cntrl_var(name="ntmin", value={"min":"2"}, comment="Minimization algorithm; 2 = Steepest descent", priority=3)
355  cntrl_vars["dx0"] = cntrl_var(name="dx0", value={"min":"0.1"}, comment="Minimizer step length", priority=3)
356  cntrl_vars["maxcyc"] = cntrl_var(name="maxcyc", value={"min":"%i" % nsteps, "sp":"1"}, comment="Number of minimization steps", priority=3)
357  # MD options - time step and number of steps
358  cntrl_vars["dt"] = cntrl_var(name="dt", value={"eq":"%.8f" % timestep, "md":"%.8f" % timestep}, comment="Time step (ps)", priority=2)
359  cntrl_vars["nstlim"] = cntrl_var(name="nstlim", value={"eq":"%i" % nsteps, "md":"%i" % nsteps}, comment="Number of MD steps", priority=2)
360  # ntpr, ntwx and ntwr for eq and md runs should be set by this function.
361  cntrl_vars["ntpr"] = cntrl_var(name="ntpr", value={"min":"%i" % nsave,"eq":"%i" % nsave,"md":"%i" % nsave}, comment="Interval for printing output", priority={"min":1,"eq":2,"md":2,"sp":1})
362  cntrl_vars["ntwx"] = cntrl_var(name="ntwx", value={"min":"%i" % nsave,"eq":"%i" % nsave,"md":"%i" % nsave}, comment="Interval for writing trajectory", priority={"min":1,"eq":2,"md":2,"sp":1})
363  cntrl_vars["ntwr"] = cntrl_var(name="ntwr", value={"min":"%i" % nsave,"eq":"%i" % nsteps,"md":"%i" % nsteps}, comment="Interval for writing restart", priority={"min":1,"eq":1,"md":1,"sp":1})
364  cntrl_vars["ntwv"] = cntrl_var(name="ntwv", value={"md":"-1"}, comment="Interval for writing velocities", priority={"min":1,"eq":1,"md":2,"sp":1})
365  cntrl_vars["ntwe"] = cntrl_var(name="ntwe", value={"md":"%i" % nsave}, comment="Interval for writing energies (disabled)", priority=1)
366  cntrl_vars["nscm"] = cntrl_var(name="nscm", value={"eq":"1000","md":"1000"}, comment="Interval for removing COM translation/rotation", priority=3)
367  # Insist on NetCDF trajectories for ntxo, ioutfm
368  cntrl_vars["ntxo"] = cntrl_var(name="ntxo", value={"min":"2","eq":"2","md":"2"}, comment="Restart output format; 1 = ASCII, 2 = NetCDF", priority=1)
369  cntrl_vars["ioutfm"] = cntrl_var(name="ioutfm", value={"min":"1","eq":"1","md":"1"}, comment="Trajectory format; 0 = ASCII, 1 = NetCDF", priority=1)
370  # min and eq read coors only; md is a full restart
371  cntrl_vars["ntx"] = cntrl_var(name="ntx", value={"min":"1","eq":"1","md":"5"}, comment="1 = Read coors only; 5 = Full restart", priority=1)
372  cntrl_vars["irest"] = cntrl_var(name="irest", value={"min":"0","eq":"0","md":"1"}, comment="0 = Do not restart ; 1 = Restart", priority=1)
373  # Use AMBER's default nonbonded cutoff if the user does not provide
374  # Set the PBC and pressure variables: ntb, ntp, barostat, mcbarint
375  if pbc:
376  ntb_eqmd = "2" if pressure is not None else "1"
377  ntp_eqmd = "1" if pressure is not None else "0"
378  cntrl_vars["cut"] = cntrl_var(name="cut", value={"min":"8.0","eq":"8.0","md":"8.0","sp":"8.0"}, comment="Nonbonded cutoff", priority=3)
379  cntrl_vars["ntb"] = cntrl_var(name="ntb", value={"min":"1","eq":ntb_eqmd,"md":ntb_eqmd,"sp":ntb_eqmd}, comment="0 = no PBC ; 1 = constant V ; 2 = constant P", priority=1)
380  cntrl_vars["ntp"] = cntrl_var(name="ntp", value={"min":"0","eq":ntp_eqmd,"md":ntp_eqmd,"sp":ntp_eqmd}, comment="0 = constant V ; 1 = isotropic scaling", priority=1)
381  cntrl_vars["iwrap"] = cntrl_var(name="iwrap", value={"min":"1","eq":"1","md":"1"}, comment="Wrap molecules back into box", priority=3)
382  cntrl_vars["igb"] = cntrl_var(name="igb", value={"min":"0","eq":"0","md":"0","sp":"0"}, comment="0 = No generalized Born model", priority=3)
383  if pressure is not None:
384  # We should use Berendsen for equilibration and MC for production.
385  cntrl_vars["barostat"] = cntrl_var(name="barostat", value={"eq":"1","md":"2"}, comment="1 = Berendsen; 2 = Monte Carlo", priority=1)
386  cntrl_vars["mcbarint"] = cntrl_var(name="mcbarint", value={"md":"25"}, comment="MC barostat rescaling interval", priority=3)
387  else:
388  # If there is no pressure, these variables should not be printed.
389  cntrl_vars["barostat"] = cntrl_var(name="barostat", value={}, comment="1 = Berendsen; 2 = Monte Carlo", priority=1)
390  cntrl_vars["mcbarint"] = cntrl_var(name="mcbarint", value={}, comment="MC barostat rescaling interval", priority=1)
391  else:
392  cntrl_vars["cut"] = cntrl_var(name="cut", value={"min":"9999.0","eq":"9999.0","md":"9999.0","sp":"9999.0"}, comment="Nonbonded cutoff", priority=1)
393  cntrl_vars["ntb"] = cntrl_var(name="ntb", value={"min":"0","eq":"0","md":"0","sp":"0"}, comment="0 = no PBC ; 1 = constant V ; 2 = constant P", priority=1)
394  cntrl_vars["ntp"] = cntrl_var(name="ntp", value={}, comment="0 = constant V ; 1 = isotropic scaling", priority=1)
395  cntrl_vars["igb"] = cntrl_var(name="igb", value={"min":"6","eq":"6","md":"6","sp":"6"}, comment="6 = Vacuum phase simulation", priority=1)
396  cntrl_vars["iwrap"] = cntrl_var(name="iwrap", value={}, comment="Wrap molecules back into box", priority=1)
397  cntrl_vars["barostat"] = cntrl_var(name="barostat", value={}, comment="1 = Berendsen; 2 = Monte Carlo", priority=1)
398  cntrl_vars["mcbarint"] = cntrl_var(name="mcbarint", value={}, comment="MC barostat rescaling interval", priority=1)
399  # Set the temperature variables tempi, temp0, ntt, gamma_ln
400  if temperature is not None:
401  cntrl_vars["tempi"] = cntrl_var(name="tempi", value={"eq":"%i" % temperature,"md":"%i" % temperature}, comment="Initial temperature", priority=1)
402  cntrl_vars["temp0"] = cntrl_var(name="temp0", value={"eq":"%i" % temperature,"md":"%i" % temperature}, comment="Reference temperature", priority=1)
403  cntrl_vars["ntt"] = cntrl_var(name="ntt", value={"eq":"3","md":"3"}, comment="Thermostat ; 3 = Langevin", priority=1)
404  cntrl_vars["gamma_ln"] = cntrl_var(name="gamma_ln", value={"eq":"1.0","md":"1.0"}, comment="Langevin collision frequency (ps^-1)", priority=3)
405  else:
406  cntrl_vars["tempi"] = cntrl_var(name="tempi", value={}, comment="Initial temperature", priority=1)
407  cntrl_vars["temp0"] = cntrl_var(name="temp0", value={}, comment="Reference temperature", priority=1)
408  cntrl_vars["ntt"] = cntrl_var(name="ntt", value={}, comment="Thermostat ; 3 = Langevin", priority=1)
409  cntrl_vars["gamma_ln"] = cntrl_var(name="gamma_ln", value={}, comment="Langevin collision frequency (ps^-1)", priority=1)
410  # Options having to do with constraints; these should be set by the user if SHAKE is desired.
411  # SHAKE is always turned off for minimization and for single-point properties.
412  cntrl_vars["ntc"] = cntrl_var(name="ntc", value={"min":"1","eq":"1","md":"1","sp":"1"}, comment="SHAKE; 1 = none, 2 = H-bonds, 3 = All-bonds", priority={"min":1,"eq":3,"md":3,"sp":1})
413  cntrl_vars["ntf"] = cntrl_var(name="ntf", value={"min":"1","eq":"1","md":"1","sp":"1"}, comment="No bonds involving H-atoms (use with NTC=2)", priority={"min":1,"eq":3,"md":3,"sp":1})
414  cntrl_vars["tol"] = cntrl_var(name="tol", value={}, comment="SHAKE tolerance,", priority=4)
415  # Random number seed for equilibration and dynamics
416  cntrl_vars["ig"] = cntrl_var(name="ig", value={"eq":"-1","md":"-1"}, comment="Random number seed; -1 based on date/time", priority=3)
417 
418  def get_priority(prio_in):
419  if type(prio_in) is int:
420  return prio_in
421  elif type(prio_in) is dict:
422  return prio_in[calctype]
423 
424  if mdin_orig is not None:
425  comments, names, block_dicts, suffixes = parse_amber_namelist(mdin_orig)
426  comments.append("Generated by ForceBalance from %s" % mdin_orig)
427  else:
428  comments = ["Generated by ForceBalance"]
429  names = ['cntrl']
430  block_dicts = [{}]
431  suffixes = [[]]
432 
433  for name, block_dict in zip(names, block_dicts):
434  if name == 'cntrl':
435  user_cntrl = block_dict
436  break
437 
438  cntrl_out = OrderedDict()
439  cntrl_comm = OrderedDict()
440 
441  # Fill in the "high priority" options set by ForceBalance
442  # Note that if value[calctype] is not set for a high-priority option,
443  # that means the variable is erased from the output namelist
444  checked_list = []
445  for name, var in cntrl_vars.items():
446  priority = get_priority(var.priority)
447  if priority in [1, 2]:
448  checked_list.append(name)
449  if calctype in var.value:
450  cntrl_out[name] = var.value[calctype]
451  if priority == 1:
452  cntrl_comm[name] = "Set by FB at runtime : %s" % var.comment
453  elif priority == 2:
454  cntrl_comm[name] = "From FB input file : %s" % var.comment
455 
456  # Fill in the other options set by the user
457  for name, value in user_cntrl.items():
458  if name not in checked_list:
459  checked_list.append(name)
460  cntrl_out[name] = value
461  cntrl_comm[name] = "Set via user-provided mdin file"
462 
463  # Fill in default options not set by the user
464  for name, var in cntrl_vars.items():
465  if name not in checked_list and get_priority(var.priority) == 3:
466  checked_list.append(name)
467  if calctype in var.value:
468  cntrl_out[name] = var.value[calctype]
469  cntrl_comm[name] = "FB set by default : %s" % var.comment
470 
471  # Note: priority-4 options from cntrl_vars
472  # are not used at all in this function
473 
474  for iname, name, in enumerate(names):
475  if name == 'cntrl':
476  block_dicts[iname] = cntrl_out
477 
478  if fout is not None:
479  with open(fout, 'w') as f:
480  for line in comments:
481  print(line, file=f)
482  for name, block_dict, suffix in zip(names, block_dicts, suffixes):
483  print("&%s" % name, file=f)
484  for key, val in block_dict.items():
485  print("%-20s ! %s" % ("%s=%s," % (key, val), cntrl_comm[key]), file=f)
486  print("/", file=f)
487  for line in suffix:
488  print("%s" % line, file=f)
489  f.close()
490 
491  return cntrl_out
492 
493 class Mol2_Reader(BaseReader):
494  """Finite state machine for parsing Mol2 force field file. (just for parameterizing the charges)"""
495 
496  def __init__(self,fnm):
497  # Initialize the superclass. :)
498  super(Mol2_Reader,self).__init__(fnm)
499 
500  self.pdict = mol2_pdict
501 
502  self.atom = []
503 
504  self.atomnames = []
505 
506  self.section = None
507  # The name of the molecule
508  self.mol = None
509 
510  def feed(self, line):
511  s = line.split()
512  self.ln += 1
513  # In mol2 files, the only defined interaction type is the Coulomb interaction.
514  if line.strip().lower() == '@<tripos>atom':
515  self.itype = 'COUL'
516  self.section = 'Atom'
517  elif line.strip().lower() == '@<tripos>bond':
518  self.itype = 'None'
519  self.section = 'Bond'
520  elif line.strip().lower() == '@<tripos>substructure':
521  self.itype = 'None'
522  self.section = 'Substructure'
523  elif line.strip().lower() == '@<tripos>molecule':
524  self.itype = 'None'
525  self.section = 'Molecule'
526  elif self.section == 'Molecule' and self.mol is None:
527  self.mol = '_'.join(s)
528  elif not is_mol2_atom(line):
529  self.itype = 'None'
530 
531  if is_mol2_atom(line) and self.itype == 'COUL':
532  #self.atomnames.append(s[self.pdict[self.itype]['Atom'][0]])
533  #self.adict.setdefault(self.mol,[]).append(s[self.pdict[self.itype]['Atom'][0]])
534  self.atomnames.append(s[0])
535  self.adict.setdefault(self.mol,[]).append(s[0])
536 
537  if self.itype in self.pdict:
538  if 'Atom' in self.pdict[self.itype] and match(' *[0-9]', line):
539  # List the atoms in the interaction.
540  #self.atom = [s[i] for i in self.pdict[self.itype]['Atom']]
541  self.atom = [s[0]]
542  # The suffix of the parameter ID is built from the atom #
543  # types/classes involved in the interaction.
544  self.suffix = ':' + '-'.join([self.mol,''.join(self.atom)])
545  #self.suffix = '.'.join(self.atom)
546  self.molatom = (self.mol, self.atom if type(self.atom) is list else [self.atom])
547 
548 class FrcMod_Reader(BaseReader):
549  """Finite state machine for parsing FrcMod force field file."""
550 
551  def __init__(self,fnm):
552  # Initialize the superclass. :)
553  super(FrcMod_Reader,self).__init__(fnm)
554 
555  self.pdict = frcmod_pdict
556 
557  self.atom = []
558 
559  self.dihe = False
560 
561  self.adict = {None:None}
562 
563  def Split(self, line):
564  return split(' +(?!-(?![0-9.]))', line.strip().replace('\n',''))
565 
566  def Whites(self, line):
567  return findall(' +(?!-(?![0-9.]))', line.replace('\n',''))
569  def build_pid(self, pfld):
570  """ Returns the parameter type (e.g. K in BONDSK) based on the
571  current interaction type.
572 
573  Both the 'pdict' dictionary (see gmxio.pdict) and the
574  interaction type 'state' (here, BONDS) are needed to get the
575  parameter type.
576 
577  If, however, 'pdict' does not contain the ptype value, a suitable
578  substitute is simply the field number.
579 
580  Note that if the interaction type state is not set, then it
581  defaults to the file name, so a generic parameter ID is
582  'filename.line_num.field_num'
583 
584  """
585  if self.dihe and not self.haveAtomLine:
586  pfld += 1
587  if hasattr(self, 'overpfx'):
588  return self.overpfx + ':%i:' % pfld + self.oversfx
589  ptype = self.pdict.get(self.itype,{}).get(pfld,':%i.%i' % (self.ln,pfld))
590  answer = self.itype
591  answer += ptype
592  answer += '/'+self.suffix
593  return answer
594 
595  def feed(self, line):
596  s = self.Split(line)
597  self.ln += 1
598 
599  if len(line.strip()) == 0:
600  return
601  if match('^dihe', line.strip().lower()):
602  self.dihe = True
603  return
604  elif match('^mass$', line.strip().lower()):
605  self.dihe = False
606  self.itype = 'MASS'
607  return
608  elif match('^bond$', line.strip().lower()):
609  self.dihe = False
610  self.itype = 'BONDS'
611  return
612  elif match('^angle$', line.strip().lower()):
613  self.dihe = False
614  self.itype = 'ANGLES'
615  return
616  elif match('^improper$', line.strip().lower()):
617  self.dihe = False
618  self.itype = 'IDIHS'
619  return
620  elif match('^nonbon$', line.strip().lower()):
621  self.dihe = False
622  self.itype = 'VDW'
623  return
624  elif len(s) == 0:
625  self.dihe = False
626  return
627 
628  if self.dihe:
629  if '-' in s[0]:
630  self.haveAtomLine = True
631  self.itype = 'PDIHS%i' % int(np.abs(float(s[4])))
632  else:
633  self.haveAtomLine = False
634  self.itype = 'PDIHS%i' % int(np.abs(float(s[3])))
635  else:
636  self.haveAtomLine = True
637 
638  if self.itype in self.pdict:
639  if 'Atom' in self.pdict[self.itype] and self.haveAtomLine:
640  # List the atoms in the interaction.
641  self.atom = [s[i].replace(" -","-") for i in self.pdict[self.itype]['Atom']]
642 
643  # The suffix of the parameter ID is built from the atom #
644  # types/classes involved in the interaction.
645  self.suffix = ''.join(self.atom)
646 
647 #=============================================================================================
648 # AMBER parmtop loader (from 'zander', by Randall J. Radmer)
649 #=============================================================================================
650 
651 # A regex for extracting print format info from the FORMAT lines.
652 FORMAT_RE_PATTERN=re.compile("([0-9]+)([a-zA-Z]+)([0-9]+)\.?([0-9]*)")
654 # Pointer labels which map to pointer numbers at top of prmtop files
655 POINTER_LABELS = """
656  NATOM, NTYPES, NBONH, MBONA, NTHETH, MTHETA,
657  NPHIH, MPHIA, NHPARM, NPARM, NEXT, NRES,
658  NBONA, NTHETA, NPHIA, NUMBND, NUMANG, NPTRA,
659  NATYP, NPHB, IFPERT, NBPER, NGPER, NDPER,
660  MBPER, MGPER, MDPER, IFBOX, NMXRS, IFCAP
661 """
662 
663 # Pointer labels (above) as a list, not string.
664 POINTER_LABEL_LIST = POINTER_LABELS.replace(',', '').split()
665 
666 class PrmtopLoader(object):
667  """Parsed AMBER prmtop file.
668 
669  ParmtopLoader reads, parses and manages content from a AMBER prmtop file.
670 
671  EXAMPLES
673  Parse a prmtop file of alanine dipeptide in implicit solvent.
674 
675  >>> import os, os.path
676  >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa')
677  >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
678  >>> prmtop = PrmtopLoader(prmtop_filename)
679 
680  Parse a prmtop file of alanine dipeptide in explicit solvent.
681 
682  >>> import os, os.path
683  >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
684  >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
685  >>> prmtop = PrmtopLoader(prmtop_filename)
686 
687  """
688  def __init__(self, inFilename):
689  """
690  Create a PrmtopLoader object from an AMBER prmtop file.
691 
692  ARGUMENTS
693 
694  inFilename (string) - AMBER 'new-style' prmtop file, probably generated with one of the AMBER tleap/xleap/sleap
695 
696  """
697 
698  self._prmtopVersion=None
699  self._flags=[]
700  self._raw_format={}
701  self._raw_data={}
702 
703  fIn=open(inFilename)
704  for line in fIn:
705  if line.startswith('%VERSION'):
706  tag, self._prmtopVersion = line.rstrip().split(None, 1)
707  elif line.startswith('%FLAG'):
708  tag, flag = line.rstrip().split(None, 1)
709  self._flags.append(flag)
710  self._raw_data[flag] = []
711  elif line.startswith('%FORMAT'):
712  format = line.rstrip()
713  index0=format.index('(')
714  index1=format.index(')')
715  format = format[index0+1:index1]
716  m = FORMAT_RE_PATTERN.search(format)
717  self._raw_format[self._flags[-1]] = (format, m.group(1), m.group(2), m.group(3), m.group(4))
718  elif self._flags \
719  and 'TITLE'==self._flags[-1] \
720  and not self._raw_data['TITLE']:
721  self._raw_data['TITLE'] = line.rstrip()
722  else:
723  flag=self._flags[-1]
724  (format, numItems, itemType,
725  itemLength, itemPrecision) = self._getFormat(flag)
726  iLength=int(itemLength)
727  line = line.rstrip()
728  for index in range(0, len(line), iLength):
729  item = line[index:index+iLength]
730  if item:
731  self._raw_data[flag].append(item.strip())
732  fIn.close()
733 
734  def _getFormat(self, flag=None):
735  if not flag:
736  flag=self._flags[-1]
737  return self._raw_format[flag]
738 
739  def _getPointerValue(self, pointerLabel):
740  """Return pointer value given pointer label
741 
742  Parameter:
743  - pointerLabel: a string matching one of the following:
744 
745  NATOM : total number of atoms
746  NTYPES : total number of distinct atom types
747  NBONH : number of bonds containing hydrogen
748  MBONA : number of bonds not containing hydrogen
749  NTHETH : number of angles containing hydrogen
750  MTHETA : number of angles not containing hydrogen
751  NPHIH : number of dihedrals containing hydrogen
752  MPHIA : number of dihedrals not containing hydrogen
753  NHPARM : currently not used
754  NPARM : currently not used
755  NEXT : number of excluded atoms
756  NRES : number of residues
757  NBONA : MBONA + number of constraint bonds
758  NTHETA : MTHETA + number of constraint angles
759  NPHIA : MPHIA + number of constraint dihedrals
760  NUMBND : number of unique bond types
761  NUMANG : number of unique angle types
762  NPTRA : number of unique dihedral types
763  NATYP : number of atom types in parameter file, see SOLTY below
764  NPHB : number of distinct 10-12 hydrogen bond pair types
765  IFPERT : set to 1 if perturbation info is to be read in
766  NBPER : number of bonds to be perturbed
767  NGPER : number of angles to be perturbed
768  NDPER : number of dihedrals to be perturbed
769  MBPER : number of bonds with atoms completely in perturbed group
770  MGPER : number of angles with atoms completely in perturbed group
771  MDPER : number of dihedrals with atoms completely in perturbed groups
772  IFBOX : set to 1 if standard periodic box, 2 when truncated octahedral
773  NMXRS : number of atoms in the largest residue
774  IFCAP : set to 1 if the CAP option from edit was specified
775  """
776  index = POINTER_LABEL_LIST.index(pointerLabel)
777  return float(self._raw_data['POINTERS'][index])
778 
779  def getNumAtoms(self):
780  """Return the number of atoms in the system"""
781  return int(self._getPointerValue('NATOM'))
782 
783  def getNumTypes(self):
784  """Return the number of AMBER atoms types in the system"""
785  return int(self._getPointerValue('NTYPES'))
786 
787  def getIfBox(self):
788  """Return True if the system was build with periodic boundary conditions (PBC)"""
789  return int(self._getPointerValue('IFBOX'))
790 
791  def getIfCap(self):
792  """Return True if the system was build with the cap option)"""
793  return int(self._getPointerValue('IFCAP'))
794 
795  def getIfPert(self):
796  """Return True if the system was build with the perturbation parameters)"""
797  return int(self._getPointerValue('IFPERT'))
798 
799  def getMasses(self):
800  """Return a list of atomic masses in the system"""
801  try:
802  return self._massList
803  except AttributeError:
804  pass
805 
806  self._massList=[]
807  raw_masses=self._raw_data['MASS']
808  for ii in range(self.getNumAtoms()):
809  self._massList.append(float(raw_masses[ii]))
810  self._massList = self._massList
811  return self._massList
813  def getCharges(self):
814  """Return a list of atomic charges in the system"""
815  try:
816  return self._chargeList
817  except AttributeError:
818  pass
819 
820  self._chargeList=[]
821  raw_charges=self._raw_data['CHARGE']
822  for ii in range(self.getNumAtoms()):
823  self._chargeList.append(float(raw_charges[ii])/18.2223)
824  self._chargeList = self._chargeList
825  return self._chargeList
826 
827  def getAtomName(self, iAtom):
828  """Return the atom name for iAtom"""
829  atomNames = self.getAtomNames()
830  return atomNames[iAtom]
831 
832  def getAtomNames(self):
833  """Return the list of the system atom names"""
834  return self._raw_data['ATOM_NAME']
835 
836  def _getAtomTypeIndexes(self):
837  try:
838  return self._atomTypeIndexes
839  except AttributeError:
840  pass
841  self._atomTypeIndexes=[]
842  for atomTypeIndex in self._raw_data['ATOM_TYPE_INDEX']:
843  self._atomTypeIndexes.append(int(atomTypeIndex))
844  return self._atomTypeIndexes
845 
846  def getAtomType(self, iAtom):
847  """Return the AMBER atom type for iAtom"""
848  atomTypes=self.getAtomTypes()
849  return atomTypes[iAtom]
850 
851  def getAtomTypes(self):
852  """Return the list of the AMBER atom types"""
853  return self._raw_data['AMBER_ATOM_TYPE']
854 
855  def getResidueNumber(self, iAtom):
856  """Return iAtom's residue number"""
857  return self._getResiduePointer(iAtom)+1
858 
859  def getResidueLabel(self, iAtom=None, iRes=None):
860  """Return residue label for iAtom OR iRes"""
861  if iRes==None and iAtom==None:
862  raise Exception("only specify iRes or iAtom, not both")
863  if iRes!=None and iAtom!=None:
864  raise Exception("iRes or iAtom must be set")
865  if iRes!=None:
866  return self._raw_data['RESIDUE_LABEL'][iRes]
867  else:
868  return self.getResidueLabel(iRes=self._getResiduePointer(iAtom))
869 
870  def _getResiduePointer(self, iAtom):
871  try:
872  return self.residuePointerDict[iAtom]
873  except:
874  pass
875  self.residuePointerDict = {}
876  resPointers=self._raw_data['RESIDUE_POINTER']
877  firstAtom = [int(p)-1 for p in resPointers]
878  firstAtom.append(self.getNumAtoms())
879  res = 0
880  for i in range(self.getNumAtoms()):
881  while firstAtom[res+1] <= i:
882  res += 1
883  self.residuePointerDict[i] = res
884  return self.residuePointerDict[iAtom]
885 
886  def getNonbondTerms(self):
887  """Return list of all rVdw, epsilon pairs for each atom. Work in the AMBER unit system."""
888  try:
889  return self._nonbondTerms
890  except AttributeError:
891  pass
892  self._nonbondTerms=[]
893  lengthConversionFactor = 1.0
894  energyConversionFactor = 1.0
895  for iAtom in range(self.getNumAtoms()):
896  numTypes=self.getNumTypes()
897  atomTypeIndexes=self._getAtomTypeIndexes()
898  index=(numTypes+1)*(atomTypeIndexes[iAtom]-1)
899  nbIndex=int(self._raw_data['NONBONDED_PARM_INDEX'][index])-1
900  if nbIndex<0:
901  raise Exception("10-12 interactions are not supported")
902  acoef = float(self._raw_data['LENNARD_JONES_ACOEF'][nbIndex])
903  bcoef = float(self._raw_data['LENNARD_JONES_BCOEF'][nbIndex])
904  try:
905  rMin = (2*acoef/bcoef)**(1/6.0)
906  epsilon = 0.25*bcoef*bcoef/acoef
907  except ZeroDivisionError:
908  rMin = 1.0
909  epsilon = 0.0
910  rVdw = rMin/2.0*lengthConversionFactor
911  epsilon = epsilon*energyConversionFactor
912  self._nonbondTerms.append( (rVdw, epsilon) )
913  return self._nonbondTerms
914 
915  def _getBonds(self, bondPointers):
916  forceConstant=self._raw_data["BOND_FORCE_CONSTANT"]
917  bondEquil=self._raw_data["BOND_EQUIL_VALUE"]
918  returnList=[]
919  forceConstConversionFactor = 1.0
920  lengthConversionFactor = 1.0
921  for ii in range(0,len(bondPointers),3):
922  if int(bondPointers[ii])<0 or \
923  int(bondPointers[ii+1])<0:
924  raise Exception("Found negative bonded atom pointers %s"
925  % ((bondPointers[ii],
926  bondPointers[ii+1]),))
927  iType=int(bondPointers[ii+2])-1
928  returnList.append((int(int(bondPointers[ii])/3),
929  int(int(bondPointers[ii+1])/3),
930  float(forceConstant[iType])*forceConstConversionFactor,
931  float(bondEquil[iType])*lengthConversionFactor))
932  return returnList
933 
934  def getBondsWithH(self):
935  """Return list of bonded atom pairs, K, and Rmin for each bond with a hydrogen"""
936  try:
937  return self._bondListWithH
938  except AttributeError:
939  pass
940  bondPointers=self._raw_data["BONDS_INC_HYDROGEN"]
941  self._bondListWithH = self._getBonds(bondPointers)
942  return self._bondListWithH
943 
944 
945  def getBondsNoH(self):
946  """Return list of bonded atom pairs, K, and Rmin for each bond with no hydrogen"""
947  try:
948  return self._bondListNoH
949  except AttributeError:
950  pass
951  bondPointers=self._raw_data["BONDS_WITHOUT_HYDROGEN"]
952  self._bondListNoH = self._getBonds(bondPointers)
953  return self._bondListNoH
954 
955  def getAngles(self):
956  """Return list of atom triplets, K, and ThetaMin for each bond angle"""
957  try:
958  return self._angleList
959  except AttributeError:
960  pass
961  forceConstant=self._raw_data["ANGLE_FORCE_CONSTANT"]
962  angleEquil=self._raw_data["ANGLE_EQUIL_VALUE"]
963  anglePointers = self._raw_data["ANGLES_INC_HYDROGEN"] \
964  +self._raw_data["ANGLES_WITHOUT_HYDROGEN"]
965  self._angleList=[]
966  forceConstConversionFactor = 1.0
967  for ii in range(0,len(anglePointers),4):
968  if int(anglePointers[ii])<0 or \
969  int(anglePointers[ii+1])<0 or \
970  int(anglePointers[ii+2])<0:
971  raise Exception("Found negative angle atom pointers %s"
972  % ((anglePointers[ii],
973  anglePointers[ii+1],
974  anglePointers[ii+2]),))
975  iType=int(anglePointers[ii+3])-1
976  self._angleList.append((int(int(anglePointers[ii])/3),
977  int(int(anglePointers[ii+1])/3),
978  int(int(anglePointers[ii+2])/3),
979  float(forceConstant[iType])*forceConstConversionFactor,
980  float(angleEquil[iType])))
981  return self._angleList
982 
983  def getDihedrals(self):
984  """Return list of atom quads, K, phase and periodicity for each dihedral angle"""
985  try:
986  return self._dihedralList
987  except AttributeError:
988  pass
989  forceConstant=self._raw_data["DIHEDRAL_FORCE_CONSTANT"]
990  phase=self._raw_data["DIHEDRAL_PHASE"]
991  periodicity=self._raw_data["DIHEDRAL_PERIODICITY"]
992  dihedralPointers = self._raw_data["DIHEDRALS_INC_HYDROGEN"] \
993  +self._raw_data["DIHEDRALS_WITHOUT_HYDROGEN"]
994  self._dihedralList=[]
995  forceConstConversionFactor = 1.0
996  for ii in range(0,len(dihedralPointers),5):
997  if int(dihedralPointers[ii])<0 or int(dihedralPointers[ii+1])<0:
998  raise Exception("Found negative dihedral atom pointers %s"
999  % ((dihedralPointers[ii],
1000  dihedralPointers[ii+1],
1001  dihedralPointers[ii+2],
1002  dihedralPointers[ii+3]),))
1003  iType=int(dihedralPointers[ii+4])-1
1004  self._dihedralList.append((int(int(dihedralPointers[ii])/3),
1005  int(int(dihedralPointers[ii+1])/3),
1006  int(abs(int(dihedralPointers[ii+2]))/3),
1007  int(abs(int(dihedralPointers[ii+3]))/3),
1008  float(forceConstant[iType])*forceConstConversionFactor,
1009  float(phase[iType]),
1010  int(0.5+float(periodicity[iType]))))
1011  return self._dihedralList
1012 
1014  """Return list of atom pairs, chargeProduct, rMin and epsilon for each 1-4 interaction"""
1015  dihedralPointers = self._raw_data["DIHEDRALS_INC_HYDROGEN"] \
1016  +self._raw_data["DIHEDRALS_WITHOUT_HYDROGEN"]
1017  returnList=[]
1018  charges=self.getCharges()
1019  nonbondTerms = self.getNonbondTerms()
1020  for ii in range(0,len(dihedralPointers),5):
1021  if int(dihedralPointers[ii+2])>0 and int(dihedralPointers[ii+3])>0:
1022  iAtom = int(int(dihedralPointers[ii])/3)
1023  lAtom = int(int(dihedralPointers[ii+3])/3)
1024  chargeProd = charges[iAtom]*charges[lAtom]
1025  (rVdwI, epsilonI) = nonbondTerms[iAtom]
1026  (rVdwL, epsilonL) = nonbondTerms[lAtom]
1027  rMin = (rVdwI+rVdwL)
1028  epsilon = math.sqrt(epsilonI*epsilonL)
1029  returnList.append((iAtom, lAtom, chargeProd, rMin, epsilon))
1030  return returnList
1031 
1032  def getExcludedAtoms(self):
1033  """Return list of lists, giving all pairs of atoms that should have no non-bond interactions"""
1034  try:
1035  return self._excludedAtoms
1036  except AttributeError:
1037  pass
1038  self._excludedAtoms=[]
1039  numExcludedAtomsList=self._raw_data["NUMBER_EXCLUDED_ATOMS"]
1040  excludedAtomsList=self._raw_data["EXCLUDED_ATOMS_LIST"]
1041  total=0
1042  for iAtom in range(self.getNumAtoms()):
1043  index0=total
1044  n=int(numExcludedAtomsList[iAtom])
1045  total+=n
1046  index1=total
1047  atomList=[]
1048  for jAtom in excludedAtomsList[index0:index1]:
1049  j=int(jAtom)
1050  if j>0:
1051  atomList.append(j-1)
1052  self._excludedAtoms.append(atomList)
1053  return self._excludedAtoms
1054 
1055  def getGBParms(self, symbls=None):
1056  """Return list giving GB params, Radius and screening factor"""
1057  try:
1058  return self._gb_List
1059  except AttributeError:
1060  pass
1061  self._gb_List=[]
1062  radii=self._raw_data["RADII"]
1063  screen=self._raw_data["SCREEN"]
1064  # Update screening parameters for GBn if specified
1065  if symbls:
1066  for (i, symbl) in enumerate(symbls):
1067  if symbl[0] == ('c' or 'C'):
1068  screen[i] = 0.48435382330
1069  elif symbl[0] == ('h' or 'H'):
1070  screen[i] = 1.09085413633
1071  elif symbl[0] == ('n' or 'N'):
1072  screen[i] = 0.700147318409
1073  elif symbl[0] == ('o' or 'O'):
1074  screen[i] = 1.06557401132
1075  elif symbl[0] == ('s' or 'S'):
1076  screen[i] = 0.602256336067
1077  else:
1078  screen[i] = 0.5
1079  lengthConversionFactor = 1.0
1080  for iAtom in range(len(radii)):
1081  self._gb_List.append((float(radii[iAtom])*lengthConversionFactor, float(screen[iAtom])))
1082  return self._gb_List
1083 
1084  def getBoxBetaAndDimensions(self):
1085  """Return periodic boundary box beta angle and dimensions"""
1086  beta=float(self._raw_data["BOX_DIMENSIONS"][0])
1087  x=float(self._raw_data["BOX_DIMENSIONS"][1])
1088  y=float(self._raw_data["BOX_DIMENSIONS"][2])
1089  z=float(self._raw_data["BOX_DIMENSIONS"][3])
1090  return (beta, x, y, z)
1091 
1092 class AMBER(Engine):
1093 
1094  """ Engine for carrying out general purpose AMBER calculations. """
1095 
1096  def __init__(self, name="amber", **kwargs):
1097 
1098  self.valkwd = ['amberhome', 'pdb', 'mol2', 'frcmod', 'leapcmd', 'mdin', 'reqpdb']
1099  super(AMBER,self).__init__(name=name, **kwargs)
1100 
1101  def setopts(self, **kwargs):
1102 
1103  """ Called by __init__ ; Set AMBER-specific options. """
1104 
1105  if 'amberhome' in kwargs:
1106  self.amberhome = kwargs['amberhome']
1107  if not os.path.exists(os.path.join(self.amberhome, "bin", "sander")):
1108  warn_press_key("The 'sander' executable indicated by %s doesn't exist! (Check amberhome)" \
1109  % os.path.join(self.amberhome,"bin","sander"))
1110  else:
1111  warn_once("The 'amberhome' option was not specified; using default.")
1112  if which('sander') == '':
1113  warn_press_key("Please add AMBER executables to the PATH or specify amberhome.")
1114  self.amberhome = os.path.split(which('sander'))[0]
1115 
1116  self.have_pmemd_cuda = False
1117  if os.path.exists(os.path.join(self.amberhome, "bin", "pmemd.cuda")):
1118  self.callamber('pmemd.cuda -h', persist=True)
1119  if _exec.returncode != 0:
1120  warn_press_key("pmemd.cuda gave a nonzero returncode; CUDA environment variables likely missing")
1121  else:
1122  logger.info("pmemd.cuda is available, using CUDA to accelerate calculations.\n")
1123  self.have_pmemd_cuda = True
1124 
1125  with wopen('.quit.leap') as f:
1126  print('quit', file=f)
1128  # AMBER search path
1129  self.spath = []
1130  for line in self.callamber('tleap -f .quit.leap'):
1131  if 'Adding' in line and 'to search path' in line:
1132  self.spath.append(line.split('Adding')[1].split()[0])
1133  os.remove('.quit.leap')
1134 
1135  def readsrc(self, **kwargs):
1136 
1137  """ Called by __init__ ; read files from the source directory. """
1138 
1139  self.leapcmd = onefile(kwargs.get('leapcmd'), 'leap', err=True)
1140  self.mdin = onefile(kwargs.get('mdin'), 'mdin', err=False)
1141  self.absleap = os.path.abspath(self.leapcmd)
1142  if self.mdin is not None:
1143  self.absmdin = os.path.abspath(self.mdin)
1144 
1145  # Name of the molecule, currently just call it a default name.
1146  self.mname = 'molecule'
1147 
1148  # Whether to throw an error if a PDB file doesn't exist.
1149  reqpdb = kwargs.get('reqpdb', 1)
1150 
1151  # Determine the PDB file name. Amber could use this in tleap if it wants.
1152  # If 'pdb' is provided to Engine initialization, it will be used to
1153  # copy over topology information (chain, atomname etc.). If mol/coords
1154  # is not provided, then it will also provide the coordinates.
1155  pdbfnm = onefile(kwargs.get('pdb'), 'pdb' if reqpdb else None, err=reqpdb)
1156 
1157  # If the molecule object is provided as a keyword argument, it now
1158  # becomes an Engine attribute as well. Otherwise, we create the
1159  # Engine.mol from the provided coordinates (if they exist).
1160  if 'mol' in kwargs:
1161  self.mol = kwargs['mol']
1162  else:
1163  crdfile = None
1164  if 'coords' in kwargs:
1165  crdfile = onefile(kwargs.get('coords'), None, err=True)
1166  elif pdbfnm is not None:
1167  crdfile = pdbfnm
1168  if crdfile is None:
1169  logger.error("Cannot find a coordinate file to use\n")
1170  raise RuntimeError
1171  self.mol = Molecule(crdfile, top=pdbfnm, build_topology=False)
1173 
1174  # If a .pdb was not provided, we create one.
1175  if pdbfnm is None:
1176  pdbfnm = self.name + ".pdb"
1177  # AMBER doesn't always like the CONECT records
1178  self.mol[0].write(pdbfnm, write_conect=False)
1179  self.abspdb = os.path.abspath(pdbfnm)
1180 
1181  # Write the PDB that AMBER is going to read in.
1182  # This may or may not be identical to the one used to initialize the engine.
1183  # self.mol.write('%s.pdb' % self.name)
1184  # self.abspdb = os.path.abspath('%s.pdb' % self.name)
1185 
1186  def callamber(self, command, stdin=None, print_to_screen=False, print_command=False, **kwargs):
1187 
1188  """ Call AMBER; prepend amberhome to calling the appropriate ambertools program. """
1189 
1190  csplit = command.split()
1191  # Sometimes the engine changes dirs and the inpcrd/prmtop go missing, so we link it.
1192  # Prepend the AMBER path to the program call.
1193  prog = os.path.join(self.amberhome, "bin", csplit[0])
1194  csplit[0] = prog
1195  # No need to catch exceptions since failed AMBER calculations will return nonzero exit status.
1196  o = _exec(' '.join(csplit), stdin=stdin, print_to_screen=print_to_screen, print_command=print_command, rbytes=1024, **kwargs)
1197  return o
1198 
1199  def prepare(self, pbc=False, **kwargs):
1200 
1201  """ Called by __init__ ; prepare the temp directory and figure out the topology. """
1202 
1203  self.AtomLists = defaultdict(list)
1204  self.pbc = pbc
1205 
1206  self.mol2 = []
1207  self.frcmod = []
1208  # if 'mol2' in kwargs:
1209  # self.mol2 = kwargs['mol2'][:]
1210  # if 'frcmod' in kwargs:
1211  # self.frcmod = kwargs['frcmod'][:]
1212 
1213  if hasattr(self,'FF'):
1214  # If the parameter files don't already exist, create them for the purpose of
1215  # preparing the engine, but then delete them afterward.
1216  prmtmp = False
1217  for fnm in self.FF.amber_frcmod + self.FF.amber_mol2:
1218  if not os.path.exists(fnm):
1219  self.FF.make(np.zeros(self.FF.np))
1220  prmtmp = True
1221  # Currently force field object only allows one mol2 and frcmod file although this can be lifted.
1222  for mol2 in self.FF.amber_mol2:
1223  self.mol2.append(mol2)
1224  for frcmod in self.FF.amber_frcmod:
1225  self.frcmod.append(frcmod)
1226  # self.mol2 = [self.FF.amber_mol2]
1227  # self.frcmod = [self.FF.amber_frcmod]
1228  # if 'mol2' in kwargs:
1229  # logger.error("FF object is provided, which overrides mol2 keyword argument")
1230  # raise RuntimeError
1231  # if 'frcmod' in kwargs:
1232  # logger.error("FF object is provided, which overrides frcmod keyword argument")
1233  # raise RuntimeError
1234  else:
1235  prmtmp = False
1236  # mol2 and frcmod files in the target folder should also be included
1237  self.absmol2 = []
1238  for mol2 in listfiles(kwargs.get('mol2'), 'mol2', err=False):
1239  if mol2 not in self.mol2:
1240  self.mol2.append(mol2)
1241  self.absmol2.append(os.path.abspath(mol2))
1242  self.absfrcmod = []
1243  for frcmod in listfiles(kwargs.get('frcmod'), 'frcmod', err=False):
1244  if frcmod not in self.frcmod:
1245  self.frcmod.append(frcmod)
1246  self.absfrcmod.append(os.path.abspath(frcmod))
1247 
1248  # Figure out the topology information.
1249  self.leap(read_prmtop=True, count_mols=True)
1250 
1251  # I also need to write the trajectory
1252  if 'boxes' in self.mol.Data.keys():
1253  logger.info("\x1b[91mWriting %s-all.crd file with no periodic box information\x1b[0m\n" % self.name)
1254  del self.mol.Data['boxes']
1255 
1256  if hasattr(self, 'target') and hasattr(self.target,'loop_over_snapshots') and self.target.loop_over_snapshots:
1257  if hasattr(self.target, 'qmatoms'):
1258  self.qmatoms = self.target.qmatoms
1259  else:
1260  self.qmatoms = list(range(self.mol.na))
1261  # if hasattr(self.target, 'shots'):
1262  # self.mol.write("%s-all.crd" % self.name, selection=range(self.target.shots), ftype="mdcrd")
1263  # else:
1264  # self.mol.write("%s-all.crd" % self.name, ftype="mdcrd")
1265 
1266  if prmtmp:
1267  for f in self.FF.fnms:
1268  os.unlink(f)
1269 
1270  def leap(self, read_prmtop=False, count_mols=False, name=None, delcheck=False):
1271  if not os.path.exists(self.leapcmd):
1272  LinkFile(self.absleap, self.leapcmd)
1273  pdb = os.path.basename(self.abspdb)
1274  if not os.path.exists(pdb):
1275  LinkFile(self.abspdb, pdb)
1276  # Link over "static" mol2 and frcmod files from target folder
1277  # print(self.absmol2, self.absfrcmod)
1278  for mol2 in self.absmol2:
1279  if not os.path.exists(os.path.split(mol2)[-1]):
1280  LinkFile(mol2, os.path.split(mol2)[-1])
1281  for frcmod in self.absfrcmod:
1282  if not os.path.exists(os.path.split(frcmod)[-1]):
1283  LinkFile(frcmod, os.path.split(frcmod)[-1])
1284  if name is None: name = self.name
1285  write_leap(self.leapcmd, mol2=self.mol2, frcmod=self.frcmod, pdb=pdb, prefix=name, spath=self.spath, delcheck=delcheck)
1286  self.callamber("tleap -f %s_" % self.leapcmd)
1287  if read_prmtop:
1288  prmtop = PrmtopLoader('%s.prmtop' % name)
1289  na = prmtop.getNumAtoms()
1290  self.natoms = na
1291  self.AtomLists['Charge'] = prmtop.getCharges()
1292  self.AtomLists['Name'] = prmtop.getAtomNames()
1293  self.AtomLists['Mass'] = prmtop.getMasses()
1294  self.AtomLists['ResidueNumber'] = [prmtop.getResidueNumber(i) for i in range(na)]
1295  self.AtomLists['ResidueName'] = [prmtop.getResidueLabel(i) for i in range(na)]
1296  # AMBER virtual sites don't have to have mass zero; this is my
1297  # best guess at figuring out where they are.
1298  self.AtomMask = [self.AtomLists['Mass'][i] >= 1.0 or self.AtomLists['Name'][i] != 'LP' for i in range(na)]
1299  if self.pbc != prmtop.getIfBox():
1300  raise RuntimeError("Engine was created with pbc = %i but prmtop.getIfBox() = %i" % (self.pbc, prmtop.getIfBox()))
1301  # This is done only optionally, because it is costly
1302  if count_mols:
1303  G = nx.Graph()
1304  for i in range(na):
1305  G.add_node(i)
1306  for b in prmtop.getBondsNoH():
1307  G.add_edge(b[0], b[1])
1308  for b in prmtop.getBondsWithH():
1309  G.add_edge(b[0], b[1])
1310  gs = [G.subgraph(c).copy() for c in nx.connected_components(G)]
1311  # Deprecated in networkx 2.2
1312  # gs = list(nx.connected_component_subgraphs(G))
1313  self.AtomLists['MoleculeNumber'] = [None for i in range(na)]
1314  for ig, g in enumerate(gs):
1315  for i in g.nodes():
1316  self.AtomLists['MoleculeNumber'][i] = ig
1317 
1318  def get_charges(self):
1319  self.leap(read_prmtop=True, count_mols=False)
1320  return np.array(self.AtomLists['Charge'])
1321 
1322  def evaluate_sander(self, leap=True, traj_fnm=None, snapshots=None):
1323  """
1324  Utility function for computing energy and forces using AMBER.
1325  Coordinates (and boxes, if pbc) are obtained from the
1326  Molecule object stored internally
1327 
1328  Parameters
1329  ----------
1330  snapshots : None or list
1331  If a list is provided, only compute energies / forces for snapshots listed.
1332 
1333  Outputs:
1334  Result: Dictionary containing energies and forces.
1335  """
1336  # Figure out where the trajectory information is coming from
1337  # First priority: Passed as input to trajfnm
1338  # Second priority: Using self.trajectory filename attribute
1339  # Third priority: Using internal Molecule object
1340  # 0 = using internal Molecule object
1341  # 1 = using NetCDF trajectory format
1342  mode = 0
1343  if traj_fnm is None and hasattr(self, 'trajectory'):
1344  traj_fnm = self.trajectory
1345  if traj_fnm is not None:
1346  try:
1347  nc = netcdf_file(traj_fnm, 'r')
1348  mode = 1
1349  except TypeError:
1350  print("Failed to load traj as netcdf, trying to load as Molecule object")
1351  mol = Molecule(traj_fnm)
1352  else:
1353  mol = self.mol
1354 
1355  def get_coord_box(i):
1356  box = None
1357  if mode == 0:
1358  coords = mol.xyzs[i]
1359  if self.pbc:
1360  box = [mol.boxes[i].a, mol.boxes[i].b, mol.boxes[i].c,
1361  mol.boxes[i].alpha, mol.boxes[i].beta, mol.boxes[i].gamma]
1362  elif mode == 1:
1363  coords = nc.variables['coordinates'].data[i].copy()
1364  if self.pbc:
1365  cl = nc.variables['cell_lengths'].data[i].copy()
1366  ca = nc.variables['cell_angles'].data[i].copy()
1367  box = [cl[0], cl[1], cl[2], ca[0], ca[1], ca[2]]
1368  return coords, box
1369 
1370  self.leap(read_prmtop=False, count_mols=False, delcheck=True)
1371  cntrl_vars = write_mdin('sp', pbc=self.pbc, mdin_orig=self.mdin)
1372  if self.pbc:
1373  inp = sander.pme_input()
1374  else:
1375  inp = sander.gas_input()
1376  if 'ntc' in cntrl_vars: inp.ntc = int(cntrl_vars['ntc'])
1377  if 'ntf' in cntrl_vars: inp.ntf = int(cntrl_vars['ntf'])
1378  if 'cut' in cntrl_vars: inp.cut = float(cntrl_vars['cut'])
1379  coord, box = get_coord_box(0)
1380  sander.setup("%s.prmtop" % self.name, coord, box, inp)
1381 
1382  Energies = []
1383  Forces = []
1384  if snapshots == None:
1385  if mode == 0:
1386  snapshots = range(len(self.mol))
1387  elif mode == 1:
1388  snapshots = range(nc.variables['coordinates'].shape[0])
1389  # Keep real atoms in mask.
1390  # sander API cannot handle virtual sites when igb > 0
1391  # so these codes are not needed.
1392  # atomsel = np.where(self.AtomMask)
1393  # coordsel = sum([i, i+1, i+2] for i in atomsel)
1394 
1395  for i in snapshots:
1396  coord, box = get_coord_box(i)
1397  if self.pbc:
1398  sander.set_box(*box)
1399  sander.set_positions(coord)
1400  e, f = sander.energy_forces()
1401  Energies.append(e.tot * 4.184)
1402  frc = np.array(f).flatten() * 4.184 * 10
1403  Forces.append(frc)
1404  sander.cleanup()
1405  if mode == 1:
1406  nc.close()
1407  Result = OrderedDict()
1408  Result["Energy"] = np.array(Energies)
1409  Result["Force"] = np.array(Forces)
1410  return Result
1411 
1412  def energy_force_one(self, shot):
1413  """ Computes the energy and force using AMBER for one snapshot. """
1414  Result = self.evaluate_sander(snapshots=[shot])
1415  return np.hstack((Result["Energy"].reshape(-1,1), Result["Force"]))
1416 
1417  def energy(self):
1418  """ Computes the energy using AMBER over a trajectory. """
1419  return self.evaluate_sander()["Energy"]
1420 
1421  def energy_force(self):
1422  """ Computes the energy and force using AMBER over a trajectory. """
1423  Result = self.evaluate_sander()
1424  return np.hstack((Result["Energy"].reshape(-1,1), Result["Force"]))
1425 
1426  def evaluate_cpptraj(self, leap=True, traj_fnm=None, potential=False):
1427  """ Use cpptraj to evaluate properties over a trajectory file. """
1428  # Figure out where the trajectory information is coming from
1429  # First priority: Passed as input to trajfnm
1430  # Second priority: Using self.trajectory filename attribute
1431  if traj_fnm is None:
1432  if hasattr(self, 'trajectory'):
1433  traj_fnm = self.trajectory
1434  else:
1435  raise RuntimeError("evaluate_cpptraj needs a trajectory file name")
1436  if leap:
1437  self.leap(read_prmtop=False, count_mols=False, delcheck=True)
1438  cpptraj_in=['parm %s.prmtop' % self.name]
1439  cpptraj_in.append("trajin %s" % traj_fnm)
1440  precision_lines = []
1441  if potential:
1442  cntrl_vars = write_mdin('sp', pbc=self.pbc, mdin_orig=self.mdin)
1443  for key in ['igb', 'ntb', 'cut', 'ntc', 'ntf']:
1444  if key not in cntrl_vars:
1445  raise RuntimeError("Cannot use sander API because key %s not set" % key)
1446  cpptraj_in.append("esander POTENTIAL out esander.txt ntb %s igb %s cut %s ntc %s ntf %s" %
1447  (cntrl_vars['ntb'], cntrl_vars['igb'], cntrl_vars['cut'], cntrl_vars['ntc'], cntrl_vars['ntf']))
1448  precision_lines.append("precision esander.txt 18 8")
1449  cpptraj_in.append("vector DIPOLE dipole out dipoles.txt")
1450  precision_lines.append("precision dipoles.txt 18 8")
1451  if self.pbc:
1452  cpptraj_in.append("volume VOLUME out volumes.txt")
1453  precision_lines.append("precision volumes.txt 18 8")
1454  cpptraj_in += precision_lines
1455  with open('%s-cpptraj.in' % self.name, 'w') as f:
1456  print('\n'.join(cpptraj_in), file=f)
1457  self.callamber("cpptraj -i %s-cpptraj.in" % self.name)
1458  # Gather the results
1459  Result = OrderedDict()
1460  if potential:
1461  esander_lines = list(open('esander.txt').readlines())
1462  ie = 0
1463  for iw, w in enumerate(esander_lines[0].split()):
1464  if w == "POTENTIAL[total]":
1465  ie = iw
1466  if ie == 0:
1467  raise RuntimeError("Cannot find field corresponding to total energies")
1468  potentials = np.array([float(line.split()[ie]) for line in esander_lines[1:]])*4.184
1469  Result["Potentials"] = potentials
1470  # Convert e*Angstrom to debye
1471  dipoles = np.array([[float(w) for w in line.split()[1:4]] for line in list(open("dipoles.txt").readlines())[1:]]) / 0.20819434
1472  Result["Dips"] = dipoles
1473  # Volume of simulation boxes in cubic nanometers
1474  # Conversion factor derived from the following:
1475  # In [22]: 1.0 * gram / mole / (1.0 * nanometer)**3 / AVOGADRO_CONSTANT_NA / (kilogram/meter**3)
1476  # Out[22]: 1.6605387831627252
1477  conv = 1.6605387831627252
1478  if self.pbc:
1479  volumes = np.array([float(line.split()[1]) for line in list(open("volumes.txt").readlines())[1:]])/1000
1480  densities = conv * np.sum(self.AtomLists['Mass']) / volumes
1481  Result["Volumes"] = volumes
1482  Result["Rhos"] = densities
1483  return Result
1484 
1485  def kineticE_cpptraj(self, leap=False, traj_fnm=None):
1486  """
1487  Evaluate the kinetic energy of each frame in a trajectory using cpptraj.
1488  This requires a netcdf-formatted trajectory containing velocities, which
1489  is generated using ntwv=-1 and ioutfm=1.
1490  """
1491  # Figure out where the trajectory information is coming from
1492  # First priority: Passed as input to trajfnm
1493  # Second priority: Using self.trajectory filename attribute
1494  if traj_fnm is None:
1495  if hasattr(self, 'trajectory'):
1496  traj_fnm = self.trajectory
1497  else:
1498  raise RuntimeError("evaluate_cpptraj needs a trajectory file name")
1499  if leap:
1500  self.leap(read_prmtop=False, count_mols=False, delcheck=True)
1501  cpptraj_in=['parm %s.prmtop' % self.name]
1502  cpptraj_in.append("trajin %s" % traj_fnm)
1503  cpptraj_in.append("temperature TEMPERATURE out temperature.txt")
1504  cpptraj_in.append("precision temperature.txt 18 8")
1505  with open('%s-cpptraj-temp.in' % self.name, 'w') as f:
1506  print('\n'.join(cpptraj_in), file=f)
1507  self.callamber("cpptraj -i %s-cpptraj-temp.in" % self.name)
1508  temps = np.array([float(line.split()[1]) for line in list(open("temperature.txt").readlines())[1:]])
1509  dof = 3*self.natoms
1510  kinetics = 4.184 * kb_kcal * dof * temps / 2.0
1511  print("Average temperature is %.2f, kinetic energy %.2f" % (np.mean(temps), np.mean(kinetics)))
1512  # os.unlink("temperature.txt")
1513  return kinetics
1514 
1515  def energy_dipole(self):
1516  """ Computes the energy and dipole using AMBER over a trajectory. """
1517  Result = self.evaluate_cpptraj(potential=True)
1518  return np.hstack((Result["Potentials"].reshape(-1,1), Result["Dips"]))
1519 
1520  def interaction_energy(self, fraga, fragb):
1521 
1522  """ Calculate the interaction energy for two fragments. """
1523 
1524  self.A = AMBER(name="A", mol=self.mol.atom_select(fraga), amberhome=self.amberhome, leapcmd=self.leapcmd, mol2=self.mol2, frcmod=self.frcmod, reqpdb=False)
1525  self.B = AMBER(name="B", mol=self.mol.atom_select(fragb), amberhome=self.amberhome, leapcmd=self.leapcmd, mol2=self.mol2, frcmod=self.frcmod, reqpdb=False)
1526 
1527  # Interaction energy needs to be in kcal/mol.
1528  return (self.energy() - self.A.energy() - self.B.energy()) / 4.184
1529 
1530  def molecular_dynamics(self, nsteps, timestep, temperature=None, pressure=None, nequil=0, nsave=1000, minimize=True, anisotropic=False, threads=1, verbose=False, **kwargs):
1531 
1532  """
1533  Method for running a molecular dynamics simulation.
1535  Required arguments:
1536  nsteps = (int) Number of total time steps
1537  timestep = (float) Time step in FEMTOSECONDS
1538  temperature = (float) Temperature control (Kelvin)
1539  pressure = (float) Pressure control (atmospheres)
1540  nequil = (int) Number of additional time steps at the beginning for equilibration
1541  nsave = (int) Step interval for saving and printing data
1542  minimize = (bool) Perform an energy minimization prior to dynamics
1543  threads = (int) Specify how many OpenMP threads to use
1544 
1545  Returns simulation data:
1546  Rhos = (array) Density in kilogram m^-3
1547  Potentials = (array) Potential energies
1548  Kinetics = (array) Kinetic energies
1549  Volumes = (array) Box volumes
1550  Dips = (3xN array) Dipole moments
1551  EComps = (dict) Energy components
1552  """
1553 
1554  if anisotropic:
1555  raise RuntimeError("Anisotropic barostat not implemented in AMBER")
1556  if threads>1:
1557  raise RuntimeError("Multiple threads not implemented in AMBER - for fast runs, use pmemd.cuda")
1558 
1559  md_command = "pmemd.cuda" if (self.have_pmemd_cuda and self.pbc) else "sander"
1560 
1561  if minimize:
1562  # LPW 2018-02-11 Todo: Implement a separate function for minimization that works for
1563  # RMSD / vibrations as well.
1564  if verbose: printcool("Minimizing the energy", color=0)
1565  write_mdin('min', '%s-min.mdin' % self.name, pbc=self.pbc, mdin_orig=self.mdin)
1566  self.leap(read_prmtop=False, count_mols=False, delcheck=True)
1567  self.callamber("sander -i %s-min.mdin -o %s-min.mdout -p %s.prmtop -c %s.inpcrd -r %s-min.restrt -x %s-min.netcdf -inf %s-min.mdinfo -O" %
1568  (self.name, self.name, self.name, self.name, self.name, self.name, self.name), print_command=True)
1569  nextrst = "%s-min.restrt" % self.name
1571  else:
1572  self.leap(read_prmtop=False, count_mols=False, delcheck=True)
1573  nextrst = "%s.inpcrd" % self.name
1574 
1575  # Run equilibration.
1576  if nequil > 0:
1577  write_mdin('eq', '%s-eq.mdin' % self.name, nsteps=nequil, timestep=timestep/1000, nsave=nsave, pbc=self.pbc,
1578  temperature=temperature, pressure=pressure, mdin_orig=self.mdin)
1579  if verbose: printcool("Running equilibration dynamics", color=0)
1580  self.callamber("%s -i %s-eq.mdin -o %s-eq.mdout -p %s.prmtop -c %s -r %s-eq.restrt -x %s-eq.netcdf -inf %s-eq.mdinfo -O" %
1581  (md_command, self.name, self.name, self.name, nextrst, self.name, self.name, self.name), print_command=True)
1582  nextrst = "%s-eq.restrt" % self.name
1583 
1584 
1585  # Run production.
1586  if verbose: printcool("Running production dynamics", color=0)
1587  write_mdin('md', '%s-md.mdin' % self.name, nsteps=nsteps, timestep=timestep/1000, nsave=nsave, pbc=self.pbc,
1588  temperature=temperature, pressure=pressure, mdin_orig=self.mdin)
1589  self.callamber("%s -i %s-md.mdin -o %s-md.mdout -p %s.prmtop -c %s -r %s-md.restrt -x %s-md.netcdf -inf %s-md.mdinfo -O" %
1590  (md_command, self.name, self.name, self.name, nextrst, self.name, self.name, self.name), print_command=True)
1591  nextrst = "%s-md.restrt" % self.name
1592  self.trajectory = '%s-md.netcdf' % self.name
1593 
1594  prop_return = self.evaluate_cpptraj(leap=False, potential=True)
1595  prop_return["Kinetics"] = self.kineticE_cpptraj()
1596  ecomp = OrderedDict()
1597  ecomp["Potential Energy"] = prop_return["Potentials"].copy()
1598  ecomp["Kinetic Energy"] = prop_return["Kinetics"].copy()
1599  ecomp["Total Energy"] = prop_return["Potentials"] + prop_return["Kinetics"]
1600  prop_return["Ecomps"] = ecomp
1601 
1602  return prop_return
1603 
1604 
1605  def normal_modes(self, shot=0, optimize=True):
1606  self.leap(read_prmtop=False, count_mols=False, delcheck=True)
1607  if optimize:
1608  # Copied from AMBER tests folder.
1609  opt_temp = """ Newton-Raphson minimization
1610  &data
1611  ntrun = 4, nsave=20, ndiag=2,
1612  nprint=1, ioseen=0,
1613  drms = 0.000001, maxcyc=4000, bdwnhl=0.1, dfpred = 0.1,
1614  scnb=2.0, scee=1.2, idiel=1,
1615  /
1616 """
1617  with wopen("%s-nr.in" % self.name) as f: print(opt_temp.format(), file=f)
1618  self.callamber("nmode -O -i %s-nr.in -c %s.inpcrd -p %s.prmtop -r %s.rst -o %s-nr.out" % (self.name, self.name, self.name, self.name, self.name))
1619  nmode_temp = """ normal modes
1620  &data
1621  ntrun = 1, nsave=20, ndiag=2,
1622  nprint=1, ioseen=0,
1623  drms = 0.0001, maxcyc=1, bdwnhl=1.1, dfpred = 0.1,
1624  scnb=2.0, scee=1.2, idiel=1,
1625  nvect={nvect}, eta=0.9, ivform=2,
1626  /
1627 """
1628  with wopen("%s-nmode.in" % self.name) as f: print(nmode_temp.format(nvect=3*self.mol.na), file=f)
1629  self.callamber("nmode -O -i %s-nmode.in -c %s.rst -p %s.prmtop -v %s-vecs.out -o %s-vibs.out" % (self.name, self.name, self.name, self.name, self.name))
1630  # My attempt at parsing AMBER frequency output.
1631  vmode = 0
1632  ln = 0
1633  freqs = []
1634  modeA = []
1635  modeB = []
1636  modeC = []
1637  vecs = []
1638  for line in open("%s-vecs.out" % self.name).readlines():
1639  if line.strip() == "$FREQ":
1640  vmode = 1
1641  elif line.strip().startswith("$"):
1642  vmode = 0
1643  elif vmode == 1:
1644  # We are in the vibrational block now.
1645  if ln == 0: pass
1646  elif ln == 1:
1647  freqs += [float(i) for i in line.split()]
1648  else:
1649  modeA.append([float(i) for i in line.split()[0:3]])
1650  modeB.append([float(i) for i in line.split()[3:6]])
1651  modeC.append([float(i) for i in line.split()[6:9]])
1652  if len(modeA) == self.mol.na:
1653  vecs.append(modeA)
1654  vecs.append(modeB)
1655  vecs.append(modeC)
1656  modeA = []
1657  modeB = []
1658  modeC = []
1659  ln = -1
1660  ln += 1
1661  calc_eigvals = np.array(freqs)
1662  calc_eigvecs = np.array(vecs)
1663  # Sort by frequency absolute value and discard the six that are closest to zero
1664  calc_eigvecs = calc_eigvecs[np.argsort(np.abs(calc_eigvals))][6:]
1665  calc_eigvals = calc_eigvals[np.argsort(np.abs(calc_eigvals))][6:]
1666  # Sort again by frequency
1667  calc_eigvecs = calc_eigvecs[np.argsort(calc_eigvals)]
1668  calc_eigvals = calc_eigvals[np.argsort(calc_eigvals)]
1669  os.system("rm -rf *.xyz_* *.[0-9][0-9][0-9]")
1670  return calc_eigvals, calc_eigvecs
1671 
1672  def multipole_moments(self, shot=0, optimize=True, polarizability=False):
1673 
1674  logger.error('Multipole moments are not yet implemented in AMBER interface')
1675  raise NotImplementedError
1676 
1677  """ Return the multipole moments of the 1st snapshot in Debye and Buckingham units. """
1678  #=================
1679  # Below is copied from tinkerio.py
1680  #=================
1681  # This line actually runs TINKER
1682  # if optimize:
1683  # self.optimize(shot, crit=1e-6)
1684  # o = self.calltinker("analyze %s.xyz_2 M" % (self.name))
1685  # else:
1686  # self.mol[shot].write('%s.xyz' % self.name, ftype="tinker")
1687  # o = self.calltinker("analyze %s.xyz M" % (self.name))
1688  # # Read the TINKER output.
1689  # qn = -1
1690  # ln = 0
1691  # for line in o:
1692  # s = line.split()
1693  # if "Dipole X,Y,Z-Components" in line:
1694  # dipole_dict = OrderedDict(zip(['x','y','z'], [float(i) for i in s[-3:]]))
1695  # elif "Quadrupole Moment Tensor" in line:
1696  # qn = ln
1697  # quadrupole_dict = OrderedDict([('xx',float(s[-3]))])
1698  # elif qn > 0 and ln == qn + 1:
1699  # quadrupole_dict['xy'] = float(s[-3])
1700  # quadrupole_dict['yy'] = float(s[-2])
1701  # elif qn > 0 and ln == qn + 2:
1702  # quadrupole_dict['xz'] = float(s[-3])
1703  # quadrupole_dict['yz'] = float(s[-2])
1704  # quadrupole_dict['zz'] = float(s[-1])
1705  # ln += 1
1706  # calc_moments = OrderedDict([('dipole', dipole_dict), ('quadrupole', quadrupole_dict)])
1707  # if polarizability:
1708  # if optimize:
1709  # o = self.calltinker("polarize %s.xyz_2" % (self.name))
1710  # else:
1711  # o = self.calltinker("polarize %s.xyz" % (self.name))
1712  # # Read the TINKER output.
1713  # pn = -1
1714  # ln = 0
1715  # polarizability_dict = OrderedDict()
1716  # for line in o:
1717  # s = line.split()
1718  # if "Total Polarizability Tensor" in line:
1719  # pn = ln
1720  # elif pn > 0 and ln == pn + 2:
1721  # polarizability_dict['xx'] = float(s[-3])
1722  # polarizability_dict['yx'] = float(s[-2])
1723  # polarizability_dict['zx'] = float(s[-1])
1724  # elif pn > 0 and ln == pn + 3:
1725  # polarizability_dict['xy'] = float(s[-3])
1726  # polarizability_dict['yy'] = float(s[-2])
1727  # polarizability_dict['zy'] = float(s[-1])
1728  # elif pn > 0 and ln == pn + 4:
1729  # polarizability_dict['xz'] = float(s[-3])
1730  # polarizability_dict['yz'] = float(s[-2])
1731  # polarizability_dict['zz'] = float(s[-1])
1732  # ln += 1
1733  # calc_moments['polarizability'] = polarizability_dict
1734  # os.system("rm -rf *.xyz_* *.[0-9][0-9][0-9]")
1735  # return calc_moments
1736 
1737  def optimize(self, shot=0, method="newton", crit=1e-4):
1738 
1739  """ Optimize the geometry and align the optimized geometry to the starting geometry. """
1740 
1741  logger.error('Geometry optimizations are not yet implemented in AMBER interface')
1742  raise NotImplementedError
1743 
1744  # # Code from tinkerio.py, reference for later implementation.
1745  # if os.path.exists('%s.xyz_2' % self.name):
1746  # os.unlink('%s.xyz_2' % self.name)
1747  # self.mol[shot].write('%s.xyz' % self.name, ftype="tinker")
1748  # if method == "newton":
1749  # if self.rigid: optprog = "optrigid"
1750  # else: optprog = "optimize"
1751  # elif method == "bfgs":
1752  # if self.rigid: optprog = "minrigid"
1753  # else: optprog = "minimize"
1754  # o = self.calltinker("%s %s.xyz %f" % (optprog, self.name, crit))
1755  # # Silently align the optimized geometry.
1756  # M12 = Molecule("%s.xyz" % self.name, ftype="tinker") + Molecule("%s.xyz_2" % self.name, ftype="tinker")
1757  # if not self.pbc:
1758  # M12.align(center=False)
1759  # M12[1].write("%s.xyz_2" % self.name, ftype="tinker")
1760  # rmsd = M12.ref_rmsd(0)[1]
1761  # cnvgd = 0
1762  # mode = 0
1763  # for line in o:
1764  # s = line.split()
1765  # if len(s) == 0: continue
1766  # if "Optimally Conditioned Variable Metric Optimization" in line: mode = 1
1767  # if "Limited Memory BFGS Quasi-Newton Optimization" in line: mode = 1
1768  # if mode == 1 and isint(s[0]): mode = 2
1769  # if mode == 2:
1770  # if isint(s[0]): E = float(s[1])
1771  # else: mode = 0
1772  # if "Normal Termination" in line:
1773  # cnvgd = 1
1774  # if not cnvgd:
1775  # for line in o:
1776  # logger.info(str(line) + '\n')
1777  # logger.info("The minimization did not converge in the geometry optimization - printout is above.\n")
1778  # return E, rmsd
1779 
1780  def energy_rmsd(self, shot=0, optimize=True):
1781 
1782  """ Calculate energy of the selected structure (optionally minimize and return the minimized energy and RMSD). In kcal/mol. """
1783 
1784  logger.error('Geometry optimization is not yet implemented in AMBER interface')
1785  raise NotImplementedError
1787  # # Below is TINKER code as reference for later implementation.
1788  # rmsd = 0.0
1789  # # This line actually runs TINKER
1790  # # xyzfnm = sysname+".xyz"
1791  # if optimize:
1792  # E_, rmsd = self.optimize(shot)
1793  # o = self.calltinker("analyze %s.xyz_2 E" % self.name)
1794  # #----
1795  # # Two equivalent ways to get the RMSD, here for reference.
1796  # #----
1797  # # M1 = Molecule("%s.xyz" % self.name, ftype="tinker")
1798  # # M2 = Molecule("%s.xyz_2" % self.name, ftype="tinker")
1799  # # M1 += M2
1800  # # rmsd = M1.ref_rmsd(0)[1]
1801  # #----
1802  # # oo = self.calltinker("superpose %s.xyz %s.xyz_2 1 y u n 0" % (self.name, self.name))
1803  # # for line in oo:
1804  # # if "Root Mean Square Distance" in line:
1805  # # rmsd = float(line.split()[-1])
1806  # #----
1807  # os.system("rm %s.xyz_2" % self.name)
1808  # else:
1809  # o = self.calltinker("analyze %s.xyz E" % self.name)
1810  # # Read the TINKER output.
1811  # E = None
1812  # for line in o:
1813  # if "Total Potential Energy" in line:
1814  # E = float(line.split()[-2].replace('D','e'))
1815  # if E is None:
1816  # logger.error("Total potential energy wasn't encountered when calling analyze!\n")
1817  # raise RuntimeError
1818  # if optimize and abs(E-E_) > 0.1:
1819  # warn_press_key("Energy from optimize and analyze aren't the same (%.3f vs. %.3f)" % (E, E_))
1820  # return E, rmsd
1821 
1822 class AbInitio_AMBER(AbInitio):
1823 
1824  """Subclass of Target for force and energy matching
1825  using AMBER."""
1826 
1827  def __init__(self,options,tgt_opts,forcefield):
1828 
1829  self.set_option(tgt_opts,'coords',default="all.mdcrd")
1831  self.set_option(tgt_opts,'pdb',default="conf.pdb")
1832 
1833  self.set_option(options, 'amberhome')
1834 
1835  self.set_option(tgt_opts, 'amber_leapcmd', 'leapcmd')
1836 
1837  self.engine_ = AMBER
1838 
1839  super(AbInitio_AMBER,self).__init__(options,tgt_opts,forcefield)
1840 
1841 class Interaction_AMBER(Interaction):
1842 
1843  """Subclass of Target for calculating and matching ineraction energies
1844  using AMBER. """
1845 
1846  def __init__(self,options,tgt_opts,forcefield):
1847 
1848  self.set_option(tgt_opts, 'coords')
1849 
1850  self.set_option(tgt_opts, 'pdb')
1851 
1852  self.set_option(options, 'amberhome')
1853 
1854  self.set_option(tgt_opts, 'amber_leapcmd', 'leapcmd')
1855 
1856  self.engine_ = AMBER
1857 
1858  super(Interaction_AMBER,self).__init__(options,tgt_opts,forcefield)
1859 
1860 class Vibration_AMBER(Vibration):
1861 
1862  """Subclass of Target for calculating and matching vibrational modes using AMBER. """
1863 
1864  def __init__(self,options,tgt_opts,forcefield):
1865 
1866  self.set_option(tgt_opts, 'coords')
1867 
1868  self.set_option(tgt_opts, 'pdb')
1869 
1870  self.set_option(options, 'amberhome')
1871 
1872  self.set_option(tgt_opts, 'amber_leapcmd', 'leapcmd')
1873 
1874  self.engine_ = AMBER
1875 
1876  super(Vibration_AMBER,self).__init__(options,tgt_opts,forcefield)
1878 class Liquid_AMBER(Liquid):
1879 
1880  """Subclass of Target for calculating and matching liquid properties using AMBER. """
1881 
1882  def __init__(self,options,tgt_opts,forcefield):
1883  # Name of the liquid PDB file.
1884  self.set_option(tgt_opts,'liquid_coords',default='liquid.pdb',forceprint=True)
1885  # Name of the gas PDB file.
1886  self.set_option(tgt_opts,'gas_coords',default='gas.pdb',forceprint=True)
1887  # Class for creating engine object.
1888  self.engine_ = AMBER
1889 
1890  self.set_option(options, 'amberhome')
1891  # Set some options for the polarization correction calculation.
1892  self.gas_engine_args = {}
1893  # Scripts to be copied from the ForceBalance installation directory.
1894  self.scripts = []
1895  # Send back last frame of production trajectory.
1896  self.extra_output = ['liquid-md.restrt']
1898  self.engine_ = AMBER
1899  # Name of the engine to pass to npt.py.
1900  self.engname = "amber"
1901  # Command prefix.
1902  self.nptpfx = ""
1903  # Extra files to be linked into the temp-directory.
1904  self.nptfiles = ['%s.leap' % os.path.splitext(f)[0] for f in [self.liquid_coords, self.gas_coords]]
1905 
1906  super(Liquid_AMBER,self).__init__(options,tgt_opts,forcefield)
1907  for f in [self.liquid_coords, self.gas_coords]:
1908  if os.path.exists(os.path.join(self.root, self.tgtdir, '%s.mdin' % os.path.splitext(f)[0])):
1909  self.nptfiles.append('%s.mdin' % os.path.splitext(f)[0])
1910  if f == self.gas_coords:
1911  self.gas_engine_args['mdin'] = os.path.splitext(f)[0]
1912  for mol2 in listfiles(None, 'mol2', err=False, dnm=os.path.join(self.root, self.tgtdir)):
1913  self.nptfiles.append(mol2)
1914  for frcmod in listfiles(None, 'frcmod', err=False, dnm=os.path.join(self.root, self.tgtdir)):
1915  self.nptfiles.append(frcmod)
1916  for i in self.nptfiles:
1917  if not os.path.exists(os.path.join(self.root, self.tgtdir, i)):
1918  logger.error('Please provide %s; it is needed to proceed.\n' % i)
1919  raise RuntimeError
1920  # Send back the trajectory file.
1921  if self.save_traj > 0:
1922  self.extra_output += ['liquid-md.netcdf']
1923  # These functions need to be called after self.nptfiles is populated
1924  self.post_init(options)
def Split(self, line)
Definition: amberio.py:570
def getAtomTypes(self)
Return the list of the AMBER atom types.
Definition: amberio.py:874
def getIfPert(self)
Return True if the system was build with the perturbation parameters)
Definition: amberio.py:812
def __init__(self, inFilename)
Create a PrmtopLoader object from an AMBER prmtop file.
Definition: amberio.py:706
def __init__(self, name="amber", kwargs)
Definition: amberio.py:1130
def readsrc(self, kwargs)
Called by init ; read files from the source directory.
Definition: amberio.py:1172
Subclass of Target for calculating and matching ineraction energies using AMBER.
Definition: amberio.py:1894
def write_leap(fnm, mol2=[], frcmod=[], pdb=None, prefix='amber', spath=[], delcheck=False)
Parse and edit an AMBER LEaP input file.
Definition: amberio.py:67
def __init__(self, options, tgt_opts, forcefield)
Definition: amberio.py:1877
Vibrational mode fitting module.
def multipole_moments(self, shot=0, optimize=True, polarizability=False)
Definition: amberio.py:1719
def interaction_energy(self, fraga, fragb)
Calculate the interaction energy for two fragments.
Definition: amberio.py:1567
Engine for carrying out general purpose AMBER calculations.
Definition: amberio.py:1127
def energy(self)
Computes the energy using AMBER over a trajectory.
Definition: amberio.py:1459
Nifty functions, intended to be imported by any module within ForceBalance.
Ab-initio fitting module (energies, forces, resp).
def is_mol2_atom(line)
Definition: amberio.py:59
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.
Definition: amberio.py:1598
def getResidueLabel(self, iAtom=None, iRes=None)
Return residue label for iAtom OR iRes.
Definition: amberio.py:884
def energy_rmsd(self, shot=0, optimize=True)
Calculate energy of the selected structure (optionally minimize and return the minimized energy and R...
Definition: amberio.py:1830
def getExcludedAtoms(self)
Return list of lists, giving all pairs of atoms that should have no non-bond interactions.
Definition: amberio.py:1064
def _getResiduePointer(self, iAtom)
Definition: amberio.py:894
def normal_modes(self, shot=0, optimize=True)
Definition: amberio.py:1652
def getNumAtoms(self)
Return the number of atoms in the system.
Definition: amberio.py:792
def kineticE_cpptraj(self, leap=False, traj_fnm=None)
Evaluate the kinetic energy of each frame in a trajectory using cpptraj.
Definition: amberio.py:1534
def energy_force_one(self, shot)
Computes the energy and force using AMBER for one snapshot.
Definition: amberio.py:1453
def getBoxBetaAndDimensions(self)
Return periodic boundary box beta angle and dimensions.
Definition: amberio.py:1118
def LinkFile(src, dest, nosrcok=False)
Definition: nifty.py:1313
def getAtomName(self, iAtom)
Return the atom name for iAtom.
Definition: amberio.py:847
def _getAtomTypeIndexes(self)
Definition: amberio.py:856
def evaluate_cpptraj(self, leap=True, traj_fnm=None, potential=False)
Use cpptraj to evaluate properties over a trajectory file.
Definition: amberio.py:1470
atom
The atom numbers in the interaction (stored in the parser)
Definition: amberio.py:564
def isint(word)
ONLY matches integers! If you have a decimal point? None shall pass!
Definition: molecule.py:401
def which(fnm)
Definition: nifty.py:1362
def build_pid(self, pfld)
Returns the parameter type (e.g.
Definition: amberio.py:592
def getDihedrals(self)
Return list of atom quads, K, phase and periodicity for each dihedral angle.
Definition: amberio.py:1013
def getIfBox(self)
Return True if the system was build with periodic boundary conditions (PBC)
Definition: amberio.py:802
def energy_force(self)
Computes the energy and force using AMBER over a trajectory.
Definition: amberio.py:1464
pdict
The parameter dictionary (defined in this file)
Definition: amberio.py:562
def write_mdin(calctype, fout=None, nsteps=None, timestep=None, nsave=None, pbc=False, temperature=None, pressure=None, mdin_orig=None)
Write an AMBER .mdin file to carry out a calculation using sander or pmemd.cuda.
Definition: amberio.py:316
def feed(self, line)
Definition: amberio.py:516
def getResidueNumber(self, iAtom)
Return iAtom&#39;s residue number.
Definition: amberio.py:879
def evaluate_sander(self, leap=True, traj_fnm=None, snapshots=None)
Utility function for computing energy and forces using AMBER.
Definition: amberio.py:1374
dihe
Whether we&#39;re inside the dihedral section.
Definition: amberio.py:566
Finite state machine for parsing Mol2 force field file.
Definition: amberio.py:500
def parse_amber_namelist(fin)
Parse a file containing an AMBER namelist (only significantly tested for sander input).
Definition: amberio.py:206
def getAtomNames(self)
Return the list of the system atom names.
Definition: amberio.py:853
Subclass of Target for calculating and matching vibrational modes using AMBER.
Definition: amberio.py:1913
def leap(self, read_prmtop=False, count_mols=False, name=None, delcheck=False)
Definition: amberio.py:1308
def getNumTypes(self)
Return the number of AMBER atoms types in the system.
Definition: amberio.py:797
def get_charges(self)
Definition: amberio.py:1356
engine_
Coordinate file.
Definition: amberio.py:1907
def getBondsWithH(self)
Return list of bonded atom pairs, K, and Rmin for each bond with a hydrogen.
Definition: amberio.py:961
Parsed AMBER prmtop file.
Definition: amberio.py:696
def _getPointerValue(self, pointerLabel)
Return pointer value given pointer label.
Definition: amberio.py:786
def getBondsNoH(self)
Return list of bonded atom pairs, K, and Rmin for each bond with no hydrogen.
Definition: amberio.py:973
def warn_press_key(warning, timeout=10)
Definition: nifty.py:1599
def __init__(self, options, tgt_opts, forcefield)
Definition: amberio.py:1897
pdict
The parameter dictionary (defined in this file)
Definition: amberio.py:506
def getCharges(self)
Return a list of atomic charges in the system.
Definition: amberio.py:832
def prepare(self, pbc=False, kwargs)
Called by init ; prepare the temp directory and figure out the topology.
Definition: amberio.py:1238
engine_
Coordinate file.
Definition: amberio.py:1887
def printcool(text, sym="#", bold=False, color=2, ansi=None, bottom='-', minwidth=50, center=True, sym2="=")
Cool-looking printout for slick formatting of output.
Definition: nifty.py:321
atomnames
The mol2 file provides a list of atom names.
Definition: amberio.py:510
def callamber(self, command, stdin=None, print_to_screen=False, print_command=False, kwargs)
Call AMBER; prepend amberhome to calling the appropriate ambertools program.
Definition: amberio.py:1224
Interaction energy fitting module.
def onefile(fnm=None, ext=None, err=False)
Definition: nifty.py:1119
def feed(self, line)
Definition: amberio.py:603
def listfiles(fnms=None, ext=None, err=False, dnm=None)
Definition: nifty.py:1173
Finite state machine for parsing FrcMod force field file.
Definition: amberio.py:556
def Whites(self, line)
Definition: amberio.py:573
def warn_once(warning, warnhash=None)
Prints a warning but will only do so once in a given run.
Definition: nifty.py:1611
def getGBParms(self, symbls=None)
Return list giving GB params, Radius and screening factor.
Definition: amberio.py:1088
def getIfCap(self)
Return True if the system was build with the cap option)
Definition: amberio.py:807
def optimize(self, shot=0, method="newton", crit=1e-4)
Optimize the geometry and align the optimized geometry to the starting geometry.
Definition: amberio.py:1786
section
The section that we&#39;re in.
Definition: amberio.py:512
adict
The frcmod file never has any atoms in it.
Definition: amberio.py:568
def get14Interactions(self)
Return list of atom pairs, chargeProduct, rMin and epsilon for each 1-4 interaction.
Definition: amberio.py:1044
def isfloat(word)
Matches ANY number; it can be a decimal, scientific notation, integer, or what have you...
Definition: molecule.py:406
def splitComment(mystr, debug=False)
Remove the comment from a line in an AMBER namelist.
Definition: amberio.py:150
Subclass of Target for calculating and matching liquid properties using AMBER.
Definition: amberio.py:1932
def wopen(dest, binary=False)
If trying to write to a symbolic link, remove it first.
Definition: nifty.py:1304
def _getFormat(self, flag=None)
Definition: amberio.py:744
def __init__(self, fnm)
Definition: amberio.py:502
atom
The atom numbers in the interaction (stored in the parser)
Definition: amberio.py:508
def getNonbondTerms(self)
Return list of all rVdw, epsilon pairs for each atom.
Definition: amberio.py:912
Matching of liquid bulk properties.
Subclass of Target for force and energy matching using AMBER.
Definition: amberio.py:1874
def _getBonds(self, bondPointers)
Definition: amberio.py:940
def energy_dipole(self)
Computes the energy and dipole using AMBER over a trajectory.
Definition: amberio.py:1561
def getMasses(self)
Return a list of atomic masses in the system.
Definition: amberio.py:817
def getAtomType(self, iAtom)
Return the AMBER atom type for iAtom.
Definition: amberio.py:868
def getAngles(self)
Return list of atom triplets, K, and ThetaMin for each bond angle.
Definition: amberio.py:984