def print_pdb(atoms, cell, filedesc=sys.stdout, title="", cell_conv=1.0, atoms_conv=1.0): """Prints an atomic configuration into a pdb formatted file. Also prints the cell parameters in standard pdb form. Note that the angles are in degrees. Args: atoms: An atoms object giving the atom positions. cell: A cell object giving the system box. filedesc: An open writable file object. Defaults to standard output. title: An optional string of max. 70 characters. """ fmt_cryst = "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f%s%4i\n" fmt_atom = "ATOM %5i %4s%1s%3s %1s%4i%1s %8.3f%8.3f%8.3f%6.2f%6.2f %2s%2i\n" if title != "": filedesc.write("TITLE %70s\n" % (title)) a, b, c, alpha, beta, gamma = mt.h2abc_deg(cell.h * cell_conv) z = 1 filedesc.write(fmt_cryst % (a, b, c, alpha, beta, gamma, " P 1 ", z)) natoms = atoms.natoms qs = dstrip(atoms.q) * atoms_conv lab = dstrip(atoms.names) for i in range(natoms): data = (i + 1, lab[i], ' ', ' 1', ' ', 1, ' ', qs[3 * i], qs[3 * i + 1], qs[3 * i + 2], 0.0, 0.0, ' ', 0) filedesc.write(fmt_atom % data) filedesc.write("END\n")
def print_json(atoms, cell, filedesc=sys.stdout, title="", cell_conv=1.0, atoms_conv=1.0): """Prints an atomic configuration into an XYZ formatted file. Args: atoms: An atoms object giving the centroid positions. cell: A cell object giving the system box. filedesc: An open writable file object. Defaults to standard output. title: This gives a string to be appended to the comment line. """ a, b, c, alpha, beta, gamma = mt.h2abc_deg(cell.h * cell_conv) natoms = atoms.natoms # direct access to avoid unnecessary slow-down qs = dstrip(atoms.q) * atoms_conv lab = dstrip(atoms.names) data = {} data['natoms'] = natoms data['cell'] = [a, b, c, alpha, beta, gamma] data['title'] = title data['q'] = qs.tolist() data['labels'] = lab.tolist() filedesc.write(json.dumps(data)) filedesc.write(" \n")
def print_xyz(atoms, cell, filedesc=sys.stdout, title="", cell_conv=1.0, atoms_conv=1.0): """Prints an atomic configuration into an XYZ formatted file. Args: atoms: An atoms object giving the centroid positions. cell: A cell object giving the system box. filedesc: An open writable file object. Defaults to standard output. title: This gives a string to be appended to the comment line. """ a, b, c, alpha, beta, gamma = mt.h2abc_deg(cell.h * cell_conv) natoms = atoms.natoms fmt_header = "%d\n# CELL(abcABC): %10.5f %10.5f %10.5f %10.5f %10.5f %10.5f %s\n" filedesc.write(fmt_header % (natoms, a, b, c, alpha, beta, gamma, title)) # direct access to avoid unnecessary slow-down qs = dstrip(atoms.q) * atoms_conv lab = dstrip(atoms.names) for i in range(natoms): filedesc.write("%8s %12.5e %12.5e %12.5e\n" % (lab[i], qs[3 * i], qs[3 * i + 1], qs[3 * i + 2]))
def print_xyz_path(beads, cell, filedesc=sys.stdout, cell_conv=1.0, atoms_conv=1.0): """Prints all the bead configurations into a XYZ formatted file. Prints all the replicas for each time step separately, rather than all at once. Args: beads: A beads object giving the bead positions. cell: A cell object giving the system box. filedesc: An open writable file object. Defaults to standard output. """ a, b, c, alpha, beta, gamma = mt.h2abc_deg(cell.h * cell_conv) fmt_header = "%d\n# bead: %d CELL(abcABC): %10.5f %10.5f %10.5f %10.5f %10.5f %10.5f \n" natoms = beads.natoms nbeads = beads.nbeads for j in range(nbeads): filedesc.write(fmt_header % (natoms, j, a, b, c, alpha, beta, gamma)) for i in range(natoms): qs = dstrip(beads.q) * atoms_conv lab = dstrip(beads.names) filedesc.write( "%8s %12.5e %12.5e %12.5e\n" % (lab[i], qs[j][3 * i], qs[j][3 * i + 1], qs[j][3 * i + 2]))
def __init__( self, latency=1.0e-3, name="", pars=None, dopbc=False, threaded=False, init_file="", plumeddat="", plumedstep=0, ): """Initialises FFPlumed. Args: pars: Optional dictionary, giving the parameters needed by the driver. """ # a socket to the communication library is created or linked if plumed is None: raise ImportError( "Cannot find plumed libraries to link to a FFPlumed object/") super(FFPlumed, self).__init__(latency, name, pars, dopbc=False, threaded=threaded) self.plumed = plumed.Plumed() self.plumeddat = plumeddat self.plumedstep = plumedstep self.init_file = init_file if self.init_file.mode == "xyz": infile = open(self.init_file.value, "r") myframe = read_file(self.init_file.mode, infile) myatoms = myframe["atoms"] mycell = myframe["cell"] myatoms.q *= unit_to_internal("length", self.init_file.units, 1.0) mycell.h *= unit_to_internal("length", self.init_file.units, 1.0) self.natoms = myatoms.natoms self.plumed.cmd("setNatoms", self.natoms) self.plumed.cmd("setPlumedDat", self.plumeddat) self.plumed.cmd("setTimestep", 1.0) self.plumed.cmd( "setMDEnergyUnits", 2625.4996 ) # Pass a pointer to the conversion factor between the energy unit used in your code and kJ mol-1 self.plumed.cmd( "setMDLengthUnits", 0.052917721 ) # Pass a pointer to the conversion factor between the length unit used in your code and nm self.plumed.cmd("setMDTimeUnits", 2.4188843e-05) self.plumedrestart = False if self.plumedstep > 0: # we are restarting, signal that PLUMED should continue self.plumedrestart = True self.plumed.cmd("setRestart", 1) self.plumed.cmd("init") self.charges = dstrip(myatoms.q) * 0.0 self.masses = dstrip(myatoms.m) self.lastq = np.zeros(3 * self.natoms)
def step(self, step=None): """ Does one simulation time step Attributes: ttime: The time taken in applying the thermostat steps. """ self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) # Store previous forces for warning exit condition self.old_f[:] = self.forces.f # Check for fixatoms if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 dq1 = dstrip(self.old_f) # Move direction for steepest descent dq1_unit = dq1 / np.sqrt(np.dot(dq1.flatten(), dq1.flatten())) info(" @GEOP: Determined SD direction", verbosity.debug) # Set position and direction inside the mapper self.lm.set_dir(dstrip(self.beads.q), dq1_unit) # Reuse initial value since we have energy and forces already u0, du0 = (self.forces.pot.copy(), np.dot(dstrip(self.forces.f.flatten()), dq1_unit.flatten())) # Do one SD iteration; return positions and energy # (x, fx,dfx) = min_brent(self.lm, fdf0=(u0, du0), x0=0.0, #DELETE min_brent(self.lm, fdf0=(u0, du0), x0=0.0, tol=self.ls_options["tolerance"] * self.tolerances["energy"], itmax=self.ls_options["iter"], init_step=self.ls_options["step"]) info(" Number of force calls: %d" % (self.lm.fcount)) self.lm.fcount = 0 # Update positions and forces self.beads.q = self.lm.dbeads.q self.forces.transfer_forces( self.lm.dforces) # This forces the update of the forces d_x = np.absolute(np.subtract(self.beads.q, self.lm.x0)) x = np.linalg.norm(d_x) # Automatically adapt the search step for the next iteration. # Relaxes better with very small step --> multiply by factor of 0.1 or 0.01 self.ls_options["step"] = 0.1 * x * self.ls_options["adaptive"] + ( 1 - self.ls_options["adaptive"]) * self.ls_options["step"] # Exit simulation step d_x_max = np.amax(np.absolute(d_x)) self.exitstep(self.forces.pot, u0, d_x_max)
def step(self, step=None): """ Does one simulation time step Attributes: ttime: The time taken in applying the thermostat steps. """ self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) # Store previous forces for warning exit condition self.old_f[:] = self.forces.f # Check for fixatoms if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 dq1 = dstrip(self.old_f) # Move direction for steepest descent dq1_unit = dq1 / np.sqrt(np.dot(dq1.flatten(), dq1.flatten())) info(" @GEOP: Determined SD direction", verbosity.debug) # Set position and direction inside the mapper self.lm.set_dir(dstrip(self.beads.q), dq1_unit) # Reuse initial value since we have energy and forces already u0, du0 = (self.forces.pot.copy(), np.dot(dstrip(self.forces.f.flatten()), dq1_unit.flatten())) # Do one SD iteration; return positions and energy # (x, fx,dfx) = min_brent(self.lm, fdf0=(u0, du0), x0=0.0, #DELETE min_brent(self.lm, fdf0=(u0, du0), x0=0.0, tol=self.ls_options["tolerance"] * self.tolerances["energy"], itmax=self.ls_options["iter"], init_step=self.ls_options["step"]) info(" Number of force calls: %d" % (self.lm.fcount)); self.lm.fcount = 0 # Update positions and forces self.beads.q = self.lm.dbeads.q self.forces.transfer_forces(self.lm.dforces) # This forces the update of the forces d_x = np.absolute(np.subtract(self.beads.q, self.lm.x0)) x = np.linalg.norm(d_x) # Automatically adapt the search step for the next iteration. # Relaxes better with very small step --> multiply by factor of 0.1 or 0.01 self.ls_options["step"] = 0.1 * x * self.ls_options["adaptive"] + (1 - self.ls_options["adaptive"]) * self.ls_options["step"] # Exit simulation step d_x_max = np.amax(np.absolute(d_x)) self.exitstep(self.forces.pot, u0, d_x_max)
def b2tob1(self, q): """Transforms a matrix from one value of beads to another. Args: q: A matrix with nbeads2 rows, in the bead representation. """ if self.noop: # still must return a copy, as the contraction is meant to return new data, not a view q_scal = dstrip(q).copy() else: # see b1tob2 for an explanation of the rationale for dealing with open path transformations q_scal = np.tensordot(self._b2tob1, q, axes=(1, 0)) if len(self._open) > 0: if len(q_scal.shape) == 2: for io in self._open: q_scal[:, 3 * io] = np.tensordot(self._o_b2tob1, q[:, 3 * io], axes=(1, 0)) q_scal[:, 3 * io + 1] = np.tensordot(self._o_b2tob1, q[:, 3 * io + 1], axes=(1, 0)) q_scal[:, 3 * io + 2] = np.tensordot(self._o_b2tob1, q[:, 3 * io + 2], axes=(1, 0)) else: q_scal = np.tensordot(self._o_b2tob1, q, axes=(1, 0)) return q_scal
def step(self, step=None): """ Does one simulation time step Attributes: ttime: The time taken in applying the thermostat steps. """ self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) if step == 0: info(" @GEOP: Initializing L-BFGS", verbosity.debug) print self.d self.d += dstrip(self.forces.f) / np.sqrt(np.dot(self.forces.f.flatten(), self.forces.f.flatten())) self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pot self.old_f[:] = self.forces.f if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 # Reduce the dimensionality masked_old_x = self.old_x[:, self.gm.fixatoms_mask] masked_d = self.d[:, self.gm.fixatoms_mask] # self.gm is reduced inside its __init__() and __call__() functions masked_qlist = self.qlist[:, self.gm.fixatoms_mask] masked_glist = self.glist[:, self.gm.fixatoms_mask] fdf0 = (self.old_u, -self.old_f[:, self.gm.fixatoms_mask]) # We update everything within L_BFGS (and all other calls). L_BFGS(masked_old_x, masked_d, self.gm, masked_qlist, masked_glist, fdf0, self.big_step, self.ls_options["tolerance"] * self.tolerances["energy"], self.ls_options["iter"], self.corrections, self.scale, step) # Restore the dimensionality self.d[:, self.gm.fixatoms_mask] = masked_d self.qlist[:, self.gm.fixatoms_mask] = masked_qlist self.glist[:, self.gm.fixatoms_mask] = masked_glist else: fdf0 = (self.old_u, -self.old_f) # We update everything within L_BFGS (and all other calls). L_BFGS(self.old_x, self.d, self.gm, self.qlist, self.glist, fdf0, self.big_step, self.ls_options["tolerance"] * self.tolerances["energy"], self.ls_options["iter"], self.corrections, self.scale, step) info(" Number of force calls: %d" % (self.gm.fcount)); self.gm.fcount = 0 # Update positions and forces self.beads.q = self.gm.dbeads.q self.forces.transfer_forces(self.gm.dforces) # This forces the update of the forces # Exit simulation step d_x_max = np.amax(np.absolute(np.subtract(self.beads.q, self.old_x))) self.exitstep(self.forces.pot, self.old_u, d_x_max)
def geop_thread(self, ieval, nstr, nevent, ostr=None): self.geop[ieval].reset() ipot = self.dforces[ieval].pot for i in xrange(self.nstep): # print "geop ", i, self.dforces[ieval].pot self.geop[ieval].step(i) #if self.geop[ieval].converged[0]: break newq = dstrip(self.dbeads[ieval].q[0]).copy() newpot = self.dforces[ieval].pot # print "geop ", self.nstep, self.dforces[ieval].pot with self._threadlock: self.ecache[nstr] = newpot self.qcache[nstr] = newq self.ncache += 1 nevent[2] = self.ecache[nstr] nevent[3] = self.qcache[nstr] # launches TS calculation #if not ostr is None: # self.ts_thread(ieval, ostr, nstr, nevent) #else: # with self._threadlock: # self.feval[ieval] = 1 with self._threadlock: self.feval[ieval] = 1 with self._threadlock: print "Finished ", nstr print "Energy, initial - TS - final: ", ipot, nevent[-1], newpot
def initialize(self, step): if step == 0: info(" @GEOP: Initializing instanton", verbosity.low) if self.beads.nbeads == 1: raise ValueError("We can not perform an splitting calculation with nbeads =1") else: if ((self.beads.q - self.beads.q[0]) == 0).all(): # If the coordinates in all the imaginary time slices are the same self.initial_geo() else: info(" @GEOP: Starting from the provided geometry in the extended phase space", verbosity.low) # This must be done after the stretching and before the self.d. if self.im.f is None: self.im(self.beads.q, ret=False) # Init instanton mapper if (self.optarrays["old_x"] == np.zeros((self.beads.nbeads, 3 * self.beads.natoms), float)).all(): self.optarrays["old_x"][:] = self.beads.q # Specific for LBFGS if np.linalg.norm(self.optarrays["d"]) == 0.0: f = self.forces.f + self.im.f self.optarrays["d"] += dstrip(f) / np.sqrt(np.dot(f.flatten(), f.flatten())) self.update_old_pos_for() self.init = True
def b1tob2(self, q): """Transforms a matrix from one value of beads to another. Args: q: A matrix with nbeads1 rows, in the bead representation. """ if self.noop: # still must return a copy, as the contraction is meant to return new data, not a view q_scal = dstrip(q).copy() else: # this applies to both bead property arrays (e.g. potentials) and bead vector properties (e.g. positions, forces) q_scal = np.dot(self._b1tob2, q) if len(self._open) > 0: if len(q_scal.shape) == 2: for io in self._open: q_scal[:, 3 * io] = np.dot(self._o_b1tob2, q[:, 3 * io]) q_scal[:, 3 * io + 1] = np.dot(self._o_b1tob2, q[:, 3 * io + 1]) q_scal[:, 3 * io + 2] = np.dot(self._o_b1tob2, q[:, 3 * io + 2]) else: # this applies the open path contraction to EVERYTHING because otherwise we don't know how to handle # the fact that only some beads are open. clearly this is a hack, and in practice the point is that # a "per bead" NM transformation of the potential is not well-defined when different beads have different # NM transformations q_scal = np.dot(self._o_b1tob2, q) return q_scal
def print_pdb_path(beads, cell, filedesc=sys.stdout, cell_conv=1.0, atoms_conv=1.0): """Prints all the bead configurations, into a pdb formatted file. Prints the ring polymer springs as well as the bead positions using the CONECT command. Also prints the cell parameters in standard pdb form. Note that the angles are in degrees. Args: beads: A beads object giving the bead positions. cell: A cell object giving the system box. filedesc: An open writable file object. Defaults to standard output. """ fmt_cryst = "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f%s%4i\n" fmt_atom = "ATOM %5i %4s%1s%3s %1s%4i%1s %8.3f%8.3f%8.3f%6.2f%6.2f %2s%2i\n" fmt_conect = "CONECT%5i%5i\n" a, b, c, alpha, beta, gamma = mt.h2abc_deg(cell.h * cell_conv) z = 1 # What even is this parameter? filedesc.write(fmt_cryst % (a, b, c, alpha, beta, gamma, " P 1 ", z)) natoms = beads.natoms nbeads = beads.nbeads for j in range(nbeads): for i in range(natoms): qs = dstrip(beads.q) * atoms_conv lab = dstrip(beads.names) data = (j * natoms + i + 1, lab[i], ' ', ' 1', ' ', 1, ' ', qs[j][3 * i], qs[j][3 * i + 1], qs[j][3 * i + 2], 0.0, 0.0, ' ', 0) filedesc.write(fmt_atom % data) if nbeads > 1: for i in range(natoms): filedesc.write(fmt_conect % (i + 1, (nbeads - 1) * natoms + i + 1)) for j in range(nbeads - 1): for i in range(natoms): filedesc.write(fmt_conect % (j * natoms + i + 1, (j + 1) * natoms + i + 1)) filedesc.write("END\n")
def __call__(self, x): """ computes energy and gradient for optimization step determines new position (x0+d*x)""" self.fcount += 1 self.dbeads.q[:, self.fixatoms_mask] = self.x0[:, self.fixatoms_mask] + self.d * x e = self.dforces.pot # Energy g = - np.dot(dstrip(self.dforces.f[:, self.fixatoms_mask]).flatten(), self.d.flatten()) # Gradient return e, g
def __call__(self, x): """ computes energy and gradient for optimization step determines new position (x0+d*x)""" self.fcount += 1 self.dbeads.q = self.x0 + self.d * x e = self.dforces.pot # Energy g = - np.dot(dstrip(self.dforces.f).flatten(), self.d.flatten()) # Gradient return e, g
def __init__(self, latency=1.0e-3, name="", pars=None, dopbc=False, init_file="", plumeddat="", precision=8, plumedstep=0): """Initialises FFPlumed. Args: pars: Optional dictionary, giving the parameters needed by the driver. """ # a socket to the communication library is created or linked if plumed is None: raise ImportError("Cannot find plumed libraries to link to a FFPlumed object/") super(FFPlumed, self).__init__(latency, name, pars, dopbc=False) self.plumed = plumed.Plumed(precision) self.precision = precision self.plumeddat = plumeddat self.plumedstep = plumedstep self.init_file = init_file if self.init_file.mode == "xyz": infile = open(self.init_file.value, "r") myframe = read_file(self.init_file.mode, infile) myatoms = myframe['atoms'] mycell = myframe['cell'] myatoms.q *= unit_to_internal("length", self.init_file.units, 1.0) mycell.h *= unit_to_internal("length", self.init_file.units, 1.0) self.natoms = myatoms.natoms self.plumed.cmd("setNatoms", self.natoms) self.plumed.cmd("setPlumedDat", self.plumeddat) self.plumed.cmd("setTimestep", 1.) self.plumed.cmd("setMDEnergyUnits", 2625.4996) # Pass a pointer to the conversion factor between the energy unit used in your code and kJ mol-1 self.plumed.cmd("setMDLengthUnits", 0.052917721) # Pass a pointer to the conversion factor between the length unit used in your code and nm self.plumed.cmd("setMDTimeUnits", 2.4188843e-05) self.plumedrestart = False if self.plumedstep > 0: # we are restarting, signal that PLUMED should continue self.plumedrestart = True self.plumed.cmd("setRestart", 1) self.plumed.cmd("init") self.charges = dstrip(myatoms.q) * 0.0 self.masses = dstrip(myatoms.m) self.lastq = np.zeros(3 * self.natoms)
def print_xyz_path(beads, cell, filedesc=sys.stdout, cell_conv=1.0, atoms_conv=1.0): """Prints all the bead configurations into a XYZ formatted file. Prints all the replicas for each time step separately, rather than all at once. Args: beads: A beads object giving the bead positions. cell: A cell object giving the system box. filedesc: An open writable file object. Defaults to standard output. """ a, b, c, alpha, beta, gamma = mt.h2abc_deg(cell.h * cell_conv) fmt_header = "%d\n# bead: %d CELL(abcABC): %10.5f %10.5f %10.5f %10.5f %10.5f %10.5f \n" natoms = beads.natoms nbeads = beads.nbeads for j in range(nbeads): filedesc.write(fmt_header % (natoms, j, a, b, c, alpha, beta, gamma)) for i in range(natoms): qs = dstrip(beads.q) * atoms_conv lab = dstrip(beads.names) filedesc.write("%8s %12.5e %12.5e %12.5e\n" % (lab[i], qs[j][3 * i], qs[j][3 * i + 1], qs[j][3 * i + 2]))
def step(self, step=None): """ Does one simulation time step. Attributes: qtime: The time taken in updating the positions. """ self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) if step == 0: info(" @GEOP: Initializing BFGS", verbosity.debug) self.d += dstrip(self.forces.f) / np.sqrt( np.dot(self.forces.f.flatten(), self.forces.f.flatten())) if len(self.fixatoms) > 0: for dqb in self.d: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pot self.old_f[:] = self.forces.f if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 fdf0 = (self.old_u, -self.old_f) # Do one iteration of BFGS # The invhessian and the directions are updated inside. BFGS(self.old_x, self.d, self.gm, fdf0, self.invhessian, self.big_step, self.ls_options["tolerance"] * self.tolerances["energy"], self.ls_options["iter"]) info(" Number of force calls: %d" % (self.gm.fcount)) self.gm.fcount = 0 # Update positions and forces self.beads.q = self.gm.dbeads.q self.forces.transfer_forces( self.gm.dforces) # This forces the update of the forces # Exit simulation step d_x_max = np.amax(np.absolute(np.subtract(self.beads.q, self.old_x))) self.exitstep(self.forces.pot, self.old_u, d_x_max)
def step(self, step=None): """ Does one simulation time step. Attributes: qtime: The time taken in updating the positions. """ self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) if step == 0: info(" @GEOP: Initializing BFGS", verbosity.debug) self.d += dstrip(self.forces.f) / np.sqrt(np.dot(self.forces.f.flatten(), self.forces.f.flatten())) if len(self.fixatoms) > 0: for dqb in self.d: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pot self.old_f[:] = self.forces.f if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 fdf0 = (self.old_u, -self.old_f) # Do one iteration of BFGS # The invhessian and the directions are updated inside. BFGS(self.old_x, self.d, self.gm, fdf0, self.invhessian, self.big_step, self.ls_options["tolerance"] * self.tolerances["energy"], self.ls_options["iter"]) info(" Number of force calls: %d" % (self.gm.fcount)); self.gm.fcount = 0 # Update positions and forces self.beads.q = self.gm.dbeads.q self.forces.transfer_forces(self.gm.dforces) # This forces the update of the forces # Exit simulation step d_x_max = np.amax(np.absolute(np.subtract(self.beads.q, self.old_x))) self.exitstep(self.forces.pot, self.old_u, d_x_max)
def step(self, step=None): """ Does one simulation time step Attributes: ttime: The time taken in applying the thermostat steps. """ self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) if step == 0: info(" @GEOP: Initializing L-BFGS", verbosity.debug) self.d += dstrip(self.forces.f) / np.sqrt(np.dot(self.forces.f.flatten(), self.forces.f.flatten())) self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pot self.old_f[:] = self.forces.f if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 fdf0 = (self.old_u, -self.old_f) # We update everything within L_BFGS (and all other calls). L_BFGS(self.old_x, self.d, self.gm, self.qlist, self.glist, fdf0, self.big_step, self.ls_options["tolerance"] * self.tolerances["energy"], self.ls_options["iter"], self.corrections, self.scale, step) info(" Number of force calls: %d" % (self.gm.fcount)); self.gm.fcount = 0 # Update positions and forces self.beads.q = self.gm.dbeads.q self.forces.transfer_forces(self.gm.dforces) # This forces the update of the forces # Exit simulation step d_x_max = np.amax(np.absolute(np.subtract(self.beads.q, self.old_x))) self.exitstep(self.forces.pot, self.old_u, d_x_max)
def queue(self, atoms, cell, reqid=-1): """Adds a request. Note that the pars dictionary need to be sent as a string of a standard format so that the initialisation of the driver can be done. Args: atoms: An Atoms object giving the atom positions. cell: A Cell object giving the system box. pars: An optional dictionary giving the parameters to be sent to the driver for initialisation. Defaults to {}. reqid: An optional integer that identifies requests of the same type, e.g. the bead index Returns: A list giving the status of the request of the form {'pos': An array giving the atom positions folded back into the unit cell, 'cell': Cell object giving the system box, 'pars': parameter string, 'result': holds the result as a list once the computation is done, 'status': a string labelling the status of the calculation, 'id': the id of the request, usually the bead number, 'start': the starting time for the calculation, used to check for timeouts.}. """ par_str = " " if not self.pars is None: for k, v in self.pars.items(): par_str += k + " : " + str(v) + " , " else: par_str = " " pbcpos = dstrip(atoms.q).copy() # Indexes come from input in a per atom basis and we need to make a per atom-coordinate basis # Reformat indexes for full system (default) or piece of system # fullat=True if self.active[0] == -1: activehere = np.array([i for i in range(len(pbcpos))]) else: activehere = np.array([[3 * n, 3 * n + 1, 3 * n + 2] for n in self.active]) # fullat=False # # if (self.active[0]!=-1 and fullat==False): # temp=np.array([[3*n, 3*n+1, 3*n+2] for n in self.active]) # Reassign active indexes in order to use them activehere = activehere.flatten() # Perform sanity check for active atoms if (len(activehere) > len(pbcpos) or activehere[-1] > (len(pbcpos) - 1)): raise ValueError("There are more active atoms than atoms!") if self.dopbc: cell.array_pbc(pbcpos) newreq = ForceRequest({ "id": reqid, "pos": pbcpos, "active": activehere, "cell": (dstrip(cell.h).copy(), dstrip(cell.ih).copy()), "pars": par_str, "result": None, "status": "Queued", "start": -1, "t_queued": time.time(), "t_dispatched": 0, "t_finished": 0 }) self._threadlock.acquire() try: self.requests.append(newreq) finally: self._threadlock.release() return newreq
def queue(self, atoms, cell, reqid=-1, template=None): """Adds a request. Note that the pars dictionary need to be sent as a string of a standard format so that the initialisation of the driver can be done. Args: atoms: An Atoms object giving the atom positions. cell: A Cell object giving the system box. pars: An optional dictionary giving the parameters to be sent to the driver for initialisation. Defaults to {}. reqid: An optional integer that identifies requests of the same type, e.g. the bead index template: a dict giving a base model for the request item - e.g. to add entries that are not needed for the base class execution Returns: A dict giving the status of the request of the form {'pos': An array giving the atom positions folded back into the unit cell, 'cell': Cell object giving the system box, 'pars': parameter string, 'result': holds the result as a list once the computation is done, 'status': a string labelling the status of the calculation, 'id': the id of the request, usually the bead number, 'start': the starting time for the calculation, used to check for timeouts.}. """ par_str = " " if self.pars is not None: for k, v in list(self.pars.items()): par_str += k + " : " + str(v) + " , " else: par_str = " " pbcpos = dstrip(atoms.q).copy() # Indexes come from input in a per atom basis and we need to make a per atom-coordinate basis # Reformat indexes for full system (default) or piece of system # active atoms do not change but we only know how to build this array once we get the positions once if self.iactive is None: if self.active[0] == -1: activehere = np.arange(len(pbcpos)) else: activehere = np.array([[3 * n, 3 * n + 1, 3 * n + 2] for n in self.active]) # Reassign active indexes in order to use them activehere = activehere.flatten() # Perform sanity check for active atoms if len(activehere) > len(pbcpos) or activehere[-1] > (len(pbcpos) - 1): raise ValueError("There are more active atoms than atoms!") self.iactive = activehere if self.dopbc: cell.array_pbc(pbcpos) if template is None: template = {} template.update({ "id": reqid, "pos": pbcpos, "active": self.iactive, "cell": (dstrip(cell.h).copy(), dstrip(cell.ih).copy()), "pars": par_str, "result": None, "status": "Queued", "start": -1, "t_queued": time.time(), "t_dispatched": 0, "t_finished": 0, }) newreq = ForceRequest(template) with self._threadlock: self.requests.append(newreq) if not self.threaded: self.poll() return newreq
def step(self, step=None): """Does one simulation time step Attributes: ptime: The time taken in updating the velocities. qtime: The time taken in updating the positions. ttime: The time taken in applying the thermostat steps. """ self.ptime = 0.0 self.ttime = 0.0 self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) if step == 0: gradf1 = dq1 = dstrip(self.forces.f) # Move direction for 1st conjugate gradient step dq1_unit = dq1 / np.sqrt(np.dot(gradf1.flatten(), gradf1.flatten())) info(" @GEOP: Determined SD direction", verbosity.debug) else: gradf0 = self.old_f dq0 = self.d gradf1 = dstrip(self.forces.f) beta = np.dot((gradf1.flatten() - gradf0.flatten()), gradf1.flatten()) / (np.dot(gradf0.flatten(), gradf0.flatten())) dq1 = gradf1 + max(0.0, beta) * dq0 dq1_unit = dq1 / np.sqrt(np.dot(dq1.flatten(), dq1.flatten())) info(" @GEOP: Determined CG direction", verbosity.debug) # Store force and direction for next CG step self.d[:] = dq1 self.old_f[:] = gradf1 if len(self.fixatoms) > 0: for dqb in dq1_unit: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 self.lm.set_dir(dstrip(self.beads.q), dq1_unit) # Reuse initial value since we have energy and forces already u0, du0 = (self.forces.pot.copy(), np.dot(dstrip(self.forces.f.flatten()), dq1_unit.flatten())) # Do one CG iteration; return positions and energy min_brent(self.lm, fdf0=(u0, du0), x0=0.0, tol=self.ls_options["tolerance"] * self.tolerances["energy"], itmax=self.ls_options["iter"], init_step=self.ls_options["step"]) info(" Number of force calls: %d" % (self.lm.fcount)); self.lm.fcount = 0 # Update positions and forces self.beads.q = self.lm.dbeads.q self.forces.transfer_forces(self.lm.dforces) # This forces the update of the forces d_x = np.absolute(np.subtract(self.beads.q, self.lm.x0)) x = np.linalg.norm(d_x) # Automatically adapt the search step for the next iteration. # Relaxes better with very small step --> multiply by factor of 0.1 or 0.01 self.ls_options["step"] = 0.1 * x * self.ls_options["adaptive"] + (1 - self.ls_options["adaptive"]) * self.ls_options["step"] # Exit simulation step d_x_max = np.amax(np.absolute(d_x)) self.exitstep(self.forces.pot, u0, d_x_max)
def queue(self, atoms, cell, reqid=-1): """Adds a request. Note that the pars dictionary need to be sent as a string of a standard format so that the initialisation of the driver can be done. Args: atoms: An Atoms object giving the atom positions. cell: A Cell object giving the system box. pars: An optional dictionary giving the parameters to be sent to the driver for initialisation. Defaults to {}. reqid: An optional integer that identifies requests of the same type, e.g. the bead index Returns: A list giving the status of the request of the form {'pos': An array giving the atom positions folded back into the unit cell, 'cell': Cell object giving the system box, 'pars': parameter string, 'result': holds the result as a list once the computation is done, 'status': a string labelling the status of the calculation, 'id': the id of the request, usually the bead number, 'start': the starting time for the calculation, used to check for timeouts.}. """ par_str = " " if not self.pars is None: for k, v in self.pars.items(): par_str += k + " : " + str(v) + " , " else: par_str = " " pbcpos = dstrip(atoms.q).copy() # Indexes come from input in a per atom basis and we need to make a per atom-coordinate basis # Reformat indexes for full system (default) or piece of system # active atoms do not change but we only know how to build this array once we get the positions once if self.iactive is None: if self.active[0] == -1: activehere = np.arange(len(pbcpos)) else: activehere = np.array([[3 * n, 3 * n + 1, 3 * n + 2] for n in self.active]) # Reassign active indexes in order to use them activehere = activehere.flatten() # Perform sanity check for active atoms if (len(activehere) > len(pbcpos) or activehere[-1] > (len(pbcpos) - 1)): raise ValueError("There are more active atoms than atoms!") self.iactive = activehere if self.dopbc: cell.array_pbc(pbcpos) newreq = ForceRequest({ "id": reqid, "pos": pbcpos, "active": self.iactive, "cell": (dstrip(cell.h).copy(), dstrip(cell.ih).copy()), "pars": par_str, "result": None, "status": "Queued", "start": -1, "t_queued": time.time(), "t_dispatched": 0, "t_finished": 0 }) with self._threadlock: self.requests.append(newreq) if not self.threaded: self.poll() return newreq
def main(prefix, temp): T = float(temp) fns_pos = sorted(glob.glob(prefix + ".pos*")) fns_for = sorted(glob.glob(prefix + ".for*")) fn_out_kin = prefix + ".kin.xyz" fn_out_kod = prefix + ".kod.xyz" # check that we found the same number of positions and forces files nbeads = len(fns_pos) if nbeads != len(fns_for): print fns_pos print fns_for raise ValueError("Mismatch between number of input files for forces and positions.") # print some information print 'temperature = {:f} K'.format(T) print print 'number of beads = {:d}'.format(nbeads) print print 'positions and forces file names:' for fn_pos, fn_for in zip(fns_pos, fns_for): print '{:s} {:s}'.format(fn_pos, fn_for) print print 'output file names:' print fn_out_kin print fn_out_kod print temp = unit_to_internal("energy", "kelvin", T) # open input and output files ipos = [open(fn, "r") for fn in fns_pos] ifor = [open(fn, "r") for fn in fns_for] ikin = open(fn_out_kin, "w") ikod = open(fn_out_kod, "w") natoms = 0 ifr = 0 while True: # print progress if ifr % 100 == 0: print '\rProcessing frame {:d}'.format(ifr), sys.stdout.flush() # load one frame try: for i in range(nbeads): ret = read_file("xyz", ipos[i]) pos = ret["atoms"] ret = read_file("xyz", ifor[i]) force = ret["atoms"] if natoms == 0: natoms = pos.natoms beads = Beads(natoms, nbeads) forces = Beads(natoms, nbeads) kcv = np.zeros((natoms, 6), float) beads[i].q = pos.q forces[i].q = force.q except EOFError: # finished reading files break # calculate kinetic energies q = dstrip(beads.q) f = dstrip(forces.q) qc = dstrip(beads.qc) kcv[:] = 0 for j in range(nbeads): for i in range(natoms): kcv[i, 0] += (q[j, i * 3 + 0] - qc[i * 3 + 0]) * f[j, i * 3 + 0] kcv[i, 1] += (q[j, i * 3 + 1] - qc[i * 3 + 1]) * f[j, i * 3 + 1] kcv[i, 2] += (q[j, i * 3 + 2] - qc[i * 3 + 2]) * f[j, i * 3 + 2] kcv[i, 3] += (q[j, i * 3 + 0] - qc[i * 3 + 0]) * f[j, i * 3 + 1] + (q[j, i * 3 + 1] - qc[i * 3 + 1]) * f[j, i * 3 + 0] kcv[i, 4] += (q[j, i * 3 + 0] - qc[i * 3 + 0]) * f[j, i * 3 + 2] + (q[j, i * 3 + 2] - qc[i * 3 + 2]) * f[j, i * 3 + 0] kcv[i, 5] += (q[j, i * 3 + 1] - qc[i * 3 + 1]) * f[j, i * 3 + 2] + (q[j, i * 3 + 2] - qc[i * 3 + 2]) * f[j, i * 3 + 1] kcv *= -0.5 / nbeads kcv[:, 0:3] += 0.5 * Constants.kb * temp kcv[:, 3:6] *= 0.5 # write output ikin.write("%d\n# Centroid-virial kinetic energy estimator [a.u.] - diagonal terms: xx yy zz\n" % natoms) ikod.write("%d\n# Centroid-virial kinetic energy estimator [a.u.] - off-diag terms: xy xz yz\n" % natoms) for i in range(natoms): ikin.write("%8s %12.5e %12.5e %12.5e\n" % (pos.names[i], kcv[i, 0], kcv[i, 1], kcv[i, 2])) ikod.write("%8s %12.5e %12.5e %12.5e\n" % (pos.names[i], kcv[i, 3], kcv[i, 4], kcv[i, 5])) ifr += 1 print '\rProcessed {:d} frames.'.format(ifr) ikin.close() ikod.close()
def step(self, step=None): kT = Constants.kb * self.ensemble.temp # computes current energy (if not already stored) ostr = "".join( self.state ) # this is a unique id string that charactrizes the current state self.tscache[ostr] = {} if not ostr in self.ecache: self.dbeads[0].q[0, :] = self.sites[self.unique_idx( self.state)].flatten() rv = [0, 0, 0, 0, 0] self.geop_thread(0, ostr, rv) #self.beads.q[0,:] = self.dbeads[0].q[0,:] # also updates current position # self.forces.transfer_forces(self.dforces[0]) # forces have already been computed here... ecurr = self.ecache[ostr] # enumerates possible reactive events (vacancy swaps) levents = [] ethreads = [None] * self.neval # loops over the vacancy for ivac in xrange(self.natoms, self.natoms + self.nvac): svac = self.idx[ivac] # lattice site associated with this vacancy if self.state[svac] != "V": raise IndexError( "Something got screwed and a vacancy state is not a vacancy anymore!" ) # loops over the neighbors of the selected vacancy for sneigh in self.neigh[svac]: # if the neighbor is a vacancy, move on. does not make sense to swap two vacancies! if self.state[sneigh] == "V": continue # creates a new state vector with swapped atoms-vacancy and the associated label string nstate = self.state.copy() nstate[svac], nstate[sneigh] = self.state[sneigh], self.state[ svac] nstr = "".join( nstate ) # this is the string that corresponds to the new state if not nstr in self.ecache: # new state, must compute! # creates a swapped index nidx = self.idx.copy() if self.ridx[svac] != ivac or self.idx[ivac] != svac: raise IndexError( "Something got screwed and the index does not correspond anymore to site occupancy" ) #ivac = self.ridx[svac] ineigh = self.ridx[ sneigh] # gets index of atom associated with the neighboring site nidx[ivac], nidx[ineigh] = self.idx[ineigh], self.idx[ivac] ieval = self.find_eval(ethreads) # launches evaluator self.dbeads[ieval].q[0, :] = self.sites[self.unique_idx( nstate)].flatten() nevent = [svac, sneigh, 0.0, 0.0, 0.0] # runs a geometry optimization #self.geop_thread(ieval=ieval, nstr=nstr, nevent=nevent) st = threading.Thread(target=self.geop_thread, name=str(ieval), kwargs={ "ieval": ieval, "nstr": nstr, "nevent": nevent, "ostr": ostr }) st.daemon = True st.start() ethreads[ieval] = st else: print "Found state ", nstr, " retrieving cached energy ", self.ecache[ nstr] # fetch energy from previous calculation nevent = [ svac, sneigh, self.ecache[nstr], self.qcache[nstr], 0.0 ] # EVALUATION OF TS ENERGY IS DISABLED FOR THE MOMENT... # we might still need to compute the TS energy! # if not nstr in self.tscache[ostr]: # print "Computing TS" # ieval = self.find_eval(ethreads) # st = threading.Thread(target=self.ts_thread, name=str(ieval), kwargs={"ieval":ieval, "ostr": ostr, "nstr":nstr, "nevent" : nevent}) # st.daemon = True # st.start() # ethreads[ieval] = st #else: # print "Found TS" # nevent[3] = self.tscache[ostr][nstr] nevent[-1] = self.state[sneigh] levents.append(nevent) # wait for all evaluators to finish for st in ethreads: while not st is None and st.isAlive(): st.join(2) print "Computed ", len( levents), " possible reactions. Cache len ", len(self.ecache) # get list of rates rates = np.zeros(len(levents), float) crates = np.zeros(len(levents), float) cdf = 0.0 for i in xrange(len(levents)): #print ("Barrier, naive: %f, static: %f" % (0.5*(ecurr + levents[i][2]) + self.diffusion_barrier_al, levents[i][4])) ets = 0.5 * (ecurr + levents[i][2]) + self.barriers[ levents[i][-1]] #naive heuristic for the ts energy print "Event ", i, levents[i][-1], ecurr, ">>", ets, ">>", levents[ i][2] rates[i] = self.prefactors[levents[i][-1]] * np.exp( -(ets - ecurr) / kT) cdf += rates[i] crates[i] = cdf # KMC selection rule based on the rate fpick = self.prng.u * cdf isel = 0 while fpick > crates[isel]: isel += 1 dt = -1.0 / cdf * np.log(1.0 - self.prng.u) print("Time spent %12.5e at %s nrg %12.5e" % (dt, ostr, ecurr)) print "Selected event ", isel, " with rate ", rates[isel], " / ", cdf iev = levents[isel] # levents[self.prng.randint(len(levents))] svac, sneigh = iev[0], iev[1] ivac, ineigh = self.ridx[svac], self.ridx[sneigh] # does the swap (never reject, for the moment) self.state[svac], self.state[sneigh] = self.state[sneigh], self.state[ svac] self.ridx[svac], self.ridx[sneigh] = self.ridx[sneigh], self.ridx[svac] self.idx[ivac], self.idx[ineigh] = self.idx[ineigh], self.idx[ivac] # we got a new configuration but the residence time is linked to the previous configuration so we output that self.kmcfile.write("%12.5e %12.5e %18.11e %s\n" % (self.tottime, dt, ecurr, ostr)) self.kmcfile.flush() self.tottime += dt self.ensemble.time += dt # updates time counter print "Finishing step at ", "".join(self.state) # updates the positions self.cell.h = self.dcell.h uidx = self.unique_idx(self.state) ruidx = np.zeros(self.nsites, int) ruidx[uidx] = range(self.nsites) self.sites[self.unique_idx(self.state)] oldq = dstrip(self.beads.q[0]).copy() newq = np.zeros(self.nsites * 3, float) # we want continuity (modulo PBC jumps, that we'll take care of later...) for i in xrange(self.nsites): # in which site sits atom i? isite = self.idx[i] # which atom sits in this site in the unique-mapped structure? iuid = ruidx[self.idx[i]] newq[3 * i:3 * i + 3] = iev[3][3 * iuid:3 * (iuid + 1)] newq -= oldq self.cell.array_pbc(newq) self.beads.q[0] += newq
def step(self, step=None): """ Does one simulation time step.""" self.qtime = -time.time() info("\n Instanton optimization STEP %d" % step, verbosity.low) if step == 0: info(" @GEOP: Initializing instanton", verbosity.low) if self.beads.nbeads == 1: raise ValueError("We can not perform an splitting calculation with nbeads =1") # get_hessian(self.hessian, self.gm, self.beads.q) else: if ((self.beads.q - self.beads.q[0]) == 0).all(): # If the coordinates in all the imaginary time slices are the same info(" @GEOP: We stretch the initial geometry with an 'amplitud' of %4.2f" % self.delta, verbosity.low) imvector = get_imvector(self.initial_hessian, self.beads.m3[0].flatten()) for i in range(self.beads.nbeads): self.beads.q[i, :] += self.delta * np.cos(i * np.pi / float(self.beads.nbeads - 1)) * imvector[:] else: info(" @GEOP: Starting from the provided geometry in the extended phase space", verbosity.low) # Update positions and forces self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pots self.old_f[:] = self.forces.f # This must be done after the stretching and before the self.d. if type(self.im.f) == type(None): self.im(self.beads.q, ret=False) # Init instanton mapper # Specific for LBFGS if np.linalg.norm(self.d) == 0.0: f = self.forces.f + self.im.f # ALBERTO1 self.d += dstrip(f) / np.sqrt(np.dot(f.flatten(), f.flatten())) if (self.old_x == np.zeros((self.beads.nbeads, 3 * self.beads.natoms), float)).all(): self.old_x[:] = self.beads.q if self.exit: softexit.trigger("Geometry optimization converged. Exiting simulation") if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 e, g = self.fm(self.beads.q) fdf0 = (e, g) # Do one step. Update hessian for the new position. Update the position and force inside the mapper. L_BFGS(self.old_x, self.d, self.fm, self.qlist, self.glist, fdf0, self.big_step, self.ls_options["tolerance"] * self.tolerances["energy"], self.ls_options["iter"], self.corrections, self.scale, step) # ALBERTO2 # Update positions and forces self.beads.q = self.gm.dbeads.q self.forces.transfer_forces(self.gm.dforces) # This forces the update of the forces # Exit simulation step d_x_max = np.amax(np.absolute(np.subtract(self.beads.q, self.old_x))) self.exit = self.exitstep(self.forces.pot, self.old_u.sum(), d_x_max, self.exit, step) # Update positions and forces self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pots self.old_f[:] = self.forces.f # Print current instanton geometry and hessian if (self.save > 0 and np.mod(step, self.save) == 0) or self.exit: print_instanton_geo(self.prefix, step, self.im.dbeads.nbeads, self.im.dbeads.natoms, self.im.dbeads.names, self.im.dbeads.q, self.old_u, self.cell, self.energy_shift, self.output_maker)
def step(self, step=None): """ Does one simulation time step.""" self.qtime = -time.time() info("\n Instanton optimization STEP %d" % step, verbosity.low) if step == 0: info(" @GEOP: Initializing INSTANTON", verbosity.low) if self.beads.nbeads == 1: raise ValueError( "We can not perform an splitting calculation with nbeads =1" ) # get_hessian(self.hessian, self.gm, self.beads.q) else: if ((self.beads.q - self.beads.q[0]) == 0).all( ): # If the coordinates in all the imaginary time slices are the same info( " @GEOP: We stretch the initial geometry with an 'amplitud' of %4.2f" % self.delta, verbosity.low) imvector = get_imvector(self.initial_hessian, self.beads.m3[0].flatten()) for i in range(self.beads.nbeads): self.beads.q[i, :] += self.delta * np.cos( i * np.pi / float(self.beads.nbeads - 1)) * imvector[:] else: info( " @GEOP: Starting from the provided geometry in the extended phase space", verbosity.low) # Update positions and forces self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pots self.old_f[:] = self.forces.f # This must be done after the stretching and before the self.d. if type(self.im.f) == type(None): self.im(self.beads.q, ret=False) # Init instanton mapper # Specific for LBFGS if np.linalg.norm(self.d) == 0.0: f = self.forces.f + self.im.f #ALBERTO1 self.d += dstrip(f) / np.sqrt(np.dot(f.flatten(), f.flatten())) if (self.old_x == np.zeros((self.beads.nbeads, 3 * self.beads.natoms), float)).all(): self.old_x[:] = self.beads.q if self.exit: softexit.trigger( "Geometry optimization converged. Exiting simulation") if len(self.fixatoms) > 0: for dqb in self.old_f: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 e, g = self.fm(self.beads.q) fdf0 = (e, g) # Do one step. Update hessian for the new position. Update the position and force inside the mapper. L_BFGS(self.old_x, self.d, self.fm, self.qlist, self.glist, fdf0, self.big_step, self.ls_options["tolerance"] * self.tolerances["energy"], self.ls_options["iter"], self.corrections, self.scale, step) # ALBERTO2 # Update positions and forces self.beads.q = self.gm.dbeads.q self.forces.transfer_forces( self.gm.dforces) # This forces the update of the forces # Exit simulation step d_x_max = np.amax(np.absolute(np.subtract(self.beads.q, self.old_x))) self.exit = self.exitstep(self.forces.pot, self.old_u.sum(), d_x_max, self.exit, step) # Update positions and forces self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pots self.old_f[:] = self.forces.f # Print current instanton geometry and hessian if (self.save > 0 and np.mod(step, self.save) == 0) or self.exit: print_instanton_geo(self.prefix, step, self.im.dbeads.nbeads, self.im.dbeads.natoms, self.im.dbeads.names, self.im.dbeads.q, self.old_u, self.cell, self.energy_shift)
def main(prefix, temp): T = float(temp) fns_pos = sorted(glob.glob(prefix + ".pos*")) fns_for = sorted(glob.glob(prefix + ".for*")) fn_out_kin = prefix + ".kin.xyz" fn_out_kod = prefix + ".kod.xyz" # check that we found the same number of positions and forces files nbeads = len(fns_pos) if nbeads != len(fns_for): print fns_pos print fns_for raise ValueError( "Mismatch between number of input files for forces and positions.") # print some information print 'temperature = {:f} K'.format(T) print print 'number of beads = {:d}'.format(nbeads) print print 'positions and forces file names:' for fn_pos, fn_for in zip(fns_pos, fns_for): print '{:s} {:s}'.format(fn_pos, fn_for) print print 'output file names:' print fn_out_kin print fn_out_kod print temp = unit_to_internal("energy", "kelvin", T) # open input and output files ipos = [open(fn, "r") for fn in fns_pos] ifor = [open(fn, "r") for fn in fns_for] ikin = open(fn_out_kin, "w") ikod = open(fn_out_kod, "w") natoms = 0 ifr = 0 while True: # print progress if ifr % 100 == 0: print '\rProcessing frame {:d}'.format(ifr), sys.stdout.flush() # load one frame try: for i in range(nbeads): ret = read_file("xyz", ipos[i]) pos = ret["atoms"] ret = read_file("xyz", ifor[i]) force = ret["atoms"] if natoms == 0: natoms = pos.natoms beads = Beads(natoms, nbeads) forces = Beads(natoms, nbeads) kcv = np.zeros((natoms, 6), float) beads[i].q = pos.q forces[i].q = force.q except EOFError: # finished reading files break # calculate kinetic energies q = dstrip(beads.q) f = dstrip(forces.q) qc = dstrip(beads.qc) kcv[:] = 0 for j in range(nbeads): for i in range(natoms): kcv[i, 0] += (q[j, i * 3 + 0] - qc[i * 3 + 0]) * f[j, i * 3 + 0] kcv[i, 1] += (q[j, i * 3 + 1] - qc[i * 3 + 1]) * f[j, i * 3 + 1] kcv[i, 2] += (q[j, i * 3 + 2] - qc[i * 3 + 2]) * f[j, i * 3 + 2] kcv[i, 3] += ( q[j, i * 3 + 0] - qc[i * 3 + 0]) * f[j, i * 3 + 1] + ( q[j, i * 3 + 1] - qc[i * 3 + 1]) * f[j, i * 3 + 0] kcv[i, 4] += ( q[j, i * 3 + 0] - qc[i * 3 + 0]) * f[j, i * 3 + 2] + ( q[j, i * 3 + 2] - qc[i * 3 + 2]) * f[j, i * 3 + 0] kcv[i, 5] += ( q[j, i * 3 + 1] - qc[i * 3 + 1]) * f[j, i * 3 + 2] + ( q[j, i * 3 + 2] - qc[i * 3 + 2]) * f[j, i * 3 + 1] kcv *= -0.5 / nbeads kcv[:, 0:3] += 0.5 * Constants.kb * temp kcv[:, 3:6] *= 0.5 # write output ikin.write( "%d\n# Centroid-virial kinetic energy estimator [a.u.] - diagonal terms: xx yy zz\n" % natoms) ikod.write( "%d\n# Centroid-virial kinetic energy estimator [a.u.] - off-diag terms: xy xz yz\n" % natoms) for i in range(natoms): ikin.write("%8s %12.5e %12.5e %12.5e\n" % (pos.names[i], kcv[i, 0], kcv[i, 1], kcv[i, 2])) ikod.write("%8s %12.5e %12.5e %12.5e\n" % (pos.names[i], kcv[i, 3], kcv[i, 4], kcv[i, 5])) ifr += 1 print '\rProcessed {:d} frames.'.format(ifr) ikin.close() ikod.close()