Пример #1
0
def make_InAs001_surface(inas_layers = 4, licras_layers = 4, vacuum_layers = 8):
    total_layers = inas_layers + licras_layers + vacuum_layers
    err = 1./total_layers/10
    a = Length(6.0583, "ang")
    fcc_lattice = np.array([[.0,.5,.5],[.5,.0,.5],[.5,.5,.0]])
    lattice = Lattice(fcc_lattice * a)
    surface = Structure(lattice, ['Li', 'Cr', 'As'], [[0.50,0.50,0.50],[0.00,0.00,0.00],[0.25,0.25,0.25]])
    surface.make_supercell([[0,0,1],[1,-1,0],[1,1,-1]])
    surface.make_supercell([[1,0,0],[0,1,0],[0,0,total_layers]])
    to_remove = []
    print (1.*inas_layers / total_layers)
    print 1.*(inas_layers+licras_layers)/total_layers
    print 1. - 1. / vacuum_layers
    print err
    for idx,site in enumerate(surface):
        if (site.frac_coords[2] - 1.*inas_layers / total_layers) < -err:
            if site.specie.symbol == 'Li': to_remove.append(idx)
            if site.specie.symbol == 'Cr': surface.replace(idx,Element('In'))
        if (site.frac_coords[2] - 1. + 1./vacuum_layers/8.) > -err:
            surface.replace(idx,Element('H'))
        elif (1.*(inas_layers+licras_layers)/total_layers) - site.frac_coords[2] < err:
            to_remove.append(idx)
    surface.remove_sites(to_remove)
    selective_dynamics = len(surface) * [[True,True,True]]
    for idx,site in enumerate(surface):
        if (np.linalg.norm(site.frac_coords) < err):
            selective_dynamics[idx] = [False,False,False]
    return surface.get_sorted_structure(),np.array(selective_dynamics)
Пример #2
0
def make_Si001_surface(Si_layers = 4, vacuum_layers = 4):
    total_layers = Si_layers + vacuum_layers
    a = Length(5.431, "ang")
    fcc_lattice = np.array([[.0,.5,.5],[.5,.0,.5],[.5,.5,.0]])
    lattice = Lattice(fcc_lattice * a)
    surface = Structure(lattice, ['Si','Si'], [[0.00,0.00,0.00],[0.25,0.25,0.25]])
    surface.make_supercell([[0,0,1],[1,-1,0],[1,1,-1]])
    surface.make_supercell([[2,0,0],[0,2,0],[0,0,total_layers]])
    to_remove = []
    for idx,site in enumerate(surface):
        if (site.frac_coords[2] > 1.0*Si_layers/total_layers):
            to_remove.append(idx)
    surface.remove_sites(to_remove)
    print to_remove
    selective_dynamics = len(surface) * [[True,True,True]]
    for idx,site in enumerate(surface):
        if (site.frac_coords[2] < 0.001):
            selective_dynamics[idx] = [False,False,False]
            surface.replace(idx,Element('H'))
    return surface.get_sorted_structure(),np.array(selective_dynamics)
def slab_has_mirror_sym(slab, nterm=1, tol=0.01):
    '''This function tests if the input slab has a mirror symmetry in the c-direction (given tolerance 'tol' in Angstrom).
    Specify 'nterm' (number of unique terminations) to ensure enough subtractions from the top/bottom of the layer are considered.
    Unit cell can be of any shape, however make sure that the layer thickness in c-direction is thick enough
    (recommendation: SlabGenerator with min_slab_size=4, in_unit_planes=True)
    Remark: A bug in pymatgen/transformations/standard_transformations.py was fixed in version pymatgen-2020.4.29

    Output: [mirror_sym, error] where 'mirror_sym' is the mirror symmetry (True or False),
    'error' may contain an error message in case any step of the unit cell rotation didn't work, otherwise contains empty string

    Procedure:
    1. Rotate unit cell such that Cartesian coordinates of a and b basis vectors is of form (a_x,0,0) and (b_x,b_y,0), respectively.
    2. Create new c-direction orthogonal to a and b.
    3. Add atoms from slab in step 1 to new unit cell of step 2.
    4. Use function mirror_sym_check to check symmetry for the initial slab, the slab with the topmost, the slab with lowermost layer missing,
    and the slab with both topmost/lowermost layers missing. If nterm > 3, additionally np.floor(nterm/2) layers are subtracted from either side and their symmetry checked.
    '''
    error = ""
    if round(slab.lattice.alpha, 1) == 90.0 and round(slab.lattice.beta,
                                                      1) == 90.0:
        slab_straight = Structure(slab._lattice, slab.species_and_occu,
                                  slab.frac_coords)
    else:
        R = slab.lattice.matrix
        #print(R)
        #if a base vector not parallel to x-axis in caartesian coords, rotate the cell/structure such that it is
        if abs(R[0, 1]) > 0.0001 or abs(R[0, 2]) > 0.0001:
            x = [1, 0, 0]
            rot_axis = np.cross(R[0], x)
            angle = np.arccos(
                np.clip(
                    np.dot(R[0] / np.linalg.norm(R[0]), x / np.linalg.norm(x)),
                    -1.0, 1.0))
            slab = RotationTransformation(
                rot_axis, math.degrees(angle)).apply_transformation(slab)
            R = slab.lattice.matrix
            #In case the wrong sign of the rotation was applied, rotate back twice the angle:
            if abs(R[0, 1]) > 0.0001 or abs(R[0, 2]) > 0.0001:
                slab = RotationTransformation(
                    rot_axis,
                    -2 * math.degrees(angle)).apply_transformation(slab)
                R = slab.lattice.matrix
            if abs(R[0, 1]) > 0.0001 or abs(R[0, 2]) > 0.0001:
                error = "Error. Could not rotate a-axis to be parallel to x-axis."
            #print(R)
        #if b vector not lying in cartesian x-y plane, rotate to make it so (i.e. z-component of b vector = 0)
        if abs(R[1, 2]) > 0.0001 and not error:
            b_x_flat = [0, 0, 0]
            b_x_flat[1] = R[1, 1]
            b_x_flat[2] = R[1, 2]
            x = [1, 0, 0]
            y = [0, 1, 0]
            angle2 = np.arccos(
                np.clip(
                    np.dot(b_x_flat / np.linalg.norm(b_x_flat),
                           y / np.linalg.norm(y)), -1.0, 1.0)
            )  #angle between y-axis and b vector projected onto y-z-plane
            slab = RotationTransformation(
                x, math.degrees(angle2)).apply_transformation(slab)
            R = slab.lattice.matrix
            #In case the wrong sign of the rotation was applied, rotate back twice the angle:
            if abs(R[1, 2]) > 0.0001:
                slab = RotationTransformation(
                    x, -2 * math.degrees(angle2)).apply_transformation(slab)
                R = slab.lattice.matrix
            if abs(R[1, 2]) > 0.0001:
                error = "Error. Could not rotate b-vector to lie within x-y-plane."
            if R[1, 1] < -0.0001:
                error = "Error. Vector b faces into negative y-direction (which could cause problems)."
        #Now create new c-direction that is orthogonal to the rotated a and b vectors
        if not error:
            N = np.array(R)  #new lattice vectors
            N[2] = np.cross(R[0], R[1])
            N[2] = N[2] * np.dot(
                R[2], N[2]) / (np.linalg.norm(N[2]) * np.linalg.norm(N[2]))
            latticeN = Lattice(N)  #new lattice with c perpendicular to a,b
            #Add atoms from rotated unit cell to new unit cell with orthogonal c-direction
            slab_straight = Structure(latticeN,
                                      slab.species,
                                      slab.cart_coords,
                                      coords_are_cartesian=True)
    #Check mirror symmetry for straightened slab as well as slabs that are missing either (or both) topmost, lowermost atom-layers
    if not error:
        mirror_sym = False

        #Checks mirror symmetry of slab_straight without any layers removed
        slab_orig = Structure(slab_straight._lattice,
                              slab_straight.species_and_occu,
                              slab_straight.frac_coords)
        msym, err = mirror_sym_check(slab_orig, tol=tol)
        if msym:
            mirror_sym = True
        if not err == '':
            error = err

        #Checks mirror symmetry of slab_straight with lowermost layer removed
        min_value = np.min(slab_orig.cart_coords[:, 2])
        first_layer_size = len(
            np.where(np.abs(min_value - slab_orig.cart_coords[:, 2]) < tol)[0])
        for i in range(first_layer_size):
            index_to_remove = np.argmin(slab_orig.frac_coords[:, 2])
            slab_orig.remove_sites([index_to_remove])
        msym, err = mirror_sym_check(slab_orig, tol=tol)
        if msym:
            mirror_sym = True
        if not err == '':
            error = err

        #Checks mirror symmetry of slab_straight with topmost layer removed
        max_value = np.max(slab_straight.cart_coords[:, 2])
        first_layer_size = len(
            np.where(
                np.abs(max_value - slab_straight.cart_coords[:, 2]) < tol)[0])
        for i in range(first_layer_size):
            index_to_remove = np.argmax(slab_straight.frac_coords[:, 2])
            slab_straight.remove_sites([index_to_remove])
        msym, err = mirror_sym_check(slab_straight, tol=tol)
        if msym:
            mirror_sym = True
        if not err == '':
            error = err

        #Checks mirror symmetry of slab_straight with lowermost and topmost layers removed
        min_value = np.min(slab_straight.cart_coords[:, 2])
        first_layer_size = len(
            np.where(
                np.abs(min_value - slab_straight.cart_coords[:, 2]) < tol)[0])
        for i in range(first_layer_size):
            index_to_remove = np.argmin(slab_straight.frac_coords[:, 2])
            slab_straight.remove_sites([index_to_remove])
        msym, err = mirror_sym_check(slab_straight, tol=tol)
        if msym:
            mirror_sym = True
        if not err == '':
            error = err

        #If nterm is larger than 3, remove additonal layers from either side to ensure
        #that we check all relevant slabs that could yield proof that there is mirror symmetry
        if nterm > 3:
            slab_orig = Structure(slab_straight._lattice,
                                  slab_straight.species_and_occu,
                                  slab_straight.frac_coords)
            #delete more layers nterm/2 rounded down on either side from the slab_straight (which had one each side already subtracted)
            for term in range(int(np.floor(nterm / 2))):
                max_value = np.max(slab_straight.cart_coords[:, 2])
                first_layer_size = len(
                    np.where(
                        np.abs(max_value -
                               slab_straight.cart_coords[:, 2]) < tol)[0])
                for i in range(first_layer_size):
                    index_to_remove = np.argmax(slab_straight.frac_coords[:,
                                                                          2])
                    slab_straight.remove_sites([index_to_remove])
                msym, err = mirror_sym_check(slab_straight, tol=tol)
                if msym:
                    mirror_sym = True
                if not err == '':
                    error = err

                min_value = np.min(slab_orig.cart_coords[:, 2])
                first_layer_size = len(
                    np.where(
                        np.abs(min_value -
                               slab_orig.cart_coords[:, 2]) < tol)[0])
                for i in range(first_layer_size):
                    index_to_remove = np.argmin(slab_orig.frac_coords[:, 2])
                    slab_orig.remove_sites([index_to_remove])
                msym, err = mirror_sym_check(slab_orig, tol=tol)
                if msym:
                    mirror_sym = True
                if not err == '':
                    error = err

    else:
        #Mirror symmetry is set to False in case there was an error along the way.
        mirror_sym = False

    return mirror_sym, error