Exemplo n.º 1
0
def create_surface(sc_shape):
    from scipy.spatial import cKDTree as KDTree
    atoms = bulk("Al") * sc_shape
    if sc_shape[2] % 2 != 0:
        raise ValueError("The third direction has to be divisible by 2!")
    num_layers = int(sc_shape[2] / 2)

    # Create a cut such that a3 vector is the normal vector
    slab = cut(atoms,
               a=(1, 0, 0),
               b=(0, 1, 0),
               c=(0, 0, 1),
               nlayers=num_layers)
    tags, _ = get_layers(slab, (1, -1, 0))
    for tag, atom in zip(tags, slab):
        if tag % 2 == 0:
            atom.symbol = "Mg"
        else:
            atom.symbol = "Si"
    tree = KDTree(atoms.get_positions())
    used_indices = []
    for atom in slab:
        _, closest_indx = tree.query(atom.position)
        if closest_indx in used_indices:
            raise RuntimeError("Two atoms are mapped onto the same index!")
        atoms[closest_indx].symbol = atom.symbol
        used_indices.append(closest_indx)
    return atoms
Exemplo n.º 2
0
def main():
    aluminum = build.bulk("Al", crystalstructure="fcc") * 4
    print(len(aluminum))

    # Extract 3 111 planes
    planes = build.cut(aluminum, (1, -1, 0), (1, 1, -2), nlayers=3)
    view(planes, viewer="Avogadro")
def main():
    atoms = read("data/mgsi100_fully_relaxed.xyz")

    # Create slab
    plane1 = atoms
    slab = cut(plane1, a=(1, 0, -1), b=(0, 1, 0))

    # Plane perp to planes
    plane = slab * (6, 1, 6)
    write("data/plate_perp2layers.xyz", plane)

    # Plane parallel to planes
    plane = slab * (6, 6, 1)
    write("data/plate_par2planes.xyz", plane)

    # Needles perp to planes
    needle = slab * (6, 2, 2)
    write("data/needle_perp2planes.xyz", needle)
    view(needle)

    # Needle par to planes
    needle = slab * (2, 2, 6)
    write("data/needle_par2planes.xyz", needle)
    view(needle)
    view(slab)
Exemplo n.º 4
0
def create_clathrate(size):
    x = io.read('jp111328v_si_001.cif')
    # need to reorder things for the tip4p water model, so read the cif file to do so
    with open('jp111328v_si_001.cif') as f:
        txt = f.readlines()
    read = False
    nums = []
    for line in txt:
        if line.startswith('_geom_bond_site_symmetry_2'):
            read = True
            continue
        elif read and line.startswith('O'):
            nums.append(int(line.split()[1][1:]))
        else:
            continue
    # now we reorder, because we need our water molecules to list O
    # first, followed by the two Hs, for the tip4p model
    # We also replace methane hydrogens with S as a placeholder in ase
    acc = x[0:1]
    acc.append(x[nums[0] - 1])
    acc.append(x[nums[1] - 1])
    for i in range(1, 46):
        acc.append(x[i])
        acc.append(x[nums[2 * i] - 1])
        acc.append(x[nums[2 * i + 1] - 1])
    for i in range(138, len(x)):
        if x[i].symbol == 'C':
            acc.append(x[i])
        else:
            new_xi = x[i]
            new_xi.symbol = 'S'  # replace methane hydrogen with s as placeholder
            acc.append(new_xi)
    # now let's create our supercell; need to use cut in order to make sure
    # atoms aren't removed
    cell_len = acc.cell[0, 0]
    m = np.identity(3) * size
    mh_super = cut(acc, m[0], m[1], m[2], tolerance=1e-5)
    # now let's move atoms if they were previously across the pbc
    i = 0
    while i < len(mh_super):
        if mh_super.numbers[i] == 8:
            natoms = 3
        elif mh_super.numbers[i] == 6:
            natoms = 5
        else:
            raise ValueError
        for d in range(3):
            pos_d = mh_super.positions[i:i + natoms, d]
            max_d = np.max(pos_d)
            pos_d[np.abs(pos_d - max_d) > cell_len / 2] += acc.cell[d, d]
        i += natoms
    mh_super.positions[:, 1] += cell_len / 2
    mh_super.positions[:, 1] %= (cell_len * size[1])
    # scale positions based off Tung et al, 2011
    mh_super.cell *= 23.74 / (2 * cell_len)
    mh_super.positions *= 23.74 / (2 * cell_len)
    return mh_super
Exemplo n.º 5
0
def passivate_zinc_blende_slab_nonprimitive(slab, passivant, direction):
    """
    The idea is to have the slabs passivated with passivant in the direction.
    right now only works for the 110 direction
    default zinc blende sturcture should be input as slab if not in the 001 direction
    """
    import numpy as np

    if direction == (0, 0, 1):
        return passivate_zincblende_slab_001(slab, passivant)
    else:
        c = np.array(direction, dtype=float)
        b = np.array((0, 0, 1), dtype=float)
        a = np.cross(b, direction)
        y = cut(slab, a, b, nlayers=6)
        # y.edit()
        # coords = y.get_positions()

        # Fixing the amount that you want to displace by
        d = 2  # displace by this amount
        xy = .75

        if passivant == "H":
            atoms_up = [
                20, 15, 23, 22
            ]  # These are atoms that were coreated using an extra layer at the top
            atoms_down = [
                0, 6, 3, 2
            ]  # These are atoms that were coreated using an extra layer at the bottom
            for num in atoms_up:
                y[num].symbol = f"{passivant}"
                # Since in the 110 direction we have only the one bond to take care of in zincblende we need only replace the atoms with all of these
                y[num].position = [
                    y[num].position[0], y[num].position[1], y[num].position[2]
                ]
            for num in atoms_down:
                y[num].symbol = f"{passivant}"
                # Since in the 110 direction we have only the one bond to take care of in zincblende we need only replace the atoms with all of these
                y[num].position = [
                    y[num].position[0], y[num].position[1], y[num].position[2]
                ]
        elif passivant == "O":
            pass
        return y
Exemplo n.º 6
0
def supercell(atoms, max_cell_length=12, bulk=True):
    """Returns atoms in supercell from the unit cell.

    Parameters
    ----------
        atoms : object
        max_cell_length : desiered maximum unit-cell-parameter
        bulk : Whether it is a bulk system.
    """
    cell = atoms.get_cell()
    nkx = int(round(max_cell_length/np.linalg.norm(cell[0]),0))
    nky = int(round(max_cell_length/np.linalg.norm(cell[1]),0))
    if bulk == True:
        nkz = int(round(max_cell_length/np.linalg.norm(cell[2]),0))
    else:
        nkz = 1
    new_atoms = cut(atoms, a=(nkx,0,0), b=(0,nky,0), c=(0,0,nkz), clength=None, origo=(0, 0, 0),
                  nlayers=None, extend=1.0, tolerance=0.01, maxatoms=None)
    return(new_atoms)
Exemplo n.º 7
0
 def gamaToObtuseangle(self):  # 把晶胞betta变成钝角方便比较切应变, 注:不改变self.crys
     cell_par_lst = self.crys.get_cell_lengths_and_angles()
     if cell_par_lst[5] >= 90:
         return self.crys
     else:
         vector = self.crys.get_cell()
         b = cell_par_lst[1]
         gama = cell_par_lst[5]
         mubiaob = [
             b * np.cos((180 - gama) / 180 * np.pi), b * np.sin(
                 (180 - gama) / 180 * np.pi), 0
         ]
         A = vector.T
         r = np.linalg.solve(A, mubiaob)  # 求解线性方程组,直角坐标系下----用晶胞坐标系表示
         real = cut(self.crys,
                    a=[1, 0, 0],
                    b=r.T,
                    c=[0, 0, 1],
                    origo=(0, 0, 0))
         return real
Exemplo n.º 8
0
def make_supercell(prim, P):
    """Generate a supercell by applying a general transformation (*P*) to
    the input configuration (*prim*).

    The transformation is described by a 3x3 integer matrix
    `\mathbf{P}`. Specifically, the new cell metric
    `\mathbf{h}` is given in terms of the metric of the input
    configuraton `\mathbf{h}_p` by `\mathbf{P h}_p =
    \mathbf{h}`.

    Internally this function uses the :func:`~ase.build.cut` function.

    Parameters:

    prim: ASE Atoms object
        Input configuration.
    P: 3x3 integer matrix
        Transformation matrix `\mathbf{P}`.

    """

    from ase.build import cut
    return cut(prim, P[0], P[1], P[2])
Exemplo n.º 9
0
def make_supercell(prim, P):
    """Generate a supercell by applying a general transformation (*P*) to
    the input configuration (*prim*).

    The transformation is described by a 3x3 integer matrix
    `\mathbf{P}`. Specifically, the new cell metric
    `\mathbf{h}` is given in terms of the metric of the input
    configuraton `\mathbf{h}_p` by `\mathbf{P h}_p =
    \mathbf{h}`.

    Internally this function uses the :func:`~ase.build.cut` function.

    Parameters:

    prim: ASE Atoms object
        Input configuration.
    P: 3x3 integer matrix
        Transformation matrix `\mathbf{P}`.

    """

    from ase.build import cut
    return cut(prim, P[0], P[1], P[2])
Exemplo n.º 10
0
"""Test the ase.geometry module and ase.build.cut() function."""

from __future__ import division

import numpy as np

from ase.build import cut, bulk
from ase.geometry import (get_layers, wrap_positions,
                          crystal_structure_from_cell)
from ase.spacegroup import crystal, get_spacegroup

al = crystal('Al', [(0, 0, 0)], spacegroup=225, cellpar=4.05)

# Cut out slab of 5 Al(001) layers
al001 = cut(al, nlayers=5)
correct_pos = np.array([[0., 0., 0.],
                        [0., 0.5, 0.2],
                        [0.5, 0., 0.2],
                        [0.5, 0.5, 0.],
                        [0., 0., 0.4],
                        [0., 0.5, 0.6],
                        [0.5, 0., 0.6],
                        [0.5, 0.5, 0.4],
                        [0., 0., 0.8],
                        [0.5, 0.5, 0.8]])
assert np.allclose(correct_pos, al001.get_scaled_positions())

# Check layers along 001
tags, levels = get_layers(al001, (0, 0, 1))
assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4])
assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1])
Exemplo n.º 11
0
def crystal(symbols=None,
            basis=None,
            occupancies=None,
            spacegroup=1,
            setting=1,
            cell=None,
            cellpar=None,
            ab_normal=(0, 0, 1),
            a_direction=None,
            size=(1, 1, 1),
            onduplicates='warn',
            symprec=0.001,
            pbc=True,
            primitive_cell=False,
            **kwargs):
    """Create an Atoms instance for a conventional unit cell of a
    space group.

    Parameters:

    symbols : str | sequence of str | sequence of Atom | Atoms
        Element symbols of the unique sites.  Can either be a string
        formula or a sequence of element symbols. E.g. ('Na', 'Cl')
        and 'NaCl' are equivalent.  Can also be given as a sequence of
        Atom objects or an Atoms object.
    basis : list of scaled coordinates
        Positions of the unique sites corresponding to symbols given
        either as scaled positions or through an atoms instance.  Not
        needed if *symbols* is a sequence of Atom objects or an Atoms
        object.
    occupancies : list of site occupancies
        Occupancies of the unique sites. Defaults to 1.0 and thus no mixed
        occupancies are considered if not explicitly asked for. If occupancies
        are given, the most dominant species will yield the atomic number.
    spacegroup : int | string | Spacegroup instance
        Space group given either as its number in International Tables
        or as its Hermann-Mauguin symbol.
    setting : 1 | 2
        Space group setting.
    cell : 3x3 matrix
        Unit cell vectors.
    cellpar : [a, b, c, alpha, beta, gamma]
        Cell parameters with angles in degree. Is not used when `cell`
        is given.
    ab_normal : vector
        Is used to define the orientation of the unit cell relative
        to the Cartesian system when `cell` is not given. It is the
        normal vector of the plane spanned by a and b.
    a_direction : vector
        Defines the orientation of the unit cell a vector. a will be
        parallel to the projection of `a_direction` onto the a-b plane.
    size : 3 positive integers
        How many times the conventional unit cell should be repeated
        in each direction.
    onduplicates : 'keep' | 'replace' | 'warn' | 'error'
        Action if `basis` contain symmetry-equivalent positions:
            'keep'    - ignore additional symmetry-equivalent positions
            'replace' - replace
            'warn'    - like 'keep', but issue an UserWarning
            'error'   - raises a SpacegroupValueError
    symprec : float
        Minimum "distance" betweed two sites in scaled coordinates
        before they are counted as the same site.
    pbc : one or three bools
        Periodic boundary conditions flags.  Examples: True,
        False, 0, 1, (1, 1, 0), (True, False, False).  Default
        is True.
    primitive_cell : bool
        Wheter to return the primitive instead of the conventional
        unit cell.

    Keyword arguments:

    All additional keyword arguments are passed on to the Atoms
    constructor.  Currently, probably the most useful additional
    keyword arguments are `info`, `constraint` and `calculator`.

    Examples:

    Two diamond unit cells (space group number 227)

    >>> diamond = crystal('C', [(0,0,0)], spacegroup=227,
    ...     cellpar=[3.57, 3.57, 3.57, 90, 90, 90], size=(2,1,1))
    >>> ase.view(diamond)  # doctest: +SKIP

    A CoSb3 skutterudite unit cell containing 32 atoms

    >>> skutterudite = crystal(('Co', 'Sb'),
    ...     basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)],
    ...     spacegroup=204, cellpar=[9.04, 9.04, 9.04, 90, 90, 90])
    >>> len(skutterudite)
    32
    """
    sg = Spacegroup(spacegroup, setting)
    if (not isinstance(symbols, basestring)
            and hasattr(symbols, '__getitem__') and len(symbols) > 0
            and isinstance(symbols[0], ase.Atom)):
        symbols = ase.Atoms(symbols)
    if isinstance(symbols, ase.Atoms):
        basis = symbols
        symbols = basis.get_chemical_symbols()
    if isinstance(basis, ase.Atoms):
        basis_coords = basis.get_scaled_positions()
        if cell is None and cellpar is None:
            cell = basis.cell
        if symbols is None:
            symbols = basis.get_chemical_symbols()
    else:
        basis_coords = np.array(basis, dtype=float, copy=False, ndmin=2)

    if occupancies is not None:
        occupancies_dict = {}

        for index, coord in enumerate(basis_coords):
            # Compute all distances and get indices of nearest atoms
            dist = spatial.distance.cdist(coord.reshape(1, 3), basis_coords)
            indices_dist = np.flatnonzero(dist < symprec)

            occ = {symbols[index]: occupancies[index]}

            # Check nearest and update occupancy
            for index_dist in indices_dist:
                if index == index_dist:
                    continue
                else:
                    occ.update({symbols[index_dist]: occupancies[index_dist]})

            occupancies_dict[index] = occ.copy()

    sites, kinds = sg.equivalent_sites(basis_coords,
                                       onduplicates=onduplicates,
                                       symprec=symprec)

    # this is needed to handle deuterium masses
    masses = None
    if 'masses' in kwargs:
        masses = kwargs['masses'][kinds]
        del kwargs['masses']

    symbols = parse_symbols(symbols)

    if occupancies is None:
        symbols = [symbols[i] for i in kinds]
    else:
        # make sure that we put the dominant species there
        symbols = [
            sorted(occupancies_dict[i].items(), key=lambda x: x[1])[-1][0]
            for i in kinds
        ]

    if cell is None:
        cell = cellpar_to_cell(cellpar, ab_normal, a_direction)

    info = dict(spacegroup=sg)
    if primitive_cell:
        info['unit_cell'] = 'primitive'
    else:
        info['unit_cell'] = 'conventional'

    if 'info' in kwargs:
        info.update(kwargs['info'])

    if occupancies is not None:
        info['occupancy'] = occupancies_dict

    kwargs['info'] = info

    atoms = ase.Atoms(symbols,
                      scaled_positions=sites,
                      cell=cell,
                      pbc=pbc,
                      masses=masses,
                      **kwargs)

    #  if all occupancies are 1, no partial occupancy present
    if occupancies:
        if not all([occ == 1 for occ in occupancies]):
            # use tags to identify sites, and in particular the occupancy
            atoms.set_tags(kinds)

    if isinstance(basis, ase.Atoms):
        for name in basis.arrays:
            if not atoms.has(name):
                array = basis.get_array(name)
                atoms.new_array(name, [array[i] for i in kinds],
                                dtype=array.dtype,
                                shape=array.shape[1:])

    if primitive_cell:
        from ase.build import cut
        prim_cell = sg.scaled_primitive_cell

        # Preserve calculator if present:
        calc = atoms.calc
        atoms = cut(atoms, a=prim_cell[0], b=prim_cell[1], c=prim_cell[2])
        atoms.calc = calc

    if size != (1, 1, 1):
        atoms = atoms.repeat(size)
    return atoms
Exemplo n.º 12
0
def orthogonalize_cell(atoms: Atoms,
                       max_repetitions: int = 5,
                       return_transform: bool = False,
                       transform: Union[bool, str] = True,
                       tolerance=0.01):
    """
    Make the cell of an ASE atoms object orthogonal. This is accomplished by repeating the cell until lattice vectors
    are close to the three principal Cartesian directions. If the structure is not exactly orthogonal after the
    structure is repeated by a given maximum the remaining difference will be made up by applying strain.

    Parameters
    ----------
    atoms : ASE atoms object
        The non-orthogonal atoms object.
    max_repetitions : int
        The maximum number of repetions allowed. Increase this to allow more repetitions and hence less strain.
    return_transform : bool
        If true, return the transformations that were applied to make the atoms orthogonal.
    transform : bool
        If false no transformation is applied to make the cell orthogonal, hence a non-orthogonal cell may be returned.


    Returns
    -------
    atoms : ASE atoms object
        The orthogonal atoms.
    transform : tuple of arrays
        The applied transform in the form the euler angles
    """
    eps = 1e-12

    zero_vectors = np.linalg.norm(atoms.cell, axis=0) < eps

    if zero_vectors.sum() > 1:
        raise RuntimeError(
            "two or more lattice vectors of the provided Atoms has no length")
    elif zero_vectors.sum() == 1:
        atoms.center(axis=np.where(zero_vectors)[0], vacuum=tolerance + eps)

    k = np.arange(-max_repetitions, max_repetitions + 1)
    l = np.arange(-max_repetitions, max_repetitions + 1)
    m = np.arange(-max_repetitions, max_repetitions + 1)

    a, b, c = atoms.cell

    vectors = np.abs(((k[:, None] * a[None])[:, None, None] +
                      (l[:, None] * b[None])[None, :, None] +
                      (m[:, None] * c[None])[None, None, :]))

    norm = np.linalg.norm(vectors, axis=-1)
    nonzero = norm > eps
    norm[nonzero == 0] = eps

    new_vectors = []
    for i in range(3):
        angles = vectors[..., i] / norm

        optimal = np.abs(angles.max() - angles < eps)

        optimal = np.where(optimal * nonzero)
        n = np.linalg.norm(vectors[optimal], axis=1)

        j = np.argmin(n)
        new_vector = np.array(
            [k[optimal[0][j]], l[optimal[1][j]], m[optimal[2][j]]])

        new_vector = np.sign(np.dot(new_vector, atoms.cell)[i]) * new_vector
        new_vectors.append(new_vector)

    atoms = cut(atoms, *new_vectors, tolerance=tolerance)

    cell = Cell.new(np.linalg.norm(atoms.cell, axis=0))
    A = np.linalg.solve(atoms.cell.complete(), cell.complete())

    if transform is True:
        atoms.positions[:] = np.dot(atoms.positions, A)
        atoms.cell[:] = cell

    elif transform == 'raise':
        if not is_cell_orthogonal(atoms):
            raise RuntimeError()

    atoms = shrink_cell(atoms, 2)

    if return_transform and transform:
        rotation, zoom, shear = decompose_affine_transform(A)
        return atoms, (np.array(rotation_matrix_to_euler(rotation)), zoom,
                       shear)
    else:
        return atoms
Exemplo n.º 13
0
"""Test the ase.geometry module and ase.build.cut() function."""

from __future__ import division

import numpy as np

from ase.build import cut, bulk
from ase.geometry import (get_layers, wrap_positions,
                          crystal_structure_from_cell)
from ase.spacegroup import crystal

al = crystal('Al', [(0, 0, 0)], spacegroup=225, cellpar=4.05)

# Cut out slab of 5 Al(001) layers
al001 = cut(al, nlayers=5)
correct_pos = np.array([[0., 0., 0.],
                        [0., 0.5, 0.2],
                        [0.5, 0., 0.2],
                        [0.5, 0.5, 0.],
                        [0., 0., 0.4],
                        [0., 0.5, 0.6],
                        [0.5, 0., 0.6],
                        [0.5, 0.5, 0.4],
                        [0., 0., 0.8],
                        [0.5, 0.5, 0.8]])
assert np.allclose(correct_pos, al001.get_scaled_positions())

# Check layers along 001
tags, levels = get_layers(al001, (0, 0, 1))
assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4])
assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1])
import numpy.testing as npt

from ase.build import bulk, cut
from icet import ClusterSpace

# initializes cluster space and get the internal primitive structure
prim = bulk('Au', a=4.0, crystalstructure='hcp')
subelements = ['Au', 'Pd']
cutoffs = [0.0]

cs = ClusterSpace(prim, cutoffs, subelements)
structure_prim = cs.primitive_structure

# create a supercell using permutation matrix
p_trial = [[1, 0, 0], [0, 1, 5], [0, 0, 2]]
supercell = cut(structure_prim, p_trial[0], p_trial[1], p_trial[2])

# setup cartesian input to generate a random population
cartesian_product_input = []
for i in range(len(supercell)):
    cartesian_product_input.append(['Pd', 'Au'])

# loop over element combinations and assert expected singlet value
for subset in itertools.product(*cartesian_product_input):
    for atom, element in zip(supercell, subset):
        atom.symbol = element
    cv = cs.get_cluster_vector(supercell)
    expected_singlet = -supercell.get_chemical_symbols().count('Pd')
    expected_singlet += supercell.get_chemical_symbols().count('Au')
    expected_singlet /= len(supercell)
    npt.assert_almost_equal(cv[1], expected_singlet)
Exemplo n.º 15
0
def crystal(symbols=None, basis=None, spacegroup=1, setting=1,
            cell=None, cellpar=None,
            ab_normal=(0, 0, 1), a_direction=None, size=(1, 1, 1),
            onduplicates='warn', symprec=0.001,
            pbc=True, primitive_cell=False, **kwargs):
    """Create an Atoms instance for a conventional unit cell of a
    space group.

    Parameters:

    symbols : str | sequence of str | sequence of Atom | Atoms
        Element symbols of the unique sites.  Can either be a string
        formula or a sequence of element symbols. E.g. ('Na', 'Cl')
        and 'NaCl' are equivalent.  Can also be given as a sequence of
        Atom objects or an Atoms object.
    basis : list of scaled coordinates
        Positions of the unique sites corresponding to symbols given
        either as scaled positions or through an atoms instance.  Not
        needed if *symbols* is a sequence of Atom objects or an Atoms
        object.
    spacegroup : int | string | Spacegroup instance
        Space group given either as its number in International Tables
        or as its Hermann-Mauguin symbol.
    setting : 1 | 2
        Space group setting.
    cell : 3x3 matrix
        Unit cell vectors.
    cellpar : [a, b, c, alpha, beta, gamma]
        Cell parameters with angles in degree. Is not used when `cell`
        is given.
    ab_normal : vector
        Is used to define the orientation of the unit cell relative
        to the Cartesian system when `cell` is not given. It is the
        normal vector of the plane spanned by a and b.
    a_direction : vector
        Defines the orientation of the unit cell a vector. a will be
        parallel to the projection of `a_direction` onto the a-b plane.
    size : 3 positive integers
        How many times the conventional unit cell should be repeated
        in each direction.
    onduplicates : 'keep' | 'replace' | 'warn' | 'error'
        Action if `basis` contain symmetry-equivalent positions:
            'keep'    - ignore additional symmetry-equivalent positions
            'replace' - replace
            'warn'    - like 'keep', but issue an UserWarning
            'error'   - raises a SpacegroupValueError
    symprec : float
        Minimum "distance" betweed two sites in scaled coordinates
        before they are counted as the same site.
    pbc : one or three bools
        Periodic boundary conditions flags.  Examples: True,
        False, 0, 1, (1, 1, 0), (True, False, False).  Default
        is True.
    primitive_cell : bool
        Wheter to return the primitive instead of the conventional
        unit cell.

    Keyword arguments:

    All additional keyword arguments are passed on to the Atoms
    constructor.  Currently, probably the most useful additional
    keyword arguments are `info`, `constraint` and `calculator`.

    Examples:

    Two diamond unit cells (space group number 227)

    >>> diamond = crystal('C', [(0,0,0)], spacegroup=227,
    ...     cellpar=[3.57, 3.57, 3.57, 90, 90, 90], size=(2,1,1))
    >>> ase.view(diamond)  # doctest: +SKIP

    A CoSb3 skutterudite unit cell containing 32 atoms

    >>> skutterudite = crystal(('Co', 'Sb'),
    ...     basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)],
    ...     spacegroup=204, cellpar=[9.04, 9.04, 9.04, 90, 90, 90])
    >>> len(skutterudite)
    32
    """
    sg = Spacegroup(spacegroup, setting)
    if (not isinstance(symbols, str) and
        hasattr(symbols, '__getitem__') and
        len(symbols) > 0 and
        isinstance(symbols[0], ase.Atom)):
        symbols = ase.Atoms(symbols)
    if isinstance(symbols, ase.Atoms):
        basis = symbols
        symbols = basis.get_chemical_symbols()
    if isinstance(basis, ase.Atoms):
        basis_coords = basis.get_scaled_positions()
        if cell is None and cellpar is None:
            cell = basis.cell
        if symbols is None:
            symbols = basis.get_chemical_symbols()
    else:
        basis_coords = np.array(basis, dtype=float, copy=False, ndmin=2)
    sites, kinds = sg.equivalent_sites(basis_coords,
                                       onduplicates=onduplicates,
                                       symprec=symprec)
    symbols = parse_symbols(symbols)
    symbols = [symbols[i] for i in kinds]
    if cell is None:
        cell = cellpar_to_cell(cellpar, ab_normal, a_direction)

    info = dict(spacegroup=sg)
    if primitive_cell:
        info['unit_cell'] = 'primitive'
    else:
        info['unit_cell'] = 'conventional'

    if 'info' in kwargs:
        info.update(kwargs['info'])
    kwargs['info'] = info

    atoms = ase.Atoms(symbols,
                      scaled_positions=sites,
                      cell=cell,
                      pbc=pbc,
                      **kwargs)

    if isinstance(basis, ase.Atoms):
        for name in basis.arrays:
            if not atoms.has(name):
                array = basis.get_array(name)
                atoms.new_array(name, [array[i] for i in kinds],
                                dtype=array.dtype, shape=array.shape[1:])

    if primitive_cell:
        from ase.build import cut
        prim_cell = sg.scaled_primitive_cell
        atoms = cut(atoms, a=prim_cell[0], b=prim_cell[1], c=prim_cell[2])

    if size != (1, 1, 1):
        atoms = atoms.repeat(size)
    return atoms
Exemplo n.º 16
0
""" 读取结构扩胞 """
pri_struct = read('POSCAR1.vasp')  # 读取原胞POSCAR结构
write(filename='poscar3.vasp', images=pri_struct,
      format='vasp')  # 默认format为文件名后缀
# # view(pri_struct)
# ase.io.vasp.write_vasp("POSCAR4x4x4.vasp", pri_struct*(4,4,4), label='444supercell',direct=True,sort=True)   # 扩胞操作
a1 = read('POSCAR4x4x4.vasp')
""" 切表面 """
# Cutsurface = cut(a1, c=(0, 0, 1), origo=(0, 0, 0), nlayers=4)    # 切(0,0,1)面,保留4层
# Cutsurface.center(vacuum=10, axis=2)   # 加真空层,axis=0是x方向,1是y方向,z是c方向
# view(Cutsurface)   # alt+x/y/z  切换视图的方向
# print(Cutsurface.cell)
# print('--------------')
# print(Cutsurface.positions)
# print('--------------')
# print(Cutsurface.numbers)
# write_poscar(Cutsurface, '001.vasp')

Cutsurface = cut(a1, c=(1, 1, 1), origo=(0, 0, 0), nlayers=4)  # 切(1,1,1)面,保留4层
Cutsurface.center(vacuum=10, axis=2)  # 加真空层
# view(Cutsurface)
# print(Cutsurface.cell)
# write_poscar(Cutsurface, '111.vasp')
# write(filename='poscar3.vasp', images=Cutsurface)

# Cutsurface = cut(a1, c=(0, 1, 1), origo=(0, 0, 0), nlayers=4)    # 切(1,1,1)面,保留4层
# Cutsurface.center(vacuum=10, axis=2)   # 加真空层
# # view(Cutsurface)
# write_poscar(Cutsurface, '011.vasp')
Exemplo n.º 17
0
def test_geometry():
    """Test the ase.geometry module and ase.build.cut() function."""

    import numpy as np

    from ase.build import cut, bulk, fcc111
    from ase.cell import Cell
    from ase.geometry import get_layers, wrap_positions
    from ase.spacegroup import crystal, get_spacegroup

    al = crystal('Al', [(0, 0, 0)], spacegroup=225, cellpar=4.05)

    # Cut out slab of 5 Al(001) layers
    al001 = cut(al, nlayers=5)
    correct_pos = np.array([[0., 0., 0.],
                            [0., 0.5, 0.2],
                            [0.5, 0., 0.2],
                            [0.5, 0.5, 0.],
                            [0., 0., 0.4],
                            [0., 0.5, 0.6],
                            [0.5, 0., 0.6],
                            [0.5, 0.5, 0.4],
                            [0., 0., 0.8],
                            [0.5, 0.5, 0.8]])
    assert np.allclose(correct_pos, al001.get_scaled_positions())

    # Check layers along 001
    tags, levels = get_layers(al001, (0, 0, 1))
    assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4])
    assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1])

    # Check layers along 101
    tags, levels = get_layers(al001, (1, 0, 1))
    assert np.allclose(tags, [0, 1, 5, 3, 2, 4, 8, 7, 6, 9])
    assert np.allclose(levels, [0.000, 0.752, 1.504, 1.880, 2.256, 2.632, 3.008,
                                3.384, 4.136, 4.888],
                       atol=0.001)

    # Check layers along 111
    tags, levels = get_layers(al001, (1, 1, 1))
    assert np.allclose(tags, [0, 2, 2, 4, 1, 5, 5, 6, 3, 7])
    assert np.allclose(levels, [0.000, 1.102, 1.929, 2.205, 2.756, 3.031, 3.858,
                                4.960],
                       atol=0.001)

    # Cut out slab of three Al(111) layers
    al111 = cut(al, (1, -1, 0), (0, 1, -1), nlayers=3)
    correct_pos = np.array([[0.5, 0., 0.],
                            [0., 0.5, 0.],
                            [0.5, 0.5, 0.],
                            [0., 0., 0.],
                            [1 / 6., 1 / 3., 1 / 3.],
                            [1 / 6., 5 / 6., 1 / 3.],
                            [2 / 3., 5 / 6., 1 / 3.],
                            [2 / 3., 1 / 3., 1 / 3.],
                            [1 / 3., 1 / 6., 2 / 3.],
                            [5 / 6., 1 / 6., 2 / 3.],
                            [5 / 6., 2 / 3., 2 / 3.],
                            [1 / 3., 2 / 3., 2 / 3.]])
    assert np.allclose(correct_pos, al111.get_scaled_positions())

    # Cut out cell including all corner and edge atoms (non-periodic structure)
    al = cut(al, extend=1.1)
    correct_pos = np.array([[0., 0., 0.],
                            [0., 2.025, 2.025],
                            [2.025, 0., 2.025],
                            [2.025, 2.025, 0.],
                            [0., 0., 4.05],
                            [2.025, 2.025, 4.05],
                            [0., 4.05, 0.],
                            [2.025, 4.05, 2.025],
                            [0., 4.05, 4.05],
                            [4.05, 0., 0.],
                            [4.05, 2.025, 2.025],
                            [4.05, 0., 4.05],
                            [4.05, 4.05, 0.],
                            [4.05, 4.05, 4.05]])
    assert np.allclose(correct_pos, al.positions)

    # Create an Ag(111)/Si(111) interface
    ag = crystal(['Ag'], basis=[(0, 0, 0)], spacegroup=225, cellpar=4.09)
    si = crystal(['Si'], basis=[(0, 0, 0)], spacegroup=227, cellpar=5.43)
    try:
        assert get_spacegroup(ag).no == 225
        assert get_spacegroup(si).no == 227
    except ImportError:
        pass

    ag111 = cut(ag, a=(4, -4, 0), b=(4, 4, -8), nlayers=5)  # noqa
    si111 = cut(si, a=(3, -3, 0), b=(3, 3, -6), nlayers=5)  # noqa
    #
    # interface = stack(ag111, si111)
    # assert len(interface) == 1000
    # assert np.allclose(interface.positions[::100],
    #                   [[  4.08125   ,  -2.040625  ,   -2.040625  ],
    #                    [  8.1625    ,   6.121875  ,  -14.284375  ],
    #                    [ 10.211875  ,   0.00875   ,    2.049375  ],
    #                    [ 24.49041667,  -4.07833333,  -16.32208333],
    #                    [ 18.37145833,  14.29020833,  -24.48166667],
    #                    [ 24.49916667,  12.25541667,  -20.39458333],
    #                    [ 18.36854167,  16.32791667,  -30.60645833],
    #                    [ 19.0575    ,   0.01166667,    5.45333333],
    #                    [ 23.13388889,   6.80888889,    1.36722222],
    #                    [ 35.3825    ,   5.45333333,  -16.31333333]])
    #

    # Test the wrap_positions function.
    positions = np.array([
        [4.0725, -4.0725, -1.3575],
        [1.3575, -1.3575, -1.3575],
        [2.715, -2.715, 0.],
        [4.0725, 1.3575, -1.3575],
        [0., 0., 0.],
        [2.715, 2.715, 0.],
        [6.7875, -1.3575, -1.3575],
        [5.43, 0., 0.]])
    cell = np.array([[5.43, 5.43, 0.0], [5.43, -5.43, 0.0], [0.00, 0.00, 40.0]])
    positions += np.array([6.1, -0.1, 10.1])
    result_positions = wrap_positions(positions=positions, cell=cell)
    correct_pos = np.array([
        [4.7425, 1.2575, 8.7425],
        [7.4575, -1.4575, 8.7425],
        [3.385, 2.615, 10.1],
        [4.7425, -4.1725, 8.7425],
        [6.1, -0.1, 10.1],
        [3.385, -2.815, 10.1],
        [2.0275, -1.4575, 8.7425],
        [0.67, -0.1, 10.1]])
    assert np.allclose(correct_pos, result_positions)

    positions = wrap_positions(positions, cell, pbc=[False, True, False])
    correct_pos = np.array([
        [4.7425, 1.2575, 8.7425],
        [7.4575, -1.4575, 8.7425],
        [3.385, 2.615, 10.1],
        [10.1725, 1.2575, 8.7425],
        [6.1, -0.1, 10.1],
        [8.815, 2.615, 10.1],
        [7.4575, 3.9725, 8.7425],
        [6.1, 5.33, 10.1]])
    assert np.allclose(correct_pos, positions)

    # Test center away from values 0, 0.5
    result_positions = wrap_positions(positions, cell,
                                      pbc=[True, True, False],
                                      center=0.2)
    correct_pos = [[4.7425, 1.2575, 8.7425],
                   [2.0275, 3.9725, 8.7425],
                   [3.385, 2.615, 10.1],
                   [-0.6875, 1.2575, 8.7425],
                   [6.1, -0.1, 10.1],
                   [3.385, -2.815, 10.1],
                   [2.0275, -1.4575, 8.7425],
                   [0.67, -0.1, 10.1]]
    assert np.allclose(correct_pos, result_positions)

    # Test pretty_translation keyword
    positions = np.array([
        [0, 0, 0],
        [0, 1, 1],
        [1, 0, 1],
        [1, 1, 0.]])
    cell = np.diag([2, 2, 2])
    result = wrap_positions(positions, cell, pbc=[True, True, True],
                            pretty_translation=True)
    assert np.max(result) < 1 + 1E-10
    assert np.min(result) > -1E-10

    result = wrap_positions(positions - 5, cell, pbc=[True, True, True],
                            pretty_translation=True)
    assert np.max(result) < 1 + 1E-10

    result = wrap_positions(positions - 5, cell, pbc=[False, True, True],
                            pretty_translation=True)
    assert np.max(result[:, 0]) < -3
    assert np.max(result[:, 1:]) < 1 + 1E-10

    # Get the correct crystal structure from a range of different cells

    def checkcell(cell, name):
        cell = Cell.ascell(cell)
        lat = cell.get_bravais_lattice()
        assert lat.name == name, (lat.name, name)

    checkcell(bulk('Al').cell, 'FCC')
    checkcell(bulk('Fe').cell, 'BCC')
    checkcell(bulk('Zn').cell, 'HEX')
    checkcell(fcc111('Au', size=(1, 1, 3), periodic=True).cell, 'HEX')
    checkcell([[1, 0, 0], [0, 1, 0], [0, 0, 1]], 'CUB')
    checkcell([[1, 0, 0], [0, 1, 0], [0, 0, 2]], 'TET')
    checkcell([[1, 0, 0], [0, 2, 0], [0, 0, 3]], 'ORC')
    checkcell([[1, 0, 0], [0, 2, 0], [0.5, 0, 3]], 'ORCC')
    checkcell([[1, 0, 0], [0, 2, 0], [0.501, 0, 3]], 'MCL')
    checkcell([[1, 0, 0], [0.5, 3**0.5 / 2, 0], [0, 0, 3]], 'HEX')
Exemplo n.º 18
0
from ase.build import bulk
from ase.spacegroup import crystal
from wulffpack import SingleCrystal
from ase.visualize import view
from ase.spacegroup import get_spacegroup
from ase.build import cut

gamma1 = 0.33
gamma2 = 0.57
surface_energies = {(0, 0, 1): gamma1, (1, 0, 0): gamma2}
prim = bulk('Al', crystalstructure='fcc', cubic=True)
prim.symbols[[0, 3]] = 'Mg'
prim.symbols[[2, 1]] = 'Si'
sg = get_spacegroup(prim)
prim_cell = sg.scaled_primitive_cell
prim = cut(prim, a=prim_cell[0], b=prim_cell[1], c=prim_cell[2])
from ase.visualize import view
view(prim)

particle = SingleCrystal(surface_energies,
                         primitive_structure=prim,
                         natoms=1000)
view(particle.atoms)
Exemplo n.º 19
0
def passivate_zinc_blende_slab(slab,
                               passivant,
                               direction,
                               n_bilayers,
                               primtive=True):
    """
    The idea is to have the slabs passivated with passivant in the direction.
    right now only works for the 110 direction
    default zinc blende sturcture should be input as slab if not in the 001 direction
    n_bilayers is the number of bilayers in the slab
    """
    import numpy as np

    if direction == (0, 0, 1):
        c = np.array(direction, dtype=float)
        a = np.array((1, 0, 0), dtype=float)
        b = np.cross(c, a)
        # here we set the scale of the xy directions
        if primtive == True: xy_scale = 1
        if primtive == False: xy_scale = 2
        y = cut(slab,
                xy_scale * a / 2,
                xy_scale * b / 2,
                nlayers=2 * n_bilayers + 2)
        # Fixing the amount that you want to displace by
        d = .5  # displace by this amount
        xy = slab.get_cell()[0][0]
        # print(xy)
        # xy = .75
        # y.edit()

        if passivant == "H":
            if primtive == True:
                atoms_up = [
                    -1
                ]  # These are atoms that were coreated using an extra layer at the top
                atoms_down = [
                    0
                ]  # These are atoms that were coreated using an extra layer at the bottom

                up_pos = [
                    y[atoms_up[0]].position[0], y[atoms_up[0]].position[1],
                    y[atoms_up[0]].position[2]
                ]
                dwn_pos = [
                    y[atoms_down[0]].position[0], y[atoms_down[0]].position[1],
                    y[atoms_down[0]].position[2]
                ]

            if primtive == False:
                pass
                atoms_up = [
                    -4, -9, -1, -2
                ]  # These are atoms that were coreated using an extra layer at the top
                atoms_down = [
                    0, 6, 3, 2
                ]  # These are atoms that were coreated using an extra layer at the bottom

            for num in atoms_up:
                y[num].symbol = f"{passivant}"
                # Since in the 110 direction we have only the one bond to take care of in zincblende we need only replace the atoms with all of these
                y[num].position = [
                    up_pos[0] - xy / 8, up_pos[1] - xy / 8, up_pos[2]
                ]
                # y[num].position = [y[num].position[0]-xy, y[num].position[1]+xy, y[num].position[2]]
            for num in atoms_down:
                y[num].symbol = f"{passivant}"
                # Since in the 110 direction we have only the one bond to take care of in zincblende we need only replace the atoms with all of these
                y[num].position = [
                    dwn_pos[0] + xy / 8, dwn_pos[1] + xy / 8, dwn_pos[2]
                ]
                # y[num].position = [y[num].position[0]-xy, y[num].position[1]+xy, y[num].position[2]]

            # # adding extra atoms for up
            for num in atoms_up:
                y.append(f"{passivant}")
                y[-1].position = [
                    up_pos[0] - 3 * xy / 8, up_pos[1] - 3 * xy / 8, up_pos[2]
                ]
                # y[-1].position = [y[num].position[0]+2*xy, y[num].position[1]-2*xy, y[num].position[2]]

            for num in atoms_down:
                y.append(f"{passivant}")
                y[-1].position = [
                    dwn_pos[0] + 3 * xy / 8, dwn_pos[1] + 3 * xy / 8,
                    dwn_pos[2]
                ]
                # y[-1].position = [y[num].position[0]+2*xy, y[num].position[1]-2*xy, y[num].position[2]]

        elif passivant == "O":
            pass
        # y.edit()
        # y*= (3,3,1)
        # y.edit()

        return y
    else:
        c = np.array(direction, dtype=float)
        b = np.array((0, 0, 1), dtype=float)
        a = np.cross(b, direction)

        # here we set the scale of the xy directions
        if primtive == True: xy_scale = 1
        if primtive == False: xy_scale = 2

        y = cut(slab, xy_scale * a / 2, b, nlayers=2 * n_bilayers + 2)
        # y.edit()
        # coords = y.get_positions()

        # Fixing the amount that you want to displace by
        d = 2  # displace by this amount
        xy = .75

        if passivant == "H":
            if primtive == True:
                atoms_up = [
                    -1, -2
                ]  # These are atoms that were coreated using an extra layer at the top
                atoms_down = [
                    0, 1
                ]  # These are atoms that were coreated using an extra layer at the bottom
            if primtive == False:
                atoms_up = [
                    -4, -9, -1, -2
                ]  # These are atoms that were coreated using an extra layer at the top
                atoms_down = [
                    0, 6, 3, 2
                ]  # These are atoms that were coreated using an extra layer at the bottom
            for num in atoms_up:
                y[num].symbol = f"{passivant}"
                # Since in the 110 direction we have only the one bond to take care of in zincblende we need only replace the atoms with all of these
                y[num].position = [
                    y[num].position[0], y[num].position[1], y[num].position[2]
                ]
            for num in atoms_down:
                y[num].symbol = f"{passivant}"
                # Since in the 110 direction we have only the one bond to take care of in zincblende we need only replace the atoms with all of these
                y[num].position = [
                    y[num].position[0], y[num].position[1], y[num].position[2]
                ]
        elif passivant == "O":
            pass
        return y
Exemplo n.º 20
0
#/usr/local/bin/python3
import ase.io as io
from ase.build import cut
from ase.spacegroup import crystal

a = 9.04
skutterudite = crystal(('Co', 'Sb'),
                       basis=[(0.25, 0.25, 0.25), (0.0, 0.335, 0.158)],
                       spacegroup=204,
                       cellpar=[a, a, a, 90, 90, 90])

# Create a new atoms instance with Co at origo including all atoms on the
# surface of the unit cell
cosb3 = cut(skutterudite, origo=(0.25, 0.25, 0.25), extend=1.01)

# Define the atomic bonds to show
bondatoms = []
symbols = cosb3.get_chemical_symbols()
for i in range(len(cosb3)):
    for j in range(i):
        if (symbols[i] == symbols[j] == 'Co' and
            cosb3.get_distance(i, j) < 4.53):
            bondatoms.append((i, j))
        elif (symbols[i] == symbols[j] == 'Sb' and
              cosb3.get_distance(i, j) < 2.99):
            bondatoms.append((i, j))

# Create nice-looking image using povray
io.write('spacegroup-cosb3.pov', cosb3,
         transparent=False,
         display=False,
Exemplo n.º 21
0
import ase.io as io
from ase.build import cut
from ase.spacegroup import crystal

a = 9.04
skutterudite = crystal(('Co', 'Sb'),
                       basis=[(0.25, 0.25, 0.25), (0.0, 0.335, 0.158)],
                       spacegroup=204,
                       cellpar=[a, a, a, 90, 90, 90])

# Create a new atoms instance with Co at origo including all atoms on the
# surface of the unit cell
cosb3 = cut(skutterudite, origo=(0.25, 0.25, 0.25), extend=1.01)

# Define the atomic bonds to show
bondatoms = []
symbols = cosb3.get_chemical_symbols()
for i in range(len(cosb3)):
    for j in range(i):
        if (symbols[i] == symbols[j] == 'Co' and
            cosb3.get_distance(i, j) < 4.53):
            bondatoms.append((i, j))
        elif (symbols[i] == symbols[j] == 'Sb' and
              cosb3.get_distance(i, j) < 2.99):
            bondatoms.append((i, j))

# Create nice-looking image using povray
io.write('spacegroup-cosb3.pov', cosb3,
         transparent=False,
         display=False,
         run_povray=True,