def get_doses(self, method="gaussian", step=0.1, width=0.2): """ Compute the electronic DOS on a linear mesh. Args: method: String defining the method for the computation of the DOS. step: Energy step (eV) of the linear mesh. width: Standard deviation (eV) of the gaussian. Returns: |ElectronDos| object. """ self.kpoints.check_weights() edos = self.ddks[0].ebands.get_edos(method=method, step=step, width=width) values = np.zeros((self.nsppol, nw)) mesh = edos[0].mesh #vmod_skb = self.vskb if method == "gaussian": for spin in range(self.nsppol): for k, kpoint in enumerate(self.kpoints): wk = kpoint.weight for band in range(self.nband): e = self.eigens[spin, k, band] values[spin] += wk * vmod[spin, k, band] * gaussian(mesh, width, center=e) else: raise NotImplementedError("Method %s is not supported" % method) vdos_spin = [Function1D(mesh, values[spin]) for spin in range(self.nsppol)] vdos = 2 * Function1D(mesh, values[0]) if self.nsppol == 1 else vdos_spin[0] + vdos_spin[1] return dict2namedtuple(edos=edos, vdos=vdos, vdos_spin=vdos_spin)
def __init__(self, structure, qpoints, wmesh, emacros_q, info): """ Args: structure: |Structure| object. qpoints: |KpointList| with the q-points in reduced coordinates. wmesh: Array-like object with the frequency mesh (eV). emacros_q: Iterable with the macroscopic dielectric function for the different q-points. info: Dictionary containing info on the calculation that produced the results (read from file). It must contain the following keywords: - "lfe": True if local field effects are included. - "calc_type": string defining the calculation type. """ self.wmesh = np.array(wmesh) self.qpoints = qpoints assert len(self.qpoints) == len(emacros_q) self.info = info self.emacros_q, em_avg = [], np.zeros(len(wmesh), dtype=complex) for emq in emacros_q: em_avg += emq self.emacros_q.append(Function1D(wmesh, emq)) self.emacros_q = tuple(self.emacros_q) # Compute the average value. # TODO: One should take into account the star of q, but I need the symops self.emacro_avg = Function1D(wmesh, em_avg / self.num_qpoints)
def __init__(self, mesh, values): values = np.atleast_2d(values) self.nsppol = len(values) self.a2f_spin, a2f_tot = [], np.zeros(len(mesh)) for vs in values: self.a2f_spin.append(Function1D(mesh, vs)) a2f_tot += vs # Spin dependent and total a2F(w) self.a2f_spin = tuple(self.a2f_spin) self.a2f = Function1D(mesh, a2f_tot)
def get_emacro_lf(self, qpoint=(0, 0, 0)): """ Compute the macroscopic dielectric function *with* local field effects 1/ em1_{0,0)(q=0, w). Return :class:`Function1D` """ em1 = self.get_em1(qpoint=qpoint) emacro = 1 / em1.wggmat[:, 0, 0] return Function1D(em1.real_wpts, emacro[:em1.nrew])
def to_func1d(self, red_coords=True): """Return list of Function.""" table = self.to_array(red_coords) all_funcs = [] for i in np.arange(3): for j in np.arange(3): all_funcs.append(Function1D(self._wmesh, table[:, i, j])) return all_funcs
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_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 get_harmonic_thermo(self, tstart, tstop, num=50): """ Compute thermodinamic properties from the phonon DOS within the harmonic approximation. tstart: The starting value (in Kelvin) of the temperature mesh. tstop: The end value (in Kelvin) of the mesh. num: int, optional Number of samples to generate. Default is 50. """ tmesh = np.linspace(tstart, tstop, num=num) coth = lambda x: 1.0 / np.tanh(x) csch2 = lambda x: 1.0 / (np.sinh(x)**2) # Boltzmann constant in Ha/K kb_HaK = 8.617343e-5 / Ha_to_eV for i, gw in enumerate(self.values): if gw > 0: break #i = self.dos.find_mesh_index(0.0) # Use atomic units w = self.mesh[i:] * eV_to_Ha gw = self.values[i:] * Ha_to_eV from scipy.integrate import cumtrapz def integrate(values): return cumtrapz(values, x=w)[-1] #w, gw = w[i:], gw[i:] # TODO # Check for possible numerical instabilities when w ~ 0 or negative # Prefactors are missing! df, de, cv, s = map(np.empty, 4 * (len(tmesh), )) for i, temp in enumerate(tmesh): # Equations in Xavier's paper. kt = kb_HaK * temp wd2kt = w / (2 * kt) df[i] = kt * integrate(np.log(2 * np.sinh(wd2kt)) * gw) de[i] = integrate(w * coth(wd2kt) * gw) cv[i] = integrate(wd2kt * wd2kt * csch2(wd2kt) * gw) s[i] = integrate( (wd2kt * coth(wd2kt) - np.log(2 * np.sinh(wd2kt))) * gw) locvars = locals() return HarmonicThermo( **{ name: Function1D(tmesh, locvars[name]) for name in ("df", "de", "cv", "s") })
def get_emacro_nlf(self, qpoint=(0, 0, 0)): """ Compute the macroscopic dielectric function *without* local field effects. e_{0,0)(q=0, w). Return :class:`Function1D` .. warning: This function performs the inversion of e-1 to get e. that can be quite expensive and memory demanding for large matrices! """ em1 = self.get_em1(qpoint=qpoint) e = np.linalg.inv(em1.wggmat[:em1.nrew, :, :]) return Function1D(em1.real_wpts, e[:, 0, 0])
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 get_thermal_expansion_coeff(self, tstart=0, tstop=800, num=100): """ Calculates the thermal expansion coefficient as a function of temperature, using finite difference on the fitted values of the volume as a function of temperature. Args: tstart: The starting value (in Kelvin) of the temperature mesh. tstop: The end value (in Kelvin) of the mesh. num: int, optional Number of samples to generate. Default is 100. Returns: |Function1D| """ f = self.fit_energies(tstart, tstop, num) dt = f.temp[1] - f.temp[0] alpha = (f.min_vol[2:] - f.min_vol[:-2]) / (2 * dt) / f.min_vol[1:-1] return Function1D(f.temp[1:-1], alpha)
def get_intr2j0(self, ecut, numq=3001): r""" Compute 4\pi\int[(\frac{\sin(2\pi q r)}{2\pi q r})(r^2 n(r))dr]. """ qmax = np.sqrt(ecut / 2) / np.pi qmesh = np.linspace(0, qmax, num=numq, endpoint=True) outs = np.empty(len(qmesh)) # Treat q == 0. Note that rmesh[0] > 0 f = 4 * np.pi * self.rmesh**2 * self.values outs[0] = cumtrapz(f, x=self.rmesh)[-1] for i, q in enumerate(qmesh[1:]): twopiqr = 2 * np.pi * q * self.rmesh f = 4 * np.pi * (np.sin(twopiqr) / twopiqr) * self.rmesh**2 * self.values outs[i + 1] = cumtrapz(f, x=self.rmesh)[-1] from abipy.core.func1d import Function1D ecuts = 2 * np.pi**2 * qmesh**2 return Function1D(ecuts, outs)
#!/usr/bin/env python import wx import numpy as np from abipy.core.func1d import Function1D from abipy.gui.awx.func1dframe import Func1dPlotFrame app = wx.App() func1d = Function1D.from_func(np.sin, np.arange(1,100,1)) frame = Func1dPlotFrame(None, func1d) frame.Show() app.MainLoop()
def get_spfunc(self, spin, kpoint, band): wmesh, spf_values = self.reader.read_spfunc(spin, kpoint, band) return Function1D(wmesh, spf_values)
def get_lorentz_intensity(self, temp, laser_freq, width, non_anal_dir=None, min_freq=None, max_freq=None, num=1000, relative=False, units="eV", pol_in=None, pol_out=None): """ Calculates the broadened Raman intensities in arbitrary units for frequencies in an interval. It is possible to use the susceptibilities from the transverse modes only or to specify one of the directions with non analytical contributions. By default it returns a 3x3 matrix where each component is a Function1D object with the Raman intensities with different incoming (the first index) and outgoing (the second index) polarization. These are along the components along the standard axes (e.g. "xx" or "yz" components). If pol_in and pol_out a single Function1D is returned corresponding to the selected polarizations. Args: temp: temperature in K. laser_freq: frequency of the incident laser. The units are determined the "units" argument. width: the width of the Lorentz distribution. The units are determined the "units" argument. non_anal_dir: index of the direction along which the non analytical contribution has been calculated. Corresponds to the indices in the non_anal_directions attribute. min_freq: minimum frequency considered. If None it will be given by the minimum frequency with non zero intensities minus 10 times the width of the distribution. If given the units are determined by the "units" argument. max_freq: maximum frequency considered. If None it will be given by the maximum frequency with non zero intensities plus 10 times the width of the distribution. If given the units are determined by the "units" argument. num: number of frequencies in the interval (min_freq, max_freq). relative: if True the intensities will be rescaled so that the largest value is 1. units: the units in which the input and the output frequencies will be given. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz") pol_in: the polarization of the incoming photon. If not None can be either either a string with one of the cartesian components i.e. "x", "y", "z" or an array with 3 elements representing the polarization direction. If not None pol_out can not be None. pol_out: the polarization of the outgoing photon. If not None can be either either a string with one of the cartesian components i.e. "x", "y", "z" or an array with 3 elements representing the polarization direction. If not None pol_in can not be None. Returns: If pol_in==pol_out==None a 3x3 list with a Function1D corresponding to the different components of the intensities. Otherwise a single Function1D with the intensities of the selected polarizations. Each Function1D has "num" points. """ i = self.get_modes_intensities(temp=temp, laser_freq=laser_freq, non_anal_dir=non_anal_dir, units=units, pol_in=pol_in, pol_out=pol_out) freqs, lorentz = self._get_lorentz_freqs_and_factor( intensity=i, non_anal_dir=non_anal_dir, min_freq=min_freq, max_freq=max_freq, num=num, width=width, units=units) # convert the frequencies to the desired units for the output x = freqs * abu.phfactor_ev2units(units) if pol_in is not None and pol_out is not None: li = np.dot(i, lorentz) if relative: li /= li.max() return Function1D(x, li) else: li = np.einsum("ij, ikl -> jkl", lorentz, i) li_func = [[None] * 3] * 3 for i in range(3): for j in range(3): y = li[:, i, j] if relative: y /= y.max() li_func[i][j] = Function1D(x, y) return li_func
def get_powder_lorentz_intensity(self, temp, laser_freq, width, non_anal_dir=None, min_freq=None, max_freq=None, num=1000, relative=False, units="eV"): """ Calculates the broadened Raman intensities in arbitrary units integrated over all possible orientation to reproduce the powder measurements for frequencies in an interval. It is possible to use the susceptibilities from the transverse modes only or to specify one of the directions with non analytical contributions. Args: temp: temperature in K. laser_freq: frequency of the incident laser. The units are determined the "units" argument. width: the width of the Lorentz distribution. The units are determined the "units" argument. non_anal_dir: index of the direction along which the non analytical contribution has been calculated. Corresponds to the indices in the non_anal_directions attribute. min_freq: minimum frequency considered. If None it will be given by the minimum frequency with non zero intensities minus 10 times the width of the distribution. If given the units are determined by the "units" argument. max_freq: maximum frequency considered. If None it will be given by the maximum frequency with non zero intensities plus 10 times the width of the distribution. If given the units are determined by the "units" argument. num: number of frequencies in the interval (min_freq, max_freq). relative: if True the intensities will be rescaled so that the largest value of the total intensity is 1. units: the units in which the input and the output frequencies will be given. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz") Returns: A PowderIntensity with the parallel, perpendicular and total components of the powder intensities. Each one is a Function1D with "num" points. """ pi = self.get_powder_intensity(temp=temp, laser_freq=laser_freq, non_anal_dir=non_anal_dir, units=units) freqs, lorentz = self._get_lorentz_freqs_and_factor( intensity=pi.tot, non_anal_dir=non_anal_dir, min_freq=min_freq, max_freq=max_freq, num=num, width=width, units=units) lpi = np.array([i.dot(lorentz) for i in pi]) if relative: lpi /= lpi[2].max() # now convert the frequencies to the desired units for the output x = freqs * abu.phfactor_ev2units(units) return PowderIntensity(*(Function1D(x, y) for y in lpi))
#!/usr/bin/env python # # This example shows how to use the Function1D object to analyze and plot results. import numpy as np import matplotlib.pyplot as plt from abipy.core.func1d import Function1D # Build mesh [0, 2pi] with 100 points. mesh = np.linspace(0, 2*np.pi, num=100) # Compute sine function. sine = Function1D.from_func(np.sin, mesh) # Call matplotlib to plot data. fig = plt.figure() ax = fig.add_subplot(1,1,1) # Plot sine. sine.plot_ax(ax, label="sin(x)") # Spline sine on the coarse mesh sx, and plot the data. sx = np.linspace(0, 2*np.pi, num=25) splsine = sine.spline(sx) plt.plot(sx, splsine, "ro", label="splined sine") # Compute the 1-st and 2-nd order derivatives # with finite differences (5-point stencil). for order in [1,2]: der = sine.finite_diff(order=order)
#!/usr/bin/env python import wx import numpy as np from abipy.core.func1d import Function1D from abipy.gui.awx.func1dframe import Func1dPlotFrame app = wx.App() func1d = Function1D.from_func(np.sin, np.arange(1, 100, 1)) frame = Func1dPlotFrame(None, func1d) frame.Show() app.MainLoop()
def __init__(self, spin, kpoint, band, wmesh, sigmaxc_values, spfunc_values): self.spin, self.kpoint, self.band = spin, kpoint, band self.wmesh = np.array(wmesh) self.xc = Function1D(self.wmesh, sigmaxc_values) self.spfunc = Function1D(self.wmesh, spfunc_values)
def ae_core_density_on_mesh(cls, valence_density, structure, rhoc_files, maxr=2.0, nelec=None, method='mesh3d_dist_gridpoints', small_dist_mesh=(8, 8, 8), small_dist_factor=1.5): """ Initialize the all electron core density of the structure from the pseudopotentials *rhoc* files Note that these *rhoc* files contain one column with the radii in Bohrs and one column with the density in #/Bohr^3 multiplied by a factor 4pi. """ rhoc_atom_splines = [None]*len(structure) if isinstance(rhoc_files, (list, tuple)): if len(structure) != len(rhoc_files): raise ValueError('Number of rhoc_files should be equal to the number of sites in the structure') for ifname, fname in rhoc_files: rad_rho = np.fromfile(fname, sep=' ') rad_rho = rad_rho.reshape((len(rad_rho)/2, 2)) radii = rad_rho[:, 0] * bohr_to_angstrom rho = rad_rho[:, 1] / (4.0*np.pi) / (bohr_to_angstrom ** 3) func1d = Function1D(radii, rho) rhoc_atom_splines[ifname] = func1d.spline elif isinstance(rhoc_files, collections.Mapping): atoms_symbols = [elmt.symbol for elmt in structure.composition] if not np.all([atom in rhoc_files for atom in atoms_symbols]): raise ValueError('The rhoc_files should be provided for all the atoms in the structure') splines = {} for symbol, fname in rhoc_files.items(): rad_rho = np.fromfile(fname, sep=' ') rad_rho = rad_rho.reshape((len(rad_rho)/2, 2)) radii = rad_rho[:, 0] * bohr_to_angstrom rho = rad_rho[:, 1] / (4.0*np.pi) / (bohr_to_angstrom ** 3) func1d = Function1D(radii, rho) splines[symbol] = func1d.spline for isite, site in enumerate(structure): rhoc_atom_splines[isite] = splines[site.specie.symbol] core_den = np.zeros_like(valence_density.datar) dvx = valence_density.mesh.dvx dvy = valence_density.mesh.dvy dvz = valence_density.mesh.dvz maxdiag = max([np.linalg.norm(dvx+dvy+dvz), np.linalg.norm(dvx+dvy-dvz), np.linalg.norm(dvx-dvy+dvz), np.linalg.norm(dvx-dvy-dvz)]) smallradius = small_dist_factor*maxdiag if method == 'get_sites_in_sphere': for ix in range(valence_density.mesh.nx): for iy in range(valence_density.mesh.ny): for iz in range(valence_density.mesh.nz): rpoint = valence_density.mesh.rpoint(ix=ix, iy=iy, iz=iz) # TODO: optimize this ! sites = structure.get_sites_in_sphere(pt=rpoint, r=maxr, include_index=True) for site, dist, site_index in sites: if dist > smallradius: core_den[0, ix, iy, iz] += rhoc_atom_splines[site_index](dist) # For small distances, integrate over the small volume dv around the point as the core # density is extremely high close to the atom else: total = 0.0 nnx, nny, nnz = small_dist_mesh ddvx = dvx/nnx ddvy = dvy/nny ddvz = dvz/nnz rpi = rpoint - 0.5 * (dvx + dvy + dvz) + 0.5*ddvx + 0.5*ddvy + 0.5*ddvz for iix in range(nnx): for iiy in range(nny): for iiz in range(nnz): rpoint2 = rpi + iix*ddvx + iiy*ddvy + iiz*ddvz dist2 = np.linalg.norm(rpoint2 - site.coords) total += rhoc_atom_splines[site_index](dist2) total /= (nnx*nny*nnz) core_den[0, ix, iy, iz] += total elif method == 'mesh3d_dist_gridpoints': site_coords = [site.coords for site in structure] dist_gridpoints_sites = valence_density.mesh.dist_gridpoints_in_spheres(points=site_coords, radius=maxr) for isite, dist_gridpoints_site in enumerate(dist_gridpoints_sites): for igp_uc, dist, igp in dist_gridpoints_site: if dist > smallradius: core_den[0, igp_uc[0], igp_uc[1], igp_uc[2]] += rhoc_atom_splines[isite](dist) # For small distances, integrate over the small volume dv around the point as the core density # is extremely high close to the atom else: total = 0.0 nnx, nny, nnz = small_dist_mesh ddvx = dvx/nnx ddvy = dvy/nny ddvz = dvz/nnz rpoint = valence_density.mesh.rpoint(ix=igp[0], iy=igp[1], iz=igp[2]) rpi = rpoint - 0.5 * (dvx + dvy + dvz) + 0.5*ddvx + 0.5*ddvy + 0.5*ddvz for iix in range(nnx): for iiy in range(nny): for iiz in range(nnz): rpoint2 = rpi + iix*ddvx + iiy*ddvy + iiz*ddvz dist2 = np.linalg.norm(rpoint2 - site_coords[isite]) total += rhoc_atom_splines[isite](dist2) total /= (nnx*nny*nnz) core_den[0, igp_uc[0], igp_uc[1], igp_uc[2]] += total else: raise ValueError('Method "{}" is not allowed'.format(method)) if nelec is not None: sum_elec = np.sum(core_den)*valence_density.mesh.dv if np.abs(sum_elec-nelec) / nelec > 0.01: raise ValueError('Summed electrons is different from the actual number of electrons by ' 'more than 1% ...') core_den = core_den / sum_elec * nelec return cls(nspinor=1, nsppol=1, nspden=1, rhor=core_den, structure=structure, iorder='c')