Example #1
0
def sphericalFCC(elem, latticeconstant, nlayers):
    r""" Geneartes spherical cluster of atoms of FCC metal

    Parameters
    ----------
    elem: string
        symbol of chemical element.
    lattice constant: float
        lattice constant in Angstr.
    nlayers:
        number of atomic layers passed to FaceCenteredCubic. Guards the radius of cluster

    Returns
    -------
    ase.Atoms object

    Example
    --------
    >>> atoms = sphericalFCC('Ag', 4.09, 8)
    """
    # 1. generate cubical cluster
    surfaces = [(1, 0, 0)]
    layers = [nlayers]
    atoms = FaceCenteredCubic(elem, surfaces, layers, latticeconstant)
    atoms.center()
    # 2. cut al lextra atom from cube to make it spherical
    Xmin = atoms.positions[:, 0].min()
    Xmax = atoms.positions[:, 0].max()
    C =  (Xmin+Xmax)/2.0
    R = C
    ia = 0
    while ia < len(atoms):
        x2 = (atoms.positions[ia, 0] - C)**2
        y2 = (atoms.positions[ia, 1] - C)**2
        z2 = (atoms.positions[ia, 2] - C)**2
        if (x2 + y2 + z2) > R**2:
            del atoms[ia]
        else:
            ia += 1
    return atoms
Example #2
0
                dist = 0.001
            energy += self.A / dist**self.alpha
        return energy

    def __repr__(self):
        return 'Repulsion potential'

    def copy(self):
        return CentralRepulsion(self, R=self.R, A=self.A, alpha=self.alpha)


if __name__ == '__main__':
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.calculators.emt import EMT
    from ase.md.verlet import VelocityVerlet
    from ase.units import fs

    atoms = FaceCenteredCubic('Ag', [(1, 0, 0)], [1], 4.09)
    atoms.center(10)

    atoms.set_calculator(EMT())
    c = ConstantForce(10, [0, 1, 0])  # y=dircted force
    atoms.set_constraint(c)

    md = VelocityVerlet(atoms, 1*fs, trajectory='cf_test.traj',
                        logfile='-')
    md.run(100)

    # from ase.visualize import view
    # view(atoms)
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.
surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
layers = [3, 3, 3]
lc = 3.61000
atoms = FaceCenteredCubic('Cu',
                          surfaces,
                          layers,
                          latticeconstant=lc,
                          vacuum=10)
add_to_dict(AseAtomsAdaptor.get_structure(atoms), 'Cu_cluster')

from ase.cluster import Octahedron

atoms = Octahedron(['Pt', 'Au'], length=3, alloy=True, latticeconstant=4)
atoms.set_cell([15] * 3)
atoms.center()
add_to_dict(AseAtomsAdaptor.get_structure(atoms), 'PtAu_cluster')

from ase.cluster import Icosahedron

atoms = Icosahedron(
    'Pt',
    noshells=3,
)
atoms.set_cell([21] * 3)
atoms.center()
add_to_dict(AseAtomsAdaptor.get_structure(atoms), 'Pt_cluster')

surfaces = [(1, 0, 0), (1, 1, 1), (1, -1, 1)]
layers = [3, 1, -1]
atoms = FaceCenteredCubic('Au', surfaces, layers)
Example #5
0
                    atom.momentum[i] = abs(atom.momentum[i])
                if atom.position[i] > cell[i,i]-self.margin:
                    atom.momentum[i] = -abs(atom.momentum[i])

if __name__ == '__main__':
    # test
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.calculators.emt import EMT
    from ase.md.nvtberendsen import NVTBerendsen
    from ase.io.trajectory import Trajectory
    from ase.md.velocitydistribution import MaxwellBoltzmannDistribution, Stationary, ZeroRotation
    from ase.units import fs, kB

    atoms = FaceCenteredCubic(
      'Pt', [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [2, 3, 1], 4.09)
    atoms.center(vacuum=5)
    atoms.set_calculator( EMT() )
    T = 8000    # K -- try to vaporize
    MaxwellBoltzmannDistribution(atoms, kB*T)
    Stationary(atoms)
    ZeroRotation(atoms)
    #dyn = NVTBerendsen(atoms, 1*fs, T, 500*fs, logfile='-')
    dyn = NVTBerendsen(atoms, 1*fs, T, 500*fs, logfile='-')
    traj = Trajectory('borders_test.traj', 'w', atoms)
    dyn.attach(traj.write, interval=10)
    fr =  Freeze(atoms)
    #fr =  Mirror(atoms)
    dyn.attach( fr, interval=20 )
    dyn.run(5000)
    traj.close()