def get_kpoints_weight(outcar, nkpts): """ :param outcar: content of the OUTCAR file (list of strings) :param nkpts: number of kpoints (int) :return: """ index_weight = bf.grep(outcar, 'k-points in reciprocal lattice and weights', nb_found=1)[0][1] index_weight_end = [ g for f, g in zip(outcar, range(len(outcar))) if (f == ' ') & (g > index_weight) ][0] kpoints_weights_str = outcar[index_weight + 1:index_weight_end] kpoints_weights = np.transpose([[float(f) for f in q.split()] for q in kpoints_weights_str])[-1] if len(kpoints_weights) != nkpts: raise bf.PydefImportError( 'Number of kpoint weights retrieved and number of kpoints are not consistent' ) else: return kpoints_weights
def get_electrostatic_potentials(outcar, atoms): """ :param outcar: content of the OUTCAR file (list of strings) :param atoms: number of atoms of each atomic species (list of integers) :return: electrostatic averaged potentials """ index_potentials = bf.grep( outcar, 'average (electrostatic) potential at core', nb_found=1)[0][1] # index where the potentials are index_potentials_end = [ g for f, g in zip(outcar, range(len(outcar))) if (f == ' ') & (g > index_potentials) ][0] # first blank line after the potentials potentials_str = outcar[ index_potentials + 3:index_potentials_end] # all of potentials as strings potentials_atom_nb = np.concatenate( [[float(f) for f in re.split(' |-', q)[1:]] for q in potentials_str]) # All potentials and corresponding atom potentials = [-f[1] for f in np.split(potentials_atom_nb, len(atoms))] if len(potentials) != len(atoms): raise bf.PydefImportError( 'Number of electrostatic potentials retrieved and number are not consistent' ) return dict(zip(list(atoms), potentials))
def get_band_occupation(outcar, nkpts, functional, nbands): """ :param outcar: content of the outcar file (list of strings) :param nkpts: number of kpoints (int) :param functional: functional used (string) :param nbands: number of bands (int) :return: last energy and occupation of the bands for each kpoint """ kpoints_indices = [f[1] for f in bf.grep(outcar, 'k-point ')[-nkpts:] ] # last k-points occupation calculated if functional != 'G0W0@GGA' and functional != 'GW0@GGA': nb_bands = [[ g for f, g in zip(outcar, range(len(outcar))) if (f == '') & (g > h) ][0] for h in kpoints_indices ][0] - kpoints_indices[0] - 2 # number of bands # check that the number of bands found is consistence with the number if bands retrieved previously if nb_bands != nbands: raise bf.PydefImportError( 'The number of bands retrieved from the kpoints is not consistent' ) bands_str = [outcar[f + 2:f + nb_bands + 2] for f in kpoints_indices ] # band occupation for each k-points as strings col_index = 1 # index of the column containing the energy else: nb_bands = [[ g for f, g in zip(outcar, range(len(outcar))) if (f == '') & (g > h) ][0] for h in kpoints_indices ][1] - kpoints_indices[0] - 6 # number of bands bands_str = [outcar[f + 3:f + 3 + nb_bands] for f in kpoints_indices ] # band occupation for each k-points as strings col_index = 2 # index of the column containing the energy bands_data = [ np.transpose([[float(s) for s in f.split()] for f in q]) for q in bands_str ] # band occupation for each k-points return [[f[col_index], f[-1]] for f in bands_data ] # return energy and occupation for each k-point
def get_atoms_positions(outcar, atoms): """ :param outcar: content of the outcar file (list of strings) :param atoms: number of atoms of each atomic species (list of integers) :return: position of each atom as a dictionary """ index_beg = bf.grep( outcar, 'position of ions in cartesian coordinates (Angst):', nb_found=1)[0][1] + 1 # index of the first atom position index_end = [ f[1] for f in bf.grep(outcar, '---------') if f[1] > index_beg ][0] - 3 # index of the last atom position atoms_positions = [[float(f) for f in g.split()] for g in outcar[index_beg:index_end]] # Check that the number of positions retrieved is equal to the number of atoms if len(atoms_positions) != len(atoms): raise bf.PydefImportError( "The number of atoms positions is not consistent with the total number of atoms" ) else: return dict(zip(atoms, atoms_positions))
def __init__(self, OUTCAR, DOSCAR): """ :param OUTCAR: location of the OUTCAR file (string) :param DOSCAR: location of the DOSCAR file (string) :return: an object containing data on the calculation """ # --------------------------------------------------- OUTCAR --------------------------------------------------- self.OUTCAR = OUTCAR self.DOSCAR = DOSCAR self.outcar = bf.read_file(OUTCAR) # content of the OUTCAR file if self.outcar[0][:6] != ' vasp.': raise bf.PydefOutcarError( 'The given file appears to not be a valid OUTCAR file.') # ------------------------------------------- CALCULATION PROPERTIES ------------------------------------------- self.functional, self.functional_title = get_functional( self.outcar) # functional used self.nedos = bf.grep(self.outcar, 'NEDOS =', 0, 'number of ions', 'int', 1) # number of point in the DOS self.encut = bf.grep(self.outcar, 'ENCUT =', 0, 'eV', 'float', 1) # ENCUT used self.ediff = bf.grep(self.outcar, 'EDIFF =', 0, 'stopping', 'float', 1) # EDIFF value self.emin = bf.grep(self.outcar, 'EMIN =', 0, ';', 'float', 1) # minimum energy for the DOS self.emax = bf.grep(self.outcar, 'EMAX =', 0, 'energy-range', 'float', 1) # maximum energy for the DOS self.ismear = bf.grep(self.outcar, 'ISMEAR =', 0, ';', 'int', 1) # ISMEAR used self.lorbit = bf.grep(self.outcar, 'LORBIT =', 0, '0 simple, 1 ext', 'int', 1) # LORBIT used self.isym = bf.grep(self.outcar, 'ISYM =', 0, '0-nonsym', 'float', 1) # ISYM used self.istart = bf.grep(self.outcar, 'ISTART =', 0, 'job', 'float', 1) # ISTART tag # --------------------------------------------- SYSTEM PROPERTIES ---------------------------------------------- self.nb_atoms_tot = bf.grep(self.outcar, 'NIONS =', 0, False, 'int', 1) # total number of atoms self.nb_atoms = [ int(f) for f in bf.grep(self.outcar, 'ions per type =', 0).split() ] # population of each atomic species self.atoms_types = [ bf.grep(self.outcar, 'VRHFIN =', f, ':') for f in range(len(bf.grep(self.outcar, 'VRHFIN ='))) ] # atomic species self.population = dict(zip(self.atoms_types, self.nb_atoms)) self.atoms_valence = [ int(float(f)) for f in bf.grep(self.outcar, 'ZVAL =', -1).split() ] # valence of each atomic species self.atoms = np.concatenate( [[f + ' (' + str(g) + ')' for g in range(1, q + 1)] for f, q in zip(self.atoms_types, self.nb_atoms)]) # atoms list self.nb_electrons = bf.grep(self.outcar, 'NELECT =', 0, 'total number', 'float', 1) # total number of electrons self.charge = sum( np.array(self.nb_atoms) * np.array(self.atoms_valence)) - self.nb_electrons self.orbitals = [ f for f in bf.grep(self.outcar, '# of ion', 0, 'tot').split(' ') if f != '' ] # verification of the consistence of the data retrieved if self.nb_atoms_tot != sum(self.nb_atoms) or \ len(self.nb_atoms) != len(self.atoms_types) or \ len(self.nb_atoms) != len(self.atoms_valence): raise bf.PydefImportError( 'Numbers of atoms retrieved are not consistent') self.name, self.display_name = get_system_name(self.atoms_types, self.nb_atoms, False) self.rname, self.display_rname = get_system_name( self.atoms_types, self.nb_atoms, True) # --------------------------------------------- CALCULATION RESULT --------------------------------------------- # Number of electronic steps if self.functional != 'G0W0@GGA' and self.functional != 'GW0@GGA': self.nb_iterations = len(bf.grep( self.outcar, 'Iteration')) # for non GW calculations else: self.nb_iterations = bf.grep(self.outcar, 'NELM =', 0, 'number', 'int', 1) # for GW calculations # Cristallographic properties self.cell_parameters = get_cell_parameters( self.outcar) # cristallographic parameters self.atoms_positions = get_atoms_positions( self.outcar, self.atoms) # atoms positions # Energy & Density of states self.energy = bf.grep(self.outcar, 'free energy TOTEN =', -1, 'eV', 'float', self.nb_iterations) # total energy self.fermi_energy = bf.grep(self.outcar, ' BZINTS: Fermi energy:', -1, ';', 'float') # fermi energy if self.ismear == 0: self.fermi_energy = bf.grep(self.outcar, 'E-fermi :', 0, 'XC(G=0)', 'float', nb_found=1) self.nkpts = bf.grep(self.outcar, 'NKPTS =', 0, 'k-points in BZ', 'int', 1) # number of k-points self.kpoints_weights = get_kpoints_weight(self.outcar, self.nkpts) self.nbands = bf.grep(self.outcar, 'NBANDS=', 0, False, 'int', 1) # number of bands self.bands_data = get_band_occupation( self.outcar, self.nkpts, self.functional, self.nbands) # bands energy and occupation self.VBM, self.CBM = get_band_extrema( self.bands_data) # VBM and CBM energies self.gap = self.CBM - self.VBM # electronic gap if self.functional != 'G0W0@GGA' and self.functional != 'GW0@GGA': self.potentials = get_electrostatic_potentials( self.outcar, self.atoms) # electrostatic averaged potentials else: self.potentials = None # --------------------------------------------------- OTHERS --------------------------------------------------- self.ID = ''.join([f + str(g) for f, g in zip(self.atoms_types, self.nb_atoms)]) + '_' + self.functional\ + '_q' + str(int(self.charge)) self.title = self.display_name + ' ' + self.functional_title + ' q=%.0f' % self.charge # title of the plot # --------------------------------------------------- DOSCAR --------------------------------------------------- if self.DOSCAR != '': self.doscar = bf.read_file(DOSCAR) # content of the DOSCAR file # Check that the OUTCAR and DOSCAR files are consistent if self.lorbit == 11: if len(self.doscar) != 6 + sum( self.nb_atoms) * (self.nedos + 1) + self.nedos: raise bf.PydefDoscarError( 'The DOSCAR file is inconsistent with the OUTCAR file') else: if len(self.doscar) != 6 + sum( self.nb_atoms ): # Beware of the white line at the end of the file raise bf.PydefDoscarError( 'The DOSCAR file is inconsistent with the OUTCAR file') dos_data = np.transpose([[float(f) for f in q.split()] for q in self.doscar[6:self.nedos + 6] ])[1] self.dosmax = max(dos_data) # maximum value of the total DOS self.dpp = Dos_Plot_Parameters(self) # DOS plot parameters