class ScrReader(ETSF_Reader): """ This object reads the results stored in the SCR (Screening) file produced by ABINIT. It provides helper functions to access the most important quantities. """ def __init__(self, filepath): super(ScrReader, self).__init__(filepath) # Read and store important quantities. self.structure = self.read_structure() qred_coords = self.read_value("qpoints_dielectric_function") self.qpoints = KpointList(self.structure.reciprocal_lattice, qred_coords) self.wpts = self.read_value("frequencies_dielectric_function", cmode="c") #def read_params(self): # """ # Read the most importan parameters used to generate the data, i.e. # the parameters that may be subject to convergence studies. # Returns: # :class:`AttrDict` a dictionary whose keys can be accessed # with the dot notation i.e. d.key. # """ # keys = ["npwwfn_used", "nbands_used",] # return AttrDict({k: self.read_value(k) for k in keys}) def find_qpoint_fileindex(self, qpoint): """ Returns the q-point and the index of in the netcdf file. Accepts `Kpoint` instance or integer Raise: `KpointsError` if qpoint cannot be found. """ if isinstance(qpoint, int): iq = qpoint else: iq = self.qpoints.index(qpoint) return self.qpoints[iq], iq def read_wggfunc(self, qpoint, cls): """ Read data at the given q-point and return an instance of `cls` where `cls` is a subclass of `WGGFunction` """ qpoint, iq = self.find_qpoint_fileindex(qpoint) # TODO: I don't remember how to slice in python-netcdf # ecuteps all_gvecs = self.read_value("reduced_coordinates_plane_waves_dielectric_function") ecuteps = 2 # TODO: Gpshere.find is very slow if we don't take advantage of shells gsphere = GSphere(ecuteps, self.structure.reciprocal_lattice, qpoint, all_gvecs[iq]) full_wggmat = self.read_value(cls.etsf_name, cmode="c") wggmat = full_wggmat[iq] return cls(qpoint, self.wpts, gsphere, wggmat, inord="F")
class ScrReader(ETSF_Reader): """ This object reads the results stored in the SCR (Screening) file produced by ABINIT. It provides helper functions to access the most important quantities. double inverse_dielectric_function(number_of_qpoints_dielectric_function, number_of_frequencies_dielectric_function, number_of_spins, number_of_spins, number_of_coefficients_dielectric_function, number_of_coefficients_dielectric_function, complex) .. rubric:: Inheritance Diagram .. inheritance-diagram:: ScrReader """ def __init__(self, filepath): super(ScrReader, self).__init__(filepath) # Read and store important quantities. self.structure = self.read_structure() qfrac_coords = self.read_value("qpoints_dielectric_function") self.kpoints = KpointList(self.structure.reciprocal_lattice, qfrac_coords) self.wpoints = self.read_value("frequencies_dielectric_function", cmode="c") self.ng = self.read_dimvalue( "number_of_coefficients_dielectric_function") # Find number of real/imaginary frequencies. self.nw = len(self.wpoints) self.nrew = self.nw self.nimw = 0 for i, w in enumerate(self.wpoints): if np.iscomplex(w): self.nrew = i break self.nimw = self.nw - self.nrew if self.nimw and not np.all(np.iscomplex( self.wpoints[self.nrew + 1:])): raise ValueError( "wpoints should contained real points packed in the first positions\n" "followed by imaginary points but got: %s" % str(self.wpoints)) # Define self.netcdf_name from the data available on file. nfound = 0 netcdf_names = [ "polarizability", "dielectric_function", "inverse_dielectric_function" ] for tryname in netcdf_names: if tryname in self.rootgrp.variables: self.netcdf_name = tryname nfound += 1 if nfound == 0: raise RuntimeError("Cannot find `%s` in netcdf file" % str(netcdf_names)) if nfound > 1: raise RuntimeError( "Find multiple netcdf arrays (%s) in netcdf file!" % str(netcdf_names)) def read_params(self): """ Read the most important parameters used to compute the screening i.e. the parameters that may be subject to convergence studies. Returns: |AttrDict| a dictionary whose keys can be accessed with the dot notation i.e. ``d.key``. """ # TODO: ecuteps is missing! keys = [ "ikxc", "inclvkb", "gwcalctyp", "nbands_used", "npwwfn_used", "spmeth", "test_type", "tordering", "awtr", "icutcoul", "gwcomp", "gwgamma", "mbpt_sciss", "spsmear", "zcut", "gwencomp" ] def convert(arr): """Convert to scalar if size == 1""" return np.asscalar(arr) if arr.size == 1 else arr return AttrDict({k: convert(self.read_value(k)) for k in keys}) def read_emacro_lf(self, kpoint=(0, 0, 0)): """ Read the macroscopic dielectric function *with* local field effects 1 / em1_{0,0)(kpoint, omega). Return: |Function1D| object. """ if self.netcdf_name == "inverse_dielectric_function": em1 = self.read_wslice(kpoint, ig1=0, ig2=0) emacro = 1 / em1[:self.nrew] else: raise NotImplementedError( "emacro_lf with netcdf != InverseDielectricFunction") return Function1D(np.real(self.wpoints[:self.nrew]).copy(), emacro) def read_emacro_nlf(self, kpoint=(0, 0, 0)): """ Read the macroscopic dielectric function *without* local field effects e_{0,0)(kpoint, omega). Return: |Function1D| .. warning:: This function performs the inversion of e-1 to get e. that can be quite expensive and memory demanding for large matrices! """ if self.netcdf_name == "inverse_dielectric_function": em1 = self.read_wggmat(kpoint) e = np.linalg.inv(em1.wggmat[:self.nrew, :, :]) else: raise NotImplementedError( "emacro_nlf with netcdf != InverseDielectricFunction") return Function1D(np.real(self.wpoints[:self.nrew]).copy(), e[:, 0, 0]) def read_eelf(self, kpoint=(0, 0, 0)): """ Read electron energy loss function - Im(1/ emacro) Return: |Function1D| object. """ # eelf = -Im(1 / eM) emacro_lf = self.read_emacro_lf(kpoint=kpoint) #emacro_lf = self.read_emacro_nlf(kpoint=kpoint) values = (-1 / emacro_lf.values).imag return Function1D(emacro_lf.mesh.copy(), values) def read_wggmat(self, kpoint, spin1=0, spin2=0, cls=None): """ Read data at the given k-point and return an instance of ``cls`` where ``cls`` is a subclass of :class:`_AwggMatrix` """ cls = _AwggMatrix.class_from_netcdf_name( self.netcdf_name) if cls is None else cls var = self.rootgrp.variables[ "reduced_coordinates_plane_waves_dielectric_function"] # Use ik=0 because the basis set is not k-dependent. ik0 = 0 gvecs = var[ik0, :] #print("gvecs", gvecs) kpoint, ik = self.find_kpoint_fileindex(kpoint) # FIXME ecuteps is missing # TODO: Gpshere.find is very slow if we don't take advantage of shells ecuteps = 2 gsphere = GSphere(ecuteps, self.structure.reciprocal_lattice, kpoint, gvecs) # Exchange spin due to F --> C values = self.rootgrp.variables[self.netcdf_name][ik, :, spin2, spin1, :, :, :] wggmat = values[:, :, :, 0] + 1j * values[:, :, :, 1] return cls(self.wpoints, gsphere, wggmat, inord="F") def find_kpoint_fileindex(self, kpoint): """ Returns the k-point and the index of the k-point in the netcdf file. Accepts |Kpoint| instance or integer. """ if duck.is_intlike(kpoint): ik = int(kpoint) else: ik = self.kpoints.index(kpoint) return self.kpoints[ik], ik def read_wslice(self, kpoint, ig1=0, ig2=0, spin1=0, spin2=0): """Read slice along the frequency dimension.""" kpoint, ik = self.find_kpoint_fileindex(kpoint) var = self.rootgrp.variables[self.netcdf_name] values = var[ik, :, spin2, spin1, ig2, ig1, :] # Exchange G indices F --> C return values[:, 0] + 1j * values[:, 1]
def test_kpointlist(self): """Test KpointList.""" lattice = self.lattice frac_coords = [0, 0, 0, 1 / 2, 1 / 2, 1 / 2, 1 / 3, 1 / 3, 1 / 3] weights = [0.1, 0.2, 0.7] klist = KpointList(lattice, frac_coords, weights=weights) repr(klist) str(klist) self.serialize_with_pickle(klist, protocols=[-1]) self.assertMSONable(klist, test_if_subclass=False) self.assert_equal(klist.frac_coords.flatten(), frac_coords) self.assert_equal(klist.get_cart_coords(), np.reshape([k.cart_coords for k in klist], (-1, 3))) assert klist.sum_weights() == 1 assert len(klist) == 3 for i, kpoint in enumerate(klist): assert kpoint in klist assert klist.count(kpoint) == 1 assert klist.index(kpoint) == i assert klist.find(kpoint) == i # Changing the weight of the Kpoint object should change the weights of klist. for kpoint in klist: kpoint.set_weight(1.0) assert np.all(klist.weights == 1.0) # Test find_closest iclose, kclose, dist = klist.find_closest([0, 0, 0]) assert iclose == 0 and dist == 0. iclose, kclose, dist = klist.find_closest( Kpoint([0.001, 0.002, 0.003], klist.reciprocal_lattice)) assert iclose == 0 self.assert_almost_equal(dist, 0.001984943324127921) # Compute mapping k_index --> (k + q)_index, g0 k2kqg = klist.get_k2kqg_map((0, 0, 0)) assert all(ikq == ik for ik, (ikq, g0) in k2kqg.items()) k2kqg = klist.get_k2kqg_map((1 / 2, 1 / 2, 1 / 2)) assert len(k2kqg) == 2 assert k2kqg[0][0] == 1 and np.all(k2kqg[0][1] == 0) assert k2kqg[1][0] == 0 and np.all(k2kqg[1][1] == 1) frac_coords = [0, 0, 0, 1 / 2, 1 / 3, 1 / 3] other_klist = KpointList(lattice, frac_coords) # Test __add__ add_klist = klist + other_klist for k in itertools.chain(klist, other_klist): assert k in add_klist assert add_klist.count([0, 0, 0]) == 2 # Remove duplicated k-points. add_klist = add_klist.remove_duplicated() assert add_klist.count([0, 0, 0]) == 1 assert len(add_klist) == 4 assert add_klist == add_klist.remove_duplicated() frac_coords = [1 / 2, 1 / 2, 1 / 2, 1 / 2, 1 / 2, 1 / 2] klist = KpointList(lattice, frac_coords, weights=None) assert np.all(klist.get_all_kindices([1 / 2, 1 / 2, 1 / 2]) == [0, 1]) with self.assertRaises(ValueError): klist.index((0, 0, 0))
class ScrReader(ETSF_Reader): """ This object reads the results stored in the SCR (Screening) file produced by ABINIT. It provides helper functions to access the most important quantities. double inverse_dielectric_function(number_of_qpoints_dielectric_function, number_of_frequencies_dielectric_function, number_of_spins, number_of_spins, number_of_coefficients_dielectric_function, number_of_coefficients_dielectric_function, complex) .. rubric:: Inheritance Diagram .. inheritance-diagram:: ScrReader """ def __init__(self, filepath): super(ScrReader, self).__init__(filepath) # Read and store important quantities. self.structure = self.read_structure() qfrac_coords = self.read_value("qpoints_dielectric_function") self.kpoints = KpointList(self.structure.reciprocal_lattice, qfrac_coords) self.wpoints = self.read_value("frequencies_dielectric_function", cmode="c") self.ng = self.read_dimvalue("number_of_coefficients_dielectric_function") # Find number of real/imaginary frequencies. self.nw = len(self.wpoints) self.nrew = self.nw self.nimw = 0 for i, w in enumerate(self.wpoints): if np.iscomplex(w): self.nrew = i break self.nimw = self.nw - self.nrew if self.nimw and not np.all(np.iscomplex(self.wpoints[self.nrew+1:])): raise ValueError("wpoints should contained real points packed in the first positions\n" "followed by imaginary points but got: %s" % str(self.wpoints)) # Define self.netcdf_name from the data available on file. nfound = 0 netcdf_names = ["polarizability", "dielectric_function", "inverse_dielectric_function"] for tryname in netcdf_names: if tryname in self.rootgrp.variables: self.netcdf_name = tryname nfound += 1 if nfound == 0: raise RuntimeError("Cannot find `%s` in netcdf file" % str(netcdf_names)) if nfound > 1: raise RuntimeError("Find multiple netcdf arrays (%s) in netcdf file!" % str(netcdf_names)) def read_params(self): """ Read the most important parameters used to compute the screening i.e. the parameters that may be subject to convergence studies. Returns: |AttrDict| a dictionary whose keys can be accessed with the dot notation i.e. ``d.key``. """ # TODO: ecuteps is missing! keys = ["ikxc", "inclvkb", "gwcalctyp", "nbands_used", "npwwfn_used", "spmeth", "test_type", "tordering", "awtr", "icutcoul", "gwcomp", "gwgamma", "mbpt_sciss", "spsmear", "zcut", "gwencomp"] def convert(arr): """Convert to scalar if size == 1""" return np.asscalar(arr) if arr.size == 1 else arr return AttrDict({k: convert(self.read_value(k)) for k in keys}) def read_emacro_lf(self, kpoint=(0, 0, 0)): """ Read the macroscopic dielectric function *with* local field effects 1 / em1_{0,0)(kpoint, omega). Return: |Function1D| object. """ if self.netcdf_name == "inverse_dielectric_function": em1 = self.read_wslice(kpoint, ig1=0, ig2=0) emacro = 1 / em1[:self.nrew] else: raise NotImplementedError("emacro_lf with netcdf != InverseDielectricFunction") return Function1D(np.real(self.wpoints[:self.nrew]).copy(), emacro) def read_emacro_nlf(self, kpoint=(0, 0, 0)): """ Read the macroscopic dielectric function *without* local field effects e_{0,0)(kpoint, omega). Return: |Function1D| .. warning:: This function performs the inversion of e-1 to get e. that can be quite expensive and memory demanding for large matrices! """ if self.netcdf_name == "inverse_dielectric_function": em1 = self.read_wggmat(kpoint) e = np.linalg.inv(em1.wggmat[:self.nrew, :, :]) else: raise NotImplementedError("emacro_nlf with netcdf != InverseDielectricFunction") return Function1D(np.real(self.wpoints[:self.nrew]).copy(), e[:, 0, 0]) def read_eelf(self, kpoint=(0, 0, 0)): """ Read electron energy loss function - Im(1/ emacro) Return: |Function1D| object. """ # eelf = -Im(1 / eM) emacro_lf = self.read_emacro_lf(kpoint=kpoint) #emacro_lf = self.read_emacro_nlf(kpoint=kpoint) values = (-1 / emacro_lf.values).imag return Function1D(emacro_lf.mesh.copy(), values) def read_wggmat(self, kpoint, spin1=0, spin2=0, cls=None): """ Read data at the given k-point and return an instance of ``cls`` where ``cls`` is a subclass of :class:`_AwggMatrix` """ cls = _AwggMatrix.class_from_netcdf_name(self.netcdf_name) if cls is None else cls var = self.rootgrp.variables["reduced_coordinates_plane_waves_dielectric_function"] # Use ik=0 because the basis set is not k-dependent. ik0 = 0 gvecs = var[ik0, :] #print("gvecs", gvecs) kpoint, ik = self.find_kpoint_fileindex(kpoint) # FIXME ecuteps is missing # TODO: Gpshere.find is very slow if we don't take advantage of shells ecuteps = 2 gsphere = GSphere(ecuteps, self.structure.reciprocal_lattice, kpoint, gvecs) # Exchange spin due to F --> C values = self.rootgrp.variables[self.netcdf_name][ik, :, spin2, spin1, :, :, :] wggmat = values[:, :, :, 0] + 1j * values[:, :, :, 1] return cls(self.wpoints, gsphere, wggmat, inord="F") def find_kpoint_fileindex(self, kpoint): """ Returns the k-point and the index of the k-point in the netcdf file. Accepts |Kpoint| instance or integer. """ if duck.is_intlike(kpoint): ik = int(kpoint) else: ik = self.kpoints.index(kpoint) return self.kpoints[ik], ik def read_wslice(self, kpoint, ig1=0, ig2=0, spin1=0, spin2=0): """Read slice along the frequency dimension.""" kpoint, ik = self.find_kpoint_fileindex(kpoint) var = self.rootgrp.variables[self.netcdf_name] values = var[ik, :, spin2, spin1, ig2, ig1, :] # Exchange G indices F --> C return values[:, 0] + 1j * values[:, 1]
class ScrReader(ETSF_Reader): """ This object reads the results stored in the SCR (Screening) file produced by ABINIT. It provides helper functions to access the most important quantities. """ def __init__(self, filepath): super(ScrReader, self).__init__(filepath) # Read and store important quantities. self.structure = self.read_structure() qred_coords = self.read_value("qpoints_dielectric_function") self.qpoints = KpointList(self.structure.reciprocal_lattice, qred_coords) self.wpts = self.read_value("frequencies_dielectric_function", cmode="c") #def read_params(self): # """ # Read the most importan parameters used to generate the data, i.e. # the parameters that may be subject to convergence studies. # Returns: # :class:`AttrDict` a dictionary whose keys can be accessed # with the dot notation i.e. d.key. # """ # keys = ["npwwfn_used", "nbands_used",] # return AttrDict({k: self.read_value(k) for k in keys}) def find_qpoint_fileindex(self, qpoint): """ Returns the q-point and the index of in the netcdf file. Accepts `Kpoint` instance or integer Raise: `KpointsError` if qpoint cannot be found. """ if isinstance(qpoint, int): iq = qpoint else: iq = self.qpoints.index(qpoint) return self.qpoints[iq], iq def read_wggfunc(self, qpoint, cls): """ Read data at the given q-point and return an instance of `cls` where `cls` is a subclass of `WGGFunction` """ qpoint, iq = self.find_qpoint_fileindex(qpoint) # TODO: I don't remember how to slice in python-netcdf # ecuteps all_gvecs = self.read_value( "reduced_coordinates_plane_waves_dielectric_function") ecuteps = 2 # TODO: Gpshere.find is very slow if we don't take advantage of shells gsphere = GSphere(ecuteps, self.structure.reciprocal_lattice, qpoint, all_gvecs[iq]) full_wggmat = self.read_value(cls.etsf_name, cmode="c") wggmat = full_wggmat[iq] return cls(qpoint, self.wpts, gsphere, wggmat, inord="F")
class SigresReader(ETSF_Reader): """This object provides method to read data from the SIGRES file produced ABINIT. # See 70gw/m_sigma_results.F90 # Name of the diagonal matrix elements stored in the file. # b1gw:b2gw,nkibz,nsppol*nsig_ab)) #_DIAGO_MELS = [ # "sigxme", # "vxcme", # "vUme", # "dsigmee0", # "sigcmee0", # "sigxcme", # "ze0", #] integer :: b1gw,b2gw ! min and Max gw band indeces over spin and k-points (used to dimension) integer :: gwcalctyp ! Flag defining the calculation type. integer :: nkptgw ! No. of points calculated integer :: nkibz ! No. of irreducible k-points. integer :: nbnds ! Total number of bands integer :: nomega_r ! No. of real frequencies for the spectral function. integer :: nomega_i ! No. of frequencies along the imaginary axis. integer :: nomega4sd ! No. of real frequencies to evaluate the derivative of $\Sigma(E)$. integer :: nsig_ab ! 1 if nspinor=1,4 for noncollinear case. integer :: nsppol ! No. of spin polarizations. integer :: usepawu ! 1 if we are using LDA+U as starting point (only for PAW) real(dp) :: deltae ! Frequency step for the calculation of d\Sigma/dE real(dp) :: maxomega4sd ! Max frequency around E_ks for d\Sigma/dE. real(dp) :: maxomega_r ! Max frequency for spectral function. real(dp) :: scissor_ene ! Scissor energy value. zero for None. integer,pointer :: maxbnd(:,:) ! maxbnd(nkptgw,nsppol) ! Max band index considered in GW for this k-point. integer,pointer :: minbnd(:,:) ! minbnd(nkptgw,nsppol) ! Min band index considered in GW for this k-point. real(dp),pointer :: degwgap(:,:) ! degwgap(nkibz,nsppol) ! Difference btw the QPState and the KS optical gap. real(dp),pointer :: egwgap(:,:) ! egwgap(nkibz,nsppol)) ! QPState optical gap at each k-point and spin. real(dp),pointer :: en_qp_diago(:,:,:) ! en_qp_diago(nbnds,nkibz,nsppol)) ! QPState energies obtained from the diagonalization of the Hermitian approximation to Sigma (QPSCGW) real(dp),pointer :: e0(:,:,:) ! e0(nbnds,nkibz,nsppol) ! KS eigenvalues for each band, k-point and spin. In case of self-consistent? real(dp),pointer :: e0gap(:,:) ! e0gap(nkibz,nsppol), ! KS gap at each k-point, for each spin. real(dp),pointer :: omega_r(:) ! omega_r(nomega_r) ! real frequencies used for the self energy. real(dp),pointer :: kptgw(:,:) ! kptgw(3,nkptgw) ! ! TODO there is a similar array in sigma_parameters ! List of calculated k-points. real(dp),pointer :: sigxme(:,:,:) ! sigxme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! Diagonal matrix elements of $\Sigma_x$ i.e $\<nks|\Sigma_x|nks\>$ real(dp),pointer :: vxcme(:,:,:) ! vxcme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! $\<nks|v_{xc}[n_val]|nks\>$ matrix elements of vxc (valence-only contribution). real(dp),pointer :: vUme(:,:,:) ! vUme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! $\<nks|v_{U}|nks\>$ for LDA+U. complex(dpc),pointer :: degw(:,:,:) ! degw(b1gw:b2gw,nkibz,nsppol)) ! Difference between the QPState and the KS energies. complex(dpc),pointer :: dsigmee0(:,:,:) ! dsigmee0(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! Derivative of $\Sigma_c(E)$ calculated at the KS eigenvalue. complex(dpc),pointer :: egw(:,:,:) ! egw(nbnds,nkibz,nsppol)) ! QPState energies, $\epsilon_{nks}^{QPState}$. complex(dpc),pointer :: eigvec_qp(:,:,:,:) ! eigvec_qp(nbnds,nbnds,nkibz,nsppol)) ! Expansion of the QPState amplitude in the KS basis set. complex(dpc),pointer :: hhartree(:,:,:,:) ! hhartree(b1gw:b2gw,b1gw:b2gw,nkibz,nsppol*nsig_ab) ! $\<nks|T+v_H+v_{loc}+v_{nl}|mks\>$ complex(dpc),pointer :: sigcme(:,:,:,:) ! sigcme(b1gw:b2gw,nkibz,nomega_r,nsppol*nsig_ab)) ! $\<nks|\Sigma_{c}(E)|nks\>$ at each nomega_r frequency complex(dpc),pointer :: sigmee(:,:,:) ! sigmee(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! $\Sigma_{xc}E_{KS} + (E_{QPState}- E_{KS})*dSigma/dE_KS complex(dpc),pointer :: sigcmee0(:,:,:) ! sigcmee0(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! Diagonal mat. elements of $\Sigma_c(E)$ calculated at the KS energy $E_{KS}$ complex(dpc),pointer :: sigcmesi(:,:,:,:) ! sigcmesi(b1gw:b2gw,nkibz,nomega_i,nsppol*nsig_ab)) ! Matrix elements of $\Sigma_c$ along the imaginary axis. ! Only used in case of analytical continuation. complex(dpc),pointer :: sigcme4sd(:,:,:,:) ! sigcme4sd(b1gw:b2gw,nkibz,nomega4sd,nsppol*nsig_ab)) ! Diagonal matrix elements of \Sigma_c around the zeroth order eigenvalue (usually KS). complex(dpc),pointer :: sigxcme(:,:,:,:) ! sigxme(b1gw:b2gw,nkibz,nomega_r,nsppol*nsig_ab)) ! $\<nks|\Sigma_{xc}(E)|nks\>$ at each real frequency frequency. complex(dpc),pointer :: sigxcmesi(:,:,:,:) ! sigxcmesi(b1gw:b2gw,nkibz,nomega_i,nsppol*nsig_ab)) ! Matrix elements of $\Sigma_{xc}$ along the imaginary axis. ! Only used in case of analytical continuation. complex(dpc),pointer :: sigxcme4sd(:,:,:,:) ! sigxcme4sd(b1gw:b2gw,nkibz,nomega4sd,nsppol*nsig_ab)) ! Diagonal matrix elements of \Sigma_xc for frequencies around the zeroth order eigenvalues. complex(dpc),pointer :: ze0(:,:,:) ! ze0(b1gw:b2gw,nkibz,nsppol)) ! renormalization factor. $(1-\dfrac{\partial\Sigma_c} {\partial E_{KS}})^{-1}$ complex(dpc),pointer :: omega_i(:) ! omegasi(nomega_i) ! Frequencies along the imaginary axis used for the analytical continuation. complex(dpc),pointer :: omega4sd(:,:,:,:) ! omega4sd(b1gw:b2gw,nkibz,nomega4sd,nsppol). ! Frequencies used to evaluate the Derivative of Sigma. """ def __init__(self, path): self.ks_bands = ElectronBands.from_file(path) self.nsppol = self.ks_bands.nsppol super(SigresReader, self).__init__(path) try: self.nomega_r = self.read_dimvalue("nomega_r") except self.Error: self.nomega_r = 0 #self.nomega_i = self.read_dim("nomega_i") # Save important quantities needed to simplify the API. self.structure = self.read_structure() self.gwcalctyp = self.read_value("gwcalctyp") self.usepawu = self.read_value("usepawu") # 1) The K-points of the homogeneous mesh. self.ibz = self.ks_bands.kpoints # 2) The K-points where QPState corrections have been calculated. gwred_coords = self.read_redc_gwkpoints() self.gwkpoints = KpointList(self.structure.reciprocal_lattice, gwred_coords) # minbnd[nkptgw,nsppol] gives the minimum band index computed # Note conversion between Fortran and python convention. self.gwbstart_sk = self.read_value("minbnd") - 1 self.gwbstop_sk = self.read_value("maxbnd") # min and Max band index for GW corrections. self.min_gwbstart = np.min(self.gwbstart_sk) self.max_gwbstart = np.max(self.gwbstart_sk) self.min_gwbstop = np.min(self.gwbstop_sk) self.max_gwbstop = np.max(self.gwbstop_sk) self._egw = self.read_value("egw", cmode="c") # Read and save important matrix elements. # All these arrays are dimensioned # vxcme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) self._vxcme = self.read_value("vxcme") self._sigxme = self.read_value("sigxme") self._hhartree = self.read_value("hhartree", cmode="c") self._vUme = self.read_value("vUme") #if self.usepawu == 0: self._vUme.fill(0.0) # Complex arrays self._sigcmee0 = self.read_value("sigcmee0", cmode="c") self._ze0 = self.read_value("ze0", cmode="c") # Frequencies for the spectral function. if self.has_spfunc: self._omega_r = self.read_value("omega_r") self._sigcme = self.read_value("sigcme", cmode="c") self._sigxcme = self.read_value("sigxcme", cmode="c") # Self-consistent case self._en_qp_diago = self.read_value("en_qp_diago") # <KS|QPState> self._eigvec_qp = self.read_value("eigvec_qp", cmode="c") #self._mlda_to_qp #def is_selfconsistent(self, mode): # return self.gwcalctyp @property def has_spfunc(self): """True if self contains the spectral function.""" return self.nomega_r def kpt2fileindex(self, kpoint): """ Helper function that returns the index of kpoint in the netcdf file. Accepts `Kpoint` instance or integer Raise: `KpointsError` if kpoint cannot be found. .. note:: This function is needed since arrays in the netcdf file are dimensioned with the total number of k-points in the IBZ. """ if isinstance(kpoint, int): return kpoint #kpoint = self.gwkpoints[kpoint] try: return self.ibz.index(kpoint) except: raise def gwkpt2seqindex(self, gwkpoint): """ This function returns the index of the GW k-point in (0:nkptgw) Used to access data in the arrays that are dimensioned [0:nkptgw] e.g. minbnd. """ if isinstance(gwkpoint, int): return gwkpoint else: return self.gwkpoints.index(gwkpoint) def read_redc_gwkpoints(self): return self.read_value("kptgw") def read_allqps(self): qps_spin = self.nsppol * [None] for spin in range(self.nsppol): qps = [] for gwkpoint in self.gwkpoints: ik = self.gwkpt2seqindex(gwkpoint) bands = range(self.gwbstart_sk[spin,ik], self.gwbstop_sk[spin,ik]) for band in bands: qps.append(self.read_qp(spin, gwkpoint, band)) qps_spin[spin] = QPList(qps) return tuple(qps_spin) def read_qplist_sk(self, spin, kpoint): ik = self.gwkpt2seqindex(kpoint) bstart, bstop = self.gwbstart_sk[spin, ik], self.gwbstop_sk[spin, ik] return QPList([self.read_qp(spin, kpoint, band) for band in range(bstart, bstop)]) #def read_qpene(self, spin, kpoint, band) def read_qpenes(self): return self._egw[:, :, :] def read_qp(self, spin, kpoint, band): ik_file = self.kpt2fileindex(kpoint) ib_file = band - self.gwbstart_sk[spin, self.gwkpt2seqindex(kpoint)] return QPState( spin=spin, kpoint=kpoint, band=band, e0=self.read_e0(spin, ik_file, band), qpe=self._egw[spin, ik_file, band], qpe_diago=self._en_qp_diago[spin, ik_file, band], vxcme=self._vxcme[spin, ik_file, ib_file], sigxme=self._sigxme[spin, ik_file, ib_file], sigcmee0=self._sigcmee0[spin, ik_file, ib_file], vUme=self._vUme[spin, ik_file, ib_file], ze0=self._ze0[spin, ik_file, ib_file], ) def read_qpgaps(self): """Read the QP gaps. Returns ndarray with shape [nsppol, nkibz] in eV""" return self.read_value("egwgap") def read_e0(self, spin, kfile, band): return self.ks_bands.eigens[spin, kfile, band] def read_sigmaw(self, spin, kpoint, band): """Returns the real and the imaginary part of the self energy.""" if not self.has_spfunc: raise ValueError("%s does not contain spectral function data" % self.path) ik = self.kpt2fileindex(kpoint) return self._omega_r, self._sigxcme[spin,:,ik,band] def read_spfunc(self, spin, kpoint, band): """ Returns the spectral function. one/pi * ABS(AIMAG(Sr%sigcme(ib,ikibz,io,is))) / ( (REAL(Sr%omega_r(io)-Sr%hhartree(ib,ib,ikibz,is)-Sr%sigxcme(ib,ikibz,io,is)))**2 & +(AIMAG(Sr%sigcme(ib,ikibz,io,is)))**2) / Ha_eV,& """ if not self.has_spfunc: raise ValueError("%s does not contain spectral function data" % self.path) ik = self.kpt2fileindex(kpoint) ib = band - self.gwbstart_sk[spin, self.gwkpt2seqindex(kpoint)] aim_sigc = np.abs(self._sigcme[spin,:,ik,ib].imag) den = np.zeros(self.nomega_r) for (io, omega) in enumerate(self._omega_r): den[io] = (omega - self._hhartree[spin,ik,ib,ib].real - self._sigxcme[spin,io,ik,ib].real) ** 2 + \ self._sigcme[spin,io,ik,ib].imag ** 2 return self._omega_r, 1./np.pi * (aim_sigc/den) def read_eigvec_qp(self, spin, kpoint, band=None): """ Returns <KS|QPState> for the given spin, kpoint and band. If band is None, <KS_b|QP_{b'}> is returned. """ ik = self.kpt2fileindex(kpoint) if band is not None: return self._eigvec_qp[spin,ik,:,band] else: return self._eigvec_qp[spin,ik,:,:] def read_params(self): """ Read the parameters of the calculation. Returns :class:`AttrDict` instance with the value of the parameters. """ param_names = [ "ecutwfn", "ecuteps", "ecutsigx", "scr_nband", "sigma_nband", "gwcalctyp", "scissor_ene", ] params = AttrDict() for pname in param_names: params[pname] = self.read_value(pname, default=None) # Other quantities that might be subject to convergence studies. params["nkibz"] = len(self.ibz) return params def print_qps(self, spin=None, kpoints=None, bands=None, fmt=None, stream=sys.stdout): """ Args: spin: Spin index, if None all spins are considered kpoints: List of k-points to select. Default: all kpoints bands: List of bands to select. Default is all bands fmt: Format string passe to `to_strdict` stream: file-like object. Returns List of tables. """ spins = range(self.nsppol) if spin is None else [spin] kpoints = self.gwkpoints if kpoints is None else [kpoints] if bands is not None: bands = [bands] header = QPState.get_fields(exclude=["spin", "kpoint"]) tables = [] for spin in spins: for kpoint in kpoints: table_sk = PrettyTable(header) if bands is None: ik = self.gwkpt2seqindex(kpoint) bands = range(self.gwbstart_sk[spin,ik], self.gwbstop_sk[spin,ik]) for band in bands: qp = self.read_qp(spin, kpoint, band) d = qp.to_strdict(fmt=fmt) table_sk.add_row([d[k] for k in header]) stream.write("\nkpoint: %s, spin: %s, energy units: eV (NB: bands start from zero)\n" % (kpoint, spin)) print(table_sk, file=stream) stream.write("\n") # Add it to tables. tables.append(table_sk) return tables
class SigresReader(ETSF_Reader): """This object provides method to read data from the SIGRES file produced ABINIT. # See 70gw/m_sigma_results.F90 # Name of the diagonal matrix elements stored in the file. # b1gw:b2gw,nkibz,nsppol*nsig_ab)) #_DIAGO_MELS = [ # "sigxme", # "vxcme", # "vUme", # "dsigmee0", # "sigcmee0", # "sigxcme", # "ze0", #] integer :: b1gw,b2gw ! min and Max gw band indeces over spin and k-points (used to dimension) integer :: gwcalctyp ! Flag defining the calculation type. integer :: nkptgw ! No. of points calculated integer :: nkibz ! No. of irreducible k-points. integer :: nbnds ! Total number of bands integer :: nomega_r ! No. of real frequencies for the spectral function. integer :: nomega_i ! No. of frequencies along the imaginary axis. integer :: nomega4sd ! No. of real frequencies to evaluate the derivative of $\Sigma(E)$. integer :: nsig_ab ! 1 if nspinor=1,4 for noncollinear case. integer :: nsppol ! No. of spin polarizations. integer :: usepawu ! 1 if we are using LDA+U as starting point (only for PAW) real(dp) :: deltae ! Frequency step for the calculation of d\Sigma/dE real(dp) :: maxomega4sd ! Max frequency around E_ks for d\Sigma/dE. real(dp) :: maxomega_r ! Max frequency for spectral function. real(dp) :: scissor_ene ! Scissor energy value. zero for None. integer,pointer :: maxbnd(:,:) ! maxbnd(nkptgw,nsppol) ! Max band index considered in GW for this k-point. integer,pointer :: minbnd(:,:) ! minbnd(nkptgw,nsppol) ! Min band index considered in GW for this k-point. real(dp),pointer :: degwgap(:,:) ! degwgap(nkibz,nsppol) ! Difference btw the QPState and the KS optical gap. real(dp),pointer :: egwgap(:,:) ! egwgap(nkibz,nsppol)) ! QPState optical gap at each k-point and spin. real(dp),pointer :: en_qp_diago(:,:,:) ! en_qp_diago(nbnds,nkibz,nsppol)) ! QPState energies obtained from the diagonalization of the Hermitian approximation to Sigma (QPSCGW) real(dp),pointer :: e0(:,:,:) ! e0(nbnds,nkibz,nsppol) ! KS eigenvalues for each band, k-point and spin. In case of self-consistent? real(dp),pointer :: e0gap(:,:) ! e0gap(nkibz,nsppol), ! KS gap at each k-point, for each spin. real(dp),pointer :: omega_r(:) ! omega_r(nomega_r) ! real frequencies used for the self energy. real(dp),pointer :: kptgw(:,:) ! kptgw(3,nkptgw) ! ! TODO there is a similar array in sigma_parameters ! List of calculated k-points. real(dp),pointer :: sigxme(:,:,:) ! sigxme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! Diagonal matrix elements of $\Sigma_x$ i.e $\<nks|\Sigma_x|nks\>$ real(dp),pointer :: vxcme(:,:,:) ! vxcme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! $\<nks|v_{xc}[n_val]|nks\>$ matrix elements of vxc (valence-only contribution). real(dp),pointer :: vUme(:,:,:) ! vUme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! $\<nks|v_{U}|nks\>$ for LDA+U. complex(dpc),pointer :: degw(:,:,:) ! degw(b1gw:b2gw,nkibz,nsppol)) ! Difference between the QPState and the KS energies. complex(dpc),pointer :: dsigmee0(:,:,:) ! dsigmee0(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! Derivative of $\Sigma_c(E)$ calculated at the KS eigenvalue. complex(dpc),pointer :: egw(:,:,:) ! egw(nbnds,nkibz,nsppol)) ! QPState energies, $\epsilon_{nks}^{QPState}$. complex(dpc),pointer :: eigvec_qp(:,:,:,:) ! eigvec_qp(nbnds,nbnds,nkibz,nsppol)) ! Expansion of the QPState amplitude in the KS basis set. complex(dpc),pointer :: hhartree(:,:,:,:) ! hhartree(b1gw:b2gw,b1gw:b2gw,nkibz,nsppol*nsig_ab) ! $\<nks|T+v_H+v_{loc}+v_{nl}|mks\>$ complex(dpc),pointer :: sigcme(:,:,:,:) ! sigcme(b1gw:b2gw,nkibz,nomega_r,nsppol*nsig_ab)) ! $\<nks|\Sigma_{c}(E)|nks\>$ at each nomega_r frequency complex(dpc),pointer :: sigmee(:,:,:) ! sigmee(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! $\Sigma_{xc}E_{KS} + (E_{QPState}- E_{KS})*dSigma/dE_KS complex(dpc),pointer :: sigcmee0(:,:,:) ! sigcmee0(b1gw:b2gw,nkibz,nsppol*nsig_ab)) ! Diagonal mat. elements of $\Sigma_c(E)$ calculated at the KS energy $E_{KS}$ complex(dpc),pointer :: sigcmesi(:,:,:,:) ! sigcmesi(b1gw:b2gw,nkibz,nomega_i,nsppol*nsig_ab)) ! Matrix elements of $\Sigma_c$ along the imaginary axis. ! Only used in case of analytical continuation. complex(dpc),pointer :: sigcme4sd(:,:,:,:) ! sigcme4sd(b1gw:b2gw,nkibz,nomega4sd,nsppol*nsig_ab)) ! Diagonal matrix elements of \Sigma_c around the zeroth order eigenvalue (usually KS). complex(dpc),pointer :: sigxcme(:,:,:,:) ! sigxme(b1gw:b2gw,nkibz,nomega_r,nsppol*nsig_ab)) ! $\<nks|\Sigma_{xc}(E)|nks\>$ at each real frequency frequency. complex(dpc),pointer :: sigxcmesi(:,:,:,:) ! sigxcmesi(b1gw:b2gw,nkibz,nomega_i,nsppol*nsig_ab)) ! Matrix elements of $\Sigma_{xc}$ along the imaginary axis. ! Only used in case of analytical continuation. complex(dpc),pointer :: sigxcme4sd(:,:,:,:) ! sigxcme4sd(b1gw:b2gw,nkibz,nomega4sd,nsppol*nsig_ab)) ! Diagonal matrix elements of \Sigma_xc for frequencies around the zeroth order eigenvalues. complex(dpc),pointer :: ze0(:,:,:) ! ze0(b1gw:b2gw,nkibz,nsppol)) ! renormalization factor. $(1-\dfrac{\partial\Sigma_c} {\partial E_{KS}})^{-1}$ complex(dpc),pointer :: omega_i(:) ! omegasi(nomega_i) ! Frequencies along the imaginary axis used for the analytical continuation. complex(dpc),pointer :: omega4sd(:,:,:,:) ! omega4sd(b1gw:b2gw,nkibz,nomega4sd,nsppol). ! Frequencies used to evaluate the Derivative of Sigma. """ def __init__(self, path): self.ks_bands = ElectronBands.from_file(path) self.nsppol = self.ks_bands.nsppol super(SigresReader, self).__init__(path) try: self.nomega_r = self.read_dimvalue("nomega_r") except self.Error: self.nomega_r = 0 #self.nomega_i = self.read_dim("nomega_i") # Save important quantities needed to simplify the API. self.structure = self.read_structure() self.gwcalctyp = self.read_value("gwcalctyp") self.usepawu = self.read_value("usepawu") # 1) The K-points of the homogeneous mesh. self.ibz = self.ks_bands.kpoints # 2) The K-points where QPState corrections have been calculated. gwred_coords = self.read_redc_gwkpoints() self.gwkpoints = KpointList(self.structure.reciprocal_lattice, gwred_coords) # minbnd[nkptgw,nsppol] gives the minimum band index computed # Note conversion between Fortran and python convention. self.gwbstart_sk = self.read_value("minbnd") - 1 self.gwbstop_sk = self.read_value("maxbnd") # min and Max band index for GW corrections. self.min_gwbstart = np.min(self.gwbstart_sk) self.max_gwbstart = np.max(self.gwbstart_sk) self.min_gwbstop = np.min(self.gwbstop_sk) self.max_gwbstop = np.max(self.gwbstop_sk) self._egw = self.read_value("egw", cmode="c") # Read and save important matrix elements. # All these arrays are dimensioned # vxcme(b1gw:b2gw,nkibz,nsppol*nsig_ab)) self._vxcme = self.read_value("vxcme") self._sigxme = self.read_value("sigxme") self._hhartree = self.read_value("hhartree", cmode="c") self._vUme = self.read_value("vUme") #if self.usepawu == 0: self._vUme.fill(0.0) # Complex arrays self._sigcmee0 = self.read_value("sigcmee0", cmode="c") self._ze0 = self.read_value("ze0", cmode="c") # Frequencies for the spectral function. if self.has_spfunc: self._omega_r = self.read_value("omega_r") self._sigcme = self.read_value("sigcme", cmode="c") self._sigxcme = self.read_value("sigxcme", cmode="c") # Self-consistent case self._en_qp_diago = self.read_value("en_qp_diago") # <KS|QPState> self._eigvec_qp = self.read_value("eigvec_qp", cmode="c") #self._mlda_to_qp #def is_selfconsistent(self, mode): # return self.gwcalctyp @property def has_spfunc(self): """True if self contains the spectral function.""" return self.nomega_r def kpt2fileindex(self, kpoint): """ Helper function that returns the index of kpoint in the netcdf file. Accepts `Kpoint` instance of integer Raise: `KpointsError` if kpoint cannot be found. .. note:: This function is needed since arrays in the netcdf file are dimensioned with the total number of k-points in the IBZ. """ if isinstance(kpoint, int): kpoint = self.gwkpoints[kpoint] try: return self.ibz.index(kpoint) except: raise def gwkpt2seqindex(self, gwkpoint): """ This function returns the index of the GW k-point in (0:nkptgw) Used to access data in the arrays that are dimensioned [0:nkptgw] e.g. minbnd. """ if isinstance(gwkpoint, int): return gwkpoint else: return self.gwkpoints.index(gwkpoint) def read_redc_gwkpoints(self): return self.read_value("kptgw") def read_allqps(self): qps_spin = self.nsppol * [None] for spin in range(self.nsppol): qps = [] for gwkpoint in self.gwkpoints: ik = self.gwkpt2seqindex(gwkpoint) bands = range(self.gwbstart_sk[spin,ik], self.gwbstop_sk[spin,ik]) for band in bands: qps.append(self.read_qp(spin, gwkpoint, band)) qps_spin[spin] = QPList(qps) return tuple(qps_spin) def read_qplist_sk(self, spin, kpoint): ik = self.gwkpt2seqindex(kpoint) bstart, bstop = self.gwbstart_sk[spin, ik], self.gwbstop_sk[spin, ik] return QPList([self.read_qp(spin, kpoint, band) for band in range(bstart, bstop)]) #def read_qpene(self, spin, kpoint, band) def read_qpenes(self): return self._egw[:, :, :] def read_qp(self, spin, kpoint, band): ik_file = self.kpt2fileindex(kpoint) ib_file = band - self.gwbstart_sk[spin, self.gwkpt2seqindex(kpoint)] return QPState( spin=spin, kpoint=kpoint, band=band, e0=self.read_e0(spin, ik_file, band), qpe=self._egw[spin, ik_file, band], qpe_diago=self._en_qp_diago[spin, ik_file, band], vxcme=self._vxcme[spin, ik_file, ib_file], sigxme=self._sigxme[spin, ik_file, ib_file], sigcmee0=self._sigcmee0[spin, ik_file, ib_file], vUme=self._vUme[spin, ik_file, ib_file], ze0=self._ze0[spin, ik_file, ib_file], ) def read_qpgaps(self): """Read the QP gaps. Returns ndarray with shape [nsppol, nkibz] in eV""" return self.read_value("egwgap") def read_e0(self, spin, kfile, band): return self.ks_bands.eigens[spin, kfile, band] def read_sigmaw(self, spin, kpoint, band): """Returns the real and the imaginary part of the self energy.""" if not self.has_spfunc: raise ValueError("%s does not contain spectral function data" % self.path) ik = self.kpt2fileindex(kpoint) return self._omega_r, self._sigxcme[spin,:,ik,band] def read_spfunc(self, spin, kpoint, band): """ Returns the spectral function. one/pi * ABS(AIMAG(Sr%sigcme(ib,ikibz,io,is))) / ( (REAL(Sr%omega_r(io)-Sr%hhartree(ib,ib,ikibz,is)-Sr%sigxcme(ib,ikibz,io,is)))**2 & +(AIMAG(Sr%sigcme(ib,ikibz,io,is)))**2) / Ha_eV,& """ if not self.has_spfunc: raise ValueError("%s does not contain spectral function data" % self.path) ik = self.kpt2fileindex(kpoint) ib = band - self.gwbstart_sk[spin, self.gwkpt2seqindex(kpoint)] aim_sigc = np.abs(self._sigcme[spin,:,ik,ib].imag) den = np.zeros(self.nomega_r) for (io, omega) in enumerate(self._omega_r): den[io] = (omega - self._hhartree[spin,ik,ib,ib].real - self._sigxcme[spin,io,ik,ib].real) ** 2 + \ self._sigcme[spin,io,ik,ib].imag ** 2 return self._omega_r, 1./np.pi * (aim_sigc/den) def read_eigvec_qp(self, spin, kpoint, band=None): """ Returns <KS|QPState> for the given spin, kpoint and band. If band is None, <KS_b|QP_{b'}> is returned. """ ik = self.kpt2fileindex(kpoint) if band is not None: return self._eigvec_qp[spin,ik,:,band] else: return self._eigvec_qp[spin,ik,:,:] def read_params(self): """ Read the parameters of the calculation. Returns :class:`AttrDict` instance with the value of the parameters. """ param_names = [ "ecutwfn", "ecuteps", "ecutsigx", "scr_nband", "sigma_nband", "gwcalctyp", "scissor_ene", ] params = AttrDict() for pname in param_names: params[pname] = self.read_value(pname, default=None) # Other quantities that might be subject to convergence studies. params["nkibz"] = len(self.ibz) return params def print_qps(self, spin=None, kpoints=None, bands=None, fmt=None, stream=sys.stdout): """ Args: spin: Spin index, if None all spins are considered kpoints: List of k-points to select. Default: all kpoints bands: List of bands to select. Default is all bands fmt: Format string passe to `to_strdict` stream: file-like object. Returns List of tables. """ spins = range(self.nsppol) if spin is None else [spin] kpoints = self.gwkpoints if kpoints is None else [kpoints] if bands is not None: bands = [bands] header = QPState.get_fields(exclude=["spin", "kpoint"]) tables = [] for spin in spins: for kpoint in kpoints: table_sk = PrettyTable(header) if bands is None: ik = self.gwkpt2seqindex(kpoint) bands = range(self.gwbstart_sk[spin,ik], self.gwbstop_sk[spin,ik]) for band in bands: qp = self.read_qp(spin, kpoint, band) d = qp.to_strdict(fmt=fmt) table_sk.add_row([d[k] for k in header]) stream.write("\nkpoint: %s, spin: %s, energy units: eV (NB: bands start from zero)\n" % (kpoint, spin)) print(table_sk, file=stream) stream.write("\n") # Add it to tables. tables.append(table_sk) return tables