Source code for EPWpy.utilities.EPW_util

""" For epw related utilities """
import numpy as np
import os
from EPWpy.utilities.printing import *
from EPWpy.plotting import plot_polaron_matplotlib as plot_pl
from EPWpy.plotting import plot_wannier_matplotlib as plot_wann
from EPWpy.utilities import kramers_kronig as kk
import re

[docs] class EPWProperties: """ Class representing computed properties from EPW (Electron-Phonon Wannier) calculations. This class stores and organizes EPW-calculated quantities such as electron-phonon coupling matrices and polaronic displacements. It is typically used to read, process, and analyze EPW output data. Attributes ---------- file : str Name of the EPW output file containing computed properties. system : str Name or prefix of the system being studied. prefix : str Alias for the system, used to construct file paths. energy_range : float, optional Energy range for properties extraction or analysis (e.g., around Fermi level). epw_params : dict, optional Dictionary of EPW parameters used for calculations (can include grid size, smearing, interpolation settings, etc.). epw_fold : str Name of the EPW output folder. Default is 'epw'. epw_file : str Full path to the EPW output file, constructed from system, folder, and filename. Notes ----- - This class is intended for post-processing EPW output. - The primary properties handled include: - `dtau` : polaronic displacements. - `gkk` : electron-phonon matrix elements. Examples -------- >>> props = EPWProperties(file='epw2.out', system='graphene', energy_range=0.5) >>> print(props.epw_file) 'graphene/epw/epw1.out' """ def __init__(self, file: str, system: str, energy_range: float = None, epw_fold: str = 'epw', epw_params: dict = None, version: float = 5.9): """ Initialize the EPWProperties object. Parameters ---------- file : str Name of the EPW output file. system : str Prefix or name of the system being studied. energy_range : float, optional Energy range to consider for the properties. epw_fold : str, optional Name of the EPW output folder. Default is 'epw'. epw_params : dict, optional Dictionary of EPW calculation parameters. """ self.file = file self.system = system self.prefix = self.system self.energy_range = energy_range self.epw_params = epw_params self.epw_fold = epw_fold self.epw_file = f'{self.system}/{self.epw_fold}/{self.file}' self.epw_ver = version @property def dtau(self): """ Polaron displacement from EPW. Returns ------- array-like or None Returns the polaron displacement if the EPW file exists; otherwise, returns None. """ try: return read_dtau(self.epw_file) except FileNotFoundError: return None @property def psir_plrn(self): """ Polaron wavefunction from EPW. Returns ------- array-like or None Returns the polaron wavefunction if the EPW file exists; otherwise, returns None. """ try: return read_psir_plrn(self.epw_file) except FileNotFoundError: return None @property def gkk(self): """ Electron-phonon matrix elements. The returned array has dimensions g(ibnd, jbnd, imode, ik, iq). Returns ------- array-like or None Electron-phonon matrix if the EPW file exists; otherwise, prints a message and returns None. """ try: return get_gkk(self.epw_file, self.epw_ver) except FileNotFoundError: print('EPW file not found') return None @property def ibte_mobility_e(self): _,ibte_mob = read_mobility(f'{self.epw_file}',float(self.epw_params['temps'])) return(ibte_mob) @property def serta_mobility_e(self): serta_mob,_ = read_mobility(f'{self.epw_file}',float(self.epw_params['temps'])) return(serta_mob) @property def ibte_mobility_h(self): _,ibte_mob = read_mobility(f'{self.epw_file}',float(self.epw_params['temps']),typ='Hole') return(ibte_mob) @property def serta_mobility_h(self): serta_mob,_ = read_mobility(f'{self.epw_file}',float(self.epw_params['temps']),typ = 'Hole') return(serta_mob) @property def Eform(self): Ef = read_plrn(f'{self.epw_file}') return(Ef) @property def relaxation_time_e(self): """ get electron relaxation time """ if (self.epw_ver >= 6.0): return(read_inv_taucb(f'{self.system}/{self.epw_fold}/inv_taucb_0.fmt')) else: return(read_inv_taucb(f'{self.system}/{self.epw_fold}/inv_taucb.fmt')) @property def relaxation_time_h(self): """ get hole relaxation time """ if (self.epw_ver >= 6.0): return(read_inv_taucb(f'{self.system}/{self.epw_fold}/inv_tau_0.fmt')) else: return(read_inv_taucb(f'{self.system}/{self.epw_fold}/inv_tau.fmt')) @property def relaxation_time_imp(self): """ get ionized impurity relaxation time """ if (self.epw_ver >= 6.0): return(read_inv_taucb(f'{self.system}/{self.epw_fold}/inv_tau_0.fmt')) else: return(read_inv_taucb(f'{self.system}/{self.epw_fold}/inv_tau.fmt'))
[docs] def eps1(self, eps0=1.0, leng=100): T=self.temp file = f'{self.system}/{self.epw_fold}' if('lindabs' in self.epw_params.keys()): _,eps_real,_,_ = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K.dat',eps0,leng) if('loptabs' in self.epw_params.keys()): _,eps_real,_,_ = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K_'+self.epw_params['meshnum']+'.dat',eps0, leng) return(eps_real)
[docs] def nr(self, eps0=1, leng=100): file = f'{self.system}/{self.epw_fold}' T=self.temp#default_epw_input['temps'] if('lindabs' in self.epw_params.keys()): nr,eps_real,_,_ = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K.dat',eps0,leng) if('loptabs' in self.epw_params.keys()): nr,eps_real,_,_ = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K_'+self.default_epw_input['meshnum']+'.dat',eps0,leng) return(nr)
[docs] def eps2(self, eps0=1, leng=100): file = f'{self.system}/{self.epw_fold}' T=self.temp#default_epw_input['temps'] if('lindabs' in self.epw_params.keys()): _,_,eps2,_ = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K.dat', eps0,leng) if('loptabs' in self.epw_params.keys()): _,_,eps2,_ = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K_'+self.default_epw_input['meshnum']+'.dat',eps,leng) return(eps2)
[docs] def omega(self, eps0=1, leng=100): file = f'{self.system}/{self.epw_fold}' T=self.temp#default_epw_input['temps'] if('lindabs' in self.epw_params.keys()): _,_,_,omega = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K.dat',eps0,leng) if('loptabs' in self.epw_params.keys()): _,_,_,omega = kk.calc_epr_real(f'{file}/epsilon2_indabs_'+str(T)+'K_'+self.default_epw_input['meshnum']+'.dat',eps0,leng) return(omega)
[docs] def optical_properties(self, eps: int = 1, leng: int = None, Temps= None): """ Extract and return optical properties from EPW output. This method parses the dielectric response (ε₂) data obtained from EPW calculations. It reads the direct, indirect, and quasi-degenerate perturbation theory (QDPT) contributions to the imaginary part of the dielectric function from the `epsilon2` output files. Parameters ---------- eps : int, optional Index specifying which dielectric tensor component to extract (default: ``1``). leng : int, optional Number of frequency points to read. If ``None``, uses the full data length (default: ``None``). Temps : list or None, optional List of temperatures at which the optical properties are computed. If not provided, attempts to read from ``self.epw_params['temps']``. Returns ------- dict Dictionary containing dielectric data: - ``'direct'`` : dict Direct electronic transitions contribution. - ``'indirect'`` : dict Phonon-assisted (indirect) transitions contribution. - ``'qdpt'`` : dict QDPT-corrected optical response. Notes ----- - The method automatically detects available temperatures from EPW input parameters. - Currently, only ε₂ (imaginary part) is parsed; ε₁ and derived quantities (e.g., refractive index) can be added later. Example ------- >>> epw = EPWProperties(file='epw2.out', system='Si', epw_fold='epw') >>> data = epw.optical_properties() >>> direct_eps2 = data['direct'] """ file = f'{self.system}/{self.epw_fold}' if Temps is None: T = self.epw_params['temps'] print('Available temps:', T) try: arr = [float(x) for x in T.split()] except AttributeError: arr = np.array([T,0]) TT = arr[0] direct, indirect, qdpt = read_epsilon2_files(f'{file}') for key in direct.keys(): raw_eps2 = direct[key] if leng is None: leng = len(raw_eps2[:,0]) #omega = self.omega(leng = leng) #eps1 = self.eps1(leng = leng) #eps2 = self.eps2(leng = leng) #nr = self.nr(leng = leng) return {'direct':direct, 'indirect':indirect, 'qdpt':qdpt}
[docs] def wannier_files(self): """ List all Wannier cube files in the EPW output folder. This method scans the EPW output folder for files with the `.cube` extension, which are typically Wannier-related outputs for visualization. Returns ------- list of str List of cube filenames present in the folder. Example ------- >>> props = EPWProperties(file='epw1.out', system='graphene') >>> cube_files = props.wannier_files() Present Wannier files are: graphene_epw1.cube graphene_epw2.cube """ folder = f'{self.system}/{self.epw_fold}' cube_files = [f for f in os.listdir(folder) if f.endswith(".cube")] print('Present Wannier files are:') for files in cube_files: print(files) return cube_files
[docs] def polaron(self, psir_file='psir_plrn.xsf', dtau_file='dtau_disp.plrn'): """ Retrieve polaron properties from EPW output files. Reads the polaron wavefunction and displacement from EPW-generated files and returns them in a dictionary along with formation energy. Parameters ---------- psir_file : str, optional Filename for the polaron wavefunction. Default is 'psir_plrn.xsf'. dtau_file : str, optional Filename for the polaron displacement. Default is 'dtau_disp.plrn'. Returns ------- dict Dictionary containing: - 'dtau': polaronic displacement - 'psir_plrn': polaron wavefunction - 'Eform': polaron formation energy Example ------- >>> props = EPWProperties(file='epw1.out', system='graphene') >>> polaron_data = props.polaron() >>> print(polaron_data['dtau']) """ self.epw_file = f'{self.system}/{self.epw_fold}/{psir_file}' psir_plrn = self.psir_plrn self.epw_file = f'{self.system}/{self.epw_fold}/{dtau_file}' dtau = self.dtau self.epw_file = f'{self.system}/{self.epw_fold}/{self.file}' return {'dtau': dtau, 'psir_plrn': psir_plrn, 'Eform': self.Eform}
[docs] def transport(self, tau=False): """ Retrieve transport properties from EPW calculations. Returns a dictionary of electron and hole mobilities, as well as relaxation times for both carrier types. Values are available for both semi-classical (SERTA) and fully iterated (IBTE) Boltzmann transport approaches. Parameters ---------- tau : bool, optional If True, may be used in future to scale properties by relaxation time (currently not implemented). Default is False. Returns ------- dict Dictionary containing transport properties: - 'serta_mobility_elec': electron mobility (SERTA) - 'serta_mobility_hole': hole mobility (SERTA) - 'ibte_mobility_elec': electron mobility (IBTE) - 'ibte_mobility_hole': hole mobility (IBTE) - 'relaxation_time_elec': electron relaxation time - 'relaxation_time_hole': hole relaxation time Example ------- >>> props = EPWProperties(file='epw1.out', system='graphene') >>> transport_data = props.transport() >>> print(transport_data['serta_mobility_elec']) """ return {'serta_mobility_elec': self.serta_mobility_e, 'serta_mobility_hole': self.serta_mobility_h, 'ibte_mobility_elec': self.ibte_mobility_e, 'ibte_mobility_hole': self.ibte_mobility_h, 'relaxation_time_elec': self.relaxation_time_e, 'relaxation_time_hole': self.relaxation_time_h}
[docs] def elph(self): """ Retrieve electron-phonon (el-ph) data from EPW output. Returns a dictionary containing the electron-phonon matrix and derived properties such as the number of k-points, q-points, modes, and bands. Returns ------- dict Dictionary with keys: - 'gkk' : electron-phonon matrix elements g(ibnd, jbnd, imode, ik, iq) - 'nk' : number of k-points - 'nq' : number of q-points - 'nmode' : number of phonon modes - 'nband' : number of electronic bands Example ------- >>> props = EPWProperties(file='epw1.out', system='graphene') >>> elph_data = props.elph() >>> print(elph_data['nk']) """ return {'gkk': self.gkk, 'nk': len(self.gkk[0, 0, :, 0, 0]), 'nq': len(self.gkk[0, 0, 0, :, 0]), 'nmode': len(self.gkk[0, 0, 0, 0, :]), 'nband': len(self.gkk[:, 0, 0, 0, 0]) }
[docs] def plot_polaron(self, psir_file='psir_plrn.xsf', view=None): """ Plot the polaron wavefunction using Matplotlib. Parameters ---------- psir_file : str, optional Filename of the polaron wavefunction. Default is 'psir_plrn.xsf'. view : dict, optional Dictionary of plotting parameters. Can include 'iso_level' to set the isosurface threshold. Default is {}. Returns ------- matplotlib.figure.Figure Matplotlib figure object showing the polaron isosurface. Example ------- >>> props = EPWProperties(file='epw1.out', system='graphene') >>> fig = props.plot_polaron(iso_level=0.01) """ if view is None: view = {} Data = read_psir_plrn(f'{self.system}/{self.epw_fold}/{psir_file}') iso_level = 0.005 if 'iso_level' in view.keys(): iso_level = view['iso_level'] return plot_pl.plot_psir_plrn_matplotlib( Data, view=view, iso_level=iso_level*np.nanmax(Data['Density']) )
[docs] def plot_wannier(self, wannier_file=None, view=None, bond_length=5.0): """ Plot Wannier functions (cube files) using Matplotlib. Parameters ---------- wannier_file : str, optional Filename of the cube file to plot. If None, the first available Wannier cube file in the folder is used. view : dict, optional Dictionary of plotting parameters (e.g., camera angles). Default is {}. bond_length : float, optional Bond length cutoff used to read the cube file. Default is 5.0 Å. Returns ------- matplotlib.figure.Figure Matplotlib figure object showing the Wannier isosurfaces. Example ------- >>> props = EPWProperties(file='epw1.out', system='graphene') >>> fig = props.plot_wannier(bond_length=4.0) """ if view is None: view = {} if wannier_file is None: wannier_file = self.wannier_files()[0] print(f'Plotting {wannier_file} since no file was provided') Data = read_cube_file(f'{self.system}/{self.epw_fold}/{wannier_file}', bond_length) return plot_wann.plot_isosurfaces_matplotlib(Data, view=view)
[docs] def read_epsilon2_files(folder_path): """ Reads all epsilon2 files in the given folder and categorizes them into: - direct: 'dirabs' - indirect: 'indabs' without number suffix - qdpt: 'indabs' with number suffix (e.g., _12, _5, etc.) Returns: direct: dict of {filename: data} indirect: dict of {filename: data} qdpt: dict of {filename: data} """ direct = {} indirect = {} qdpt = {} dirnum = 0 indnum = 0 qdptnum = 0 for fname in os.listdir(folder_path): if fname.startswith("epsilon2") and fname.endswith(".dat"): path = os.path.join(folder_path, fname) try: data = np.loadtxt(path) except Exception as e: print(f"Error reading {fname}: {e}") continue if "dirabs" in fname: direct[f'{dirnum}'] = data dirnum +=1 elif "indabs" in fname: # QDPT files have a number suffix before '.dat' if re.search(r'_?\d+\.dat$', fname): qdpt[f'{qdptnum}'] = data qdptnum +=1 else: indirect[f'{indnum}'] = data indnum +=1 return direct, indirect, qdpt
[docs] def read_inv_taucb(filein): """ reads inverse taucb """ inv_tau=np.loadtxt(f'{filein}') inv_tau[:,3]=inv_tau[:,3]*13.6 inv_tau[:,4]=inv_tau[:,4]*20670.6944033 return(inv_tau)
[docs] def read_plrn(file1): with open(file1, 'r') as f: for line in f: if( len(line.split())>0 ): if( line.split()[0] == 'Formation'): Eform=float(line.split()[3]) return(Eform)
[docs] def sig2eig(filename, outfile, eigs=11): Data = [] eig = [] with open (f'{filename}','r') as f: for line in f: Data.append(line.split()[-1]) eig.append(int(line.split()[0])) t = 0 eigs = max(eig) with open(f'{outfile}','w') as f: f.write(' \n') f.write(' \n') for data in Data: if (t == eigs): f.write(' \n') t = 0 f.write(f'{data}\n') t += 1 return(Data)
[docs] def get_connections(x, y, z, bond_leng=3.5, min_leng=0.4): connections = list() distance = [] for i in range(len(x)): for j in range(len(x)): dist = np.sqrt((x[i]-x[j])**2+(y[i]-y[j])**2+(z[i]-z[j])**2) if ((dist < bond_leng) & (dist > min_leng)): connections.append((i, j)) distance.append(dist) return(connections, distance)
[docs] def read_dtau(filein): """ reads the dtau """ t = 0 data=[] x=[] y=[] z=[] u=[] v=[] w=[] ilength = np.zeros((3,1),dtype=int) nline = 1000 mat = [] with open(filein, 'r') as f: for line in f: if (t == 10): nline = int(line.split()[0]) print(nline) if ((t>10) & (t<=10+nline)): mat.append(int(line.split()[0])) x.append(float(line.split()[1])) y.append(float(line.split()[2])) z.append(float(line.split()[3])) u.append(float(line.split()[4])) v.append(float(line.split()[5])) w.append(float(line.split()[6])) t +=1 x = np.array(x) y = np.array(y) z = np.array(z) u = np.array(u) v = np.array(v) w = np.array(w) mat = np.array(mat) connections,_ = get_connections(x,y,z) connections = np.array(connections) return(x,y,z,u,v,w,connections,mat)
[docs] def read_psir_plrn(filein, bond_leng=2.5): """ reads the psir_plrn """ t = 0 data=[] x=[] y=[] z=[] u=[] v=[] w=[] ilength = np.zeros((3,1),dtype=int) lattice_vec = np.zeros((3,3),dtype = float) nline = 1000 mat = [] grid = list() Density=[] base =1e12 base2=2e12 mat = [] with open(filein, 'r') as f: for line in f: if ((t > 5) & (t<9)): print(line.split()) lattice_vec[t-6,:] = np.array(line.split()[:]).astype(float) if (t == 10): nline = int(line.split()[0]) print(nline) if ((t>10) & (t<=10+nline)): # print(line.split()[0]) mat.append(int(line.split()[0])) x.append(float(line.split()[1])) y.append(float(line.split()[2])) z.append(float(line.split()[3])) u.append(float(line.split()[4])) v.append(float(line.split()[5])) w.append(float(line.split()[6])) if (t == 10+nline+6): base = 10+nline+10 for d in line.split(): grid.append(int(d)) base2 = grid[0]*grid[1]*grid[2] if ((t > base) & (t < base+base2-1)): try: for data in line.split(): Density.append(float(data)) except ValueError: break #dd +=1 t +=1 Dense = np.zeros((grid[0],grid[1],grid[2]),dtype = float) x = np.array(x) y = np.array(y) z = np.array(z) u = np.array(u) v = np.array(v) w = np.array(w) mat = np.array(mat) Grid = np.zeros((grid[0],grid[1],grid[2],3),dtype = float) xx,yy,zz = np.mgrid[0:1:int(grid[0])*1j,0:1:grid[1]*1j,0:1:grid[2]*1j] t = 0 pts = [] #print(np.shape(Dense)) #print(len(Density)) for i in range(grid[0]): for j in range(grid[1]): for k in range(grid[2]): Grid[i,j,k,:] = lattice_vec[0,:]*xx[i,j,k]+lattice_vec[1,:]*yy[i,j,k]+lattice_vec[2,:]*zz[i,j,k] Dense[i,j,k] = Density[t] pts.append(Grid[i,j,k,:]) t +=1 connections,_ = get_connections(x,y,z, bond_leng = bond_leng) Data = {'mat':mat, 'x':x, 'y':y, 'z':z, 'u':u, 'v':v, 'w':w, 'Dense':Dense, 'Grid':Grid, 'pts':pts, 'Density':Density, 'connections':np.array(connections)} return(Data)
[docs] def read_wfc(filein): """ reads the psir_plrn """ t = 0 data=[] x=[] y=[] z=[] u=[] v=[] w=[] ilength = np.zeros((3,1),dtype=int) nline = 1000 mat = [] with open(filein, 'r') as f: for line in f: if (t == 10): nline = int(line.split()[0]) print(nline) if((t>10) & (t<=10+nline)): mat.append(int(line.split()[0])) x.append(float(line.split()[1])) y.append(float(line.split()[2])) z.append(float(line.split()[3])) u.append(float(line.split()[4])) v.append(float(line.split()[5])) w.append(float(line.split()[6])) t +=1 x = np.array(x) y = np.array(y) z = np.array(z) u = np.array(u) v = np.array(v) w = np.array(w) mat = np.array(mat) connections,_ = get_connections(x,y,z) return(x,y,z,u,v,w,np.array(connections),mat)
[docs] def read_cube_file(filein, bond_leng=3.5): """ Read a .cube file and return the scalar field data, grid dimensions, atomic positions, origin, and axis vectors. """ Data = {} with open(filein, 'r') as f: lines = f.readlines() # Skip the first two comment lines i = 2 num_atoms, origin_x, origin_y, origin_z = map(float, lines[i].split()) # Number of atoms and origin num_atoms = int(num_atoms) i += 1 # Read the grid dimensions and voxel information grid_info = [] for _ in range(3): grid_info.append(list(map(float, lines[i].split()))) i += 1 # Extract grid dimensions and axis vectors n_points_x, n_points_y, n_points_z = abs(int(grid_info[0][0])), abs(int(grid_info[1][0])), abs(int(grid_info[2][0])) spacing_x, spacing_y, spacing_z = grid_info[0][1], grid_info[1][1], grid_info[2][1] axis_x = np.array(grid_info[0][1:4]) # Axis vector for X axis_y = np.array(grid_info[1][1:4]) # Axis vector for Y axis_z = np.array(grid_info[2][1:4]) # Axis vector for Z # Atoms data atomic_positions = [] for _ in range(num_atoms): parts = lines[i].split() atom_number = int(parts[0]) # Atom number charge = float(parts[1]) # Atom charge x, y, z = map(float, parts[2:]) # Atomic position atomic_positions.append((atom_number, charge, x, y, z)) i += 1 # Bonds connections,_ = get_connections(np.array(atomic_positions)[:,2],np.array(atomic_positions)[:,3],np.array(atomic_positions)[:,4], bond_leng) connections = np.array(connections) # Read the scalar field data scalar_data = [] expected_num_values = n_points_x * n_points_y * n_points_z # Collect scalar data in a list of lists while i < len(lines): line = lines[i].split() if len(line) > 0: scalar_data.append(line) i += 1 # Flatten the scalar data and ensure correct number of values scalar_data = [float(value) for sublist in scalar_data for value in sublist] # Check if the scalar data contains the expected number of values scalar_data_size = len(scalar_data) if scalar_data_size != expected_num_values: print(f"Warning: Expected {expected_num_values} scalar data points, but found {scalar_data_size}.") # Pad the data with NaN if it is smaller than expected if scalar_data_size < expected_num_values: padding = [np.nan] * (expected_num_values - scalar_data_size) scalar_data.extend(padding) # Truncate if there are more values than expected elif scalar_data_size > expected_num_values: scalar_data = scalar_data[:expected_num_values] # Reshape the scalar data into a 3D grid scalar_data = np.array(scalar_data).reshape((n_points_z, n_points_y, n_points_x)) # Return all extracted data Data = {'scalar_data': scalar_data, 'grid_shape': (n_points_x, n_points_y, n_points_z), 'spacing': (spacing_x, spacing_y, spacing_z), 'origin': (origin_x, origin_y, origin_z), 'atomic_positions': atomic_positions, 'connections': connections, 'axis_x': axis_x, 'axis_y': axis_y, 'axis_z': axis_z} return Data
[docs] def read_gkk(filn, nbnd, nq, nk, nmode, version=5.9): """ Read the g matrix """ G=np.zeros((nbnd,nbnd,nmode,nk,nq),dtype=float) t=0 iq=0 with open(filn,'r') as f: k=0 q=0 flag = 0 read=0 for line in f: if ('ik ' in line): k +=1 if (k == nk+1): k = 1 if ('iq ' in line): q +=1 if (q == nq+1): q = 1 if (('-----' in line) & (k > 0)): flag = 1 if (('-----' in line) & (flag >= 2)): read = 0 flag = 0 if ((flag >= 2) & (len(line.split())>0)): #print(line.split()) try: ibnd=int(line.split()[0])-1 jbnd=int(line.split()[1])-1 imode=int(line.split()[2])-1 if (version >= 6.0): G[ibnd,jbnd,imode,k-1,q-1] = float(line.split()[-3]) else: G[ibnd,jbnd,imode,k-1,q-1] = float(line.split()[-1]) flag +=1 except ValueError: flag = 0 if (flag >= 1): flag +=1 f.close() return(G)
[docs] def get_gkk(epw_file, version=5.9): nq = [] nk = [] q_arr = [] k_arr = [] g_mat = [] band1=[] band2=[] mode = [] kk = [] qq = [] t = 0 iq = 0 ik = 0 with open(epw_file, 'r') as f: for line in f: if ('iq' in line): Arr = line.split()[2:] t = 1 if (Arr in q_arr): pass else: q_arr.append(Arr) iq +=1 if ('ik' in line): Arr = line.split()[2:] if (Arr in k_arr): pass else: k_arr.append(Arr) ik +=1 t = t*2 if ((t > 16) & ('-----------' in line)): t = 0 if (t > 16): kk.append(ik) qq.append(iq) band1.append(int(line.split()[0])) band2.append(int(line.split()[1])) mode.append(int(line.split()[2])) g_mat.append(float(line.split()[-1])) f.close() g = read_gkk(epw_file, max(band1), len(q_arr), len(k_arr), max(mode), version=version) return(g)
[docs] def read_ibndmin(file): """ Reads the minimum bands used""" with open(file, 'r') as f: for line in f: if('ibndmin' in line): ibndmin = int(line.split()[2]) f.close() return(ibndmin)
[docs] def read_ibndmax(file): """ Reads the maximum number of bands """ with open(file, 'r') as f: for line in f: if('ibndmax' in line): ibndmax = int(line.split()[2]) f.close() return(ibndmax)
[docs] def read_mobility(file, T, typ='Elec'): read_mob_serta = False read_mob_ibte = False read_serta = False read_ibte = False serta_mob=np.zeros((3,3),dtype=float) ibte_mob=np.zeros((3,3),dtype=float) temp_found = False t = 0 with open(f'{file}', 'r') as f: for line in f: if ((len(line.split()) != 0) & (read_mob_serta == True)): try: if((float(line.split()[0]) == T) & (t == 0)): temp_found = True if(temp_found == True): serta_mob[t,:] = np.array(line.split()).astype(float)[-3:] t +=1 if(t == 3): read_mob_serta = False read_serta = False t = 0 temp_found = False except ValueError: pass if ((len(line.split()) != 0) & (read_mob_ibte == True)): try: if((float(line.split()[0]) == T) & (t == 0)): temp_found = True if(temp_found == True): ibte_mob[t,:] = np.array(line.split()).astype(float)[-3:] t +=1 if(t == 3): read_mob_ibte = False t = 0 temp_found = False except ValueError: pass if ('SERTA' in line): read_serta = True if ('Iteration number' in line): read_ibte = True if (f'Drift {typ} mobility' in line): if(read_serta == True) : read_mob_serta = True elif(read_ibte == True): read_mob_ibte = True f.close() return(serta_mob, ibte_mob)
[docs] def read_grr(filn, nbnd, nq, nmode): pass
[docs] def read_dtau_plrn(filename): """ For reading dtau.plrn for further postprocessing """ file = open(filename, 'r'); control = ( file.readline() ).split(); control = np.array(control, dtype=int); # dim = [control[0], control[1], control[2]] dim.append(control[-1] // 3) dim.append(3) # disp = np.zeros((dim),dtype=complex); # for ix in range(control[0]): for iy in range(control[1]): for iz in range(control[2]): for iat in range(dim[-2]): for icart in range(3): temp = ( file.readline() ).split() disp[ix,iy,iz,iat,icart] = float(temp[-2]) + 1j * float(temp[-1]) # file.close(); return disp
[docs] def generate_dtau_disp(dtau, direction='x'): """ Shift the polaron along the [100] direction """ shift_x = np.zeros((np.shape(dtau)),dtype=complex); # params = np.shape(dtau) # idx = np.zeros((params[0]), dtype=int) idy = np.zeros((params[0]), dtype=int) idz = np.zeros((params[0]), dtype=int) idx[0:params[0] - 1] = np.arange(1, params[0], dtype=int); idx[-1] = 0 idy[0:params[1] - 1] = np.arange(1, params[1], dtype=int); idy[-1] = 0 idz[0:params[2] - 1] = np.arange(1, params[2], dtype=int); idz[-1] = 0 # for ix in range(params[0]): for iy in range(params[1]): for iz in range(params[2]): for iat in range(params[3]): for icart in range(3): if direction == 'x': shift_x[idx[ix], iy, iz, iat, icart] = dtau[ix, iy, iz, iat, icart] elif direction == 'y': shift_x[ix, idy[iy], iz, iat, icart] = dtau[ix, iy, iz, iat, icart] elif direction == 'z': shift_x[ix, iy, idz[iz], iat, icart] = dtau[ix, iy, iz, iat, icart] else: raise ValueError("Only shift directions x, y, z are implemented.") # return shift_x
[docs] def write_dtau_disp(initial, final, ntau, fileout): """ Generate the dtau_disp.plrn_* files """ params = np.shape(final) midpoints = ntau; scaling = np.linspace(0,1,ntau,endpoint=True) shift = final - initial for i in range(midpoints): file = open(fileout + str(i+1), 'w'); file.write( '{:10d}'.format(params[0]) + '{:10d}'.format(params[1]) + '{:10d}'.format(params[2]) ); file.write( '{:10d}'.format(params[0] * params[1] * params[2]) ); file.write( '{:10d}'.format(params[3] * 3) + '\n'); for ix in range(params[0]): for iy in range(params[1]): for iz in range(params[2]): iunit = ix * params[1] * params[2] + iy * params[2] + iz + 1 for iat in range(params[3]): for icart in range(3): ifree = iat * 3 + icart + 1 disp_value = shift[ix, iy, iz, iat, icart] * scaling[i]+\ initial[ix, iy, iz, iat, icart]; file.write( '{:18.10E}'.format(np.real(disp_value)) + '{:18.10E}'.format(np.imag(disp_value)) +'\n' ) file.close();
if __name__=="__main__": #from plot_g import * import matplotlib.pyplot as plt G=get_gkk('notebooks_basic/si/epw/epw1.out') print(np.shape(G)) #plot_gkk(4,4,G[:,:,:,0,:]) #plt.show() # print(np.shape(G)) # print(G)