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