def calcIETS(options, GFp, GFm, basis, hw): # Calculate product of electronic traces and voltage functions print 'Inelastica.calcIETS: nHTp =', GFp.nHT * PC.unitConv # OK print 'Inelastica.calcIETS: nHTm =', GFm.nHT * PC.unitConv # OK print 'Inelastica.calcIETS: HTp =', GFp.HT # OK print 'Inelastica.calcIETS: HTm =', GFm.HT # OK # Set up grid and Hilbert term kT = options.Temp / 11604.0 # (eV) # Generate grid for numerical integration of Hilbert term max_hw = max(hw) max_win = max(-options.minBias, max_hw) + 20 * kT + 4 * options.Vrms min_win = min(-options.maxBias, -max_hw) - 20 * kT - 4 * options.Vrms pts = int(N.floor((max_win - min_win) / kT * 3)) Egrid = N.linspace(min_win, max_win, pts) print "Inelastica.calcIETS: Hilbert integration grid : %i pts [%f,%f]" % ( pts, min(Egrid), max(Egrid)) NN = options.biasPoints print 'Inelastica.calcIETS: Biaspoints =', NN # Add some points for the Lock in broadening approxdV = (options.maxBias - options.minBias) / (NN - 1) NN += int(((8 * options.Vrms) / approxdV) + .5) Vl = N.linspace(options.minBias - 4 * options.Vrms, options.maxBias + 4 * options.Vrms, NN) # Vector implementation on Vgrid: wp = (1 + N.sign(Vl)) / 2. # weights for positive V wm = (1 - N.sign(Vl)) / 2. # weights for negative V # Mode occupation and power dissipation Pow = N.zeros((len(hw), NN), N.float) # (modes,Vgrid) nPh = N.zeros((len(hw), NN), N.float) t0 = N.clip(Vl / kT, -700, 700) cosh0 = N.cosh(t0) # Vgrid sinh0 = N.sinh(t0) for i in (hw > options.modeCutoff).nonzero()[0]: P1T = wm * GFm.P1T[i] + wp * GFp.P1T[i] P2T = wm * GFm.P2T[i] + wp * GFp.P2T[i] # Bose distribution nB = 1 / (N.exp(N.clip(hw[i] / kT, -300, 300)) - 1) # number t1 = N.clip(hw[i] / (2 * kT), -700, 700) # number coth1 = N.cosh(t1) / N.sinh(t1) # Emission rate and e-h damping damp = P1T * hw[i] / N.pi # Vgrid emis = P2T * (hw[i] * (cosh0 - 1) * coth1 - Vl * sinh0) / (N.cosh(2 * t1) - cosh0) / N.pi # Determine mode occupation if options.PhHeating: nPh[i, :] = emis / (hw[i] * P1T / N.pi + options.PhExtDamp) + nB else: nPh[i, :] = nB # Mode-resolved power dissipation Pow[i, :] = hw[i] * ((nB - nPh[i]) * damp + emis) # Current: non-Hilbert part (InH) InH = N.zeros((NN, ), N.float) # Vgrid IsymF = N.zeros((NN, ), N.float) for i in (hw > options.modeCutoff).nonzero()[0]: nHT = wm * GFm.nHT[i] + wp * GFp.nHT[i] # Vgrid t1 = hw[i] / (2 * kT) # number t1 = N.clip(t1, -700, 700) coth1 = N.cosh(t1) / N.sinh(t1) t2 = (hw[i] + Vl) / (2 * kT) # Vgrid t2 = N.clip(t2, -700, 700) coth2 = N.cosh(t2) / N.sinh(t2) t3 = (hw[i] - Vl) / (2 * kT) # Vgrid t3 = N.clip(t3, -700, 700) coth3 = N.cosh(t3) / N.sinh(t3) # Vgrid # Isym function Isym = 0.5 * (hw[i] + Vl) * (coth1 - coth2) # Vgrid Isym -= 0.5 * (hw[i] - Vl) * (coth1 - coth3) # non-Hilbert part InH += (Isym + 2 * Vl * nPh[i]) * nHT # Vgrid IsymF += Isym # Current: Add Landauer part, GFm.TeF = GFp.TeF InH += GFp.TeF * Vl # Vgrid # Current: Asymmetric/Hilbert part (IH) try: import scipy.special as SS print "Inelastica: Computing asymmetric term using digamma function," print "... see G. Bevilacqua et al., Eur. Phys. J. B (2016) 89: 3" IH = N.zeros((NN, ), N.float) IasymF = N.zeros((NN, ), N.float) for i in (hw > options.modeCutoff).nonzero()[0]: v0 = hw[i] / (2 * N.pi * kT) vp = (hw[i] + Vl) / (2 * N.pi * kT) vm = (hw[i] - Vl) / (2 * N.pi * kT) Iasym = kT * (2 * v0 * SS.psi(1.j * v0) - vp * SS.psi(1.j * vp) - vm * SS.psi(1.j * vm)).real IasymF += Iasym IH += GFp.HT[i] * N.array(Vl > 0.0, dtype=int) * Iasym IH += GFm.HT[i] * N.array(Vl < 0.0, dtype=int) * Iasym except: print "Computing using explit Hilbert transformation" IH = N.zeros((NN, ), N.float) IasymF = N.zeros((NN, ), N.float) # Prepare box/window function on array Vl2 = N.outer(Vl, N.ones(Egrid.shape)) Egrid2 = N.outer(N.ones(Vl.shape), Egrid) # Box/window function nF(E-Vl2)-nF(E-0): kasse = MM.box(0, -Vl2, Egrid2, kT) # (Vgrid,Egrid) ker = None for i in (hw > options.modeCutoff).nonzero()[0]: # Box/window function nF(E-hw)-nF(E+hw) tmp = MM.box(-hw[i], hw[i], Egrid, kT) hilb, ker = MM.Hilbert(tmp, ker) # Egrid # Calculate Iasym for each bias point for j in range(len(Vl)): Iasym = MM.trapez(Egrid, kasse[j] * hilb, equidistant=True).real / 2 IasymF[j] += Iasym if Vl[j] > 0: IH[j] += GFp.HT[i] * Iasym else: IH[j] += GFm.HT[i] * Iasym # Compute inelastic shot noise terms here: absVl = N.absolute(Vl) Inew = N.zeros(len(Vl), N.float) Snew = N.zeros(len(Vl), N.float) print 'Noise factors:' print GFp.dIel print GFp.dIinel print GFp.dSel print GFp.dSinel for i in (hw > options.modeCutoff).nonzero()[0]: # Elastic part Inew += GFp.dIel[i] * Vl Snew += GFp.dSel[i] * absVl # Inelastic part indx = (absVl - hw[i] < 0).nonzero()[0] fct = absVl - hw[i] fct[indx] = 0.0 # set elements to zero Inew += GFp.dIinel[i] * fct * N.sign(Vl) Snew += GFp.dSinel[i] * fct # Get the right units for gamma_eh, gamma_heat gamma_eh_p = N.zeros((len(hw), ), N.float) gamma_eh_m = N.zeros((len(hw), ), N.float) gamma_heat_p = N.zeros((len(hw), ), N.float) gamma_heat_m = N.zeros((len(hw), ), N.float) for i in (hw > options.modeCutoff).nonzero()[0]: # Units [Phonons per Second per dN where dN is number extra phonons] gamma_eh_p[i] = GFp.P1T[i] * hw[i] * PC.unitConv gamma_eh_m[i] = GFm.P1T[i] * hw[i] * PC.unitConv # Units [Phonons per second per eV [eV-ihw] gamma_heat_p[i] = GFp.P2T[i] * PC.unitConv gamma_heat_m[i] = GFm.P2T[i] * PC.unitConv print 'Inelastica.calcIETS: gamma_eh_p =', gamma_eh_p # OK print 'Inelastica.calcIETS: gamma_eh_m =', gamma_eh_m # OK print 'Inelastica.calcIETS: gamma_heat_p =', gamma_heat_p # OK print 'Inelastica.calcIETS: gamma_heat_m =', gamma_heat_m # OK V, I, dI, ddI, BdI, BddI = Broaden(options, Vl, InH + IH) V, Is, dIs, ddIs, BdIs, BddIs = Broaden(options, Vl, IsymF) V, Ia, dIa, ddIa, BdIa, BddIa = Broaden(options, Vl, IasymF) # Interpolate quantities to new V-grid NPow = N.zeros((len(Pow), len(V)), N.float) NnPh = N.zeros((len(Pow), len(V)), N.float) for ii in range(len(Pow)): NPow[ii] = MM.interpolate(V, Vl, Pow[ii]) NnPh[ii] = MM.interpolate(V, Vl, nPh[ii]) # Interpolate inelastic noise NV, NI, NdI, NddI, NBdI, NBddI = Broaden(options, Vl, GFp.TeF * Vl + Inew) NV, NS, NdS, NddS, NBdS, NBddS = Broaden(options, Vl, Snew) print 'Inelastica.calcIETS: V[:5] =', V[:5] # OK print 'Inelastica.calcIETS: V[-5:][::-1] =', V[-5:][::-1] # OK print 'Inelastica.calcIETS: I[:5] =', I[:5] # OK print 'Inelastica.calcIETS: I[-5:][::-1] =', I[-5:][::-1] # OK print 'Inelastica.calcIETS: BdI[:5] =', BdI[:5] # OK print 'Inelastica.calcIETS: BdI[-5:][::-1] =', BdI[-5:][::-1] # OK print 'Inelastica.calcIETS: BddI[:5] =', BddI[:5] # OK print 'Inelastica.calcIETS: BddI[-5:][::-1] =', BddI[-5:][::-1] # OK datafile = '%s/%s.IN' % (options.DestDir, options.systemlabel) # ascii format writeLOEData2Datafile(datafile + 'p', hw, GFp.TeF, GFp.nHT, GFp.HT) writeLOEData2Datafile(datafile + 'm', hw, GFm.TeF, GFm.nHT, GFm.HT) # netcdf format outNC = initNCfile(datafile, hw, V) write2NCfile(outNC, BddI / BdI, 'IETS', 'Broadened BddI/BdI [1/V]') write2NCfile(outNC, ddI / dI, 'IETS_0', 'Intrinsic ddI/dI [1/V]') write2NCfile(outNC, BdI, 'BdI', 'Broadened BdI, G0') write2NCfile(outNC, BddI, 'BddI', 'Broadened BddI, G0') write2NCfile(outNC, I, 'I', 'Intrinsic I, G0 V') write2NCfile(outNC, dI, 'dI', 'Intrinsic dI, G0') write2NCfile(outNC, ddI, 'ddI', 'Intrinsic ddI, G0/V') if options.LOEscale == 0.0: write2NCfile( outNC, GFp.nHT, 'ISymTr', 'Trace giving Symmetric current contribution (prefactor to universal function)' ) write2NCfile( outNC, GFp.HT, 'IAsymTr', 'Trace giving Asymmetric current contribution (prefactor to universal function)' ) write2NCfile(outNC, gamma_eh_p, 'gamma_eh', 'e-h damping [*deltaN=1/Second]') write2NCfile(outNC, gamma_heat_p, 'gamma_heat', 'Phonon heating [*(bias-hw) (eV) = 1/Second]') # New stuff related to the noise implementation write2NCfile( outNC, NI, 'Inew', 'Intrinsic Inew (new implementation incl. elastic renormalization, T=0)' ) write2NCfile( outNC, NdI, 'dInew', 'Intrinsic dInew (new implementation incl. elastic renormalization, T=0)' ) write2NCfile( outNC, NddI, 'ddInew', 'Intrinsic ddInew (new implementation incl. elastic renormalization, T=0)' ) write2NCfile( outNC, NddI / NdI, 'IETSnew_0', 'Intrinsic ddInew/dInew (new implementation incl. elastic renormalization, T=0) [1/V]' ) write2NCfile( outNC, NBdI, 'BdInew', 'Broadened BdInew (new implementation incl. elastic renormalization, T=0)' ) write2NCfile( outNC, NBddI, 'BddInew', 'Broadened BddInew (new implementation incl. elastic renormalization, T=0)' ) write2NCfile( outNC, NBddI / NBdI, 'IETSnew', 'Broadened BddInew/BdInew (new implementation incl. elastic renormalization, T=0) [1/V]' ) write2NCfile( outNC, NdS, 'dSnew', 'Inelastic first-derivative of the shot noise dSnew (T=0)') else: write2NCfile( outNC, GFp.nHT, 'ISymTr_p', 'Trace giving Symmetric current contribution (prefactor to universal function)' ) write2NCfile( outNC, GFp.HT, 'IAsymTr_p', 'Trace giving Asymmetric current contribution (prefactor to universal function)' ) write2NCfile( outNC, GFm.nHT, 'ISymTr_m', 'Trace giving Symmetric current contribution (prefactor to universal function)' ) write2NCfile( outNC, GFm.HT, 'IAsymTr_m', 'Trace giving Asymmetric current contribution (prefactor to universal function)' ) write2NCfile(outNC, gamma_eh_p, 'gamma_eh_p', 'e-h damping [*deltaN=1/Second]') write2NCfile(outNC, gamma_heat_p, 'gamma_heat_p', 'Phonon heating [*(bias-hw) (eV) = 1/Second]') write2NCfile(outNC, gamma_eh_m, 'gamma_eh_m', 'e-h damping [*deltaN=1/Second]') write2NCfile(outNC, gamma_heat_m, 'gamma_heat_m', 'Phonon heating [*(bias-hw) (eV) = 1/Second]') # Phonon occupations and power balance write2NCfile(outNC, NnPh, 'nPh', 'Number of phonons') write2NCfile(outNC, N.sum(NnPh, axis=0), 'nPh_tot', 'Total number of phonons') write2NCfile(outNC, NPow, 'Pow', 'Mode-resolved power balance') write2NCfile(outNC, N.sum(NPow, axis=0), 'Pow_tot', 'Total power balance') # Write "universal functions" write2NCfile(outNC, dIs, 'dIs', 'dIsym function') write2NCfile(outNC, dIa, 'dIa', 'dIasym function') write2NCfile(outNC, ddIs, 'ddIs', 'ddIasym function') write2NCfile(outNC, ddIa, 'ddIa', 'ddIasym function') # Write energy reference where Greens functions are evaluated outNC.createDimension('number', 1) tmp = outNC.createVariable('EnergyRef', 'd', ('number', )) tmp[:] = N.array(options.energy) # Write LOEscale tmp = outNC.createVariable('LOEscale', 'd', ('number', )) tmp[:] = N.array(options.LOEscale) # Write k-point outNC.createDimension('vector', 3) tmp = outNC.createVariable('kpoint', 'd', ('vector', )) tmp[:] = N.array(options.kpoint) outNC.close() return V, I, dI, ddI, BdI, BddI
from Inelastica import MiscMath as mm import numpy as N from Inelastica import WriteXMGR as XMGR import scipy.special as ss # We try a simple Gaussian function: x0 = 50 x = N.linspace(-x0, x0, 1e5) gauss = -1j * N.pi**-0.5 * N.exp(-x**2) # Output from our Hilbert function: Hg, ker = mm.Hilbert(gauss) # We can compare with this: # Hilbert transform of a Gaussian function is related to Faddeva/w(z) functions: # https://en.wikipedia.org/wiki/Dawson_function ex = -1j * N.pi**0.5 * ss.wofz(x) # Collect data in a plot data1 = XMGR.XYset(x, gauss.imag, legend='Gauss', Lwidth=2) data2 = XMGR.XYset(x, ex.real, legend='Faddeva', Lwidth=2) data3 = XMGR.XYset(x, N.pi * Hg.imag, legend='Inelastica', Lwidth=1) graph1 = XMGR.Graph(data1, data2, data3) graph1.SetXaxis(autoscale=True) graph1.SetYaxis(autoscale=True) graph1.ShowLegend() # Compute error/difference between the two methods: err = ex.real - N.pi * Hg.imag data4 = XMGR.XYset(x, err, legend='Difference', Lwidth=2) graph2 = XMGR.Graph(data4)