Beispiel #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
Beispiel #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