def cssm(metal, data_dict): # cleave_stable_surfaces_from_metals name = 'POSCAR_' + metal if metal in bcc: # For bcc metals, cleave 110 surface lattice_a = float(data_dict.get(metal)[2]) for i in range(1, 4): name_out = name + '_' + str(i) slab = bcc110(metal, a=lattice_a, size=(i, i, 4), vacuum=7.5) '''(i,i,4) means repeat i i 4 in x y and z directions. vacuum will be 7.5 * 2 because it was added on the two sides.''' constraint_l = FixAtoms(indices=[ atom.index for atom in slab if atom.index < i * i * 2 ]) slab.set_constraint(constraint_l) ase.io.write(name_out, slab, format='vasp') ### Add the element line to the POSCAR file ### subprocess.call(['sed -i ' + '\'5a' + metal + '\' ' + name_out], shell=True) bottom(name_out) elif metal in hcp: # For hcp metals, cleave 0001 surface lattice_a, lattice_c = [float(i) for i in data_dict.get(metal)[2:]] for i in range(1, 4): name_out = name + '_' + str(i) slab = hcp0001(metal, a=lattice_a, c=lattice_c, size=(i, i, 4), vacuum=7.5) constraint_l = FixAtoms(indices=[ atom.index for atom in slab if atom.index < i * i * 2 ]) slab.set_constraint(constraint_l) ase.io.write(name_out, slab, format='vasp') subprocess.call(['sed -i ' + '\'5a' + metal + '\' ' + name_out], shell=True) bottom(name_out) elif metal in fcc: # For fcc metals, cleave 111 surface lattice_a = float(data_dict.get(metal)[2]) for i in range(1, 4): name_out = name + '_' + str(i) slab = fcc111(metal, a=lattice_a, size=(i, i, 4), vacuum=7.5) # slab.center(vacuum=7.5, axis = 2) constraint_l = FixAtoms(indices=[ atom.index for atom in slab if atom.index < i * i * 2 ]) slab.set_constraint(constraint_l) ase.io.write(name_out, slab, format='vasp') subprocess.call(['sed -i ' + '\'5a' + metal + '\' ' + name_out], shell=True) bottom(name_out) else: print( 'Please add your element in the crystal structure lists: bcc, hcp, and fcc' )
def hcp0001_root(symbol, root, size, a=None, c=None, vacuum=None, orthogonal=False): """HCP(0001) surface maniupulated to have a x unit side length of *root* before repeating. This also results in *root* number of repetitions of the cell. The first 20 valid roots for nonorthogonal are... 1, 3, 4, 7, 9, 12, 13, 16, 19, 21, 25, 27, 28, 31, 36, 37, 39, 43, 48, 49""" atoms = hcp0001(symbol=symbol, size=(1, 1, size[2]), a=a, c=c, vacuum=vacuum, orthogonal=orthogonal) atoms = root_surface(atoms, root) atoms *= (size[0], size[1], 1) return atoms
def test_root_surf(): from ase.build import fcc111 from ase.build import bcc111 from ase.build import hcp0001 from ase.build import fcc111_root from ase.build import root_surface from ase.build import root_surface_analysis # Make samples of primitive cell prim_fcc111 = fcc111("H", (1, 1, 2), a=1) prim_bcc111 = bcc111("H", (1, 1, 2), a=1) prim_hcp0001 = hcp0001("H", (1, 1, 2), a=1) # Check valid roots up to root 21 (the 10th root cell) valid_fcc111 = root_surface_analysis(prim_fcc111, 21) valid_bcc111 = root_surface_analysis(prim_bcc111, 21) valid_hcp0001 = root_surface_analysis(prim_hcp0001, 21) # These should have different positions, but the same # cell geometry. assert valid_fcc111 == valid_bcc111 == valid_hcp0001 # Make an easy sample to check code errors atoms1 = root_surface(prim_fcc111, 7) # Ensure the valid roots are the roots are valid against # a set of manually checked roots for this system assert valid_fcc111 == [1.0, 3.0, 4.0, 7.0, 9.0, 12.0, 13.0, 16.0, 19.0, 21.0] # Remake easy sample using surface function atoms2 = fcc111_root("H", 7, (1, 1, 2), a=1) # Right number of atoms assert len(atoms1) == len(atoms2) == 14 # Same positions assert (atoms1.positions == atoms2.positions).all() # Same cell assert (atoms1.cell == atoms2.cell).all()
def hcp0001_root(symbol, root, size, a=None, c=None, vacuum=None, orthogonal=False): """HCP(0001) surface maniupulated to have a x unit side length of *root* before repeating.This also results in *root* number of repetitions of the cell. The first 20 valid roots for nonorthogonal are... 1, 3, 4, 7, 9, 12, 13, 16, 19, 21, 25, 27, 28, 31, 36, 37, 39, 43, 48, 49""" atoms = hcp0001(symbol=symbol, size=(1, 1, size[2]), a=a, c=c, vacuum=vacuum, orthogonal=orthogonal) atoms = root_surface(atoms, root) atoms *= (size[0], size[1], 1) return atoms
# Vacuum and hcp lattice parameter for graphene d = 4.0 a = 2.4437 calc = GPAW(h=0.15, xc='LDA', nbands=-4, txt='-', basis='dzp', convergence={ 'energy': 1e-5, 'density': 1e-5 }) # Calculate potential energy per atom for orthogonal unitcell atoms = hcp0001('C', a=a / sqrt(3), vacuum=d, size=(3, 2, 1), orthogonal=True) del atoms[[1, -1]] atoms.center(axis=0) atoms.set_calculator(calc) kpts_c = np.ceil(50 / np.sum(atoms.get_cell()**2, axis=1)**0.5).astype(int) kpts_c[~atoms.get_pbc()] = 1 calc.set(kpts=kpts_c) eppa1 = atoms.get_potential_energy() / len(atoms) F1_av = atoms.get_forces() equal(np.abs(F1_av).max(), 0, 5e-3) # Redo calculation with non-orthogonal unitcell atoms = Atoms(symbols='C2', pbc=(True, True, False), positions=[(a / 2, -sqrt(3) / 6 * a, d),
from ase.io import Trajectory from ase.optimize.mdmin import MDMin try: from asap3 import EMT except ImportError: pass else: a = 3.6 b = a / 2 cu = Atoms('Cu', cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=1) * (6, 6, 6) cu.set_calculator(EMT()) f = UnitCellFilter(cu, [1, 1, 1, 0, 0, 0]) opt = LBFGS(f) t = Trajectory('Cu-fcc.traj', 'w', cu) opt.attach(t) opt.run(5.0) # HCP: from ase.build import hcp0001 cu = hcp0001('Cu', (1, 1, 2), a=a / sqrt(2)) cu.cell[1, 0] += 0.05 cu *= (6, 6, 3) cu.set_calculator(EMT()) print(cu.get_forces()) print(cu.get_stress()) f = UnitCellFilter(cu) opt = MDMin(f, dt=0.01) t = Trajectory('Cu-hcp.traj', 'w', cu) opt.attach(t) opt.run(0.2)
def opt_hcp0001(self, c: int = None) -> None: ''' Optimize hcp0001 slab ''' slab = hcp0001(self.symbol, self.repeats_surface, self.a, c, self.vacuum) self.prepare_slab_opt(slab)
try: from asap3 import EMT except ImportError: pass else: a = 3.6 b = a / 2 cu = Atoms('Cu', cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=1) * (6, 6, 6) cu.set_calculator(EMT()) f = UnitCellFilter(cu, [1, 1, 1, 0, 0, 0]) opt = LBFGS(f) t = Trajectory('Cu-fcc.traj', 'w', cu) opt.attach(t) opt.run(5.0) # HCP: from ase.build import hcp0001 cu = hcp0001('Cu', (1, 1, 2), a=a / sqrt(2)) cu.cell[1, 0] += 0.05 cu *= (6, 6, 3) cu.set_calculator(EMT()) print(cu.get_forces()) print(cu.get_stress()) f = UnitCellFilter(cu) opt = MDMin(f, dt=0.01) t = Trajectory('Cu-hcp.traj', 'w', cu) opt.attach(t) opt.run(0.2)
from ase.build import fcc111 from ase.build import bcc111 from ase.build import hcp0001 from ase.build import fcc111_root from ase.build import root_surface from ase.build import root_surface_analysis # Make samples of primitive cell prim_fcc111 = fcc111("H", (1, 1, 2), a=1) prim_bcc111 = bcc111("H", (1, 1, 2), a=1) prim_hcp0001 = hcp0001("H", (1, 1, 2), a=1) # Check valid roots up to root 21 (the 10th root cell) valid_fcc111 = root_surface_analysis(prim_fcc111, 21) valid_bcc111 = root_surface_analysis(prim_bcc111, 21) valid_hcp0001 = root_surface_analysis(prim_hcp0001, 21) # These should have different positions, but the same # cell geometry. assert valid_fcc111 == valid_bcc111 == valid_hcp0001 # Make an easy sample to check code errors atoms1 = root_surface(prim_fcc111, 7) # Ensure the valid roots are the roots are valid against # a set of manually checked roots for this system assert valid_fcc111 == [1.0, 3.0, 4.0, 7.0, 9.0, 12.0, 13.0, 16.0, 19.0, 21.0] # Remake easy sample using surface function atoms2 = fcc111_root("H", 7, (1, 1, 2), a=1)
from __future__ import print_function import numpy as np from ase.dft.kpoints import monkhorst_pack from ase.parallel import paropen from ase.build import hcp0001, add_adsorbate from gpaw import GPAW, PW, FermiDirac, MixerSum from gpaw.xc.exx import EXX kpts = monkhorst_pack((16, 16, 1)) kpts += np.array([1 / 32., 1 / 32., 0]) a = 2.51 # Lattice parameter of Co slab = hcp0001('Co', a=a, c=4.07, size=(1, 1, 4)) pos = slab.get_positions() cell = slab.get_cell() cell[2, 2] = 20. + pos[-1, 2] slab.set_cell(cell) slab.set_initial_magnetic_moments([0.7, 0.7, 0.7, 0.7]) ds = [1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 5.0, 6.0, 10.0] for d in ds: pos = slab.get_positions() add_adsorbate(slab, 'C', d, position=(pos[3, 0], pos[3, 1])) add_adsorbate(slab, 'C', d, position=(cell[0, 0] / 3 + cell[1, 0] / 3, cell[0, 1] / 3 + cell[1, 1] / 3)) #view(slab) calc = GPAW(xc='PBE',
def create_bimetal_surf(composition, surf_type, size, latt='', fix_layers=2, vacuum=12.0): """ Create bimetallic surfaces composition: "M1" - single-metal "M1,M2,config" - bimetallic surface with the configuration latt: """ # extract lattice constants if available if latt == '': if composition == 'Co' and surf_type == 'fcc111': a_latt = 3.544 else: a_latt, c_latt = (None, None) else: tmp = latt.split(',') a_latt = float(tmp[0]) if len(tmp) > 1: c_latt = float(tmp[1]) # set the surface type name if surf_type == 'fcc111': surf_type_name = '111' elif surf_type == 'hcp0001': surf_type_name = '0001' # set the metallic surface slab tmp = composition.split(',') M1_name = tmp[0] if len(tmp) > 1: M2_name = tmp[1] config = tmp[2] else: M2_name = '' config = '' if not "bulk" in config: if surf_type == 'fcc111': surf = fcc111(M1_name, a=a_latt, size=size, vacuum=vacuum) elif surf_type == 'hcp0001': surf = hcp0001(M1_name, a=a_latt, c=c_latt, size=size, vacuum=vacuum) surf_name = M1_name + surf_type_name + "-%d%d%d" % (size[0], size[1], size[2]) if M2_name != '': surf_name = surf_name + '-' + config_bimetal + "-" + M2_name if config_bimetal == 'top': for atom in surf: if atom.tag == 1: atom.symbol = M2_name elif config_bimetal == 'sub': for atom in surf: if atom.tag == 2: atom.symbol = M2_name elif config_bimetal == 'mixtop': for atom in surf: if atom.tag == 1: atom.symbol = M2_name break elif config_bimetal == 'mixsub': for atom in surf: if atom.tag == 2: atom.symbol = M2_name break else: print "ERROR: Not implemented yet for the bimetal configuration %s!!!" % ( config) # get the number of layers nlayers = max(surf.get_tags()) mask = [atom.tag > nlayers - fix_layers for atom in surf] constr_bottom = FixAtoms(mask=mask) surf.set_constraint([constr_bottom]) return surf, surf_name
def build(): p = OptionParser(usage='%prog [options] [ads@]surf [output file]', version='%prog 0.1', description='Example ads/surf: fcc-CO@2x2Ru0001') p.add_option('-l', '--layers', type='int', default=4, help='Number of layers.') p.add_option('-v', '--vacuum', type='float', default=5.0, help='Vacuum.') p.add_option('-x', '--crystal-structure', help='Crystal structure.', choices=['sc', 'fcc', 'bcc', 'hcp']) p.add_option('-a', '--lattice-constant', type='float', help='Lattice constant in Angstrom.') p.add_option('--c-over-a', type='float', help='c/a ratio.') p.add_option('--height', type='float', help='Height of adsorbate over surface.') p.add_option('--distance', type='float', help='Distance between adsorbate and nearest surface atoms.') p.add_option('-M', '--magnetic-moment', type='float', default=0.0, help='Magnetic moment.') p.add_option('-G', '--gui', action='store_true', help="Pop up ASE's GUI.") p.add_option('-P', '--python', action='store_true', help="Write Python script.") opt, args = p.parse_args() if not 1 <= len(args) <= 2: p.error("incorrect number of arguments") if '@' in args[0]: ads, surf = args[0].split('@') else: ads = None surf = args[0] if surf[0].isdigit(): i1 = surf.index('x') n = int(surf[:i1]) i2 = i1 + 1 while surf[i2].isdigit(): i2 += 1 m = int(surf[i1 + 1:i2]) surf = surf[i2:] else: n = 1 m = 1 if surf[-1].isdigit(): if surf[1].isdigit(): face = surf[1:] surf = surf[0] else: face = surf[2:] surf = surf[:2] else: face = None Z = atomic_numbers[surf] state = reference_states[Z] if opt.crystal_structure: x = opt.crystal_structure else: x = state['symmetry'] if opt.lattice_constant: a = opt.lattice_constant else: a = estimate_lattice_constant(surf, x, opt.c_over_a) script = ['from ase.build import ', 'vac = %r' % opt.vacuum, 'a = %r' % a] if x == 'fcc': if face is None: face = '111' slab = fcc111(surf, (n, m, opt.layers), a, opt.vacuum) script[0] += 'fcc111' script += ['slab = fcc111(%r, (%d, %d, %d), a, vac)' % (surf, n, m, opt.layers)] r = a / np.sqrt(2) / 2 elif x == 'bcc': if face is None: face = '110' if face == '110': slab = bcc110(surf, (n, m, opt.layers), a, opt.vacuum) elif face == '100': slab = bcc100(surf, (n, m, opt.layers), a, opt.vacuum) script[0] += 'bcc' + face script += ['slab = bcc%s(%r, (%d, %d, %d), a, vac)' % (face, surf, n, m, opt.layers)] r = a * np.sqrt(3) / 4 elif x == 'hcp': if face is None: face = '0001' if opt.c_over_a is None: c = np.sqrt(8 / 3.0) * a else: c = opt.c_over_a * a slab = hcp0001(surf, (n, m, opt.layers), a, c, opt.vacuum) script[0] += 'hcp0001' script += ['c = %r * a' % (c / a), 'slab = hcp0001(%r, (%d, %d, %d), a, c, vac)' % (surf, n, m, opt.layers)] r = a / 2 elif x == 'diamond': if face is None: face = '111' slab = diamond111(surf, (n, m, opt.layers), a, opt.vacuum) script[0] += 'diamond111' script += ['slab = diamond111(%r, (%d, %d, %d), a, vac)' % (surf, n, m, opt.layers)] r = a * np.sqrt(3) / 8 else: raise NotImplementedError magmom = opt.magnetic_moment if magmom is None: magmom = {'Ni': 0.6, 'Co': 1.2, 'Fe': 2.3}.get(surf, 0.0) slab.set_initial_magnetic_moments([magmom] * len(slab)) if magmom != 0: script += ['slab.set_initial_magnetic_moments([%r] * len(slab))' % magmom] slab.pbc = 1 script += ['slab.pbc = True'] name = '%dx%d%s%s' % (n, m, surf, face) if ads: site = 'ontop' if '-' in ads: site, ads = ads.split('-') name = site + '-' + ads + '@' + name symbols = string2symbols(ads) nads = len(symbols) if nads == 1: script[:0] = ['from ase import Atoms'] script += ['ads = Atoms(%r)' % ads] ads = Atoms(ads) else: script[:0] = ['from ase.build import molecule'] script += ['ads = molecule(%r)' % ads] ads = molecule(ads) add_adsorbate(slab, ads, 0.0, site) d = opt.distance if d is None: d = r + covalent_radii[ads[0].number] / 2 h = opt.height if h is None: R = slab.positions y = ((R[:-nads] - R[-nads])**2).sum(1).min()**0.5 h = (d**2 - y**2)**0.5 else: assert opt.distance is None slab.positions[-nads:, 2] += h script[1] += ', add_adsorbate' script += ['add_adsorbate(slab, ads, %r, %r)' % (h, site)] if len(args) == 2: write(args[1], slab) script[1:1] = ['from ase.io import write'] script += ['write(%r, slab)' % args[1]] elif not opt.gui: write(name + '.traj', slab) script[1:1] = ['from ase.io import write'] script += ['write(%r, slab)' % (name + '.traj')] if opt.gui: view(slab) script[1:1] = ['from ase.visualize import view'] script += ['view(slab)'] if opt.python: print('\n'.join(script))
def adsorb(db, height=1.2, nlayers=3, nkpts=7, ecut=400): """Adsorb nitrogen in hcp-site on Ru(0001) surface. Do calculations for N/Ru(0001), Ru(0001) and a nitrogen atom if they have not already been done. db: Database Database for collecting results. height: float Height of N-atom above top Ru-layer. nlayers: int Number of Ru-layers. nkpts: int Use a (nkpts * nkpts) Monkhorst-Pack grid that includes the Gamma point. ecut: float Cutoff energy for plane waves. Returns height. """ name = f'Ru{nlayers}-{nkpts}x{nkpts}-{ecut:.0f}' parameters = dict(mode=PW(ecut), eigensolver=Davidson(niter=2), poissonsolver={'dipolelayer': 'xy'}, kpts={'size': (nkpts, nkpts, 1), 'gamma': True}, xc='PBE') # N/Ru(0001): slab = hcp0001('Ru', a=a, c=c, size=(1, 1, nlayers)) z = slab.positions[:, 2].max() + height x, y = np.dot([2 / 3, 2 / 3], slab.cell[:2, :2]) slab.append('N') slab.positions[-1] = [x, y, z] slab.center(vacuum=vacuum, axis=2) # 2: z-axis # Fix first nlayer atoms: slab.constraints = FixAtoms(indices=list(range(nlayers))) id = db.reserve(name=f'N/{nlayers}Ru(0001)', nkpts=nkpts, ecut=ecut) if id is not None: # skip calculation if already done slab.calc = GPAW(txt='N' + name + '.txt', **parameters) optimizer = BFGSLineSearch(slab, logfile='N' + name + '.opt') optimizer.run(fmax=0.01) height = slab.positions[-1, 2] - slab.positions[:-1, 2].max() del db[id] db.write(slab, name=f'N/{nlayers}Ru(0001)', nkpts=nkpts, ecut=ecut, height=height) # Clean surface (single point calculation): id = db.reserve(name=f'{nlayers}Ru(0001)', nkpts=nkpts, ecut=ecut) if id is not None: del slab[-1] # remove nitrogen atom slab.calc = GPAW(txt=name + '.txt', **parameters) slab.get_forces() del db[id] db.write(slab, name=f'{nlayers}Ru(0001)', nkpts=nkpts, ecut=ecut) # Nitrogen atom: id = db.reserve(name='N-atom', ecut=ecut) if id is not None: # Create spin-polarized nitrogen atom: molecule = Atoms('N', magmoms=[3]) molecule.center(vacuum=4.0) # Remove parameters that make no sense for an isolated atom: del parameters['kpts'] del parameters['poissonsolver'] # Calculate energy: molecule.calc = GPAW(txt=name + '.txt', **parameters) molecule.get_potential_energy() del db[id] db.write(molecule, name='N-atom', ecut=ecut) return height
from sys import argv from ase.build import hcp0001, add_adsorbate from ase.constraints import FixAtoms from ase.optimize.lbfgs import LBFGS from gpaw import GPAW, Mixer, FermiDirac tag = 'Ru001_Ru8' adsorbate_heights = {'H': 1.0, 'N': 1.108, 'O': 1.257} slab = hcp0001('Ru', size=(2, 2, 4), a=2.72, c=1.58 * 2.72, vacuum=7.0, orthogonal=True) slab.center(axis=2) if len(argv) > 1: adsorbate = argv[1] tag = adsorbate + tag add_adsorbate(slab, adsorbate, adsorbate_heights[adsorbate], 'hcp') slab.set_constraint(FixAtoms(mask=slab.get_tags() >= 3)) calc = GPAW(xc='PBE', h=0.2, mixer=Mixer(0.1, 5, weight=100.0), occupations=FermiDirac(width=0.1), kpts=[4, 4, 1], setups={'Ru': '8'},