def calcTraces(options, GF1, GF2, basis, NCfile, ihw): # Calculate various traces over the electronic structure # Electron-phonon couplings ihw = int(ihw) M = N.array(NCfile.variables['He_ph'][ihw, options.iSpin, :, :], N.complex) try: M += 1.j * N.array( NCfile.variables['ImHe_ph'][ihw, options.iSpin, :, :], N.complex) except: print 'Warning: Variable ImHe_ph not found' # Calculation of intermediate quantity MARGLGM = MM.mm(M, GF1.ARGLG, M) MARGLGM2 = MM.mm(M, GF2.ARGLG, M) # LOE expressions in compact form t1 = MM.mm(MARGLGM, GF2.AR) t2 = MM.mm(MARGLGM2, GF1.AL) # Note that compared with Eq. (10) of PRB89, 081405 (2014) we here use # the definition B_lambda = MM.trace(t1-dagger(t2)), which in turn gives # ReB = MM.trace(t1).real-MM.trace(t2).real # ImB = MM.trace(t1).imag+MM.trace(t2).imag K23 = MM.trace(t1).imag + MM.trace(t2).imag K4 = MM.trace(MM.mm(M, GF1.ALT, M, GF2.AR)) aK23 = 2 * (MM.trace(t1).real - MM.trace(t2).real) # asymmetric part # Non-Hilbert term defined here with a minus sign GF1.nHT[ihw] = NEGF.AssertReal(K23 + K4, 'nHT[%i]' % ihw) GF1.HT[ihw] = NEGF.AssertReal(aK23, 'HT[%i]' % ihw) # Power, damping and current rates GF1.P1T[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.A, M, GF2.A)), 'P1T[%i]' % ihw) GF1.P2T[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.AL, M, GF2.AR)), 'P2T[%i]' % ihw) GF1.ehDampL[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.AL, M, GF2.AL)), 'ehDampL[%i]' % ihw) GF1.ehDampR[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.AR, M, GF2.AR)), 'ehDampR[%i]' % ihw) # Remains from older version (see before rev. 219): #GF.dGnout.append(EC.calcCurrent(options,basis,GF.HNO,mm(Us,-0.5j*(tmp1-dagger(tmp1)),Us))) #GF.dGnin.append(EC.calcCurrent(options,basis,GF.HNO,mm(Us,mm(G,MA1M,Gd)-0.5j*(tmp2-dagger(tmp2)),Us))) # NB: TF Should one use GF.HNO (nonorthogonal) or GF.H (orthogonalized) above? if options.LOEscale == 0.0: # Check against original LOE-WBA formulation isym1 = MM.mm(GF1.ALT, M, GF2.AR, M) isym2 = MM.mm(MM.dagger(GF1.ARGLG), M, GF2.A, M) isym3 = MM.mm(GF1.ARGLG, M, GF2.A, M) isym = MM.trace(isym1) + 1j / 2. * (MM.trace(isym2) - MM.trace(isym3)) print 'LOE-WBA check: Isym diff', K23 + K4 - isym iasym1 = MM.mm(MM.dagger(GF1.ARGLG), M, GF2.AR - GF2.AL, M) iasym2 = MM.mm(GF1.ARGLG, M, GF2.AR - GF2.AL, M) iasym = MM.trace(iasym1) + MM.trace(iasym2) print 'LOE-WBA check: Iasym diff', aK23 - iasym # Compute inelastic shot noise terms according to the papers # Haupt, Novotny & Belzig, PRB 82, 165441 (2010) and # Avriller & Frederiksen, PRB 86, 155411 (2012) # Zero-temperature limit TT = MM.mm(GF1.GammaL, GF1.AR) # this matrix has the correct shape for MM ReGr = (GF1.Gr + GF1.Ga) / 2. tmp = MM.mm(GF1.Gr, M, ReGr, M, GF1.AR) tmp = tmp + MM.dagger(tmp) Tlambda0 = MM.mm(GF1.GammaL, tmp) tmp1 = MM.mm(M, GF1.AR, M) tmp2 = MM.mm(M, GF1.A, M, GF1.Gr, GF1.GammaR) tmp = tmp1 + 1j / 2. * (MM.dagger(tmp2) - tmp2) Tlambda1 = MM.mm(GF1.GammaL, GF1.Gr, tmp, GF1.Ga) MARGL = MM.mm(M, GF1.AR, GF1.GammaL) tmp1 = MM.mm(MARGL, GF1.AR, M) tmp2 = MM.mm(MARGL, GF1.Gr, M, GF1.Gr, GF1.GammaR) tmp = tmp1 + tmp2 tmp = tmp + MM.dagger(tmp) Qlambda = MM.mm(-GF1.Ga, GF1.GammaL, GF1.Gr, tmp) tmp = -2 * TT OneMinusTwoT = tmp + N.identity(len(GF1.GammaL)) # Store relevant traces GF1.dIel[ihw] = NEGF.AssertReal(MM.trace(Tlambda0), 'dIel[%i]' % ihw) GF1.dIinel[ihw] = NEGF.AssertReal(MM.trace(Tlambda1), 'dIinel[%i]' % ihw) GF1.dSel[ihw] = NEGF.AssertReal( MM.trace(MM.mm(OneMinusTwoT, Tlambda0)), 'dSel[%i]' % ihw) GF1.dSinel[ihw] = NEGF.AssertReal( MM.trace(Qlambda + MM.mm(OneMinusTwoT, Tlambda1)), 'dSinel[%i]' % ihw)
def main(options): """ Main routine to compute inelastic transport characteristics (dI/dV, d2I/dV2, IETS etc) Parameters ---------- options : an ``options`` instance """ CF.CreatePipeOutput(options.DestDir + '/' + options.Logfile) VC.OptionsCheck(options, 'Inelastica') CF.PrintMainHeader('Inelastica', options) options.XV = '%s/%s.XV' % (options.head, options.systemlabel) options.geom = MG.Geom(options.XV, BufferAtoms=options.buffer) # Voltage fraction over left-center interface VfracL = options.VfracL # default is 0.5 print 'Inelastica: Voltage fraction over left-center interface: VfracL =', VfracL # Set up electrodes and device Greens function elecL = NEGF.ElectrodeSelfEnergy(options.fnL, options.NA1L, options.NA2L, options.voltage * VfracL) elecL.scaling = options.scaleSigL elecL.semiinf = options.semiinfL elecR = NEGF.ElectrodeSelfEnergy(options.fnR, options.NA1R, options.NA2R, options.voltage * (VfracL - 1.)) elecR.scaling = options.scaleSigR elecR.semiinf = options.semiinfR # Read phonons NCfile = NC4.Dataset(options.PhononNetCDF, 'r') print 'Inelastica: Reading ', options.PhononNetCDF hw = NCfile.variables['hw'][:] # Work with GFs etc for positive (V>0: \mu_L>\mu_R) and negative (V<0: \mu_L<\mu_R) bias voltages GFp = NEGF.GF(options.TSHS, elecL, elecR, Bulk=options.UseBulk, DeviceAtoms=options.DeviceAtoms, BufferAtoms=options.buffer) # Prepare lists for various trace factors #GF.dGnout = [] #GF.dGnin = [] GFp.P1T = N.zeros(len(hw), N.float) # M.A.M.A (total e-h damping) GFp.P2T = N.zeros(len(hw), N.float) # M.AL.M.AR (emission) GFp.ehDampL = N.zeros(len(hw), N.float) # M.AL.M.AL (L e-h damping) GFp.ehDampR = N.zeros(len(hw), N.float) # M.AR.M.AR (R e-h damping) GFp.nHT = N.zeros(len(hw), N.float) # non-Hilbert/Isym factor GFp.HT = N.zeros(len(hw), N.float) # Hilbert/Iasym factor GFp.dIel = N.zeros(len(hw), N.float) GFp.dIinel = N.zeros(len(hw), N.float) GFp.dSel = N.zeros(len(hw), N.float) GFp.dSinel = N.zeros(len(hw), N.float) # GFm = NEGF.GF(options.TSHS, elecL, elecR, Bulk=options.UseBulk, DeviceAtoms=options.DeviceAtoms, BufferAtoms=options.buffer) GFm.P1T = N.zeros(len(hw), N.float) # M.A.M.A (total e-h damping) GFm.P2T = N.zeros(len(hw), N.float) # M.AL.M.AR (emission) GFm.ehDampL = N.zeros(len(hw), N.float) # M.AL.M.AL (L e-h damping) GFm.ehDampR = N.zeros(len(hw), N.float) # M.AR.M.AR (R e-h damping) GFm.nHT = N.zeros(len(hw), N.float) # non-Hilbert/Isym factor GFm.HT = N.zeros(len(hw), N.float) # Hilbert/Iasym factor GFm.dIel = N.zeros(len(hw), N.float) GFm.dIinel = N.zeros(len(hw), N.float) GFm.dSel = N.zeros(len(hw), N.float) GFm.dSinel = N.zeros(len(hw), N.float) # Calculate transmission at Fermi level GFp.calcGF(options.energy + options.eta * 1.0j, options.kpoint[0:2], ispin=options.iSpin, etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff) L = options.bufferL # Pad lasto with zeroes to enable basis generation... lasto = N.zeros((GFp.HS.nua + L + 1, ), N.int) lasto[L:] = GFp.HS.lasto basis = SIO.BuildBasis(options.fn, options.DeviceAtoms[0] + L, options.DeviceAtoms[1] + L, lasto) basis.ii -= L TeF = MM.trace(GFp.TT).real GFp.TeF = TeF GFm.TeF = TeF # Check consistency of PHrun vs TSrun inputs IntegrityCheck(options, GFp, basis, NCfile) # Calculate trace factors one mode at a time print 'Inelastica: LOEscale =', options.LOEscale if options.LOEscale == 0.0: # LOEscale=0.0 => Original LOE-WBA method, PRB 72, 201101(R) (2005) [cond-mat/0505473]. GFp.calcGF(options.energy + options.eta * 1.0j, options.kpoint[0:2], ispin=options.iSpin, etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff) GFm.calcGF(options.energy + options.eta * 1.0j, options.kpoint[0:2], ispin=options.iSpin, etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff) for ihw in (hw > options.modeCutoff).nonzero()[0]: calcTraces(options, GFp, GFm, basis, NCfile, ihw) calcTraces(options, GFm, GFp, basis, NCfile, ihw) writeFGRrates(options, GFp, hw, NCfile) else: # LOEscale=1.0 => Generalized LOE, PRB 89, 081405(R) (2014) [arXiv:1312.7625] for ihw in (hw > options.modeCutoff).nonzero()[0]: GFp.calcGF(options.energy + hw[ihw] * options.LOEscale * VfracL + options.eta * 1.0j, options.kpoint[0:2], ispin=options.iSpin, etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff) GFm.calcGF(options.energy + hw[ihw] * options.LOEscale * (VfracL - 1.) + options.eta * 1.0j, options.kpoint[0:2], ispin=options.iSpin, etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff) calcTraces(options, GFp, GFm, basis, NCfile, ihw) if VfracL != 0.5: GFp.calcGF(options.energy - hw[ihw] * options.LOEscale * (VfracL - 1.) + options.eta * 1.0j, options.kpoint[0:2], ispin=options.iSpin, etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff) GFm.calcGF(options.energy - hw[ihw] * options.LOEscale * VfracL + options.eta * 1.0j, options.kpoint[0:2], ispin=options.iSpin, etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff) calcTraces(options, GFm, GFp, basis, NCfile, ihw) # Multiply traces with voltage-dependent functions data = calcIETS(options, GFp, GFm, basis, hw) NCfile.close() NEGF.SavedSig.close() CF.PrintMainFooter('Inelastica') return data