1 from __future__
import division
2 from __future__
import print_function
3 from builtins
import zip
4 from builtins
import str
5 from builtins
import object
10 from forcebalance.target
import Target
17 from collections
import OrderedDict
19 from forcebalance.output
import getLogger
20 logger = getLogger(__name__)
25 Compute the first and second derivatives of a set of snapshot 26 energies with respect to the force field parameters. 28 This basically calls the finite difference subroutine on the 29 energy_driver subroutine also in this script. 31 In the future we may need to be more sophisticated with 32 controlling the quantities which are differentiated, but for 35 @param[in] engine Engine object for calculating energies 36 @param[in] FF Force field object 37 @param[in] mvals Mathematical parameter values 38 @param[in] h Finite difference step size 39 @param[in] pgrad List of active parameters for differentiation 40 @param[in] dipole Switch for dipole derivatives. 41 @return G First derivative of the energies in a N_param x N_coord array 42 @return GDx First derivative of the box dipole moment x-component in a N_param x N_coord array 43 @return GDy First derivative of the box dipole moment y-component in a N_param x N_coord array 44 @return GDz First derivative of the box dipole moment z-component in a N_param x N_coord array 47 def single_point(mvals_):
50 return engine.energy_dipole()
52 return engine.energy()
54 ED0 = single_point(mvals)
56 G[
'potential'] = np.zeros((FF.np, ED0.shape[0]))
58 G[
'dipole'] = np.zeros((FF.np, ED0.shape[0], 3))
60 logger.info(
"%i %s\r" % (i, (FF.plist[i] +
" "*30)))
63 G[
'potential'][i] = edg[:,0]
64 G[
'dipole'][i] = edg[:,1:]
66 G[
'potential'][i] = edg[:]
72 A target for fitting general experimental data sets. The 73 experimental data is described in a .txt file and is handled with a 77 def __init__(self, options, tgt_opts, forcefield):
79 super(Thermo, self).
__init__(options, tgt_opts, forcefield)
83 self.set_option(tgt_opts,
"expdata_txt", forceprint=
True)
85 self.set_option(tgt_opts,
"quantities", forceprint=
True)
87 self.set_option(tgt_opts,
"n_sim_chain", forceprint=
True)
89 self.set_option(tgt_opts,
"eq_steps", forceprint=
True)
91 self.set_option(tgt_opts,
"md_steps", forceprint=
True)
112 for f
in self.scripts:
113 LinkFile(os.path.join(os.path.split(__file__)[0],
"data", f),
114 os.path.join(self.root, self.tempdir, f))
116 def _read_expdata(self, expdata):
117 """Read and store experimental data. 122 Read experimental data from this filename. 140 if line.lstrip().startswith(
"#")
or not line.strip():
145 param, value = line.split(
"=")
146 param = param.strip().lower()
147 if param ==
"denoms":
148 for e, v
in enumerate(value.split()):
149 self.
denoms[self.quantities[e]] = float(v)
150 elif param ==
"weights":
151 for e, v
in enumerate(value.split()):
152 self.
weights[self.quantities[e]] = float(v)
157 label = (vals[0], label_header, label_unit)
158 refs = np.array(vals[1:-2:2]).astype(float)
159 wts = np.array(vals[2:-2:2]).astype(float)
160 temperature = float(vals[-2])
161 pressure =
None if vals[-1].lower() ==
"none" else \
164 dp =
Point(count, label=label, refs=refs, weights=wts,
165 names=names, units=units,
166 temperature=temperature, pressure=pressure)
170 headers = list(zip(*[tuple(h.split(
"_"))
for h
in line.split()
173 label_header = list(headers[0])[0]
174 label_unit = list(headers[1])[0]
175 names = list(headers[0][1:-2])
176 units = list(headers[1][1:-2])
181 """Retrieve the molecular dynamics (MD) results and store the calculated 182 quantities in the Point object dp. 187 Store the calculated quantities in this point. 194 abspath = os.path.join(os.getcwd(),
'%d/md_result.p' % dp.idnr)
196 if os.path.exists(abspath):
197 logger.info(
'Reading data from ' + abspath +
'.\n')
199 vals, errs, grads =
lp_load(abspath)
201 dp.data[
"values"] = vals
202 dp.data[
"errors"] = errs
203 dp.data[
"grads"] = grads
206 msg =
'The file ' + abspath +
' does not exist so we cannot read it.\n' 209 dp.data[
"values"] = np.zeros((len(self.quantities)))
210 dp.data[
"errors"] = np.zeros((len(self.quantities)))
211 dp.data[
"grads"] = np.zeros((len(self.quantities), self.FF.np))
213 def submit_jobs(self, mvals, AGrad=True, AHess=True):
214 """This routine is called by Objective.stage() and will run before "get". 215 It submits the jobs and the stage() function will wait for jobs 221 Mathematical parameter values. 223 Switch to turn on analytic gradient. 225 Switch to turn on analytic Hessian. 236 os.makedirs(str(pt.idnr))
237 except OSError
as exception:
238 if exception.errno != errno.EEXIST:
242 os.chdir(str(pt.idnr))
245 for f
in self.scripts:
246 LinkFile(os.path.join(self.root, self.tempdir, f),
247 os.path.join(os.getcwd(), f))
250 str(pt.idnr)), os.getcwd())
253 lp_dump((self.FF, mvals, self.OptionDict, AGrad),
'forcebalance.p')
256 cmdstr = (
"%s python md_chain.py " % self.mdpfx +
257 " ".join(self.quantities) +
" " +
258 "--engine %s " % self.engname +
259 "--length %d " % self.n_sim_chain +
260 "--name %s " % self.
simpfx +
261 "--temperature %f " % pt.temperature +
262 "--pressure %f " % pt.pressure +
263 "--nequil %d " % self.eq_steps +
264 "--nsteps %d " % self.md_steps)
265 _exec(cmdstr, copy_stderr=
True, outfnm=
'md_chain.out')
270 """Shows optimization state.""" 271 AGrad = hasattr(self,
'Gp')
272 PrintDict = OrderedDict()
274 def print_item(key, physunit):
276 the_title = (
"%s %s (%s)\n" % (self.name, key.capitalize(), physunit) +
277 "No. Temperature Pressure Reference " +
278 "Calculated +- Stddev " +
279 " Delta Weight Term ")
282 color=4, keywidth=15)
284 bar =
printcool((
"%s objective function: % .3f%s" %
285 (key.capitalize(), self.
Xp[key],
286 ", Derivative:" if AGrad
else "")))
288 self.FF.print_map(vals=self.
Gp[key])
291 PrintDict[key] = ((
"% 10.5f % 8.3f % 14.5e" %
292 (self.
Xp[key], self.
Wp[key],
293 self.
Xp[key]*self.
Wp[key])))
295 for i, q
in enumerate(self.quantities):
296 print_item(q, self.
points[0].ref[
"units"][i])
298 PrintDict[
'Total'] =
"% 10s % 8s % 14.5e" % (
"",
"", self.
Objective)
300 Title = (
"%s Thermodynamic Properties:\n %-20s %40s" %
301 (self.name,
"Property",
"Residual x Weight = Contribution"))
306 """Calculates the contribution to the objective function (the term) for a 312 Calculate the objective term for this quantity. 317 `term` is a dict with keys `X`, `G`, `H` and `info`. The values of 318 these keys are the objective term itself (`X`), its gradient (`G`), 319 its Hessian (`H`), and an OrderedDict with print information on 320 individiual data points (`info`). 324 Gradient = np.zeros(self.FF.np)
325 Hessian = np.zeros((self.FF.np, self.FF.np))
328 qid = self.quantities.index(quantity)
329 Exp = np.array([pt.ref[
"refs"][qid]
for pt
in self.
points])
330 Weights = np.array([pt.ref[
"weights"][qid]
for pt
in self.
points])
331 Denom = self.
denoms[quantity]
334 Weights /= np.sum(Weights)
335 logger.info(
"Renormalized weights to " + str(np.sum(Weights)) +
"\n")
336 logger.info((
"Physical quantity '%s' uses denominator = %g %s\n" %
337 (quantity.capitalize(), Denom,
338 self.
points[0].ref[
"units"][self.quantities.index(quantity)])))
341 values = np.array([pt.data[
"values"][qid]
for pt
in self.
points])
342 errors = np.array([pt.data[
"errors"][qid]
for pt
in self.
points])
343 grads = np.array([pt.data[
"grads"][qid]
for pt
in self.
points])
355 Deltas = values - Exp
356 Objs = np.einsum(
"i,i->i", Weights, Deltas**2) / Denom / Denom
357 Grads = 2.0*np.einsum(
"i,i,ij->ij", Weights, Deltas, grads) / Denom / Denom
358 Hess = 2.0*np.einsum(
"i,jk,lm->ijm", Weights, grads.T, grads) / Denom / Denom
361 Objective += np.sum(Objs, axis=0)
362 Gradient += np.sum(Grads, axis=0)
363 Hessian += np.sum(Hess, axis=0)
366 GradMapPrint = [[
"#Point"] + self.FF.plist]
369 temp = pt.temperature
371 GradMapPrint.append([
' %8.2f %8.1f' % (temp, press)] +
372 [
"% 9.3e" % i
for i
in grads[pt.idnr-1]])
374 o =
wopen(
'gradient_%s.dat' % quantity)
375 for line
in GradMapPrint:
376 print(
' '.join(line), file=o)
379 printer = OrderedDict([(
" %-5d %-12.2f %-8.1f" 380 % (pt.idnr, pt.temperature, pt.pressure),
381 (
"% -10.3f % -10.3f +- %-8.3f % -8.3f % -9.5f % -9.5f" 382 % (Exp[pt.idnr-1], values[pt.idnr-1],
383 errors[pt.idnr-1], Deltas[pt.idnr-1],
384 Weights[pt.idnr-1], Objs[pt.idnr-1])))
387 return {
"X": Objective,
"G": Gradient,
"H": Hessian,
"info": printer }
389 def get(self, mvals, AGrad=True, AHess=True):
390 """Return the contribution to the total objective function. This is a 391 weighted average of the calculated quantities. 396 Mathematical parameter values. 398 Switch to turn on analytic gradient. 400 Switch to turn on analytic Hessian. 405 Contribution to the objective function. `Answer` is a dict with keys 406 `X` for the objective function, `G` for its gradient and `H` for its 413 Gradient = np.zeros(self.FF.np)
414 Hessian = np.zeros((self.FF.np, self.FF.np))
422 for q
in self.quantities:
432 reweighted.append(self.
weights[q])
435 reweighted = np.array(reweighted)
436 wtot = np.sum(reweighted)
437 reweighted = reweighted/wtot
if wtot > 0
else reweighted
443 Xs = np.array([dic[
"X"]
for dic
in obj.values()])
444 Gs = np.array([dic[
"G"]
for dic
in obj.values()])
445 Hs = np.array([dic[
"H"]
for dic
in obj.values()])
449 Objective = np.average(Xs, weights=(
None if np.all(reweighted == 0)
else \
452 Gradient = np.average(Gs, weights=(
None if np.all(reweighted == 0)
else \
455 Hessian = np.average(Hs, weights=(
None if np.all(reweighted == 0)
else \
460 self.
Xp = {q : dic[
"X"]
for (q, dic)
in obj.items()}
461 self.
Wp = {q : reweighted[self.quantities.index(q)]
462 for (q, dic)
in obj.items()}
463 self.
Pp = {q : dic[
"info"]
for (q, dic)
in obj.items()}
466 self.
Gp = {q : dic[
"G"]
for (q, dic)
in obj.items()}
470 Answer = {
"X": Objective,
"G": Gradient,
"H": Hessian }
475 def __init__(self, idnr, label=None, refs=None, weights=None, names=None,
476 units=None, temperature=None, pressure=None, data=None):
478 self.ref = {
"label" : label,
485 self.
data = data
if data
is not None else {}
490 msg.append(
"State: Unknown.")
492 msg.append(
"State: Point " + str(self.
idnr) +
" at " +
495 msg.append(
"State: Point " + str(self.
idnr) +
" at " +
499 msg.append(
"Point " + str(self.
idnr) +
" reference data " +
"-"*30)
501 msg.append(
" " + key.strip() +
" = " + str(self.
ref[key]).strip())
503 msg.append(
"Point " + str(self.
idnr) +
" calculated data " +
"-"*30)
504 for key
in self.
data:
505 msg.append(
" " + key.strip() +
" = " + str(self.
data[key]).strip())
507 return "\n".join(msg)
def indicate(self)
Shows optimization state.
def lp_load(fnm)
Read an object from a bzipped file specified by the path.
Nifty functions, intended to be imported by any module within ForceBalance.
def LinkFile(src, dest, nosrcok=False)
def _read_expdata(self, expdata)
Read and store experimental data.
A target for fitting general experimental data sets.
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 fdwrap(func, mvals0, pidx, key=None, kwargs)
A function wrapper for finite difference designed for differentiating 'get'-type functions.
def lp_dump(obj, fnm, protocol=0)
Write an object to a zipped pickle file specified by the path.
loop_over_snapshots
Initialize base class.
def link_dir_contents(abssrcdir, absdestdir)
def retrieve(self, dp)
Retrieve the molecular dynamics (MD) results and store the calculated quantities in the Point object ...
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 energy_derivatives(engine, FF, mvals, h, pgrad, dipole=False)
Compute the first and second derivatives of a set of snapshot energies with respect to the force fiel...
def printcool(text, sym="#", bold=False, color=2, ansi=None, bottom='-', minwidth=50, center=True, sym2="=")
Cool-looking printout for slick formatting of output.
def submit_jobs(self, mvals, AGrad=True, AHess=True)
This routine is called by Objective.stage() and will run before "get".
def get(self, mvals, AGrad=True, AHess=True)
Return the contribution to the total objective function.
def objective_term(self, quantity)
Calculates the contribution to the objective function (the term) for a given quantity.
def f12d3p(f, h, f0=None)
A three-point finite difference stencil.
def wopen(dest, binary=False)
If trying to write to a symbolic link, remove it first.