def adjust_positions(self, atoms, new): p1, p2 = atoms.positions[self.indices] d, p = find_mic(np.array([p2 - p1]), atoms._cell, atoms._pbc) q1, q2 = new[self.indices] d, q = find_mic(np.array([q2 - q1]), atoms._cell, atoms._pbc) d *= 0.5 * (p - q) / q new[self.indices] = (q1 - d[0], q2 + d[0])
def adjust_positions(self, atoms, new): p1, p2 = atoms.positions[self.indices] d_old, p_old = find_mic(np.array([p2 - p1]), atoms._cell, pbc=False) d_old = d_old[0] q1, q2 = new[self.indices] d_new, p_new = find_mic(np.array([q2 - q1]), atoms._cell, pbc=False) d_new = d_new[0] if self.direction is None: # amount to put back distance to old value delta_d = 0.5 * d_new * (p_old - p_new) / p_new # desired increase amount sbit = 0.5 * d_new / p_new * self.bond_speed else: self.adjust_direction(atoms) p_old = np.abs(np.dot(d_old, self.direction)) p_new = np.dot(d_new, self.direction) swapsign = np.sign(p_new) length_new = np.abs(p_new) # amount to put back distance to old value by resizing distance vector d_new delta_d = 0.5 * (p_old - p_new) * self.direction * swapsign # desired increase amount sbit = 0.5 * self.direction * self.bond_speed * swapsign new[self.indices] = (q1 - delta_d - sbit, q2 + delta_d + sbit) self.distance = p_old + self.bond_speed
def fit0(E, F, R, cell=None, pbc=None): """Constructs curve parameters from the NEB images.""" E = np.array(E) - E[0] n = len(E) Efit = np.empty((n - 1) * 20 + 1) Sfit = np.empty((n - 1) * 20 + 1) s = [0] dR = np.zeros_like(R) for i in range(n): if i < n - 1: dR[i] = R[i + 1] - R[i] if cell is not None and pbc is not None: dR[i], _ = find_mic(dR[i], cell, pbc) s.append(s[i] + sqrt((dR[i]**2).sum())) else: dR[i] = R[i] - R[i - 1] if cell is not None and pbc is not None: dR[i], _ = find_mic(dR[i], cell, pbc) lines = [] dEds0 = None for i in range(n): d = dR[i] if i == 0: ds = 0.5 * s[1] elif i == n - 1: ds = 0.5 * (s[-1] - s[-2]) else: ds = 0.25 * (s[i + 1] - s[i - 1]) d = d / sqrt((d**2).sum()) dEds = -(F[i] * d).sum() x = np.linspace(s[i] - ds, s[i] + ds, 3) y = E[i] + dEds * (x - s[i]) lines.append((x, y)) if i > 0: s0 = s[i - 1] s1 = s[i] x = np.linspace(s0, s1, 20, endpoint=False) c = np.linalg.solve(np.array([(1, s0, s0**2, s0**3), (1, s1, s1**2, s1**3), (0, 1, 2 * s0, 3 * s0**2), (0, 1, 2 * s1, 3 * s1**2)]), np.array([E[i - 1], E[i], dEds0, dEds])) y = c[0] + x * (c[1] + x * (c[2] + x * c[3])) Sfit[(i - 1) * 20:i * 20] = x Efit[(i - 1) * 20:i * 20] = y dEds0 = dEds Sfit[-1] = s[-1] Efit[-1] = E[-1] return s, E, Sfit, Efit, lines
def get_forces(self,atoms): # Scale MD forces to match QM elasticity tot_force = self.mm_calc.get_forces(atoms)/self.beta # make system object and center atoms in box qm_system = atoms.copy() del qm_system.constraints qm_force = np.zeros(tot_force.shape) qm_system = qm_system[self.dft_atoms] qm_positions = qm_system.get_positions() scaled_qm_positions = qm_positions*self.alpha + self.qm_shift qm_system.set_positions(scaled_qm_positions.copy()) qm_system.set_cell(self.cell.copy()) dft_atom_count = np.sum(self.dft_atoms) tmp_qmf = self.qm_calc.get_forces(qm_system) qm_force[self.dft_atoms] = tmp_qmf # Reindexing check - will occur when we have multiple species if np.linalg.norm(scaled_qm_positions-qm_system.get_positions()) > 1e-9: #print "ATOMS REINDEXING IN QM CALC!" remap = np.zeros(dft_atom_count).astype(int) for i in range(len(remap)): remap[i] = find_mic(np.linalg.norm(qm_system.get_positions()-scaled_qm_positions[i],self.cell,pbc=self.pbc)) qm_force[self.dft_atoms] = tmp_qmf[remap] tot_force[self.qm_region] = qm_force[self.qm_region] return tot_force
def adjust_forces(self, atoms, forces): d = np.subtract.reduce(atoms.positions[self.indices]) d, p = find_mic(np.array([d]), atoms._cell, atoms._pbc) d = d[0] d *= 0.5 * np.dot(np.subtract.reduce(forces[self.indices]), d) / p**2 self.constraint_force = d forces[self.indices] += (-d, d)
def get_forces(self,atoms): # Scale MD forces to match QM elasticity tot_force = self.mm_calc.get_forces(atoms)/self.beta # make system object and center atoms in box qm_system = atoms.copy() del qm_system.constraints qm_force = np.zeros(tot_force.shape) qm_system = qm_system[self.dft_atoms] qm_positions = qm_system.get_positions() scaled_qm_positions = qm_positions*self.alpha + self.qm_shift qm_system.set_positions(scaled_qm_positions.copy()) qm_system.set_cell(self.cell.copy()) dft_atom_count = np.sum(self.dft_atoms) tmp_qmf = self.qm_calc.get_forces(qm_system) qm_force[self.dft_atoms] = tmp_qmf # Reindexing check - will occur when we have multiple species if np.linalg.norm(scaled_qm_positions-qm_system.get_positions()) > 1e-9: #print "ATOMS REINDEXING IN QM CALC!" remap = np.zeros(dft_atom_count).astype(int) for i in range(len(remap)): remap[i] = find_mic(np.linalg.norm(qm_system.get_positions()-scaled_qm_positions[i],self.cell,pbc=self.pbc)) qm_force[self.dft_atoms] = tmp_qmf[remap] tot_force[self.qm_region] = qm_force[self.qm_region] return tot_force
def get_potential_energies(self): _tmp_E = np.zeros(self.nimages) al, spl_E = self.force_integrator() _tmp_E[0]=0. _tmp_E[-1] = spl_E[-1] tot_d = np.linalg.norm(find_mic(self.images[-1].get_positions() - self.images[0].get_positions(), self.images[0].get_cell(), self.images[0].pbc)[0]) for i in range(1,self.nimages - 1): _d = find_mic(self.images[i].get_positions() - self.images[0].get_positions(), self.images[0].get_cell(), self.images[0].pbc)[0] _d_b = np.linalg.norm(find_mic(self.images[i].get_positions() - self.images[-1].get_positions(), self.images[-1].get_cell(), self.images[-1].pbc)[0]) _dist = .5*np.linalg.norm(_d)/tot_d + .5-.5*_d_b/tot_d _ind = min(int(len(spl_E)*_dist),len(spl_E)-1) _tmp_E[i] = spl_E[_ind] return _tmp_E
def adjust_direction(self, atoms): if not self.adjust: return p1, p2 = atoms.positions[self.indices] direction, _ = find_mic(np.array([p2 - p1]), atoms._cell, pbc=False) direction = direction[0] direction[2] = 0. distance = np.linalg.norm(direction) direction /= distance self.direction = direction self.distance = distance
def calculate_constraint(at, tipatoms): f = at.get_array('force') cnstr = SlowGrowthBondLength(*tipatoms, bond_speed=0., direction=tipatoms) cnstr.adjust_forces(at, f) p1, p2 = at.positions[tipatoms] d, _ = find_mic(np.array([p2 - p1]), at._cell, pbc=False) d = d[0] d = np.dot(d, cnstr.direction) f_cnstr = np.dot(cnstr.get_constraint_force(), cnstr.direction) return np.abs(d), f_cnstr
def force_integrator(self,force=False,spline_points=100): tot_d = np.linalg.norm(find_mic(self.images[-1].get_positions() - self.images[0].get_positions(), self.images[0].get_cell(), self.images[0].pbc)[0]) knot_dist = np.zeros(self.nimages) _U = np.zeros((self.nimages,3*self.natoms)) _F = np.zeros((self.nimages,3*self.natoms)) _U[0] = self.images[0].get_positions().reshape(3* self.natoms) _F[0] = self.forces[0].reshape(3*self.natoms) for _i,img in enumerate(self.images[1:]): _d = find_mic(img.get_positions() - self.images[0].get_positions(), self.images[0].get_cell(), self.images[0].pbc)[0] _d_b = np.linalg.norm(find_mic(img.get_positions() - self.images[-1].get_positions(), self.images[-1].get_cell(), self.images[-1].pbc)[0]) knot_dist[_i+1] = .5*np.linalg.norm(_d)/tot_d + .5-.5*_d_b/tot_d _d += self.images[0].get_positions() _U[_i+1] = _d.reshape(3* self.natoms) _F[_i+1] = self.forces[_i+1].reshape(3*self.natoms) print 'knot_dist = ', list(knot_dist) U_spl = interp1d(knot_dist, _U.T, 'cubic') F_spl = interp1d(knot_dist, _F.T, 'cubic') #r = np.linspace(0., 1.+1./(spline_points-1), spline_points+1, endpoint=False) r = np.linspace(0., 1.0, spline_points, endpoint=True) dU_spl = U_spl._spline.derivative() dU = dU_spl(r) F = F_spl(r).T #dU = spleval(U_spl._spline, r, deriv=1) #F = spleval(F_spl._spline, r, deriv=0) splined_force = (dU * F).sum(axis=1) splined_energy = -cumtrapz(splined_force, r) if force: return r[:-1], splined_energy, splined_force[:-1] else: return r[:-1], splined_energy
def adjust_forces(self, atoms, forces): d = np.subtract.reduce(atoms.positions[self.indices]) d, p = find_mic(np.array([d]), atoms._cell, pbc=False) d = d[0] if self.direction is None: f = 0.5 * d * np.dot(np.subtract.reduce(forces[self.indices]), d) / p**2 else: if self.adjust: self.adjust_direction(atoms) f = 0.5 * np.dot(np.subtract.reduce(forces[self.indices]), self.direction) * self.direction else: f = 0.5 * np.dot(np.subtract.reduce(forces[self.indices]), self.direction) * np.sign(np.dot(d, self.direction)) * self.direction self.constraint_force = f forces[self.indices] += (-f, f)
def interpolate(images, mic=False): """Given a list of images, linearly interpolate the positions of the interior images.""" pos1 = images[0].get_positions() pos2 = images[-1].get_positions() d = pos2 - pos1 if mic: d = find_mic(d, images[0].get_cell(), images[0].pbc)[0] d /= (len(images) - 1.0) for i in range(1, len(images) - 1): images[i].set_positions(pos1 + i * d) # Parallel NEB with Jacapo needs this: try: images[i].get_calculator().set_atoms(images[i]) except AttributeError: pass
def interpolate(images, mic=False): """Given a list of images, linearly interpolate the positions of the interior images.""" pos1 = images[0].get_positions() pos2 = images[-1].get_positions() d = pos2 - pos1 if mic: d = find_mic(d, images[0].get_cell(), images[0].pbc)[0] d /= (len(images) - 1.0) for i in range(1, len(images) - 1): images[i].set_positions(pos1 + i * d) # Parallel NEB with Jacapo needs this: try: images[i].get_calculator().set_atoms(images[i]) except AttributeError: pass
def interpolate(self, initial=0, final=-1, mic=False): """Interpolate linearly between initial and final images.""" if final < 0: final = self.nimages + final n = final - initial pos1 = self.images[initial].get_positions() pos2 = self.images[final].get_positions() dist = (pos2 - pos1) if mic: cell = self.images[initial].get_cell() assert((cell == self.images[final].get_cell()).all()) pbc = self.images[initial].get_pbc() assert((pbc == self.images[final].get_pbc()).all()) dist, D_len = find_mic(dist, cell, pbc) dist /= n for i in range(1, n): self.images[initial + i].set_positions(pos1 + i * dist)
def calculate(self, atoms, properties, system_changes): Calculator.calculate(self, atoms, properties, system_changes) P = atoms.get_positions() d = [] D = [] for p in P: Di = P - p if self.mic: Di, di = find_mic(Di, atoms.get_cell(), atoms.get_pbc()) else: di = np.sqrt((Di**2).sum(1)) d.append(di) D.append(Di) d = np.array(d) D = np.array(D) dd = d - self.target d.ravel()[::len(d) + 1] = 1 # avoid dividing by zero d4 = d**4 e = 0.5 * (dd**2 / d4).sum() f = -2 * ((dd * (1 - 2 * dd / d) / d**5)[..., np.newaxis] * D).sum(0) self.results = {'energy': e, 'forces': f}
def calculate(self, atoms, properties, system_changes): Calculator.calculate(self, atoms, properties, system_changes) P = atoms.get_positions() d = [] D = [] for p in P: Di = P - p if self.mic: Di, di = find_mic(Di, atoms.get_cell(), atoms.get_pbc()) else: di = np.sqrt((Di**2).sum(1)) d.append(di) D.append(Di) d = np.array(d) D = np.array(D) dd = d - self.target d.ravel()[::len(d) + 1] = 1 # avoid dividing by zero d4 = d**4 e = 0.5 * (dd**2 / d4).sum() f = -2 * ((dd * (1 - 2 * dd / d) / d**5)[..., np.newaxis] * D).sum(0) self.results = {'energy': e, 'forces': f}
def get_forces(self): """Evaluate and return the forces.""" images = self.images forces = np.empty(((self.nimages - 2), self.natoms, 3)) energies = np.empty(self.nimages - 2) if not self.parallel: # Do all images - one at a time: for i in range(1, self.nimages - 1): energies[i - 1] = images[i].get_potential_energy() forces[i - 1] = images[i].get_forces() elif self.world.size == 1: def run(image, energies, forces): energies[:] = image.get_potential_energy() forces[:] = image.get_forces() threads = [threading.Thread(target=run, args=(images[i], energies[i - 1:i], forces[i - 1:i])) for i in range(1, self.nimages - 1)] for thread in threads: thread.start() for thread in threads: thread.join() else: # Parallelize over images: i = self.world.rank * (self.nimages - 2) // self.world.size + 1 try: energies[i - 1] = images[i].get_potential_energy() forces[i - 1] = images[i].get_forces() except: # Make sure other images also fail: error = self.world.sum(1.0) raise else: error = self.world.sum(0.0) if error: raise RuntimeError('Parallel NEB failed!') for i in range(1, self.nimages - 1): root = (i - 1) * self.world.size // (self.nimages - 2) self.world.broadcast(energies[i - 1:i], root) self.world.broadcast(forces[i - 1], root) imax = 1 + np.argsort(energies)[-1] self.emax = energies[imax - 1] tangent1 = find_mic(images[1].get_positions() - images[0].get_positions(), images[0].get_cell(), images[0].pbc)[0] for i in range(1, self.nimages - 1): tangent2 = find_mic(images[i + 1].get_positions() - images[i].get_positions(), images[i].get_cell(), images[i].pbc)[0] if i < imax: tangent = tangent2 elif i > imax: tangent = tangent1 else: tangent = tangent1 + tangent2 tt = np.vdot(tangent, tangent) f = forces[i - 1] ft = np.vdot(f, tangent) if i == imax and self.climb: f -= 2 * ft / tt * tangent else: f -= ft / tt * tangent f -= np.vdot(tangent1 * self.k[i - 1] - tangent2 * self.k[i], tangent) / tt * tangent tangent1 = tangent2 return forces.reshape((-1, 3))
def get_forces(self): """Evaluate and return the forces.""" images = self.images forces = np.empty(((self.nimages - 2), self.natoms, 3)) energies = np.empty(self.nimages-2) if not self.parallel: for i in range(1, self.nimages - 1): if self.force_only: energies[i - 1] = 0. else: energies[i - 1] = images[i].get_potential_energy() forces[i - 1] = images[i].get_forces() elif self.world.size == 1: def run(image, energies, forces): if self.force_only: energies[:] = 0. else: energies[:] = image.get_potential_energy() forces[:] = image.get_forces() threads = [threading.Thread(target=run, args=(images[i], energies[i - 1:i], forces[i - 1:i])) for i in range(1, self.nimages - 1)] for thread in threads: thread.start() for thread in threads: thread.join() else: # Parallelize over images: i = self.world.rank * (self.nimages - 2) // self.world.size + 1 try: if self.force_only: energies[i - 1] = 0. else: energies[i - 1] = images[i].get_potential_energy() forces[i - 1] = images[i].get_forces() except: # Make sure other images also fail: error = self.world.sum(1.0) raise else: error = self.world.sum(0.0) if error: raise RuntimeError('Parallel NEB failed!') for i in range(1, self.nimages - 1): root = (i - 1) * self.world.size // (self.nimages - 2) self.world.broadcast(energies[i - 1:i], root) self.world.broadcast(forces[i - 1], root) for i in range(1, self.nimages - 1): self.forces[i] = forces[i-1].copy() self.energies[i] = energies[i-1].copy() if self.force_only and self.climb: self.energies = self.get_potential_energies() energies = self.energies[1:-1] if self.force_only == True and self.climb == False: imax = self.nimages // 2 # just to avoid non-assigned error else: imax = 1 + np.argsort(energies)[-1] self.emax = energies[imax - 1] # Backwards tangent tangent1 = find_mic(images[1].get_positions() - images[0].get_positions(), images[0].get_cell(), images[0].pbc)[0] for i in range(1, self.nimages - 1): # Forwards tangent tangent2 = find_mic(images[i + 1].get_positions() - images[i].get_positions(), images[i].get_cell(), images[i].pbc)[0] if i < imax: tangent = tangent2 elif i > imax: tangent = tangent1 else: tangent = tangent1 + tangent2 if self.modified_neb: cos_t = np.vdot(tangent1, tangent2) / \ np.linalg.norm(tangent1) / np.linalg.norm(tangent2) f_c_t = .5 * (1. + np.cos(np.pi * cos_t)) else: f_c_t = 0. tt = np.vdot(tangent, tangent) f = forces[i - 1] ft = np.vdot(f, tangent) if i == imax and self.climb: f -= 2 * ft / tt * tangent else: # -= F.T/|T| * T/|T| perp. MD force f -= ft / tt * tangent # f -= k(|T2| - |T1|) * (T/|T|) ~ ll spring force f -= (self.k[i-1]*np.linalg.norm(tangent1)-self.k[i] * \ np.linalg.norm(tangent2)) * tangent / np.sqrt(tt) if self.modified_neb: f -= np.vdot(tangent1 * self.k[i - 1] - tangent2 * \ self.k[i], tangent) / tt * tangent * (-f_c_t) f -= (tangent1 * self.k[i - 1] - \ tangent2 * self.k[i]) * f_c_t tangent1 = tangent2.copy() return forces.reshape((-1, 3))
try: clamp_mask = at.get_array('clamp_mask') fix_line_mask = at.get_array('fix_line_mask') initial_positions = at.get_array('pos_orig') except: at_help = quippy.Atoms('crack.xyz') clamp_mask = at_help.get_array('clamp_mask') # fix_line_mask = at_help.get_array('fix_line_mask') initial_positions = at_help.get_array('pos_orig') at_help = None at.set_calculator(calc) # set atoms order _, dists = find_mic(initial_positions - initial_positions[tipatoms[0]], at.get_cell()) at_order = np.argsort(dists)[:120] # ***** Setup constraints ***** # springs = [Hookean(a1=i, a2=[0,-1,0,initial_positions[i,1]], k=k_spring, rt=0) for i in np.where(clamp_mask)[0]] springs = [ Hookean(a1=i, a2=initial_positions[i], k=params.k_spring, rt=0) for i in np.where(clamp_mask)[0] ] from ase.constraints import FixedLine left = initial_positions[:, 0].min() right = initial_positions[:, 0].max() fix_line_mask = np.logical_or(initial_positions[:, 0] > right - 5., initial_positions[:, 0] < left + 5.) fix_line_idx = np.where(fix_line_mask)[0]