def fit_volume(self, name, atoms, data=None): N, x = self.fit cell0 = atoms.get_cell() v = atoms.get_volume() if x > 0: strains = np.linspace(1 - x, 1 + x, N) else: strains = np.linspace(1 + x / v, 1 - x / v, N)**(1./3) energies = [] traj = Trajectory(self.get_filename(name, 'fit.traj'), 'w') for s in strains: atoms.set_cell(cell0 * s, scale_atoms=True) energies.append(atoms.get_potential_energy()) traj.write(atoms) traj.close() if data is not None: data['strains'] = strains data['energies'] = energies else: assert N % 2 == 1 data = {'energy': energies[N // 2], 'strains': strains, 'energies': energies} return data
def eosfit_spec(atoms, calc, rg, method="birchmurnaghan"): from ase.eos import EquationOfState from numpy import linspace rootdir = os.getcwd() if not os.path.exists('eosfit'): os.mkdir('eosfit') os.chdir('eosfit') cell = atoms.get_cell() atoms.set_calculator(calc) traj = Trajectory('eosfit.traj', 'w') for x in rg: print(str(x)) atoms.set_cell(cell * x, scale_atoms=True) atoms.get_potential_energy() traj.write(atoms) configs = Trajectory('eosfit.traj', 'r') volumes = [at.get_volume() for at in configs] energies = [at.get_potential_energy() for at in configs] eos = EquationOfState(volumes, energies, eos=method) v0, e0, B = eos.fit() eos.plot('eosfit.svg') fp = open('eosdata.dat', 'w') fp.write('Volume\t\tEnergy') for i in range(len(configs)): fp.write(str(volumes[i]) + '\t' + str(energies[i]) + '\n') os.chdir(rootdir)
def plot(self, out_folder='.', unit_cell='POSCAR', code_name='vasp'): try: from ase.io.trajectory import Trajectory from ase.io import read, iread from ase.visualize import view except ImportError: raise ImportError( "\nThe parent directory of ase package must be included in 'sys.path'" ) if code_name == 'espresso': code_name = 'espresso-in' # aims, espresso-in, vasp atom = read(unit_cell, format=code_name) _current_position_true = atom.positions.copy()[ self.process.unit_cell.atom_true] _mass_weight = self.process.unit_cell.mass_true.reshape( (-1, 3)) / self.process.unit_cell.mass_true.max() for mode_ind in self.mode_inds: traj = Trajectory( out_folder + "/Trajectory_{0}.traj".format(mode_ind), 'w') for ind, x in enumerate( np.linspace(0, 2 * np.pi, self.num_images, endpoint=False), 1): atom.positions[self.process.unit_cell.atom_true] = _current_position_true \ + np.sin(x) * self.mode[mode_ind, :].reshape((-1, 3)).real / np.sqrt(_mass_weight) traj.write(atom) traj.close() atoms = iread(out_folder + "/Trajectory_{0}.traj".format(mode_ind)) view(atoms)
def concatenate_trajs(directory, traj): """Concantenate trajectories Merge all the history and trajectory files in one full trajectory of relaxation Arguments: directory {str} -- the directory with unfinished calculation traj {trajectory name} -- name of trajectory file """ history_trajs = [ i for i in os.listdir(directory) if "history" in i ] temp_traj = Trajectory(os.path.join(directory, "temp.traj"), "a") for t in history_trajs: tt = Trajectory(os.path.join(directory, t)) # Merge history trajectories without clashed last steps in each # Since the first step and the last step in the trajectories are the same for at in tt[:-1]: temp_traj.write(at) tt.close() last_traj = Trajectory(os.path.join(directory, traj)) for at in last_traj: temp_traj.write(at) last_traj.close() temp_traj.close() os.rename( os.path.join(directory, "temp.traj"), os.path.join(directory, traj), ) # Cleaning up for i in history_trajs: os.remove(os.path.join(directory, i))
class Ensemble(Optimizer): def __init__(self, atoms, restart=None, logfile=None, trajectory=None, seed=None, verbose=False): Optimizer.__init__(self, atoms, restart, logfile, trajectory=None) atoms.get_forces() atoms.get_potential_energy() if seed is None: seed = np.random.randint(0, 2 ** 31) self.verbose = verbose self.random_state = RandomState(seed) self.starting_atoms = dc(atoms) self.pe = [] self.metadata = {'seed': seed} self.traj = [dc(atoms)] print(self.traj[0].get_momenta()) if trajectory is not None: self.trajectory = Trajectory(trajectory, mode='w') self.trajectory.write(self.traj[-1]) if self.verbose: print('Trajectory written', len(self.traj)) else: self.trajectory = None if verbose: print('trajectory file', self.trajectory) def check_eq(self, eq_steps, tol): ret = np.cumsum(self.pe, dtype=float) ret[eq_steps:] = ret[eq_steps:] - ret[:-eq_steps] ret = ret[eq_steps - 1:] / eq_steps return np.sum(np.gradient(ret[eq_steps:])) < tol def run(self, steps=100000000, eq_steps=None, eq_tol=None, **kwargs): self.metadata['planned iterations'] = steps i = 0 while i < steps: # Check if we are at equilibrium, if we want that if eq_steps is not None: if self.check_eq(eq_steps, eq_tol): break # Verboseness if self.verbose: print('iteration number', i) try: self.step() i += 1 # If we blow up, write the last structure down and exit gracefully except KeyboardInterrupt: print('Interupted, returning data') return self.traj, self.metadata if self.trajectory is not None: self.trajectory.close() return self.traj, self.metadata def step(self): pass def estimate_simulation_duration(self, atoms, iterations): pass
def findConformers(self, strref=None, icsref=None): """ all stretches have to be the same; """ if strref is None or icsref is None: stre = self.notdis[0].ic.getStretchBendTorsOop()[0][0] ics = self.notdis[0].ic.getStretchBendTorsOop()[1] else: stre = strref ics = icsref xx = Trajectory('confs.traj', 'a') yy = Trajectory('notconfs.traj', 'a') cnt = 0 for i in self.notdis: istre = i.ic.getStretchBendTorsOop()[0][0] iics = i.ic.getStretchBendTorsOop()[1] same = True if len(stre) > len(istre): same = False else: for j in stre: #print j #print i.oldn if not (iics[j][0] == ics[j][0] and iics[j][1] == iics[j][1]): same = False break if same: xx.write(i.atoms) cnt += 1 print str(cnt) + ' - ' + str(i.oldn) self.conf.append(i) else: yy.write(i.atoms)
def write_trajectory(filename, ccdata, popname="mulliken", index=None): """Write an ASE Trajectory object from a ccData object. We try to write the following properties: atomcoords, atomnos, atomcharges, atomspins, atommasses, scfenergies, grads, moments (dipole only) and freeenergy. No charge or mult is written since ASE calculates them from atomcharges and atomspins. When a program do a final single point calculation at the end of an optimization (e.g., ORCA), we'll have unequal number of grads and atomcoords. This means the last geometry in the written .traj will lack forces. Some unit conversions are done here, but things are converted back in read_trajectory/makecclib. Inputs: filename - path to traj file to be written. ccdata - an instance of ccData. popname - population analysis to use for atomic partial charges and atomic spin densities. Molecular charge and multiplicity are evaluated from them. index - sequence of integers indicating which atomcoords indices should be exported. By default, all are exported. """ _check_ase(_found_ase) traj = Trajectory(filename, "w") for i, atomcoords in enumerate(ccdata.atomcoords): if index is not None and i not in index: continue atomspins = None if hasattr(ccdata, "atomspins"): atomspins = ccdata.atomspins[popname] atoms = makease( atomcoords, ccdata.atomnos, ccdata.atomcharges[popname], atomspins, ccdata.atommasses, ) properties = {} if hasattr(ccdata, "scfenergies"): properties.update({"energy": ccdata.scfenergies[i]}) if hasattr(ccdata, "grads"): try: properties.update( {"forces": -ccdata.grads[i] * units.Hartree / units.Bohr}) except IndexError: pass if i == len(ccdata.atomcoords) - 1: # last geometry if hasattr(ccdata, "moments"): properties.update({"dipole": ccdata.moments[1] * units.Bohr}) if hasattr(ccdata, "free_energy"): properties.update( {"free_energy": ccdata.freeenergy * units.Hartree}) traj.write(atoms, **properties)
def eos(self, atoms, name): args = self.args traj = Trajectory(self.get_filename(name, 'traj'), 'w', atoms) N, eps = args.equation_of_state.split(',') N = int(N) eps = float(eps) / 100 strains = np.linspace(1 - eps, 1 + eps, N) v1 = atoms.get_volume() volumes = strains**3 * v1 energies = [] cell1 = atoms.cell for s in strains: atoms.set_cell(cell1 * s, scale_atoms=True) energies.append(atoms.get_potential_energy()) traj.write(atoms) traj.close() eos = EquationOfState(volumes, energies, args.eos_type) v0, e0, B = eos.fit() atoms.set_cell(cell1 * (v0 / v1)**(1 / 3), scale_atoms=True) data = { 'volumes': volumes, 'energies': energies, 'fitted_energy': e0, 'fitted_volume': v0, 'bulk_modulus': B, 'eos_type': args.eos_type } return data
def do_vasp(pos1, pos2, snimgs): nimgs = int(snimgs) ini = read(pos1) fin = read(pos2) ini.wrap() fin.wrap() traj = [ini] traj += [ini.copy() for i in range(nimgs)] traj += [fin] neb = NEB(traj) neb.interpolate('idpp') images = neb.images if not os.path.exists('00'): os.mkdir('00') if not os.path.exists(i2str(nimgs + 1)): os.mkdir(i2str(nimgs + 1)) images[0].write('00/POSCAR', vasp5=True, direct=True) images[-1].write(i2str(nimgs + 1) + '/POSCAR', vasp5=True, direct=True) for i in range(nimgs): if not os.path.exists(i2str(i + 1)): os.mkdir(i2str(i + 1)) images[i + 1].write(i2str(i + 1) + '/POSCAR', vasp5=True, direct=True) traj = Trajectory('images.tarj', 'w') for i in images: traj.write(i)
def eos(self, atoms, name): args = self.args traj = Trajectory(self.get_filename(name, 'traj'), 'w', atoms) N, eps = args.equation_of_state.split(',') N = int(N) eps = float(eps) / 100 strains = np.linspace(1 - eps, 1 + eps, N) v1 = atoms.get_volume() volumes = strains**3 * v1 energies = [] cell1 = atoms.cell for s in strains: atoms.set_cell(cell1 * s, scale_atoms=True) energies.append(atoms.get_potential_energy()) traj.write(atoms) traj.close() eos = EquationOfState(volumes, energies, args.eos_type) v0, e0, B = eos.fit() atoms.set_cell(cell1 * (v0 / v1)**(1 / 3), scale_atoms=True) data = {'volumes': volumes, 'energies': energies, 'fitted_energy': e0, 'fitted_volume': v0, 'bulk_modulus': B, 'eos_type': args.eos_type} return data
def eos(self, atoms, name): opts = self.opts traj = Trajectory(self.get_filename(name, 'traj'), 'w', atoms) eps = 0.01 strains = np.linspace(1 - eps, 1 + eps, 5) v1 = atoms.get_volume() volumes = strains**3 * v1 energies = [] cell1 = atoms.cell for s in strains: atoms.set_cell(cell1 * s, scale_atoms=True) energies.append(atoms.get_potential_energy()) traj.write(atoms) traj.close() eos = EquationOfState(volumes, energies, opts.eos_type) v0, e0, B = eos.fit() atoms.set_cell(cell1 * (v0 / v1)**(1 / 3), scale_atoms=True) data = {'volumes': volumes, 'energies': energies, 'fitted_energy': e0, 'fitted_volume': v0, 'bulk_modulus': B, 'eos_type': opts.eos_type} return data
def check_interpolation(initial, final, n_max, interpolation="linear", verbose=True, save=True): ''' Interpolates the provided geometries with n_max total images and checks whether any bond lengths are below sane defaults. Saves the interpolation in interpolation.traj Parameters: initial: Atoms object or string Starting geometry for interpolation. final: Atoms object or string End point geometry for interpolation n_max: integer Desired total number of images for the interpolation including start and end point. interpolation: string "linear" or "idpp". First better for error identification, latter for use in NEB calculation verbose: boolean If verbose output of information is required save: boolean Whether to save the trajectory for transfer on to an NEB calculation ''' from ase.neb import NEB from carmm.analyse.bonds import search_abnormal_bonds from ase.io.trajectory import Trajectory from ase.io import read # Pre-requirements if not isinstance(n_max, int): raise ValueError print("Max number of images must be an integer.") # Make a band consisting of 10 images: images = [initial] images += [initial.copy() for i in range(n_max-2)] images += [final] neb = NEB(images) # Interpolate linearly the potisions of the middle images: neb.interpolate(interpolation, apply_constraint=True) #TODO: Tidy up this horrible mix of if statements. if save: t = Trajectory('interpolation.traj', 'w') flag = True for i in range(0, n_max): if verbose: print("Assessing image", str(i+1) + '.') updated_flag = search_abnormal_bonds(images[i], verbose) if save: t.write(images[i]) if (not updated_flag): flag = updated_flag if save: t.close() return flag
def main(fname): fname_out = fname.split(".")[0] fname_out += "only_cluster.traj" traj = Trajectory(fname, mode="r") traj_clust = Trajectory(fname_out, mode="w") for i, atoms in enumerate(traj): print("{} of {}".format(i, len(traj))) cluster = extract_cluster(atoms) traj_clust.write(cluster)
def check_interpolation(initial, final, n_max): ''' Interpolates the provided geometries with n_max total images and checks whether any bond lengths below 0.74 Angstrom exist saves the interpolation in interpolation.traj # TODO: incorporate ase.neighborlist.natural_cutoff # for abnormal bond lengths based on typical A-B bonds Parameters: initial: Atoms object or string If a string, e.g. 'initial.traj', a file of this name will be read. Starting geometry for interpolation. final: Atoms object or string If a string, e.g. 'final.traj', a file of this name will be read. End point geometry for interpolation n_max: integer Desired total number of images for the interpolation including start and end point. ''' from ase.neb import NEB from software.analyse.Interatomic_distances.analyse_bonds import search_abnormal_bonds from ase.io.trajectory import Trajectory from ase.io import read # Pre-requirements if isinstance(initial, str) is True: initial = read(initial) if isinstance(final, str) is True: final = read(final) if not isinstance(n_max, int): raise ValueError print("Max number of images must be an integer.") # Make a band consisting of 10 images: images = [initial] images += [initial.copy() for i in range(n_max - 2)] images += [final] neb = NEB(images, climb=True) # Interpolate linearly the potisions of the middle images: neb.interpolate() t = Trajectory('interpolation.traj', 'w') for i in range(0, n_max): print("Assessing image", str(i + 1) + '.') search_abnormal_bonds(images[i]) t.write(images[i]) t.close()
def test_trajectory_heterogeneous(): from ase.constraints import FixAtoms, FixBondLength from ase.build import molecule, bulk from ase.io.trajectory import Trajectory, get_header_data from ase.io import read a0 = molecule('H2O') a1 = a0.copy() a1.rattle(stdev=0.5) a2 = a0.copy() a2.set_masses() a2.center(vacuum=2.0) a2.rattle(stdev=0.2) a3 = molecule('CH3CH2OH') a4 = bulk('Au').repeat((2, 2, 2)) a5 = bulk('Cu').repeat((2, 2, 3)) # Add constraints to some of the images: images = [a0, a1, a2, a3, a4, a5] for i, img in enumerate(images[3:]): img.set_constraint(FixAtoms(indices=range(i + 3))) if i == 2: img.constraints.append(FixBondLength(5, 6)) traj = Trajectory('out.traj', 'w') for i, img in enumerate(images): traj.write(img) print(i, traj.multiple_headers) assert traj.multiple_headers == (i >= 2) traj.close() rtraj = Trajectory('out.traj') newimages = list(rtraj) assert len(images) == len(newimages) for i in range(len(images)): assert images[i] == newimages[i], i h1 = get_header_data(images[i]) h2 = get_header_data(newimages[i]) print(i, images[i]) print(h1) print(h2) print() # assert headers_equal(h1, h2) # Test append mode: with Trajectory('out.traj', 'a') as atraj: atraj.write(molecule('H2')) atraj.write(molecule('H2')) read('out.traj', index=':')
def plot(self, out_folder='.', unit_cell='POSCAR', code_name='vasp'): """ Visualize phonon Mode using modules of the `Atomic Simulation Environment (ASE) <https://wiki.fysik.dtu.dk/ase/index.html>`_. :param out_folder: Folder path for **Trajectory.traj** to be stored, defaults to . :type out_folder: str :param unit_cell: Path of unit cell input file, defaults to POSCAR :type unit_cell: str :param code_name: Specification of the file-format by a DFT program, defaults to vasp :type code_name: str """ try: from ase.io.trajectory import Trajectory from ase.io import read, iread from ase.visualize import view except ImportError: raise ImportError( "\nThe parent directory of ase package must be included in 'sys.path'" ) if code_name == 'espresso': code_name = 'espresso-in' # aims, espresso-in, vasp atom = read(unit_cell, format=code_name) _current_position_true = np.transpose( atom.positions.copy()[self.process.unit_cell.atom_true, :]) _mass_weight = np.transpose( self.process.unit_cell.mass_true.reshape( (-1, 3))) / self.process.unit_cell.mass_true.max() for mode_ind in self.mode_inds: traj = Trajectory( out_folder + "/Trajectory_{0}.traj".format(mode_ind), 'w') for _, x in enumerate( np.linspace(0, 2 * np.pi, self.num_images, endpoint=False), 1): atom.positions[self.process.unit_cell.atom_true, :] = \ np.transpose(_current_position_true + np.sin(x) * np.transpose(self.mode[mode_ind, :].reshape((-1, 3)).real) / np.sqrt(_mass_weight)) # np.sin(x + np.dot(_q, _current_position_true)) traj.write(atom) traj.close() atoms = iread(out_folder + "/Trajectory_{0}.traj".format(mode_ind)) view(atoms)
def write(): """Run this with an old version of ASE. Did it with 3.18.1. """ a1 = Atoms('H') a1.constraints = FixAtoms(indices=[0]) a2 = Atoms('HLi', cell=[1, 2, 3, 90, 80, 70], pbc=True) t = Trajectory('old.traj', 'w') t.write(a1) t.write(a2) b = Path('old.traj').read_bytes() data = b64encode(b) print('data = {!r} # noqa'.format(data))
def relaxstr(atom, steps, *args): rootdir = os.getcwd() traj = Trajectory('realx.traj', 'w') if not os.path.exists('relax'): os.mkdir('relax') os.chdir('relax') for i in range(steps): try: atom.set_calculator(args[i]) atom.get_potential_energy() traj.write(atom) except: print('Error occurs at step' + str(i + 1)) fp = open('_TAG_FAILED', 'w') fp.close() os.chdir(rootdir) return 0 os.chdir(rootdir) return 1
def parse_relax(label, write_traj=False, pbc=False, cell=None, chemical_symbols=[]): f = open(label + '.relax') #f = open(label + '.restart') text = f.read() # Parse out the steps if text == '': return None steps = text.split(':RELAXSTEP:')[1:] if write_traj == False: steps = [steps[-1]] else: traj = Trajectory(label + '.traj', mode='w') # Parse out the energies n_geometric = len(steps) s = os.popen('grep "Total free energy" ' + label + '.out') engs = s.readlines()[-n_geometric:] engs = [float(a.split()[-2]) * Hartree for a in engs] s.close() # build a traj file out of the steps for j, step in enumerate(steps): positions = step.split(':')[2].strip().split('\n') forces = step.split(':')[4].strip().split('\n') frc = np.empty((len(forces), 3)) atoms = Atoms() for i, f in enumerate(forces): frc[i, :] = [float(a) * Hartree / Bohr for a in f.split()] atoms += Atom(chemical_symbols[i], [float(a) * Bohr for a in positions[i].split()]) atoms.set_calculator( SinglePointCalculator(atoms, energy=engs[j], forces=frc)) atoms.set_pbc(pbc) atoms.cell = cell if write_traj == True: traj.write(atoms) atoms.set_calculator() return atoms
def write_mode(self, n=None, kT=units.kB * 300, nimages=30): """Write mode number n to trajectory file. If n is not specified, writes all non-zero modes.""" if n is None: for index, energy in enumerate(self.get_energies()): if abs(energy) > 1e-5: self.write_mode(n=index, kT=kT, nimages=nimages) return mode = self.get_mode(n) * sqrt(kT / abs(self.hnu[n])) if self.imagetype == 'atoms': p = self.atom.positions.copy() n %= 3 * len(self.free_atoms) traj = Trajectory('%s.%d.traj' % (self.name, n), 'w') for x in np.linspace(0, 2 * pi, nimages, endpoint=False): self.atom.set_positions(p + sin(x) * mode) traj.write(self.atom) self.atom.set_positions(p) traj.close()
def write_mode(self, n=None, kT=units.kB * 300, nimages=30): """Write mode number n to trajectory file. If n is not specified, writes all non-zero modes.""" if n is None: for index, energy in enumerate(self.get_energies()): if abs(energy) > 1e-5: self.write_mode(n=index, kT=kT, nimages=nimages) return mode = self.get_mode(n) * sqrt(kT / abs(self.hnu[n])) p = self.atoms.positions.copy() n %= 3 * len(self.indices) traj = Trajectory('%s.%d.traj' % (self.name, n), 'w') calc = self.atoms.get_calculator() self.atoms.set_calculator() for x in np.linspace(0, 2 * pi, nimages, endpoint=False): self.atoms.set_positions(p + sin(x) * mode) traj.write(self.atoms) self.atoms.set_positions(p) self.atoms.set_calculator(calc) traj.close()
def main(): arg = sys.argv inputfile = arg[1] outputfile = arg[2] atom1 = int(arg[3]) atom2 = int(arg[4]) f = open(inputfile) output = open(outputfile, 'w') atoms = None atom_output = Trajectory('trajectory.traj', 'w', atoms) iteration = 0 trajectories = [] a = 10.0 b = 10.0 c = 10.0 cell_atoms = np.array([[a, 0, 0], [0, b, 0], [0, 0, c]]) while True: coordinates = [] type_atoms = [] line = f.readline() if not line: break atom_numb = int(line.split()[0]) line = f.readline() for i in range(0, atom_numb): line = f.readline() fields = line.split() type_atoms.append(fields[0]) coordinates.append( [float(fields[1]), float(fields[2]), float(fields[3])]) traj = Atoms(np.array(type_atoms), positions=np.array(coordinates), cell=cell_atoms, pbc=True) traj.wrap() atom_output.write(traj) output.write("%6d %8.4f\n" % (iteration, traj.get_distance(atom1, atom2, mic=True))) iteration += 1
def make_inspection_traj(self, points=10, filename=None): """Make trajectory file for the vibrational mode for inspection""" if filename is None: filename = self.an_filename + '_inspect.traj' traj = Trajectory(filename, mode='w', atoms=self.atoms) old_pos = self.atoms.positions.copy() calc = self.atoms.get_calculator() self.atoms.set_calculator() displacements = self.get_initial_displacements(displacements=points) for displacement in displacements: new_pos = self.get_displacement_positions(displacement) self.atoms.set_positions(new_pos) traj.write(self.atoms) self.atoms.set_positions(old_pos) self.atoms.set_calculator(calc) traj.close()
def make_inspection_traj(self, num_displacements=10, filename=None): """Make trajectory file for translational mode to inspect""" if filename is None: filename = self.an_filename + '_inspect.traj' traj = Trajectory(filename, mode='w', atoms=self.atoms) old_pos = self.atoms.positions.copy() calc = self.atoms.get_calculator() self.atoms.set_calculator() angles = self.get_initial_angles(nsamples=num_displacements) for angle in angles: new_pos = self.get_rotate_positions(angle) self.atoms.set_positions(new_pos) traj.write(self.atoms) self.atoms.set_positions(old_pos) self.atoms.set_calculator(calc) traj.close()
def make_inspection_traj(self, points=10, filename=None): """Make trajectory file for the vibrational mode for inspection""" if filename is None: filename = self.an_filename+'_inspect.traj' traj = Trajectory(filename, mode='w', atoms=self.atoms) old_pos = self.atoms.positions.copy() calc = self.atoms.get_calculator() self.atoms.set_calculator() displacements = self.get_initial_displacements(displacements=points) for displacement in displacements: new_pos = self.get_displacement_positions(displacement) self.atoms.set_positions(new_pos) traj.write(self.atoms) self.atoms.set_positions(old_pos) self.atoms.set_calculator(calc) traj.close()
def kptconverge(atom, calc, kptlist, dirname='kptcoverge'): import matplotlib.pyplot as plt rootdir = os.getcwd() traj = Trajectory('kptcoverge.traj', 'w') engs = [] if not os.path.exists(dirname): os.mkdir(dirname) os.chdir(dirname) for i in kptlist: calc.set(kpts=i) try: atom.set_calculator(calc) eng = atom.get_potential_energy() engs.append(eng) traj.write(atom) except: print("Error while kpts = ", str(i)) plt.plot(engs, '-') plt.xlabel('KPT') plt.ylabel('Energe/eV') plt.savefig('kptcoverge.svg') os.chdir(rootdir)
def append_equilibrium_trajectory(self,weight,calc,traj,comment=None,label=None,color=None): """ Calculates the V'rep(r) from a given equilibrium trajectory. The trajectory is set of three (or more, albeit not necessary) frames where atoms move near their equilibrium structure. To first approximation, the energies of these frames ARE THE SAME. This method is then equivalent to append_energy_curve method for given trajectory, with a flat energy curve. * Atoms should move as parallel to the fitted bonds as possible. * Amplitude should be small enough (say, 0.01 Angstroms) parameters: =========== weight: fitting weight calc: Hotbit calculator (remember charge and k-points) traj: filename for ASE trajectory (energies need not be defined) comment: fitting comment for par-file (replaced by comment if None) label: plotting label (replaced by comment if None) color: plotting color """ traj1 = Trajectory(traj) atoms2 = traj1[0].copy() calc2 = NullCalculator() atoms2.set_calculator(calc2) tmpfile = '_tmp.traj' traj2 = Trajectory(tmpfile,'w',atoms2) for atoms1 in traj1: atoms2.set_positions(atoms1.get_positions()) atoms2.set_cell( atoms1.get_cell() ) atoms2.get_potential_energy() traj2.write() traj2.close() self.append_energy_curve(weight,calc,tmpfile,comment,label,color) os.remove(tmpfile) if os.path.isfile(tmpfile+'.bak'): os.remove(tmpfile+'.bak')
def run(self, fmax=0.05, maxstep=0.001, steps=1): if not steps: return if self.log_path: log = open(self.log_path, 'w') if self.traj_path: traj = Trajectory(self.traj_path, 'w') for step in range(steps): r0 = deepcopy(self.atoms.positions) f = self.atoms.get_forces() # -dE/dx m_f = np.linalg.norm(f, axis=1).max() nrg = self.atoms.get_potential_energy() if self.log_path: log.write(f'step {step}, nrg: {nrg}, max_f: {m_f}\n') else: print(f'step {step}, nrg: {nrg}, max_f: {m_f}') if self.traj_path: traj.write(self.atoms) if m_f < fmax: break dr = self.lr * f print(np.max(dr)) # check the max length of dr max_step_length = np.linalg.norm(dr, axis=1).max() if max_step_length > maxstep: scale = maxstep / max_step_length dr = dr * scale r = r0 + dr self.atoms.set_positions(r) if self.traj_path: traj.write(self.atoms) if self.log_path: log.close() if self.traj_path: traj.close()
def check_ase(self, value): """ Test NUTS simulation Parameters ---------- value: list or tuple The values to use in the tests """ print(self.traj_file) ideal_atoms, _ = value[0] write_traj = Trajectory(self.traj_file.name, mode='w') traj = [] for i in range(3): write_traj.write(ideal_atoms) traj.append(ideal_atoms) self.traj_file.close() assert os.path.exists(self.traj_file.name) print(self.traj_file) read_traj = TrajectoryReader(self.traj_file.name) print(len(traj), len(read_traj)) assert len(traj) == len(read_traj) del traj
def write_h_spacemodes(self, n=None, kT=units.kB * 300, nimages=30): """Write mode number n to trajectory file. If n is not specified, writes all non-zero modes.""" if n is None: for index, energy in enumerate(self.hnu_h_post): if abs(energy) > 1e-5: self.write_h_spacemodes(n=index, kT=kT, nimages=nimages) return mode = self.reduced_h_modes[n] * np.sqrt(kT / abs(self.hnu_h_post[n])) p = self.atoms.positions.copy() traj = Trajectory('%sHarmonic_%02d.traj' % (self.pre_names, n), 'w') calc = self.atoms.get_calculator() self.atoms.set_calculator() for x in np.linspace(0, 2 * np.pi, nimages, endpoint=False): pos_delta = np.zeros_like(p) pos_delta[self.vib.indices] += (np.sin(x) * mode.reshape( (len(self.vib.indices), 3))) self.atoms.set_positions(p + pos_delta) traj.write(self.atoms) self.atoms.set_positions(p) self.atoms.set_calculator(calc) traj.close()
def write_h_spacemodes(self, n=None, kT=units.kB * 300, nimages=30): """Write mode number n to trajectory file. If n is not specified, writes all non-zero modes.""" if n is None: for index, energy in enumerate(self.hnu_h_post): if abs(energy) > 1e-5: self.write_h_spacemodes(n=index, kT=kT, nimages=nimages) return mode = self.reduced_h_modes[n] * np.sqrt(kT / abs(self.hnu_h_post[n])) p = self.atoms.positions.copy() traj = Trajectory('%sHarmonic_%02d.traj' % (self.pre_names, n), 'w') calc = self.atoms.get_calculator() self.atoms.set_calculator() for x in np.linspace(0, 2 * np.pi, nimages, endpoint=False): pos_delta = np.zeros_like(p) pos_delta[self.vib.indices] += ( np.sin(x) * mode.reshape((len(self.vib.indices), 3))) self.atoms.set_positions(p + pos_delta) traj.write(self.atoms) self.atoms.set_positions(p) self.atoms.set_calculator(calc) traj.close()
def make_inspection_traj( self, num_displacements=10, filename=None): """Make trajectory file for translational mode to inspect""" if filename is None: filename = self.an_filename+'_inspect.traj' traj = Trajectory(filename, mode='w', atoms=self.atoms) old_pos = self.atoms.positions.copy() calc = self.atoms.get_calculator() self.atoms.set_calculator() angles = self.get_initial_angles(nsamples=num_displacements) for angle in angles: new_pos = self.get_rotate_positions(angle) self.atoms.set_positions(new_pos) traj.write(self.atoms) self.atoms.set_positions(old_pos) self.atoms.set_calculator(calc) traj.close()
def fit_bond_length(self, name, atoms, data=None): N, x = self.fit d0 = atoms.get_distance(0, 1) distances = np.linspace(d0 * (1 - x), d0 * (1 + x), N) energies = [] traj = Trajectory(self.get_filename(name, 'fit.traj'), 'w') for d in distances: atoms.set_distance(0, 1, d) energies.append(atoms.get_potential_energy()) self.check_occupation_numbers(atoms) traj.write(atoms) traj.close() if data is not None: data['distances'] = distances data['energies'] = energies else: assert N % 2 == 1 data = {'energy': energies[N // 2], 'distances': distances, 'energies': energies} return data
class AdsorbatePlacer: def __init__(self, slab: Atoms, adsorbate: Atoms, bonds: List[Tuple[int, int]], dists: List[float], initial_height: float = 1.0, weight: float = 1.0, scale: float = 1.0, trajectory: str = None) -> None: self.slab = slab self.adsorbate = adsorbate zmax_slab = np.max(self.slab.positions[:, 2]) zmin_ads = np.min(self.adsorbate.positions[:, 2]) self.adsorbate.positions[:, 2] += initial_height + zmax_slab - zmin_ads self.ads_ref = self.slab + self.adsorbate self.bonds = bonds self.dists = dists self.weight = weight self.scale = scale self.trajectory = trajectory if self.trajectory is not None: self.trajectory = Trajectory(self.trajectory, 'w', self.ads_ref) @property def nslab(self) -> int: ''' How many atoms are in slab Returns ------- nslab : int number of atoms in slab ''' return len(self.slab) @property def nadsorbate(self) -> int: ''' How many atoms are in adsorbate Returns ------- nadsorbate : int number of atoms in adsorbate ''' return len(self.adsorbate) @property def nx(self) -> int: ''' Return the product of array elements over a given axis Returns ------- nx : int product of array of elements self.adsorbate.positions.shape ''' return np.prod(self.adsorbate.positions.shape) def set_y(self, yin: np.ndarray) -> None: ''' Set y axis Parameters ---------- yin : np.ndarray an old y which is used to set a new one ''' ads = self.adsorbate.copy() center = yin[:3] axis = yin[3:] angle = np.linalg.norm(axis) * 180 / np.pi ads.positions += center - ads.positions.mean(0) ads.rotate(angle, v=axis, center=center) self.ads_ref.positions[self.nslab:] = ads.positions def penalty(self, y: np.ndarray) -> float: ''' Define a penalty function Parameters ---------- y : np.ndarray an axis Returns ------- pen : float a weighted penalty function ''' self.set_y(y) pen = 0. for bond, dist in zip(self.bonds, self.dists): pen += (self.ads_ref.get_distance(*bond) - dist)**2 return pen * self.weight def dpdx(self, y: np.ndarray) -> np.ndarray: ''' Return a flaten dpdx array Parameters ---------- y : np.ndarray an axis Returns ------- dpdx : np.ndarray a flaten dpdx array ''' self.set_y(y) dpdx = np.zeros((self.nadsorbate, 3)) for bond, dist in zip(self.bonds, self.dists): xij = self.ads_ref.get_distance(*bond, vector=True) dij = np.linalg.norm(xij) deriv = 2 * (dij - dist) * xij / dij if bond[0] >= self.nslab: dpdx[bond[0] - self.nslab] -= deriv if bond[1] >= self.nslab: dpdx[bond[1] - self.nslab] += deriv return dpdx.ravel() def penalty_jac(self, y: np.ndarray) -> np.ndarray: ''' Deal with jacobians Parameters ---------- y : np.ndarray an axis Returns ------- self.dpdx(y) @ self.dxdy(y) : np.ndarray a product of multiplied matrix ''' return self.dpdx(y) @ self.dxdy(y) def x(self, y: np.ndarray) -> np.ndarray: ''' Return a flaten array of adsorbate positions Parameters ---------- y : np.ndarray an axis Returns ------- np.ndarray a flaten array of adsorbate positions ''' self.set_y(y) return self.ads_ref.positions[self.nslab:].ravel().copy() def dxdy(self, y: np.ndarray) -> np.ndarray: ''' Get a dxdy array Parameters ---------- y : np.ndarray an axis Returns ------- dxdy : np.ndarray a reshaped (-1, 6) dxdy array ''' self.set_y(y) dxdy = np.zeros((self.nadsorbate, 3, 6)) dxdy[:, :, :3] = np.eye(3)[np.newaxis] v = y[3:] theta = np.linalg.norm(v) u = v / theta sint = np.sin(theta) cost = np.cos(theta) cosct = (1 - cost) / theta dX = self.adsorbate.positions.copy() dX -= dX.mean(0) for i, xi in enumerate(dX): dxdy[i, :, 3:] = (-sint * np.outer(xi, u) + cost * np.outer(np.cross(u, xi), u) - sint * (skew(xi) + np.outer(np.cross(u, xi), u)) / theta + sint * (u @ xi) * np.outer(u, u) + cosct * ((u @ xi) * np.eye(3) + np.outer(u, xi) - 2 * (u @ xi) * np.outer(u, u))) return dxdy.reshape((-1, 6)) def energy(self, y: np.ndarray) -> float: ''' Return a sum of penalty function and total energy Parameters ---------- y : np.ndarray an axis Returns ------- energy : float a sum of penalty function and total energy ''' return self.penalty(y) + self.ads_ref.get_potential_energy() def gradient(self, y: np.ndarray) -> np.ndarray: ''' Return a scaled gradient Parameters ---------- y : np.ndarray an axis Returns ------- np.ndarray a scaled gradient ''' jac = self.penalty_jac(y) forces = self.ads_ref.get_forces() if self.trajectory is not None: self.trajectory.write() jac -= forces[self.nslab:].ravel() @ self.dxdy(y) return jac * self.scale def optimize(self) -> Atoms: ''' Minimize penalty function defined as: .. math:: P = min\\left[E_{xTB} + \\sum_{i = 1}^{N}{\\left(r^{ts\\_guess}_{X_i M} - \\overline{{r^{min}_{X_{i} M}}}\\right)^2}\\right] Where: :math:`r^{ts\\_guess}_{X_i M}` -- distance between reacting atom and the nearest surface atom ina TS guess; :math:`\overline{{r^{min}_{X_{i} M}}}` -- target bond distance, i.e. an average distance betweenreacting atom and the nearest surface atom calculated for all symmetry distinct minima; :math:`N` -- number of atoms included in a penalty function definition; :math:`E_{xTB}` -- total energy of the system calculated using a robust semi-empirical xTB code Returns ------- ads_ref : Atoms a new structure with improve location of TS guess for which the penalty function is in minimum ''' y0 = np.zeros(6) y0[:3] = self.ads_ref.positions[self.nslab:].mean(0) y0[3:] = 0.001 res1 = minimize(self.penalty, y0, jac=self.penalty_jac, method='l-bfgs-b') y1 = res1['x'] res2 = minimize(self.energy, y1, jac=self.gradient, method='bfgs', options={'disp': True}) # an alternative but usualy bfgs is better/more stable here # res2 = minimize(self.energy, y1, jac=self.gradient, # method='l-bfgs-b', # options={'iprint': 1}) print(res2) y2 = res2['x'] self.set_y(y2) return self.ads_ref.copy()
a2.center(vacuum=2.0) a2.rattle(stdev=0.2) a3 = molecule('CH3CH2OH') a4 = bulk('Au').repeat((2, 2, 2)) a5 = bulk('Cu').repeat((2, 2, 3)) # Add constraints to some of the images: images = [a0, a1, a2, a3, a4, a5] for i, img in enumerate(images[3:]): img.set_constraint(FixAtoms(indices=range(i + 3))) if i == 2: img.constraints.append(FixBondLength(5, 6)) traj = Trajectory('out.traj', 'w') for i, img in enumerate(images): traj.write(img) print(i, traj.multiple_headers) assert traj.multiple_headers == (i >= 2) traj.close() #view(images) rtraj = Trajectory('out.traj') newimages = list(rtraj) assert len(images) == len(newimages) for i in range(len(images)): assert images[i] == newimages[i], i h1 = get_header_data(images[i]) h2 = get_header_data(newimages[i]) print(i, images[i])
def write_modes(self, q_c, branches=0, kT=units.kB * 300, born=False, repeat=(1, 1, 1), nimages=30, center=False): """Write modes to trajectory file. Parameters: q_c: ndarray q-vector of the modes. branches: int or list Branch index of modes. kT: float Temperature in units of eV. Determines the amplitude of the atomic displacements in the modes. born: bool Include non-analytic contribution to the force constants at q -> 0. repeat: tuple Repeat atoms (l, m, n) times in the directions of the lattice vectors. Displacements of atoms in repeated cells carry a Bloch phase factor given by the q-vector and the cell lattice vector R_m. nimages: int Number of images in an oscillation. center: bool Center atoms in unit cell if True (default: False). """ if isinstance(branches, int): branch_l = [branches] else: branch_l = list(branches) # Calculate modes omega_l, u_l = self.band_structure([q_c], modes=True, born=born) # Repeat atoms atoms = self.atoms * repeat # Center if center: atoms.center() # Here ``Na`` refers to a composite unit cell/atom dimension pos_Nav = atoms.get_positions() # Total number of unit cells N = np.prod(repeat) # Corresponding lattice vectors R_m R_cN = np.indices(repeat).reshape(3, -1) # Bloch phase phase_N = np.exp(2.j * pi * np.dot(q_c, R_cN)) phase_Na = phase_N.repeat(len(self.atoms)) for l in branch_l: omega = omega_l[0, l] u_av = u_l[0, l] # Mean displacement of a classical oscillator at temperature T u_av *= sqrt(kT) / abs(omega) mode_av = np.zeros((len(self.atoms), 3), dtype=complex) # Insert slice with atomic displacements for the included atoms mode_av[self.indices] = u_av # Repeat and multiply by Bloch phase factor mode_Nav = np.vstack(N * [mode_av]) * phase_Na[:, np.newaxis] traj = Trajectory('%s.mode.%d.traj' % (self.name, l), 'w') for x in np.linspace(0, 2 * pi, nimages, endpoint=False): atoms.set_positions( (pos_Nav + np.exp(1.j * x) * mode_Nav).real) traj.write(atoms) traj.close()
through the other. Such atoms have coordinates outside the box. This facilitates analysis of e.g. diffusion, but can be problematic when plotting. This script reads through a trajectory file, and write a new one where all atoms are mapped into the computational box. If there are axes with free boundary conditions, the corresponding coordinate is left unchanged. SIDE EFFECT: All energies, forces and stresses are removed (yes, this can be considered as a bug!) """ from __future__ import print_function import sys from ase.io.trajectory import Trajectory if len(sys.argv) != 3: print(__doc__) sys.exit(1) infile = Trajectory(sys.argv[1]) outfile = None for atoms in infile: atoms.set_scaled_positions(atoms.get_scaled_positions()) atoms.set_calculator(None) # or the singlepointcalculator fails! if outfile is None: outfile = Trajectory(sys.argv[2], "w") outfile.write(atoms) outfile.close()
def write(self, filename): from ase.io.trajectory import Trajectory traj = Trajectory(filename, 'w', self) traj.write() traj.close()
def write_modes(self, q_c, branches=0, kT=units.kB*300, born=False, repeat=(1, 1, 1), nimages=30, center=False): """Write modes to trajectory file. Parameters ---------- q_c: ndarray q-vector of the modes. branches: int or list Branch index of modes. kT: float Temperature in units of eV. Determines the amplitude of the atomic displacements in the modes. born: bool Include non-analytic contribution to the force constants at q -> 0. repeat: tuple Repeat atoms (l, m, n) times in the directions of the lattice vectors. Displacements of atoms in repeated cells carry a Bloch phase factor given by the q-vector and the cell lattice vector R_m. nimages: int Number of images in an oscillation. center: bool Center atoms in unit cell if True (default: False). """ if isinstance(branches, int): branch_l = [branches] else: branch_l = list(branches) # Calculate modes omega_l, u_l = self.band_structure([q_c], modes=True, born=born) # Repeat atoms atoms = self.atoms * repeat # Center if center: atoms.center() # Here ``Na`` refers to a composite unit cell/atom dimension pos_Nav = atoms.get_positions() # Total number of unit cells N = np.prod(repeat) # Corresponding lattice vectors R_m R_cN = np.indices(repeat).reshape(3, -1) # Bloch phase phase_N = np.exp(2.j * pi * np.dot(q_c, R_cN)) phase_Na = phase_N.repeat(len(self.atoms)) for l in branch_l: omega = omega_l[0, l] u_av = u_l[0, l] # Mean displacement of a classical oscillator at temperature T u_av *= sqrt(kT) / abs(omega) mode_av = np.zeros((len(self.atoms), 3), dtype=complex) # Insert slice with atomic displacements for the included atoms mode_av[self.indices] = u_av # Repeat and multiply by Bloch phase factor mode_Nav = np.vstack(N * [mode_av]) * phase_Na[:, np.newaxis] traj = Trajectory('%s.mode.%d.traj' % (self.name, l), 'w') for x in np.linspace(0, 2*pi, nimages, endpoint=False): atoms.set_positions((pos_Nav + np.exp(1.j * x) * mode_Nav).real) traj.write(atoms) traj.close()
class BasinHopping(Dynamics): """Basin hopping algorithm. After Wales and Doye, J. Phys. Chem. A, vol 101 (1997) 5111-5116 and David J. Wales and Harold A. Scheraga, Science, Vol. 285, 1368 (1999) """ def __init__(self, atoms, temperature=100 * kB, optimizer=FIRE, fmax=0.1, dr=0.1, logfile='-', trajectory='lowest.traj', optimizer_logfile='-', local_minima_trajectory='local_minima.traj', adjust_cm=True): """Parameters: atoms: Atoms object The Atoms object to operate on. trajectory: string Pickle file used to store trajectory of atomic movement. logfile: file object or str If *logfile* is a string, a file with that name will be opened. Use '-' for stdout. """ self.kT = temperature self.optimizer = optimizer self.fmax = fmax self.dr = dr if adjust_cm: self.cm = atoms.get_center_of_mass() else: self.cm = None self.optimizer_logfile = optimizer_logfile self.lm_trajectory = local_minima_trajectory if isinstance(local_minima_trajectory, str): self.lm_trajectory = Trajectory(local_minima_trajectory, 'w', atoms) Dynamics.__init__(self, atoms, logfile, trajectory) self.initialize() def todict(self): d = {'type': 'optimization', 'optimizer': self.__class__.__name__, 'local-minima-optimizer': self.optimizer.__name__, 'temperature': self.kT, 'max-force': self.fmax, 'maximal-step-width': self.dr} return d def initialize(self): self.positions = 0.0 * self.atoms.get_positions() self.Emin = self.get_energy(self.atoms.get_positions()) or 1.e32 self.rmin = self.atoms.get_positions() self.positions = self.atoms.get_positions() self.call_observers() self.log(-1, self.Emin, self.Emin) def run(self, steps): """Hop the basins for defined number of steps.""" ro = self.positions Eo = self.get_energy(ro) for step in range(steps): En = None while En is None: rn = self.move(ro) En = self.get_energy(rn) if En < self.Emin: # new minimum found self.Emin = En self.rmin = self.atoms.get_positions() self.call_observers() self.log(step, En, self.Emin) accept = np.exp((Eo - En) / self.kT) > np.random.uniform() if accept: ro = rn.copy() Eo = En def log(self, step, En, Emin): if self.logfile is None: return name = self.__class__.__name__ self.logfile.write('%s: step %d, energy %15.6f, emin %15.6f\n' % (name, step, En, Emin)) self.logfile.flush() def move(self, ro): """Move atoms by a random step.""" atoms = self.atoms # displace coordinates disp = np.random.uniform(-1., 1., (len(atoms), 3)) rn = ro + self.dr * disp atoms.set_positions(rn) if self.cm is not None: cm = atoms.get_center_of_mass() atoms.translate(self.cm - cm) rn = atoms.get_positions() world.broadcast(rn, 0) atoms.set_positions(rn) return atoms.get_positions() def get_minimum(self): """Return minimal energy and configuration.""" atoms = self.atoms.copy() atoms.set_positions(self.rmin) return self.Emin, atoms def get_energy(self, positions): """Return the energy of the nearest local minimum.""" if np.sometrue(self.positions != positions): self.positions = positions self.atoms.set_positions(positions) opt = self.optimizer(self.atoms, logfile=self.optimizer_logfile) opt.run(fmax=self.fmax) if self.lm_trajectory is not None: self.lm_trajectory.write(self.atoms) self.energy = self.atoms.get_potential_energy() return self.energy
from numpy import linspace from ase.calculators.fleur import FLEUR from ase.build import bulk from ase.io.trajectory import Trajectory atoms = bulk('Ni', a=3.52) calc = FLEUR(xc='PBE', kmax=3.6, kpts=(10, 10, 10), workdir='lat_const') atoms.set_calculator(calc) traj = Trajectory('Ni.traj','w', atoms) cell0 = atoms.get_cell() for s in linspace(0.95, 1.05, 7): cell = cell0 * s atoms.set_cell((cell)) ene = atoms.get_potential_energy() traj.write()
def write(self, filename): traj = Trajectory(filename, 'w', self) traj.write() traj.close()
results = {} # prepare energies and volumes b = bulk('Al', 'fcc', a=4.0, orthorhombic=True) b.set_calculator(EMT()) cell = b.get_cell() volumes = [] energies = [] traj = Trajectory('eos.traj', 'w') for x in np.linspace(0.97, 1.03, 5): b.set_cell(cell * x, scale_atoms=True) volumes.append(b.get_volume()) energies.append(b.get_potential_energy()) traj.write(b) for n, (v, e) in enumerate(zip(volumes, energies)): vref = ref['volumes'][n] eref = ref['energies'][n] vabserr = abs((v - vref) / vref) vstrerr = str(n) + ' volume: ' + str(v) + ': ' + str(vref) + ': ' + str(vabserr) assert vabserr < 1.e-6, vstrerr eabserr = abs((e - eref) / eref) estrerr = str(n) + ' energy: ' + str(e) + ': ' + str(eref) + ': ' + str(eabserr) assert eabserr < 1.e-4, estrerr # ASE2 try: from ASE.Utilities.EquationOfState import EquationOfState as eos2
calc = get_calc(kpts) atoms.set_calculator(calc) n = 0 E = atoms.get_potential_energy() # Run the optimization with larger convergence limit n += 1 xdat = xdat2traj(trajectory='%.3f-%02i.traj' % (a ,n), calc=calc) xdat.convert() calc.set(ediff=1e-6, ibrion=1, ediffg=-0.005, # Change the van der Waals interactions below gga='MK', luse_vdw=True, param1=0.1234, param2=0.711357, zab_vdw=-1.8867, aggac=0.0000, lasph=True) atoms.get_potential_energy() # run the more accurate optimization n += 1 xdat = xdat2traj(trajectory='%.3f-%02i.traj' % (a ,n), calc=calc) xdat.convert() print(a, E) calc.clean() traj.write(atoms) # Generate a trajectory file with the parabolic relatoin between E and a traj.close()
import numpy as np from ase import Atoms from ase.io.trajectory import Trajectory from ase.calculators.emt import EMT a = 4.0 # approximate lattice constant b = a / 2 ag = Atoms('Ag', cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=1, calculator=EMT()) # use EMT potential cell = ag.get_cell() traj = Trajectory('Ag.traj', 'w') for x in np.linspace(0.95, 1.05, 5): ag.set_cell(cell * x, scale_atoms=True) ag.get_potential_energy() traj.write(ag)