def __init__(self, initial_structure, miller_index, min_slab_size,
                 min_vacuum_size, lll_reduce=False, center_slab=False,
                 in_unit_planes=False, primitive=True, max_normal_search=None,
                 reorient_lattice=True):
        """
        Calculates the slab scale factor and uses it to generate a unit cell
        of the initial structure that has been oriented by its miller index.
        Also stores the initial information needed later on to generate a slab.

        Args:
            initial_structure (Structure): Initial input structure. Note that to
                ensure that the miller indices correspond to usual
                crystallographic definitions, you should supply a conventional
                unit cell structure.
            miller_index ([h, k, l]): Miller index of plane parallel to
                surface. Note that this is referenced to the input structure. If
                you need this to be based on the conventional cell,
                you should supply the conventional structure.
            min_slab_size (float): In Angstroms or number of hkl planes
            min_vacuum_size (float): In Angstroms or number of hkl planes
            lll_reduce (bool): Whether to perform an LLL reduction on the
                eventual structure.
            center_slab (bool): Whether to center the slab in the cell with
                equal vacuum spacing from the top and bottom.
            in_unit_planes (bool): Whether to set min_slab_size and min_vac_size
                in units of hkl planes (True) or Angstrom (False/default).
                Setting in units of planes is useful for ensuring some slabs
                have a certain nlayer of atoms. e.g. for Cs (100), a 10 Ang
                slab will result in a slab with only 2 layer of atoms, whereas
                Fe (100) will have more layer of atoms. By using units of hkl
                planes instead, we ensure both slabs
                have the same number of atoms. The slab thickness will be in
                min_slab_size/math.ceil(self._proj_height/dhkl)
                multiples of oriented unit cells.
            primitive (bool): Whether to reduce any generated slabs to a
                primitive cell (this does **not** mean the slab is generated
                from a primitive cell, it simply means that after slab
                generation, we attempt to find shorter lattice vectors,
                which lead to less surface area and smaller cells).
            max_normal_search (int): If set to a positive integer, the code will
                conduct a search for a normal lattice vector that is as
                perpendicular to the surface as possible by considering
                multiples linear combinations of lattice vectors up to
                max_normal_search. This has no bearing on surface energies,
                but may be useful as a preliminary step to generating slabs
                for absorption and other sizes. It is typical that this will
                not be the smallest possible cell for simulation. Normality
                is not guaranteed, but the oriented cell will have the c
                vector as normal as possible (within the search range) to the
                surface. A value of up to the max absolute Miller index is
                usually sufficient.
            reorient_lattice (bool): reorients the lattice parameters such that
                the c direction is the third vector of the lattice matrix

        """
        latt = initial_structure.lattice
        miller_index = reduce_vector(miller_index)
        # Calculate the surface normal using the reciprocal lattice vector.
        recp = latt.reciprocal_lattice_crystallographic
        normal = recp.get_cartesian_coords(miller_index)
        normal /= np.linalg.norm(normal)

        slab_scale_factor = []
        non_orth_ind = []
        eye = np.eye(3, dtype=np.int)
        for i, j in enumerate(miller_index):
            if j == 0:
                # Lattice vector is perpendicular to surface normal, i.e.,
                # in plane of surface. We will simply choose this lattice
                # vector as one of the basis vectors.
                slab_scale_factor.append(eye[i])
            else:
                # Calculate projection of lattice vector onto surface normal.
                d = abs(np.dot(normal, latt.matrix[i])) / latt.abc[i]
                non_orth_ind.append((i, d))

        # We want the vector that has maximum magnitude in the
        # direction of the surface normal as the c-direction.
        # Results in a more "orthogonal" unit cell.
        c_index, dist = max(non_orth_ind, key=lambda t: t[1])

        if len(non_orth_ind) > 1:
            lcm_miller = lcm(*[miller_index[i] for i, d in non_orth_ind])
            for (i, di), (j, dj) in itertools.combinations(non_orth_ind, 2):
                l = [0, 0, 0]
                l[i] = -int(round(lcm_miller / miller_index[i]))
                l[j] = int(round(lcm_miller / miller_index[j]))
                slab_scale_factor.append(l)
                if len(slab_scale_factor) == 2:
                    break

        if max_normal_search is None:
            slab_scale_factor.append(eye[c_index])
        else:
            index_range = sorted(
                reversed(range(-max_normal_search, max_normal_search + 1)),
                key=lambda x: abs(x))
            candidates = []
            for uvw in itertools.product(index_range, index_range, index_range):
                if (not any(uvw)) or abs(
                        np.linalg.det(slab_scale_factor + [uvw])) < 1e-8:
                    continue
                vec = latt.get_cartesian_coords(uvw)
                l = np.linalg.norm(vec)
                cosine = abs(np.dot(vec, normal) / l)
                candidates.append((uvw, cosine, l))
                if abs(abs(cosine) - 1) < 1e-8:
                    # If cosine of 1 is found, no need to search further.
                    break
            # We want the indices with the maximum absolute cosine,
            # but smallest possible length.
            uvw, cosine, l = max(candidates, key=lambda x: (x[1], -x[2]))
            slab_scale_factor.append(uvw)

        slab_scale_factor = np.array(slab_scale_factor)

        # Let's make sure we have a left-handed crystallographic system
        if np.linalg.det(slab_scale_factor) < 0:
            slab_scale_factor *= -1

        # Make sure the slab_scale_factor is reduced to avoid
        # unnecessarily large slabs

        reduced_scale_factor = [reduce_vector(v) for v in slab_scale_factor]
        slab_scale_factor = np.array(reduced_scale_factor)

        single = initial_structure.copy()
        single.make_supercell(slab_scale_factor)

        # When getting the OUC, lets return the most reduced
        # structure as possible to reduce calculations
        self.oriented_unit_cell = Structure.from_sites(single,
                                                       to_unit_cell=True)
        self.max_normal_search = max_normal_search
        self.parent = initial_structure
        self.lll_reduce = lll_reduce
        self.center_slab = center_slab
        self.slab_scale_factor = slab_scale_factor
        self.miller_index = miller_index
        self.min_vac_size = min_vacuum_size
        self.min_slab_size = min_slab_size
        self.in_unit_planes = in_unit_planes
        self.primitive = primitive
        self._normal = normal
        a, b, c = self.oriented_unit_cell.lattice.matrix
        self._proj_height = abs(np.dot(normal, c))
        self.reorient_lattice = reorient_lattice
def __init__ ( self , initial_structure , miller_index , min_slab_size , min_vacuum_size , lll_reduce = False , center_slab = False , in_unit_planes = False , primitive = True , max_normal_search = None , reorient_lattice = True ) : latt = initial_structure . lattice miller_index = reduce_vector ( miller_index ) recp = latt . reciprocal_lattice_crystallographic normal = recp . get_cartesian_coords ( miller_index ) normal /= np . linalg . norm ( normal ) slab_scale_factor = [ ] non_orth_ind = [ ] eye = np . eye ( 3 , dtype = np . int ) for i , j in enumerate ( miller_index ) : if j == 0 : slab_scale_factor . append ( eye [ i ] ) else : d = abs ( np . dot ( normal , latt . matrix [ i ] ) ) / latt . abc [ i ] non_orth_ind . append ( ( i , d ) ) c_index , dist = max ( non_orth_ind , key = lambda t : t [ 1 ] ) if len ( non_orth_ind ) > 1 : lcm_miller = lcm ( * [ miller_index [ i ] for i , d in non_orth_ind ] ) for ( i , di ) , ( j , dj ) in itertools . combinations ( non_orth_ind , 2 ) : l = [ 0 , 0 , 0 ] l [ i ] = - int ( round ( lcm_miller / miller_index [ i ] ) ) l [ j ] = int ( round ( lcm_miller / miller_index [ j ] ) ) slab_scale_factor . append ( l ) if len ( slab_scale_factor ) == 2 : break if max_normal_search is None : slab_scale_factor . append ( eye [ c_index ] ) else : index_range = sorted ( reversed ( range ( - max_normal_search , max_normal_search + 1 ) ) , key = lambda x : abs ( x ) ) candidates = [ ] for uvw in itertools . product ( index_range , index_range , index_range ) : if ( not any ( uvw ) ) or abs ( np . linalg . det ( slab_scale_factor + [ uvw ] ) ) < 1e-8 : continue vec = latt . get_cartesian_coords ( uvw ) l = np . linalg . norm ( vec ) cosine = abs ( np . dot ( vec , normal ) / l ) candidates . append ( ( uvw , cosine , l ) ) if abs ( abs ( cosine ) - 1 ) < 1e-8 : break uvw , cosine , l = max ( candidates , key = lambda x : ( x [ 1 ] , - x [ 2 ] ) ) slab_scale_factor . append ( uvw ) slab_scale_factor = np . array ( slab_scale_factor ) if np . linalg . det ( slab_scale_factor ) < 0 : slab_scale_factor *= - 1 reduced_scale_factor = [ reduce_vector ( v ) for v in slab_scale_factor ] slab_scale_factor = np . array ( reduced_scale_factor ) single = initial_structure . copy ( ) single . make_supercell ( slab_scale_factor ) self . oriented_unit_cell = Structure . from_sites ( single , to_unit_cell = True ) self . max_normal_search = max_normal_search self . parent = initial_structure self . lll_reduce = lll_reduce self . center_slab = center_slab self . slab_scale_factor = slab_scale_factor self . miller_index = miller_index self . min_vac_size = min_vacuum_size self . min_slab_size = min_slab_size self . in_unit_planes = in_unit_planes self . primitive = primitive self . _normal = normal a , b , c = self . oriented_unit_cell . lattice . matrix self . _proj_height = abs ( np . dot ( normal , c ) ) self . reorient_lattice = reorient_lattice
Calculates the slab scale factor and uses it to generate a unit cell
        of the initial structure that has been oriented by its miller index.
        Also stores the initial information needed later on to generate a slab.

        Args:
            initial_structure (Structure): Initial input structure. Note that to
                ensure that the miller indices correspond to usual
                crystallographic definitions, you should supply a conventional
                unit cell structure.
            miller_index ([h, k, l]): Miller index of plane parallel to
                surface. Note that this is referenced to the input structure. If
                you need this to be based on the conventional cell,
                you should supply the conventional structure.
            min_slab_size (float): In Angstroms or number of hkl planes
            min_vacuum_size (float): In Angstroms or number of hkl planes
            lll_reduce (bool): Whether to perform an LLL reduction on the
                eventual structure.
            center_slab (bool): Whether to center the slab in the cell with
                equal vacuum spacing from the top and bottom.
            in_unit_planes (bool): Whether to set min_slab_size and min_vac_size
                in units of hkl planes (True) or Angstrom (False/default).
                Setting in units of planes is useful for ensuring some slabs
                have a certain nlayer of atoms. e.g. for Cs (100), a 10 Ang
                slab will result in a slab with only 2 layer of atoms, whereas
                Fe (100) will have more layer of atoms. By using units of hkl
                planes instead, we ensure both slabs
                have the same number of atoms. The slab thickness will be in
                min_slab_size/math.ceil(self._proj_height/dhkl)
                multiples of oriented unit cells.
            primitive (bool): Whether to reduce any generated slabs to a
                primitive cell (this does **not** mean the slab is generated
                from a primitive cell, it simply means that after slab
                generation, we attempt to find shorter lattice vectors,
                which lead to less surface area and smaller cells).
            max_normal_search (int): If set to a positive integer, the code will
                conduct a search for a normal lattice vector that is as
                perpendicular to the surface as possible by considering
                multiples linear combinations of lattice vectors up to
                max_normal_search. This has no bearing on surface energies,
                but may be useful as a preliminary step to generating slabs
                for absorption and other sizes. It is typical that this will
                not be the smallest possible cell for simulation. Normality
                is not guaranteed, but the oriented cell will have the c
                vector as normal as possible (within the search range) to the
                surface. A value of up to the max absolute Miller index is
                usually sufficient.
            reorient_lattice (bool): reorients the lattice parameters such that
                the c direction is the third vector of the lattice matrix