def calculate(self, coords): # Update atom coordinates from line array coord_count = 0 for a in self.atoms: a.x, a.y, a.z = coords[coord_count:coord_count + 3] coord_count += 3 # Set gradients to 0 self.gradient = [ np.array([0., 0., 0.]) for i in range(len(self.atoms)) ] # Loop through the atoms self.energy = np.float128(0.) energy_hold = [] for i, aa in enumerate(self.atoms): a = np.array([aa.x, aa.y, aa.z]) gx, gy, gz = [], [], [] for j, bb in enumerate(self.atoms): # If comparing against same atom, skip if i == j: continue b = np.array([bb.x, bb.y, bb.z]) dist = np.linalg.norm(a - b) direction = (a - b) / dist # Equations from http://www.phys.ubbcluj.ro/~tbeu/MD/C2_for.pdf calc_F = np.float128( direction * 48.0 * self.epsilon / np.power(dist, 2) * (np.power((self.sigma / dist), 12) - 0.5 * np.power( (self.sigma / dist), 6))) calc_E = np.float128(4.0 * self.epsilon * (np.power( (self.sigma / dist), 12) - np.power( (self.sigma / dist), 6))) gx.append(np.float128(-calc_F[0])) gy.append(np.float128(-calc_F[1])) gz.append(np.float128(-calc_F[2])) energy_hold.append(np.float128(calc_E)) x, y, z = fsum(gx), fsum(gy), fsum(gz) self.gradient[i] = np.array([x, y, z]) self.energy = fsum(energy_hold) self.energy /= np.float128(2.0) # Remove double counts self.gradient = np.array(self.gradient).flatten() force_mags = (self.gradient.reshape((-1, 3))**2).sum(axis=1) self.RMS_force = geometry.rms(force_mags) self.MAX_force = max(force_mags) self.error = self.RMS_force print("%d\t%.4f\t%.4f\t%.4f" % (self.step, self.RMS_force, self.MAX_force, self.energy)) self.step += 1
def calculate(self, coords): self.calls_to_calculate += 1 # Update coordinates in states. This won't change anything on # the first run through, but will on subsequent ones coord_count = 0 for s in self.states[1:-1]: for a in s: a.x, a.y, a.z = coords[coord_count:coord_count + 3] coord_count += 3 # Start DFT jobs running_jobs = [] for i, state in enumerate(self.states): if (i == 0 or i == len(self.states) - 1) and self.step > 0: # No need to calculate anything for first and last states # after the first step pass else: running_jobs.append( self.start_job(self, i, state, self.charge, self.multiplicity, self.procs, self.queue, self.initial_guess, self.extra_section, self.mem, self.priority, self.extra_keywords)) # Wait for jobs to finish if self.job_hang_time is not None: time.sleep(self.job_hang_time) for j in running_jobs: j.wait() if self.job_hang_time is not None: time.sleep(self.job_hang_time) # Get forces and energies from DFT calculations if not self.no_energy: energies = [] for i, state in enumerate(self.states): # State 0 and state N-1 don't change, so just use result # from self.step == 0 if (i == 0 or i == len(self.states) - 1): step_to_use = 0 else: step_to_use = self.step new_energy, new_atoms = self.get_results(self, step_to_use, i, state) if not self.no_energy: energies.append(new_energy) # V = potential energy from DFT. energies = V+springs if not self.no_energy: V = copy.deepcopy(energies) # In climbing image neb, after a few iterations we take the highest # energy image and use that. if self.ci_neb and self.ci_img is None and self.step > self.ci_N: self.ci_img = V.index(max(V)) if self.ci_img in [0, len(self.states) - 1]: raise Exception("CI found endpoint. Is your band correct?") # Get positions in a flat array def get_positions(image): pos = np.array([np.empty([3]) for j in image]) for j, atom in enumerate(image): if j not in self.spring_atoms: continue pos[j] = np.array([atom.x, atom.y, atom.z]) return pos.flatten() # Add spring forces to atoms for i in range(1, len(self.states) - 1): a = get_positions(self.states[i - 1]) b = get_positions(self.states[i]) c = get_positions(self.states[i + 1]) real_force = np.array([np.empty([3]) for j in self.states[i]]) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue real_force[j] = np.array([atom.fx, atom.fy, atom.fz]) real_force = real_force.flatten() # Find tangent tplus = c - b tminus = b - a if not self.no_energy: dVmin = min(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) dVmax = max(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) if self.no_energy: tangent = (c.copy() - b.copy()) / np.linalg.norm(c.copy() - b.copy()) + \ (b.copy() - a.copy()) / np.linalg.norm(b.copy() - a.copy()) tangent = tangent / np.linalg.norm(tangent) else: if V[i + 1] > V[i] and V[i] > V[i - 1]: tangent = tplus.copy() elif V[i + 1] < V[i] and V[i] < V[i - 1]: tangent = tminus.copy() elif V[i + 1] > V[i - 1]: tangent = tplus * dVmax + tminus * dVmin else: tangent = tplus * dVmin + tminus * dVmax # Normalize tangent tangent_norm = np.sqrt(np.vdot(tangent, tangent)) if tangent_norm != 0: tangent /= tangent_norm F_spring_parallel = self.k * (np.linalg.norm(tplus) - np.linalg.norm(tminus)) * tangent F_real_perpendicular = real_force -\ (np.vdot(real_force, tangent) * tangent) # Set NEB forces # Note, in climbing image we have the formula: # F = F_real - 2*F_real*tau*tau # Vs the normal: # F = F_spring_parallel + F_real_perpendicular if self.ci_img is not None and i == self.ci_img: forces = real_force - 2.0 * np.vdot(real_force, tangent) * tangent else: forces = F_spring_parallel + F_real_perpendicular forces = forces.reshape((-1, 3)) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue atom.fx, atom.fy, atom.fz = forces[j] # Remove net translation forces from the gradient if self.fit_rigid: net_translation_force = [] for state in self.states[1:-1]: net_force = np.zeros(3) for a in state: net_force += (a.fx, a.fy, a.fz) net_trans = np.sqrt((net_force**2).sum()) / len(state) net_translation_force.append(net_trans) for a in state: a.fx -= net_force[0] / len(state) a.fy -= net_force[1] / len(state) a.fz -= net_force[2] / len(state) # Set gradient self.gradient = [] for state in self.states[1:-1]: for a in state: # Gradient of self.error self.gradient += [-a.fx, -a.fy, -a.fz] # Calculate RMS Force and Max force force_mags = [(a.fx**2 + a.fy**2 + a.fz**2)**0.5 for state in self.states[1:-1] for a in state] RMS_force = geometry.rms(force_mags) self.RMS_force = RMS_force MAX_force = max(force_mags) self.MAX_force = MAX_force # Print data if not self.no_energy: V = V[:1] + [ units.convert_energy("Ha", "kT_300", e - V[0]) for e in V[1:] ] if not self.no_energy: MAX_energy = max(V) else: MAX_energy = float('inf') if self.prv_RMS is None or self.prv_RMS > RMS_force: rms = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)), 'GREEN') else: rms = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)), 'RED') if self.prv_MAX is None or self.prv_MAX > MAX_force: max_f = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)), 'GREEN') else: max_f = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)), 'RED') if not self.no_energy: if self.prv_MAX_E is None or self.prv_MAX_E > MAX_energy: max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'GREEN') else: max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'RED') if not self.no_energy: if self.step == 0: print("Step\tRMS_F (eV/Ang)\tMAX_F (eV/Ang)\tMAX_E (kT_300)\ \tEnergies (kT_300)\n----") print("%d\t%s\t\t%s\t\t%s" % (self.step, rms, max_f, max_e)), print ' \t\t\t\t', '%7.5g +'\ % V[0], ('%5.1f ' * len(V[1:])) % tuple(V[1:]) else: if self.step == 0: print("Step\tRMS_F (eV/Ang)\tMAX_F (eV/Ang)\n----") print("%d\t%s\t\t%s" % (self.step, rms, max_f)) sys.stdout.flush() if self.prv_RMS is None: self.prv_RMS = RMS_force self.prv_RMS = min(RMS_force, self.prv_RMS) if self.prv_MAX is None: self.prv_MAX = MAX_force self.prv_MAX = min(MAX_force, self.prv_MAX) if not self.no_energy: if self.prv_MAX_E is None: self.prv_MAX_E = MAX_energy self.prv_MAX_E = min(MAX_energy, self.prv_MAX_E) # Set error self.error = RMS_force # Increment step self.step += 1 if self.callback is not None: self.callback(self.states)
def calculate(self, coords): self.calls_to_calculate += 1 # Update coordinates in states. This won't change anything on # the first run through, but will on subsequent ones coord_count = 0 for s in self.states[1:-1]: for a in s: a.x, a.y, a.z = coords[coord_count:coord_count + 3] coord_count += 3 # Start DFT jobs running_jobs = [] if self.initialize == True: # Run a single point calculation to determine energies before main curve-smoothing begins for i, state in enumerate(self.states): running_jobs.append( self.start_job(self, i, state, self.charge, self.procs, self.queue, self.initial_guess, self.extra_section, self.mem, self.priority)) else: # Once initialization is complete, run main spline_NEB simulation for curve smoothing for i, state in enumerate(self.states): if (i == 0 or i == self.peak or i == len(self.states) - 1) and self.step > 0: # No need to calculate anything for first and last states # after the first step pass else: running_jobs.append( self.start_job(self, i, state, self.charge, self.procs, self.queue, self.initial_guess, self.extra_section, self.mem, self.priority)) # Wait for jobs to finish for j in running_jobs: j.wait() # Get forces and energies from DFT calculations energies = [] for i, state in enumerate(self.states): # State 0 and state N-1 don't change, so just use result # from self.step == 0 if (i == 0 or i == self.peak or i == len(self.states) - 1): step_to_use = 0 else: step_to_use = self.step new_energy, new_atoms = self.get_results(self, step_to_use, i, state) energies.append(new_energy) # V = potential energy from DFT. energies = V+springs V = copy.deepcopy(energies) # Get positions in a flat array def get_positions(image): pos = np.array([np.empty([3]) for j in image]) for j, atom in enumerate(image): if j not in self.spring_atoms: continue pos[j] = np.array([atom.x, atom.y, atom.z]) return pos.flatten() if self.initialize == True: # During initialization phase, use single point energies previously calculated to # determine highest energy frame (peak) and generate spring constants between frames # Peak of reaction coordinate is highest energy frame self.peak = energies.index(max(energies)) #Calculate spring constants for smoothing curve d_before = np.linalg.norm( get_positions(self.states[self.peak]) - get_positions(self.states[0])) d_after = np.linalg.norm( get_positions(self.states[self.peak]) - get_positions(self.states[-1])) l_before = -d_before**2 / np.log(self.gamma) l_after = -d_after**2 / np.log(self.gamma) x1, x2 = [], [] for i in range(self.peak): v = (get_positions(self.states[i]) + get_positions(self.states[i+1])) / 2.0 - \ get_positions(self.states[0]) x1.append(np.linalg.norm(v)) for i in range(self.peak, len(self.states) - 1): v = (get_positions(self.states[i]) + get_positions(self.states[i+1])) / 2.0 - \ get_positions(self.states[0]) x2.append(np.linalg.norm(v)) for x in x1: self.k.append(self.k_max * exp(-((x - d_before)**2) / l_before)) for x in x2: self.k.append(self.k_max * exp(-((x - d_after)**2) / l_after)) # Add spring forces to atoms for i in range(1, len(self.states) - 1): if i == self.peak: # Set NEB forces at peak to 0 for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue atom.fx, atom.fy, atom.fz = [0, 0, 0] else: a = get_positions(self.states[i - 1]) b = get_positions(self.states[i]) c = get_positions(self.states[i + 1]) real_force = np.array([np.empty([3]) for j in self.states[i]]) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue real_force[j] = np.array([atom.fx, atom.fy, atom.fz]) real_force = real_force.flatten() # Find tangent tplus = c - b tminus = b - a dVmin = min(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) dVmax = max(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) if V[i + 1] > V[i] and V[i] > V[i - 1]: tangent = tplus.copy() elif V[i + 1] < V[i] and V[i] < V[i - 1]: tangent = tminus.copy() elif V[i + 1] > V[i - 1]: tangent = tplus * dVmax + tminus * dVmin else: tangent = tplus * dVmin + tminus * dVmax # Normalize tangent tangent_norm = np.sqrt(np.vdot(tangent, tangent)) if tangent_norm != 0: tangent /= tangent_norm # Set NEB forces forces = (self.k[i] * np.linalg.norm(tplus) - self.k[i - 1] * np.linalg.norm(tminus)) * tangent forces = forces.reshape((-1, 3)) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue atom.fx, atom.fy, atom.fz = forces[j] # Remove net translation forces from the gradient if self.fit_rigid: net_translation_force = [] for state in self.states[1:-1]: net_force = np.zeros(3) for a in state: net_force += (a.fx, a.fy, a.fz) net_trans = np.sqrt((net_force**2).sum()) / len(state) net_translation_force.append(net_trans) for a in state: a.fx -= net_force[0] / len(state) a.fy -= net_force[1] / len(state) a.fz -= net_force[2] / len(state) max_translation_force = units.convert("Ha/Ang", "eV/Ang", max(net_translation_force)) else: max_translation_force = 0 # Set gradient self.gradient = [] for state in self.states[1:-1]: for a in state: # Gradient of self.error self.gradient += [-a.fx, -a.fy, -a.fz] # Calculate RMS Force and Max force force_mags = [(a.fx**2 + a.fy**2 + a.fz**2)**0.5 for state in self.states[1:-1] for a in state] RMS_force = geometry.rms(force_mags) self.RMS_force = RMS_force MAX_force = max(force_mags) self.MAX_force = MAX_force # Print data V = V[:1] + [ units.convert_energy("Ha", "kT_300", e - V[0]) for e in V[1:] ] MAX_energy = max(V) if self.prv_RMS is None or self.prv_RMS > RMS_force: rms = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)), 'GREEN') else: rms = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)), 'RED') if self.prv_MAX is None or self.prv_MAX > MAX_force: max_f = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)), 'GREEN') else: max_f = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)), 'RED') if self.prv_MAX_E is None or self.prv_MAX_E > MAX_energy: max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'GREEN') else: max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'RED') if self.step == 0 and self.initialize == False: print("Step\tRMS_F (eV/Ang)\tMAX_F (eV/Ang)\tMAX_E (kT_300)\ \tMAX Translational Force (eV/Ang)\tEnergies (kT_300)\n----") print("%d\t%s\t\t%s\t\t%s\t\t%.4f" % (self.step, rms, max_f, max_e, max_translation_force)), print ' \t\t\t\t', '%7.5g +'\ % V[0], ('%5.1f ' * len(V[1:])) % tuple(V[1:]) sys.stdout.flush() if self.prv_RMS is None: self.prv_RMS = RMS_force self.prv_RMS = min(RMS_force, self.prv_RMS) if self.prv_MAX is None: self.prv_MAX = MAX_force self.prv_MAX = min(MAX_force, self.prv_MAX) if self.prv_MAX_E is None: self.prv_MAX_E = MAX_energy self.prv_MAX_E = min(MAX_energy, self.prv_MAX_E) # Set error self.error = RMS_force # Increment step self.step += 1 # End initialization phase self.initialize = False if self.callback is not None: self.callback(self.states)