ForceBalance API  1.3
Automated optimization of force fields and empirical potentials
lipid.py
Go to the documentation of this file.
1 """ @package forcebalance.lipid Matching of lipid bulk properties. Under development.
2 
3 author Lee-Ping Wang
4 @date 04/2012
5 """
6 from __future__ import division
7 from __future__ import print_function
8 
9 from builtins import str
10 from builtins import zip
11 from builtins import map
12 from builtins import range
13 import abc
14 import os
15 import shutil
17 from forcebalance.nifty import *
18 from forcebalance.nifty import _exec
19 from forcebalance.target import Target
20 import numpy as np
21 from forcebalance.molecule import Molecule
22 from re import match, sub
23 import subprocess
24 from subprocess import PIPE
25 try:
26  from lxml import etree
27 except: pass
28 from pymbar import pymbar
29 import itertools
30 from collections import defaultdict, namedtuple, OrderedDict
31 import csv
32 import copy
33 
34 from forcebalance.output import getLogger
35 logger = getLogger(__name__)
36 
37 def weight_info(W, PT, N_k, verbose=True):
38  C = []
39  N = 0
40  W += 1.0e-300
41  I = np.exp(-1*np.sum((W*np.log(W))))
42  for ns in N_k:
43  C.append(sum(W[N:N+ns]))
44  N += ns
45  C = np.array(C)
46  if verbose:
47  logger.info("MBAR Results for Phase Point %s, Box, Contributions:\n" % str(PT))
48  logger.info(str(C) + '\n')
49  logger.info("InfoContent: % .2f snapshots (%.2f %%)\n" % (I, 100*I/len(W)))
50  return C
51 
52 # NPT_Trajectory = namedtuple('NPT_Trajectory', ['fnm', 'Rhos', 'pVs', 'Energies', 'Grads', 'mEnergies', 'mGrads', 'Rho_errs', 'Hvap_errs'])
53 
54 class Lipid(Target):
55 
56  """ Subclass of Target for lipid property matching."""
57 
58  def __init__(self,options,tgt_opts,forcefield):
59  # Initialize base class
60  super(Lipid,self).__init__(options,tgt_opts,forcefield)
61  # Weight of the density
62  self.set_option(tgt_opts,'w_rho',forceprint=True)
63  # Weight of the thermal expansion coefficient
64  self.set_option(tgt_opts,'w_alpha',forceprint=True)
65  # Weight of the isothermal compressibility
66  self.set_option(tgt_opts,'w_kappa',forceprint=True)
67  # Weight of the isobaric heat capacity
68  self.set_option(tgt_opts,'w_cp',forceprint=True)
69  # Weight of the dielectric constant
70  self.set_option(tgt_opts,'w_eps0',forceprint=True)
71  # Weight of the area per lipid
72  self.set_option(tgt_opts,'w_al',forceprint=True)
73  # Weight of the bilayer isothermal compressibility
74  self.set_option(tgt_opts,'w_lkappa',forceprint=True)
75  # Weight of the deuterium order parameter
76  self.set_option(tgt_opts,'w_scd',forceprint=True)
77  # Normalize the property contributions to the objective function
78  self.set_option(tgt_opts,'w_normalize',forceprint=True)
79  # Optionally pause on the zeroth step
80  self.set_option(tgt_opts,'manual')
81  # Number of time steps in the lipid "equilibration" run
82  self.set_option(tgt_opts,'lipid_eq_steps',forceprint=True)
83  # Number of time steps in the lipid "production" run
84  self.set_option(tgt_opts,'lipid_md_steps',forceprint=True)
85  # Number of time steps in the gas "equilibration" run
86  self.set_option(tgt_opts,'gas_eq_steps',forceprint=False)
87  # Number of time steps in the gas "production" run
88  self.set_option(tgt_opts,'gas_md_steps',forceprint=False)
89  # Cutoff for nonbonded interactions in the liquid
90  if tgt_opts['nonbonded_cutoff'] is not None:
91  self.set_option(tgt_opts,'nonbonded_cutoff')
92  # Cutoff for vdW interactions if different from other nonbonded interactions
93  if tgt_opts['vdw_cutoff'] is not None:
94  self.set_option(tgt_opts,'vdw_cutoff')
95  # Time step length (in fs) for the lipid production run
96  self.set_option(tgt_opts,'lipid_timestep',forceprint=True)
97  # Time interval (in ps) for writing coordinates
98  self.set_option(tgt_opts,'lipid_interval',forceprint=True)
99  # Time step length (in fs) for the gas production run
100  self.set_option(tgt_opts,'gas_timestep',forceprint=True)
101  # Time interval (in ps) for writing coordinates
102  self.set_option(tgt_opts,'gas_interval',forceprint=True)
103  # Minimize the energy prior to running any dynamics
104  self.set_option(tgt_opts,'minimize_energy',forceprint=True)
105  # Isolated dipole (debye) for analytic self-polarization correction.
106  self.set_option(tgt_opts,'self_pol_mu0',forceprint=True)
107  # Molecular polarizability (ang**3) for analytic self-polarization correction.
108  self.set_option(tgt_opts,'self_pol_alpha',forceprint=True)
109  # Set up the simulation object for self-polarization correction.
110  self.do_self_pol = (self.self_pol_mu0 > 0.0 and self.self_pol_alpha > 0.0)
111  # Enable anisotropic periodic box
112  self.set_option(tgt_opts,'anisotropic_box',forceprint=True)
113  # Whether to save trajectories (0 = never, 1 = delete after good step, 2 = keep all)
114  self.set_option(tgt_opts,'save_traj')
115 
116  #======================================#
117  # Variables which are set here #
118  #======================================#
119 
121  self.loop_over_snapshots = False
122  # List of trajectory files that may be deleted if self.save_traj == 1.
123  self.last_traj = []
124  # Extra files to be copied back at the end of a run.
125  self.extra_output = []
126  # Read the reference data
127  self.read_data()
128  # Read in lipid starting coordinates.
129  if 'n_ic' in self.RefData:
130  # Linked IC folder into the temp-directory.
131  self.nptfiles += ["IC"]
132  # Store IC frames in a dictionary.
133  self.lipid_mols = OrderedDict()
134  self.lipid_mols_new = OrderedDict()
135  for pt in self.PhasePoints:
136  pt_label = "IC/%sK-%s%s" % (pt[0], pt[1], pt[2])
137  if not os.path.exists(os.path.join(self.root, self.tgtdir, pt_label, self.lipid_coords)):
138  raise RuntimeError("Initial condition files don't exist; please provide IC directory")
139  # Create molecule object for each IC.
140  all_ic = Molecule(os.path.join(self.root, self.tgtdir, pt_label, self.lipid_coords))
141  self.lipid_mols[pt] = []
142  n_uniq_ic = int(self.RefData['n_ic'][pt])
143  if n_uniq_ic > len(all_ic):
144  raise RuntimeError("Number of frames in initial conditions .gro file is less than the number of parallel simulations requested in data.csv")
145  # Index ICs by pressure and temperature in a dictionary.
146  for ic in range(n_uniq_ic):
147  self.lipid_mols[pt].append(all_ic[ic])
148  else:
149  # Read in lipid starting coordinates.
150  if not os.path.exists(os.path.join(self.root, self.tgtdir, self.lipid_coords)):
151  logger.error("%s doesn't exist; please provide lipid_coords option\n" % self.lipid_coords)
152  raise RuntimeError
153  self.lipid_mol = Molecule(os.path.join(self.root, self.tgtdir, self.lipid_coords), toppbc=True)
154  # Extra files to be linked into the temp-directory.
155  self.nptfiles += [self.lipid_coords]
156  # Scripts to be copied from the ForceBalance installation directory.
157  self.scripts += ['npt_lipid.py']
158  # Prepare the temporary directory.
160  # Build keyword dictionary to pass to engine.
161  if self.do_self_pol:
162  self.gas_engine_args.update(self.OptionDict)
163  self.gas_engine_args.update(options)
164  del self.gas_engine_args['name']
165  # Create engine object for gas molecule to do the polarization correction.
166  self.gas_engine = self.engine_(target=self, mol=self.gas_mol, name="selfpol", **self.gas_engine_args)
167  # Don't read indicate.log when calling meta_indicate()
168  self.read_indicate = False
169  self.write_indicate = False
170  # Don't read objective.p when calling meta_get()
171  self.read_objective = False
172  #======================================#
173  # UNDER DEVELOPMENT #
174  #======================================#
175  # Put stuff here that I'm not sure about. :)
176  np.set_printoptions(precision=4, linewidth=100)
177  np.seterr(under='ignore')
178 
179  self.SavedMVal = {}
180 
181  self.SavedTraj = defaultdict(dict)
182 
183  self.MBarEnergy = defaultdict(lambda:defaultdict(dict))
184 
186  """ Prepare the temporary directory by copying in important files. """
187  abstempdir = os.path.join(self.root,self.tempdir)
188  for f in self.nptfiles:
189  LinkFile(os.path.join(self.root, self.tgtdir, f), os.path.join(abstempdir, f))
190  for f in self.scripts:
191  LinkFile(os.path.join(os.path.split(__file__)[0],"data",f),os.path.join(abstempdir,f))
192 
193  def read_data(self):
194  # Read the 'data.csv' file. The file should contain guidelines.
195  with open(os.path.join(self.tgtdir,'data.csv'),'rU') as f: R0 = list(csv.reader(f))
196  # All comments are erased.
197  R1 = [[sub('#.*$','',word) for word in line] for line in R0 if len(line[0]) > 0 and line[0][0] != "#"]
198  # All empty lines are deleted and words are converted to lowercase.
199  R = [[wrd.lower() for wrd in line] for line in R1 if any([len(wrd) for wrd in line]) > 0]
200  global_opts = OrderedDict()
201  found_headings = False
202  known_vars = ['mbar','rho','hvap','alpha','kappa','cp','eps0','cvib_intra',
203  'cvib_inter','cni','devib_intra','devib_inter', 'al', 'scd', 'n_ic', 'lkappa']
204  self.RefData = OrderedDict()
205  for line in R:
206  if line[0] == "global":
207  # Global options are mainly denominators for the different observables.
208  if isfloat(line[2]):
209  global_opts[line[1]] = float(line[2])
210  elif line[2].lower() == 'false':
211  global_opts[line[1]] = False
212  elif line[2].lower() == 'true':
213  global_opts[line[1]] = True
214  elif not found_headings:
215  found_headings = True
216  headings = line
217  if len(set(headings)) != len(headings):
218  logger.error('Column headings in data.csv must be unique\n')
219  raise RuntimeError
220  if 'p' not in headings:
221  logger.error('There must be a pressure column heading labeled by "p" in data.csv\n')
222  raise RuntimeError
223  if 't' not in headings:
224  logger.error('There must be a temperature column heading labeled by "t" in data.csv\n')
225  raise RuntimeError
226  elif found_headings:
227  try:
228  # Temperatures are in kelvin.
229  t = [float(val) for head, val in zip(headings,line) if head == 't'][0]
230  # For convenience, users may input the pressure in atmosphere or bar.
231  pval = [float(val.split()[0]) for head, val in zip(headings,line) if head == 'p'][0]
232  punit = [val.split()[1] if len(val.split()) >= 1 else "atm" for head, val in zip(headings,line) if head == 'p'][0]
233  unrec = set([punit]).difference(['atm','bar'])
234  if len(unrec) > 0:
235  logger.error('The pressure unit %s is not recognized, please use bar or atm\n' % unrec[0])
236  raise RuntimeError
237  # This line actually reads the reference data and inserts it into the RefData dictionary of dictionaries.
238  for head, val in zip(headings,line):
239  if head == 't' or head == 'p' : continue
240  if isfloat(val):
241  self.RefData.setdefault(head,OrderedDict([]))[(t,pval,punit)] = float(val.strip())
242  elif val.lower() == 'true':
243  self.RefData.setdefault(head,OrderedDict([]))[(t,pval,punit)] = True
244  elif val.lower() == 'false':
245  self.RefData.setdefault(head,OrderedDict([]))[(t,pval,punit)] = False
246  elif head == 'scd':
247  self.RefData.setdefault(head,OrderedDict([]))[(t,pval,punit)] = np.array(list(map(float, val.split())))
248  except:
249  logger.error(line + '\n')
250  logger.error('Encountered an error reading this line!\n')
251  raise RuntimeError
252  else:
253  logger.error(line + '\n')
254  logger.error('I did not recognize this line!\n')
255  raise RuntimeError
256  # Check the reference data table for validity.
257  default_denoms = defaultdict(int)
258  PhasePoints = None
259  RefData_copy = copy.deepcopy(self.RefData)
260  for head in self.RefData:
261  if head == 'n_ic':
262  continue
263  if head not in known_vars+[i+"_wt" for i in known_vars]:
264  # Only hard-coded properties may be recognized.
265  logger.error("The column heading %s is not recognized in data.csv\n" % head)
266  raise RuntimeError
267  if head in known_vars:
268  if head+"_wt" not in self.RefData:
269  # If the phase-point weights are not specified in the reference data file, initialize them all to one.
270  RefData_copy[head+"_wt"] = OrderedDict([(key, 1.0) for key in self.RefData[head]])
271  wts = np.array(list(RefData_copy[head+"_wt"].values()))
272  dat = np.array(list(self.RefData[head].values()))
273  # S_cd specifies an array of averages (one for each tail node). Find avg over axis 0.
274  avg = np.average(dat, weights=wts, axis=0)
275  if len(wts) > 1:
276  # If there is more than one data point, then the default denominator is the
277  # standard deviation of the experimental values.
278  if head == 'scd':
279  default_denoms[head+"_denom"] = np.average(np.sqrt(np.dot(wts, (dat-avg)**2)/wts.sum()))
280  else:
281  default_denoms[head+"_denom"] = np.sqrt(np.dot(wts, (dat-avg)**2)/wts.sum())
282  else:
283  # If there is only one data point, then the denominator is just the single
284  # data point itself.
285  if head == 'scd':
286  default_denoms[head+"_denom"] = np.average(np.sqrt(np.abs(dat[0])))
287  else:
288  default_denoms[head+"_denom"] = np.sqrt(np.abs(dat[0]))
289  self.PhasePoints = list(self.RefData[head].keys())
290  # This prints out all of the reference data.
291  # printcool_dictionary(self.RefData[head],head)
292  self.RefData = RefData_copy
293  # Create labels for the directories.
294  self.Labels = ["%.2fK-%.1f%s" % i for i in self.PhasePoints]
295  logger.debug("global_opts:\n%s\n" % str(global_opts))
296  logger.debug("default_denoms:\n%s\n" % str(default_denoms))
297  for opt in global_opts:
298  if "_denom" in opt:
299  # Record entries from the global_opts dictionary so they can be retrieved from other methods.
300  self.set_option(global_opts,opt,default=default_denoms[opt])
301  else:
302  self.set_option(global_opts,opt)
303 
304  def check_files(self, there):
305  there = os.path.abspath(there)
306  havepts = 0
307  if all([i in os.listdir(there) for i in self.Labels]):
308  for d in os.listdir(there):
309  if d in self.Labels:
310  if os.path.exists(os.path.join(there, d, 'npt_result.p')):
311  havepts += 1
312  if (float(havepts)/len(self.Labels)) > 0.75:
313  return 1
314  else:
315  return 0
316  def npt_simulation(self, temperature, pressure, simnum):
317  """ Submit a NPT simulation to the Work Queue. """
318  wq = getWorkQueue()
319  if not os.path.exists('npt_result.p'):
320  link_dir_contents(os.path.join(self.root,self.rundir),os.getcwd())
321  self.last_traj += [os.path.join(os.getcwd(), i) for i in self.extra_output]
322  self.lipid_mol[simnum%len(self.lipid_mol)].write(self.lipid_coords, ftype='tinker' if self.engname == 'tinker' else None)
323  cmdstr = '%s python npt_lipid.py %s %.3f %.3f' % (self.nptpfx, self.engname, temperature, pressure)
324  if wq is None:
325  logger.info("Running condensed phase simulation locally.\n")
326  logger.info("You may tail -f %s/npt.out in another terminal window\n" % os.getcwd())
327  _exec(cmdstr, copy_stderr=True, outfnm='npt.out')
328  else:
329  queue_up(wq, command = cmdstr+' &> npt.out',
330  input_files = self.nptfiles + self.scripts + ['forcebalance.p'],
331  output_files = ['npt_result.p', 'npt.out'] + self.extra_output, tgt=self)
332 
333  def polarization_correction(self,mvals):
334  d = self.gas_engine.get_multipole_moments(optimize=True)['dipole']
335  if not in_fd():
336  logger.info("The molecular dipole moment is % .3f debye\n" % np.linalg.norm(d))
337  # Taken from the original OpenMM interface code, this is how we calculate the conversion factor.
338  # dd2 = ((np.linalg.norm(d)-self.self_pol_mu0)*debye)**2
339  # eps0 = 8.854187817620e-12 * coulomb**2 / newton / meter**2
340  # epol = 0.5*dd2/(self.self_pol_alpha*angstrom**3*4*np.pi*eps0)/(kilojoule_per_mole/AVOGADRO_CONSTANT_NA)
341  # In [2]: eps0 = 8.854187817620e-12 * coulomb**2 / newton / meter**2
342  # In [7]: 1.0 * debye ** 2 / (1.0 * angstrom**3*4*np.pi*eps0) / (kilojoule_per_mole/AVOGADRO_CONSTANT_NA)
343  # Out[7]: 60.240179789402056
344  convert = 60.240179789402056
345  dd2 = (np.linalg.norm(d)-self.self_pol_mu0)**2
346  epol = 0.5*convert*dd2/self.self_pol_alpha
347  return epol
348 
349  def indicate(self):
350  AGrad = hasattr(self, 'Gp')
351  PrintDict = OrderedDict()
352  def print_item(key, heading, physunit):
353  if self.Xp[key] > 0:
354  printcool_dictionary(self.Pp[key], title='%s %s%s\nTemperature Pressure Reference Calculated +- Stdev Delta Weight Term ' %
355  (self.name, heading, " (%s) " % physunit if physunit else ""), bold=True, color=4, keywidth=15)
356  bar = printcool("%s objective function: % .3f%s" % (heading, self.Xp[key], ", Derivative:" if AGrad else ""))
357  if AGrad:
358  self.FF.print_map(vals=self.Gp[key])
359  logger.info(bar)
360  PrintDict[heading] = "% 10.5f % 8.3f % 14.5e" % (self.Xp[key], self.Wp[key], self.Xp[key]*self.Wp[key])
361 
362  print_item("Rho", "Density", "kg m^-3")
363  print_item("Alpha", "Thermal Expansion Coefficient", "10^-4 K^-1")
364  print_item("Kappa", "Isothermal Compressibility", "10^-6 bar^-1")
365  print_item("Cp", "Isobaric Heat Capacity", "cal mol^-1 K^-1")
366  print_item("Eps0", "Dielectric Constant", None)
367  print_item("Al", "Average Area per Lipid", "nm^2")
368  print_item("Scd", "Deuterium Order Parameter", None)
369  print_item("LKappa", "Bilayer Isothermal Compressibility", "mN/m")
370 
371  PrintDict['Total'] = "% 10s % 8s % 14.5e" % ("","",self.Objective)
372 
373  Title = "%s Condensed Phase Properties:\n %-20s %40s" % (self.name, "Property Name", "Residual x Weight = Contribution")
374  printcool_dictionary(PrintDict,color=4,title=Title,keywidth=31)
375  return
376 
377  def objective_term(self, points, expname, calc, err, grad, name="Quantity", SubAverage=False):
378  if expname in self.RefData:
379  exp = self.RefData[expname]
380  Weights = self.RefData[expname+"_wt"]
381  Denom = getattr(self,expname+"_denom")
382  else:
383  # If the reference data doesn't exist then return nothing.
384  return 0.0, np.zeros(self.FF.np), np.zeros((self.FF.np,self.FF.np)), None
385 
386  Sum = sum(Weights.values())
387  for i in Weights:
388  Weights[i] /= Sum
389  logger.info("Weights have been renormalized to " + str(sum(Weights.values())) + "\n")
390  # Use least-squares or hyperbolic (experimental) objective.
391  LeastSquares = True
392 
393  logger.info("Physical quantity %s uses denominator = % .4f\n" % (name, Denom))
394  if not LeastSquares:
395  # If using a hyperbolic functional form
396  # we still want the contribution to the
397  # objective function to be the same when
398  # Delta = Denom.
399  Denom /= 3 ** 0.5
400 
401  Objective = 0.0
402  Gradient = np.zeros(self.FF.np)
403  Hessian = np.zeros((self.FF.np,self.FF.np))
404  Objs = {}
405  GradMap = []
406  avgCalc = 0.0
407  avgExp = 0.0
408  avgGrad = np.zeros(self.FF.np)
409  for i, PT in enumerate(points):
410  avgCalc += Weights[PT]*calc[PT]
411  avgExp += Weights[PT]*exp[PT]
412  avgGrad += Weights[PT]*grad[PT]
413  for i, PT in enumerate(points):
414  if SubAverage:
415  G = grad[PT]-avgGrad
416  Delta = calc[PT] - exp[PT] - avgCalc + avgExp
417  else:
418  G = grad[PT]
419  Delta = calc[PT] - exp[PT]
420  if hasattr(Delta, "__len__"):
421  Delta = np.average(Delta)
422  if LeastSquares:
423  # Least-squares objective function.
424  ThisObj = Weights[PT] * Delta ** 2 / Denom**2
425  Objs[PT] = ThisObj
426  ThisGrad = 2.0 * Weights[PT] * Delta * G / Denom**2
427  GradMap.append(G)
428  Objective += ThisObj
429  Gradient += ThisGrad
430  # Gauss-Newton approximation to the Hessian.
431  Hessian += 2.0 * Weights[PT] * (np.outer(G, G)) / Denom**2
432  else:
433  # L1-like objective function.
434  D = Denom
435  S = Delta**2 + D**2
436  ThisObj = Weights[PT] * (S**0.5-D) / Denom
437  ThisGrad = Weights[PT] * (Delta/S**0.5) * G / Denom
438  ThisHess = Weights[PT] * (1/S**0.5-Delta**2/S**1.5) * np.outer(G,G) / Denom
439  Objs[PT] = ThisObj
440  GradMap.append(G)
441  Objective += ThisObj
442  Gradient += ThisGrad
443  Hessian += ThisHess
444  GradMapPrint = [["#PhasePoint"] + self.FF.plist]
445  for PT, g in zip(points,GradMap):
446  GradMapPrint.append([' %8.2f %8.1f %3s' % PT] + ["% 9.3e" % i for i in g])
447  o = wopen('gradient_%s.dat' % name)
448  for line in GradMapPrint:
449  print(' '.join(line), file=o)
450  o.close()
451 
452  Delta = np.array([calc[PT] - exp[PT] for PT in points])
453  delt = {PT : r for PT, r in zip(points,Delta)}
454  if expname == 'scd':
455  print_out = OrderedDict([(' %8.2f %8.1f %3s' % PT, '\n %s' % (' '.join('\t \t \t %9.6f %9.6f +- %-7.6f % 7.6f \n' % F for F in zip(exp[PT], calc[PT], flat(err[PT]), delt[PT])))) for PT in calc])
456  else:
457  print_out = OrderedDict([(' %8.2f %8.1f %3s' % PT, "%9.3f %9.3f +- %-7.3f % 7.3f % 9.5f % 9.5f" % (exp[PT],calc[PT],err[PT],delt[PT],Weights[PT],Objs[PT])) for PT in calc])
458 
459  return Objective, Gradient, Hessian, print_out
460 
461  def submit_jobs(self, mvals, AGrad=True, AHess=True):
462  # This routine is called by Objective.stage() will run before "get".
463  # It submits the jobs to the Work Queue and the stage() function will wait for jobs to complete.
464  #
465  # First dump the force field to a pickle file
466  lp_dump((self.FF,mvals,self.OptionDict,AGrad),'forcebalance.p')
467 
468  # Give the user an opportunity to copy over data from a previous (perhaps failed) run.
469  if (not self.evaluated) and self.manual:
470  warn_press_key("Now's our chance to fill the temp directory up with data!\n(Considering using 'read' or 'continue' for better checkpointing)", timeout=7200)
471 
472  # If self.save_traj == 1, delete the trajectory files from a previous good optimization step.
473  if self.evaluated and self.goodstep and self.save_traj < 2:
474  for fn in self.last_traj:
475  if os.path.exists(fn):
476  os.remove(fn)
477  self.last_traj = []
478 
479  # Set up and run the NPT simulations.
480  snum = 0
481  for label, pt in zip(self.Labels, self.PhasePoints):
482  T = pt[0]
483  P = pt[1]
484  Punit = pt[2]
485  if Punit == 'bar':
486  P *= 1.0 / 1.01325
487  if not os.path.exists(label):
488  os.makedirs(label)
489  os.chdir(label)
490  if 'n_ic' in self.RefData:
491  n_uniq_ic = int(self.RefData['n_ic'][pt])
492  # Loop over parallel trajectories.
493  for trj in range(n_uniq_ic):
494  rel_trj = "trj_%i" % trj
495  # Create directories for each parallel simulation.
496  if not os.path.exists(rel_trj):
497  os.makedirs(rel_trj)
498  os.chdir(rel_trj)
499  # Pull each simulation molecule from the lipid_mols dictionary.
500  # lipid_mols is a dictionary of paths to either the initial
501  # geometry files, or the geometries from the final frame of the
502  # previous iteration.
503  self.lipid_mol = self.lipid_mols[pt][trj]
504  self.lipid_mol.write(self.lipid_coords)
505  if not self.lipid_coords in self.nptfiles:
506  self.nptfiles += [self.lipid_coords]
507  self.npt_simulation(T,P,snum)
508  os.chdir('..')
509  else:
510  self.npt_simulation(T,P,snum)
511  os.chdir('..')
512  snum += 1
513 
514  def get(self, mvals, AGrad=True, AHess=True):
515 
516  """
517  Fitting of lipid bulk properties. This is the current major
518  direction of development for ForceBalance. Basically, fitting
519  the QM energies / forces alone does not always give us the
520  best simulation behavior. In many cases it makes more sense
521  to try and reproduce some experimentally known data as well.
522 
523  In order to reproduce experimentally known data, we need to
524  run a simulation and compare the simulation result to
525  experiment. The main challenge here is that the simulations
526  are computationally intensive (i.e. they require energy and
527  force evaluations), and furthermore the results are noisy. We
528  need to run the simulations automatically and remotely
529  (i.e. on clusters) and a good way to calculate the derivatives
530  of the simulation results with respect to the parameter values.
531 
532  This function contains some experimentally known values of the
533  density and enthalpy of vaporization (Hvap) of lipid water.
534  It launches the density and Hvap calculations on the cluster,
535  and gathers the results / derivatives. The actual calculation
536  of results / derivatives is done in a separate file.
537 
538  After the results come back, they are gathered together to form
539  an objective function.
540 
541  @param[in] mvals Mathematical parameter values
542  @param[in] AGrad Switch to turn on analytic gradient
543  @param[in] AHess Switch to turn on analytic Hessian
544  @return Answer Contribution to the objective function
545 
546  """
547 
548  mbar_verbose = False
549 
550  Answer = {}
551 
552  Results = {}
553  Points = [] # These are the phase points for which data exists.
554  BPoints = [] # These are the phase points for which we are doing MBAR for the condensed phase.
555  tt = 0
556  for label, PT in zip(self.Labels, self.PhasePoints):
557  if 'n_ic' in self.RefData:
558  self.lipid_mols[PT] = [Molecule(last_frame) for last_frame in self.lipid_mols[PT]]
559  n_uniq_ic = int(self.RefData['n_ic'][PT])
560  for ic in range(n_uniq_ic):
561  if os.path.exists('./%s/trj_%s/npt_result.p' % (label, ic)):
562  # Read in each each parallel simulation's data, and concatenate each property time series.
563  ts = lp_load('./%s/trj_%s/npt_result.p' % (label, ic))
564  if ic == 0:
565  ts_concat = list(ts)
566  else:
567  for d_arr in range(len(ts)):
568  if isinstance(ts[d_arr], np.ndarray):
569  # Gradients need a unique append format.
570  if d_arr == 5:
571  ts_concat[d_arr] = np.append(ts_concat[d_arr], ts[d_arr], axis = 1)
572  else:
573  ts_concat[d_arr] = np.append(ts_concat[d_arr], ts[d_arr], axis = 0)
574  if isinstance(ts_concat[d_arr], list):
575  ts_concat[d_arr] = [np.append(ts_concat[d_arr][i], ts[d_arr][i], axis = 1) for i in range(len(ts_concat[d_arr]))]
576  # Write concatenated time series to a pickle file.
577  if ic == (int(n_uniq_ic) - 1):
578  lp_dump((ts_concat), './%s/npt_result.p' % label)
579  if os.path.exists('./%s/npt_result.p' % label):
580  logger.info('Reading information from ./%s/npt_result.p\n' % label)
581  Points.append(PT)
582  Results[tt] = lp_load('./%s/npt_result.p' % label)
583  tt += 1
584  else:
585  logger.warning('The file ./%s/npt_result.p does not exist so we cannot read it\n' % label)
586  pass
587  # for obs in self.RefData:
588  # del self.RefData[obs][PT]
589  if len(Points) == 0:
590  logger.error('The lipid simulations have terminated with \x1b[1;91mno readable data\x1b[0m - this is a problem!\n')
591  raise RuntimeError
592 
593  # Assign variable names to all the stuff in npt_result.p
594  Rhos, Vols, Potentials, Energies, Dips, Grads, GDips, \
595  Rho_errs, Alpha_errs, Kappa_errs, Cp_errs, Eps0_errs, NMols, Als, Al_errs, Scds, Scd_errs, LKappa_errs = ([Results[t][i] for t in range(len(Points))] for i in range(18))
596  # Determine the number of molecules
597  if len(set(NMols)) != 1:
598  logger.error(str(NMols))
599  logger.error('The above list should only contain one number - the number of molecules\n')
600  raise RuntimeError
601  else:
602  NMol = list(set(NMols))[0]
603 
604  R = np.array(list(itertools.chain(*list(Rhos))))
605  V = np.array(list(itertools.chain(*list(Vols))))
606  E = np.array(list(itertools.chain(*list(Energies))))
607  Dx = np.array(list(itertools.chain(*list(d[:,0] for d in Dips))))
608  Dy = np.array(list(itertools.chain(*list(d[:,1] for d in Dips))))
609  Dz = np.array(list(itertools.chain(*list(d[:,2] for d in Dips))))
610  G = np.hstack(tuple(Grads))
611  GDx = np.hstack(tuple(gd[0] for gd in GDips))
612  GDy = np.hstack(tuple(gd[1] for gd in GDips))
613  GDz = np.hstack(tuple(gd[2] for gd in GDips))
614  A = np.array(list(itertools.chain(*list(Als))))
615  S = np.array(list(itertools.chain(*list(Scds))))
616 
617  Rho_calc = OrderedDict([])
618  Rho_grad = OrderedDict([])
619  Rho_std = OrderedDict([])
620  Alpha_calc = OrderedDict([])
621  Alpha_grad = OrderedDict([])
622  Alpha_std = OrderedDict([])
623  Kappa_calc = OrderedDict([])
624  Kappa_grad = OrderedDict([])
625  Kappa_std = OrderedDict([])
626  Cp_calc = OrderedDict([])
627  Cp_grad = OrderedDict([])
628  Cp_std = OrderedDict([])
629  Eps0_calc = OrderedDict([])
630  Eps0_grad = OrderedDict([])
631  Eps0_std = OrderedDict([])
632  Al_calc = OrderedDict([])
633  Al_grad = OrderedDict([])
634  Al_std = OrderedDict([])
635  LKappa_calc = OrderedDict([])
636  LKappa_grad = OrderedDict([])
637  LKappa_std = OrderedDict([])
638  Scd_calc = OrderedDict([])
639  Scd_grad = OrderedDict([])
640  Scd_std = OrderedDict([])
641 
642  # The unit that converts atmospheres * nm**3 into kj/mol :)
643  pvkj=0.061019351687175
644 
645  # Run MBAR using the total energies. Required for estimates that use the kinetic energy.
646  BSims = len(BPoints)
647  Shots = len(Energies[0])
648  Shots_m = [len(i) for i in Energies]
649  N_k = np.ones(BSims)*Shots
650  # Use the value of the energy for snapshot t from simulation k at potential m
651  U_kln = np.zeros([BSims,BSims,Shots])
652  for m, PT in enumerate(BPoints):
653  T = PT[0]
654  P = PT[1] / 1.01325 if PT[2] == 'bar' else PT[1]
655  beta = 1. / (kb * T)
656  for k in range(BSims):
657  # The correct Boltzmann factors include PV.
658  # Note that because the Boltzmann factors are computed from the conditions at simulation "m",
659  # the pV terms must be rescaled to the pressure at simulation "m".
660  kk = Points.index(BPoints[k])
661  U_kln[k, m, :] = Energies[kk] + P*Vols[kk]*pvkj
662  U_kln[k, m, :] *= beta
663  W1 = None
664  if len(BPoints) > 1:
665  logger.info("Running MBAR analysis on %i states...\n" % len(BPoints))
666  mbar = pymbar.MBAR(U_kln, N_k, verbose=mbar_verbose, relative_tolerance=5.0e-8)
667  W1 = mbar.getWeights()
668  logger.info("Done\n")
669  elif len(BPoints) == 1:
670  W1 = np.ones((BPoints*Shots,BPoints))
671  W1 /= BPoints*Shots
672 
673  def fill_weights(weights, phase_points, mbar_points, snapshots):
674  """ Fill in the weight matrix with MBAR weights where MBAR was run,
675  and equal weights otherwise. """
676  new_weights = np.zeros([len(phase_points)*snapshots,len(phase_points)])
677  for m, PT in enumerate(phase_points):
678  if PT in mbar_points:
679  mm = mbar_points.index(PT)
680  for kk, PT1 in enumerate(mbar_points):
681  k = phase_points.index(PT1)
682  logger.debug("Will fill W2[%i:%i,%i] with W1[%i:%i,%i]\n" % (k*snapshots,k*snapshots+snapshots,m,kk*snapshots,kk*snapshots+snapshots,mm))
683  new_weights[k*snapshots:(k+1)*snapshots,m] = weights[kk*snapshots:(kk+1)*snapshots,mm]
684  else:
685  logger.debug("Will fill W2[%i:%i,%i] with equal weights\n" % (m*snapshots,(m+1)*snapshots,m))
686  new_weights[m*snapshots:(m+1)*snapshots,m] = 1.0/snapshots
687  return new_weights
688 
689  W2 = fill_weights(W1, Points, BPoints, Shots)
690 
691  if self.do_self_pol:
692  EPol = self.polarization_correction(mvals)
693  GEPol = np.array([(f12d3p(fdwrap(self.polarization_correction, mvals, p), h = self.h, f0 = EPol)[0] if p in self.pgrad else 0.0) for p in range(self.FF.np)])
694  bar = printcool("Self-polarization correction to \nenthalpy of vaporization is % .3f kJ/mol%s" % (EPol, ", Derivative:" if AGrad else ""))
695  if AGrad:
696  self.FF.print_map(vals=GEPol)
697  logger.info(bar)
698 
699  for i, PT in enumerate(Points):
700  T = PT[0]
701  P = PT[1] / 1.01325 if PT[2] == 'bar' else PT[1]
702  PV = P*V*pvkj
703  H = E + PV
704  # The weights that we want are the last ones.
705  W = flat(W2[:,i])
706  C = weight_info(W, PT, np.ones(len(Points), dtype=int)*Shots, verbose=mbar_verbose)
707  Gbar = flat(np.dot(G,col(W)))
708  mBeta = -1/kb/T
709  Beta = 1/kb/T
710  kT = kb*T
711  # Define some things to make the analytic derivatives easier.
712  def avg(vec):
713  return np.dot(W,vec)
714  def covde(vec):
715  return flat(np.dot(G,col(W*vec))) - avg(vec)*Gbar
716  def deprod(vec):
717  return flat(np.dot(G,col(W*vec)))
718 
719  Rho_calc[PT] = np.dot(W,R)
720  Rho_grad[PT] = mBeta*(flat(np.dot(G,col(W*R))) - np.dot(W,R)*Gbar)
721 
723  Alpha_calc[PT] = 1e4 * (avg(H*V)-avg(H)*avg(V))/avg(V)/(kT*T)
724  GAlpha1 = -1 * Beta * deprod(H*V) * avg(V) / avg(V)**2
725  GAlpha2 = +1 * Beta * avg(H*V) * deprod(V) / avg(V)**2
726  GAlpha3 = deprod(V)/avg(V) - Gbar
727  GAlpha4 = Beta * covde(H)
728  Alpha_grad[PT] = 1e4 * (GAlpha1 + GAlpha2 + GAlpha3 + GAlpha4)/(kT*T)
729 
730  bar_unit = 0.06022141793 * 1e6
731  Kappa_calc[PT] = bar_unit / kT * (avg(V**2)-avg(V)**2)/avg(V)
732  GKappa1 = +1 * Beta**2 * avg(V**2) * deprod(V) / avg(V)**2
733  GKappa2 = -1 * Beta**2 * avg(V) * deprod(V**2) / avg(V)**2
734  GKappa3 = +1 * Beta**2 * covde(V)
735  Kappa_grad[PT] = bar_unit*(GKappa1 + GKappa2 + GKappa3)
736 
737  Cp_calc[PT] = 1000/(4.184*NMol*kT*T) * (avg(H**2) - avg(H)**2)
738  if hasattr(self,'use_cvib_intra') and self.use_cvib_intra:
739  logger.debug("Adding " + str(self.RefData['devib_intra'][PT]) + " to the heat capacity\n")
740  Cp_calc[PT] += self.RefData['devib_intra'][PT]
741  if hasattr(self,'use_cvib_inter') and self.use_cvib_inter:
742  logger.debug("Adding " + str(self.RefData['devib_inter'][PT]) + " to the heat capacity\n")
743  Cp_calc[PT] += self.RefData['devib_inter'][PT]
744  GCp1 = 2*covde(H) * 1000 / 4.184 / (NMol*kT*T)
745  GCp2 = mBeta*covde(H**2) * 1000 / 4.184 / (NMol*kT*T)
746  GCp3 = 2*Beta*avg(H)*covde(H) * 1000 / 4.184 / (NMol*kT*T)
747  Cp_grad[PT] = GCp1 + GCp2 + GCp3
748 
749  prefactor = 30.348705333964077
750  D2 = avg(Dx**2)+avg(Dy**2)+avg(Dz**2)-avg(Dx)**2-avg(Dy)**2-avg(Dz)**2
751  Eps0_calc[PT] = 1.0 + prefactor*(D2/avg(V))/T
752  GD2 = 2*(flat(np.dot(GDx,col(W*Dx))) - avg(Dx)*flat(np.dot(GDx,col(W)))) - Beta*(covde(Dx**2) - 2*avg(Dx)*covde(Dx))
753  GD2 += 2*(flat(np.dot(GDy,col(W*Dy))) - avg(Dy)*flat(np.dot(GDy,col(W)))) - Beta*(covde(Dy**2) - 2*avg(Dy)*covde(Dy))
754  GD2 += 2*(flat(np.dot(GDz,col(W*Dz))) - avg(Dz)*flat(np.dot(GDz,col(W)))) - Beta*(covde(Dz**2) - 2*avg(Dz)*covde(Dz))
755  Eps0_grad[PT] = prefactor*(GD2/avg(V) - mBeta*covde(V)*D2/avg(V)**2)/T
756 
757  Al_calc[PT] = np.dot(W,A)
758  Al_grad[PT] = mBeta*(flat(np.dot(G,col(W*A))) - np.dot(W,A)*Gbar)
759 
760  A_m2 = A * 1e-18
761  kbT = 1.3806488e-23 * T
762  LKappa_calc[PT] = (1e3 * 2 * kbT / 128) * (avg(A_m2) / (avg(A_m2**2)-avg(A_m2)**2))
763  al_avg = avg(A_m2)
764  al_sq_avg = avg(A_m2**2)
765  al_avg_sq = al_avg**2
766  al_var = al_sq_avg - al_avg_sq
767  GLKappa1 = covde(A_m2) / al_var
768  GLKappa2 = (al_avg / al_var**2) * (covde(A_m2**2) - (2 * al_avg * covde(A)))
769  LKappa_grad[PT] = (1e3 * 2 * kbT / 128) * (GLKappa1 - GLKappa2)
770 
771  Scd_calc[PT] = np.dot(W,S)
772  # LPW: In case I did not do the conversion correctly, the line of code previously here was:
773  # Scd_grad[PT] = mBeta * (flat(np.average(np.mat(G) * (S * W[:, np.newaxis]), axis = 1)) - np.average(np.average(S * W[:, np.newaxis], axis = 0), axis = 0) * Gbar)
774  Scd_grad[PT] = mBeta * (flat(np.average(np.dot(G, (S * W[:, np.newaxis])), axis = 1)) - np.average(np.average(S * W[:, np.newaxis], axis = 0), axis = 0) * Gbar)
775 
776  Rho_std[PT] = np.sqrt(sum(C**2 * np.array(Rho_errs)**2))
777  Alpha_std[PT] = np.sqrt(sum(C**2 * np.array(Alpha_errs)**2)) * 1e4
778  Kappa_std[PT] = np.sqrt(sum(C**2 * np.array(Kappa_errs)**2)) * 1e6
779  Cp_std[PT] = np.sqrt(sum(C**2 * np.array(Cp_errs)**2))
780  Eps0_std[PT] = np.sqrt(sum(C**2 * np.array(Eps0_errs)**2))
781  Al_std[PT] = np.sqrt(sum(C**2 * np.array(Al_errs)**2))
782  # LPW: In case I did not do the conversion correctly, the line of code previously here was:
783  # Scd_std[PT] = np.sqrt(sum(np.mat(C**2) * np.array(Scd_errs)**2))
784  Scd_std[PT] = np.sqrt(sum(np.dot(row(C**2), np.array(Scd_errs)**2)))
785  LKappa_std[PT] = np.sqrt(sum(C**2 * np.array(LKappa_errs)**2)) * 1e6
786 
787  # Get contributions to the objective function
788  X_Rho, G_Rho, H_Rho, RhoPrint = self.objective_term(Points, 'rho', Rho_calc, Rho_std, Rho_grad, name="Density")
789  X_Alpha, G_Alpha, H_Alpha, AlphaPrint = self.objective_term(Points, 'alpha', Alpha_calc, Alpha_std, Alpha_grad, name="Thermal Expansion")
790  X_Kappa, G_Kappa, H_Kappa, KappaPrint = self.objective_term(Points, 'kappa', Kappa_calc, Kappa_std, Kappa_grad, name="Compressibility")
791  X_Cp, G_Cp, H_Cp, CpPrint = self.objective_term(Points, 'cp', Cp_calc, Cp_std, Cp_grad, name="Heat Capacity")
792  X_Eps0, G_Eps0, H_Eps0, Eps0Print = self.objective_term(Points, 'eps0', Eps0_calc, Eps0_std, Eps0_grad, name="Dielectric Constant")
793  X_Al, G_Al, H_Al, AlPrint = self.objective_term(Points, 'al', Al_calc, Al_std, Al_grad, name="Avg Area per Lipid")
794  X_Scd, G_Scd, H_Scd, ScdPrint = self.objective_term(Points, 'scd', Scd_calc, Scd_std, Scd_grad, name="Deuterium Order Parameter")
795  X_LKappa, G_LKappa, H_LKappa, LKappaPrint = self.objective_term(Points, 'lkappa', LKappa_calc, LKappa_std, LKappa_grad, name="Bilayer Compressibility")
796 
797  Gradient = np.zeros(self.FF.np)
798  Hessian = np.zeros((self.FF.np,self.FF.np))
799 
800  if X_Rho == 0: self.w_rho = 0.0
801  if X_Alpha == 0: self.w_alpha = 0.0
802  if X_Kappa == 0: self.w_kappa = 0.0
803  if X_Cp == 0: self.w_cp = 0.0
804  if X_Eps0 == 0: self.w_eps0 = 0.0
805  if X_Al == 0: self.w_al = 0.0
806  if X_Scd == 0: self.w_scd = 0.0
807  if X_LKappa == 0: self.w_lkappa = 0.0
809  if self.w_normalize:
810  w_tot = self.w_rho + self.w_alpha + self.w_kappa + self.w_cp + self.w_eps0 + self.w_al + self.w_scd + self.w_lkappa
811  else:
812  w_tot = 1.0
813  w_1 = self.w_rho / w_tot
814  w_3 = self.w_alpha / w_tot
815  w_4 = self.w_kappa / w_tot
816  w_5 = self.w_cp / w_tot
817  w_6 = self.w_eps0 / w_tot
818  w_7 = self.w_al / w_tot
819  w_8 = self.w_scd / w_tot
820  w_9 = self.w_lkappa / w_tot
821 
822  Objective = w_1 * X_Rho + w_3 * X_Alpha + w_4 * X_Kappa + w_5 * X_Cp + w_6 * X_Eps0 + w_7 * X_Al + w_8 * X_Scd + w_9 * X_LKappa
823  if AGrad:
824  Gradient = w_1 * G_Rho + w_3 * G_Alpha + w_4 * G_Kappa + w_5 * G_Cp + w_6 * G_Eps0 + w_7 * G_Al + w_8 * G_Scd + w_9 * G_LKappa
825  if AHess:
826  Hessian = w_1 * H_Rho + w_3 * H_Alpha + w_4 * H_Kappa + w_5 * H_Cp + w_6 * H_Eps0 + w_7 * H_Al + w_8 * H_Scd + w_9 * H_LKappa
827 
828  if not in_fd():
829  self.Xp = {"Rho" : X_Rho, "Alpha" : X_Alpha,
830  "Kappa" : X_Kappa, "Cp" : X_Cp, "Eps0" : X_Eps0, "Al" : X_Al, "Scd" : X_Scd, "LKappa" : X_LKappa}
831  self.Wp = {"Rho" : w_1, "Alpha" : w_3,
832  "Kappa" : w_4, "Cp" : w_5, "Eps0" : w_6, "Al" : w_7, "Scd" : w_8, "LKappa" : w_9}
833  self.Pp = {"Rho" : RhoPrint, "Alpha" : AlphaPrint,
834  "Kappa" : KappaPrint, "Cp" : CpPrint, "Eps0" : Eps0Print, "Al" : AlPrint, "Scd" : ScdPrint, "LKappa": LKappaPrint}
835  if AGrad:
836  self.Gp = {"Rho" : G_Rho, "Alpha" : G_Alpha,
837  "Kappa" : G_Kappa, "Cp" : G_Cp, "Eps0" : G_Eps0, "Al" : G_Al, "Scd" : G_Scd, "LKappa" : G_LKappa}
838  self.Objective = Objective
840  Answer = {'X':Objective, 'G':Gradient, 'H':Hessian}
841  return Answer
def npt_simulation(self, temperature, pressure, simnum)
Submit a NPT simulation to the Work Queue.
Definition: lipid.py:321
def check_files(self, there)
Definition: lipid.py:307
w_rho
Density.
Definition: lipid.py:803
def lp_load(fnm)
Read an object from a bzipped file specified by the path.
Definition: nifty.py:836
Nifty functions, intended to be imported by any module within ForceBalance.
MBarEnergy
Evaluated energies for all trajectories (i.e.
Definition: lipid.py:185
loop_over_snapshots
LPW 2018-02-11: This is set to True if the target calculates a single-point property over several exi...
Definition: lipid.py:123
def LinkFile(src, dest, nosrcok=False)
Definition: nifty.py:1313
def objective_term(self, points, expname, calc, err, grad, name="Quantity", SubAverage=False)
Definition: lipid.py:381
def flat(vec)
Given any list, array, or matrix, return a single-index array.
Definition: nifty.py:467
def read_data(self)
Definition: lipid.py:196
def in_fd()
Invoking this function from anywhere will tell us whether we&#39;re being called by a finite-difference f...
def submit_jobs(self, mvals, AGrad=True, AHess=True)
Definition: lipid.py:465
def fdwrap(func, mvals0, pidx, key=None, kwargs)
A function wrapper for finite difference designed for differentiating &#39;get&#39;-type functions.
def queue_up(wq, command, input_files, output_files, tag=None, tgt=None, verbose=True, print_time=60)
Submit a job to the Work Queue.
Definition: nifty.py:941
def lp_dump(obj, fnm, protocol=0)
Write an object to a zipped pickle file specified by the path.
Definition: nifty.py:817
def col(vec)
Given any list, array, or matrix, return a 1-column 2D array.
Definition: nifty.py:448
def link_dir_contents(abssrcdir, absdestdir)
Definition: nifty.py:1345
def indicate(self)
Definition: lipid.py:353
def get(self, mvals, AGrad=True, AHess=True)
Fitting of lipid bulk properties.
Definition: lipid.py:550
def warn_press_key(warning, timeout=10)
Definition: nifty.py:1599
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...
Definition: nifty.py:366
def polarization_correction(self, mvals)
Definition: lipid.py:337
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
SavedTraj
Saved trajectories for all iterations and all temperatures.
Definition: lipid.py:183
def weight_info(W, PT, N_k, verbose=True)
Definition: lipid.py:38
SavedMVal
Saved force field mvals for all iterations.
Definition: lipid.py:181
def prepare_temp_directory(self)
Prepare the temporary directory by copying in important files.
Definition: lipid.py:189
def __init__(self, options, tgt_opts, forcefield)
Definition: lipid.py:60
Subclass of Target for lipid property matching.
Definition: lipid.py:57
def f12d3p(f, h, f0=None)
A three-point finite difference stencil.
def isfloat(word)
Matches ANY number; it can be a decimal, scientific notation, integer, or what have you...
Definition: molecule.py:406
def wopen(dest, binary=False)
If trying to write to a symbolic link, remove it first.
Definition: nifty.py:1304
def getWorkQueue()
Definition: nifty.py:904
def row(vec)
Given any list, array, or matrix, return a 1-row 2D array.
Definition: nifty.py:458