def get_all(self): """Driver routine. When one of the force, potential or virial are called, this sends the atoms and cell to the driver through the interface, requesting that the driver does the calculation. This then waits until the driver is finished, and then returns the ufvx list. Returns: A list of the form [potential, force, virial, extra]. """ # this is converting the distribution library requests into [ u, f, v ] lists if self.request is None: self.request = self.socket.queue(self.atoms, self.cell, pars=self.pars, reqid=-1) while self.request["status"] != "Done": if self.request["status"] == "Exit": break time.sleep(self.socket.latency) if self.request["status"] == "Exit": softexit.trigger(" @Force: Requested returned a Exit status") # data has been collected, so the request can be released and a slot #freed up for new calculations self.socket.release(self.request) result = self.request["result"] self.request = None return result
def pre_step(self, step=None, adaptative=False): """ Todo before actual step""" if self.exit: softexit.trigger("Geometry optimization converged. Exiting simulation") func = lambda x: 2 * np.sum(x) - x[0] - x[-1] - 2 * self.im.dbeads.nbeads if not self.init: self.initialize(step) # print('old_coef',func(self.im.coef)) #cons = scipy.optimize.NonlinearConstraint(func,-0.1,0.1) #cons=({'type':'eq','fun':lambda x: 2*np.sum(x)-x[0]-x[1]-2*self.im.dbeads.nbeads}) if adaptative: import scipy.optimize new_coef = scipy.optimize.minimize(self.opt_coef, self.im.coef, method='L-BFGS-B', options={'gtol': 1e-8, 'disp': False}) func = lambda x: 2 * np.sum(x) - x[0] - x[-1] coef = np.absolute(new_coef.x) s = func(coef) coef *= 2 * self.im.dbeads.nbeads / s self.im.set_coef(coef) self.gm.set_coef(coef) self.qtime = -time.time() info("\n Instanton optimization STEP {}".format(step), verbosity.low) activearrays = self.fix.get_active_array(self.optarrays) return activearrays
def exitstep(self, fx, u0, x): """ Exits the simulation step. Computes time, checks for convergence. """ info(" @GEOP: Updating bead positions", verbosity.debug) self.qtime += time.time() f = np.amax(np.absolute(self.forces.f)) e = np.absolute((fx - u0) / self.beads.natoms) info("@GEOP", verbosity.medium) self.tolerances["position"] info(" Current energy %e" % (fx)) info( " Position displacement %e Tolerance %e" % (x, self.tolerances["position"]), verbosity.medium) info( " Max force component %e Tolerance %e" % (f, self.tolerances["force"]), verbosity.medium) info( " Energy difference per atom %e Tolerance %e" % (e, self.tolerances["energy"]), verbosity.medium) if (np.linalg.norm(self.forces.f.flatten() - self.old_f.flatten()) <= 1e-20): softexit.trigger( "Something went wrong, the forces are not changing anymore." " This could be due to an overly small tolerance threshold " "that makes no physical sense. Please check if you are able " "to reach such accuracy with your force evaluation" " code (client).") if (np.absolute((fx - u0) / self.beads.natoms) <= self.tolerances["energy"]) \ and ( ( np.amax(np.absolute(self.forces.f)) <= self.tolerances["force"])) \ and (x <= self.tolerances["position"]): softexit.trigger( "Geometry optimization converged. Exiting simulation")
def exitstep(self, fx, u0, x): """ Exits the simulation step. Computes time, checks for convergence. """ info(" @GEOP: Updating bead positions", verbosity.debug) self.qtime += time.time() if len(self.fixatoms) > 0: ftmp = self.forces.f.copy() for dqb in ftmp: dqb[self.fixatoms * 3] = 0.0 dqb[self.fixatoms * 3 + 1] = 0.0 dqb[self.fixatoms * 3 + 2] = 0.0 fmax = np.amax(np.absolute(ftmp)) else: fmax = np.amax(np.absolute(self.forces.f)) e = np.absolute((fx - u0) / self.beads.natoms) info("@GEOP", verbosity.medium) self.tolerances["position"] info(" Current energy %e" % (fx)) info(" Position displacement %e Tolerance %e" % (x, self.tolerances["position"]), verbosity.medium) info(" Max force component %e Tolerance %e" % (fmax, self.tolerances["force"]), verbosity.medium) info(" Energy difference per atom %e Tolerance %e" % (e, self.tolerances["energy"]), verbosity.medium) if (np.linalg.norm(self.forces.f.flatten() - self.old_f.flatten()) <= 1e-20): softexit.trigger("Something went wrong, the forces are not changing anymore." " This could be due to an overly small tolerance threshold " "that makes no physical sense. Please check if you are able " "to reach such accuracy with your force evaluation" " code (client).") if (np.absolute((fx - u0) / self.beads.natoms) <= self.tolerances["energy"]) \ and (fmax <= self.tolerances["force"]) \ and (x <= self.tolerances["position"]): softexit.trigger("Geometry optimization converged. Exiting simulation")
def _kill_handler(self, signal, frame): """Deals with handling a kill call gracefully. Prevents any of the threads becoming zombies, by intercepting a kill signal using the standard python function signal.signal() and then closing the socket and the spawned threads before closing the main thread. Called when signals SIG_INT and SIG_TERM are received. Args: signal: An integer giving the signal number of the signal received from the socket. frame: Current stack frame. """ warning(" @SOCKET: Kill signal. Trying to make a clean exit.", verbosity.low) self.end_thread() softexit.trigger(" @SOCKET: Kill signal received") try: self.__del__() except: pass if signal in self._prev_kill: self._prev_kill[signal](signal, frame)
def get_all(self): """Driver routine. When one of the force, potential or virial are called, this sends the atoms and cell to the client code, requesting that it calculates the potential, forces and virial tensor. This then waits until the driver is finished, and then returns the ufvx list. Returns: A list of the form [potential, force, virial, extra]. """ # because we thread over many systems and outputs, we might get called # more than once. keep track of how many times we are called so we # can make sure to wait until the last call has returned before we release self._threadlock.acquire() try: self._getallcount += 1 finally: self._threadlock.release() # this is converting the distribution library requests into [ u, f, v ] lists if self.request is None: self.request = self.ff.queue(self.atoms, self.cell) # sleeps until the request has been evaluated while self.request["status"] != "Done": if self.request["status"] == "Exit" or softexit.triggered: # now, this is tricky. we are stuck here and we cannot return meaningful results. # if we return, we may as well output wrong numbers, or mess up things. # so we can only call soft-exit and wait until that is done. then kill the thread # we are in. softexit.trigger( " @ FORCES : cannot return so will die off here") while softexit.exiting: time.sleep(self.ff.latencyt) sys.exit() time.sleep(self.ff.latency) # data has been collected, so the request can be released and a slot # freed up for new calculations result = self.request["result"] # reduce the reservation count (and wait for all calls to return) self._threadlock.acquire() try: self._getallcount -= 1 finally: self._threadlock.release() # releases just once, but wait for all requests to be complete if self._getallcount == 0: self.ff.release(self.request) self.request = None else: while self._getallcount > 0: time.sleep(self.ff.latency) return result
def step(self, step=None): """Executes one step of phonon computation. """ if (step < 3 * self.beads.natoms): self.phcalc.step(step) else: self.phcalc.transform() self.refdynmatrix = self.apply_asr(self.refdynmatrix.copy()) self.printall(self.prefix, self.refdynmatrix.copy()) softexit.trigger("Dynamic matrix is calculated. Exiting simulation")
def step(self, step=None): if self.optimizer.converged: # if required, exit upon convergence. otherwise just return without action if self.conv_exit: softexit.trigger("Geometry optimization converged. Exiting simulation") else: info("Convergence threshold met. Will carry on but do nothing.", verbosity.high) else: self.optimizer.step(step)
def get_all(self): """Driver routine. When one of the force, potential or virial are called, this sends the atoms and cell to the client code, requesting that it calculates the potential, forces and virial tensor. This then waits until the driver is finished, and then returns the ufvx list. Returns: A list of the form [potential, force, virial, extra]. """ # because we thread over many systems and outputs, we might get called # more than once. keep track of how many times we are called so we # can make sure to wait until the last call has returned before we release with self._threadlock: self._getallcount += 1 # this is converting the distribution library requests into [ u, f, v ] lists if self.request is None: self.request = self.queue() # sleeps until the request has been evaluated while self.request["status"] != "Done": if self.request["status"] == "Exit" or softexit.triggered: # now, this is tricky. we are stuck here and we cannot return meaningful results. # if we return, we may as well output wrong numbers, or mess up things. # so we can only call soft-exit and wait until that is done. then kill the thread # we are in. softexit.trigger(" @ FORCES : cannot return so will die off here") while softexit.exiting: time.sleep(self.ff.latencyt) sys.exit() time.sleep(self.ff.latency) # print diagnostics about the elapsed time info("# forcefield %s evaluated in %f (queue) and %f (dispatched) sec." % (self.ff.name, self.request["t_finished"] - self.request["t_queued"], self.request["t_finished"] - self.request["t_dispatched"]), verbosity.debug) # data has been collected, so the request can be released and a slot # freed up for new calculations result = self.request["result"] # reduce the reservation count (and wait for all calls to return) with self._threadlock: self._getallcount -= 1 # releases just once, but wait for all requests to be complete if self._getallcount == 0: self.ff.release(self.request) self.request = None else: while self._getallcount > 0: time.sleep(self.ff.latency) return result
def step(self, step=None): if self.isc == self.max_iter: softexit.trigger( " @SCP: Reached maximum iterations. Terminating the SCP calculation." ) if self.imc == 0: self.phononator.reset() elif self.imc >= 1 and self.imc <= self.max_steps: self.phononator.step(step) elif self.imc > self.max_steps: self.phononator.print_energetics() self.phononator.displace()
def step(self, step=None): """Does one replay time step.""" self.ptime = 0.0 self.ttime = 0.0 self.qtime = -time.time() while True: self.rstep += 1 try: if self.intraj.mode == "xyz": for b in self.beads: myframe = read_file("xyz", self.rfile) myatoms = myframe["atoms"] mycell = myframe["cell"] myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) mycell.h *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q self.cell.h[:] = mycell.h elif self.intraj.mode == "pdb": for b in self.beads: myatoms, mycell = read_file("pdb", self.rfile) myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) mycell.h *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q self.cell.h[:] = mycell.h elif self.intraj.mode == "chk" or self.intraj.mode == "checkpoint": # TODO: Adapt the new `Simulation.load_from_xml`? # reads configuration from a checkpoint file xmlchk = xml_parse_file(self.rfile) # Parses the file. from ipi.inputs.simulation import InputSimulation simchk = InputSimulation() simchk.parse(xmlchk.fields[0][1]) mycell = simchk.cell.fetch() mybeads = simchk.beads.fetch() self.cell.h[:] = mycell.h self.beads.q[:] = mybeads.q softexit.trigger(" # Read single checkpoint") except EOFError: softexit.trigger(" # Finished reading re-run trajectory") if (step is None) or (self.rstep > step): break self.qtime += time.time()
def bind(self, ens, beads, nm, cell, bforce, prng, omaker): """Binds beads, cell, bforce and prng to GeopMotion Args: beads: The beads object from which the bead positions are taken. nm: A normal modes object used to do the normal modes transformation. cell: The cell object from which the system box is taken. bforce: The forcefield object from which the force and virial are taken. prng: The random number generator object which controls random number generation. """ super(GeopMotion, self).bind(ens, beads, nm, cell, bforce, prng, omaker) # Binds optimizer self.optimizer.bind(self) if len(self.fixatoms) == len(self.beads[0]): softexit.trigger("WARNING: all atoms are fixed, geometry won't change. Exiting simulation")
def step(self): """Does one simulation time step.""" self.ptime = self.ttime = 0 self.qtime = -time.time() try: if (self.intraj.mode == "xyz"): for b in self.beads: myatoms = read_xyz(self.rfile) myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q elif (self.intraj.mode == "pdb"): for b in self.beads: myatoms, mycell = read_pdb(self.rfile) myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) mycell.h *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q self.cell.h[:] = mycell.h elif (self.intraj.mode == "chk" or self.intraj.mode == "checkpoint"): # reads configuration from a checkpoint file xmlchk = xml_parse_file(self.rfile) # Parses the file. from ipi.inputs.simulation import InputSimulation simchk = InputSimulation() simchk.parse(xmlchk.fields[0][1]) mycell = simchk.cell.fetch() mybeads = simchk.beads.fetch() self.cell.h[:] = mycell.h self.beads.q[:] = mybeads.q softexit.trigger(" # Read single checkpoint") except EOFError: softexit.trigger(" # Finished reading re-run trajectory") self.qtime += time.time()
def step(self): """Does one simulation time step.""" self.ptime = self.ttime = 0 self.qtime = -time.time() try: if self.intraj.mode == "xyz": for b in self.beads: myatoms = read_xyz(self.rfile) myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q elif self.intraj.mode == "pdb": for b in self.beads: myatoms, mycell = read_pdb(self.rfile) myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) mycell.h *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q self.cell.h[:] = mycell.h elif self.intraj.mode == "chk" or self.intraj.mode == "checkpoint": # reads configuration from a checkpoint file xmlchk = xml_parse_file(self.rfile) # Parses the file. from ipi.inputs.simulation import InputSimulation simchk = InputSimulation() simchk.parse(xmlchk.fields[0][1]) mycell = simchk.cell.fetch() mybeads = simchk.beads.fetch() self.cell.h[:] = mycell.h self.beads.q[:] = mybeads.q softexit.trigger(" # Read single checkpoint") except EOFError: softexit.trigger(" # Finished reading re-run trajectory") self.qtime += time.time()
def pool_update(self): """Deals with keeping the pool of client drivers up-to-date during a force calculation step. Deals with maintaining the client list. Clients that have disconnected are removed and their jobs removed from the list of running jobs and new clients are connected to the server. """ for c in self.clients[:]: if not (c.status & Status.Up): try: warning( " @SOCKET: Client " + str(c.peername) + " died or got unresponsive(C). Removing from the list.", verbosity.low) softexit.trigger( "Error: One client has died unexpectedly. Exiting i-PI..." ) # c*g eprint( "Error: One client has died unexpectedly. Exiting i-PI..." ) # c*g c.shutdown(socket.SHUT_RDWR) c.close() except socket.error: pass c.status = Status.Disconnected self.clients.remove(c) for [k, j] in self.jobs[:]: if j is c: self.jobs = [ w for w in self.jobs if not (w[0] is k and w[1] is j) ] # removes pair in a robust way #self.jobs.remove([k,j]) k["status"] = "Queued" k["start"] = -1 if len(self.clients) == 0: searchtimeout = SERVERTIMEOUT else: searchtimeout = 0.0 keepsearch = True while keepsearch: readable, writable, errored = select.select([self.server], [], [], searchtimeout) if self.server in readable: client, address = self.server.accept() client.settimeout(TIMEOUT) driver = Driver(client) info( " @SOCKET: Client asked for connection from " + str(address) + ". Now hand-shaking.", verbosity.low) driver.poll() if (driver.status | Status.Up): self.clients.append(driver) info( " @SOCKET: Handshaking was successful. Added to the client list.", verbosity.low) self.poll_iter = UPDATEFREQ # if a new client was found, will try again a harder next time searchtimeout = SERVERTIMEOUT else: warning( " @SOCKET: Handshaking failed. Dropping connection.", verbosity.low) client.shutdown(socket.SHUT_RDWR) client.close() else: keepsearch = False
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 run(self): """Runs the simulation. Does all the simulation steps, and outputs data to the appropriate files when necessary. Also deals with starting and cleaning up the threads used in the communication between the driver and the PIMD code. """ self.forces.run() # prints inital configuration -- only if we are not restarting if (self.step == 0): self.step = -1 for o in self.outputs: o.write() self.step = 0 steptime = 0.0 simtime = time.time() cstep = 0 tptime = 0.0 tqtime = 0.0 tttime = 0.0 ttot = 0.0 # main MD loop for self.step in range(self.step,self.tsteps): # stores the state before doing a step. # this is a bit time-consuming but makes sure that we can honor soft # exit requests without screwing the trajectory steptime = -time.time() self.chk.store() self.ensemble.step() for o in self.outputs: o.write() if os.path.exists("EXIT"): # soft-exit self.rollback = False softexit.trigger() steptime += time.time() ttot += steptime tptime += self.ensemble.ptime tqtime += self.ensemble.qtime tttime += self.ensemble.ttime cstep += 1 if verbosity.high or (verbosity.medium and self.step%100 == 0) or (verbosity.low and self.step%1000 == 0): info(" # Average timings at MD step % 7d. t/step: %10.5e [p: %10.5e q: %10.5e t: %10.5e]" % ( self.step, ttot/cstep, tptime/cstep, tqtime/cstep, tttime/cstep ) ) cstep = 0 tptime = 0.0 tqtime = 0.0 tttime = 0.0 ttot = 0.0 info(" # MD diagnostics: V: %10.5e Kcv: %10.5e Ecns: %10.5e" % (self.properties["potential"], self.properties["kinetic_cv"], self.properties["conserved"] ) ) if (self.ttime > 0 and time.time() - simtime > self.ttime): info(" # Wall clock time expired! Bye bye!", verbosity.low ) break info(" # Simulation ran successfully for the prescribed total_step! Bye bye!", verbosity.low ) self.rollback = False softexit.trigger()
def step(self, step=None): """Does one simulation time step.""" info("\nMD STEP %d" % step, verbosity.debug) # Fetch spring constants self.nebbfgsm.kappa = self.spring["kappa"] self.neblm.kappa = self.spring["kappa"] self.ptime = self.ttime = 0 self.qtime = -time.time() if self.mode == "lbfgs": # L-BFGS Minimization # Initialize direction to the steepest descent direction if step == 0: # or np.sqrt(np.dot(self.bfgsm.d, self.bfgsm.d)) == 0.0: <-- this part for restarting at claimed minimum info(" @GEOP: Initializing L-BFGS", verbosity.debug) fx, nebgrad = self.nebbfgsm(self.beads.q) # Set direction to direction of NEB forces self.nebbfgsm.d = -nebgrad self.nebbfgsm.xold = self.beads.q.copy() # Initialize lists of previous positions and gradients self.qlist = np.zeros( (self.corrections, len(self.beads.q.flatten()))) self.glist = np.zeros( (self.corrections, len(self.beads.q.flatten()))) else: fx, nebgrad = self.nebbfgsm(self.beads.q) # Intial gradient and gradient modulus u0, du0 = (fx, nebgrad) # Store old force self.old_f[:] = -nebgrad # Do one iteration of L-BFGS and return positions, gradient modulus, # direction, list of positions, list of gradients # self.beads.q, fx, self.nebbfgsm.d, self.qlist, self.glist = L_BFGS(self.beads.q, L_BFGS(self.beads.q, self.nebbfgsm.d, self.nebbfgsm, self.qlist, self.glist, fdf0=(u0, du0), big_step=self.big_step, tol=self.ls_options["tolerance"], itmax=self.ls_options["iter"], m=self.corrections, scale=self.scale, k=step) info(" @GEOP: Updated position list", verbosity.debug) info(" @GEOP: Updated gradient list", verbosity.debug) # x = current position - previous position. Use to determine converged minimization x = np.amax( np.absolute(np.subtract(self.beads.q, self.nebbfgsm.xold))) # Store old positions self.nebbfgsm.xold[:] = self.beads.q self.beads.q = self.nebbfgsm.dbeads.q self.forces.transfer_forces(self.nebbfgsm.dforces) info(" @GEOP: Updated bead positions", verbosity.debug) # Routine for steepest descent and conjugate gradient # TODO: CURRENTLY DOES NOT WORK. MUST BE ELIMINATED OR DEBUGGED else: if (self.mode == "sd" or step == 0): # Steepest descent minimization # gradf1 = force at current atom position # dq1 = direction of steepest descent # dq1_unit = unit vector of dq1 nebgrad = self.neblm(self.beads.q)[0] gradf1 = dq1 = -nebgrad # Move direction for steepest descent and 1st conjugate gradient step dq1_unit = dq1 / np.sqrt( np.dot(gradf1.flatten(), gradf1.flatten())) info(" @GEOP: Determined SD direction", verbosity.debug) else: # Conjugate gradient, Polak-Ribiere # gradf1: force at current atom position # gradf0: force at previous atom position # dq1 = direction to move # dq0 = previous direction # dq1_unit = unit vector of dq1 gradf0 = self.old_f dq0 = self.old_d nebgrad = self.neblm(self.beads.q)[0] gradf1 = -nebgrad 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.old_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.neblm.set_dir(dstrip(self.beads.q), dq1_unit) # Reuse initial value since we have energy and forces already u0 = np.dot(-nebgrad.flatten(), dq1_unit.flatten()) u0 = np.sqrt(np.dot(u0, u0)) (x, fx) = min_brent_neb(self.neblm, fdf0=u0, x0=0.0, tol=self.ls_options["tolerance"], itmax=self.ls_options["iter"], init_step=self.ls_options["step"]) # 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"] self.beads.q += dq1_unit * x info(" @GEOP: Updated bead positions", verbosity.debug) self.qtime += time.time() # Determine conditions for converged relaxation if ((fx - u0) / self.beads.natoms <= self.tolerances["energy"])\ and ((np.amax(np.absolute(self.forces.f)) <= self.tolerances["force"]) or (np.sqrt(np.dot(self.forces.f.flatten() - self.old_f.flatten(), self.forces.f.flatten() - self.old_f.flatten())) == 0.0))\ and (x <= self.tolerances["position"]): softexit.trigger( "Geometry optimization converged. Exiting simulation") else: info( " @GEOP: Not converged, deltaEnergy = %.8f, tol = %.8f" % ((fx - u0 / self.beads.natoms), self.tolerances["energy"]), verbosity.debug) info( " @GEOP: Not converged, force = %.8f, tol = %f" % (np.amax( np.absolute(self.forces.f)), self.tolerances["force"]), verbosity.debug) info( " @GEOP: Not converged, deltaForce = %.8f, tol = 0.00000000" % (np.sqrt( np.dot(self.forces.f.flatten() - self.old_f.flatten(), self.forces.f.flatten() - self.old_f.flatten()))), verbosity.debug) info( " @GEOP: Not converged, deltaX = %.8f, tol = %.8f" % (x, self.tolerances["position"]), verbosity.debug)
def __init__( self, timestep, mode="nve", splitting="obabo", thermostat=None, barostat=None, fixcom=False, fixatoms=None, nmts=None, ): """Initialises a "dynamics" motion object. Args: dt: The timestep of the simulation algorithms. fixcom: An optional boolean which decides whether the centre of mass motion will be constrained or not. Defaults to False. """ super(Dynamics, self).__init__(fixcom=fixcom, fixatoms=fixatoms) dself = dd(self) # noqa # initialize time step. this is the master time step that covers a full time step dd(self).dt = depend_value(name="dt", value=timestep) if thermostat is None: self.thermostat = Thermostat() else: if ( thermostat.__class__.__name__ is ("ThermoPILE_G" or "ThermoNMGLEG ") ) and (len(fixatoms) > 0): softexit.trigger( "!! Sorry, fixed atoms and global thermostat on the centroid not supported. Use a local thermostat. !!" ) self.thermostat = thermostat if nmts is None or len(nmts) == 0: dd(self).nmts = depend_array(name="nmts", value=np.asarray([1], int)) else: dd(self).nmts = depend_array(name="nmts", value=np.asarray(nmts, int)) if barostat is None: self.barostat = Barostat() else: self.barostat = barostat self.enstype = mode if self.enstype == "nve": self.integrator = NVEIntegrator() elif self.enstype == "nvt": self.integrator = NVTIntegrator() elif self.enstype == "nvt-cc": self.integrator = NVTCCIntegrator() elif self.enstype == "npt": self.integrator = NPTIntegrator() elif self.enstype == "nst": self.integrator = NSTIntegrator() elif self.enstype == "sc": self.integrator = SCIntegrator() elif self.enstype == "scnpt": self.integrator = SCNPTIntegrator() else: self.integrator = DummyIntegrator() # splitting mode for the integrators dd(self).splitting = depend_value(name="splitting", value=splitting) # constraints self.fixcom = fixcom if fixatoms is None: self.fixatoms = np.zeros(0, int) else: self.fixatoms = fixatoms
def run(self): """Runs the simulation. Does all the simulation steps, and outputs data to the appropriate files when necessary. Also deals with starting and cleaning up the threads used in the communication between the driver and the PIMD code. """ self.forces.run() # prints initial configuration -- only if we are not restarting if (self.step == 0): self.step = -1 for o in self.outputs: o.write() self.step = 0 steptime = 0.0 simtime = time.time() cstep = 0 tptime = 0.0 tqtime = 0.0 tttime = 0.0 ttot = 0.0 # main MD loop for self.step in range(self.step,self.tsteps): # stores the state before doing a step. # this is a bit time-consuming but makes sure that we can honor soft # exit requests without screwing the trajectory steptime = -time.time() self.chk.store() self.ensemble.step() for o in self.outputs: o.write() if os.path.exists("EXIT"): # soft-exit self.rollback = False softexit.trigger() steptime += time.time() ttot += steptime tptime += self.ensemble.ptime tqtime += self.ensemble.qtime tttime += self.ensemble.ttime cstep += 1 if verbosity.high or (verbosity.medium and self.step%100 == 0) or (verbosity.low and self.step%1000 == 0): info(" # Average timings at MD step % 7d. t/step: %10.5e [p: %10.5e q: %10.5e t: %10.5e]" % ( self.step, ttot/cstep, tptime/cstep, tqtime/cstep, tttime/cstep ) ) cstep = 0 tptime = 0.0 tqtime = 0.0 tttime = 0.0 ttot = 0.0 info(" # MD diagnostics: V: %10.5e Kcv: %10.5e Ecns: %10.5e" % (self.properties["potential"], self.properties["kinetic_cv"], self.properties["conserved"] ) ) if (self.ttime > 0 and time.time() - simtime > self.ttime): info(" # Wall clock time expired! Bye bye!", verbosity.low ) break info(" # Simulation ran successfully for the prescribed total_step! Bye bye!", verbosity.low ) self.rollback = False softexit.trigger()
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: info(" @GEOP: Classical TS search", verbosity.low) if self.hessian_init == 'true': 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[:] if self.hessian_init != 'true': info( " @GEOP: Hessian_init isn't true but we have stretched the polymer so we are going to compute the initial hessian anyway.", verbosity.low) self.hessian_init = 'true' else: info( " @GEOP: Starting from the provided geometry in the extended phase space", verbosity.low) if not (self.initial_hessian is None): raise ValueError( " You have to provided a hessian with size (3xnatoms)^2 but also geometry in the extended phase space (nbeads>1). Please check the inputs\n" ) if self.hessian_init == 'true': info(" @GEOP: We are computing the initial hessian", verbosity.low) get_hessian(self.hessian, self.gm, self.beads.q) # Update positions and forces self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pots self.old_f[:] = self.forces.f if type(self.im.f) == type(None): self.im(self.beads.q, ret=False) #Init instanton mapper 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 # Do one step. Update hessian for the new position. Update the position and force inside the mapper. Instanton(self.old_x, self.old_f, self.im.f, self.hessian, self.hessian_update, self.hessian_asr, self.im, self.gm, self.big_step, self.opt, self.mode) # 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 # 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) print_instanton_hess(self.prefix, step, self.hessian) # 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
def run(self): """Runs the simulation. Does all the simulation steps, and outputs data to the appropriate files when necessary. Also deals with starting and cleaning up the threads used in the communication between the driver and the PIMD code. """ # registers the softexit routine softexit.register_function(self.softexit) softexit.start(self.ttime) for k, f in self.fflist.iteritems(): f.run() # prints inital configuration -- only if we are not restarting info("Step:", self.step) if self.step == 0: print # me c*g print "Waiting for all CP2K clients to connect before starting the simulation." # me c*g trials_count = 0 # me c*g trials_max = 2*self.syslist[0].beads.nbeads + 500 # me c*g while self.syslist[0].beads.nbeads != len(self.fflist["cp2k"].socket.clients): # me c*g trials_count += 1 # me c*g print " * Currently " + str(len(self.fflist["cp2k"].socket.clients)) + " of " + str(self.syslist[0].beads.nbeads) + " CP2K clients have connected. Sleeping one second... (trial " + str(trials_count) + " of " + str(trials_max) + ")." # me c*g if trials_count == trials_max: # me c*g softexit.trigger("Sufficiently many CP2K clients failed to connect within the maximum waiting time of " + str(trials_max) + " seconds.") # me c*g time.sleep(1) # me c*g print "All CP2K clients have connected. Continuing..." # me c*g self.step = -1 # must use multi-threading to avoid blocking in multi-system runs with WTE stepthreads = [] for o in self.outputs: o.write() # threaded output seems to cause random hang-ups. should make things properly thread-safe #st = threading.Thread(target=o.write, name=o.filename) #st.daemon = True #st.start() #stepthreads.append(st) for st in stepthreads: while st.isAlive(): # This is necessary as join() without timeout prevents main # from receiving signals. st.join(2.0) if self.mode == "paratemp": self.paratemp.parafile.write("%10d" % (self.step + 1)) for i in self.paratemp.temp_index: self.paratemp.parafile.write(" %5d" % i) self.paratemp.parafile.write("\n") self.paratemp.parafile.flush() os.fsync(self.paratemp.parafile) self.step = 0 steptime = 0.0 simtime = time.time() cstep = 0 #tptime = 0.0 #tqtime = 0.0 #tttime = 0.0 ttot = 0.0 # main MD loop for self.step in range(self.step, self.tsteps): # stores the state before doing a step. # this is a bit time-consuming but makes sure that we can honor soft # exit requests without screwing the trajectory # Checking if no CP2K client has disconnected steptime = -time.time() if softexit.triggered: break self.chk.store() stepthreads = [] # steps through all the systems #for s in self.syslist: # s.motion.step() for s in self.syslist: # creates separate threads for the different systems #st = threading.Thread(target=s.motion.step, name=s.prefix, kwargs={"step":self.step}) #st.daemon = True s.motion.step(step=self.step) #st.start() #stepthreads.append(st) for st in stepthreads: while st.isAlive(): # This is necessary as join() without timeout prevents main # from receiving signals. st.join(2.0) if softexit.triggered: # Don't continue if we are about to exit. break for o in self.outputs: o.write() # does parallel tempering if self.mode == "paratemp": # because of where this is in the loop, we must write out BEFORE doing the swaps. self.paratemp.parafile.write("%10d" % (self.step + 1)) for i in self.paratemp.temp_index: self.paratemp.parafile.write(" %5d" % i) self.paratemp.parafile.write("\n") self.paratemp.parafile.flush() os.fsync(self.paratemp.parafile) self.paratemp.swap(self.step) if softexit.triggered: # Don't write if we are about to exit. break steptime += time.time() ttot += steptime cstep += 1 if (verbosity.high or (verbosity.medium and self.step % 100 == 0) or (verbosity.low and self.step % 1000 == 0)): info(" # Average timings at MD step % 7d. t/step: %10.5e" % (self.step, ttot / cstep)) cstep = 0 ttot = 0.0 #info(" # MD diagnostics: V: %10.5e Kcv: %10.5e Ecns: %10.5e" % # (self.properties["potential"], self.properties["kinetic_cv"], self.properties["conserved"] ) ) if os.path.exists("EXIT"): info(" # EXIT file detected! Bye bye!", verbosity.low) break if (self.ttime > 0) and (time.time() - simtime > self.ttime): info(" # Wall clock time expired! Bye bye!", verbosity.low) break self.rollback = False
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: info(" @GEOP: Classical TS search", verbosity.low) if self.hessian_init == 'true': get_hessian(self.hessian, self.gm, self.beads.q, self.output_maker) 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[:] if self.hessian_init != 'true': info(" @GEOP: Hessian_init isn't true but we have stretched the polymer so we are going to compute the initial hessian anyway.", verbosity.low) self.hessian_init = 'true' else: info(" @GEOP: Starting from the provided geometry in the extended phase space", verbosity.low) if not (self.initial_hessian is None): raise ValueError(" You have to provided a hessian with size (3xnatoms)^2 but also geometry in the extended phase space (nbeads>1). Please check the inputs\n") if self.hessian_init == 'true': info(" @GEOP: We are computing the initial hessian", verbosity.low) get_hessian(self.hessian, self.gm, self.beads.q, self.output_maker) # Update positions and forces self.old_x[:] = self.beads.q self.old_u[:] = self.forces.pots self.old_f[:] = self.forces.f if type(self.im.f) == type(None): self.im(self.beads.q, ret=False) # Init instanton mapper 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 # Do one step. Update hessian for the new position. Update the position and force inside the mapper. Instanton(self.old_x, self.old_f, self.im.f, self.hessian, self.hessian_update, self.hessian_asr, self.im, self.gm, self.big_step, self.opt, self.mode, self.output_maker) # 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 # 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) print_instanton_hess(self.prefix, step, self.hessian, self.output_maker) # 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
def step(self, step=None): """Does one simulation time step.""" info("\nMD STEP %d" % step, verbosity.debug) # Fetch spring constants self.nebbfgsm.kappa = self.spring["kappa"] self.neblm.kappa = self.spring["kappa"] self.ptime = self.ttime = 0 self.qtime = -time.time() if self.mode == "lbfgs": # L-BFGS Minimization # Initialize direction to the steepest descent direction if step == 0: # or np.sqrt(np.dot(self.bfgsm.d, self.bfgsm.d)) == 0.0: <-- this part for restarting at claimed minimum info(" @GEOP: Initializing L-BFGS", verbosity.debug) fx, nebgrad = self.nebbfgsm(self.beads.q) # Set direction to direction of NEB forces self.nebbfgsm.d = -nebgrad self.nebbfgsm.xold = self.beads.q.copy() # Initialize lists of previous positions and gradients self.qlist = np.zeros((self.corrections, len(self.beads.q.flatten()))) self.glist = np.zeros((self.corrections, len(self.beads.q.flatten()))) else: fx, nebgrad = self.nebbfgsm(self.beads.q) # Intial gradient and gradient modulus u0, du0 = (fx, nebgrad) # Store old force self.old_f[:] = -nebgrad # Do one iteration of L-BFGS and return positions, gradient modulus, # direction, list of positions, list of gradients # self.beads.q, fx, self.nebbfgsm.d, self.qlist, self.glist = L_BFGS(self.beads.q, L_BFGS(self.beads.q, self.nebbfgsm.d, self.nebbfgsm, self.qlist, self.glist, fdf0=(u0, du0), big_step=self.big_step, tol=self.ls_options["tolerance"], itmax=self.ls_options["iter"], m=self.corrections, scale=self.scale, k=step) info(" @GEOP: Updated position list", verbosity.debug) info(" @GEOP: Updated gradient list", verbosity.debug) # x = current position - previous position. Use to determine converged minimization x = np.amax(np.absolute(np.subtract(self.beads.q, self.nebbfgsm.xold))) # Store old positions self.nebbfgsm.xold[:] = self.beads.q self.beads.q = self.nebbfgsm.dbeads.q self.forces.transfer_forces(self.nebbfgsm.dforces) info(" @GEOP: Updated bead positions", verbosity.debug) # Routine for steepest descent and conjugate gradient # TODO: CURRENTLY DOES NOT WORK. MUST BE ELIMINATED OR DEBUGGED else: if (self.mode == "sd" or step == 0): # Steepest descent minimization # gradf1 = force at current atom position # dq1 = direction of steepest descent # dq1_unit = unit vector of dq1 nebgrad = self.neblm(self.beads.q)[0] gradf1 = dq1 = -nebgrad # Move direction for steepest descent and 1st conjugate gradient step dq1_unit = dq1 / np.sqrt(np.dot(gradf1.flatten(), gradf1.flatten())) info(" @GEOP: Determined SD direction", verbosity.debug) else: # Conjugate gradient, Polak-Ribiere # gradf1: force at current atom position # gradf0: force at previous atom position # dq1 = direction to move # dq0 = previous direction # dq1_unit = unit vector of dq1 gradf0 = self.old_f dq0 = self.old_d nebgrad = self.neblm(self.beads.q)[0] gradf1 = -nebgrad 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.old_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.neblm.set_dir(dstrip(self.beads.q), dq1_unit) # Reuse initial value since we have energy and forces already u0 = np.dot(-nebgrad.flatten(), dq1_unit.flatten()) u0 = np.sqrt(np.dot(u0, u0)) (x, fx) = min_brent_neb(self.neblm, fdf0=u0, x0=0.0, tol=self.ls_options["tolerance"], itmax=self.ls_options["iter"], init_step=self.ls_options["step"]) # 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"] self.beads.q += dq1_unit * x info(" @GEOP: Updated bead positions", verbosity.debug) self.qtime += time.time() # Determine conditions for converged relaxation if ((fx - u0) / self.beads.natoms <= self.tolerances["energy"])\ and ((np.amax(np.absolute(self.forces.f)) <= self.tolerances["force"]) or (np.sqrt(np.dot(self.forces.f.flatten() - self.old_f.flatten(), self.forces.f.flatten() - self.old_f.flatten())) == 0.0))\ and (x <= self.tolerances["position"]): softexit.trigger("Geometry optimization converged. Exiting simulation") else: info(" @GEOP: Not converged, deltaEnergy = %.8f, tol = %.8f" % ((fx - u0 / self.beads.natoms), self.tolerances["energy"]), verbosity.debug) info(" @GEOP: Not converged, force = %.8f, tol = %f" % (np.amax(np.absolute(self.forces.f)), self.tolerances["force"]), verbosity.debug) info(" @GEOP: Not converged, deltaForce = %.8f, tol = 0.00000000" % (np.sqrt(np.dot(self.forces.f.flatten() - self.old_f.flatten(), self.forces.f.flatten() - self.old_f.flatten()))), verbosity.debug) info(" @GEOP: Not converged, deltaX = %.8f, tol = %.8f" % (x, self.tolerances["position"]), verbosity.debug)
def force(beads, cell, masses, temp, dt, state): global bead_out_files nbeads = len(beads.q) if len(bead_out_files) == 0: bead_out_files = [ open('CV_' + str(i + 1) + '.out', 'w') for i in range(nbeads) ] if len(state['modes']['a']) > (nbeads + 1) / 2: state['modes']['a'] = np.resize(state['modes']['a'], (nbeads + 1) / 2) cv_set = [cv.get_cv_set(q, state['CV'], masses) for q in beads.q] if state['ghts'].get('M') is None: state['ghts']['M'] = np.average( [bead_cv_set.m for bead_cv_set in cv_set], 0) / AMU state['ghts']['n'] = np.matmul(state['ghts']['M'], state['ghts']['n']) params = convert_params(state['params']) modes = convert_modes(state['modes']) nmodes = len(modes['a']) ghts = convert_ghts(state['ghts']) q = rp.get_q(ghts, cv_set) r = nm.get_r(q, nmodes) sigma = nm.get_sigma(modes, r) d = rp.get_d(ghts, cv_set) stage = state['stage'] stage['step'] = stage.get('step', 0) step = stage['step'] print_CV_every = state['output'].get('print_CV_every', 1) if step % print_CV_every == 0: restraints = [[ cv.get_cv(bead, restr).value for restr in state['restraints'] ] for bead in beads.q] write_centroid_data(cv_set, sigma, q, d, r, restraints, out_file) write_bead_data(cv_set, ghts, restraints, bead_out_files) stage['step'] += 1 if stage['name'] == 'optimize': optimizer.move(modes, ghts, cv_set, r, sigma, params, params['K'] / temp, dt) if stage['name'] == 'sample': if (stage.get('optimizer_data') and stage['step'] % stage.get('optimizer_data_step', 1) == 0): with open(stage['optimizer_data'], 'a+') as f: write_optimizer_data(cv_set, f) stage['last_save_step'] = stage.get('last_save_step', stage['step']) stage['last_saved'] = stage.get('last_saved', (stage['walker'] - 1) * stage['structures']) if abs(sigma.value * SQAMU) < stage['q_threshold'] and \ stage['step'] >= stage['last_save_step'] + stage['offset']: idx = stage['last_saved'] + 1 with open(str(idx) + ".xyz", 'w') as f: print_file_path('xyz', beads, cell, f, units='angstrom') stage['last_saved'] = idx stage['last_save_step'] = stage['step'] if idx == stage['walker'] * stage['structures']: softexit.trigger('exit ' + str(stage['walker'])) if stage['name'] == 'prepare' and stage['step'] <= stage['prepare_steps']: params['K'] *= float(stage['step']) / stage['prepare_steps'] params['K_d'] *= float(stage['step']) / stage['prepare_steps'] if stage['step'] == stage['prepare_steps']: stage['name'] = 'optimize' stage['step'] = 0 sigma_bias = harmonic_bias(rp.get_sigma(sigma, q), params['K'] * nbeads, 0) d_bias = side_harmonic_bias(d, params['K_d'] * nbeads, params['d_max']) restraint_biases = np.zeros(beads.q.shape, beads.q.dtype) for restraint in state.get('restraints', []): restraint_biases += np.array( [restraint_bias(bead, restraint) for bead in beads.q]) if stage['name'] == 'committor': if abs(sigma.value * SQAMU) > stage['q_threshold']: softexit.trigger('q_threshold reached') return sigma_bias * 0 recover_ghts(state, ghts) recover_modes(state, modes) return sigma_bias + d_bias + restraint_biases
def step(self, step=None): """Does one simulation time step.""" self.ptime = 0.0 self.ttime = 0.0 self.qtime = -time.time() info("\nMD STEP %d" % step, verbosity.debug) if self.mode == "bfgs": # BFGS Minimization # Initialize approximate Hessian inverse to the identity and direction # to the steepest descent direction if step == 0: # or np.sqrt(np.dot(self.bfgsm.d, self.bfgsm.d)) == 0.0: <-- this part for restarting at claimed minimum (optional) info(" @GEOP: Initializing BFGS", verbosity.debug) self.bfgsm.d = depstrip(self.forces.f) / np.sqrt(np.dot(self.forces.f.flatten(), self.forces.f.flatten())) self.bfgsm.xold = self.beads.q.copy() # Current energy and forces u0 = self.forces.pot.copy() du0 = - self.forces.f # Store previous forces self.cg_old_f[:] = self.forces.f # Do one iteration of BFGS, return new point, function value, # move direction, and current Hessian to use for next iteration self.beads.q, fx, self.bfgsm.d, self.invhessian = BFGS(self.beads.q, self.bfgsm.d, self.bfgsm, fdf0=(u0, du0), invhessian=self.invhessian, max_step=self.max_step, tol=self.ls_options["tolerance"], itmax=self.ls_options["iter"]) # x = current position - previous position; use for exit tolerance x = np.amax(np.absolute(np.subtract(self.beads.q, self.bfgsm.xold))) # Store old position self.bfgsm.xold[:] = self.beads.q info(" @GEOP: Updating bead positions", verbosity.debug) elif self.mode == "lbfgs": # L-BFGS Minimization # Initialize approximate Hessian inverse to the identity and direction # to the steepest descent direction # Initialize lists of previous positions and gradient if step == 0: # or np.sqrt(np.dot(self.bfgsm.d, self.bfgsm.d)) == 0.0: <-- this part for restarting at claimed minimum (optional) info(" @GEOP: Initializing L-BFGS", verbosity.debug) self.bfgsm.d = depstrip(self.forces.f) / np.sqrt(np.dot(self.forces.f.flatten(), self.forces.f.flatten())) self.bfgsm.xold = self.beads.q.copy() self.qlist = np.zeros((self.corrections, len(self.beads.q.flatten()))) self.glist = np.zeros((self.corrections, len(self.beads.q.flatten()))) # Current energy and force u0, du0 = (self.forces.pot.copy(), - self.forces.f) # Store previous forces self.cg_old_f[:] = self.forces.f.reshape(len(self.cg_old_f)) # Do one iteration of L-BFGS, return new point, function value, # move direction, and current Hessian to use for next iteration self.beads.q, fx, self.bfgsm.d, self.qlist, self.glist = L_BFGS(self.beads.q, self.bfgsm.d, self.bfgsm, self.qlist, self.glist, fdf0=(u0, du0), max_step=self.max_step, tol=self.ls_options["tolerance"], itmax=self.ls_options["iter"], m=self.corrections, k=step) info(" @GEOP: Updated position list", verbosity.debug) info(" @GEOP: Updated gradient list", verbosity.debug) # x = current position - old position. Used for convergence tolerance x = np.amax(np.absolute(np.subtract(self.beads.q, self.bfgsm.xold))) # Store old position self.bfgsm.xold[:] = self.beads.q info(" @GEOP: Updated bead positions", verbosity.debug) # Routine for steepest descent and conjugate gradient else: if (self.mode == "sd" or step == 0): # Steepest descent minimization # gradf1 = force at current atom position # dq1 = direction of steepest descent # dq1_unit = unit vector of dq1 gradf1 = dq1 = depstrip(self.forces.f) # Move direction for steepest descent and 1st conjugate gradient step dq1_unit = dq1 / np.sqrt(np.dot(gradf1.flatten(), gradf1.flatten())) info(" @GEOP: Determined SD direction", verbosity.debug) else: # Conjugate gradient, Polak-Ribiere # gradf1: force at current atom position # gradf0: force at previous atom position # dq1 = direction to move # dq0 = previous direction # dq1_unit = unit vector of dq1 gradf0 = self.cg_old_f dq0 = self.cg_old_d gradf1 = depstrip(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.cg_old_d[:] = dq1 self.cg_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(depstrip(self.beads.q), dq1_unit) # Reuse initial value since we have energy and forces already u0, du0 = (self.forces.pot.copy(), np.dot(depstrip(self.forces.f.flatten()), dq1_unit.flatten())) # Do one SD/CG iteration; return positions and energy (x, fx) = min_brent(self.lm, fdf0=(u0, du0), x0=0.0, tol=self.ls_options["tolerance"], itmax=self.ls_options["iter"], init_step=self.ls_options["step"]) # 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"] self.beads.q += dq1_unit * x info(" @GEOP: Updated bead positions", verbosity.debug) self.qtime += time.time() # Determine conditions for converged relaxation if ((fx - u0) / self.beads.natoms <= self.tolerances["energy"])\ and ((np.amax(np.absolute(self.forces.f)) <= self.tolerances["force"]) or (np.sqrt(np.dot(self.forces.f.flatten() - self.cg_old_f.flatten(), self.forces.f.flatten() - self.cg_old_f.flatten())) == 0.0))\ and (x <= self.tolerances["position"]): softexit.trigger("Geometry optimization converged. Exiting simulation")
def store(self, sc): """Takes a motion calculation instance and stores a minimal representation of it. Args: sc: A motion calculation class. """ super(InputMotionBase, self).store(sc) tsc = -1 if type(sc) is Motion: self.mode.store("dummy") elif type(sc) is Replay: self.mode.store("replay") tsc = 0 elif type(sc) is GeopMotion: self.mode.store("minimize") self.optimizer.store(sc) tsc = 1 elif type(sc) is NEBMover: self.mode.store("neb") self.neb_optimizer.store(sc) tsc = 1 elif type(sc) is Dynamics: self.mode.store("dynamics") self.dynamics.store(sc) tsc = 1 elif type(sc) is ConstrainedDynamics: self.mode.store("constrained_dynamics") self.constrained_dynamics.store(sc) tsc = 1 elif type(sc) is DynMatrixMover: self.mode.store("vibrations") self.vibrations.store(sc) tsc = 1 elif type(sc) is AlchemyMC: self.mode.store("alchemy") self.alchemy.store(sc) tsc = 1 elif type(sc) is AtomSwap: self.mode.store("atomswap") self.atomswap.store(sc) tsc = 1 elif type(sc) is InstantonMotion: self.mode.store("instanton") self.instanton.store(sc) tsc = 1 elif type(sc) is Planetary: self.mode.store("planetary") self.planetary.store(sc) tsc = 1 elif type(sc) is TemperatureRamp: self.mode.store("t_ramp") self.t_ramp.store(sc) tsc = 1 elif type(sc) is PressureRamp: self.mode.store("p_ramp") self.p_ramp.store(sc) elif type(sc) is AlKMC: self.mode.store("al-kmc") self.al6xxx_kmc.store(sc) tsc = 1 else: raise ValueError("Cannot store Mover calculator of type " + str(type(sc))) if (sc.fixcom is True) and (len(sc.fixatoms) > 0): softexit.trigger( "Fixed atoms break translational invariance, and so should be used with <fixcom> False </fixcom>. You can disable this error if you know what you are doing." ) if tsc == 0: self.file.store(sc.intraj) elif tsc > 0: self.fixcom.store(sc.fixcom) self.fixatoms.store(sc.fixatoms)
def step(self, step=None): """Does one replay time step.""" self.ptime = 0.0 self.ttime = 0.0 self.qtime = -time.time() # If wildcard is used, check that it is consistent with Nbeads wildcard_used = False if any(char in self.intraj.value for char in "*?[]"): wildcard_used = True if len(self.rfile) != len(self.beads): info( "Error: if a wildcard is used for replay, then " "the number of files should be equal to the number of beads.", verbosity.low, ) softexit.trigger(" # Error in replay input.") while True: self.rstep += 1 try: if self.intraj.mode == "xyz": for bindex, b in enumerate(self.beads): if wildcard_used: myframe = read_file("xyz", self.rfile[bindex]) else: myframe = read_file("xyz", self.rfile) myatoms = myframe["atoms"] mycell = myframe["cell"] myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) mycell.h *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q elif self.intraj.mode == "pdb": for bindex, b in enumerate(self.beads): if wildcard_used: myatoms, mycell = read_file( "pdb", self.rfile[bindex]) else: myatoms, mycell = read_file("pdb", self.rfile) myatoms.q *= unit_to_internal("length", self.intraj.units, 1.0) mycell.h *= unit_to_internal("length", self.intraj.units, 1.0) b.q[:] = myatoms.q elif self.intraj.mode == "chk" or self.intraj.mode == "checkpoint": # TODO: Adapt the new `Simulation.load_from_xml`? # reads configuration from a checkpoint file xmlchk = xml_parse_file(self.rfile) # Parses the file. from ipi.inputs.simulation import InputSimulation simchk = InputSimulation() simchk.parse(xmlchk.fields[0][1]) mycell = simchk.cell.fetch() mybeads = simchk.beads.fetch() self.beads.q[:] = mybeads.q softexit.trigger(" # Read single checkpoint") # do not assign cell if it contains an invalid value (typically missing cell in the input) if mycell.V > 0: self.cell.h[:] = mycell.h except EOFError: softexit.trigger(" # Finished reading re-run trajectory") if (step is None) or (self.rstep > step): break self.qtime += time.time()
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)