Example #1
0
def get_primitive_cell(atoms, tol=1e-8):
    """Atoms object interface with spglib primitive cell finder:
    https://atztogo.github.io/spglib/python-spglib.html#python-spglib

    Parameters
    ----------
    atoms : object
        Atoms object to search for a primitive unit cell.
    tol : float
        Tolerance for floating point rounding errors.

    Returns
    -------
    primitive cell : object
        The primitive unit cell returned by spglib if one is found.
    """
    lattice = atoms.cell
    positions = atoms.get_scaled_positions()
    numbers = atoms.get_atomic_numbers()

    cell = (lattice, positions, numbers)
    cell = spglib.find_primitive(cell, symprec=tol)

    if cell is None:
        return None

    _lattice, _positions, _numbers = cell
    atoms = Gratoms(symbols=_numbers, cell=_lattice, pbc=atoms.pbc)
    atoms.set_scaled_positions(_positions)

    return atoms
Example #2
0
    def _build_basis(self, bulk):
        """Get the basis unit cell from bulk unit cell. This
        basis is effectively the same as the bulk, but rotated such
        that the z-axis is aligned with the surface termination.

        The basis is stored separately from the slab generated and is
        only intended for internal use.

        Returns
        -------
        basis : atoms object
            The basis slab corresponding to the provided bulk.
        """
        del bulk.constraints

        if len(np.nonzero(self.miller_index)[0]) == 1:
            mi = max(np.abs(self.miller_index))
            basis = circulant(self.miller_index[::-1] / mi).astype(int)
        else:
            h, k, l = self.miller_index
            p, q = ext_gcd(k, l)
            a1, a2, a3 = bulk.cell

            k1 = np.dot(p * (k * a1 - h * a2) + q * (l * a1 - h * a3),
                        l * a2 - k * a3)
            k2 = np.dot(l * (k * a1 - h * a2) - k * (l * a1 - h * a3),
                        l * a2 - k * a3)

            if abs(k2) > self.tol:
                i = -int(np.round(k1 / k2))
                p, q = p + i * l, q - i * k

            a, b = ext_gcd(p * k + q * l, h)

            c1 = (p * k + q * l, -p * h, -q * h)
            c2 = np.array((0, l, -k)) // abs(gcd(l, k))
            c3 = (b, a * p, a * q)

            basis = np.array([c1, c2, c3])

        basis_atoms = Gratoms(positions=bulk.positions,
                              numbers=bulk.get_atomic_numbers(),
                              cell=bulk.cell,
                              pbc=True)

        scaled = solve(basis.T, basis_atoms.get_scaled_positions().T).T
        scaled -= np.floor(scaled + self.tol)
        basis_atoms.set_scaled_positions(scaled)
        basis_atoms.set_cell(np.dot(basis, basis_atoms.cell), scale_atoms=True)

        a1, a2, a3 = basis_atoms.cell
        n1 = np.cross(a1, a2)
        a3 = n1 / norm(n1)
        rotate(basis_atoms, a3, (0, 0, 1), a1, (1, 0, 0))

        return basis_atoms
Example #3
0
def get_spglib_cell(atoms, primitive=False, idealize=True, tol=1e-5):
    """Atoms object interface with spglib primitive cell finder:
    https://atztogo.github.io/spglib/python-spglib.html#python-spglib

    Parameters
    ----------
    atoms : object
        Atoms object to search for a primitive unit cell.
    primitive : bool
        Reduce the atoms object into a primitive form.
    idealize : bool
        Convert the cell into the spglib standardized form.
    tol : float
        Tolerance for floating point rounding errors.

    Returns
    -------
    primitive cell : object
        The primitive unit cell returned by spglib if one is found.
    """
    lattice = atoms.cell
    positions = atoms.get_scaled_positions()
    numbers = atoms.get_atomic_numbers()

    cell = (lattice, positions, numbers)
    cell = spglib.standardize_cell(cell,
                                   to_primitive=primitive,
                                   no_idealize=~idealize,
                                   symprec=tol)

    if cell is None:
        return atoms

    _lattice, _positions, _numbers = cell
    atoms = Gratoms(symbols=_numbers, cell=_lattice, pbc=atoms.pbc)
    atoms.set_scaled_positions(_positions)

    return atoms
Example #4
0
    def align_crystal(self, bulk, miller_index):
        """Return an aligned unit cell from bulk unit cell. This alignment
        rotates the a and b basis vectors to be parallel to the Miller index.

        Parameters
        ----------
        bulk : Atoms object
            Bulk system to be standardized.
        miller_index : list (3,)
            Miller indices to align with the basis vectors.

        Returns
        -------
        new_bulk : Gratoms object
            Standardized bulk unit cell.
        """
        del bulk.constraints

        if len(np.nonzero(miller_index)[0]) == 1:
            mi = max(np.abs(miller_index))
            new_lattice = scipy.linalg.circulant(miller_index[::-1] /
                                                 mi).astype(int)
        else:
            h, k, l = miller_index
            p, q = utils.ext_gcd(k, l)
            a1, a2, a3 = bulk.cell

            k1 = np.dot(p * (k * a1 - h * a2) + q * (l * a1 - h * a3),
                        l * a2 - k * a3)
            k2 = np.dot(l * (k * a1 - h * a2) - k * (l * a1 - h * a3),
                        l * a2 - k * a3)

            if abs(k2) > self.tol:
                i = -int(np.round(k1 / k2))
                p, q = p + i * l, q - i * k

            a, b = utils.ext_gcd(p * k + q * l, h)

            c1 = (p * k + q * l, -p * h, -q * h)
            c2 = np.array((0, l, -k)) // abs(gcd(l, k))
            c3 = (b, a * p, a * q)
            new_lattice = np.array([c1, c2, c3])

        scaled = np.linalg.solve(new_lattice.T,
                                 bulk.get_scaled_positions().T).T
        scaled -= np.floor(scaled + self.tol)

        new_bulk = Gratoms(positions=bulk.positions,
                           numbers=bulk.get_atomic_numbers(),
                           pbc=True)

        if not self.attach_graph:
            del new_bulk._graph

        new_bulk.set_scaled_positions(scaled)
        new_bulk.set_cell(np.dot(new_lattice, bulk.cell), scale_atoms=True)

        # Align the longest of the ab basis vectors with x
        d = np.linalg.norm(new_bulk.cell[:2], axis=1)
        if d[1] > d[0]:
            new_bulk.cell[[0, 1]] = new_bulk.cell[[1, 0]]
        a = new_bulk.cell[0]
        a3 = np.cross(a, new_bulk.cell[1]) / np.max(d)
        rotate(new_bulk, a3, (0, 0, 1), a, (1, 0, 0))

        # Ensure the remaining basis vectors are positive in their
        # corresponding axis
        for i in range(1, 3):
            if new_bulk.cell[i][i] < 0:
                new_bulk.cell[i] *= -1
        new_bulk.wrap(eps=1e-3)

        return new_bulk