Exemplo n.º 1
0
def _cubic_bulk(name, x, a):
    if x == 'fcc':
        atoms = Atoms(4 * name,
                      cell=(a, a, a),
                      pbc=True,
                      scaled_positions=[(0, 0, 0), (0, 0.5, 0.5),
                                        (0.5, 0, 0.5), (0.5, 0.5, 0)])
    elif x == 'diamond':
        atoms = _cubic_bulk(2 * name, 'zincblende', a)
    elif x == 'zincblende':
        atoms = Atoms(4 * name,
                      cell=(a, a, a),
                      pbc=True,
                      scaled_positions=[(0, 0, 0), (0.25, 0.25, 0.25),
                                        (0, 0.5, 0.5), (0.25, 0.75, 0.75),
                                        (0.5, 0, 0.5), (0.75, 0.25, 0.75),
                                        (0.5, 0.5, 0), (0.75, 0.75, 0.25)])
    elif x == 'rocksalt':
        atoms = Atoms(4 * name,
                      cell=(a, a, a),
                      pbc=True,
                      scaled_positions=[(0, 0, 0), (0.5, 0, 0), (0, 0.5, 0.5),
                                        (0.5, 0.5, 0.5), (0.5, 0, 0.5),
                                        (0, 0, 0.5), (0.5, 0.5, 0),
                                        (0, 0.5, 0)])
    else:
        raise RuntimeError

    return atoms
Exemplo n.º 2
0
def read_pdb(fileobj, index=-1):
    """Read PDB files.

    The format is assumed to follow the description given in
    http://www.wwpdb.org/documentation/format32/sect9.html."""
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    atoms = Atoms()
    positions = []
    symbols = []
    for line in fileobj.readlines():
        if line.startswith('ATOM') or line.startswith('HETATM'):
            try:
                symbol = line[12:16].strip()
                # we assume that the second character is a label
                # in case that it is upper case
                if len(symbol) > 1 and symbol[1].isupper():
                    symbol = symbol[0]
                words = line[30:55].split()
                #charges.append(float(c))
                symbols.append(symbol)
                positions.append(
                    np.array(
                        [float(words[0]),
                         float(words[1]),
                         float(words[2])]))
            except:
                print("Couldn't read entry in pdb")
                pass

    return Atoms(symbols=symbols, positions=positions)
Exemplo n.º 3
0
def read_dacapo(filename):
    from ase_ext.io.pupynere import NetCDFFile

    nc = NetCDFFile(filename)
    dims = nc.dimensions
    vars = nc.variables

    cell = vars['UnitCell'][-1]
    try:
        magmoms = vars['InitialAtomicMagneticMoment'][:]
    except KeyError:
        magmoms = None
    try:
        tags = vars['AtomTags'][:]
    except KeyError:
        tags = None
    atoms = Atoms(scaled_positions=vars['DynamicAtomPositions'][-1],
                  symbols=[(a + b).strip()
                           for a, b in vars['DynamicAtomSpecies'][:]],
                  cell=cell,
                  magmoms=magmoms,
                  tags=tags,
                  pbc=True)

    try:
        energy = vars['TotalEnergy'][-1]
        force = vars['DynamicAtomForces'][-1]
    except KeyError:
        energy = None
        force = None
    calc = SinglePointCalculator(energy, force, None, None,
                                 atoms)  ### Fixme magmoms
    atoms.set_calculator(calc)

    return atoms
Exemplo n.º 4
0
def read_xsf(fileobj, index=-1):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    readline = fileobj.readline
    line = readline()

    if line.startswith('ANIMSTEPS'):
        nimages = int(line.split()[1])
        line = readline()
    else:
        nimages = 1

    if line.startswith('CRYSTAL'):
        pbc = True
    elif line.startswith('SLAB'):
        pbc = (True, True, Flase)
    elif line.startswith('POLYMER'):
        pbc = (True, False, Flase)
    else:
        pbc = False

    images = []
    for n in range(nimages):
        cell = None
        if pbc:
            line = readline()
            assert line.startswith('PRIMVEC')
            cell = []
            for i in range(3):
                cell.append([float(x) for x in readline().split()])

        line = readline()
        assert line.startswith('PRIMCOORD')

        natoms = int(readline().split()[0])
        numbers = []
        positions = []
        for a in range(natoms):
            line = readline().split()
            numbers.append(int(line[0]))
            positions.append([float(x) for x in line[1:]])

        positions = np.array(positions)
        if len(positions[0]) == 3:
            forces = None
        else:
            positions = positions[:, :3]
            forces = positions[:, 3:] * Hartree

        image = Atoms(numbers, positions, cell=cell, pbc=pbc)

        if forces is not None:
            image.set_calculator(SinglePointCalculator(None, forces, None,
                                                       None, image))
        images.append(image)

    return images[index]
Exemplo n.º 5
0
def read_xyz(fileobj, index=-1):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    lines = fileobj.readlines()
    L1 = lines[0].split()
    if len(L1) == 1:
#        del lines[:2]
        natoms = int(L1[0])
    else:
        natoms = len(lines)
    energy=0
    images = []
    while len(lines) >= natoms:
        positions = []
        symbols = []
        forces = []
        charges = []
        L2 = lines[1].split()
        for line in lines[2:natoms+2]:
            linesplit = line.split()
            if len(linesplit)==5:
                symbol, x, y, z, c = linesplit[:5]
                charges.append(c)
            else:
                symbol, x, y, z = linesplit[:4]
                
            symbols.append(symbol)
            positions.append([float(x), float(y), float(z)])
            if len(linesplit) > 9:
                fx,fy,fz = linesplit[7:10]
                forces.append([float(fx), float(fy), float(fz)])
            
        cell=[]
        if forces==[]:
         forces=None
        if len(lines[1].split())>0:
            if len(lines[1].split("\""))>1:
             L = lines[1].split("\"")[1].split()
            else:
             L = lines[1].split()
             if len(L)>9:
              energy=float(L[9])
            try:
             cell = ((float(L[0]),float(L[1]),float(L[2])),(float(L[3]),float(L[4]),float(L[5])),(float(L[6]),float(L[7]),float(L[8])))
            except ValueError:
             cell = ((0,0,0),(0,0,0),(0,0,0))
            images.append(Atoms(symbols=symbols, positions=positions,forces=forces, energy=energy,cell=cell))
        else:
            images.append(Atoms(symbols=symbols, positions=positions,forces=forces, energy=energy)) 
        if len(charges)>0:
            images[-1].set_charges(charges)
        del lines[:natoms + 2]
        if ((len(lines)>0) and (len(lines[0].split())>0)):
            natoms = int(lines[0].split()[0])
    #return images[index]
    return images
Exemplo n.º 6
0
def read_turbomole(filename='coord'):
    """Method to read turbomole coord file
    
    coords in bohr, atom types in lowercase, format:
    $coord
    x y z atomtype 
    x y z atomtype f
    $end
    Above 'f' means a fixed atom.
    """
    from ase_ext import Atoms, Atom
    from ase_ext.constraints import FixAtoms

    if isinstance(filename, str):
        f = open(filename)

    lines = f.readlines()
    atoms_pos = []
    atom_symbols = []
    dollar_count=0
    myconstraints=[]
    for line in lines:
        if ('$' in line):
            dollar_count = dollar_count + 1
            if (dollar_count >= 2):
                break
        else:
            x, y, z, symbolraw = line.split()[:4]
            symbolshort=symbolraw.strip()
            symbol=symbolshort[0].upper()+symbolshort[1:].lower()
            #print symbol
            atom_symbols.append(symbol)
            atoms_pos.append([float(x)*Bohr, float(y)*Bohr, float(z)*Bohr])
            cols = line.split()
            if (len(cols) == 5):
                fixedstr = line.split()[4].strip()
                if (fixedstr == "f"):
                    myconstraints.append(True)
                else:
                    myconstraints.append(False)
            else:
                myconstraints.append(False)
            
    if type(filename) == str:
        f.close()

    atoms = Atoms(positions = atoms_pos, symbols = atom_symbols, pbc = False)
    c = FixAtoms(myconstraints)
    atoms.set_constraint(c)
    #print c
    

    return atoms
Exemplo n.º 7
0
def read_cif(fileobj, index=-1):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    def search_key(fobj, key):
        for line in fobj:
            if key in line:
                return line
        return None

    def get_key(fobj, key, pos=1):
        line = search_key(fobj, key)
        if line:
            return float(line.split()[pos].split('(')[0])
        return None

    a = get_key(fileobj, '_cell_length_a')
    b = get_key(fileobj, '_cell_length_b')
    c = get_key(fileobj, '_cell_length_c')
    alpha =  pi * get_key(fileobj, '_cell_angle_alpha') / 180
    beta = pi * get_key(fileobj, '_cell_angle_beta') / 180
    gamma =  pi * get_key(fileobj, '_cell_angle_gamma') / 180

    va = a * np.array([1, 0, 0])
    vb = b * np.array([cos(gamma), sin(gamma), 0])
    cx = cos(beta)
    cy = (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma)
    cz = sqrt(1. - cx*cx - cy*cy)
    vc = c * np.array([cx, cy, cz])
    cell = np.array([va, vb, vc])

    atoms = Atoms(cell=cell)
    read = False
    for line in fileobj:
        if not read:
            if '_atom_site_disorder_group' in line:
                read = True
        else:
            word = line.split()
            if len(word) < 5:
                break
            symbol = word[1]
            pos = (float(word[2].split('(')[0]) * va +
                   float(word[3].split('(')[0]) * vb +
                   float(word[4].split('(')[0]) * vc   )
            atoms.append(Atom(symbol, pos))

    return atoms
Exemplo n.º 8
0
def read_vnl(filename):
    from io import StringIO
    vnl = VNLUnpickler(StringIO(ZipFile(filename).read('0_object'))).load()
    conf = vnl.data['__properties__']['Atomic Configuration'].data
    numbers = conf['_dataarray_']
    positions = conf['_positions_'].data['_dataarray_']
    return Atoms(numbers=numbers, positions=positions)
Exemplo n.º 9
0
    def bind(self, frame):
        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds').get_active():
            self.bonds = np.empty((0, 5), int)
            return

        from ase_ext.atoms import Atoms
        from ase_ext.calculators.neighborlist import NeighborList
        nl = NeighborList(self.images.r * 1.5, skin=0, self_interaction=False)
        nl.update(
            Atoms(positions=self.images.P[frame],
                  cell=(self.images.repeat[:, np.newaxis] *
                        self.images.A[frame]),
                  pbc=self.images.pbc))
        nb = nl.nneighbors + nl.npbcneighbors
        self.bonds = np.empty((nb, 5), int)
        if nb == 0:
            return

        n1 = 0
        for a in range(self.images.natoms):
            indices, offsets = nl.get_neighbors(a)
            n2 = n1 + len(indices)
            self.bonds[n1:n2, 0] = a
            self.bonds[n1:n2, 1] = indices
            self.bonds[n1:n2, 2:] = offsets
            n1 = n2

        i = self.bonds[:n2, 2:].any(1)
        self.bonds[n2:, 0] = self.bonds[i, 1]
        self.bonds[n2:, 1] = self.bonds[i, 0]
        self.bonds[n2:, 2:] = -self.bonds[i, 2:]
Exemplo n.º 10
0
def read_iwm(fileobj, index=-1):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    lines = fileobj.readlines()
    L1 = lines[1].split()
    if len(L1) == 1:
        del lines[:3]
        natoms = int(L1[0])
    else:
        natoms = len(lines)
    images = []

    positions = []
    symbols = []
    for line in lines[:natoms]:
        symbol, mass, x, y, z = line.split()[:5]
        if symbol in iwm_symbols:
            symbols.append(iwm_symbols[symbol])
        else:
            symbols.append(chemical_symbols[int(symbol)])
        positions.append([float(x), float(y), float(z)])

    del (lines[natoms:3 * natoms + 3])

    cell = []
    for line in lines[natoms:natoms + 3]:
        x, y, z = line.split()[:3]
        cell.append(np.array([float(x), float(y), float(z)]))

    images.append(Atoms(symbols=symbols, positions=positions, cell=cell))

    return images[index]
Exemplo n.º 11
0
def make_test_dft_calculation():
    a = b = 2.0
    c = 6.0
    atoms = Atoms(positions=[(0, 0, c / 2)],
                  symbols='H',
                  pbc=(1, 1, 0),
                  cell=(a, b, c),
                  calculator=TestCalculator())
    return atoms
Exemplo n.º 12
0
def read_I_info(fileobj, index=-1):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    lines = fileobj.readlines()

    del lines[0]

    finished = False
    s = Atoms()
    while not finished:
        w = lines.pop(0).split()
        if w[0].startswith('"'):
            position = Bohr * np.array([float(w[3]), float(w[4]), float(w[5])])
            s.append(Atom(w[0].replace('"', ''), position))
        else:
            finished = True

    return s
Exemplo n.º 13
0
def read_dacapo_text(fileobj):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    lines = fileobj.readlines()
    i = lines.index(' Structure:             A1           A2            A3\n')
    cell = np.array([[float(w) for w in line.split()[2:5]]
                     for line in lines[i + 1:i + 4]]).transpose()
    i = lines.index(' Structure:  >>         Ionic positions/velocities ' +
                    'in cartesian coordinates       <<\n')
    atoms = []
    for line in lines[i + 4:]:
        words = line.split()
        if len(words) != 9:
            break
        Z, x, y, z = words[2:6]
        atoms.append(Atom(int(Z), [float(x), float(y), float(z)]))

    atoms = Atoms(atoms, cell=cell.tolist())

    try:
        i = lines.index(
            ' DFT:  CPU time                           Total energy\n')
    except ValueError:
        pass
    else:
        column = lines[i + 3].split().index('selfcons') - 1
        try:
            i2 = lines.index(' ANALYSIS PART OF CODE\n', i)
        except ValueError:
            pass
        else:
            while i2 > i:
                if lines[i2].startswith(' DFT:'):
                    break
                i2 -= 1
            energy = float(lines[i2].split()[column])
            atoms.set_calculator(
                SinglePointCalculator(energy, None, None, None, atoms))

    return atoms
Exemplo n.º 14
0
def _orthorhombic_bulk(name, x, a, covera=None):
    if x == 'fcc':
        b = a / sqrt(2)
        atoms = Atoms(2 * name,
                      cell=(b, b, a),
                      pbc=True,
                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)])
    elif x == 'bcc':
        atoms = Atoms(2 * name,
                      cell=(a, a, a),
                      pbc=True,
                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)])
    elif x == 'hcp':
        atoms = Atoms(4 * name,
                      cell=(a, a * sqrt(3), covera * a),
                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0),
                                        (0.5, 1.0 / 6.0, 0.5),
                                        (0, 2.0 / 3.0, 0.5)],
                      pbc=True)
    elif x == 'diamond':
        atoms = _orthorhombic_bulk(2 * name, 'zincblende', a)
    elif x == 'zincblende':
        s1, s2 = string2symbols(name)
        b = a / sqrt(2)
        atoms = Atoms(2 * name,
                      cell=(b, b, a),
                      pbc=True,
                      scaled_positions=[(0, 0, 0), (0.5, 0, 0.25),
                                        (0.5, 0.5, 0.5), (0, 0.5, 0.75)])
    elif x == 'rocksalt':
        s1, s2 = string2symbols(name)
        b = a / sqrt(2)
        atoms = Atoms(2 * name,
                      cell=(b, b, a),
                      pbc=True,
                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0),
                                        (0.5, 0.5, 0.5), (0, 0, 0.5)])
    else:
        raise RuntimeError

    return atoms
Exemplo n.º 15
0
    def __getitem__(self, i=-1):
        N = len(self.offsets)
        if 0 <= i < N:
            self.fd.seek(self.offsets[i])
            try:
                d = pickle.load(self.fd)
            except EOFError:
                raise IndexError
            if i == N - 1:
                self.offsets.append(self.fd.tell())
            try:
                magmoms = d['magmoms']
            except KeyError:
                magmoms = None
            atoms = Atoms(positions=d['positions'],
                          numbers=self.numbers,
                          cell=d['cell'],
                          momenta=d['momenta'],
                          magmoms=magmoms,
                          tags=self.tags,
                          masses=self.masses,
                          pbc=self.pbc,
                          constraint=[c.copy() for c in self.constraints])
            if 'energy' in d:
                calc = SinglePointCalculator(d.get('energy', None),
                                             d.get('forces', None),
                                             d.get('stress', None), magmoms,
                                             atoms)
                atoms.set_calculator(calc)
            return atoms

        if i >= N:
            for j in range(N - 1, i + 1):
                atoms = self[j]
            return atoms

        i = len(self) + i
        if i < 0:
            raise IndexError('Trajectory index out of range.')
        return self[i]
Exemplo n.º 16
0
 def copy(self):
     from ase_ext.atoms import Atoms
     return Atoms(positions=self.get_positions(),
                  numbers=self.get_atomic_numbers(),
                  tags=self.get_tags(),
                  momenta=self.get_momenta(),
                  masses=self.get_masses(),
                  magmoms=self.get_initial_magnetic_moments(),
                  charges=self.get_charges(),
                  cell=self.get_cell(),
                  pbc=self.get_pbc(),
                  constraint=None,
                  calculator=None) # Don't copy the calculator
Exemplo n.º 17
0
def read_mol(fileobj, index=-1):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    lines = fileobj.readlines()
    del (lines[:3])
    L1 = lines[0].split()
    del (lines[0])
    natoms = int(L1[0])
    positions = []
    symbols = []
    for line in lines[:natoms]:
        x, y, z, symbol = line.split()[:4]
        symbols.append(symbol)
        positions.append([float(x), float(y), float(z)])
    return Atoms(symbols=symbols, positions=positions)
Exemplo n.º 18
0
def read_exciting(fileobj, index=-1):
    """Reads structure from exiting xml file.
    
    Parameters
    ----------
    fileobj: file object
        Filehandle from which data should be read.
        
    Other parameters
    ----------------
    index: integer -1
        Not used in this implementation.
    """
    from lxml import etree as ET
    #parse file into element tree
    doc = ET.parse(fileobj)
    root = doc.getroot()
    speciesnodes = root.find('structure').getiterator('species') 
 
    symbols = []
    positions = []
    basevects = []
    atoms = None
    #collect data from tree
    for speciesnode in speciesnodes:
        symbol = speciesnode.get('speciesfile').split('.')[0]
        natoms = speciesnode.getiterator('atom')
        for atom in natoms:
            x, y, z = atom.get('coord').split()
            positions.append([float(x), float(y), float(z)])
            symbols.append(symbol)
    basevectsn = doc.xpath('//basevect/text()') 
 
    for basevect in basevectsn:
        x, y, z = basevect.split()
        basevects.append([float(x) * Bohr, float(y) * Bohr, float(z) * Bohr])
    atoms = Atoms(symbols=symbols, cell=basevects)
    atoms.set_scaled_positions(positions)
    if 'molecule' in list(root.find('structure').attrib.keys()):
        if root.find('structure').attrib['molecule']:
            atoms.set_pbc(False)
    else:
        atoms.set_pbc(True)
        
    return atoms
Exemplo n.º 19
0
def read_sdf(fileobj):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    lines = fileobj.readlines()
    # first three lines header
    del lines[:3]

    #
    L1 = lines.pop(0).split()
    natoms = int(L1[0])
    positions = []
    symbols = []
    for line in lines[:natoms]:
        x, y, z, symbol = line.split()[:4]
        symbols.append(symbol)
        positions.append([float(x), float(y), float(z)])
    return Atoms(symbols=symbols, positions=positions)
Exemplo n.º 20
0
    def run(opt, args):
        images = Images()

        if opt.aneb:
            opt.image_number = '-1'

        if len(args) > 0:
            from ase_ext.io import string2index
            images.read(args, string2index(opt.image_number))
        else:
            images.initialize([Atoms()])

        if opt.interpolate:
            images.interpolate(opt.interpolate)

        if opt.aneb:
            images.aneb()

        if opt.repeat != '1':
            r = opt.repeat.split(',')
            if len(r) == 1:
                r = 3 * r
            images.repeat_images([int(c) for c in r])

        if opt.output is not None:
            images.write(opt.output, rotations=opt.rotations,
                        show_unit_cell=opt.show_unit_cell)
            opt.terminal = True

        if opt.terminal:
            if opt.graph is not None:
                data = images.graph(opt.graph)
                for line in data.T:
                    for x in line:
                        print(x, end=' ')
                    print()
        else:
            from ase_ext.gui.gui import GUI
            gui = GUI(images, opt.rotations, opt.show_unit_cell, opt.bonds)
            gui.run(opt.graph)
Exemplo n.º 21
0
def read_cube(fileobj, index=-1, read_data=False):
    if isinstance(fileobj, str):
        fileobj = open(fileobj)

    readline = fileobj.readline
    readline()
    axes = ['XYZ'.index(s[0]) for s in readline().split()[2::3]]
    if axes == []:
        axes = [0, 1, 2]
    line = readline().split()
    natoms = int(line[0])
    corner = [Bohr * float(x) for x in line[1:]]

    cell = np.empty((3, 3))
    shape = []
    for i in range(3):
        n, x, y, z = [float(s) for s in readline().split()]
        shape.append(n)
        if n % 2 == 1:
            n += 1
        cell[i] = n * Bohr * np.array([x, y, z])

    numbers = np.empty(natoms, int)
    positions = np.empty((natoms, 3))
    for i in range(natoms):
        line = readline().split()
        numbers[i] = int(line[0])
        positions[i] = [float(s) for s in line[2:]]

    positions *= Bohr
    atoms = Atoms(numbers=numbers, positions=positions, cell=cell)

    if read_data:
        data = np.array([float(s)
                         for s in fileobj.read().split()]).reshape(shape)
        if axes != [0, 1, 2]:
            data = data.transpose(axes).copy()
        return data, atoms

    return atoms
Exemplo n.º 22
0
    def get_atoms(self, frame):
        atoms = Atoms(positions=self.P[frame],
                      numbers=self.Z,
                      magmoms=self.M[0],
                      tags=self.T,
                      cell=self.A[frame],
                      pbc=self.pbc)

        # check for constrained atoms and add them accordingly:
        if not self.dynamic.all():
            atoms.set_constraint(FixAtoms(mask=1 - self.dynamic))

        atoms.set_calculator(
            SinglePointCalculator(self.E[frame], self.F[frame], None, None,
                                  atoms))
        return atoms
Exemplo n.º 23
0
def bulk(name,
         crystalstructure,
         a=None,
         c=None,
         covera=None,
         orthorhombic=False,
         cubic=False):
    """Helper function for creating bulk systems.

    name: str
        Chemical symbol or symbols as in 'MgO' or 'NaCl'.
    crystalstructure: str
        Must be one of sc, fcc, bcc, hcp, diamond, zincblende or
        rocksalt.
    a: float
        Lattice constant.
    c: float
        Lattice constant.
    covera: float
        c/a raitio used for hcp.  Defaults to ideal ratio.
    orthorhombic: bool
        Construct orthorhombic unit cell instead of primitive cell
        which is the default.
    cubic: bool
        Construct cubic unit cell.
    """

    if covera is not None and c is not None:
        raise ValueError("Don't specify both c and c/a!")

    if covera is None and c is None:
        covera = sqrt(8.0 / 3.0)

    if a is None:
        a = estimate_lattice_constant(name, crystalstructure, covera)

    if covera is None and c is not None:
        covera = c / a

    x = crystalstructure.lower()

    if orthorhombic and x != 'sc':
        return _orthorhombic_bulk(name, x, a, covera)

    if cubic and x == 'bcc':
        return _orthorhombic_bulk(name, x, a, covera)

    if cubic and x != 'sc':
        return _cubic_bulk(name, x, a)

    if x == 'sc':
        atoms = Atoms(name, cell=(a, a, a), pbc=True)
    elif x == 'fcc':
        b = a / 2
        atoms = Atoms(name, cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=True)
    elif x == 'bcc':
        b = a / 2
        atoms = Atoms(name,
                      cell=[(-b, b, b), (b, -b, b), (b, b, -b)],
                      pbc=True)
    elif x == 'hcp':
        atoms = Atoms(2 * name,
                      scaled_positions=[(0, 0, 0),
                                        (1.0 / 3.0, 1.0 / 3.0, 0.5)],
                      cell=[(a, 0, 0), (a / 2, a * sqrt(3) / 2, 0),
                            (0, 0, covera * a)],
                      pbc=True)
    elif x == 'diamond':
        atoms = bulk(2 * name, 'zincblende', a)
    elif x == 'zincblende':
        s1, s2 = string2symbols(name)
        atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a)
        atoms.positions[1] += a / 4
    elif x == 'rocksalt':
        s1, s2 = string2symbols(name)
        atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a)
        atoms.positions[1, 0] += a / 2
    else:
        raise ValueError('Unknown crystal structure: ' + crystalstructure)

    return atoms
Exemplo n.º 24
0
def nanotube(n, m, length=1, bond=1.42, symbol='C', verbose=False):
    if n < m:
        m, n = n, m
    nk = 6000
    sq3 = sqrt(3.0)
    a = sq3 * bond
    l2 = n * n + m * m + n * m
    l = sqrt(l2)
    dt = a * l / np.pi

    nd = gcd(n, m)
    if (n - m) % (3 * nd) == 0:
        ndr = 3 * nd
    else:
        ndr = nd

    nr = (2 * m + n) / ndr
    ns = -(2 * n + m) / ndr
    nt2 = 3 * l2 / ndr / ndr
    nt = np.floor(sqrt(nt2))
    nn = 2 * l2 / ndr

    ichk = 0
    if nr == 0:
        n60 = 1
    else:
        n60 = nr * 4

    absn = abs(n60)
    nnp = []
    nnq = []
    for i in range(-absn, absn + 1):
        for j in range(-absn, absn + 1):
            j2 = nr * j - ns * i
            if j2 == 1:
                j1 = m * i - n * j
                if j1 > 0 and j1 < nn:
                    ichk += 1
                    nnp.append(i)
                    nnq.append(j)

    if ichk == 0:
        raise RuntimeError('not found p, q strange!!')
    if ichk >= 2:
        raise RuntimeError('more than 1 pair p, q strange!!')

    nnnp = nnp[0]
    nnnq = nnq[0]

    if verbose:
        print('the symmetry vector is', nnnp, nnnq)

    lp = nnnp * nnnp + nnnq * nnnq + nnnp * nnnq
    r = a * sqrt(lp)
    c = a * l
    t = sq3 * c / ndr

    if 2 * nn > nk:
        raise RuntimeError('parameter nk is too small!')

    rs = c / (2.0 * np.pi)

    if verbose:
        print('radius=', rs, t)

    q1 = np.arctan((sq3 * m) / (2 * n + m))
    q2 = np.arctan((sq3 * nnnq) / (2 * nnnp + nnnq))
    q3 = q1 - q2

    q4 = 2.0 * np.pi / nn
    q5 = bond * np.cos((np.pi / 6.0) - q1) / c * 2.0 * np.pi

    h1 = abs(t) / abs(np.sin(q3))
    h2 = bond * np.sin((np.pi / 6.0) - q1)

    ii = 0
    x, y, z = [], [], []
    for i in range(nn):
        x1, y1, z1 = 0, 0, 0

        k = np.floor(i * abs(r) / h1)
        x1 = rs * np.cos(i * q4)
        y1 = rs * np.sin(i * q4)
        z1 = (i * abs(r) - k * h1) * np.sin(q3)
        kk2 = abs(np.floor((z1 + 0.0001) / t))
        if z1 >= t - 0.0001:
            z1 -= t * kk2
        elif z1 < 0:
            z1 += t * kk2
        ii += 1

        x.append(x1)
        y.append(y1)
        z.append(z1)
        z3 = (i * abs(r) - k * h1) * np.sin(q3) - h2
        ii += 1

        if z3 >= 0 and z3 < t:
            x2 = rs * np.cos(i * q4 + q5)
            y2 = rs * np.sin(i * q4 + q5)
            z2 = (i * abs(r) - k * h1) * np.sin(q3) - h2
            x.append(x2)
            y.append(y2)
            z.append(z2)
        else:
            x2 = rs * np.cos(i * q4 + q5)
            y2 = rs * np.sin(i * q4 + q5)
            z2 = (i * abs(r) - (k + 1) * h1) * np.sin(q3) - h2
            kk = abs(np.floor(z2 / t))
            if z2 >= t - 0.0001:
                z2 -= t * kk
            elif z2 < 0:
                z2 += t * kk
            x.append(x2)
            y.append(y2)
            z.append(z2)

    ntotal = 2 * nn
    X = []
    for i in range(ntotal):
        X.append([x[i], y[i], z[i]])

    if length > 1:
        xx = X[:]
        for mnp in range(2, length + 1):
            for i in range(len(xx)):
                X.append(xx[i][:2] + [xx[i][2] + (mnp - 1) * t])

    TransVec = t
    NumAtom = ntotal * length
    Diameter = rs * 2
    ChiralAngle = np.arctan((sq3 * n) / (2 * m + n)) / (np.pi * 180)

    cell = [Diameter * 2, Diameter * 2, length * t]
    atoms = Atoms(symbol + str(NumAtom),
                  positions=X,
                  cell=cell,
                  pbc=[False, False, True])
    atoms.center()
    if verbose:
        print('translation vector =', TransVec)
        print('diameter = ', Diameter)
        print('chiral angle = ', ChiralAngle)
    return atoms
Exemplo n.º 25
0
def surface(symbol, structure, face, size, a, c, vacuum, orthogonal=True):
    """Function to build often used surfaces.

    Don't call this function directly - use fcc100, fcc110, bcc111, ..."""
    
    Z = atomic_numbers[symbol]

    if a is None:
        sym = reference_states[Z]['symmetry'].lower()
        if sym != structure:
            raise ValueError("Can't guess lattice constant for %s-%s!" %
                             (structure, symbol))
        a = reference_states[Z]['a']

    if structure == 'hcp' and c is None:
        if reference_states[Z]['symmetry'].lower() == 'hcp':
            c = reference_states[Z]['c/a'] * a
        else:
            c = sqrt(8 / 3.0) * a

    positions = np.empty((size[2], size[1], size[0], 3))
    positions[..., 0] = np.arange(size[0]).reshape((1, 1, -1))
    positions[..., 1] = np.arange(size[1]).reshape((1, -1, 1))
    positions[..., 2] = np.arange(size[2]).reshape((-1, 1, 1))

    numbers = np.ones(size[0] * size[1] * size[2], int) * Z

    tags = np.empty((size[2], size[1], size[0]), int)
    tags[:] = np.arange(size[2], 0, -1).reshape((-1, 1, 1))

    slab = Atoms(numbers,
                 tags=tags.ravel(),
                 pbc=(True, True, False),
                 cell=size)

    surface_cell = None
    sites = {'ontop': (0, 0)}
    surf = structure + face
    if surf == 'fcc100':
        cell = (sqrt(0.5), sqrt(0.5), 0.5)
        positions[-2::-2, ..., :2] += 0.5
        sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)})
    elif surf == 'fcc110':
        cell = (1.0, sqrt(0.5), sqrt(0.125))
        positions[-2::-2, ..., :2] += 0.5
        sites.update({'hollow': (0.5, 0.5), 'longbridge': (0.5, 0),
                      'shortbridge': (0, 0.5)})
    elif surf == 'bcc100':
        cell = (1.0, 1.0, 0.5)
        positions[-2::-2, ..., :2] += 0.5
        sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)})
    else:
        if orthogonal and size[1] % 2 == 1:
            raise ValueError(("Can't make orthorhombic cell with size=%r.  " %
                              (tuple(size),)) +
                             'Second number in size must be even.')
        if surf == 'fcc111':
            cell = (sqrt(0.5), sqrt(0.375), 1 / sqrt(3))
            if orthogonal:
                positions[-1::-3, 1::2, :, 0] += 0.5
                positions[-2::-3, 1::2, :, 0] += 0.5
                positions[-3::-3, 1::2, :, 0] -= 0.5
                positions[-2::-3, ..., :2] += (0.0, 2.0 / 3)
                positions[-3::-3, ..., :2] += (0.5, 1.0 / 3)
            else:
                positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3)
                positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3)
            sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3),
                          'hcp': (2.0 / 3, 2.0 / 3)})
        elif surf == 'hcp0001':
            cell = (1.0, sqrt(0.75), 0.5 * c / a)
            if orthogonal:
                positions[:, 1::2, :, 0] += 0.5
                positions[-2::-2, ..., :2] += (0.0, 2.0 / 3)
            else:
                positions[-2::-2, ..., :2] += (-1.0 / 3, 2.0 / 3)
            sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3),
                          'hcp': (2.0 / 3, 2.0 / 3)})
        elif surf == 'bcc110':
            cell = (1.0, sqrt(0.5), sqrt(0.5))
            if orthogonal:
                positions[:, 1::2, :, 0] += 0.5
                positions[-2::-2, ..., :2] += (0.0, 1.0)
            else:
                positions[-2::-2, ..., :2] += (-0.5, 1.0)
            sites.update({'shortbridge': (0, 0.5),
                          'longbridge': (0.5, 0),
                          'hollow': (0.375, 0.25)})
        elif surf == 'bcc111':
            cell = (sqrt(2), sqrt(1.5), sqrt(3) / 6)
            if orthogonal:
                positions[-1::-3, 1::2, :, 0] += 0.5
                positions[-2::-3, 1::2, :, 0] += 0.5
                positions[-3::-3, 1::2, :, 0] -= 0.5
                positions[-2::-3, ..., :2] += (0.0, 2.0 / 3)
                positions[-3::-3, ..., :2] += (0.5, 1.0 / 3)
            else:
                positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3)
                positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3)
            sites.update({'hollow': (1.0 / 3, 1.0 / 3)})
            
        surface_cell = a * np.array([(cell[0], 0),
                                     (cell[0] / 2, cell[1])])
        if not orthogonal:
            cell = np.array([(cell[0], 0, 0),
                             (cell[0] / 2, cell[1], 0),
                             (0, 0, cell[2])])

    if surface_cell is None:
        surface_cell = a * np.diag(cell[:2])

    if isinstance(cell, tuple):
        cell = np.diag(cell)
        
    slab.set_positions(positions.reshape((-1, 3)))

    slab.set_cell([a * v * n for v, n in zip(cell, size)], scale_atoms=True)

    if vacuum is not None:
        slab.center(vacuum=vacuum, axis=2)
    
    slab.adsorbate_info['cell'] = surface_cell
    slab.adsorbate_info['sites'] = sites
    
    return slab

    
        
Exemplo n.º 26
0
def read_dftb(filename='dftb_in.hsd'):
    """Method to read coordinates form DFTB+ input file dftb_in.hsd
    additionally read information about fixed atoms
    and periodic boundary condition
    """
    from ase_ext import Atoms, Atom
    from ase_ext.constraints import FixAtoms
    import sys, string

    if isinstance(filename, str):
        f = open(filename)

    lines = f.readlines()
    atoms_pos = []
    atom_symbols = []
    type_names = []
    my_pbc = False
    myconstraints = []
    moved_atoms_found = False
    range_found = False
    moved_atoms = []

    for line in lines:
        if (line.strip().startswith('#')):
            pass
        else:
            if ('TypeNames' in line):
                col = line.split()
                for i in range(3, len(col) - 1):
                    type_names.append(col[i].strip("\""))
            elif ('Periodic' in line):
                if ('Yes' in line):
                    my_pbc = True
            else:
                pass

    start_reading_coords = False
    stop_reading_coords = False
    for line in lines:
        if (line.strip().startswith('#')):
            pass
        else:
            if ('TypesAndCoordinates' in line):
                start_reading_coords = True
            if start_reading_coords:
                if ('}' in line):
                    stop_reading_coords = True
            if (start_reading_coords and not (stop_reading_coords)
                    and not 'TypesAndCoordinates' in line):
                typeindexstr, x, y, z = line.split()[:4]
                typeindex = int(typeindexstr)
                symbol = type_names[typeindex - 1]
                atom_symbols.append(symbol)
                atoms_pos.append([float(x), float(y), float(z)])

    if type(filename) == str:
        f.close()

    atoms = Atoms(positions=atoms_pos, symbols=atom_symbols, pbc=my_pbc)

    return atoms
Exemplo n.º 27
0
def read(filename, index=-1, format=None):
    """Read Atoms object(s) from file.

    filename: str
        Name of the file to read from.
    index: int or slice
        If the file contains several configurations, the last configuration
        will be returned by default.  Use index=n to get configuration
        number n (counting from zero).
    format: str
        Used to specify the file-format.  If not given, the
        file-format will be guessed by the *filetype* function.

    Known formats:

    =========================  ===========
    format                     short name
    =========================  ===========
    GPAW restart-file          gpw
    Dacapo netCDF output file  dacapo
    Old ASE netCDF trajectory  nc
    Virtual Nano Lab file      vnl
    ASE pickle trajectory      traj
    GPAW text output           gpaw-text
    CUBE file                  cube
    XCrySDen Structure File    xsf  
    Dacapo text output         dacapo-text
    XYZ-file                   xyz
    VASP POSCAR/CONTCAR file   vasp
    VASP OUTCAR file           vasp_out
    Protein Data Bank          pdb
    FHI-aims geometry file     aims
    FHI-aims output file       aims_out
    VTK XML Image Data         vti
    VTK XML Structured Grid    vts
    VTK XML Unstructured Grid  vtu
    TURBOMOLE coord file       tmol
    exciting input             exi
    AtomEye configuration      cfg
    WIEN2k structure file      struct
    DftbPlus input file        dftb
    SYBYL mol file	       mol2
    =========================  ===========

    """
    if isinstance(filename, str):
        p = filename.rfind('@')
        if p != -1:
            try:
                index = string2index(filename[p + 1:])
            except ValueError:
                pass
            else:
                filename = filename[:p]

    if isinstance(index, str):
        index = string2index(index)

    if format is None:
        format = filetype(filename)

    if format.startswith('gpw'):
        import gpaw
        r = gpaw.io.open(filename, 'r')
        positions = r.get('CartesianPositions') * Bohr
        numbers = r.get('AtomicNumbers')
        cell = r.get('UnitCell') * Bohr
        pbc = r.get('BoundaryConditions')
        tags = r.get('Tags')
        magmoms = r.get('MagneticMoments')

        atoms = Atoms(positions=positions, numbers=numbers, cell=cell, pbc=pbc)
        if tags.any():
            atoms.set_tags(tags)
        if magmoms.any():
            atoms.set_initial_magnetic_moments(magmoms)

        return atoms

    if format == 'exi':
        from ase_ext.io.exciting import read_exciting
        return read_exciting(filename, index)

    if format == 'mol2':
        from ase_ext.io.mol2 import read_mol2
        return read_mol2(filename, index)

    if format == 'xyz':
        from ase_ext.io.xyz import read_xyz
        return read_xyz(filename, index)

    if format == 'quipxyz':
        from ase_ext.io.quipxyz import read_xyz
        return read_xyz(filename, index)

    if format == 'traj':
        from ase_ext.io.trajectory import read_trajectory
        return read_trajectory(filename, index)

    if format == 'cube':
        from ase_ext.io.cube import read_cube
        return read_cube(filename, index)

    if format == 'nc':
        from ase_ext.io.netcdf import read_netcdf
        return read_netcdf(filename, index)

    if format == 'gpaw-text':
        from ase_ext.io.gpawtext import read_gpaw_text
        return read_gpaw_text(filename, index)

    if format == 'dacapo-text':
        from ase_ext.io.dacapo import read_dacapo_text
        return read_dacapo_text(filename)

    if format == 'dacapo':
        from ase_ext.io.dacapo import read_dacapo
        return read_dacapo(filename)

    if format == 'xsf':
        from ase_ext.io.xsf import read_xsf
        return read_xsf(filename, index)

    if format == 'vasp':
        from ase_ext.io.vasp import read_vasp
        return read_vasp(filename)

    if format == 'vasp_out':
        from ase_ext.io.vasp import read_vasp_out
        return read_vasp_out(filename, index)

    if format == 'mol':
        from ase_ext.io.mol import read_mol
        return read_mol(filename)

    if format == 'pdb':
        from ase_ext.io.pdb import read_pdb
        return read_pdb(filename)

    if format == 'cif':
        from ase_ext.io.cif import read_cif
        return read_cif(filename)

    if format == 'struct':
        from ase_ext.io.wien2k import read_struct
        return read_struct(filename)

    if format == 'vti':
        from ase_ext.io.vtkxml import read_vti
        return read_vti(filename)

    if format == 'vts':
        from ase_ext.io.vtkxml import read_vts
        return read_vts(filename)

    if format == 'vtu':
        from ase_ext.io.vtkxml import read_vtu
        return read_vtu(filename)

    if format == 'aims':
        from ase_ext.io.aims import read_aims
        return read_aims(filename)

    if format == 'aims_out':
        from ase_ext.io.aims import read_aims_output
        return read_aims_output(filename, index)

    if format == 'iwm':
        from ase_ext.io.iwm import read_iwm
        return read_iwm(filename)

    if format == 'Cmdft':
        from ase_ext.io.cmdft import read_I_info
        return read_I_info(filename)

    if format == 'tmol':
        from ase_ext.io.turbomole import read_turbomole
        return read_turbomole(filename)

    if format == 'cfg':
        from ase_ext.io.cfg import read_cfg
        return read_cfg(filename)

    if format == 'dftb':
        from ase_ext.io.dftb import read_dftb
        return read_dftb(filename)

    if format == 'sdf':
        from ase_ext.io.sdf import read_sdf
        return read_sdf(filename)

    raise RuntimeError('File format descriptor ' + format + ' not recognized!')
Exemplo n.º 28
0
def read_netcdf(filename, index=-1):
    nc = NetCDFFile(filename)
    dims = nc.dimensions
    vars = nc.variables

    positions = vars['CartesianPositions']
    numbers = vars['AtomicNumbers'][:]
    pbc = vars['BoundaryConditions'][:]
    cell = vars['UnitCell']
    tags = vars['Tags'][:]
    if not tags.any():
        tags = None
    magmoms = vars['MagneticMoments'][:]
    if not magmoms.any():
        magmoms = None

    nimages = positions.shape[0]

    attach_calculator = False
    if 'PotentialEnergy' in vars:
        energy = vars['PotentialEnergy']
        attach_calculator = True
    else:
        energy = nimages * [None]

    if 'CartesianForces' in vars:
        forces = vars['CartesianForces']
        attach_calculator = True
    else:
        forces = nimages * [None]

    if 'Stress' in vars:
        stress = vars['Stress']
        attach_calculator = True
    else:
        stress = nimages * [None]

    if isinstance(index, int):
        indices = [index]
    else:
        indices = list(range(nimages))[index]

    images = []
    for i in indices:
        atoms = Atoms(positions=positions[i],
                      numbers=numbers,
                      cell=cell[i],
                      pbc=pbc,
                      tags=tags,
                      magmoms=magmoms)

        if attach_calculator:
            calc = SinglePointCalculator(energy[i], forces[i], stress[i], None,
                                         atoms)  ### Fixme magmoms
            atoms.set_calculator(calc)

        images.append(atoms)

    if isinstance(index, int):
        return images[0]
    else:
        return images
Exemplo n.º 29
0
def graphene_nanoribbon(n,
                        m,
                        side='zigzag',
                        saturated="no",
                        C_H=1.09,
                        C_C=1.42,
                        vacc=5.,
                        magnetic=None,
                        initial_mag=1.12,
                        sheet=False,
                        main_element='C',
                        satur_element='H'):
    """Create a graphene nanoribbon.

    Creates a graphene nanoribbon in the x-z plane, with the nanoribbon
    running along the z axis.

    Parameters:

    n: The width of the nanoribbon

    m: The length of the nanoribbon.

    side ('zigzag'): The orientation of the ribbon.  Must be either 'zigzag'
    or 'armchair'.

    saturated (no/yes/side):  If yes, hydrogen atoms are placed along the edges. If side, then only 2 sides are saturated.

    C_H: Carbon-hydrogen bond length.  Default: 1.09 Angstrom

    C_C: Carbon-carbon bond length.  Default: 1.42 Angstrom.

    vacc:  Amount of vacuum.  Default 5 Angstrom.

    magnetic:  Make the edges magnetic.

    initial_mag: Magnitude of magnetic moment if magnetic=True.

    sheet:  If true, make an infinite sheet instead of a ribbon.
    """
    #This function creates the coordinates for a graphene nanoribbon,
    #n is width, m is length

    b = sqrt(3) * C_C / 4
    arm_unit = Atoms(main_element + '4',
                     pbc=(1, 0, 1),
                     cell=[4 * b, vacc, 3 * C_C])
    arm_unit.positions = [[0, 0, 0], [b * 2, 0, C_C / 2.],
                          [b * 2, 0, 3 * C_C / 2.], [0, 0, 2 * C_C]]
    zz_unit = Atoms(main_element + '2',
                    pbc=(1, 0, 1),
                    cell=[3 * C_C / 2., vacc, b * 4])
    zz_unit.positions = [[0, 0, 0], [C_C / 2., 0, b * 2]]
    atoms = Atoms()
    tol = 1e-4
    if sheet:
        vacc2 = 0.0
    else:
        vacc2 = vacc
    if side == 'zigzag':
        edge_index0 = np.arange(m) * 2 + 1
        edge_index1 = (n - 1) * m * 2 + np.arange(m) * 2
        edge_index2 = []
        edge_index3 = []
        for i in range(0, n, 2):
            edge_index2.append(i * m * 2)
            edge_index3.append((i + 1) * m * 2 - 1)
            if i > 1:
                edge_index2.append(i * m * 2 - 1)
                edge_index3.append(i * m * 2 - 2)
        edge_index2 = np.array(edge_index2)
        edge_index3 = np.array(edge_index3)
        if magnetic:
            mms = np.zeros(m * n * 2)
            for i in edge_index0:
                mms[i] = initial_mag
            for i in edge_index1:
                mms[i] = -initial_mag

        for i in range(n):
            layer = zz_unit.repeat((1, 1, m))
            layer.positions[:, 0] -= 3 * C_C / 2 * i
            if i % 2 == 1:
                layer.positions[:, 2] += 2 * b
                layer[-1].position[2] -= b * 4 * m
            atoms += layer
        if magnetic:
            atoms.set_initial_magnetic_moments(mms)
        if saturated != "no":
            H_atoms0 = Atoms(satur_element + str(m))
            H_atoms0.positions = atoms[edge_index0].positions
            H_atoms0.positions[:, 0] += C_H
            H_atoms1 = Atoms(satur_element + str(m))
            H_atoms1.positions = atoms[edge_index1].positions
            H_atoms1.positions[:, 0] -= C_H
            H_atoms2 = Atoms(satur_element + str(n))
            H_atoms2.positions = atoms[edge_index2].positions
            H_atoms2.positions[:, 2] -= C_H
            H_atoms3 = Atoms(satur_element + str(n))
            H_atoms3.positions = atoms[edge_index3].positions
            H_atoms3.positions[:, 2] += C_H
            if saturated == "yes":
                atoms += H_atoms0 + H_atoms1 + H_atoms2 + H_atoms3
            if saturated == "side":
                atoms += H_atoms0 + H_atoms1
        atoms.cell = [n * 3 * C_C / 2 + vacc2, vacc + 5.0, m * 4 * b]

    elif side == 'armchair':
        for i in range(n):
            layer = arm_unit.repeat((1, 1, m))
            layer.positions[:, 0] -= 4 * b * i
            atoms += layer
        atoms.cell = [b * 4 * n + vacc2, vacc, 3 * C_C * m]
    atoms.center()
    atoms.set_pbc([sheet, False, True])
    return atoms
Exemplo n.º 30
0
def add_adsorbate(slab, adsorbate, height, position=(0, 0), offset=None,
                  mol_index=0):
    """Add an adsorbate to a surface.

    This function adds an adsorbate to a slab.  If the slab is
    produced by one of the utility functions in ase_ext.lattice.surface, it
    is possible to specify the position of the adsorbate by a keyword
    (the supported keywords depend on which function was used to
    create the slab).

    If the adsorbate is a molecule, the atom indexed by the mol_index
    optional argument is positioned on top of the adsorption position
    on the surface, and it is the responsibility of the user to orient
    the adsorbate in a sensible way.

    This function can be called multiple times to add more than one
    adsorbate.

    Parameters:

    slab: The surface onto which the adsorbate should be added.

    adsorbate:  The adsorbate. Must be one of the following three types:
        A string containing the chemical symbol for a single atom.
        An atom object.
        An atoms object (for a molecular adsorbate).

    height: Height above the surface.

    position: The x-y position of the adsorbate, either as a tuple of
        two numbers or as a keyword (if the surface is produced by one
        of the functions in ase_ext.lattice.surfaces).

    offset (default: None): Offsets the adsorbate by a number of unit
        cells. Mostly useful when adding more than one adsorbate.

    mol_index (default: 0): If the adsorbate is a molecule, index of
        the atom to be positioned above the location specified by the
        position argument.

    Note *position* is given in absolute xy coordinates (or as
    a keyword), whereas offset is specified in unit cells.  This
    can be used to give the positions in units of the unit cell by
    using *offset* instead.
    
    """
    info = slab.adsorbate_info
    if 'cell' not in info:
        info['cell'] = slab.get_cell()[:2,:2]

    
    pos = np.array([0.0, 0.0])  # (x, y) part
    spos = np.array([0.0, 0.0]) # part relative to unit cell
    if offset is not None:
        spos += np.asarray(offset, float)

    if isinstance(position, str):
        # A site-name:
        if 'sites' not in info:
            raise TypeError('If the atoms are not made by an ' +
                            'ase.lattice.surface function, ' +
                            'position cannot be a name.')
        if position not in info['sites']:
            raise TypeError('Adsorption site %s not supported.' % position)
        spos += info['sites'][position]
    else:
        pos += position

    pos += np.dot(spos, info['cell'])

    # Convert the adsorbate to an Atoms object
    if isinstance(adsorbate, Atoms):
        ads = adsorbate
    elif isinstance(adsorbate, Atom):
        ads = Atoms([adsorbate])
    else:
        # Hope it is a useful string or something like that
        ads = Atoms(adsorbate)

    # Get the z-coordinate:
    try:
        a = info['top layer atom index']
    except KeyError:
        a = slab.positions[:, 2].argmax()
        info['top layer atom index']= a
    z = slab.positions[a, 2] + height

    # Move adsorbate into position
    ads.translate([pos[0], pos[1], z] - ads.positions[mol_index])

    # Attach the adsorbate
    slab.extend(ads)