ForceBalance API  1.3
Automated optimization of force fields and empirical potentials
moments.py
Go to the documentation of this file.
1 """ @package forcebalance.moments Multipole moment fitting module
2 
3 @author Lee-Ping Wang
4 @date 09/2012
5 """
6 from __future__ import division
7 
8 from builtins import zip
9 import os
10 import shutil
11 import numpy as np
12 from forcebalance.nifty import col, eqcgmx, flat, floatornan, fqcgmx, invert_svd, kb, printcool, printcool_dictionary, bohr2ang, warn_press_key
13 from forcebalance.target import Target
14 from forcebalance.molecule import Molecule, format_xyz_coord
15 from re import match, sub
16 import subprocess
17 import itertools
18 from subprocess import PIPE
19 from forcebalance.finite_difference import fdwrap, f1d2p, f12d3p, in_fd
20 from collections import OrderedDict
21 
22 from forcebalance.output import getLogger
23 logger = getLogger(__name__)
24 
25 class Moments(Target):
26 
27  """ Subclass of Target for fitting force fields to multipole moments (from experiment or theory).
28 
29  Currently Tinker is supported.
30 
31  """
32 
33  def __init__(self,options,tgt_opts,forcefield):
34  """Initialization."""
35 
36  # Initialize the SuperClass!
37  super(Moments,self).__init__(options,tgt_opts,forcefield)
38 
39  #======================================#
40  # Options that are given by the parser #
41  #======================================#
42  self.set_option(tgt_opts, 'dipole_denom')
43  self.set_option(tgt_opts, 'quadrupole_denom')
44  self.set_option(tgt_opts, 'polarizability_denom')
45  self.set_option(tgt_opts, 'optimize_geometry')
46 
47  self.denoms = {}
48  self.denoms['dipole'] = self.dipole_denom
49  self.denoms['quadrupole'] = self.quadrupole_denom
50  self.denoms['polarizability'] = self.polarizability_denom
51 
52  #======================================#
53  # Variables which are set here #
54  #======================================#
55 
57  self.loop_over_snapshots = False
58 
59  self.mfnm = os.path.join(self.tgtdir,"mdata.txt")
60 
61  self.ref_moments = OrderedDict()
62 
63  self.read_reference_data()
64 
65  engine_args = OrderedDict(list(self.OptionDict.items()) + list(options.items()))
66  engine_args.pop('name', None)
67 
68  self.engine = self.engine_(target=self, **engine_args)
69 
70  def read_reference_data(self):
71  """ Read the reference data from a file. """
72 
73  self.na = -1
74  self.ref_eigvals = []
75  self.ref_eigvecs = []
76  an = 0
77  ln = 0
78  cn = -1
79  dn = -1
80  qn = -1
81  pn = -1
82  for line in open(self.mfnm):
83  line = line.split('#')[0] # Strip off comments
84  s = line.split()
85  if len(s) == 0:
86  pass
87  elif len(s) == 1 and self.na == -1:
88  self.na = int(s[0])
89  xyz = np.zeros((self.na, 3))
90  cn = ln + 1
91  elif ln == cn:
92  pass
93  elif an < self.na and len(s) == 4:
94  xyz[an, :] = np.array([float(i) for i in s[1:]])
95  an += 1
96  elif an == self.na and s[0].lower() == 'dipole':
97  dn = ln + 1
98  elif ln == dn:
99  self.ref_moments['dipole'] = OrderedDict(zip(['x','y','z'],[float(i) for i in s]))
100  elif an == self.na and s[0].lower() in ['quadrupole', 'quadrapole']:
101  qn = ln + 1
102  elif ln == qn:
103  self.ref_moments['quadrupole'] = OrderedDict([('xx',float(s[0]))])
104  elif qn > 0 and ln == qn + 1:
105  self.ref_moments['quadrupole']['xy'] = float(s[0])
106  self.ref_moments['quadrupole']['yy'] = float(s[1])
107  elif qn > 0 and ln == qn + 2:
108  self.ref_moments['quadrupole']['xz'] = float(s[0])
109  self.ref_moments['quadrupole']['yz'] = float(s[1])
110  self.ref_moments['quadrupole']['zz'] = float(s[2])
111  elif an == self.na and s[0].lower() in ['polarizability', 'alpha']:
112  pn = ln + 1
113  elif ln == pn:
114  self.ref_moments['polarizability'] = OrderedDict([('xx',float(s[0]))])
115  self.ref_moments['polarizability']['yx'] = float(s[1])
116  self.ref_moments['polarizability']['zx'] = float(s[2])
117  elif pn > 0 and ln == pn + 1:
118  self.ref_moments['polarizability']['xy'] = float(s[0])
119  self.ref_moments['polarizability']['yy'] = float(s[1])
120  self.ref_moments['polarizability']['zy'] = float(s[2])
121  elif pn > 0 and ln == pn + 2:
122  self.ref_moments['polarizability']['xz'] = float(s[0])
123  self.ref_moments['polarizability']['yz'] = float(s[1])
124  self.ref_moments['polarizability']['zz'] = float(s[2])
125  else:
126  logger.info("%s\n" % line)
127  logger.error("This line doesn't comply with our multipole file format!\n")
128  raise RuntimeError
129  ln += 1
130  # Subtract the trace of the quadrupole moment.
131  if 'quadrupole' in self.ref_moments:
132  trace3 = (self.ref_moments['quadrupole']['xx'] + self.ref_moments['quadrupole']['yy'] + self.ref_moments['quadrupole']['zz'])/3
133  self.ref_moments['quadrupole']['xx'] -= trace3
134  self.ref_moments['quadrupole']['yy'] -= trace3
135  self.ref_moments['quadrupole']['zz'] -= trace3
136 
137  return
138 
139  def indicate(self):
140  """ Print qualitative indicator. """
141  logger.info("\rTarget: %-15s\n" % self.name)
142  #print "Multipole Moments and Po"
143  #print "Reference :", self.ref_moments
144  #print "Calculated:", self.calc_moments
145  #print "Objective = %.5e" % self.objective
146 
147  ref_momvals = self.unpack_moments(self.ref_moments)
148  calc_momvals = self.unpack_moments(self.calc_moments)
149  PrintDict = OrderedDict()
150  i = 0
151  for Ord in self.ref_moments:
152  for Comp in self.ref_moments[Ord]:
153  if abs(self.calc_moments[Ord][Comp]) > 1e-6 or abs(self.ref_moments[Ord][Comp]) > 1e-6:
154  PrintDict["%s-%s" % (Ord, Comp)] = "% 9.3f % 9.3f % 9.3f % 12.5f" % (self.calc_moments[Ord][Comp],
155  self.ref_moments[Ord][Comp],
156  self.calc_moments[Ord][Comp]-self.ref_moments[Ord][Comp],
157  (ref_momvals[i] - calc_momvals[i])**2)
158 
159  i += 1
160 
161  printcool_dictionary(PrintDict,title="Moments and/or Polarizabilities, Objective = % .5e\n %-20s %9s %9s %9s %11s" %
162  (self.objective, "Component", "Calc.", "Ref.", "Delta", "Term"))
163 
164  return
165 
166  def unpack_moments(self, moment_dict):
167  answer = np.array(list(itertools.chain(*[[dct[i]/self.denoms[ord] if self.denoms[ord] != 0.0 else 0.0 for i in dct] for ord,dct in moment_dict.items()])))
168  return answer
169 
170  def get(self, mvals, AGrad=False, AHess=False):
171  """ Evaluate objective function. """
172  Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))}
173  def get_momvals(mvals_):
174  self.FF.make(mvals_)
175  moments = self.engine.multipole_moments(polarizability='polarizability' in self.ref_moments, optimize=self.optimize_geometry)
176  # Unpack from dictionary.
177  return self.unpack_moments(moments)
178 
179  self.FF.make(mvals)
180  ref_momvals = self.unpack_moments(self.ref_moments)
181  calc_moments = self.engine.multipole_moments(polarizability='polarizability' in self.ref_moments, optimize=self.optimize_geometry)
182  calc_momvals = self.unpack_moments(calc_moments)
183 
184  D = calc_momvals - ref_momvals
185  dV = np.zeros((self.FF.np,len(calc_momvals)))
186 
187  if AGrad or AHess:
188  for p in self.pgrad:
189  dV[p,:], _ = f12d3p(fdwrap(get_momvals, mvals, p), h = self.h, f0 = calc_momvals)
190 
191  Answer['X'] = np.dot(D,D)
192  for p in self.pgrad:
193  Answer['G'][p] = 2*np.dot(D, dV[p,:])
194  for q in self.pgrad:
195  Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:])
196 
197  if not in_fd():
198  self.calc_moments = calc_moments
199  self.objective = Answer['X']
200 
201  return Answer
Nifty functions, intended to be imported by any module within ForceBalance.
na
Number of atoms.
Definition: moments.py:77
engine
Read in the reference data.
Definition: moments.py:71
def in_fd()
Invoking this function from anywhere will tell us whether we&#39;re being called by a finite-difference f...
mfnm
The mdata.txt file that contains the moments.
Definition: moments.py:62
def fdwrap(func, mvals0, pidx, key=None, kwargs)
A function wrapper for finite difference designed for differentiating &#39;get&#39;-type functions.
ref_moments
Dictionary of reference multipole moments.
Definition: moments.py:64
Subclass of Target for fitting force fields to multipole moments (from experiment or theory)...
Definition: moments.py:32
def __init__(self, options, tgt_opts, forcefield)
Initialization.
Definition: moments.py:37
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 indicate(self)
Print qualitative indicator.
Definition: moments.py:145
def f12d3p(f, h, f0=None)
A three-point finite difference stencil.
loop_over_snapshots
LPW 2018-02-11: This is set to True if the target calculates a single-point property over several exi...
Definition: moments.py:60
def get(self, mvals, AGrad=False, AHess=False)
Evaluate objective function.
Definition: moments.py:177
def read_reference_data(self)
Read the reference data from a file.
Definition: moments.py:75
def unpack_moments(self, moment_dict)
Definition: moments.py:171