def dos(filename, plot=False, output='dos.csv', width=0.1): """Calculate density of states. filename: str Name of restart-file. plot: bool Show a plot. output: str Name of CSV output file. width: float Width of Gaussians. """ calc = GPAW(filename, txt=None) dos = DOS(calc, width) D = [dos.get_dos(spin) for spin in range(calc.get_number_of_spins())] if output: fd = sys.stdout if output == '-' else open(output, 'w') for x in zip(dos.energies, *D): print(*x, sep=', ', file=fd) if output != '-': fd.close() if plot: import matplotlib.pyplot as plt for y in D: plt.plot(dos.energies, y) plt.show()
def dos(filename, plot=False, output='dos.csv', width=0.1): """Calculate density of states. filename: str Name of restart-file. plot: bool Show a plot. output: str Name of CSV output file. width: float Width of Gaussians. """ from gpaw import GPAW from ase.dft.dos import DOS calc = GPAW(filename, txt=None) dos = DOS(calc, width) D = [dos.get_dos(spin) for spin in range(calc.get_number_of_spins())] if output: fd = sys.stdout if output == '-' else open(output, 'w') for x in zip(dos.energies, *D): print(*x, sep=', ', file=fd) if output != '-': fd.close() if plot: import matplotlib.pyplot as plt for y in D: plt.plot(dos.energies, y) plt.show()
def get_dos(self, spin=None, **kwargs): """ The total DOS. Uses the ASE DOS module, and returns a tuple with (energies, dos). """ from ase.dft.dos import DOS dos = DOS(self, **kwargs) e = dos.get_energies() d = dos.get_dos(spin=spin) return e, d
def create_ase_object(objtype, dct): # We just try each object type one after another and instantiate # them manually, depending on which kind it is. # We can formalize this later if it ever becomes necessary. if objtype == 'cell': from ase.cell import Cell dct.pop('pbc', None) # compatibility; we once had pbc obj = Cell(**dct) elif objtype == 'bandstructure': from ase.spectrum.band_structure import BandStructure obj = BandStructure(**dct) elif objtype == 'bandpath': from ase.dft.kpoints import BandPath obj = BandPath(path=dct.pop('labelseq'), **dct) elif objtype == 'atoms': from ase import Atoms obj = Atoms.fromdict(dct) elif objtype == 'densityofstates': from ase.dft.dos import DOS obj = DOS(**dct) elif objtype == 'griddoscollection': from ase.spectrum.doscollection import GridDOSCollection obj = GridDOSCollection.fromdict(dct) else: raise ValueError('Do not know how to decode object type {} ' 'into an actual object'.format(objtype)) assert obj.ase_objtype == objtype return obj
def get_dos(self, width=0.15, method='gaussian', npts=501): """ density of states. :param width: smearing width :param method: 'gaussian'| 'tetra' :param npts: number of DOS energies. :returns: energies, dos. two ndarray. TODO: implement spin resolved DOS. """ if method == 'tetra': dos = tetrahedronDosClass(self, width, npts=npts) else: dos = DOS(self, width, window=None, npts=npts) return dos.get_energies(), dos.get_dos()
def get_dos(self, width=None, smearing='gaussian', npts=501): """ density of states. :param width: smearing width :param method: 'gaussian'| 'tetra' :param npts: number of DOS energies. :returns: energies, dos. two ndarray. TODO: implement spin resolved DOS. """ if width is None: width = self._width if smearing == 'tetra': raise NotImplementedError('tetrahedron DOS not implemented') #dos = tetrahedronDosClass(self, width, npts=npts) else: dos = DOS(self, width, window=None, npts=npts) return dos.get_energies(), dos.get_dos()
def test_dos(): from ase.atoms import Atoms from ase.calculators.singlepoint import SinglePointDFTCalculator from ase.calculators.singlepoint import SinglePointKPoint from ase.dft.dos import DOS atoms = Atoms('H') eFermi = [0, 1] kpts = [SinglePointKPoint(1, 0, 0), SinglePointKPoint(1, 1, 0)] kpts[0].eps_n = [-2, -1, 1] kpts[0].f_n = [1, 0, 0] kpts[1].eps_n = [-2.5, -1.5, 0.5] kpts[1].f_n = [1, 0, 0] calc = SinglePointDFTCalculator(atoms, efermi=eFermi) calc.kpts = kpts DOS(calc)
def get_dos(self,kpoints=None,width=0.1,window=None,npts=201,spin=None): """ calculate density of states. :param kpoints: set k-points for DOS calculation. :param width: smearing width :param window: energy windown. e.g. [-5,5] :param npts: number of points. :param spin: No use here. Just to keep with the ase API. """ if kpoints is not None: self.params['calculation']='nscf' self.kpts=['automatic',kpoints] self.params['occupations']='tetrahedra' self.initialize(self.atoms) self.calculate() self.read_output() mydos=DOS(self,width=width,window=window,npts=201) self.dos_energies=mydos.get_energies() self.dos=mydos.get_dos() if self.spin_polarized: self.dos_up=mydos.get_dos(spin=0) self.dos_down=mydos.get_dos(spin=1)
from jasp import * from ase.dft.dos import DOS import matplotlib.pyplot as plt with jasp('molecules/simple-co') as calc: # we already ran this! dos = DOS(calc) plt.plot(dos.get_energies(), dos.get_dos()) plt.xlabel('Energy - $E_f$ (eV)') plt.ylabel('DOS') # make sure you save the figure outside the with statement, or provide # the correct relative or absolute path to where you want it. plt.savefig('images/co-dos.png')
from ase.atoms import Atoms from ase.calculators.singlepoint import SinglePointDFTCalculator from ase.calculators.singlepoint import SinglePointKPoint from ase.dft.dos import DOS atoms = Atoms('H') eFermi = [0, 1] kpts = [SinglePointKPoint(1, 0, 0), SinglePointKPoint(1, 1, 0)] kpts[0].eps_n = [-2, -1, 1] kpts[0].f_n = [1, 0, 0] kpts[1].eps_n = [-2.5, -1.5, 0.5] kpts[1].f_n = [1, 0, 0] calc = SinglePointDFTCalculator(atoms, efermi=eFermi) calc.kpts = kpts dos = DOS(calc)
class BandStructure: '''outline of class to facilitate band structure calculations ''' def __init__(self, atoms, BZpath=[], npoints=10, outnc='harris.nc'): """Headline here ... XXX. atoms is an ase.Atoms object with calculator attached. Presumably the self-consistent charge density has already been calculated, otherwise, it will be. BZpath is a list of tuples describing the path through the Brillouin zone. The tuples have the form (label, kpt), e.g. :: [('$\Gamma$',[0.0, 0.0, 0.0]), ('X',[0.0, 0.5, 0.5]), ('L',[0.5, 0.0, 0.0]), ('$\Gamma$',[0.0, 0.0, 0.0])] the label is used in the figure and can include latex markup. npoints is the number of points on each segment. It can either be a constant, which is used for every segment, or a list of integers that is an integer for each segment. """ self.atoms = atoms self.calc = atoms.get_calculator() #first, we make sure the charge density is up to date. self.calc.get_charge_density() self.ef = self.calc.get_ef() #self-consistent fermi level self.labels = [x[0] for x in BZpath] self.kpt_path = [np.array(x[1], dtype=np.float) for x in BZpath] self.npoints = npoints #first, setup the kpt path kpts = [] #start at second kpt and go to second to last segment nsegments = len(self.kpt_path) - 1 for i in range(nsegments - 1): #get number of points on path. this counts the first point try: i_npt = npoints[i] except TypeError: i_npt = npoints #this is the vector connecting the two endpoint kpts of a segment kdiff = self.kpt_path[i + 1] - self.kpt_path[i] #make a vector of evenly spaced intervals, one longer than needed #because we chop off the last entry. for j in np.linspace(0, 1, i_npt + 1)[0:-1]: k = self.kpt_path[i] + j * kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3)) / 1.e4 k += krand kpts.append(k) #now fill in the last segment, and end on the last point try: i_npt = npoints[-1] except TypeError: i_npt = npoints kdiff = self.kpt_path[-1] - self.kpt_path[-2] for j in np.linspace(0, 1, i_npt + 1)[1:]: k = self.kpt_path[-2] + j * kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3)) / 1.e4 k += krand kpts.append(k) #these are now the points needed for the Harris calculation. self.kpts = kpts self.dos = DOS(self.calc) self.dos_energies = self.dos.get_energies() self.dos_dos = self.dos.get_dos() #try to avoid rerunning the calculation if it is already done! if os.path.exists(outnc): self.calc = Jacapo(outnc) else: print('calculation of harris required') self.calc.set_nc(outnc) #self.calc.debug=10 #save some time by not calculating stress self.calc.set_stress(False) #this seems to be necessary sometimes self.calc.delete_ncattdimvar(outnc, ncdims=['number_plane_waves']) #this has to come after removing number_of_planewaves self.calc.set_kpts(self.kpts) #freeze charge density self.calc.set_charge_mixing(updatecharge='No') #and, run calculation self.calc.calculate() def plot(self): ''' Make an interactive band-structure plot. clicking on a band will make it thicker and print which band was selected. ''' # kpoints = self.calc.get_ibz_kpoints() eigenvalues = self.calc.get_all_eigenvalues() - self.ef #eigenvalues = np.array([self.calc.get_eigenvalues(kpt=i)-self.ef # for i in range(len(kpoints))]) self.handles = [] #used to get band indexes from plot fig = plt.figure() #plot DOS in figure ax = fig.add_subplot(122) ax.plot(self.dos_dos, self.dos_energies) plt.title('self-consistent Total DOS') ax.set_xticks([]) ax.set_yticks([]) ax.set_ylim([-20, 20]) ax = fig.add_subplot(121) ax.set_title('Band structure') def onpick(event): 'make picked line bolder, set oldline back to regular thickness' self.lastartist.set_linewidth(1) self.lastartist = thisline = event.artist thisline.set_linewidth(5) plt.draw() #needed to update linewidth print('Band %i selected' % self.handles.index(thisline)) #you could insert code here to plot wavefunction, etc... fig.canvas.mpl_connect('pick_event', onpick) #we use indices for x. the tick labels are not shown and the distance #appears unimportant xdata = list(range(len(eigenvalues))) nkpts, nbands = eigenvalues.shape for i in range(nbands): #eigenvalues has shape(nkpts,nbands) #note the comma after line_handle line_handle, = ax.plot(xdata, eigenvalues[:, i], '.-', ms=1, picker=2) self.handles.append(line_handle) self.lastartist = self.handles[-1] #plot Fermi level ax.plot([0, len(self.kpts)], [0, 0], 'k--', label='$E_f$') plt.xlabel('|k|') plt.ylabel('$E-E_f$ (eV)') #set xtick locations and labels xtick_locs = np.zeros(len(self.kpt_path)) try: #this means the npoints is a list # i_npt = self.npoints[0] for j, npt in enumerate(1, self.npoints): xtick_locs[j] = xtick_locs[j - 1] + npt except TypeError: #npoints is a single number for j in range(1, len(self.labels)): xtick_locs[j] = xtick_locs[j - 1] + self.npoints #the last location is off by one, so we fix it. xtick_locs[-1] -= 1 ax.set_xlim([xtick_locs[0], xtick_locs[-1]]) ax.set_xticks(xtick_locs) ax.set_xticklabels(self.labels) #this seems reasonable to avoid very deep energy states and high energy states ax.set_ylim([-20, 20]) plt.show() return fig
def __init__(self, atoms, BZpath=[], npoints=10, outnc='harris.nc'): """Headline here ... XXX. atoms is an ase.Atoms object with calculator attached. Presumably the self-consistent charge density has already been calculated, otherwise, it will be. BZpath is a list of tuples describing the path through the Brillouin zone. The tuples have the form (label, kpt), e.g. :: [('$\Gamma$',[0.0, 0.0, 0.0]), ('X',[0.0, 0.5, 0.5]), ('L',[0.5, 0.0, 0.0]), ('$\Gamma$',[0.0, 0.0, 0.0])] the label is used in the figure and can include latex markup. npoints is the number of points on each segment. It can either be a constant, which is used for every segment, or a list of integers that is an integer for each segment. """ self.atoms = atoms self.calc = atoms.get_calculator() #first, we make sure the charge density is up to date. self.calc.get_charge_density() self.ef = self.calc.get_ef() #self-consistent fermi level self.labels = [x[0] for x in BZpath] self.kpt_path = [np.array(x[1], dtype=np.float) for x in BZpath] self.npoints = npoints #first, setup the kpt path kpts = [] #start at second kpt and go to second to last segment nsegments = len(self.kpt_path) - 1 for i in range(nsegments - 1): #get number of points on path. this counts the first point try: i_npt = npoints[i] except TypeError: i_npt = npoints #this is the vector connecting the two endpoint kpts of a segment kdiff = self.kpt_path[i + 1] - self.kpt_path[i] #make a vector of evenly spaced intervals, one longer than needed #because we chop off the last entry. for j in np.linspace(0, 1, i_npt + 1)[0:-1]: k = self.kpt_path[i] + j * kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3)) / 1.e4 k += krand kpts.append(k) #now fill in the last segment, and end on the last point try: i_npt = npoints[-1] except TypeError: i_npt = npoints kdiff = self.kpt_path[-1] - self.kpt_path[-2] for j in np.linspace(0, 1, i_npt + 1)[1:]: k = self.kpt_path[-2] + j * kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3)) / 1.e4 k += krand kpts.append(k) #these are now the points needed for the Harris calculation. self.kpts = kpts self.dos = DOS(self.calc) self.dos_energies = self.dos.get_energies() self.dos_dos = self.dos.get_dos() #try to avoid rerunning the calculation if it is already done! if os.path.exists(outnc): self.calc = Jacapo(outnc) else: print('calculation of harris required') self.calc.set_nc(outnc) #self.calc.debug=10 #save some time by not calculating stress self.calc.set_stress(False) #this seems to be necessary sometimes self.calc.delete_ncattdimvar(outnc, ncdims=['number_plane_waves']) #this has to come after removing number_of_planewaves self.calc.set_kpts(self.kpts) #freeze charge density self.calc.set_charge_mixing(updatecharge='No') #and, run calculation self.calc.calculate()
def calc_to_dict(calc, **kwargs): d = {'doc': '''JSON representation of a VASP calculation. energy is in eV forces are in eV/\AA stress is in GPa (sxx, syy, szz, syz, sxz, sxy) magnetic moments are in Bohr-magneton The density of states is reported with E_f at 0 eV. Volume is reported in \AA^3 Coordinates and cell parameters are reported in \AA If atom-projected dos are included they are in the form: {ados:{energy:data, {atom index: {orbital : dos}}} '''} d['incar'] = {'doc': 'INCAR parameters'} d['incar'].update(dict(filter(lambda item: item[1] is not None, calc.float_params.items()))) d['incar'].update(dict(filter(lambda item: item[1] is not None, calc.exp_params.items()))) d['incar'].update(dict(filter(lambda item: item[1] is not None, calc.string_params.items()))) d['incar'].update(dict(filter(lambda item: item[1] is not None, calc.int_params.items()))) d['incar'].update(dict(filter(lambda item: item[1] is not None, calc.bool_params.items()))) d['incar'].update(dict(filter(lambda item: item[1] is not None, calc.list_params.items()))) d['incar'].update(dict(filter(lambda item: item[1] is not None, calc.dict_params.items()))) d['input'] = calc.input_params d['potcar'] = calc.get_pseudopotentials() d['atoms'] = atoms_to_dict(calc.get_atoms()) d['data'] = {'doc': 'Data from the output of the calculation'} atoms = calc.get_atoms() d['data']['total_energy'] = atoms.get_potential_energy() d['data']['forces'] = atoms.get_forces().tolist() # There are times when no stress is calculated try: stress = atoms.get_stress() except NotImplementedError: stress = None except AssertionError: stress = None if stress is not None: d['data']['stress'] = atoms.get_stress().tolist() else: d['data']['stress'] = None d['data']['fermi_level'] = calc.get_fermi_level() d['data']['volume'] = atoms.get_volume() if calc.spinpol: d['data']['magmom'] = atoms.get_magnetic_moment() if (calc.int_params.get('lorbit', 0) >= 10 or calc.list_params.get('rwigs', None)): try: d['data']['magmoms'] = atoms.get_magnetic_moments().tolist() except AttributeError: d['data']['magmoms'] = None # store the metadata if hasattr(calc, 'metadata'): d['metadata'] = calc.metadata if kwargs.get('dos', None): from ase.dft.dos import DOS dos = DOS(calc, width=kwargs.get('width', 0.2)) e = dos.get_energies() d['data']['dos'] = {'doc': 'Total density of states'} d['data']['dos']['e'] = e.tolist() if calc.spinpol: d['data']['dos']['dos-up'] = dos.get_dos(spin=0).tolist() d['data']['dos']['dos-down'] = dos.get_dos(spin=1).tolist() else: d['data']['dos']['dos'] = dos.get_dos().tolist() if kwargs.get('ados', None): from ase.calculators.vasp import VaspDos ados = VaspDos(efermi=calc.get_fermi_level()) d['data']['ados'] = {'doc': 'Atom projected DOS'} nonspin_orbitals_no_phase = ['s', 'p', 'd'] nonspin_orbitals_phase = ['s', 'py', 'pz', 'px', 'dxy', 'dyz', 'dz2', 'dxz', 'dx2'] spin_orbitals_no_phase = [] for x in nonspin_orbitals_no_phase: spin_orbitals_no_phase += ['{0}-up'.format(x)] spin_orbitals_no_phase += ['{0}-down'.format(x)] spin_orbitals_phase = [] for x in nonspin_orbitals_phase: spin_orbitals_phase += ['{0}-up'.format(x)] spin_orbitals_phase += ['{0}-down'.format(x)] if calc.spinpol and calc.int_params['lorbit'] != 11: orbitals = spin_orbitals_no_phase elif calc.spinpol and calc.int_params['lorbit'] == 11: orbitals = spin_orbitals_phase elif calc.int_params['lorbit'] != 11: orbitals = nonspin_orbitals_no_phase else: orbitals = nonspin_orbitals_phase for i, atom in enumerate(atoms): d['data']['ados']['energy'] = ados.energy.tolist() d['data']['ados'][i] = {} for orbital in orbitals: d['data']['ados'][i][orbital] = ados.site_dos(0, orbital).tolist() # convert all numpy arrays to lists for key in d: try: d[key] = d[key].tolist() except: pass for key in d['input']: try: d['input'][key] = d['input'][key].tolist() except: pass return d
def dos(filename, plot=False, output='dos.csv', width=0.1, integrated=False, projection=None, emin=None, emax=None, npoints=400, show_total=None): """Calculate density of states. filename: str Name of restart-file. plot: bool Show a plot. output: str Name of CSV output file. width: float Width of Gaussians. integrated: bool Calculate integrated DOS. """ calc = GPAW(filename, txt=None) dos = DOS(calc, width, (emin, emax), npoints) nspins = calc.get_number_of_spins() spinlabels = [''] if nspins == 1 else [' up', ' dn'] if projection is None or show_total: D = [dos.get_dos(spin) for spin in range(nspins)] labels = ['DOS' + sl for sl in spinlabels] else: D = [] labels = [] if projection is not None: for p in projection.split(','): s, ll = p.split('-') if s.isdigit(): A = [int(s)] s = '#' + s else: A = [a for a, symbol in enumerate(calc.atoms.get_chemical_symbols()) if symbol == s] if not A: raise ValueError('No such atom: ' + s) for spin in range(nspins): for l in ll: d = 0.0 for a in A: d += ldos(calc, a, spin, l, width, dos.energies) labels.append(s + '-' + l + spinlabels[spin]) D.append(d) if integrated: de = dos.energies[1] - dos.energies[0] dos.energies += de / 2 D = [np.cumsum(d) * de for d in D] ylabel = 'iDOS [electrons]' else: ylabel = 'DOS [electrons/eV]' if output: fd = sys.stdout if output == '-' else open(output, 'w') for x in zip(dos.energies, *D): print(*x, sep=', ', file=fd) if output != '-': fd.close() if plot: import matplotlib.pyplot as plt for y, label in zip(D, labels): plt.plot(dos.energies, y, label=label) plt.legend() plt.ylabel(ylabel) plt.xlabel(r'$\epsilon-\epsilon_F$ [eV]') plt.show()
def calc_to_dict(calc, **kwargs): """Convert calc to a dictionary.""" d = { 'doc': '''JSON representation of a VASP calculation. energy is in eV forces are in eV/\AA stress is in GPa (sxx, syy, szz, syz, sxz, sxy) magnetic moments are in Bohr-magneton The density of states is reported with E_f at 0 eV. Volume is reported in \AA^3 Coordinates and cell parameters are reported in \AA If atom-projected dos are included they are in the form: {ados:{energy:data, {atom index: {orbital : dos}}} ''' } d['incar'] = {'doc': 'INCAR parameters'} d['incar'].update( dict( filter(lambda item: item[1] is not None, calc.float_params.items()))) d['incar'].update( dict(filter(lambda item: item[1] is not None, calc.exp_params.items()))) d['incar'].update( dict( filter(lambda item: item[1] is not None, calc.string_params.items()))) d['incar'].update( dict(filter(lambda item: item[1] is not None, calc.int_params.items()))) d['incar'].update( dict(filter(lambda item: item[1] is not None, calc.bool_params.items()))) d['incar'].update( dict(filter(lambda item: item[1] is not None, calc.list_params.items()))) d['incar'].update( dict(filter(lambda item: item[1] is not None, calc.dict_params.items()))) d['input'] = calc.input_params d['potcar'] = calc.get_pseudopotentials() d['atoms'] = atoms_to_dict(calc.get_atoms()) d['data'] = {'doc': 'Data from the output of the calculation'} atoms = calc.get_atoms() d['data']['total_energy'] = atoms.get_potential_energy() d['data']['forces'] = atoms.get_forces().tolist() # There are times when no stress is calculated try: stress = atoms.get_stress() except NotImplementedError: stress = None except AssertionError: stress = None if stress is not None: d['data']['stress'] = atoms.get_stress().tolist() else: d['data']['stress'] = None d['data']['fermi_level'] = calc.get_fermi_level() d['data']['volume'] = atoms.get_volume() if calc.spinpol: d['data']['magmom'] = atoms.get_magnetic_moment() if (calc.int_params.get('lorbit', 0) >= 10 or calc.list_params.get('rwigs', None)): try: d['data']['magmoms'] = atoms.get_magnetic_moments().tolist() except AttributeError: d['data']['magmoms'] = None # store the metadata if hasattr(calc, 'metadata'): d['metadata'] = calc.metadata if kwargs.get('dos', None): from ase.dft.dos import DOS dos = DOS(calc, width=kwargs.get('width', 0.2)) e = dos.get_energies() d['data']['dos'] = {'doc': 'Total density of states'} d['data']['dos']['e'] = e.tolist() if calc.spinpol: d['data']['dos']['dos-up'] = dos.get_dos(spin=0).tolist() d['data']['dos']['dos-down'] = dos.get_dos(spin=1).tolist() else: d['data']['dos']['dos'] = dos.get_dos().tolist() if kwargs.get('ados', None): from ase.calculators.vasp import VaspDos ados = VaspDos(efermi=calc.get_fermi_level()) d['data']['ados'] = {'doc': 'Atom projected DOS'} nonspin_orbitals_no_phase = ['s', 'p', 'd'] nonspin_orbitals_phase = [ 's', 'py', 'pz', 'px', 'dxy', 'dyz', 'dz2', 'dxz', 'dx2' ] spin_orbitals_no_phase = [] for x in nonspin_orbitals_no_phase: spin_orbitals_no_phase += ['{0}-up'.format(x)] spin_orbitals_no_phase += ['{0}-down'.format(x)] spin_orbitals_phase = [] for x in nonspin_orbitals_phase: spin_orbitals_phase += ['{0}-up'.format(x)] spin_orbitals_phase += ['{0}-down'.format(x)] if calc.spinpol and calc.int_params['lorbit'] != 11: orbitals = spin_orbitals_no_phase elif calc.spinpol and calc.int_params['lorbit'] == 11: orbitals = spin_orbitals_phase elif calc.int_params['lorbit'] != 11: orbitals = nonspin_orbitals_no_phase else: orbitals = nonspin_orbitals_phase for i, atom in enumerate(atoms): d['data']['ados']['energy'] = ados.energy.tolist() d['data']['ados'][i] = {} for orbital in orbitals: d['data']['ados'][i][orbital] = ados.site_dos( 0, orbital).tolist() # convert all numpy arrays to lists for key in d: try: d[key] = d[key].tolist() except: pass for key in d['input']: try: d['input'][key] = d['input'][key].tolist() except: pass return d
# Get band structure and dos Ebs = atoms.calc.band_structure() # Get the band structure # if world.rank == 0: print('Electronic band structure calculated') kpts = {'size': (40,40,40)} calc.set( kpts = kpts, fixdensity=True, symmetry='off', ) # Fix the potential calc.get_potential_energy() # e, dos = calc.get_dos(spin=0, npts=1001, width=0.5) # Get energy and density of states dos = DOS(calc, npts=2000, width=0.1) d = dos.get_dos() e = dos.get_energies() f = calc.get_fermi_level() print('Electronic DOS computed') e_f = calc.get_fermi_level() Edos = { 'e': e, 'dos': d, 'fermi': e_f } # Save results pickle.dump( Ebs, open( "Ebs.p", "wb" ) ) # Save the electronic band structure pickle.dump( Edos, open( "Edos.p", "wb" ) ) # Save the electronic DOS # if world.rank == 0:
def add_to_plot(self, readoutfilesobj, label=None): """ |Here you add individual plots that need to be plot and then just plot them with the plot() method |Use this method which is a part of the BandPlotterASE class you will have to give the bands to plot using a readoutfilesobj type of object """ try: Ef = readoutfilesobj.atoms_objects[0].calc.get_fermi_level() except: print(f"add_to_plot: Could not read fermi level on scf file") try: kpts = readoutfilesobj.atoms_bands_objects[ 0].calc.get_ibz_k_points() except: print(f"add_to_plot: Could not read k points on bands file") if self.include_dos or self.plot_only_dos: if self.plot_from_QE_dos_file: E = [] dos = [] with open(readoutfilesobj.dos_file_name, "r") as dos_file: for line in dos_file: # print(line) if "#" not in line: data = line.strip().split() E.append(float(data[0]) - Ef) dos.append(float(data[1])) # self.y_to_plot.append(dos) # self.x_to_plot.append(E) self.dos.append(dos) self.E_dos.append(E) if self.plot_only_dos: # Other wise we wil lappend to labels twice sicnce we will do it again at theloading of bands self.labels.append(label) else: # we are here getting the required information for DOS # self.get_dos(readoutfilesobj) calc = readoutfilesobj.atoms_nscf_objects[0].calc Ef_nscf = readoutfilesobj.atoms_nscf_objects[ 0].calc.get_fermi_level() dos = DOS(calc, width=0.2) self.dos.append(dos.get_dos()) E_nscf = dos.get_energies() Ef_diff = Ef - Ef_nscf if abs(Ef_diff) >= 0.001: print( f"Warning!!! In {readoutfilesobj.identifier[0]}:\t|E_f_scf-E_f_nscf| = {abs(Ef_diff):.3} eV (>= 0.001 eV); E_f_scf = {Ef} eV, E_f_nscf = {Ef_nscf} eV" ) if self.pin_fermi != "scf": self.E_dos.append(E_nscf) else: self.E_dos.append([E - Ef_diff for E in E_nscf]) # Test space for k path and k high symmetry points # print(kpts) # print(self.plot_only_dos) if self.plot_only_dos == False: # This is for teh band structure path = readoutfilesobj.atoms_bands_objects[0].cell.bandpath( npoints=0) kinks = find_bandpath_kinks( readoutfilesobj.atoms_bands_objects[0].cell, kpts, eps=1e-5) # These are the high symmetry points in use pathspec = resolve_custom_points( kpts[kinks], path.special_points, eps=1e-5 ) # This gives the postions for the relevant high symmetry points # path.kpts = kpts # path.path = pathspec klengths = [] for x in range(0, len(kpts)): if x == 0: kdist = np.sqrt((0.0 - kpts[x][0])**2 + (0.0 - kpts[x][1])**2 + (0.0 - kpts[x][2])**2) klengths.append(kdist) else: kdist = np.sqrt((kpts[x - 1][0] - kpts[x][0])**2 + (kpts[x - 1][1] - kpts[x][1])**2 + (kpts[x - 1][2] - kpts[x][2])**2) klengths.append(kdist + klengths[x - 1]) self.k_locations = [] for x in range(len(kinks)): self.k_locations.append(klengths[kinks[x]]) self.k_symbols = [] for x in pathspec: if x == "G": self.k_symbols.append("$\Gamma$") else: self.k_symbols.append(x) energies = [] for s in range(readoutfilesobj.atoms_bands_objects[0].calc. get_number_of_spins()): energies.append([ readoutfilesobj.atoms_bands_objects[0].calc. get_eigenvalues(kpt=k, spin=s) for k in range(len(kpts)) ]) # print(f"lenght of kpoints: {range(len(kpts))}") Energy_to_plot = [] if self.Ef_shift == True: for band in energies[0]: if self.pin_fermi != "nscf": Energy_to_plot.append([E - Ef for E in band]) else: Energy_to_plot.append( [E - Ef_nscf for E in band] ) # here the assumption is that if this option is ever reached then the idea is that an nscf calculation has already being done and dos is being plotted. tempMain = [] temp = [] for x in range(len(Energy_to_plot[0])): for kpoint in Energy_to_plot: temp.append(kpoint[x]) tempMain.append(temp) temp = [] self.y_to_plot.append(tempMain) self.x_to_plot.append(klengths) self.labels.append(label)
import matplotlib.pyplot as plt from gpaw import GPAW from ase.dft.dos import DOS calc = GPAW('groundstate.gpw') dos = DOS(calc, npts=800, width=0.1) energies = dos.get_energies() weights = dos.get_dos() ax = plt.gca() ax.plot(energies, weights) ax.set_xlabel(r'$E - E_{\mathrm{Fermi}}$ [eV]') ax.set_ylabel('DOS [1/eV]') plt.savefig('dos.png') plt.show()
start = time.time() # if world.rank == 0: print(f'Calculating EOS for Al{N}') # Define electron calculator (GPAW) calc = GPAW( mode=PW(300), # Lower for computational efficiency txt=f'./gpaw-out/EOS_{N}_1core.txt' ) # Use the same calculator as in task6 atoms.set_calculator(calc) pot_e = atoms.get_potential_energy() # Self-constistently optimize the electron density # if world.rank == 0: print(f'Cluster Al{N} finished potential energy per atom: {pot_e / N:.2f} eV') # Get the electronic DOS dos = DOS(calc, npts=800, width=0.2) e = dos.get_energies() d = dos.get_dos() e_f = calc.get_fermi_level() e -= e_f # Subtract the Fermi level from the energy ##### Get the DOS using the same method as in task6 # print('Electronic band structure calculated') # kpts = {'size': (40,40,40)} # calc.set( # kpts = kpts, # fixdensity=True, # symmetry='off', # ) # # Fix the potential
from jasp import * from ase.dft.dos import DOS import matplotlib.pyplot as plt # get the geometry from another calculation with jasp('molecules/simple-co') as calc: atoms = calc.get_atoms() with jasp( 'molecules/co-ados', encut=300, xc='PBE', rwigs=[1.0, 1.0], # these are the cutoff radii for projected states atoms=atoms) as calc: calc.calculate() # now get results dos = DOS(calc) plt.plot(dos.get_energies(), dos.get_dos() + 10) ados = VaspDos(efermi=calc.get_fermi_level()) energies = ados.energy plt.plot(energies, ados.dos + 8, label='ADOS') # these are the total DOS c_s = ados.site_dos(0, 's') c_p = ados.site_dos(0, 'p') o_s = ados.site_dos(1, 's') o_p = ados.site_dos(1, 'p') c_d = ados.site_dos(0, 'd') o_d = ados.site_dos(1, 'd') plt.plot(energies, c_s + 6, energies, o_s + 5) plt.plot(energies, c_p + 4, energies, o_p + 3) plt.plot(energies, c_d, energies, o_d + 2) plt.xlabel('Energy - $E_f$ (eV)') plt.ylabel('DOS') plt.legend(
def __init__(self, atoms, BZpath=[], npoints=10, outnc='harris.nc'): """Headline here ... XXX. atoms is an ase.Atoms object with calculator attached. Presumably the self-consistent charge density has already been calculated, otherwise, it will be. BZpath is a list of tuples describing the path through the Brillouin zone. The tuples have the form (label, kpt), e.g. :: [('$\Gamma$',[0.0, 0.0, 0.0]), ('X',[0.0, 0.5, 0.5]), ('L',[0.5, 0.0, 0.0]), ('$\Gamma$',[0.0, 0.0, 0.0])] the label is used in the figure and can include latex markup. npoints is the number of points on each segment. It can either be a constant, which is used for every segment, or a list of integers that is an integer for each segment. """ self.atoms = atoms self.calc = atoms.get_calculator() #first, we make sure the charge density is up to date. self.calc.get_charge_density() self.ef = self.calc.get_ef() #self-consistent fermi level self.labels = [x[0] for x in BZpath] self.kpt_path = [np.array(x[1],dtype=np.float) for x in BZpath] self.npoints = npoints #first, setup the kpt path kpts = [] #start at second kpt and go to second to last segment nsegments = len(self.kpt_path) - 1 for i in range(nsegments-1): #get number of points on path. this counts the first point try: i_npt = npoints[i] except TypeError: i_npt = npoints #this is the vector connecting the two endpoint kpts of a segment kdiff = self.kpt_path[i+1] - self.kpt_path[i] #make a vector of evenly spaced intervals, one longer than needed #because we chop off the last entry. for j in np.linspace(0,1,i_npt+1)[0:-1]: k = self.kpt_path[i] + j*kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3))/1.e4 k += krand kpts.append(k) #now fill in the last segment, and end on the last point try: i_npt = npoints[-1] except TypeError: i_npt = npoints kdiff = self.kpt_path[-1] - self.kpt_path[-2] for j in np.linspace(0,1,i_npt+1)[1:]: k = self.kpt_path[-2] + j*kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3))/1.e4 k += krand kpts.append(k) #these are now the points needed for the Harris calculation. self.kpts = kpts self.dos = DOS(self.calc) self.dos_energies = self.dos.get_energies() self.dos_dos = self.dos.get_dos() #try to avoid rerunning the calculation if it is already done! if os.path.exists(outnc): self.calc = Jacapo(outnc) else: print 'calculation of harris required' self.calc.set_nc(outnc) #self.calc.debug=10 #save some time by not calculating stress self.calc.set_stress(False) #this seems to be necessary sometimes self.calc.delete_ncattdimvar(outnc, ncdims=['number_plane_waves']) #this has to come after removing number_of_planewaves self.calc.set_kpts(self.kpts) #freeze charge density self.calc.set_charge_mixing(updatecharge='No') #and, run calculation self.calc.calculate()
class BandStructure: '''outline of class to facilitate band structure calculations ''' def __init__(self, atoms, BZpath=[], npoints=10, outnc='harris.nc'): """Headline here ... XXX. atoms is an ase.Atoms object with calculator attached. Presumably the self-consistent charge density has already been calculated, otherwise, it will be. BZpath is a list of tuples describing the path through the Brillouin zone. The tuples have the form (label, kpt), e.g. :: [('$\Gamma$',[0.0, 0.0, 0.0]), ('X',[0.0, 0.5, 0.5]), ('L',[0.5, 0.0, 0.0]), ('$\Gamma$',[0.0, 0.0, 0.0])] the label is used in the figure and can include latex markup. npoints is the number of points on each segment. It can either be a constant, which is used for every segment, or a list of integers that is an integer for each segment. """ self.atoms = atoms self.calc = atoms.get_calculator() #first, we make sure the charge density is up to date. self.calc.get_charge_density() self.ef = self.calc.get_ef() #self-consistent fermi level self.labels = [x[0] for x in BZpath] self.kpt_path = [np.array(x[1],dtype=np.float) for x in BZpath] self.npoints = npoints #first, setup the kpt path kpts = [] #start at second kpt and go to second to last segment nsegments = len(self.kpt_path) - 1 for i in range(nsegments-1): #get number of points on path. this counts the first point try: i_npt = npoints[i] except TypeError: i_npt = npoints #this is the vector connecting the two endpoint kpts of a segment kdiff = self.kpt_path[i+1] - self.kpt_path[i] #make a vector of evenly spaced intervals, one longer than needed #because we chop off the last entry. for j in np.linspace(0,1,i_npt+1)[0:-1]: k = self.kpt_path[i] + j*kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3))/1.e4 k += krand kpts.append(k) #now fill in the last segment, and end on the last point try: i_npt = npoints[-1] except TypeError: i_npt = npoints kdiff = self.kpt_path[-1] - self.kpt_path[-2] for j in np.linspace(0,1,i_npt+1)[1:]: k = self.kpt_path[-2] + j*kdiff #shift by small random amount to break symmetry and #prevent time-inversion reduction krand = (1. + np.random.random(3))/1.e4 k += krand kpts.append(k) #these are now the points needed for the Harris calculation. self.kpts = kpts self.dos = DOS(self.calc) self.dos_energies = self.dos.get_energies() self.dos_dos = self.dos.get_dos() #try to avoid rerunning the calculation if it is already done! if os.path.exists(outnc): self.calc = Jacapo(outnc) else: print 'calculation of harris required' self.calc.set_nc(outnc) #self.calc.debug=10 #save some time by not calculating stress self.calc.set_stress(False) #this seems to be necessary sometimes self.calc.delete_ncattdimvar(outnc, ncdims=['number_plane_waves']) #this has to come after removing number_of_planewaves self.calc.set_kpts(self.kpts) #freeze charge density self.calc.set_charge_mixing(updatecharge='No') #and, run calculation self.calc.calculate() def plot(self): ''' Make an interactive band-structure plot. clicking on a band will make it thicker and print which band was selected. ''' kpoints = self.calc.get_ibz_kpoints() eigenvalues = self.calc.get_all_eigenvalues() - self.ef #eigenvalues = np.array([self.calc.get_eigenvalues(kpt=i)-self.ef # for i in range(len(kpoints))]) self.handles = [] #used to get band indexes from plot fig = plt.figure() #plot DOS in figure ax = fig.add_subplot(122) ax.plot(self.dos_dos,self.dos_energies) plt.title('self-consistent Total DOS') ax.set_xticks([]) ax.set_yticks([]) ax.set_ylim([-20,20]) ax = fig.add_subplot(121) ax.set_title('Band structure') def onpick(event): 'make picked line bolder, set oldline back to regular thickness' self.lastartist.set_linewidth(1) self.lastartist = thisline = event.artist thisline.set_linewidth(5) plt.draw() #needed to update linewidth print 'Band %i selected' % self.handles.index(thisline) #you could insert code here to plot wavefunction, etc... fig.canvas.mpl_connect('pick_event',onpick) #we use indices for x. the tick labels are not shown and the distance #appears unimportant xdata = range(len(eigenvalues)) nkpts, nbands = eigenvalues.shape for i in range(nbands): #eigenvalues has shape(nkpts,nbands) #note the comma after line_handle line_handle, = ax.plot(xdata,eigenvalues[:,i],'.-',ms=1,picker=2) self.handles.append(line_handle) self.lastartist = self.handles[-1] #plot Fermi level ax.plot([0,len(self.kpts)],[0,0],'k--',label='$E_f$') plt.xlabel('|k|') plt.ylabel('$E-E_f$ (eV)') #set xtick locations and labels xtick_locs = np.zeros(len(self.kpt_path)) try: #this means the npoints is a list i_npt = self.npoints[0] for j,npt in enumerate(1,self.npoints): xtick_locs[j] = xtick_locs[j-1] + npt except TypeError: #npoints is a single number for j in range(1,len(self.labels)): xtick_locs[j] = xtick_locs[j-1] + self.npoints #the last location is off by one, so we fix it. xtick_locs[-1] -= 1 ax.set_xlim([xtick_locs[0],xtick_locs[-1]]) ax.set_xticks(xtick_locs) ax.set_xticklabels(self.labels) #this seems reasonable to avoid very deep energy states and high energy states ax.set_ylim([-20,20]) plt.show() return fig
testRun = False file_name_py = basename(__file__) file_name = file_name_py.replace('.py','') file_traj = file_name + '.traj' start_file = '../In2O3_110_clean.traj' try: sys = Trajectory(file_traj, 'r')[-1] except (IOError, RuntimeError): print(("Importing trajectory file from: %s" % start_file)) sys = read(start_file) else: print(("Importing trajectory file from: %s" % file_traj)) calc = set_calc_In2O3(sys) print("Constraints:") print("\tc: Fix bottom two layers.") c = FixAtoms(mask=[atom.z < 8 for atom in sys]) sys.set_constraint(c) if testRun == True: run_testRun(sys) else: dos = DOS(calc = calc) ds = dos.get_dos() es = dos.get_energies() print("Energy\tDOS") for (e, d) in zip(es, ds): print(("%f\t%f" % (e, d))) print(("Completed %s" % file_name_py))
'''Write DOS to JSON file, starting from GPAW calculator file. ''' import sys import json from gpaw import GPAW from ase.dft.dos import DOS if len(sys.argv) != 2: raise RuntimeError('I want one and only one argument, the filename!') filename = sys.argv[1] calc = GPAW(filename, txt=None) dos = DOS(calc, width=0.1) dos_dict = {} dos_dict['dos'] = list(dos.get_dos()) dos_dict['energies'] = list(dos.get_energies()) out_name = filename + '_dos.json' with open(out_name, 'w') as outfile: json.dump(dos_dict, outfile) print('DOS written to', out_name)