예제 #1
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
예제 #2
0
파일: surface.py 프로젝트: mhoffman/CatKit
    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