def main(): """ Checks whether ASE's rattle modifies fixed atoms. '""" # Constructs test system slab = fcc100("Cu", size=(3, 3, 3)) ads = molecule("CO") add_adsorbate(slab, ads, 4, offset=(1, 1)) fix_mask = [ atom.index for atom in slab if (atom.tag == 2 or atom.tag == 3) ] free_mask = [ atom.index for atom in slab if (atom.tag != 2 and atom.tag != 3) ] # Apply constraint to fix the bottom 2 layers of the slab. cons = FixAtoms(fix_mask) slab.set_constraint(cons) original_positions = slab.positions ### Rattle system rattled_image = slab.copy() rattled_image.rattle(stdev=1, seed=23794) rattled_positions = rattled_image.positions assert (original_positions[fix_mask].all() == rattled_positions[fix_mask]. all()), "Fixed atoms have been rattled!" assert (original_positions[free_mask].all() != rattled_positions[free_mask].all()), "Remaining atoms not rattled!" print("Test passed! rattle() does not modify fixed atoms")
def test_dimer_method(): # Set up a small "slab" with an adatoms atoms = fcc100('Pt', size=(2, 2, 1), vacuum=10.0) add_adsorbate(atoms, 'Pt', 1.611, 'hollow') # Freeze the "slab" mask = [atom.tag > 0 for atom in atoms] atoms.set_constraint(FixAtoms(mask=mask)) # Calculate using EMT atoms.set_calculator(EMT()) atoms.get_potential_energy() # Set up the dimer d_control = DimerControl(initial_eigenmode_method = 'displacement', \ displacement_method = 'vector', logfile = None, \ mask = [0, 0, 0, 0, 1]) d_atoms = MinModeAtoms(atoms, d_control) # Displace the atoms displacement_vector = [[0.0] * 3] * 5 displacement_vector[-1][1] = -0.1 d_atoms.displace(displacement_vector=displacement_vector) # Converge to a saddle point dim_rlx = MinModeTranslate(d_atoms, trajectory = 'dimer_method.traj', \ logfile = None) dim_rlx.run(fmax=0.001) # Test the results tolerance = 1e-3 assert (d_atoms.get_barrier_energy() - 1.03733136918 < tolerance) assert (abs(d_atoms.get_curvature() + 0.900467048707) < tolerance) assert (d_atoms.get_eigenmode()[-1][1] < -0.99) assert (abs(d_atoms.get_positions()[-1][1]) < tolerance)
def create_surface_Al(self, miller_index, N_x, N_y, N_z, lattice_param): if miller_index == '111': obj = fcc111('Al', size=(N_x, N_y, N_z), a=lattice_param, vacuum=7.5) obj.set_calculator(calc) cell = obj.get_cell() # Unit cell object of the Al FCC surface area = np.linalg.norm(np.cross(cell[0], cell[1])) # Calc. surface area surface = {'object': obj, 'size': (N_x, N_y, N_z), 'a': lattice_param, 'area': area, 'energy': 0, 'sigma': 0} elif miller_index == '100': obj = fcc100('Al', size=(N_x, N_y, N_z), a=lattice_param, vacuum=7.5) obj.set_calculator(calc) cell = obj.get_cell() # Unit cell object of the Al FCC surface area = np.linalg.norm(np.cross(cell[0], cell[1])) # Calc. surface area surface = {'object': obj, 'size': (N_x, N_y, N_z), 'a': lattice_param, 'area': area, 'energy': 0, 'sigma': 0} else: return 1 self.surface.center(axis=2) self.surfaces[miller_index].append(surface) return 0
def test_dimer_method(testdir): # Set up a small "slab" with an adatoms atoms = fcc100('Pt', size=(2, 2, 1), vacuum=10.0) add_adsorbate(atoms, 'Pt', 1.611, 'hollow') # Freeze the "slab" mask = [atom.tag > 0 for atom in atoms] atoms.set_constraint(FixAtoms(mask=mask)) # Calculate using EMT atoms.calc = EMT() atoms.get_potential_energy() # Set up the dimer with DimerControl(initial_eigenmode_method='displacement', displacement_method='vector', logfile=None, mask=[0, 0, 0, 0, 1]) as d_control: d_atoms = MinModeAtoms(atoms, d_control) # Displace the atoms displacement_vector = [[0.0] * 3] * 5 displacement_vector[-1][1] = -0.1 d_atoms.displace(displacement_vector=displacement_vector) # Converge to a saddle point with MinModeTranslate(d_atoms, trajectory='dimer_method.traj', logfile="dimer_method.log") as dim_rlx: dim_rlx.run(fmax=0.001)
def generate_data(count, filename, temp, hook, cons_t=False): """Generates test or training data with a simple MD simulation.""" traj = ase.io.Trajectory(filename, "w") slab = fcc100("Cu", size=(3, 3, 3)) ads = molecule("CO") add_adsorbate(slab, ads, 5, offset=(1, 1)) cons = FixAtoms(indices=[ atom.index for atom in slab if (atom.tag == 2 or atom.tag == 3) ]) if hook: cons2 = Hookean(a1=28, a2=27, rt=1.58, k=10.0) slab.set_constraint([cons, cons2]) else: slab.set_constraint(cons) slab.center(vacuum=13., axis=2) slab.set_pbc(True) slab.wrap(pbc=[True] * 3) slab.set_calculator(EMT()) slab.get_forces() dyn = QuasiNewton(slab) dyn.run(fmax=0.05) traj.write(slab) if cons_t is True: dyn = Langevin(slab, 1.0 * units.fs, temp * units.kB, 0.002) else: dyn = VelocityVerlet(slab, dt=1.0 * units.fs) for step in range(count): dyn.run(20) traj.write(slab)
def energy(N, k, a=4.05): fcc = fcc100('Al', (1, 1, N), a=a, vacuum=7.5) fcc.center(axis=2) calc = GPAW(nbands=N * 3, kpts=(k, k, 1), h=0.25, txt='slab-%d.txt' % N) fcc.set_calculator(calc) e = fcc.get_potential_energy() calc.write('slab-%d.gpw' % N) return e
def attach_leads(self): leadl = build.fcc100('Au', [3, 3, 6]) leadr = build.fcc100('Au', [3, 3, 6]) self.num_leadatoms = leadl.get_number_of_atoms() dAuC = 1.6 Lcl = leadl[49] Lcr = leadr[4] Mcl = self.mol[0] Mcr = self.mol[-1] xs = Lcl.x - (Mcl.x + Mcr.x) / 2 ys = Lcl.y - (Mcl.y + Mcr.y) / 2 self.mol.translate([xs, ys, Lcl.z - Mcl.z + dAuC]) leadr.translate([0.4, -0.4, Mcr.z - Lcr.z + dAuC]) self.mol = leadl + self.mol + leadr
def attach_leads(mol): leadl = build.fcc100('Au', [3, 3, 6]) leadr = build.fcc100('Au', [3, 3, 6]) #num_leadatoms = leadl.get_number_of_atoms() dAuN = dAuC = 1.6 Lcl = leadl[49] Lcr = leadr[4] Mcl = mol[0] Mcr = mol[-4] xs = Lcl.x - (Mcl.x + Mcr.x) / 2 ys = Lcl.y - (Mcl.y + Mcr.y) / 2 mol.translate([xs, ys, Lcl.z - Mcl.z + dAuN]) leadr.translate([0.4, -0.4, Mcr.z - Lcr.z + dAuC]) return leadl + mol + leadr
def get_mgsi_surface100_si_si(): from ase.build import fcc100 atoms = fcc100("Al", size=(10, 10, 5)) for atom in atoms: if atom.tag % 2 == 0: atom.symbol = "Mg" else: atom.symbol = "Si" return atoms
def prepare_Au_on_Al_slab(): atoms = fcc100("Al", size=(2, 2, 3)) add_adsorbate(atoms, "Au", 1.7, "hollow") atoms.center(axis=2, vacuum=4.0) atoms.set_calculator(EMT()) # Fix second and third layers: mask = [atom.tag > 1 for atom in atoms] atoms.set_constraint(FixAtoms(mask=mask)) # #atoms.write("STRUCT.xsf") # return atoms
def traj(tmpdir_factory): slab = fcc100('Al', size=(2, 2, 3)) add_adsorbate(slab, 'Au', 1.7, 'hollow') slab.center(axis=2, vacuum=4.0) mask = [atom.tag > 1 for atom in slab] fixlayers = FixAtoms(mask=mask) plane = FixedPlane(-1, (1, 0, 0)) slab.set_constraint([fixlayers, plane]) slab.set_calculator(EMT()) fn = tmpdir_factory.mktemp("data").join("AlAu.traj") # see /tmp/pytest-xx qn = QuasiNewton(slab, trajectory=str(fn)) qn.run(fmax=0.02) return fn
def run_relaxation(calculator, filename, steps=500): slab = fcc100("Cu", size=(3, 3, 3)) ads = molecule("CO") add_adsorbate(slab, ads, 4, offset=(1, 1)) cons = FixAtoms(indices=[ atom.index for atom in slab if (atom.tag == 2 or atom.tag == 3) ]) slab.set_constraint([cons]) slab.center(vacuum=13.0, axis=2) slab.set_pbc(True) slab.set_calculator(calculator) print("### Generating data") dyn = BFGS(slab, trajectory=filename, logfile=None) dyn.run(fmax=0.01, steps=steps)
def get_atoms(): # 2x2-Al(001) surface with 3 layers and an # Au atom adsorbed in a hollow site: slab = fcc100('Al', size=(2, 2, 3)) add_adsorbate(slab, 'Au', 1.7, 'hollow') slab.center(axis=2, vacuum=4.0) # Fix second and third layers: mask = [atom.tag > 1 for atom in slab] slab.set_constraint(FixAtoms(mask=mask)) # Use EMT potential: slab.set_calculator(EMT()) # Initial state: qn = QuasiNewton(slab, logfile=None) qn.run(fmax=0.05) initial = slab.copy() # Final state: slab[-1].x += slab.get_cell()[0, 0] / 2 qn = QuasiNewton(slab, logfile=None) qn.run(fmax=0.05) final = slab.copy() # Setup a NEB calculation constraint = FixAtoms(mask=[atom.tag > 1 for atom in initial]) images = [initial] for i in range(3): image = initial.copy() image.set_constraint(constraint) images.append(image) images.append(final) neb = NEB(images, parallel=mpi.parallel) neb.interpolate() def set_calculator(calc): i = 0 for image in neb.images[1:-1]: if not mpi.parallel or mpi.rank // (mpi.size // 3) == i: image.set_calculator(calc) i += 1 neb.set_calculator = set_calculator return neb
def traj(tmp_path_factory): slab = fcc100('Al', size=(2, 2, 3)) add_adsorbate(slab, 'Au', 1.7, 'hollow') slab.center(axis=2, vacuum=4.0) mask = [atom.tag > 1 for atom in slab] fixlayers = FixAtoms(mask=mask) plane = FixedPlane(-1, (1, 0, 0)) slab.set_constraint([fixlayers, plane]) slab.calc = EMT() temp_path = tmp_path_factory.mktemp("data") trajectory = temp_path / 'AlAu.traj' qn = QuasiNewton(slab, trajectory=str(trajectory)) qn.run(fmax=0.02) return str(trajectory)
def test_harmonic_thermo(): atoms = fcc100('Cu', (2, 2, 2), vacuum=10.) atoms.calc = EMT() add_adsorbate(atoms, 'Pt', 1.5, 'hollow') atoms.set_constraint(FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Cu'])) QuasiNewton(atoms).run(fmax=0.01) vib = Vibrations(atoms, name='harmonicthermo-vib', indices=[atom.index for atom in atoms if atom.symbol != 'Cu']) vib.run() vib.summary() vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, potentialenergy=atoms.get_potential_energy()) thermo.get_helmholtz_energy(temperature=298.15)
def construct_geometries(parent_calc, ml2relax): counter_calc = parent_calc # Initial structure guess initial_slab = fcc100("Cu", size=(2, 2, 3)) add_adsorbate(initial_slab, "C", 1.7, "hollow") initial_slab.center(axis=2, vacuum=4.0) mask = [atom.tag > 1 for atom in initial_slab] initial_slab.set_constraint(FixAtoms(mask=mask)) initial_slab.set_pbc(True) initial_slab.wrap(pbc=[True] * 3) initial_slab.set_calculator(counter_calc) # Final structure guess final_slab = initial_slab.copy() final_slab[-1].x += final_slab.get_cell()[0, 0] / 3 final_slab.set_calculator(counter_calc) if not ml2relax: print("BUILDING INITIAL") qn = BFGS(initial_slab, trajectory="initial.traj", logfile="initial_relax_log.txt") qn.run(fmax=0.01, steps=100) print("BUILDING FINAL") qn = BFGS(final_slab, trajectory="final.traj", logfile="final_relax_log.txt") qn.run(fmax=0.01, steps=100) initial_slab = read("initial.traj", "-1") final_slab = read("final.traj", "-1") # If there is already a pre-existing initial and final relaxed parent state we can read # that to use as a starting point # initial_slab = read("/content/parent_initial.traj") # final_slab = read("/content/parent_final.traj") else: initial_slab = initial_slab final_slab = final_slab # initial_force_calls = counter_calc.force_calls return initial_slab, final_slab # , initial_force_calls
def slabgen(termination, size, adsorbate, position): if termination == '100': prim = fcc100('Cu', a=3.6302862146117354, size=(1, 1, 5), vacuum=15, orthogonal=True, periodic=True) elif termination == '111': prim = fcc111_root('Cu', root=3, a=3.6302862146117354, size=(1, 1, 5), vacuum=15) super = make_supercell(prim, size) add_adsorbate(slab=super, adsorbate=adsorbate, height=ads_height, position=position) constr = FixAtoms( indices=[atom.index for atom in super if atom.position[2] < 19]) super.set_constraint(constr) return super
def aual100(site, height, calc=None): slab = fcc100('Al', size=(2, 2, 2)) slab.center(axis=2, vacuum=3.0) add_adsorbate(slab, 'Au', height, site) mask = [atom.symbol == 'Al' for atom in slab] fixlayer = FixAtoms(mask=mask) slab.set_constraint(fixlayer) if calc is None: calc = GPAW(mode=PW(200), kpts=(2, 2, 1), xc='PBE', txt=site + '.txt', eigensolver='rmm-diis', nbands=40) slab.set_calculator(calc) qn = QuasiNewton(slab, trajectory=site + calc.name + '.traj') qn.run(fmax=0.05) if isinstance(calc, GPAW): calc.write(site + '.gpw') return slab.get_potential_energy()
from ase.build import surface, fcc100, add_adsorbate from ase.constraints import FixAtoms from ase.io import read import numpy as np from ase.visualize import view #bulk = read('../bulk/converged_bulk.traj') #a=3.89 #a = np.linalg.norm(bulk.cell[0]) vac = 6 #atoms = fcc100('Pd', (2,2,4), a=a, vacuum=10) atoms = fcc100('Pd', (2, 2, {l}), vacuum=vac) nitrate = read('nitrate.xyz') add_adsorbate(atoms, nitrate, 1.5, 'ontop') c = FixAtoms(mask=[a.z < vac + 2 for a in atoms]) atoms.set_constraint(c) #atoms *= (2,2,1) atoms.write('structure.traj')
algo='Normal', ncore=4, xc='PBE', gga='RP', lreal=False, ediff=1e-4, ispin=1, nelm=100, encut=400, lwave=False, lcharg=False, nsw=0, kpts=(1, 1, 1)) # Define initial set of images, can be as few as 1. If 1, make sure to slab = fcc100("Cu", size=(3, 3, 3)) ads = molecule("CO") add_adsorbate(slab, ads, 3, offset=(1, 1)) cons = FixAtoms(indices=[atom.index for atom in slab if (atom.tag == 3)]) slab.set_constraint(cons) slab.center(vacuum=13.0, axis=2) slab.set_pbc(True) slab.wrap(pbc=True) slab.set_calculator(copy.copy(dft_calc)) sample_energy = slab.get_potential_energy(apply_constraint=False) sample_forces = slab.get_forces(apply_constraint=False) slab.set_calculator( sp(atoms=slab, energy=sample_energy, forces=sample_forces)) ase.io.write("./slab.traj", slab) images = [slab]
from ase import Atom, Atoms from ase.build import bulk, fcc100, add_adsorbate, add_vacuum from ase.calculators.vasp import Vasp from ase.calculators.kim.kim import KIM from ase.calculators.qmmm import ForceQMMM, RescaledCalculator from ase.constraints import StrainFilter from ase.optimize import LBFGS from ase.visualize import view atoms = bulk("Pd", "fcc", a=3.5, cubic=True) atoms.calc = KIM("MEAM_LAMMPS_JeongParkDo_2018_PdMo__MO_356501945107_000") opt = LBFGS(StrainFilter(atoms), logfile=None) opt.run(0.03, steps=30) length = atoms.cell.cellpar()[0] atoms = fcc100("Pd", (2,2,5), a=length, vacuum=10, periodic=True) add_adsorbate(atoms, Atoms([Atom("Mo")]), 1.2) qm_mask = [len(atoms)-1, len(atoms)-2] qm_calc = Vasp(directory="./qmmm") mm_calc = KIM("MEAM_LAMMPS_JeongParkDo_2018_PdMo__MO_356501945107_000") mm_calc = RescaledCalculator(mm_calc, 1, 1, 1, 1) qmmm = ForceQMMM(atoms, qm_mask, qm_calc, mm_calc, buffer_width=3) qmmm.initialize_qm_buffer_mask(atoms) atoms.pbc=True atoms.calc = qmmm print(atoms.get_forces())
def Nanoparticle_Builder(metal,lc,surfaces,layers,adsorbate,site,coverage,cell_height,subsys_height,unmasked_layers,E_cut,kpoints,ads_cutoff,ads111,ads100,subsys111_relaxed,subsys100_relaxed,relax): ### ### Function to set up the Nanoparticle base ### #Input pparameters # metal: The metal comprising the substrate/nanoparticle. For example 'Au'. # lc: Lattice constant. For example 4.0800. # surfaces: The surfaces in the NP. For example [(1, 0, 0),(1, 1, 1)]. # layers: The number of layers from the cetner of the NP to each surface. For example: [6,5] # adsorbate: The molecule to be adsorbed on top. Should be an object which ase can read. For example: molecule('CO') # site: Either 'OnTop' or 'Hollow'. Should be 'OnTop' for 100% coverages. # coverage: Desired coverage. Must be 1/n where n is whole number. # cell_height: The height of the subsystem unit cell. For example 6*lc. # subsys_height: Height of subsystem - no. of atom layers. For example: 4. If reading the relaxed subsystem from a traj file MAKE SURE THE HEIGHTS MATCH. # unmasked_layers: The number of layers in the NP subsystem (from the top) that is allowed to move during DFT relaxation. # E_cut: Cutoff energy for DFT PW. # kpoints: k-point grid in the first Brillouin zone. # ads_cutoff: The cutoff for adsorption energy. This determines whether adsorption has occurred for a surface or not. # ads111: If providing a relaxed subsystem, this is the adsorption energy for the 111 surface. # ads100: If providing a relaxed subsystem, this is the adsorption energy for the 100 surface. # subsys111_relaxed: This is an ASE object containing the relaxed subsystem for 111. Necessary to skip DFT. # subsys100_relaxed: This is an ASE object containing the relaxed subsystem for 100. Necessary to skip DFT. # relax: A boolean to determine whether or not DFT calculations have to be run or not. If relax=False, make sure to provide subsys111_relaxed and subsys100_relaxed along with ads111 and ads100. if site==None: site='OnTop' if cell_height==None: cell_height=6*lc if subsys_height==None: subsys_height=3 if unmasked_layers==None: unmasked_layers=1 if E_cut==None: E_cut=200 if kpoints==None: kpoints=(6,6,1) if ads_cutoff==None: ads_cutoff=0 #The bulk structure is created. Nanoparticle = FaceCenteredCubic(metal, surfaces, layers, latticeconstant=lc) Reference = FaceCenteredCubic(metal, surfaces, layers, latticeconstant=lc) #A reference is made for TEM imaging without adsorbates. ##Alternative wulff_construction-based setup. This requires N_atoms and surf_energies list to be set up (for gold, use surf_energies=[1.23,1]). #Nanoparticle = wulff_construction(metal, surfaces, surf_energies,size=N_atoms, 'fcc',rounding='above', latticeconstant=lc) #Reference=wulff_construction(metal, surfaces, surf_energies,size=N_atoms, 'fcc',rounding='above', latticeconstant=lc) surf_atoms=[] #This list will contain indeces for each atom that is on a surface of the nanoparticle. #Each surface is rotated to the top of the unit cell. The corresponding surface atoms are then the atoms with highest y-position. for i in Nanoparticle.get_surfaces(): Nanoparticle.rotate(i,[0,1,0],center="COU") y_max=0 for j in Nanoparticle: if j.position[1]>y_max: y_max=j.position[1] for j in Nanoparticle: if round(j.position[1],2)==round(y_max,2): surf_atoms.append(j.index) Nanoparticle.rotate([0,1,0],i,center="COU") #Now we need to identify the edge atoms. These are the atoms that are part of 2 or more surfaces in the nanoparticle. #Therefore, they will be the atoms that appear more than once in surf_atoms: marked=[] edge_atoms=[] for i in surf_atoms: if i in marked: edge_atoms.append(i) else: marked.append(i) #A subsystem of the bulk is defined. This will be the basis of the DFT calculation. We also define relevant functions to #translate between bulk atom coordinates and the subsystem coordinates. def sizesetup(coverage): #Define the function to set up the size of the unit cell. invconverage = math.floor(1/coverage) for i in range(1,invconverage+1): for j in range(1,invconverage+1): if j/i**2==coverage: return (i,j) break #This function wraps lattice coordinates (i,j) back into the corresponding coordinate in the unit cell. def coordreference(i,j,ucx,ucy,direction): if direction==1: ri = i%ucx ry = j%ucy if direction==3: #If the unit cell is not orthogonal: if ucx%2!=0 and ucy%2!=0: ri = i%ucx ry = j%ucy #If the unit cell is orthogonal: else: i = i+j/2-(j%ucy)/2 #Moving along j also corresponds to movement along i. ri = i%ucx ry = j%ucy return (ri,ry) ss = sizesetup(coverage) ucx = ss[0] ucy = ss[0] height = subsys_height n_adsorbates=ss[1] #Check if the requirement for orthogonal unit cell is met: if ss[0]%2==0: orthogonal=True else: orthogonal=False size = [ucx,ucy,height] #Size of the FCC bulk structure. #Set up subsystems for the 111 and 100 directions. subsys111 = fcc111(symbol=metal, size=size,a=lc, vacuum=None,orthogonal=orthogonal) subsys100=fcc100(symbol=metal, size=size,a=lc, vacuum=None, orthogonal=True) # Give all atoms in top layer a coordinate atomorigo = ucx*ucy*(height-1) n = ucx*ucy*height subsys_coordinates={} i = 0 j = 0 for q in range(atomorigo,n): if (i==ucx): j += 1 i = 0 subsys_coordinates[q] = [i,j,0] i += 1 #Now we have to find a set of basis vectors describing the subsystem surface. if ss[0]>1: #For 111: v1_111=subsys111[atomorigo+1].position-subsys111[atomorigo].position v1_111=np.array([v1_111[0],v1_111[1],0]) v2_111=subsys111[atomorigo+ss[0]].position-subsys111[atomorigo].position v2_111=np.array([v2_111[0],v2_111[1],0]) #For 100: v1_100=subsys100[atomorigo+1].position-subsys100[atomorigo].position v1_100=np.array([v1_100[0],v1_100[1],0]) v2_100=subsys100[atomorigo+ss[0]].position-subsys100[atomorigo].position v2_100=np.array([v2_100[0],v2_100[1],0]) else: v1_111=np.array([0,0,0]) v2_111=np.array([0,0,0]) v1_100=np.array([0,0,0]) v2_100=np.array([0,0,0]) #Now we add adsorbates matching the coverage. They are added along the diagonal in the unit cell. if site=='OnTop': position100=[0,0,0] position111=[0,0,0] else: position100=1/2*v1_100+1/2*v2_100 position111=1/2*v1_111+[0,1/2*v2_111[1],0] zig=True #If zig is false, then the 111 adsorbate won't zag. zags=0 adsorbate_atom_links=[]#This list will link adsorbates to an atom in the surface. for i in range(0,n_adsorbates): zig = not zig if zig and i>1: zags=zags+1 for j in adsorbate: j.tag=i scale=ss[0]/n_adsorbates adsorbate_atom_links.append([i*scale,i*scale]) pos111=position111+i*scale*v1_111+i*scale*v2_111-zags*v1_111 pos100=position100+i*scale*v1_100+i*scale*v2_100 add_adsorbate(subsys111,adsorbate,height=1*lc,position=(pos111[0],pos111[1]),mol_index=0) add_adsorbate(subsys100,adsorbate,height=1*lc,position=(pos100[0],pos100[1]),mol_index=0) subsys111.set_cell([subsys111.cell[0],subsys111.cell[1],([0.,0.,cell_height])]) subsys100.set_cell([subsys100.cell[0],subsys100.cell[1],([0.,0.,cell_height])]) #Create vectors with initial coordinates for each atom. Offsets from these coordinates are found after the DFT is run. x111_i=[] y111_i=[] z111_i=[] x100_i=[] y100_i=[] z100_i=[] for i in range(0,n): x111_i.append(subsys111[i].position[0]) y111_i.append(subsys111[i].position[1]) z111_i.append(subsys111[i].position[2]) x100_i.append(subsys100[i].position[0]) y100_i.append(subsys100[i].position[1]) z100_i.append(subsys100[i].position[2]) if relax: #A subsystem of the bulk is defined. This will be the basis of the DFT calculation. We also define relevant functions to #translate between bulk atom coordinates and the subsystem coordinates. ss = sizesetup(coverage) ucx = ss[0] ucy = ss[0] height = subsys_height n_adsorbates=ss[1] #Check if the requirement for orthogonal unit cell is met: if ss[0]%2==0: orthogonal=True else: orthogonal=False size = [ucx,ucy,height] #Size of the FCC bulk structure. Preferably size_z is odd. #Redefine subsystem for energy calculations: subsys111 = fcc111(symbol=metal, size=size,a=lc, vacuum=None,orthogonal=orthogonal) subsys100=fcc100(symbol=metal, size=size,a=lc, vacuum=None, orthogonal=True) # Give all atoms in top layer a coordinate atomorigo = ucx*ucy*(height-1) n = ucx*ucy*height subsys_coordinates={} i = 0 j = 0 for q in range(atomorigo,n): if (i==ucx): j += 1 i = 0 subsys_coordinates[q] = [i,j,0] i += 1 # Calculate system energies: energyfile = open('energies-%s-%s.txt' % (str(coverage),"butanethiolate_hollow"), 'w') energies = {} for i in ['adsorbate', 'subsys111', 'subsys100']: system = globals()[i].copy() if i=='adsorbate': system.center(vacuum=5) system.set_pbc((1,1,0)) else: system.set_cell([system.cell[0],system.cell[1],([0.,0.,cell_height])]) calc = GPAW(mode=PW(E_cut),kpts=kpoints,xc='BEEF-vdW',txt='energy-%s.txt' % i) system.set_calculator(calc) energy = system.get_potential_energy() energies[i] = energy #Now we have to find a set of basis vectors describing the subsystem surface. if ss[0]>1: #For 111: v1_111=subsys111[atomorigo+1].position-subsys111[atomorigo].position v1_111=np.array([v1_111[0],v1_111[1],0]) v2_111=subsys111[atomorigo+ss[0]].position-subsys111[atomorigo].position v2_111=np.array([v2_111[0],v2_111[1],0]) #For 100: v1_100=subsys100[atomorigo+1].position-subsys100[atomorigo].position v1_100=np.array([v1_100[0],v1_100[1],0]) v2_100=subsys100[atomorigo+ss[0]].position-subsys100[atomorigo].position v2_100=np.array([v2_100[0],v2_100[1],0]) else: v1_111=np.array([0,0,0]) v2_111=np.array([0,0,0]) v1_100=np.array([0,0,0]) v2_100=np.array([0,0,0]) #Now we add adsorbates matching the coverage. They are added along the diagonal in the unit cell. if site=='OnTop': position100=[0,0,0] position111=[0,0,0] else: position100=1/2*v1_100+1/2*v2_100 position111=1/2*v1_111+[0,1/2*v2_111[1],0] zig=True #If zig is false, then the 111 adsorbate won't zag. zags=0 #This is the number of zags that have been performed. adsorbate_atom_links=[]#This list will link adsorbates to an atom in the surface. for i in range(0,n_adsorbates): zig = not zig if zig and i>1: zags=zags+1 for j in adsorbate: j.tag=i scale=ss[0]/n_adsorbates adsorbate_atom_links.append([i*scale,i*scale]) #pos111=position111+((i*scale)/2)*v1_111+[0,i*scale*v2_111[1],0] pos111=position111+i*scale*v1_111+i*scale*v2_111-zags*v1_111 pos100=position100+i*scale*v1_100+i*scale*v2_100 add_adsorbate(subsys111,adsorbate,height=1*lc,position=(pos111[0],pos111[1]),mol_index=0) add_adsorbate(subsys100,adsorbate,height=1*lc,position=(pos100[0],pos100[1]),mol_index=0) subsys111.set_cell([subsys111.cell[0],subsys111.cell[1],([0.,0.,cell_height])]) #subsys110.set_cell([subsys110.cell[0],subsys110.cell[1],([0.,0.,cell_height])]) subsys100.set_cell([subsys100.cell[0],subsys100.cell[1],([0.,0.,cell_height])]) #Create vectors with initial coordinates for each atom. Offsets from these coordinates are found after the DFT is run. x111_i=[] y111_i=[] z111_i=[] x100_i=[] y100_i=[] z100_i=[] for i in range(0,n): x111_i.append(subsys111[i].position[0]) y111_i.append(subsys111[i].position[1]) z111_i.append(subsys111[i].position[2]) x100_i.append(subsys100[i].position[0]) y100_i.append(subsys100[i].position[1]) z100_i.append(subsys100[i].position[2]) #The calculator is set. calc111 = GPAW(mode=PW(E_cut),kpts=kpoints, xc='BEEF-vdW',txt='calc111.out') subsys111.set_calculator(calc111) #The bottom layers of the subsystem are masked such that these atoms do not move during QuasiNewton minimization/relaxation. mask = [i.tag > unmasked_layers and i.symbol==metal for i in subsys100] fixedatoms=FixAtoms(mask=mask) subsys111.set_constraint(fixedatoms) #The subsystem is relaxed until all forces on atoms are below fmax=0.02. relax = QuasiNewton(subsys111, trajectory='relax111.traj') relax.run(fmax=0.02) #The calculator is set. calc100 = GPAW(mode=PW(E_cut),kpts=kpoints, xc='BEEF-vdW',txt='calc100.out') subsys100.set_calculator(calc100) #The bottom layer of the subsystem is masked such that these atoms do not move during QuasiNewton minimization/relaxation. subsys100.set_constraint(fixedatoms) #The subsystem is relaxed until all forces on atoms are below fmax=0.02. relax = QuasiNewton(subsys100, trajectory='relax100.traj') relax.run(fmax=0.02) ## Calculate new energies for i in ['subsys111', 'subsys100']: system = globals()[i] energy = system.get_potential_energy() energies["relax"+i[6:]] = energy e_bond = {} e_bond['subsys111'] = energies['relax111'] - energies['subsys111'] - n_adsorbates*energies['adsorbate'] e_bond['subsys100'] = energies['relax100'] - energies['subsys100'] - n_adsorbates*energies['adsorbate'] print(energies,e_bond, file=energyfile) energyfile.close() ads111=e_bond['subsys111'] ads100=e_bond['subsys100'] if subsys100_relaxed!=None: subsys100=subsys100_relaxed if subsys111_relaxed!=None: subsys111=subsys111_relaxed #Check if adsorption has occurred. If not, remove adsorbates from corresponding subsystem. if ads111>=ads_cutoff: subsys111_prov=subsys111.copy() subsys111=Atoms() for i in subsys111_prov: if i.symbol==metal: subsys111.append(i) if ads100>=ads_cutoff: subsys100_prov=subsys100.copy() subsys100=Atoms() for i in subsys100_prov: if i.symbol==metal: subsys100.append(i) #Now to find offsets for each atom in the subsystems after DFT: x111_offset=[] y111_offset=[] z111_offset=[] x100_offset=[] y100_offset=[] z100_offset=[] for i in range(0,n): x111_offset.append(subsys111[i].position[0]-x111_i[i]) y111_offset.append(subsys111[i].position[1]-y111_i[i]) z111_offset.append(subsys111[i].position[2]-z111_i[i]) x100_offset.append(subsys100[i].position[0]-x100_i[i]) y100_offset.append(subsys100[i].position[1]-y100_i[i]) z100_offset.append(subsys100[i].position[2]-z100_i[i]) # Define dictionary of indexed surfaces for use in TEM indexed_surfaces= {} #Sadly, we now have to do all the rotations again: surface_coordinates={} #For convenience this dictionary will contain v1,v2 coordinates for all the surfaces - indexed by their #respective origo atom's index. for i in Nanoparticle.get_surfaces(): surface=[] #This list will contain the atoms on the specific surface at the top. Nanoparticle.rotate(i,[0,1,0],center="COU") y_max=0 for j in Nanoparticle: if j.position[1]>y_max and j.symbol==metal: y_max=j.position[1] for j in Nanoparticle: if round(j.position[1],2)==round(y_max,2) and j.symbol==metal: surface.append(j.index) #Now surface contains the indeces of atoms of the specific surface that is at the top in this rotation. direction=abs(i[0])+abs(i[1])+abs(i[2]) #This number determines the surface direction family - 100, 110, 111. #Define a dictionary with all indeces of atoms of surface i indexed_surfaces[tuple(i)] = surface #Find maximum z and x values for the surface: x_max=0 z_max=0 for k in surface: if Nanoparticle[k].position[0]>x_max: x_max=Nanoparticle[k].position[0] if Nanoparticle[k].position[2]>z_max: z_max=Nanoparticle[k].position[2] x_min=x_max z_min=z_max for k in surface: if Nanoparticle[k].position[0]<x_min: x_min=Nanoparticle[k].position[0] if Nanoparticle[k].position[2]<z_min: z_min=Nanoparticle[k].position[2] bot_row=[] #This will contain the indeces of the bottom (low z) row of atoms. #Find the atoms in the bottom row: for k in surface: if round(Nanoparticle[k].position[2],1)==round(z_min,1): bot_row.append(k) #Find the atom in the corner of the surface: corner_atom=bot_row[0] for k in bot_row: if Nanoparticle[k].position[0]<Nanoparticle[corner_atom].position[0]: corner_atom=k distance_1=2*lc #The distance between corner_atom and the nearest neighbour is at least smaller than this. neighbour_1="d" #placeholder for neighbour 1. neighbour_2="d" #placeholder for neighbour 2. ## Find the unit cell neighbours to corner_atom. v1=[] v2=[] for k in surface: if k!=corner_atom and k in edge_atoms: #The v1-axis should lie along some edge. if np.linalg.norm(Nanoparticle[k].position-Nanoparticle[corner_atom].position)<distance_1: distance_1=np.linalg.norm(Nanoparticle[k].position-Nanoparticle[corner_atom].position) neighbour_1=k #Construct the first basis vector for the surface using the first nearest neighbour coordinate. v1=Nanoparticle[neighbour_1].position-Nanoparticle[corner_atom].position v1[1]=0 #The y-coordinate of the surface basis vector is set to 0. # To find the second neighbour, we have to align the v1 vector with the x-axis: Nanoparticle.rotate(v1,[1,0,0],center='COU') for k in surface: if k!=corner_atom and k!=neighbour_1: dist_vector=Nanoparticle[k].position-Nanoparticle[corner_atom].position # We require that the angle between dist_vector and v1 is <=90. if math.acos(round(np.dot(dist_vector,v1)/(np.linalg.norm(dist_vector)*np.linalg.norm(v1)),5))<=90: # We check for a dist_vector which corresponds to one of the lattice vectors defined for the subsystem. if direction==1: if round(dist_vector[0],5)==round(v2_100[0],5) and round(dist_vector[2],5)==round(v2_100[1],5): neighbour_2=k if round(dist_vector[0],5)==round(v2_100[0],5) and round(dist_vector[2],5)==-round(v2_100[1],5): neighbour_2=k if direction==3: if round(dist_vector[0],5)==round(v2_111[0],5) and round(dist_vector[2],5)==round(v2_111[1],5): neighbour_2=k if round(dist_vector[0],5)==round(v2_111[0],5) and round(dist_vector[2],5)==-round(v2_111[1],5): neighbour_2=k # Rotate the system back after finding the second neighbour. Nanoparticle.rotate([1,0,0],v1,center='COU') #Construct the second basis vector for the surface using the nearest neighbour coordinate. v2=Nanoparticle[neighbour_2].position-Nanoparticle[corner_atom].position v2[1]=0 #The y-coordinate of the surface basis vector is set to 0. Transform_matrix=np.linalg.inv(np.array([v1,v2,[0,1,0]]).transpose()) #This transforms x-y-z coordinates to v1-v2-y. #Now to find v1,v2-coordinates for all the atoms in the surface and replace them with atoms from DFT subsystem accordingly: surface.sort for k in surface: if k not in edge_atoms: flag = False #Flag to determine wether the z-axis was flipped. #Find the coordinate of the atom in v1,v2-basis. coordinate=np.round(Transform_matrix.dot(Nanoparticle[k].position-Nanoparticle[corner_atom].position),0) #We want the origo of the surface system to be off the surface edge. Therefore, we translate the coordinates: coordinate[0]=coordinate[0]-1 coordinate[1]=coordinate[1]-1 reference=coordreference(coordinate[0],coordinate[1],ucx,ucy,direction) #This references the matching atom in the subsystem #The system is rotated again such that the bottom row of atoms lie along the x-direction. Nanoparticle.rotate(v1,[1,0,0],center="COU") #Check if v2 is in the positive z-direction. If not, rotate the system 180 degrees: if Nanoparticle[neighbour_2].position[2]-Nanoparticle[corner_atom].position[2]<0: Nanoparticle.rotate([0,0,-1],[0,0,1],center='COU') flag = True #Flag is set indicating the system has been flipped. for l in subsys_coordinates: if subsys_coordinates[l]==[reference[0],reference[1],0]: #This atom in the subsystem matches the atom in the Nanoparticle surface. if direction==1: #Apply the corresponding offset from the 100 list: Nanoparticle[k].position=Nanoparticle[k].position+[x100_offset[l],z100_offset[l],y100_offset[l]] if direction==3: #Apply the corresponding offset from the 111 list: Nanoparticle[k].position=Nanoparticle[k].position+[x111_offset[l],z111_offset[l],y111_offset[l]] if [reference[0],reference[1]] in adsorbate_atom_links: #Checks if the subsystem reference atom is linked to an adsorbate molecule. for u in range(0,len(adsorbate_atom_links)): #Find the linked adsorbate molecule tag (it's the index of adsorbate_atom_links) if adsorbate_atom_links[u] == [reference[0],reference[1]]: #Check if the reference coordinates exists in the list of linked atoms coordinates. adsorbate_tag=u #This is the tag assigned to the adsorbate that is linked to the reference atom. #Calculate the reference atom's index in the subsystem (from the v1/v2-coordinates). tagged_atom_index=int(atomorigo+((adsorbate_atom_links[adsorbate_tag][1])%ucy)*ucx+adsorbate_atom_links[adsorbate_tag][0]%ucx) if direction==1: for m in subsys100: if m.symbol!=metal and m.tag==adsorbate_tag: #Single out the adsorbate with the correct tag. m_old_position=m.position.copy() #Save the adsorbate's original position. m.position=m.position-subsys100[tagged_atom_index].position #Calculate the vector from the reference atom to the adsorbate. #Now calculate and set the position to that which the adsorbate atom should have in the Nanoparticle system: m.position=[m.position[0]+Nanoparticle[k].position[0],m.position[2]+Nanoparticle[k].position[1],m.position[1]+Nanoparticle[k].position[2]] Nanoparticle.append(m) #Finally, add the adsorbate. m.position=m_old_position #Reset the subsystem (set the adsorbate position to it's old, saved value). if direction==3: #Do exactly the same below as for the 100 surface above: for m in subsys111: if m.symbol!=metal and m.tag==adsorbate_tag: m_old_position=m.position.copy() m.position=m.position-subsys111[tagged_atom_index].position m.position=[m.position[0]+Nanoparticle[k].position[0],m.position[2]+Nanoparticle[k].position[1],m.position[1]+Nanoparticle[k].position[2]] Nanoparticle.append(m) m.position=m_old_position #Check if the z-axis was flipped. If it was, flip it back: if flag: Nanoparticle.rotate([0,0,1],[0,0,-1],center='COU') #The system is then rotated back. Nanoparticle.rotate([1,0,0],v1,center="COU") #First the x-axis alignment. Nanoparticle.rotate([0,1,0],i,center="COU") #Now rotate the surface back to it's original direction. ## Find the actual coverage of the Nanoparticle: ## This procedure is outdated, but it still works. #First we need to count the number of surface atoms (including edges). First we turn surf_atoms into a dictionary and back into # a list in order to remove duplicate values (edge atoms should only be counted once): surf_atoms = list(dict.fromkeys(surf_atoms)) #The number of surf atoms is then: n_surf_atoms=len(surf_atoms) #Now to count the number of adsorbed molecules. First we count the number of non-gold atoms: non_gold_atoms=0 for i in Nanoparticle: if i.symbol!=metal: non_gold_atoms+=1 #Then we count the number of atoms in the adsorbate molecules: adsorbate_size=len(adsorbate) #The number of adsorbed molecules: n_adsorbate=non_gold_atoms/adsorbate_size #The actual coverage is then: actual_coverage=n_adsorbate/n_surf_atoms Nanoparticle.center(vacuum=4) #Center the nanoparticle for TEM imaging. return Nanoparticle,indexed_surfaces,subsys111,subsys100 #Depending on what is needed, return the different objects.
calc = EMT() cu_bulk.set_calculator(calc) e = cu_bulk.get_potential_energy() energies.append(e) volumes.append(cu_bulk.get_volume()) eos = EquationOfState(volumes, energies) v0, e0, B = eos.fit() aref = 3.6 vref = bulk("Cu", "fcc", a=aref).get_volume() copper_lattice_constant = (v0 / vref)**(1 / 3) * aref slab = fcc100("Cu", a=copper_lattice_constant, size=(2, 2, 3)) ads = molecule("C") add_adsorbate(slab, ads, 3, offset=(1, 1)) cons = FixAtoms(indices=[atom.index for atom in slab if (atom.tag == 3)]) slab.set_constraint(cons) slab.center(vacuum=13.0, axis=2) slab.set_pbc(True) slab.wrap(pbc=[True] * 3) slab.set_calculator(copy.copy(parent_calc)) slab.set_initial_magnetic_moments() images = [slab] Gs = { "default": { "G2": {
def test_fps_memory(): slab = fcc100("Cu", size=(3, 3, 3)) ads = molecule("CO") add_adsorbate(slab, ads, 4, offset=(1, 1)) cons = FixAtoms(indices=[ atom.index for atom in slab if (atom.tag == 2 or atom.tag == 3) ]) slab.set_constraint(cons) slab.center(vacuum=13.0, axis=2) slab.set_pbc(True) slab.wrap(pbc=[True] * 3) slab.set_calculator(EMT()) images = [slab] Gs = {} Gs["G2_etas"] = [2] Gs["G2_rs_s"] = [0] Gs["G4_etas"] = [0.005] Gs["G4_zetas"] = [1.0] Gs["G4_gammas"] = [1.0] Gs["cutoff"] = 6.5 elements = np.array([atom.symbol for atoms in images for atom in atoms]) _, idx = np.unique(elements, return_index=True) elements = list(elements[np.sort(idx)]) G = make_symmetry_functions(elements=elements, type="G2", etas=Gs["G2_etas"]) G += make_symmetry_functions( elements=elements, type="G4", etas=Gs["G4_etas"], zetas=Gs["G4_zetas"], gammas=Gs["G4_gammas"], ) G = {"O": G, "C": G, "Cu": G} snn_hashes = new_hash(images, Gs=Gs) base = AtomsDataset(images, SNN_Gaussian, Gs, forcetraining=True, label="test", cores=10) for idx in range(len(images)): s_nn_hash = list(snn_hashes.keys())[idx] # SimpleNN with open("amp-data-fingerprints.ampdb/loose/" + s_nn_hash, "rb") as f: simple_nn = load(f) os.system("rm amp-data-fingerprints.ampdb/loose/" + s_nn_hash) with open("amp-data-fingerprint-primes.ampdb/loose/" + s_nn_hash, "rb") as f: simple_nn_prime = load(f) os.system("rm amp-data-fingerprint-primes.ampdb/loose/" + s_nn_hash) test = TestDataset(images[idx], base.elements, base.base_descriptor, Gs, base.fprange, 'test2', cores=2) test_fp = test.fps test_prime = test.fp_primes key = simple_nn_prime.keys() for s, am in zip(simple_nn, test_fp): for i, j in zip(s[1], am[1]): assert abs(i - j) <= 1e-4, "Fingerprints do not match! %s, %s" % ( i, j) for idx in key: for s, am in zip(simple_nn_prime[idx], test_prime[idx]): assert abs(s - am) <= 1e-4, "Fingerprint primes do not match!"
# creates: o2pt100.png import numpy as np from ase.io import write from ase.build import fcc100, add_adsorbate # the metal slab atoms = fcc100('Pt', size=[4, 10, 3], vacuum=10) transmittances = [0 for a in atoms] bonded_atoms = [] upper_layer_idx = [a.index for a in atoms if a.tag == 1] middle = atoms.positions[upper_layer_idx, :2].max(axis=0) / 2 # the dissociating oxygen... fake some dissociation curve gas_dist = 1.1 max_height = 8. min_height = 1. max_dist = 6 # running index for the bonds index = len(atoms) for i, x in enumerate(np.linspace(0, 1.5, 6)): height = (max_height - min_height) * np.exp(-2 * x) + min_height d = np.exp(1.5 * x) / np.exp(1.5**2) * max_dist + gas_dist pos = middle + [0, d / 2] add_adsorbate(atoms, 'O', height=height, position=pos) pos = middle - [0, d / 2] add_adsorbate(atoms, 'O', height=height, position=pos) transmittances += [x / 2] * 2
def opt_fcc100(self) -> None: ''' Optimize fcc100 slab ''' slab = fcc100(self.symbol, self.repeats_surface, self.a, self.vacuum) self.prepare_slab_opt(slab)
def test_thermochemistry(): """Tests of the major methods (HarmonicThermo, IdealGasThermo, CrystalThermo) from the thermochemistry module.""" # Ideal gas thermo. atoms = Atoms('N2', positions=[(0, 0, 0), (0, 0, 1.1)], calculator=EMT()) QuasiNewton(atoms).run(fmax=0.01) energy = atoms.get_potential_energy() vib = Vibrations(atoms, name='idealgasthermo-vib') vib.run() vib_energies = vib.get_energies() thermo = IdealGasThermo(vib_energies=vib_energies, geometry='linear', atoms=atoms, symmetrynumber=2, spin=0, potentialenergy=energy) thermo.get_gibbs_energy(temperature=298.15, pressure=2 * 101325.) # Harmonic thermo. atoms = fcc100('Cu', (2, 2, 2), vacuum=10.) atoms.set_calculator(EMT()) add_adsorbate(atoms, 'Pt', 1.5, 'hollow') atoms.set_constraint( FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Cu'])) QuasiNewton(atoms).run(fmax=0.01) vib = Vibrations( atoms, name='harmonicthermo-vib', indices=[atom.index for atom in atoms if atom.symbol != 'Cu']) vib.run() vib.summary() vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, potentialenergy=atoms.get_potential_energy()) thermo.get_helmholtz_energy(temperature=298.15) # Crystal thermo. atoms = bulk('Al', 'fcc', a=4.05) calc = EMT() atoms.set_calculator(calc) energy = atoms.get_potential_energy() # Phonon calculator N = 7 ph = Phonons(atoms, calc, supercell=(N, N, N), delta=0.05) ph.run() ph.read(acoustic=True) phonon_energies, phonon_DOS = ph.dos(kpts=(4, 4, 4), npts=30, delta=5e-4) thermo = CrystalThermo(phonon_energies=phonon_energies, phonon_DOS=phonon_DOS, potentialenergy=energy, formula_units=4) thermo.get_helmholtz_energy(temperature=298.15) # Hindered translator / rotor. # (Taken directly from the example given in the documentation.) vibs = np.array([ 3049.060670, 3040.796863, 3001.661338, 2997.961647, 2866.153162, 2750.855460, 1436.792655, 1431.413595, 1415.952186, 1395.726300, 1358.412432, 1335.922737, 1167.009954, 1142.126116, 1013.918680, 803.400098, 783.026031, 310.448278, 136.112935, 112.939853, 103.926392, 77.262869, 60.278004, 25.825447 ]) vib_energies = vibs / 8065.54429 # Convert to eV from cm^-1. trans_barrier_energy = 0.049313 # eV rot_barrier_energy = 0.017675 # eV sitedensity = 1.5e15 # cm^-2 rotationalminima = 6 symmetrynumber = 1 mass = 30.07 # amu inertia = 73.149 # amu Ang^-2 thermo = HinderedThermo(vib_energies=vib_energies, trans_barrier_energy=trans_barrier_energy, rot_barrier_energy=rot_barrier_energy, sitedensity=sitedensity, rotationalminima=rotationalminima, symmetrynumber=symmetrynumber, mass=mass, inertia=inertia) helmholtz = thermo.get_helmholtz_energy(temperature=298.15) target = 1.593 # Taken from documentation example. assert (helmholtz - target) < 0.001
from ase.build import fcc100, add_adsorbate from ase.constraints import FixAtoms from ase.calculators.emt import EMT from ase.dimer import DimerControl, MinModeAtoms, MinModeTranslate # Set up a small "slab" with an adatoms atoms = fcc100('Pt', size = (2, 2, 1), vacuum = 10.0) add_adsorbate(atoms, 'Pt', 1.611, 'hollow') # Freeze the "slab" mask = [atom.tag > 0 for atom in atoms] atoms.set_constraint(FixAtoms(mask = mask)) # Calculate using EMT atoms.set_calculator(EMT()) relaxed_energy = atoms.get_potential_energy() # Set up the dimer d_control = DimerControl(initial_eigenmode_method = 'displacement', \ displacement_method = 'vector', logfile = None, \ mask = [0, 0, 0, 0, 1]) d_atoms = MinModeAtoms(atoms, d_control) # Displace the atoms displacement_vector = [[0.0]*3]*5 displacement_vector[-1][1] = -0.1 d_atoms.displace(displacement_vector = displacement_vector) # Converge to a saddle point dim_rlx = MinModeTranslate(d_atoms, trajectory = 'dimer_method.traj', \ logfile = None)
from ase.build import fcc100, add_adsorbate from ase.constraints import FixAtoms, FixedPlane from ase.calculators.emt import EMT from ase.optimize import QuasiNewton # 2x2-Al(001) surface with 3 layers and an # Au atom adsorbed in a hollow site: slab = fcc100('Al', size=(2, 2, 3)) add_adsorbate(slab, 'Au', 1.7, 'hollow') slab.center(axis=2, vacuum=4.0) # Make sure the structure is correct: #from ase.visualize import view #view(slab) # Fix second and third layers: mask = [atom.tag > 1 for atom in slab] #print(mask) fixlayers = FixAtoms(mask=mask) # Constrain the last atom (Au atom) to move only in the yz-plane: plane = FixedPlane(-1, (1, 0, 0)) slab.set_constraint([fixlayers, plane]) # Use EMT potential: slab.set_calculator(EMT()) for i in range(5): qn = QuasiNewton(slab, trajectory='mep%d.traj' % i) qn.run(fmax=0.05)
from ase.build import fcc100, add_adsorbate from ase.constraints import FixAtoms from ase.calculators.emt import EMT from ase.optimize import QuasiNewton # 2x2-Al(001) surface with 3 layers and an # Au atom adsorbed in a hollow site: slab = fcc100('Al', size=(2, 2, 3)) add_adsorbate(slab, 'Au', 1.7, 'hollow') slab.center(axis=2, vacuum=4.0) # Make sure the structure is correct: #view(slab) # Fix second and third layers: mask = [atom.tag > 1 for atom in slab] #print(mask) slab.set_constraint(FixAtoms(mask=mask)) # Use EMT potential: slab.set_calculator(EMT()) # Initial state: qn = QuasiNewton(slab, trajectory='initial.traj') qn.run(fmax=0.05) # Final state: slab[-1].x += slab.get_cell()[0, 0] / 2 qn = QuasiNewton(slab, trajectory='final.traj') qn.run(fmax=0.05)
[5.00000011, 6.30002353, 5.9163716], [5.88571848, 5.0122839, 6.82246859], [5.88625613, 5.01308931, 5.01214155], [7.14329342, 7.18115393, 6.81640316], [7.14551332, 7.17200869, 5.00879027], [8.41609966, 5.00661165, 5.02355167], [8.41971183, 5.0251482, 6.83462168], [9.69568096, 7.18645894, 6.8078633], [9.68914668, 7.16663649, 5.00000011], [10.95518898, 5.02163182, 6.8289018], [11.83752486, 6.29836826, 5.90274952], [10.94464142, 5.00000011, 5.01802495]]) systems.append((atoms, 'Pentane molecule')) # slab = fcc100('Cu', size=(2, 2, 2), vacuum=3.5) add_adsorbate(slab, 'C', 1.5, 'hollow') mask = [a.tag > 1 for a in slab] constraint = FixAtoms(mask=mask) slab.set_constraint(constraint) systems.append((slab, 'C/Cu(100)')) # surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)] esurf = [0.9151, 0.9771, 0.7953] # Surface energies size = 10 # number of atoms atoms = wulff_construction('Al', surfaces, esurf, size, 'fcc', rounding='above') atoms.center(vacuum=6) atoms.rattle(0.2) systems.append((atoms, 'Alumninum cluster'))
structure.cell = cell structure.append(Atom('H', (2.0, 2.0, 2.0))) species = [['Au', 'Pd'], ['H', 'V']] sizes = range(1, 5) correct_count = 1500 count_structures(structure, sizes, species, correct_count, tag) tag = 'HCP' structure = bulk('Au', crystalstructure='hcp', a=4.0) species = ['Au', 'Pd'] sizes = range(1, 6) correct_count = 984 count_structures(structure, sizes, species, correct_count, tag) tag = 'Surface' structure = fcc100('Au', (1, 1, 1), a=4.0, vacuum=2.0) species = ['Au', 'Pd'] sizes = range(1, 9) correct_count = 271 count_structures(structure, sizes, species, correct_count, tag) tag = 'Chain' structure = bulk('Au', a=4.0) structure.set_pbc((False, False, True)) species = ['Au', 'Pd'] sizes = range(1, 9) correct_count = 62 count_structures(structure, sizes, species, correct_count, tag) tag = 'FCC, concentration restricted' structure = bulk('Au', crystalstructure='fcc')
positions=[(0, 0, 0), (0, 0, 1.1)], calculator=EMT()) QuasiNewton(atoms).run(fmax=0.01) energy = atoms.get_potential_energy() vib = Vibrations(atoms, name='idealgasthermo-vib') vib.run() vib_energies = vib.get_energies() thermo = IdealGasThermo(vib_energies=vib_energies, geometry='linear', atoms=atoms, symmetrynumber=2, spin=0, potentialenergy=energy) thermo.get_gibbs_energy(temperature=298.15, pressure=2 * 101325.) # Harmonic thermo. atoms = fcc100('Cu', (2, 2, 2), vacuum=10.) atoms.set_calculator(EMT()) add_adsorbate(atoms, 'Pt', 1.5, 'hollow') atoms.set_constraint(FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Cu'])) QuasiNewton(atoms).run(fmax=0.01) vib = Vibrations(atoms, name='harmonicthermo-vib', indices=[atom.index for atom in atoms if atom.symbol != 'Cu']) vib.run() vib.summary() vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, potentialenergy=atoms.get_potential_energy()) thermo.get_helmholtz_energy(temperature=298.15)