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