Example #1
0
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
Example #2
0
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