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)
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