def Broaden(options, VV, II): """ Broadening corresponding to Lock in measurements for the conductance and IETS spectra. Also resample II, Pow, and nPh to match a common voltage list """ II = II.copy() II = II.real # First derivative dI and bias list dV dI = (II[1:len(II)] - II[:-1]) / (VV[1] - VV[0]) dV = (VV[1:len(VV)] + VV[:-1]) / 2 # Second derivative and bias ddV ddI = (dI[1:len(dI)] - dI[:-1]) / (VV[1] - VV[0]) ddV = (dV[1:len(dV)] + dV[:-1]) / 2 # Modulation amplitude VA = N.sqrt(2.0) * options.Vrms # New bias ranges for broadening tmp = int(N.floor(VA / (dV[1] - dV[0])) + 1) BdV = dV[tmp:-tmp] BddV = ddV[tmp:-tmp] # Initiate derivatives BdI = 0 * BdV BddI = 0 * BddV # Calculate first derivative with Vrms broadening for iV, V in enumerate(BdV): SIO.printDone(iV, len(BdV), 'Inelastica.Broaden: First-derivative Vrms broadening') wt = (N.array(range(200)) / 200.0 - 0.5) * N.pi VL = V + VA * N.sin(wt) dIL = MM.interpolate(VL, dV, dI) BdI[iV] = 2 / N.pi * N.sum(dIL * (N.cos(wt)**2)) * (wt[1] - wt[0]) # Calculate second derivative with Vrms broadening for iV, V in enumerate(BddV): SIO.printDone(iV, len(BddV), 'Inelastica.Broaden: Second-derivative Vrms broadening') wt = (N.array(range(200)) / 200.0 - 0.5) * N.pi VL = V + VA * N.sin(wt) ddIL = MM.interpolate(VL, ddV, ddI) BddI[iV] = 8.0 / 3.0 / N.pi * N.sum(ddIL * (N.cos(wt)**4)) * (wt[1] - wt[0]) # Reduce to one voltage grid NN = options.biasPoints V = N.linspace(options.minBias, options.maxBias, NN) NI = MM.interpolate(V, VV, II) NdI = MM.interpolate(V, dV, dI) NddI = MM.interpolate(V, ddV, ddI) NBdI = MM.interpolate(V, BdV, BdI) NBddI = MM.interpolate(V, BddV, BddI) return V, NI, NdI, NddI, NBdI, NBddI
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