Source code for pyprocar.core.kpath

__author__ = "Pedram Tavadze and Logan Lang"
__maintainer__ = "Pedram Tavadze and Logan Lang"
__email__ = "petavazohi@mail.wvu.edu, lllang@mix.wvu.edu"
__date__ = "March 31, 2020"


import numpy as np
import pyvista

from ..utils import mathematics

# TODO Add fmt option to other codes in write_to_file

[docs] class KPath:
[docs] def __init__( self, knames=None, kticks= None, special_kpoints=None, ngrids=None, has_time_reversal=True, ): """ The Kpath object to handle labels and ticks for band structure Parameters ---------- knames : List, optional List of Knames, by default None kticks : List, optional List of kticks that maps the knames to the kpoints, by default None special_kpoints : List, optional List of special kpoints, by default None ngrids : List, optional List of how many points are between special kpoints, by default None has_time_reversal : bool, optional Determine if the kpoints contain time reversal symmetry, by default True """ latex = "$" for x in knames: if "$" in x[0] or "$" in x[1]: latex = "" self.knames = [[latex + x[0] + latex, latex + x[1] + latex] for x in knames] self.special_kpoints = special_kpoints self.ngrids = ngrids self.kticks = kticks self.has_time_reversal = has_time_reversal
@property def nsegments(self): """The number of band segments Returns ------- int The number of band segments """ return len(self.knames) @property def tick_positions(self): """The list of tick positions Returns ------- List The list of tick positions """ if self.kticks is None: pos = 0 tick_positions = [pos] for isegment in range(self.nsegments): pos += self.ngrids[isegment] tick_positions.append(pos - 1) else: tick_positions = self.kticks return tick_positions @property def tick_names(self): """The list of tick names Returns ------- List The list of tick names """ tick_names = [self.knames[0][0], self.knames[0][1]] if len(self.knames) == 1: return tick_names for isegment in range(1, self.nsegments): if self.knames[isegment][0] != self.knames[isegment-1][1]: tick_names[-1] += "|" + self.knames[isegment][0] tick_names.append(self.knames[isegment][1]) return tick_names @property def kdistances(self): """An array with the kdistance along the kpath Returns ------- np.ndarray An array with the kdistance along the kpath """ distances = [] for isegment in range(self.nsegments): distances.append( np.linalg.norm( self.special_kpoints[isegment][0] - self.special_kpoints[isegment][1] ) ) return np.array(distances)
[docs] def get_optimized_kpoints_transformed( self, transformation_matrix, same_grid_size=False ): """ A method to get the optimized kpoints after a transformation Parameters ---------- transformation_matrix : np.ndarray The transformmation matrix. same_grid_size : bool Boolean to determine if the grid should retain the same size Returns ------- pyprocar.core.KPath The transformed KPath """ new_special_kpoints = np.dot(self.special_kpoints, transformation_matrix) new_ngrids = self.ngrids.copy() for isegment in range(self.nsegments): kstart = new_special_kpoints[isegment][0] kend = new_special_kpoints[isegment][1] kpoints_old = np.linspace( self.special_kpoints[isegment][0], self.special_kpoints[isegment][1], self.ngrids[isegment], ) dk_vector_old = kpoints_old[-1] - kpoints_old[-2] dk_old = np.linalg.norm(dk_vector_old) # this part is to find the direction distance = kend - kstart # this part is to find the high symmetry points on the path expand = (np.linspace(kstart, kend, 1000) * 2).round(0) / 2 unique_indexes = np.sort(np.unique(expand, return_index=True, axis=0)[1]) symm_kpoints_path = expand[unique_indexes] # this part is to only select poits that are after kstart and not before angles = np.array( [ mathematics.get_angle(x, distance, radians=False) for x in (symm_kpoints_path - kstart) ] ).round() symm_kpoints_path = symm_kpoints_path[angles == 0] if len(symm_kpoints_path) < 2: continue suggested_kstart = symm_kpoints_path[0] suggested_kend = symm_kpoints_path[1] if np.linalg.norm(distance) > np.linalg.norm( suggested_kend - suggested_kstart ): new_special_kpoints[isegment][0] = suggested_kstart new_special_kpoints[isegment][1] = suggested_kend # this part is to get the number of gird points in the to have the # same spacing is before the transformation if same_grid_size: new_ngrids[isegment] = int( ( np.linalg.norm( new_special_kpoints[isegment][0] - new_special_kpoints[isegment][1] ) / dk_old ).round(4) + 1 ) return KPath( knames=self.knames, special_kpoints=new_special_kpoints, ngrids=new_ngrids )
[docs] def get_kpoints_transformed( self, transformation_matrix, ): """A method to get the transformed kpoints Parameters ---------- transformation_matrix : np.ndarray The transformation matrix Returns ------- pyprocar.core.KPath The transformed KPath """ new_special_kpoints = np.dot(self.special_kpoints, transformation_matrix) return KPath( knames=self.knames, special_kpoints=new_special_kpoints, ngrids=self.ngrids )
[docs] def write_to_file(self, filename="KPOINTS", fmt="vasp"): """Write the kpath to a file. Only supports vasp at the moment Parameters ---------- filename : str, optional _description_, by default "KPOINTS" fmt : str, optional _description_, by default "vasp" """ with open(filename, "w") as wf: if fmt == "vasp": wf.write("! Generated by pyprocar\n") if len(np.unique(self.ngrids)) == 1: wf.write(str(self.ngrids[0]) + "\n") else: wf.write(" ".join([str(x) for x in self.ngrids]) + "\n") wf.write("Line-mode\n") wf.write("reciprocal\n") for isegment in range(self.nsegments): wf.write( " ".join( [ " {:8.4f}".format(x) for x in self.special_kpoints[isegment][0] ] ) + " ! " + self.knames[isegment][0].replace("$", "") + "\n" ) wf.write( " ".join( [ " {:8.4f}".format(x) for x in self.special_kpoints[isegment][1] ] ) + " ! " + self.knames[isegment][1].replace("$", "") + "\n" ) wf.write("\n") return None
def __str__(self): ret = "K-Path\n" ret += "------\n" for isegment in range(self.nsegments): ret += "{:>2}. {:<9}: ({:>.2f} {:>.2f} {:>.2f}) -> {:<9}: ({:>.2f} {:>.2f} {:>.2f})\n".format(isegment+1, self.knames[isegment][0], self.special_kpoints[isegment][0][0], self.special_kpoints[isegment][0][1], self.special_kpoints[isegment][0][2], self.knames[isegment][1], self.special_kpoints[isegment][1][0], self.special_kpoints[isegment][1][1], self.special_kpoints[isegment][1][2]) return ret