1 """ @package forcebalance.vibration Vibrational mode fitting module 6 from __future__
import division
8 from builtins
import zip
9 from builtins
import range
12 from forcebalance.nifty import col, eqcgmx, flat, floatornan, fqcgmx, invert_svd, kb, printcool, bohr2ang, warn_press_key, pvec1d, pmat2d
14 from forcebalance.target
import Target
16 from re
import match, sub
18 from subprocess
import PIPE
21 from scipy
import optimize
22 from collections
import OrderedDict
25 from forcebalance.output
import getLogger
26 logger = getLogger(__name__)
29 for i
in range(len(assignment)):
30 if sum(assignment==i) != 1
and verbose:
31 logger.info(
"Vibrational mode %i is assigned %i times\n" % (i+1, sum(assignment==i)))
35 """ Subclass of Target for fitting force fields to vibrational spectra (from experiment or theory). 37 Currently Tinker is supported. 41 def __init__(self,options,tgt_opts,forcefield):
45 super(Vibration,self).
__init__(options,tgt_opts,forcefield)
50 self.set_option(tgt_opts,
'wavenumber_tol',
'denom')
51 self.set_option(tgt_opts,
'reassign_modes',
'reassign')
52 self.set_option(tgt_opts,
'normalize')
61 self.
vfnm = os.path.join(self.tgtdir,
"vdata.txt")
65 engine_args = OrderedDict(list(self.OptionDict.items()) + list(options.items()))
66 engine_args.pop(
'name',
None)
68 self.
engine = self.engine_(target=self, **engine_args)
69 if self.FF.rigid_water:
70 logger.error(
'This class cannot be used with rigid water molecules.\n')
74 """ Read the reference vibrational data from a file. """ 82 for line
in open(self.
vfnm):
83 line = line.split(
'#')[0]
85 if len(s) == 1
and self.
na == -1:
87 xyz = np.zeros((self.
na, 3))
91 elif an < self.
na and len(s) == 4:
92 xyz[an, :] = np.array([float(i)
for i
in s[1:]])
96 logger.warning(
'Warning: Setting imaginary frequency = % .3fi to zero.\n' % abs(float(s[0])))
103 self.
ref_eigvecs[-1][an, :] = np.array([float(i)
for i
in s])
108 logger.info(line +
'\n')
109 logger.error(
"This line doesn't comply with our vibration file format!\n")
115 v2 /= np.linalg.norm(v2)
119 """ Print qualitative indicator. """ 121 banner =
"Frequencies (wavenumbers)" 122 headings = [
"Mode #",
"Reference",
"Calculated",
"Difference",
"Ref(dot)Calc"]
124 self.printcool_table(data, headings, banner)
128 if hasattr(self,
'engine')
and hasattr(self.
engine,
'normal_modes'):
129 return self.
engine.normal_modes()
131 logger.error(
'Normal mode calculation not supported, try using a different engine\n')
132 raise NotImplementedError
136 Calculate overlap between vibrational modes for two Cartesian displacements. 141 The two sets of Cartesian displacements to compute overlap for, 142 3*N_atoms values each. 147 Overlap between mass-weighted eigenvectors 149 sqrtm = np.sqrt(np.array(self.
engine.AtomLists[
'Mass']))
151 v1m *= sqrtm[:, np.newaxis]
152 v1m /= np.linalg.norm(v1m)
154 v2m *= sqrtm[:, np.newaxis]
155 v2m /= np.linalg.norm(v2m)
156 return np.abs(np.dot(v1m.flatten(), v2m.flatten()))
158 def get(self, mvals, AGrad=False, AHess=False):
159 """ Evaluate objective function. """ 160 Answer = {
'X':0.0,
'G':np.zeros(self.FF.np),
'H':np.zeros((self.FF.np, self.FF.np))}
162 def get_eigvals(mvals_):
167 dev = np.array([[(np.abs(i-j)/1000)/(1.0+np.abs(i-j)/1000)
for j
in self.
ref_eigvals]
for i
in eigvals])
168 for i
in range(dev.shape[0]):
169 dev[i, :] /= max(dev[i, :])
171 if self.
reassign in [
'permute',
'overlap']:
179 row, c2r = optimize.linear_sum_assignment(a)
180 eigvals = eigvals[c2r]
182 c2r = np.argmin(a, axis=0)
185 eigvals_p.append(eigvals[j])
186 eigvals = np.array(eigvals_p)
189 eigvecs = eigvecs[c2r]
194 eigvecs_p.append(eigvecs[j])
195 eigvecs = np.array(eigvecs_p)
199 calc_eigvals = get_eigvals(mvals)
201 dV = np.zeros((self.FF.np,len(calc_eigvals)))
204 dV[p,:], _ =
f12d3p(
fdwrap(get_eigvals, mvals, p), h = self.h, f0 = calc_eigvals)
205 Answer[
'X'] = np.dot(D,D) / self.denom**2 / (len(D)
if self.normalize
else 1)
207 Answer[
'G'][p] = 2*np.dot(D, dV[p,:]) / self.denom**2 / (len(D)
if self.normalize
else 1)
209 Answer[
'H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) / self.denom**2 / (len(D)
if self.normalize
else 1)
def count_assignment(assignment, verbose=True)
loop_over_snapshots
LPW 2018-02-11: This is set to True if the target calculates a single-point property over several exi...
Nifty functions, intended to be imported by any module within ForceBalance.
def vib_overlap(self, v1, v2)
Calculate overlap between vibrational modes for two Cartesian displacements.
def in_fd()
Invoking this function from anywhere will tell us whether we're being called by a finite-difference f...
def fdwrap(func, mvals0, pidx, key=None, kwargs)
A function wrapper for finite difference designed for differentiating 'get'-type functions.
def __init__(self, options, tgt_opts, forcefield)
Initialization.
def indicate(self)
Print qualitative indicator.
def read_reference_data(self)
Read the reference vibrational data from a file.
Subclass of Target for fitting force fields to vibrational spectra (from experiment or theory)...
def get(self, mvals, AGrad=False, AHess=False)
Evaluate objective function.
def vibration_driver(self)
def f12d3p(f, h, f0=None)
A three-point finite difference stencil.
vfnm
The vdata.txt file that contains the vibrations.
engine
Read in the reference data.