Ejemplo n.º 1
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
Ejemplo n.º 2
0
from Inelastica import MiscMath as mm
import numpy as N
from Inelastica import WriteXMGR as XMGR
import scipy.special as ss

# We try a simple Gaussian function:
x0 = 50
x = N.linspace(-x0, x0, 1e5)
gauss = -1j * N.pi**-0.5 * N.exp(-x**2)

# Output from our Hilbert function:
Hg, ker = mm.Hilbert(gauss)

# We can compare with this:
# Hilbert transform of a Gaussian function is related to Faddeva/w(z) functions:
# https://en.wikipedia.org/wiki/Dawson_function
ex = -1j * N.pi**0.5 * ss.wofz(x)

# Collect data in a plot
data1 = XMGR.XYset(x, gauss.imag, legend='Gauss', Lwidth=2)
data2 = XMGR.XYset(x, ex.real, legend='Faddeva', Lwidth=2)
data3 = XMGR.XYset(x, N.pi * Hg.imag, legend='Inelastica', Lwidth=1)
graph1 = XMGR.Graph(data1, data2, data3)
graph1.SetXaxis(autoscale=True)
graph1.SetYaxis(autoscale=True)
graph1.ShowLegend()

# Compute error/difference between the two methods:
err = ex.real - N.pi * Hg.imag
data4 = XMGR.XYset(x, err, legend='Difference', Lwidth=2)
graph2 = XMGR.Graph(data4)