def rotation_plane(self, Fperp, Fperp_old, Nold): if self.rotationOpt == 'sd': self.T = vunit(Fperp) elif self.rotationOpt == 'cg': # determine self.T (big theta in the paper) with CG method a = abs(np.vdot(Fperp, Fperp_old)) b = np.vdot(Fperp_old, Fperp_old) if a <= 0.5 * b and b != 0: gamma = np.vdot(Fperp, Fperp - Fperp_old) / b else: gamma = 0 try: self.Tnorm except: self.Tnorm = 0.0 Ttmp = Fperp + gamma * self.T * self.Tnorm Ttmp = Ttmp - np.vdot(Ttmp, self.N) * self.N self.Tnorm = np.linalg.norm(Ttmp) self.T = vunit(Ttmp) elif self.rotationOpt == 'bfgs': ## BFGS from wiki Binv = self.Binv #s = (self.N - Nold).flatten() * self.dR #g1 = -Fperp.flatten() #g0 = -Fperp_old.flatten() s = (self.N - Nold).flatten() g1 = -Fperp.flatten() / self.dR g0 = -Fperp_old.flatten() / self.dR y = g1 - g0 ''' ## updating the inverse Hessian a = np.dot(s, y) b = np.dot(y, Binv.dot(y)) c = np.outer(s, s) d = np.outer(y, s) print "a, b:", a, b Binv = Binv + (a+b) * c / a**2 - (Binv.dot(d) + d.T.dot(Binv)) / a self.Binv = Binv dr = (Binv.dot(-g1)).reshape((-1, 3)) ''' ## updating Hessian rather than the inverse Hessian, see ase.optimize.BFGS a = np.dot(s, y) dg = np.dot(self.B, s) b = np.dot(s, dg) #print "a, b", a, b self.B += np.outer(y, y) / a - np.outer(dg, dg) / b omega, V = np.linalg.eigh(self.B) dr = np.dot(V, np.dot(-g1, V) / np.fabs(omega)).reshape((-1, 3)) vd = np.vdot(vunit(dr), vunit(Fperp)) if vd < 0.05: #print "////reset BFGS in rotation////" dr = Fperp self.B = self.B0 dr -= np.vdot(dr, self.N) * self.N self.T = vunit(dr)
def project_translt_rott(self, N, R0): if not self.noZeroModes: return N # Project out rigid translational mode for axisx in range(3): transVec = np.zeros((self.natom + 3, 3)) transVec[:, axisx] = 1.0 transVec = vunit(transVec) N -= np.vdot(N, transVec) * transVec # Project out rigid rotational mode for axisx in ['x', 'y', 'z']: ptmp = R0.copy() # rotate a small angle around the center of mass ptmp.rotate(axisx, 0.02, center='COM', rotate_cell=False) rottVec = ptmp.get_positions() - R0.get_positions() rottVec = vunit(rottVec) #if np.vdot(N[:-3], rottVec) > 0.1: print "mainly rotation around "+axisx N[:-3] -= np.vdot(N[:-3], rottVec) * rottVec return N
def rotation_plane(self, Fperp, Fperp_old, mode): # determine self.T (big theta in the paper) with CG method a = abs(np.vdot(Fperp, Fperp_old)) b = np.vdot(Fperp_old, Fperp_old) if a <= 0.5*b and b != 0: gamma = np.vdot(Fperp, Fperp-Fperp_old) / b else: gamma = 0 Ttmp = Fperp + gamma * self.T * self.Tnorm Ttmp = Ttmp - np.vdot(Ttmp, mode) * mode self.Tnorm = np.linalg.norm(Ttmp) self.T = vunit(Ttmp)
def step(self): if self.steps == 0: if self.ss: self.V = np.zeros((self.natom + 3, 3)) else: self.V = np.zeros((self.natom, 3)) self.steps += 1 Ftrans = self.get_forces() print "Ftrans", vmag(Ftrans) dV = Ftrans * self.dT if np.vdot(self.V, Ftrans) > 0: self.V = dV * (1.0 + np.vdot(dV, self.V) / np.vdot(dV, dV)) else: self.V = dV step = self.V * self.dT if vmag(step) > self.maxStep: step = self.maxStep * vunit(step) self.set_positions(self.get_positions() + step) self.E = self.get_potential_energy()
mode[-3:] = pfin.get_cell() - p.get_cell() icell = np.linalg.inv(p.get_cell()) mode[-3:] = np.dot(icell, mode[-3:]) * jacob ####################################### # set the initial mode randomly #mode = np.zeros((len(p1)+3,3)) #mode = vrand(mode) #constrain 3 redundant freedoms #mode[0] *=0 #mode[-3,1:]*=0 #mode[-2,2] *=0 ####################################### # displace along the initial mode direction mode = vunit(mode) cellt = p.get_cell() + np.dot(p.get_cell(), mode[-3:] / jacob) p.set_cell(cellt, scale_atoms=True) p.set_positions(p.get_positions() + mode[:-3]) # set a ssdimer_atoms object d = ssdimer.SSDimer_atoms(p, mode=mode, rotationMax=4, phi_tol=15) ################################################# # use quickmin optimizer in ssdimer d.search(minForce=0.0001, movie="dimer2.movie", interval=20) ################################################# # use MDMin optimizer in ase #dyn = MDMin(d) #dyn.run(fmax=0.0001) #################################################
def minmodesearch(self): # rotate dimer to the minimum mode direction # self.N, the dimer direction; # self.T, the rotation direction, spans the rotation plane with self.N. # project out any rigid translation and rotation if not self.ss: self.N = self.project_translt_rott(self.N, self.R0) F0 = self.update_general_forces(self.R0) F1 = self.rotation_update() phi_min = 1.5 Fperp = F1 * 0.0 # avoid Fperp_old asignment error iteration = 0 while abs(phi_min) > self.phi_tol and iteration < self.rotationMax: if iteration == 0: F0perp = F0 - np.vdot(F0, self.N) * self.N F1perp = F1 - np.vdot(F1, self.N) * self.N Fperp = 2.0 * (F1perp - F0perp) self.T = vunit(Fperp) # project out any rigid translation and rotation if not self.ss: self.T = self.project_translt_rott(self.T, self.R0) # curvature and its derivative c0 = np.vdot(F0 - F1, self.N) / self.dR c0d = np.vdot(F0 - F1, self.T) / self.dR * 2.0 phi_1 = -0.5 * atan(c0d / (2.0 * abs(c0))) if abs(phi_1) <= self.phi_tol: break # calculate F_prime: force after rotating the dimer by phi_prime N1_prime = vunit(self.N * cos(phi_1) + self.T * sin(phi_1)) self.iset_endpoint_pos(N1_prime, self.R0, self.R1_prime) F1_prime = self.update_general_forces(self.R1_prime) c0_prime = np.vdot(F0 - F1_prime, N1_prime) / self.dR # calculate phi_min b1 = 0.5 * c0d a1 = (c0 - c0_prime + b1 * sin(2 * phi_1)) / (1 - cos(2 * phi_1)) a0 = 2 * (c0 - a1) phi_min = 0.5 * atan(b1 / a1) c0_min = 0.5 * a0 + a1 * cos(2.0 * phi_min) + b1 * sin(2 * phi_min) # check whether it is minimum or maximum if c0_min > c0: phi_min += pi * 0.5 c0_min = 0.5 * a0 + a1 * cos(2.0 * phi_min) + b1 * sin( 2 * phi_min) ## for accurate BFGS s if phi_min > pi * 0.5: phi_min -= pi # update self.N Nold = self.N self.N = vunit(self.N * cos(phi_min) + self.T * sin(phi_min)) # project out any rigid translation and rotation if not self.ss: self.N = self.project_translt_rott(self.N, self.R0) c0 = c0_min # update F1 by linear extropolation F1 = F1 * (sin(phi_1 - phi_min) / sin(phi_1)) + F1_prime * (sin(phi_min) / sin(phi_1)) \ + F0 * (1.0 - cos(phi_min) - sin(phi_min) * tan(phi_1 * 0.5)) F0perp = F0 - np.vdot(F0, self.N) * self.N F1perp = F1 - np.vdot(F1, self.N) * self.N Fperp_old = Fperp Fperp = 2.0 * (F1perp - F0perp) # update self.T self.rotation_plane(Fperp, Fperp_old, Nold) iteration += 1 self.curvature = c0 return F0
def __init__(self, R0=None, mode=None, maxStep=0.2, dT=0.1, dR=0.001, phi_tol=5, rotationMax=4, ss=False, express=np.zeros((3, 3)), rotationOpt='cg', weight=1, noZeroModes=True): """ Parameters: R0 - an atoms object, which gives the starting point mode - initial mode (will be randomized if one is not provided) maxStep - longest distance dimer can move in a single iteration dT - quickmin timestep dR - separation between the two images for rotation phi_tol - rotation converging tolerence, degree rotationMax - max rotations per translational step ss - boolean, solid-state dimer or not express - 3*3 matrix, external stress tensor. Columns are the stress vectors. Needs to be in lower triangular form to avoid rigid rotation. rotation_opt - the optimization method for the rotation part: choose from "sd" (steepest descent), "cg" (conjugate gradient), and "bfgs". noZeroModes - boolean, project out the six zero modes or not. For some 2D analytical potential, it needs to be False. weight - extra weight to put on the cell degrees of freedom. """ self.steps = 0 self.dT = dT self.dR = dR self.phi_tol = phi_tol / 180.0 * pi self.R0 = R0 self.N = mode self.natom = self.R0.get_number_of_atoms() if self.N == None: print "radomly initialize the lowest eigenvector" self.N = vrand(np.zeros((self.natom + 3, 3))) self.N[-3:] *= 0.0 elif len(self.N) == self.natom: self.N = np.vstack((mode, np.zeros((3, 3)))) self.N = vunit(self.N) self.maxStep = maxStep self.Ftrans = None self.forceCalls = 0 self.R1 = self.R0.copy() self.R1_prime = self.R0.copy() calc = self.R0.get_calculator() self.R1.set_calculator(calc) self.R1_prime.set_calculator(calc) self.rotationMax = rotationMax self.rotationOpt = rotationOpt self.noZeroModes = noZeroModes # Set to False for 2D model potentials self.ss = ss self.express = express vol = self.R0.get_volume() avglen = (vol / self.natom)**(1.0 / 3.0) self.weight = weight self.jacobian = avglen * self.natom**0.5 * self.weight if self.rotationOpt == 'bfgs': ## BFGS for rotation: initial (inverse) Hessian ndim = (self.natom + 3) * 3 self.Binv0 = np.eye(ndim) / 60 self.Binv = self.Binv0 self.B0 = np.eye(ndim) * 60 self.B = self.B0
def minmodesearch(self): F0 = self.update_general_forces(self.R0) F1 = self.rotation_update() dphi = 100 ## only for atom degree of freedoms #u = self.N[:-3].flatten() ## include cell too u = self.N.flatten() beta = vmag(u) size = (self.natom + 3) * 3 #size = self.natom * 3 T = np.zeros((size, size)) Q = np.zeros((size, size)) #while dphi > self.phi_tol and iteration < self.rotationMax: for i in range(size): Q[:, i] = u / beta Hv = -(F1 - F0) / self.dR #u = Hv[:-3].flatten() u = Hv.flatten() if i > 0: u = u - beta * Q[:, i - 1] alpha = np.vdot(Q[:, i], u) u = u - alpha * Q[:, i] # re-orthogonalize u = u - np.dot(Q, np.dot(Q.T, u)) T[i, i] = alpha if i > 0: T[i - 1, i] = beta T[i, i - 1] = beta beta = vmag(u) newN = np.reshape(u / beta, (-1, 3)) #self.N[:-3] = newN self.N = newN F1 = self.rotation_update() ##Check Eigenvalues if i > 0: eigenValues, eigenVectors = np.linalg.eig(T[:i + 1, :i + 1]) idx = eigenValues.argsort() ew = eigenValues[idx][0] evT = eigenVectors[:, idx][:, 0] ##Convert eigenvector of T matrix to eigenvector of full Hessian evEst = Q[:, :i + 1].dot(evT) evEst = vunit(evEst) evAtom_old = copy.copy(evAtom) evAtom = np.reshape(evEst, (-1, 3)) c0 = ew ## check with pi/2 dotp = np.vdot(evAtom, evAtom_old) if dotp > 1: dotp = 1 elif dotp < -1: dotp = -1 dphi = np.arccos(dotp) if dphi > np.pi / 2.0: dphi = np.pi - dphi else: #evAtom = self.N[:-3] #c0 = np.vdot(Hv[:-3], evAtom) evAtom = self.N c0 = np.vdot(Hv, evAtom) #print "i, Curvature, dphi:", i, c0, dphi if dphi < self.phi_tol or i == size - 1 or i >= self.rotationMax: #self.N[:-3] = evAtom self.N = evAtom break self.curvature = c0 return F0
def minmodesearch(self, minoriso): # rotate dimer to the minimum mode direction # self.N, the dimer direction; # self.T, the rotation direction, spans the rotation plane with self.N. ## self.N or self.Contour if minoriso == "min": self.F0 = self.update_general_forces(self.R0) mode = self.N.copy() else: F0unit = vunit(self.F0) mode = self.Contour.copy() mode = mode - np.vdot(mode, F0unit) * F0unit # project out any rigid translation and rotation if not self.ss: mode = self.project_translt_rott(mode, self.R0) F0 = self.F0 F1 = self.rotation_update(mode) phi_min = 1.5 Fperp = F1 * 0.0 # avoid Fperp_old asignment error iteration = 0 while iteration < self.rotationMax: ## self.N or self.Contour if minoriso == "min": if phi_min <= self.phi_tol: break F0perp = F0 - np.vdot(F0, mode) * mode F1perp = F1 - np.vdot(F1, mode) * mode else: if phi_min <= self.phi_iso: break mode = mode - np.vdot(mode, F0unit) * F0unit F0perp = F0 * 0.0 F1iso = F1 - np.vdot(F1, F0unit) * F0unit F1perp = F1iso - np.vdot(F1iso, mode) * mode Fperp_old = Fperp Fperp = 2.0 * (F1perp - F0perp) if iteration == 0: Fperp_old = Fperp self.T = mode * 0.0 self.Tnorm= 0.0 # update self.T self.rotation_plane(Fperp, Fperp_old, mode) if minoriso == "iso": self.T = self.T - np.vdot(self.T, F0unit) * F0unit self.T = vunit(self.T) # project out any rigid translation and rotation if not self.ss: self.T = self.project_translt_rott(self.T, self.R0) # curvature and its derivative c0 = np.vdot(F0-F1, mode) / self.dR c0d = np.vdot(F0-F1, self.T) / self.dR * 2.0 phi_1 = -0.5 * atan(c0d / (2.0 * abs(c0))) if abs(phi_1) <= self.phi_tol: break # calculate F_prime: force after rotating the dimer by phi_prime N1_prime = vunit(mode * cos(phi_1) + self.T * sin(phi_1)) self.iset_endpoint_pos(N1_prime, self.R0, self.R1_prime) F1_prime = self.update_general_forces(self.R1_prime) c0_prime = np.vdot(F0-F1_prime, N1_prime) / self.dR # calculate phi_min b1 = 0.5 * c0d a1 = (c0 - c0_prime + b1 * sin(2 * phi_1)) / (1 - cos(2 * phi_1)) a0 = 2 * (c0 - a1) phi_min = 0.5 * atan(b1 / a1) c0_min = 0.5 * a0 + a1 * cos(2.0 * phi_min) + b1 * sin(2 * phi_min) # check whether it is minimum or maximum if c0_min > c0 : phi_min += pi * 0.5 c0_min = 0.5 * a0 + a1 * cos(2.0 * phi_min) + b1 * sin(2 * phi_min) # update self.N mode = vunit(mode * cos(phi_min) + self.T * sin(phi_min)) c0 = c0_min # update F1 by linear extropolation F1 = F1 * (sin(phi_1 - phi_min) / sin(phi_1)) + F1_prime * (sin(phi_min) / sin(phi_1)) \ + F0 * (1.0 - cos(phi_min) - sin(phi_min) * tan(phi_1 * 0.5)) #print "phi_min:", phi_min, "toque:", vmag(Fperp) iteration += 1 if minoriso == "min": self.curvature = c0 self.N = mode.copy() ''' ## estimate the curvature of the isophote (isoenergy curve) Fg = vunit(F0) cosalpha = abs(np.vdot(Fg, self.N)) print "cosalpha", cosalpha if cosalpha > 0.01 and cosalpha < 0.999: N1_prime = vunit(self.N - np.vdot(Fg, self.N)*Fg) self.iset_endpoint_pos(N1_prime, self.R0, self.R1_prime) F1_prime = self.update_general_forces(self.R1_prime) c0_prime = np.vdot(F0-F1_prime, N1_prime) / self.dR self.kappa = -c0_prime/vmag(F0) else: self.kappa = -0.25 ''' else: self.isocurv = -c0/vmag(F0) self.Contour = mode.copy() print "isocurv iteration:", iteration return F0