Example #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.
        """
        h, k, l = self.miller_index
        h0, k0, l0 = (self.miller_index == 0)
        if h0 and k0 or h0 and l0 or k0 and l0:
            if not h0:
                c1, c2, c3 = [(0, 1, 0), (0, 0, 1), (1, 0, 0)]
            if not k0:
                c1, c2, c3 = [(0, 0, 1), (1, 0, 0), (0, 1, 0)]
            if not l0:
                c1, c2, c3 = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
        else:
            p, q = ext_gcd(k, l)
            a1, a2, a3 = bulk.cell

            # constants describing the dot product of basis c1 and c2:
            # dot(c1,c2) = k1+i*k2, i in Z
            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 corresponding to the optimal basis
                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_atoms = Gratoms(positions=bulk.positions,
                              numbers=bulk.get_atomic_numbers(),
                              cell=bulk.cell,
                              pbc=True)
        basis = np.array([c1, c2, c3])

        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
        a3 = np.cross(a1, a2) / norm(np.cross(a1, a2))
        rotate(basis_atoms, a3, (0, 0, 1), a1, (1, 0, 0))

        return basis_atoms
Example #2
0
def orient(atoms, zone_axis, new_x=None, new_y=None):
    ''' Orient an atoms object along a particular zone axis (zone axis along
    the Cartesian z axis)

    Parameters
    ----------
    atoms : Atoms object
        an Atoms object from ASE package, created by reading any file supported
        by ASE
    zone_axis : iterable
        the zone axis, align to the Cartesian z axis (optical axis)
    new_x : iterable, optional
        the new crystal axis along the Cartesian x axis, orthogonal to the zone
        axis and the crystal axis along y axis
    new_y : iterable, optional
        the new crystal axis along the Cartesian y axis, orthogonal to the zone
        axis and the crystal axis along x axis

    Returns
    -------
    rotated : Atoms object
        the oriented Atoms object with the specified crystal axes along x, y, z
    '''

    # define the Cartesian coordinate system
    x = [1,0,0]
    y = [0,1,0]
    z = [0,0,1]

    # don't alter the orginal Atoms object
    rotated = atoms.copy()

    # get information of unit cell
    a, b, c, alpha, beta, gamma = rotated.cell.cellpar()

    # get the crystal axes along the basis of Cartesian system
    new_x, new_y, new_z = _parse_crystal_axes(zone_axis, new_x, new_y,
                                              a, b, c, alpha, beta, gamma)


    print('Crystal axis along x-axis: ', new_x)
    print('Crystal axis along y-axis: ', new_y)
    print('Crystal axis along z-axis: ', new_z)


    new_x_car = toCartesian(new_x, a, b, c, alpha, beta, gamma)
    new_y_car = toCartesian(new_y, a, b, c, alpha, beta, gamma)
    new_z_car = toCartesian(new_z, a, b, c, alpha, beta, gamma)


    # rotate such that the new crystal axes along x and y are aligned with the coordinate system
    if new_x_car is not None and new_y_car is not None:
        build.rotate(rotated, new_x_car, x, new_y_car, y, rotate_cell=True)
    else:
        rotated.rotate(new_z_car, z, rotate_cell=True)

    return rotated
Example #3
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 #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