def test_make_grid(): # This unit test doesn't pass because the primitive translation vectors # changed when I updated the code (I think). grid_pts1 = [[0, 0, 0], [1, 1, 1], [0, 0, 2], [0, 2, 0], [0, 2, 2], [2, 0, 0], [0, 2, 2], [2, 0, 2], [2, 2, 0], [2, 2, 2], [1, 3, 3], [3, 1, 3], [3, 1, 1], [1, 1, 3], [1, 3, 1], [3, 1, 1]] grid_pts1 = np.asarray(grid_pts1) * 1. / 4 cell_centering = "prim" cell_const = 1. cell_const_list = [cell_const] * 3 cell_angles = [np.pi / 2] * 3 cell_vecs = make_ptvecs(cell_centering, cell_const_list, cell_angles) grid_centering = "body" grid_const = cell_const / 2 grid_const_list = [grid_const] * 3 grid_angles = [np.pi / 2] * 3 grid_vecs = make_ptvecs(grid_centering, grid_const_list, grid_angles) offset = np.asarray([0., 0., 0.]) grid = make_grid(cell_vecs, grid_vecs, offset, False) for g1 in grid_pts1: check = False for g2 in grid: if np.allclose(g1, g2) == True: check = True assert check == True lat_type_list = ["fcc", "bcc", "sc"] lat_centering_list = ["face", "body", "prim"] lat_const_list = [10, 10.1, 3 * np.pi] lat_consts_list = [[l] * 3 for l in lat_const_list] lat_angles = [np.pi / 2] * 3 offset_list = [[1.3, 1.1, 1.7], [11, 9, 8], [np.pi, np.pi, np.pi]] r_list = [1, 2.3, np.pi] for lat_centering in lat_centering_list: for lat_consts in lat_consts_list: lat_vecs = make_ptvecs(lat_centering, lat_consts, lat_angles) lat_vecs = make_rptvecs(lat_vecs) for offset in offset_list: offset = np.asarray(offset) for r in r_list: total_grid = large_sphere_pts(lat_vecs, r, offset) grid = sphere_pts(lat_vecs, r, offset) contained = False for tg in total_grid: if np.dot(tg - offset, tg - offset) <= r: contained = False for g in grid: if np.allclose(g, tg): contained = True assert contained == True
def test_make_grid(): grid_centering = "prim" grid_consts = [1, 1, 1] grid_angles = [np.pi / 2] * 3 grid_vecs = make_ptvecs(grid_centering, grid_consts, grid_angles) lat_centering = "prim" lat_consts = [2] * 3 lat_angles = [np.pi / 2] * 3 lat_vecs = make_ptvecs(lat_centering, lat_consts, lat_angles) offset = [0] * 3 grid0 = [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] grid1 = make_grid(lat_vecs, grid_vecs, offset) assert len(grid0) == len(grid1) for g0 in grid0: contained = False for g1 in grid1: if np.allclose(g0, g1): contained = True assert contained == True grid_centering = "body" grid_consts = [1.] * 3 grid_angles = [np.pi / 2] * 3 grid_vecs = make_ptvecs(grid_centering, grid_consts, grid_angles) lat_centering = "body" lat_consts = [2.] * 3 lat_angles = [np.pi / 2] * 3 lat_vecs = make_ptvecs(lat_centering, lat_consts, lat_angles) offset = [0] * 3 a = 0.5 grid0 = [[0, 0, 0], [-a, a, a], [a, -a, a], [0, 0, 2 * a], [a, a, -a], [0, 2 * a, 0], [2 * a, 0, 0], [a, a, a]] grid1 = make_grid(lat_vecs, grid_vecs, offset) assert len(grid0) == len(grid1) for g0 in grid0: contained = False for g1 in grid1: if np.allclose(g0, g1): contained = True assert contained == True
def __init__(self, pseudo_potential=None, cutoff=None, cell_centering=None, cell_constants=None, cell_angles=None, offset=None, grid_types=None, grid_constants=None, integration_methods=None, origin=None, random=None): self.pseudo_potential = pseudo_potential or Al_PP self.cutoff = cutoff or 4. self.cell_centering = cell_centering or "prim" self.cell_constants = cell_constants or [1.] * 3 self.cell_angles = cell_angles or [np.pi / 2] * 3 self.cell_vectors = make_ptvecs(self.cell_centering, self.cell_constants, self.cell_angles) self.grid_centerings = grid_centerings or [ "prim", "base", "body", "face" ] self.grid_constants = grid_constants or [1 / n for n in range(2, 11)] self.offset = offset or [0., 0., 0.] # self.integration_methods = integration_methods or [rectangle_method] self.origin = origin or [0., 0., 0.] self.random = random or False
def plot_grid(self, i, j): """Plot one of the grids in the convergence plot. """ grid_vecs = make_ptvecs(self.grid_types[i], self.grid_constants[j]) grid_pts = make_grid(self.rcell_vectors, gr_vecs, self.offset) PlotMesh(grid_pts, self.rcell_vectors, self.offset)
def test_make_cell_points(): """Verify the grid satisfies various properties, such as verifying the neighbors of each point are withing the grid as long as the neighbors lie within the unit cell. Also verify none of the points lie outside the unit cell. """ # At the moment it only tests the cubic lattices. grid_center_list = ["prim", "face", "body"] grid_constants = np.array([1. / 2, 1. / 4]) * 2 * np.sqrt(2) grid_consts_list = [[m] * 3 for m in grid_constants] grid_angles = [np.pi / 2] * 3 cell_center_list = ["prim", "face", "body"] cell_constants = [2 * np.sqrt(2)] cell_consts_list = [[c] * 3 for c in cell_constants] cell_angles = [np.pi / 2] * 3 offsets = [[0., 0., 0.], [1. / 2, 1. / 2, 1. / 2]] # for grid_constant in grid_constants: for grid_consts in grid_consts_list: for grid_center in grid_center_list: grid_vectors = make_ptvecs(grid_center, grid_consts, grid_angles) grid_lengths = [np.linalg.norm(lv) for lv in grid_vectors] for cell_consts in cell_consts_list: for cell_center in cell_center_list: cell_vectors = make_ptvecs(cell_center, cell_consts, cell_angles) cell_lengths = [np.linalg.norm(cv) for cv in cell_vectors] for offset in offsets: grid = make_cell_points(cell_vectors, grid_vectors, offset) grid2, null_grid = make_large_grid( cell_vectors, grid_vectors, offset) # Verify all the points in the cell for the large grid # are contained in grid. print(grid_consts) print(grid_center) print(cell_consts) print(cell_center) print(offset) for g in grid: included = False for g2 in grid2: if np.allclose(g, g2): included = True assert included == True
def test_rectangular(): """This will test the rectangular methods of finding the total energy and the Fermi level. """ degree_list = range(1,5) for degree in degree_list: # Verify the Fermi level of the free electron model. lat_angles =[np.pi/2]*3 lat_consts = [1]*3 lat_centering = "prim" lattice = Lattice(lat_centering, lat_consts, lat_angles) free = FreeElectronModel(lattice, degree) grid_consts = [40]*3 grid_angles = [np.pi/2]*3 grid_centering = "prim" grid_vecs = make_ptvecs(grid_centering, grid_consts, grid_angles) rgrid_vecs = make_rptvecs(grid_vecs) offset = -np.dot(np.linalg.inv(rgrid_vecs), np.dot(lattice.reciprocal_vectors, [.5]*3)) grid = make_grid(free.lattice.reciprocal_vectors, rgrid_vecs, offset) weights = np.ones(len(grid)) free.fermi_level, temp = rectangular_method(free, grid, weights) sphere_volume = 4./3*np.pi*free.fermi_level**(3./degree) occupied_volume = free.lattice.reciprocal_volume*free.nvalence_electrons/2 fl_answer = (3*occupied_volume/(4*np.pi))**(degree/3.) print("degree ", degree) print("shere volume ", sphere_volume) print("occupied_volume ", occupied_volume) assert np.isclose(sphere_volume, occupied_volume, 1e-1, 1e-1) assert np.isclose(free.fermi_level, fl_answer, 1e-2,1e-2) weights = np.ones(len(grid)) temp, total_energy = rectangular_method(free, grid, weights) rf = free.fermi_level**(1./degree) te_answer = 4*np.pi*(rf**(3 + degree)/(3. + degree)) assert np.isclose(total_energy, te_answer, 1e-1, 1e-1)
def compare_grids(self, answer, plot=False, save=False): self.answer = answer if self.random: nm = len(self.grid_types) self.nspts = [[] for _ in range(nm + 1)] self.errors = [[] for _ in range(nm + 1)] self.integrals = [[] for _ in range(nm + 1)] self.times = [[] for _ in range(nm + 1)] npts_list = [2**n for n in range(8, 14)] for npts in npts_list: time1 = time.time() integral = monte_carlo(self.pseudo_potential, self.cell_vectors, npts, self.cutoff) self.nspts[nm].append(npts) self.integrals[nm].append(integral) self.times[nm].append((time.time() - time1)) self.errors[nm].append(np.abs(self.integrals[nm][-1] - answer)) else: self.nspts = [[] for _ in range(len(self.grid_types))] self.errors = [[] for _ in range(len(self.grid_types))] self.integrals = [[] for _ in range(len(self.grid_types))] self.times = [[] for _ in range(len(self.grid_types))] integration_method = self.integration_methods[0] for (i, grid_centering) in enumerate(self.grid_centering_list): for grid_consts in self.grid_constants_list: for grid_angles in grid_angles_list: grid_vecs = make_ptvecs(grid_centering, grid_consts, grid_angles) time1 = time.time() npts, integral = integration_method( self.pseudo_potential, self.cell_vectors, grid_vecs, self.offset, self.origin, self.cutoff) self.nspts[i].append(npts) self.integrals[i].append(integral) self.times[i].append((time.time() - time1)) self.errors[i].append(np.abs(self.integrals[i][-1] - answer)) if save: np.save("%s_times" % self.pseudo_potential, self.times) np.save("%s_integrals" % self.pseudo_potential, self.integrals) np.save("%s_errors" % self.pseudo_potential, self.errors) if plot: if self.random: plt.loglog(self.nspts[nm], self.errors[nm], label="random", color="orange") for i in range(len(self.grid_types)): plt.loglog(self.nspts[i], self.errors[i], label=self.grid_types[i]) plt.xlabel("Number of samping points") plt.ylabel("Error") test = [1. / n**(2. / 3) for n in self.nspts[0]] plt.loglog(self.nspts[0], test, label="1/n**(2/3)") lgd = plt.legend(loc='center left', bbox_to_anchor=(1, 0.5)) plt.grid() plt.show() plt.close() for i in range(len(self.grid_types)): plt.loglog(self.nspts[i], self.times[i], label=self.grid_types[i]) plt.xlabel("Number of samping points") plt.ylabel("Time (s)") lgd = plt.legend(loc='center left', bbox_to_anchor=(1, 0.5)) plt.grid() plt.show() plt.close()
def PlotVaspBandStructure(file_loc, material, lat_type, lat_consts, lat_angles, energy_shift=0.0, fermi_level=False, elimits=False, save=False, show=True): """Plot the band structure from a VASP INCAR, KPOINT, and OUTCAR file. Args: file_loc (str): the location of the directory with the VASP output files. The KPOINTS file MUST be in a very particular format: there must be 4 introductory lines, each k-point must be on its own line and there must only be one space between pairs of k-points. There mustn't be any space after the last entered k-point. material (str): the material whose band structure is being plotted. lat_type (str): the lattice type energy_shift (float): energy shift for band structure fermi_level (bool): if true, plot the Fermi level. elimits (list): the energy window for the plot. save (bool): if true, the band structure will be saved. show (bool): if false, return none. This is useful for showing multiple plots. Returns: Display or save the band structure. """ # Get the correct symmetry point dictionary. if lat_type == "fcc": sympt_dict = fcc_sympts lat_centering = "face" elif lat_type == "bcc": sympt_dict = bcc_sympts lat_centering = "body" elif lat_type == "sc": sympt_dict = sc_sympts lat_centering = "prim" else: raise ValueError("Invalid lattice type") # Extract the lattice constant from the POSCAR file. sympt_list = [] with open(file_loc + "POSCAR", "r") as file: lat_const = "" f = file.readlines() for c in f[1]: try: int(c) lat_const += c except: if c == ".": lat_const += c if c == "!": break continue lat_const = float(lat_const) angstrom_to_Bohr = 1.889725989 lat_const *= angstrom_to_Bohr lat_vecs = make_ptvecs(lat_centering, lat_consts, lat_angles) rlat_vecs = make_rptvecs(lat_vecs) nbands = "" with open(file_loc + "INCAR", "r") as file: f = file.readlines() for line in f: if "NBANDS" in line: for l in line: try: int(l) nbands += l except ValueError: continue # nbands = int(nbands) nbands = 10 # Extract the total number of k-points, number of k-points per path, # the number of paths and the symmetry points from the KPOINTs file. with open(file_loc + "KPOINTS", "r") as file: f = file.readlines() npts_path = int(f[1].split()[0]) npaths = (len(f) - 3) / 3 nkpoints = int(npts_path * npaths) sympt_list = [] f = f[4:] for line in f: spt = "" sline = line.strip() for l in sline: if (l == " " or l == "!" or l == "." or l == "\t"): continue else: try: int(l) except: spt += l if spt != "": sympt_list.append(spt) for i, sympt in enumerate(sympt_list): if sympt == "gamma" or sympt == "Gamma": sympt_list[i] = "G" # Remove all duplicate symmetry points unique_sympts = [sympt_list[i] for i in range(0, len(sympt_list), 2)] + [sympt_list[-1]] # Replace symbols representing points with their lattice coordinates. lat_sympt_coords = [sympt_dict[sp] for sp in unique_sympts] car_sympt_coords = [np.dot(rlat_vecs, k) for k in lat_sympt_coords] with open(file_loc + "OUTCAR", "r") as file: f = file.readlines() EFERMI = "" for line in f: sline = line.strip() if "EFERMI" in sline: for c in sline: try: int(c) EFERMI += c except: if c == ".": EFERMI += c EFERMI = float(EFERMI) id_line = " band No. band energies occupation \n" with open(file_loc + "OUTCAR", "r") as file: f = file.readlines() energies = [] occupancies = [] en_occ = [] lat_kpoints = [] nkpt = 0 nkpts_dr = 0 # number of kpoints with duplicates removed for i, line in enumerate(f): if line == id_line: nkpt += 1 if nkpt % npts_path == 0 and nkpt != nkpoints: continue else: nkpts_dr += 1 energies.append([]) occupancies.append([]) en_occ.append([]) lat_kpoints.append(list(map(float, f[i - 1].split()[3:6]))) for j in range(1, nbands + 1): energies[nkpts_dr - 1].append( float(f[i + j].split()[1]) - energy_shift) occupancies[nkpts_dr - 1].append( float(f[i + j].split()[2])) en_occ[nkpts_dr - 1].append(energies[nkpts_dr - 1][-1] * (occupancies[nkpts_dr - 1][-1] / 2)) car_kpoints = [np.dot(rlat_vecs, k) for k in lat_kpoints] # Find the distances between symmetry points. nsympts = len(unique_sympts) sympt_dist = [0] + [ norm(car_sympt_coords[i + 1] - car_sympt_coords[i]) for i in range(nsympts - 1) ] # Create coordinates for plotting lines = [] for i in range(nsympts - 1): start = np.sum(sympt_dist[:i + 1]) stop = np.sum(sympt_dist[:i + 2]) if i == (nsympts - 2): lines += list(np.linspace(start, stop, npts_path)) else: lines += list(np.delete(np.linspace(start, stop, npts_path), -1)) for nb in range(nbands): ienergy = [] for nk in range(len(car_kpoints)): ienergy.append(energies[nk][nb]) if nb == 0: plt.plot(lines, ienergy, label="VASP Band structure", color="blue") else: plt.plot(lines, ienergy, color="blue") # Plot a vertical line at the symmetry points with proper labels. for i in range(nsympts): pos = np.sum(sympt_dist[:i + 1]) plt.axvline(x=pos, c="gray") tick_labels = unique_sympts tick_locs = [np.sum(sympt_dist[:i + 1]) for i in range(nsympts)] plt.xticks(tick_locs, tick_labels) # Adjust the energy range if one was provided. if elimits: plt.ylim(elimits) if fermi_level: plt.axhline(y=EFERMI, c="yellow", label="Fermi level") # Adjust the legend. lgd = plt.legend(loc='center left', bbox_to_anchor=(1, 0.5)) plt.xlim([0, np.sum(sympt_dist)]) plt.xlabel("Symmetry points") plt.ylabel("Energy (eV)") plt.title("%s Band Structure" % material) plt.grid(linestyle="dotted") if save: plt.savefig("%s_band_struct.pdf" % material, bbox_extra_artists=(lgd, ), bbox_inches='tight') elif show: plt.show() else: return None