1 """ @package forcebalance.binding Binding energy fitting module. 6 from __future__
import division
8 from builtins
import str
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
17 from subprocess
import PIPE
19 from collections
import OrderedDict
20 from multiprocessing
import Pool
22 from forcebalance.output
import getLogger
23 logger = getLogger(__name__)
26 """ Parse through the interactions input file. 28 @param[in] input_file The name of the input file. 32 Systems = OrderedDict()
33 Interactions = OrderedDict()
41 logger.info(
"Reading interactions from file: %s\n" % input_file)
43 fobj = open(input_file).readlines()
44 for ln, line
in enumerate(fobj):
46 line = line.split(
"#")[0].strip()
53 if re.match(
'^\$',line):
54 word = re.sub(
'^\$',
'',line).upper()
56 if section ==
"GLOBAL":
pass 57 elif section ==
"SYSTEM":
58 if SystemName
is None:
59 warn_press_key(
"You need to specify a name for the system on line %i" % ln)
60 elif SystemName
in Systems:
61 warn_press_key(
"A system named %s already exists in Systems" % SystemName)
62 Systems[SystemName] = SystemDict
65 elif section ==
"INTERACTION":
66 if InterName
in InterDict:
67 warn_press_key(
"A system named %s already exists in InterDict" % InterName)
68 Interactions[InterName] = InterDict
70 InterName =
"I%i" % InterNum
73 warn_press_key(
"Encountered $end for unsupported section %s on line %i" % (word, ln))
75 elif section ==
"NONE":
78 warn_press_key(
"Encountered section keyword %s when already in section %s" % (word, section))
79 elif section ==
"GLOBAL":
80 if key
in [
'keyfile',
'energy_unit']:
82 elif key ==
'optimize':
83 if len(s) == 1
or s[1].lower()
in [
'y',
'yes',
'true']:
84 logger.info(
"Optimizing ALL systems by default\n")
89 warn_press_key(
"Encountered unsupported key %s in section %s on line %i" % (key, section, ln))
90 elif section ==
"SYSTEM":
93 elif key ==
'geometry':
94 SystemDict[key] = s[1]
95 elif key ==
'rmsd_weight':
96 SystemDict[key] = float(s[1])
98 SystemDict[key] = s[1]
99 elif key ==
'optimize':
100 if len(s) == 1
or s[1].lower()
in [
'y',
'yes',
'true']:
101 SystemDict[key] =
True 102 logger.info(
"Optimizing system %s\n" % SystemName)
104 SystemDict[key] =
False 106 warn_press_key(
"Encountered unsupported key %s in section %s on line %i" % (key, section, ln))
107 elif section ==
"INTERACTION":
110 elif key ==
'equation':
111 InterDict[key] =
' '.join(s[1:])
112 elif key ==
'energy':
113 InterDict[key] = float(s[1])
114 elif key ==
'weight':
115 InterDict[key] = float(s[1])
117 warn_press_key(
"Encountered unsupported key %s in section %s on line %i" % (key, section, ln))
118 return Globals, Systems, Interactions
122 """ Improved subclass of Target for fitting force fields to binding energies. """ 125 super(BindingEnergy,self).
__init__(options,tgt_opts,forcefield)
126 self.set_option(
None,
None,
'inter_txt', os.path.join(self.tgtdir,tgt_opts[
'inter_txt']))
129 for opt
in self.global_opts:
130 for sys
in self.sys_opts:
131 if opt
not in self.sys_opts[sys]:
132 self.sys_opts[sys][opt] = self.global_opts[opt]
135 self.
inter_opts[inter][opt] = self.global_opts[opt]
137 if 'energy_unit' in self.
inter_opts[inter]
and self.
inter_opts[inter][
'energy_unit'].lower()
not in [
'kilocalorie_per_mole',
'kilocalories_per_mole']:
138 logger.error(
'Usage of physical units is has been removed, please provide all binding energies in kcal/mole\n')
142 if tgt_opts[
'energy_denom'] == 0.0:
143 self.set_option(
None,
None,
'energy_denom', val=np.std(np.array([val[
'reference_physical']
for val
in self.
inter_opts.values()])))
145 self.set_option(
None,
None,
'energy_denom', val=tgt_opts[
'energy_denom'])
147 self.set_option(
None,
None,
'rmsd_denom', val=tgt_opts[
'rmsd_denom'])
149 self.set_option(tgt_opts,
'cauchy')
150 self.set_option(tgt_opts,
'attenuate')
155 logger.info(
"The energy denominator is: %s\n" % str(self.energy_denom))
156 logger.info(
"The RMSD denominator is: %s\n" % str(self.rmsd_denom))
159 logger.info(
"Each contribution to the interaction energy objective function will be scaled by 1.0 / ( denom**2 + reference**2 )\n")
161 logger.error(
'attenuate and cauchy are mutually exclusive\n')
164 logger.info(
"Repulsive interactions beyond energy_denom will be scaled by 1.0 / ( denom**2 + (reference-denom)**2 )\n")
166 engine_args = OrderedDict(list(self.OptionDict.items()) + list(options.items()))
167 engine_args.pop(
'name',
None)
170 for sysname,sysopt
in self.sys_opts.items():
171 M = Molecule(os.path.join(self.root, self.tgtdir, sysopt[
'geometry']))
172 if 'select' in sysopt:
173 atomselect = np.array(
uncommadash(sysopt[
'select']))
174 M = M.atom_select(atomselect)
175 if self.FF.rigid_water: M.rigid_water()
176 self.
engines[sysname] = self.engine_(target=self, mol=M, name=sysname, tinker_key=os.path.join(sysopt[
'keyfile']), **engine_args)
179 opts = self.sys_opts[sysname]
180 return self.
engines[sysname].energy_rmsd(optimize = (opts[
'optimize']
if 'optimize' in opts
else False))
184 (self.
energy_part,
"Interaction",
"Calc.",
"Ref.",
"Delta",
"Term"))
188 def get(self, mvals, AGrad=False, AHess=False):
189 Answer = {
'X':0.0,
'G':np.zeros(self.FF.np),
'H':np.zeros((self.FF.np, self.FF.np))}
192 EnergyDict = OrderedDict()
199 for sys_
in self.sys_opts:
202 EnergyDict[sys_] = Energy_
203 RMSDNrm_ = RMSD_ / self.rmsd_denom
204 w_ = self.sys_opts[sys_][
'rmsd_weight']
if 'rmsd_weight' in self.sys_opts[sys_]
else 1.0
205 VectorD_.append(np.sqrt(w_)*RMSDNrm_)
206 if not in_fd()
and RMSD_ != 0.0:
207 self.
RMSDDict[sys_] =
"% 9.3f % 12.5f" % (RMSD_, w_*RMSDNrm_**2)
210 def encloseInDictionary(matchobj):
211 return 'EnergyDict["' + matchobj.group(0)+
'"]' 214 evalExpr = re.sub(
'[A-Za-z_][A-Za-z0-9_]*', encloseInDictionary, self.
inter_opts[inter_][
'equation'])
215 Calculated_ = eval(evalExpr)
216 Reference_ = self.
inter_opts[inter_][
'reference_physical']
217 Delta_ = Calculated_ - Reference_
218 Denom_ = self.energy_denom
220 Divisor_ = np.sqrt(Denom_**2 + Reference_**2)
222 if Reference_ < Denom_:
225 Divisor_ = np.sqrt(Denom_**2 + (Reference_-Denom_)**2)
228 DeltaNrm_ = Delta_ / Divisor_
230 VectorE_.append(np.sqrt(w_)*DeltaNrm_)
232 self.
PrintDict[inter_] =
"% 9.3f % 9.3f % 9.3f % 12.5f" % (Calculated_, Reference_, Delta_, w_*DeltaNrm_**2)
236 self.
rmsd_part = np.dot(np.array(VectorD_),np.array(VectorD_))
237 if len(VectorE_) > 0:
238 self.
energy_part = np.dot(np.array(VectorE_),np.array(VectorE_))
241 if len(VectorE_) > 0
and len(VectorD_) > 0:
242 return np.array(VectorD_ + VectorE_)
243 elif len(VectorD_) > 0:
244 return np.array(VectorD_)
245 elif len(VectorE_) > 0:
246 return np.array(VectorE_)
250 dV = np.zeros((self.FF.np,len(V)))
253 dV[p,:], _ =
f12d3p(
fdwrap(compute, mvals, p), h = self.h, f0 = V)
255 Answer[
'X'] = np.dot(V,V)
257 Answer[
'G'][p] = 2*np.dot(V, dV[p,:])
259 Answer[
'H'][p,q] = 2*np.dot(dV[p,:], dV[q,:])
Nifty functions, intended to be imported by any module within ForceBalance.
engines
Build keyword dictionaries to pass to engine.
def __init__(self, options, tgt_opts, forcefield)
def in_fd()
Invoking this function from anywhere will tell us whether we're being called by a finite-difference f...
def system_driver(self, sysname)
def fdwrap(func, mvals0, pidx, key=None, kwargs)
A function wrapper for finite difference designed for differentiating 'get'-type functions.
def warn_press_key(warning, timeout=10)
def printcool_dictionary(Dict, title="Dictionary Keys : Values", bold=False, color=2, keywidth=25, topwidth=50, center=True, leftpad=0)
See documentation for printcool; this is a nice way to print out keys/values in a dictionary...
def get(self, mvals, AGrad=False, AHess=False)
def parse_interactions(input_file)
Parse through the interactions input file.
def f12d3p(f, h, f0=None)
A three-point finite difference stencil.
Improved subclass of Target for fitting force fields to binding energies.
loop_over_snapshots
LPW 2018-02-11: This is set to True if the target calculates a single-point property over several exi...