def bulk_with_nano_particle(self):
        atoms = bulk("Al", a=4.05) * (10, 10, 10)
        surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
        layers = [4, 7, 3]
        lc = 4.05
        nano_part = FaceCenteredCubic('Mg',
                                      surfaces,
                                      layers,
                                      latticeconstant=lc)

        nano_part_pos = nano_part.get_positions()
        com = np.mean(nano_part_pos, axis=0)
        nano_part_pos -= com

        cell = atoms.get_cell()
        diag = 0.5 * np.sum(cell, axis=0)

        atoms_pos = atoms.get_positions() - diag

        # Insert the nano particles
        for i in range(nano_part_pos.shape[0]):
            diff = atoms_pos - nano_part_pos[i, :]
            lengths = np.sum(diff**2, axis=1)
            indx = np.argmin(lengths)
            atoms[indx].symbol = nano_part[i].symbol
        return atoms
Exemplo n.º 2
0
def get_cubic_nano_particle(layer=0):
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.geometry import get_layers
    surfaces = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
    layers = cubic_np_layer[layer]
    lc = 4.05
    atoms = FaceCenteredCubic('Si', surfaces, layers, latticeconstant=lc)
    tags, array = get_layers(atoms, (1, 0, 0))
    for t, atom in zip(tags, atoms):
        if t % 2 == 0:
            atom.symbol = "Mg"
    print(atoms.get_chemical_formula())
    return atoms
Exemplo n.º 3
0
def get_nanoparticle():
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.geometry import get_layers
    surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
    layers = [10, 13, 9]
    lc = 4.05
    atoms = FaceCenteredCubic('Si', surfaces, layers, latticeconstant=lc)
    tags, array = get_layers(atoms, (1, 0, 0))
    for t, atom in zip(tags, atoms):
        if t % 2 == 0:
            atom.symbol = "Mg"
    print(atoms.get_chemical_formula())
    atoms.rotate(90, 'z', rotate_cell=True)
    return atoms
Exemplo n.º 4
0
 def _spherical_nano_particle_matches(self, conc_init):
     # Construct a spherical nano particle
     from ase.cluster.cubic import FaceCenteredCubic
     from random import choice
     surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
     layers = [6, 9, 5]
     lc = 4.05
     nanoparticle = FaceCenteredCubic('Al', surfaces, layers,
                                      latticeconstant=lc)
     nanoparticle.info = {"lc": lc}
     symbs = ["Mg", "Si"]
     for atom in nanoparticle:
         atom.symbol = choice(symbs)
     inert1, inert2 = self._nano_particle_matches(conc_init, nanoparticle)
     match, msg = self._two_covariance_tensors_matches(inert1, inert2)
     return match, msg
Exemplo n.º 5
0
def test_xrdebye():
    tolerance = 1E-5
    # previously calculated values
    expected_get = 116850.37344
    expected_xrd = np.array([18549.274677, 52303.116995, 38502.372027])
    expected_saxs = np.array([372650934.006398, 280252013.563702,
                              488123.103628])

    # test system -- cluster of 587 silver atoms
    atoms = FaceCenteredCubic('Ag', [(1, 0, 0), (1, 1, 0), (1, 1, 1)],
                              [6, 8, 8], 4.09)
    xrd = XrDebye(atoms=atoms, wavelength=wavelengths['CuKa1'], damping=0.04,
                  method='Iwasa', alpha=1.01, warn=True)
    # test get()
    obtained_get = xrd.get(s=0.09)
    assert np.abs((obtained_get - expected_get) / expected_get) < tolerance

    # test XRD
    obtained_xrd = xrd.calc_pattern(x=np.array([15, 30, 50]), mode='XRD')
    assert np.allclose(obtained_xrd, expected_xrd, rtol=tolerance)

    # test SAXS
    obtained_saxs = xrd.calc_pattern(x=np.array([0.021, 0.09, 0.53]),
                                     mode='SAXS')
    assert np.allclose(obtained_saxs, expected_saxs, rtol=tolerance)
Exemplo n.º 6
0
def test_turbomole_au13():
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.calculators.turbomole import Turbomole

    surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
    layers = [1, 2, 1]
    atoms = FaceCenteredCubic('Au', surfaces, layers, latticeconstant=4.08)

    params = {
        'title': 'Au13-',
        'task': 'energy',
        'basis set name': 'def2-SV(P)',
        'total charge': -1,
        'multiplicity': 1,
        'use dft': True,
        'density functional': 'pbe',
        'use resolution of identity': True,
        'ri memory': 1000,
        'use fermi smearing': True,
        'fermi initial temperature': 500,
        'fermi final temperature': 100,
        'fermi annealing factor': 0.9,
        'fermi h**o-lumo gap criterion': 0.09,
        'fermi stopping criterion': 0.002,
        'scf energy convergence': 1.e-4,
        'scf iterations': 250
    }

    calc = Turbomole(**params)
    atoms.calc = calc
    calc.calculate(atoms)

    # use the get_property() method
    print(calc.get_property('energy'))
    print(calc.get_property('dipole'))

    # test restart

    params = {'task': 'gradient', 'scf energy convergence': 1.e-6}

    calc = Turbomole(restart=True, **params)
    assert calc.converged
    calc.calculate()

    print(calc.get_property('energy'))
    print(calc.get_property('forces'))
    print(calc.get_property('dipole'))
Exemplo n.º 7
0
    def build_system(self, name):
        if self.bond_length is None:
            b = 2 * covalent_radii[atomic_numbers[name]]
        else:
            b = self.bond_length

        return FaceCenteredCubic(name, [(1, 0, 0)], [1],
                                 latticeconstant=b * sqrt(2))
Exemplo n.º 8
0
def Octahedron(symbol, length, cutoff=0, latticeconstant=None, alloy=False):
    """
    Returns Face Centered Cubic clusters of the octahedral class depending
    on the choice of cutoff.

    ============================    =======================
    Type                            Condition
    ============================    =======================
    Regular octahedron              cutoff = 0
    Truncated octahedron            cutoff > 0
    Regular truncated octahedron    length = 3 * cutoff + 1
    Cuboctahedron                   length = 2 * cutoff + 1
    ============================    =======================


    Parameters:

    symbol: string or sequence of int
        The chemical symbol or atomic number of the element(s).

    length: int
        Number of atoms on the square edges of the complete octahedron.

    cutoff (optional): int
        Number of layers cut at each vertex.

    latticeconstant (optional): float
        The lattice constant. If not given,
        then it is extracted form ase.data.

    alloy (optional): bool
        If true the L1_2 structure is used. Default is False.

    """

    # Check length and cutoff
    if length < 2:
        raise ValueError("The length must be greater than one.")

    if cutoff < 0 or length < 2 * cutoff + 1:
        raise ValueError(
            "The cutoff must fulfill: > 0 and <= (length - 1) / 2.")

    # Create cluster
    surfaces = [(1, 1, 1), (1, 0, 0)]
    if length % 2 == 0:
        center = np.array([0.5, 0.5, 0.5])
        layers = [length / 2, length - 1 - cutoff]
    else:
        center = np.array([0.0, 0.0, 0.0])
        layers = [(length - 1) / 2, length - 1 - cutoff]

    if not alloy:
        return FaceCenteredCubic(symbol, surfaces, layers, latticeconstant,
                                 center)
    else:
        return L1_2(symbol, surfaces, layers, latticeconstant, center)
Exemplo n.º 9
0
def xrd():
    # test system -- cluster of 587 silver atoms
    atoms = FaceCenteredCubic('Ag', [(1, 0, 0), (1, 1, 0), (1, 1, 1)],
                              [6, 8, 8], 4.09)
    return XrDebye(atoms=atoms,
                   wavelength=wavelengths['CuKa1'],
                   damping=0.04,
                   method='Iwasa',
                   alpha=1.01,
                   warn=True)
def test_model_cell(angle=0, extra_space=2):
    surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
    layers = [3, 4, 5]
    lc = 3.61000
    cell = FaceCenteredCubic('Cu', surfaces, layers, latticeconstant=lc)
    cell[46].number = 50

    m = create_model(cell, extra_space=extra_space, pbc=False)
    m2 = rotate_model(m, cell, angle)
    return m2, cell
Exemplo n.º 11
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
Exemplo n.º 12
0
def read_and_do_montecarlo(filename):
    d = SurfaceMonteCarloData()
    d.read(filename)
    print "Starting "+str(len(d))+" sims."
    surfaces = data.fcc.surface_names
            
    #for n in range(0,len(d)):
    for n in range(world.rank,len(d),world.size):
        file = outdir+"/a%05i.amc.gz" % n
        if not os.path.exists(file):
            layers = d[n][1]  # Really d[n]["layers"]
            atoms = FaceCenteredCubic(d.atomic_number,
            surfaces, layers,latticeconstant=d.lattice_constant)
            resizecluster(atoms, d.fitsize)
            print "Resized number of atoms:", len(atoms)
            do_monte_carlo(atoms,n,outdir)
    world.barrier()#Let the cpu's wait until all in same state.
Exemplo n.º 13
0
def read_and_do_montecarlo(filename, use_gas):
    d = SurfaceMonteCarloData()
    d.read(filename)
    print "Starting " + str(len(d)) + " sims."
    surfaces = data.fcc.surface_names
    #Only one worker should create the filename
    outdir = determine_create_dirname(filename)

    #for n in range(0,len(d)):
    for n in range(world.rank, len(d), world.size):
        layers = d[n][1]  # Really d[n]["layers"]
        multiplicity = d.get_multiplicity(n)
        atoms = FaceCenteredCubic(d.atomic_number,
                                  surfaces,
                                  layers,
                                  latticeconstant=d.lattice_constant)
        print "Number of atoms:", len(atoms)
        resizecluster(atoms, d.fitsize)
        print "Resized number of atoms:", len(atoms)
        do_monte_carlo(atoms, n, outdir, use_gas, multiplicity)
    world.barrier()  #Let the cpu's wait until all in same state.
Exemplo n.º 14
0
from ase.md.verlet import VelocityVerlet
from ase import units

# Use Asap for a huge performance increase if it is installed
use_asap = True

if use_asap:
    from asap3 import EMT
    size = 4
else:
    from ase.calculators.emt import EMT
    size = 2

# Set up a nanoparticle
atoms = FaceCenteredCubic('Cu',
                          surfaces=[[1, 0, 0], [1, 1, 0], [1, 1, 1]],
                          layers=(size, size, size),
                          vacuum=4)

# Describe the interatomic interactions with the Effective Medium Theory
atoms.set_calculator(EMT())

# Do a quick relaxation of the cluster
qn = QuasiNewton(atoms)
qn.run(0.001, 10)

# Set the momenta corresponding to T=1200K
MaxwellBoltzmannDistribution(atoms, 1200 * units.kB)
Stationary(atoms)  # zero linear momentum
ZeroRotation(atoms)  # zero angular momentum

# We want to run MD using the VelocityVerlet algorithm.
Exemplo n.º 15
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)
Exemplo n.º 16
0
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.
Exemplo n.º 17
0
def create_fcc_cluster_atoms(layers):
    surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
    # layers = [4, 4, 4]
    lc = 3.61000
    cluster = FaceCenteredCubic("Cu", surfaces, layers, latticeconstant=lc)
    return Atoms(cluster.symbols, cluster.positions, cell=cluster.cell)
Exemplo n.º 18
0
atoms.set_cell([10, 10, 10])
atoms.center()

struct = AseAtomsAdaptor.get_structure(atoms)
add_to_dict(struct, 'I2')

# nano-particles
import ase
from ase.cluster.cubic import FaceCenteredCubic

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',
Exemplo n.º 19
0
    #    pass

    def __repr__(self):
        return 'Push atoms out of the cell back to the cell'

    def copy(self):
        return ConstantForce(a=self.index, force=self.force)

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
    from constantforce import ConstantForce

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

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

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

    #~ from ase.visualize import view
    #~ view(atoms)
Exemplo n.º 20
0
"""generates atoms object from .smc file and views in ase gui
usage: python viewCluster.py smcfile
where smcfile is the full path to .smc file
"""
import sys
from montecarlo import *
from ase.cluster.cubic import FaceCenteredCubic
import ase
from data import fcc
from atommontecarlodata import AtomMonteCarloData
from ase.visualize import view

if len(sys.argv) < 2:
	print >>sys.stderr, __doc__
    	sys.exit(1)
#Store path to file
pp = str(sys.argv[1])
nconf= int(sys.argv[2])
#Instantiate d as SurfaceM.. object
d = SurfaceMonteCarloData()
#Read data from file to d
d.read(pp)
surfaces = fcc.surface_names
#Construct atoms
atoms = FaceCenteredCubic('Au', surfaces, d[nconf][1], latticeconstant=4.055)
view(atoms) #View atoms
#sys.exit("Script Terminated with code 0")
Exemplo n.º 21
0
from ase.cluster.cubic import FaceCenteredCubic
from ElectroLens import view

surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
layers = [6, 9, 5]
lc = 3.61000
atoms = FaceCenteredCubic('Cu', surfaces, layers, latticeconstant=lc)

view(atoms)
Exemplo n.º 22
0
"""Tests for XrDebye class"""
from __future__ import print_function

from ase.utils.xrdebye import XrDebye, wavelengths
from ase.cluster.cubic import FaceCenteredCubic
import numpy as np

tolerance = 1E-5
# previously calculated values
expected_get = 116850.37344
expected_xrd = np.array([18549.274677, 52303.116995, 38502.372027])
expected_saxs = np.array([372650934.006398, 280252013.563702, 488123.103628])

# test system -- cluster of 587 silver atoms
atoms = FaceCenteredCubic('Ag', [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [6, 8, 8],
                          4.09)
xrd = XrDebye(atoms=atoms,
              wavelength=wavelengths['CuKa1'],
              damping=0.04,
              method='Iwasa',
              alpha=1.01,
              warn=True)
# test get()
obtained_get = xrd.get(s=0.09)
assert np.abs((obtained_get - expected_get) / expected_get) < tolerance

# test XRD
obtained_xrd = xrd.calc_pattern(x=np.array([15, 30, 50]), mode='XRD')
assert np.allclose(obtained_xrd, expected_xrd, rtol=tolerance)

# test SAXS
Exemplo n.º 23
0
    s_new = struc.copy()
    s_new.rotate(angle, axis)
    pbc = Atoms(s_new.symbols.numbers, positions=s_new.positions, cell=s_new.cell)
    return pbc #p_struc

def get_perturbed_struc(struc, eps):
    s_new = struc.copy()
    pos = s_new.positions
    pos[0,0] += eps
    pbc = Atoms(s_new.symbols.numbers, positions=pos, cell=s_new.cell)
    return pbc #p_struc

surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
layers = [2, 2, 2]
lc = 3.61000
cu = FaceCenteredCubic('Cu', surfaces, layers, latticeconstant=lc, vacuum=10)
rcut = 3.0
eps = 1e-8

#TrainData = resource_filename("pyxtal_ff", "datasets/Si/UCSD/test.json")
TrainData = resource_filename("pyxtal_ff", "datasets/Si/PyXtal/Si4.json")
parameters = {'lmax': 2}
system = ['Si']
descriptor = {'type': 'Bispectrum',
              'parameters': parameters,
              'Rc': 3.0,
              'N_train': 10,
              'stress': True,
              }

descriptor_comp = {'type': 'Bispectrum',
Exemplo n.º 24
0
from ase.cluster.cubic import FaceCenteredCubic
from ase.calculators.turbomole import Turbomole

surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
layers = [1, 2, 1]
atoms = FaceCenteredCubic('Au', surfaces, layers, latticeconstant=4.08)

params = {
    'title': 'Au13-',
    'task': 'energy',
    'basis set name': 'def2-SV(P)',
    'total charge': -1,
    'multiplicity': 1,
    'use dft': True,
    'density functional': 'pbe',
    'use resolution of identity': True,
    'ri memory': 1000,
    'use fermi smearing': True,
    'fermi initial temperature': 500,
    'fermi final temperature': 100,
    'fermi annealing factor': 0.9,
    'fermi h**o-lumo gap criterion': 0.09,
    'fermi stopping criterion': 0.002,
    'scf energy convergence': 1.e-4,
    'scf iterations': 250
}

calc = Turbomole(**params)
atoms.set_calculator(calc)
calc.calculate(atoms)
Exemplo n.º 25
0
# creates: culayer.png truncated.png

from ase.io import write
from ase.cluster.cubic import FaceCenteredCubic
#from ase.cluster.hexagonal import HexagonalClosedPacked
#import numpy as np

surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
layers = [6, 9, 5]
lc = 3.61000
culayer = FaceCenteredCubic('Cu', surfaces, layers, latticeconstant=lc)
culayer.rotate(6, 'x', rotate_cell=True)
culayer.rotate(2, 'y', rotate_cell=True)
write('culayer.pov', culayer, show_unit_cell=0).render()

surfaces = [(1, 0, 0), (1, 1, 1), (1, -1, 1)]
layers = [6, 5, -1]
trunc = FaceCenteredCubic('Cu', surfaces, layers)
trunc.rotate(6, 'x', rotate_cell=True)
trunc.rotate(2, 'y', rotate_cell=True)
write('truncated.pov', trunc, show_unit_cell=0).render()

# This does not work!
#surfaces = [(0, 0, 0, 1), (1, 1, -2, 0), (1, 0, -1, 1)]
#layers = [6, 6, 6]
#graphite = Graphite('C', surfaces, layers, latticeconstant=(2.461, 6.708))
#write('graphite.pov', graphite).render()

# surfaces = [(0, 0, 0, 1), (1, 1, -2, 0), (1, 0, -1, 1)]
# layers = [6, 6, 6]
# magn = HexagonalClosedPacked('Mg', surfaces, layers)
Exemplo n.º 26
0
            for i in range(3):
                if atom.position[i] < self.margin:
                    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()