def ConfBisect(snap1, snap2, eis1, eis2, L, dmax=0.002): """ This function takes two thermal snapshots that end in two separate IS, and makes a linear interpolation between the positions of the two. Through bisection, the point that separates the two basins is found. """ assert (np.abs(eis1 - eis2) > THRES) dstart = 0.1 * dmax Natoms = snap1.particles.N dist12 = med.PeriodicDistance(snap1.particles.position, snap2.particles.position, L).sum() / Natoms #the box is cubic snap12 = LinearConfInterpolation(snap1, snap2, L) snapis12, eis12 = Minimize(snap12, verbose=False) count = 0 maxcount = 100 while dist12 > dstart: if np.abs(eis1 - eis12) <= THRES: #If snap12 belongs to snap1, snap1=snap12 snap1.particles.position[:] = snap12.particles.position eis1 = eis12 pos1 = np.array(snap1.particles.position[:], dtype=np.float64) elif np.abs(eis2 - eis12) <= THRES: #If snap12 belongs to snap2, snap2=snap12 snap2.particles.position[:] = snap12.particles.position eis2 = eis12 else: #If snap12 does not belong to either, we throw a warning and change snap2 print( "ConfBisect: found an intermediate IS while searching the TS") snap2.particles.position[:] = snap12.particles.position eis2 = eis12 dist12 = med.PeriodicDistance(snap1.particles.position, snap2.particles.position, L).sum() / Natoms count += 1 assert (np.abs(eis1 - eis2) > THRES) snap12 = LinearConfInterpolation(snap1, snap2, L) snapis12, eis12 = Minimize(snap12, verbose=False) if count > maxcount: raise SystemExit( "ConfBisect ERROR: the interpolation bisection is not converging." ) return snap1, snap2, snap12, eis1, eis2, eis12, dist12
def ConfBisect(snap1, snap2, eis1, eis2, L, dmax=0.002): """ This function takes two thermal snapshots that end in two separate IS, and makes a linear interpolation between the positions of the two. Through bisection, the point that separates the two basins is found. #0.002 is about half the typical distance between confs at subsequent time steps w/ dt=0.0025 """ print('···ConfBisect···') print('eis1 = ',eis1,'; eis2 = ',eis2) if np.abs(eis1-eis2)<args.deltaE: raise ValueError('ConfBisect ERROR: the two starting ISs the same one [abs(eis1-eis2)<args.deltaE]') dstart=0.5*dmax Natoms=snap1.particles.N dist12=med.PeriodicDistance(snap1.particles.position, snap2.particles.position, L).sum()/Natoms #the box is cubic snap12=LinearConfInterpolation(snap1, snap2, L) eis12,snapis12=Minimize(snap12) print('snap1 .particles.position[0] = ',snap1.particles.position[0]) print('snap2 .particles.position[0] = ',snap2.particles.position[0]) print('snap12.particles.position[0] = ',snap12.particles.position[0]) count=0 maxcount=100 while dist12>dstart: print('eis1 = ',eis1,'; eis2 = ',eis2,'; eis12 = ',eis12) if np.abs(eis1-eis12) <= args.deltaE: #If snap12 belongs to snap1, snap1=snap12 snap1.particles.position[:]=snap12.particles.position eis1=eis12 elif np.abs(eis2-eis12) <= args.deltaE: #If snap12 belongs to snap2, snap2=snap12 snap2.particles.position[:]=snap12.particles.position eis2=eis12 else: #If snap12 does not belong to either, we print a warning and change snap2 print("ConfBisect: found an intermediate IS while searching the TS (Eis12=%.14g)"%eis12) snap2.particles.position[:]=snap12.particles.position eis2=eis12 dist12=med.PeriodicDistance(snap1.particles.position, snap2.particles.position, L).sum()/Natoms print("dist12=",dist12) count+=1 if np.abs(eis1-eis2)<args.deltaE: raise ValueError('ConfBisect ERROR: the two ISs became the same one [abs(eis1-eis2)<args.deltaE]') snap12=LinearConfInterpolation(snap1, snap2, L) print('snap12.particles.position[0] = ',snap12.particles.position[0]) eis12,snapis12=Minimize(snap12) if count>maxcount: raise RecursionError("ConfBisect ERROR: the interpolation bisection is not converging.") return snap1,snap2,snap12,eis1,eis2,eis12,dist12
def ConfBisect(snap1, snap2, eis1, eis2, L, dmax=0.002): ''' 0.002 is about half the typical distance between confs at subsequent time steps w/ dt=0.0025 ''' assert(np.abs(eis1-eis2)>deltaE) dstart=0.1*dmax Natoms=snap_ini.particles.N pos1=np.array(snap1.particles.position, dtype=np.float64) pos2=np.array(snap2.particles.position, dtype=np.float64) dist12=med.PeriodicDistance(pos1,pos2,boxParams[0]).sum()/Natoms #the box is cubic print("dist12=",dist12) snap12=LinearConfInterpolation(snap1, snap2, L) pos12=np.array(snap12.particles.position, dtype=np.float64) eis12=Minimize(snap12) print(" eis12=",eis12) count=0 maxcount=10 while dist12>dstart: #If snap12 belongs to snap1, snap1=snap12 if np.abs(eis1-eis12) <= deltaE: snap1=snap12 eis1=eis12 pos1=np.array(snap1.particles.position, dtype=np.float64) #If snap12 belongs to snap2, snap2=snap12 elif np.abs(eis2-eis12) <= deltaE: snap2=snap12 eis2=eis12 pos2=np.array(snap2.particles.position, dtype=np.float64) #If snap12 does not belong to either, we throw a warning and change snap2 else: print("NonLocalRidge: found an intermediate IS while searching the TS") snap2=snap12 eis2=eis12 # dist12=med.PeriodicDistance(pos1,pos2,boxParams[0]).sum()/Natoms print("dist12=",dist12) count+=1 assert(np.abs(eis1-eis2)>deltaE) snap12=LinearConfInterpolation(snap1, snap2, L) eis12=Minimize(snap12) if count>maxcount: print("NonLocalRidge ERROR: the interpolation bisection is not converging.") return snap1,snap2,snap12,eis1,eis2,eis12,dist12
def MeasureDistances(pivots): ''' Misura le distanze tra i pivot in ascissa curvilinea ''' distances = [0] for i in range(1, len(pivots)): distances.append(distances[i - 1] + med.PeriodicDistance(pivots[i], pivots[i - 1], L)) return distances
def InsertOnePivot(pivots, dmax, L): ''' Create a new list of pivots, inserting an intermediate image between every two pivots at distance larger than dmax ''' newpivots = [] indices = [] for i in range(0, len(pivots) - 1): newpivots.append(pivots[i]) if med.PeriodicDistance(pivots[i], pivots[i + 1], L) > dmax: indices.append(len(newpivots)) newpivots.append( med.PeriodicIntermPoints(pivots[i], pivots[i + 1], L)) newpivots.append(pivots[-1]) return newpivots, indices
def RedistributePivots(pivots, L): ''' Redistributes the pivots so that they are all at the same distance in the tangent reference. ''' distances = [ med.PeriodicDistance(pivots[i], pivots[i - 1], L) for i in range(1, len(pivots)) ] newpivots = np.copy(pivots) meandist = np.mean(distances) for i in range(len(distances) - 1): if distances[i] >= meandist: newpivots[i + 1] = med.PeriodicInterpPoints( pivots[i + 1], pivots[i], L, meandist / distances[i]) else: newpivots[i + 1] = med.PeriodicInterpPoints( pivots[i + 2], pivots[i + 1], L, min(1, (meandist - distances[i]) / distances[i + 1])) return newpivots
def CalculateRidge(snapT1, snapT2, Eis1, Eis2, L): ''' Calculates energy at the ridge between two snapshots ''' print("--- Calculate Ridge ---\n- Eis1:",Eis1,' Eis2:',Eis2) dmax=0.0001 #0.004 is about the typical distance between confs at subsequent time steps with dt=0.0025 nsteps=1 niter=10000 snapT1.particles.velocity[:]=np.zeros((Natoms, 3)) snapT2.particles.velocity[:]=np.zeros((Natoms, 3)) #snapis are not inherent structures, but the gradually approach them snapis1,snapis2,snapis12,Eis1,Eis2,Eis12,dist12=ConfBisect(snapT1, snapT2, Eis1, Eis2, L, dmax=dmax) print("- Eis1=%.8f;\tEis2= %.8f (after ConfBisect)"%(Eis1,Eis2)) context1 = hoomd.context.SimulationContext(); with context1: system1 = hoomd.init.read_snapshot(snapis1) modeFire1=md.integrate.mode_minimize_fire(dt=dtFIRE, alpha_start=alphaFIRE, ftol=ftolFIRE, Etol=EtolFIRE, wtol=wtolFIRE) integrator1 = md.integrate.nve(group=hoomd.group.all()) analyzerFire1=SetupAnalyzer(logname=None, period='None') md.update.zero_momentum(phase=2, period=10) pair=pot.LJ(md.nlist.cell(),type="KAshort") context2 = hoomd.context.SimulationContext(); with context2: system2 = hoomd.init.read_snapshot(snapis2) modeFire2=md.integrate.mode_minimize_fire(dt=dtFIRE, alpha_start=alphaFIRE, ftol=ftolFIRE, Etol=EtolFIRE, wtol=wtolFIRE) integrator2 = md.integrate.nve(group=hoomd.group.all()) analyzerFire2=SetupAnalyzer(logname=None, period='None') md.update.zero_momentum(phase=2, period=10) pair=pot.LJ(md.nlist.cell(),type="KAshort") modeFire1.reset() modeFire2.reset() for iter in range(niter): with context1: system1.restore_snapshot(snapis1) hoomd.run(nsteps) eis1=analyzerFire1.query('potential_energy') snapis1=system1.take_snapshot(dtype='double') with context2: system2.restore_snapshot(snapis2) hoomd.run(nsteps) eis2=analyzerFire2.query('potential_energy') snapis2=system2.take_snapshot(dtype='double') dist12=med.PeriodicDistance(snapis1.particles.position, snapis2.particles.position, L).sum()/Natoms print("iter: ",iter," dist12=",dist12,"eis1: %.14f"%eis1," eis2: %.14f"%eis2) if dist12>dmax: ''' With the linear interpolations the found barrier is occasionally lower than one of the ISs, because of nonlinearities in the path. # snapRidge=LinearConfInterpolation(snapis1_old, snapis2_old, L) # Eridge=potential.CalculateEnergySlower(snapRidge) For this reason, I just take the energy of one of the two confs at the previous step. ''' if eis1>eis2: Eridge=ConsistentRidge(eis1_old,Eis1,Eis2,args.deltaE) snapRidge=snapis1_old else : Eridge=ConsistentRidge(eis2_old,Eis1,Eis2,args.deltaE) snapRidge=snapis2_old print("Eridge = ",Eridge) break snapis1_old=snapis1 snapis2_old=snapis2 eis1_old=eis1 eis2_old=eis2 if iter==niter-1: sys.exit('CalculateRidge did not converge after '+str(niter)+' steps') return Eridge,snapRidge
def NonLocalRidge(snap1, snap2, eis1,eis2): """Finds the Transition State through the algorithm proposed in Doliwa&Heuer, PRE 67 031506 (2003)""" print("Calculate TS now: |e1-e2|=",np.abs(eis1-eis2)) dmax=0.002 #0.004 is about the typical distance between confs at subsequent time steps w/ dt=0.0025 snap1,snap2,snap12,eis1,eis2,eis12,dist12=ConfBisect(snap1, snap2, eis1, eis2, np.float64(boxParams[0]),dmax=dmax) ## Calculation of the gradient e1,G1=potential.CalculateGradient(snap1) e2,G2=potential.CalculateGradient(snap2) Gsq1=np.square(G1).sum() Gsq2=np.square(G2).sum() Gsq=0.5*(Gsq1+Gsq2) #Questa variabile ha senso solo dopo ConfBisect, che garantisce che le configurazioni siano vicine eis1=Minimize(snap1) eis2=Minimize(snap2) GsqThres=0.02 niter=0 maxiter=200 print("Nonlocal Ridge, caratteristiche configurazione termica:") print("e1=",e1) print("e2=",e2) print("Gsq1=",Gsq1) print("Gsq2=",Gsq2) print("Struttura inerente:") print("eis1=",eis1/Natoms) print("eis2=",eis2/Natoms) nsteps=25 while(Gsq>GsqThres): snap1=MinimizeFewSteps(snap1, nsteps) snap2=MinimizeFewSteps(snap2, nsteps) pos1=np.array(snap1.particles.position,dtype=np.float64) pos2=np.array(snap2.particles.position,dtype=np.float64) dist12=med.PeriodicDistance(pos1,pos2,boxParams[0]).sum()/Natoms #the box is cubic # if dist12>dmax: snap1,snap2,snap12,eis1,eis2,eis12,dist12=ConfBisect(snap1, snap2, eis1, eis2, np.float64(boxParams[0]), dmax=dmax) e1,G1=potential.CalculateGradient(snap1) e2,G2=potential.CalculateGradient(snap2) e12,G12=potential.CalculateGradient(snap12) # eis1=Minimize(snap1) # eis2=Minimize(snap2) Gsq1=np.square(G1).sum() Gsq2=np.square(G2).sum() Gsq12=np.square(G12).sum() Gsq=0.5*(Gsq1+Gsq2) print("e1=",e1,"\te2=",e2) print("eis1=",eis1/Natoms,"\teis2=",eis2/Natoms) print("Gsq1=",Gsq1," Gsq2=",Gsq2," Gsq12=",Gsq12," Gsq=",Gsq,"\n") if np.abs(eis1-eis2)<deltaE: print("The two configurations are falling into the same IS. Abort.") raise SystemExit if niter>maxiter: print("IL CAZZO DI ALGORITMO DELLE SELLE DI MERDA NON CONVERGE MANCO SE LO PAGHI") raise SystemExit niter+=1 snap1,snap2,snap12,eis1,eis2,eis12,dist12=ConfBisect(snap1, snap2, eis1, eis2, np.float64(boxParams[0]),dmax=dmax) eTS=e12 e12,G12=potential.CalculateGradient(snap12) Gsq12=np.square(G12).sum() print("Ora bisogna fare una minimizzazione del gradiente quadro, a partire da snap12") print("Per la cronaca, l'energia di snap12 e` e12=",e12) print("Quella della struttura inerente e` eis12=",eis12) print("Il gradiente della configurazione termica e` Gsq12=",Gsq12) raise SystemExit return eTS
dtype=np.float64) #Make interpolations delta = 1. / args.npoints alphas = np.arange(0, 1 + delta, delta) energies = [] distances = [] for alpha in alphas: interpPos = med.PeriodicInterpPoints(pos2, pos1, L, alpha) snap.particles.position[:] = interpPos system.restore_snapshot(snap) hoomd.run(2) energy = analyzer.query('potential_energy') print(energy) energies.append(energy) distances.append(med.PeriodicDistance(pos1, interpPos, L)) #Vector of all the particle displacements disp = med.PeriodicDisplacement(pos1, pos2, L) # # FIGURES # from matplotlib import pyplot as plt from mpl_toolkits.mplot3d import Axes3D import seaborn as sns #Barrier from the linear interpolation fig, ax1 = plt.subplots() ax1.set_xlabel('distance') ax1.set_ylabel('energy')
################### # # # Calculate Ridge # # # ################### print( "-- Find two thermal configurations that are very close to eachother, but end up in two different ISs --" ) dmax = 0.0004 #0.004 is about the typical distance between confs at subsequent time steps with dt=0.0025 snap1, snap2, snap12, eis1, eis2, eis12, dist12 = ConfBisect(snapT1, snapT2, Eis1, Eis2, L, dmax=dmax) dist12 = med.PeriodicDistance(snap1.particles.position, snap2.particles.position, L).sum() / Natoms print("eis1 = ", eis1, "\teis2 = ", eis2) print("-- Prepare two parallel minimization contexts --") snapIS1 = snap1 snapIS2 = snap2 snapIS1.particles.velocity[:] = np.zeros((Natoms, 3)) snapIS2.particles.velocity[:] = np.zeros((Natoms, 3)) simIS1 = hoomd.context.SimulationContext() simIS2 = hoomd.context.SimulationContext() with simIS1: systemIS1 = hoomd.init.read_snapshot(snapIS1) assert (Natoms == len(systemIS1.particles)) myLjPair = pot.LJ(md.nlist.cell(), type="KAshort") analyzerFire1 = SetupAnalyzer(logname=None, period='None') modeFire1 = md.integrate.mode_minimize_fire(dt=dtFIRE,