Exemplo n.º 1
0
 def calcTEIG(self, channels=10):
     # Transmission matrix (complex array)
     TT = self.TT
     Trans = N.trace(TT)
     VC.Check("trans-imaginary-part", Trans.imag,
              "Transmission has large imaginary part")
     # Calculate eigenchannel transmissions too
     tval, tvec = LA.eig(TT)
     idx = (tval.real).argsort()[::-1]  # sort from largest to smallest
     tval = tval[idx]
     tvec = tvec[:, idx]
     # Compute shot noise
     Smat = MM.mm(TT, N.identity(len(TT)) - TT)
     sval = N.diag(MM.mm(MM.dagger(tvec), Smat, tvec))
     # set up arrays
     T = N.zeros(channels + 1)
     SN = N.zeros(channels + 1)
     T[0] = Trans.real
     SN[0] = N.trace(Smat).real
     for i in range(min(channels, len(TT))):
         T[i + 1] = tval[i].real
         SN[i + 1] = sval[i].real
     return T, SN
Exemplo n.º 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
Exemplo n.º 3
0
#!/usr/bin/env python

import sys, os
from Inelastica import ValueCheck as VC
from Inelastica import Inelastica as I
from Inelastica import EigenChannels as E
from Inelastica import pyTBT as T

# Change the tolerance level of the imaginary part
VC.EditCheck("zero-imaginary-part", 1e-7)

############################
#       Inelastica         #
############################
tmp_argv = ['-p', './PHrun/Dev_09-14.nc', '-f', './TSrun/RUN.fdf']
# Copy sys argv to override
if len(sys.argv) > 1:
    tmp_argv.extend([a for a in sys.argv[1:]])

# Get option parser
opts = I.GetOptions(tmp_argv)

I.main(opts)

############################
#      EigenChannels       #
############################
tmp_argv = ['-w', 'cube', '-f', './TSrun/RUN.fdf', '-e', '0.']
# Copy sys argv to override
if len(sys.argv) > 1:
    tmp_argv.extend([a for a in sys.argv[1:]])
Exemplo n.º 4
0
def main(options):
    """
    Main routine to compute elastic transmission probabilities etc.

    Parameters
    ----------
    options : an ``options`` instance
    """
    CF.CreatePipeOutput(options.DestDir + '/' + options.Logfile)
    VC.OptionsCheck(options, 'pyTBT')
    CF.PrintMainHeader('pyTBT', options)

    # K-points
    if options.Gk1 > 1:
        Nk1, t1 = options.Gk1, 'GK'
    else:
        Nk1, t1 = options.Nk1, 'LIN'
    if options.Gk2 > 1:
        Nk2, t2 = options.Gk2, 'GK'
    else:
        Nk2, t2 = options.Nk2, 'LIN'
    # Generate full k-mesh:
    mesh = Kmesh.kmesh(Nk1,
                       Nk2,
                       Nk3=1,
                       meshtype=[t1, t2, 'LIN'],
                       invsymmetry=not options.skipsymmetry)
    mesh.mesh2file(
        '%s/%s.%ix%i.mesh' %
        (options.DestDir, options.systemlabel, mesh.Nk[0], mesh.Nk[1]))
    # Setup self-energies and device GF
    elecL = NEGF.ElectrodeSelfEnergy(options.fnL, options.NA1L, options.NA2L,
                                     options.voltage / 2.)
    elecL.scaling = options.scaleSigL
    elecL.semiinf = options.semiinfL
    elecR = NEGF.ElectrodeSelfEnergy(options.fnR, options.NA1R, options.NA2R,
                                     -options.voltage / 2.)
    elecR.scaling = options.scaleSigR
    elecR.semiinf = options.semiinfR
    DevGF = NEGF.GF(options.TSHS,
                    elecL,
                    elecR,
                    Bulk=options.UseBulk,
                    DeviceAtoms=options.DeviceAtoms,
                    BufferAtoms=options.buffer)
    nspin = DevGF.HS.nspin

    # k-sample only self-energies?
    if options.singlejunction:
        elecL.mesh = mesh
        mesh = Kmesh.kmesh(3, 3, 1)

    if options.dos:
        DOSL = N.zeros((nspin, len(options.Elist), DevGF.nuo), N.float)
        DOSR = N.zeros((nspin, len(options.Elist), DevGF.nuo), N.float)

        # MPSH projections?
        MPSHL = N.zeros((nspin, len(options.Elist), DevGF.nuo), N.float)
        MPSHR = N.zeros((nspin, len(options.Elist), DevGF.nuo), N.float)
        # evaluate eigenstates at Gamma
        import scipy.linalg as SLA
        DevGF.setkpoint(N.zeros(2))
        ev0, es0 = SLA.eigh(DevGF.H, DevGF.S)
        print 'MPSH eigenvalues:', ev0
        #print 'MPSH eigenvector normalizations:',N.diag(MM.mm(MM.dagger(es0),DevGF.S,es0)).real # right

    # Loop over spin
    for iSpin in range(nspin):
        # initialize transmission and shot noise arrays
        Tkpt = N.zeros((len(options.Elist), mesh.NNk, options.numchan + 1),
                       N.float)
        SNkpt = N.zeros((len(options.Elist), mesh.NNk, options.numchan + 1),
                        N.float)
        # prepare output files
        outFile = options.DestDir + '/%s.%ix%i' % (options.systemlabel,
                                                   mesh.Nk[0], mesh.Nk[1])
        if nspin < 2: thisspinlabel = outFile
        else: thisspinlabel = outFile + ['.UP', '.DOWN'][iSpin]
        fo = open(thisspinlabel + '.AVTRANS', 'write')
        fo.write('# Nk1(%s)=%i Nk2(%s)=%i eta=%.2e etaLead=%.2e\n' %
                 (mesh.type[0], mesh.Nk[0], mesh.type[1], mesh.Nk[1],
                  options.eta, options.etaLead))
        fo.write('# E   Ttot(E)   Ti(E)(i=1-%i)   RelErrorTtot(E)\n' %
                 options.numchan)
        foSN = open(thisspinlabel + '.AVNOISE', 'write')
        foSN.write('# Nk1(%s)=%i Nk2(%s)=%i eta=%.2e etaLead=%.2e\n' %
                   (mesh.type[0], mesh.Nk[0], mesh.type[1], mesh.Nk[1],
                    options.eta, options.etaLead))
        foSN.write('# E   SNtot(E)   SNi(E)(i=1-%i)\n' % options.numchan)
        foFF = open(thisspinlabel + '.FANO', 'write')
        foFF.write('# Nk1(%s)=%i Nk2(%s)=%i eta=%.2e etaLead=%.2e\n' %
                   (mesh.type[0], mesh.Nk[0], mesh.type[1], mesh.Nk[1],
                    options.eta, options.etaLead))
        foFF.write('# E   Fano factor \n')
        # Loop over energy
        for ie, ee in enumerate(options.Elist):
            Tavg = N.zeros((options.numchan + 1, len(mesh.w)), N.float)
            SNavg = N.zeros((options.numchan + 1, len(mesh.w)), N.float)
            AavL = N.zeros((DevGF.nuo, DevGF.nuo), N.complex)
            AavR = N.zeros((DevGF.nuo, DevGF.nuo), N.complex)
            # Loops over k-points
            for ik in range(mesh.NNk):
                DevGF.calcGF(ee + options.eta * 1.0j,
                             mesh.k[ik, :2],
                             ispin=iSpin,
                             etaLead=options.etaLead,
                             useSigNCfiles=options.signc,
                             SpectralCutoff=options.SpectralCutoff)
                # Transmission and shot noise
                T, SN = DevGF.calcTEIG(options.numchan)
                for iw in range(len(mesh.w)):
                    Tavg[:, iw] += T * mesh.w[iw, ik]
                    SNavg[:, iw] += SN * mesh.w[iw, ik]
                Tkpt[ie, ik] = T
                SNkpt[ie, ik] = SN
                # DOS calculation:
                if options.dos:
                    if options.SpectralCutoff > 0.0:
                        AavL += mesh.w[0, ik] * MM.mm(DevGF.AL.L, DevGF.AL.R,
                                                      DevGF.S)
                        AavR += mesh.w[0, ik] * MM.mm(DevGF.AR.L, DevGF.AR.R,
                                                      DevGF.S)
                    else:
                        AavL += mesh.w[0, ik] * MM.mm(DevGF.AL, DevGF.S)
                        AavR += mesh.w[0, ik] * MM.mm(DevGF.AR, DevGF.S)
            # Print calculated quantities
            err = (N.abs(Tavg[0, 0] - Tavg[0, 1]) +
                   N.abs(Tavg[0, 0] - Tavg[0, 2])) / 2
            relerr = err / Tavg[0, 0]
            print 'ispin= %i, e= %.4f, Tavg= %.8f, RelErr= %.1e' % (
                iSpin, ee, Tavg[0, 0], relerr)
            transline = '\n%.10f ' % ee
            noiseline = '\n%.10f ' % ee
            for ichan in range(options.numchan + 1):
                if ichan == 0:
                    transline += '%.8e ' % Tavg[ichan, 0]
                    noiseline += '%.8e ' % SNavg[ichan, 0]
                else:
                    transline += '%.4e ' % Tavg[ichan, 0]
                    noiseline += '%.4e ' % SNavg[ichan, 0]
            transline += '%.2e ' % relerr
            fo.write(transline)
            foSN.write(noiseline)
            foFF.write('\n%.10f %.8e' % (ee, SNavg[0, 0] / Tavg[0, 0]))
            # Partial density of states:
            if options.dos:
                DOSL[iSpin, ie, :] += N.diag(AavL).real / (2 * N.pi)
                DOSR[iSpin, ie, :] += N.diag(AavR).real / (2 * N.pi)
                MPSHL[iSpin, ie, :] += N.diag(MM.mm(MM.dagger(es0), AavL,
                                                    es0)).real / (2 * N.pi)
                MPSHR[iSpin, ie, :] += N.diag(MM.mm(MM.dagger(es0), AavR,
                                                    es0)).real / (2 * N.pi)
                print 'ispin= %i, e= %.4f, DOSL= %.4f, DOSR= %.4f' % (
                    iSpin, ee, N.sum(DOSL[iSpin,
                                          ie, :]), N.sum(DOSR[iSpin, ie, :]))
        fo.write('\n')
        fo.close()
        foSN.write('\n')
        foSN.close()
        foFF.write('\n')
        foFF.close()

        # Write k-point-resolved transmission
        fo = open(thisspinlabel + '.TRANS', 'write')
        for ik in range(mesh.NNk):
            w = mesh.w[:, ik]
            fo.write('\n\n# k = %f, %f    w = %f %f %f %f' %
                     (mesh.k[ik, 0], mesh.k[ik, 1], w[0], w[1], w[2], w[3]))
            for ie, ee in enumerate(options.Elist):
                transline = '\n%.10f ' % ee
                for ichan in range(options.numchan + 1):
                    if ichan == 0:
                        transline += '%.8e ' % Tkpt[ie, ik, ichan]
                    else:
                        transline += '%.4e ' % Tkpt[ie, ik, ichan]
                fo.write(transline)
        fo.write('\n')
        fo.close()

        # Write k-point-resolved shot noise
        fo = open(thisspinlabel + '.NOISE', 'write')
        for ik in range(mesh.NNk):
            w = mesh.w[:, ik]
            fo.write('\n\n# k = %f, %f    w = %f %f %f %f' %
                     (mesh.k[ik, 0], mesh.k[ik, 1], w[0], w[1], w[2], w[3]))
            for ie, ee in enumerate(options.Elist):
                noiseline = '\n%.10f ' % ee
                for ichan in range(options.numchan + 1):
                    if ichan == 0:
                        noiseline += '%.8e ' % SNkpt[ie, ik, ichan]
                    else:
                        noiseline += '%.4e ' % SNkpt[ie, ik, ichan]
                fo.write(noiseline)
        fo.write('\n')
        fo.close()

    # End loop over spin
    NEGF.SavedSig.close()  # Make sure saved Sigma is written to file

    if options.dos:
        # Read basis
        L = options.bufferL
        # Pad lasto with zeroes to enable basis generation...
        lasto = N.zeros((DevGF.HS.nua + L + 1, ), N.int)
        lasto[L:] = DevGF.HS.lasto
        basis = SIO.BuildBasis(options.fn, 1 + L, DevGF.HS.nua + L, lasto)
        basis.ii -= L
        WritePDOS(outFile + '.PDOS.gz', options, DevGF, DOSL + DOSR, basis)
        WritePDOS(outFile + '.PDOSL.gz', options, DevGF, DOSL, basis)
        WritePDOS(outFile + '.PDOSR.gz', options, DevGF, DOSR, basis)

        WriteMPSH(outFile + '.MPSH.gz', options, DevGF, MPSHL + MPSHR, ev0)
        WriteMPSH(outFile + '.MPSHL.gz', options, DevGF, MPSHL, ev0)
        WriteMPSH(outFile + '.MPSHR.gz', options, DevGF, MPSHR, ev0)

    CF.PrintMainFooter('pyTBT')
Exemplo n.º 5
0
    def __init__(self,
                 TSHSfile,
                 elecL,
                 elecR,
                 Bulk=True,
                 DeviceAtoms=[0, 0],
                 BufferAtoms=N.empty((0, ))):
        """
        Calculate Green's functions etc for TSHSfile connected to left/right 
        electrode (class ElectrodeSelfEnergy). 
        To speed up calculations folding to smaller device region suggested
        For spin-polarized calcGF has to be called for each ispin
        Variables:
        Gr     : Retarded Green's function, folded
        H,S    : Hamiltonian, overlap, folded
        H0,S0  : Hamiltonian, overlap, not folded
        nuo    : Size of Gr
        SigL, SigR, GamL, GamR : Self energy, Gamma NOTE, not same size as Gr! 
        nuoL, nuoR : Size of Sig, Gam
        nuo0, nuoL0, nuoR0 : Non-folded sizes
        FoldedL, FoldedR : True/False
        DeviceAtoms : start/end Siesta numbering of atoms included in device
        DeviceOrbs : Start/end of orbitals. Siesta ordering.
        BufferAtoms: A list of buffer atoms
        """
        self.elecL, self.elecR, self.Bulk = elecL, elecR, Bulk
        self.HS = SIO.HS(TSHSfile, BufferAtoms=BufferAtoms)
        print 'GF: UseBulk=', Bulk
        self.DeviceAtoms = DeviceAtoms
        if DeviceAtoms[0] <= 1:
            self.DeviceAtoms[0] = 1
            self.FoldedL = False
        else:
            self.FoldedL = True
        if DeviceAtoms[1] == 0 or DeviceAtoms[1] >= self.HS.nua:
            self.DeviceAtoms[1] = self.HS.nua
            self.FoldedR = False
        else:
            self.FoldedR = True
        self.DeviceOrbs = [
            self.HS.lasto[DeviceAtoms[0] - 1] + 1,
            self.HS.lasto[DeviceAtoms[1]]
        ]

        self.nuo0, self.nuoL0, self.nuoR0 = self.HS.nuo, elecL.NA1 * elecL.NA2 * elecL.HS.nuo, elecR.NA1 * elecR.NA2 * elecR.HS.nuo
        self.nuo = self.DeviceOrbs[1] - self.DeviceOrbs[0] + 1
        self.nuoL, self.nuoR = self.nuoL0, self.nuoR0  # Not folded, for folded case changed below

        print "GF:", TSHSfile
        print "Device atoms %i-%i, orbitals %i-%i" % (tuple(self.DeviceAtoms +
                                                            self.DeviceOrbs))
        if not self.FoldedL:
            print "Suggest left folding to atom : ", self.elecL.HS.nua * self.elecL.NA1 * self.elecL.NA2 + 1
        if not self.FoldedR:
            print "Suggest right folding to atom : ", self.HS.nua - self.elecR.HS.nua * self.elecR.NA1 * self.elecR.NA2

        if self.FoldedL or self.FoldedR:
            # Check that device region is large enough!
            kpoint = N.zeros((2, ), N.float)
            self.setkpoint(kpoint, ispin=0)  # At least for one spin

            devSt, devEnd = self.DeviceOrbs[0], self.DeviceOrbs[1]
            VC.Check("Device-Elec-overlap",
                     N.abs(self.S0[0:devSt, devEnd:self.nuo0]),
                     "Too much overlap directly from left-top right",
                     "Make device region larger")
            VC.Check("Device-Elec-overlap",
                     N.abs(self.H0[0:devSt, devEnd:self.nuo0]),
                     "Too large Hamiltonian directly from left-top right.",
                     "Make device region larger")
        if self.FoldedL:
            # Find orbitals in device region coupling to left and right.
            tau = abs(self.S0[0:devSt - 1, 0:devEnd])
            coupling = N.sum(tau, axis=0)
            ii = devEnd - 1
            while coupling[ii] < 1e-10:
                ii = ii - 1
            self.devEndL = max(ii + 1, self.nuoL0)
            self.nuoL = self.devEndL - devSt + 1
            print "Left self energy on orbitals %i-%i" % (devSt, self.devEndL)
        if self.FoldedR:
            tau = abs(self.S0[devEnd - 1:self.nuo0, 0:self.nuo0])
            coupling = N.sum(tau, axis=0)
            ii = devSt - 1
            while coupling[ii] < 1e-10:
                ii = ii + 1
            self.devStR = min(ii + 1, self.nuo0 - self.nuoR0 + 1)
            self.nuoR = devEnd - self.devStR + 1
            print "Right self energy on orbitals %i-%i" % (self.devStR, devEnd)
        # Quantities expressed in nonorthogonal basis:
        self.OrthogonalDeviceRegion = False
Exemplo n.º 6
0
def AssertReal(x, label):
    VC.Check("zero-imaginary-part", abs(x.imag),
             "Imaginary part too large in quantity %s" % label)
    return x.real
Exemplo n.º 7
0
    def calcg0_old(self, ee, ispin=0, left=True):
        """
        Only used if SciPy is not installed!
        For the left surface Green's function  (1 is surface layer, 0 is all the other atoms):
        (E S00-H00  E S01-H01)   (g00 g01)    ( I 0 )
        (E S10-H10  E S11-H11) * (g01 g11)  = ( 0 I ) ->
        call E S - H for t ...

        t00 g01 + t01 g11 = 0  -> g01 = - t00^-1 t01 g11
        t10 g01 + t11 g11 = I -> - t10 t00^-1 t01 g11 + t11 g11 = I -> 

        And we get the surface Green's function:

        g11 = (t11 - t10 t00^-1 t01)^-1 with the right size of unitcell t00^-1 = g11!
        g11 = (E S11 - H11 - (E S10 - H10) g11 (E S01 - H01))^-1

        In the calculations H01^+ and S01^+ are used instead of S10 and H10.
        (For complex energies (E S01 -H01)^+ is not (E S10 -H10) because the conjugate of the energy!!!!)

        For the right surface greens function same but different order on the MM.daggers!
        i.e., (E S - H - (E S01 - H01) gs (E S01^+ -H01^+)

        Algorith: Lopez Sancho*2 J Phys F:Met Phys 15 (1985) 851

        I'm still very suspicios of this algorithm ... but it works and is really quick!
        The convergence is always checked against gs (E S - H - (E S01^+ - H01^+) gs (E S01 -H01) ) = I!
        """
        H, S, H01, S01 = self.H[ispin, :, :], self.S, self.H01[
            ispin, :, :], self.S01

        alpha, beta = MM.dagger(H01) - ee * MM.dagger(S01), H01 - ee * S01
        eps, epss = H.copy(), H.copy()

        converged = False
        iteration = 0
        while not converged:
            iteration += 1
            oldeps, oldepss = eps.copy(), epss.copy()
            oldalpha, oldbeta = alpha.copy(), beta.copy()
            tmpa = LA.solve(ee * S - oldeps, oldalpha)
            tmpb = LA.solve(ee * S - oldeps, oldbeta)
            alpha, beta = MM.mm(oldalpha, tmpa), MM.mm(oldbeta, tmpb)
            eps = oldeps + MM.mm(oldalpha, tmpb) + MM.mm(oldbeta, tmpa)
            if left:
                epss = oldepss + MM.mm(oldalpha, tmpb)
            else:
                epss = oldepss + MM.mm(oldbeta, tmpa)
            LopezConvTest = N.max(abs(alpha) + abs(beta))
            if LopezConvTest < 1.0e-40:
                gs = LA.inv(ee * S - epss)
                if left:
                    test = ee * S - H - MM.mm(
                        ee * MM.dagger(S01) - MM.dagger(H01), gs,
                        ee * S01 - H01)
                else:
                    test = ee * S - H - MM.mm(
                        ee * S01 - H01, gs,
                        ee * MM.dagger(S01) - MM.dagger(H01))
                myConvTest = N.max(
                    abs(
                        MM.mm(test, gs) -
                        N.identity((self.HS.nuo), N.complex)))
                if myConvTest < VC.GetCheck("Lopez-Sancho"):
                    converged = True
                    if myConvTest > VC.GetCheck("Lopez-Sancho-warning"):
                        v = "RIGHT"
                        if left: v = "LEFT"
                        print "WARNING: Lopez-scheme not-so-well converged for " + v + " electrode at E = %.4f eV:" % ee, myConvTest
                else:
                    VC.Check("Lopez-Sancho", myConvTest,
                             "Error: gs iteration {0}".format(iteration))
        return gs
Exemplo n.º 8
0
def main(options):
    """
    Main routine to compute eigenchannel scattering states

    Parameters
    ----------
    options : an ``options`` instance
    """

    CF.CreatePipeOutput(options.DestDir+'/'+options.Logfile)
    VC.OptionsCheck(options, 'EigenChannels')
    CF.PrintMainHeader('EigenChannels', options)

    # Read geometry
    XV = '%s/%s.XV'%(options.head, options.systemlabel)
    geom = MG.Geom(XV, BufferAtoms=options.buffer)

    # Set up device Greens function
    elecL = NEGF.ElectrodeSelfEnergy(options.fnL, options.NA1L, options.NA2L, options.voltage/2.)
    elecL.scaling = options.scaleSigL
    elecL.semiinf = options.semiinfL
    elecR = NEGF.ElectrodeSelfEnergy(options.fnR, options.NA1R, options.NA2R, -options.voltage/2.)
    elecR.scaling = options.scaleSigR
    elecR.semiinf = options.semiinfR
    DevGF = NEGF.GF(options.TSHS, elecL, elecR, Bulk=options.UseBulk,
                    DeviceAtoms=options.DeviceAtoms,
                    BufferAtoms=options.buffer)
    DevGF.calcGF(options.energy+options.eta*1.0j, options.kpoint[0:2], ispin=options.iSpin,
                 etaLead=options.etaLead, useSigNCfiles=options.signc, SpectralCutoff=options.SpectralCutoff)
    NEGF.SavedSig.close() # Make sure saved Sigma is written to file

    # Transmission
    print 'Transmission Ttot(%.4feV) = %.16f'%(options.energy, N.trace(DevGF.TT).real)

    # Build basis
    options.nspin = DevGF.HS.nspin
    L = options.bufferL
    # Pad lasto with zeroes to enable basis generation...
    lasto = N.zeros((DevGF.HS.nua+L+1,), N.int)
    lasto[L:] = DevGF.HS.lasto
    basis = SIO.BuildBasis(options.fn,
                           options.DeviceAtoms[0]+L,
                           options.DeviceAtoms[1]+L, lasto)
    basis.ii -= L

    # Calculate Eigenchannels
    DevGF.calcEigChan(options.numchan)

    # Compute bond currents?
    if options.kpoint[0]!=0.0 or options.kpoint[1]!=0.0:
        print 'Warning: The current implementation of bond currents is only valid for the Gamma point (should be easy to fix)'
        BC = False
    else:
        BC = True

    # Eigenchannels from left
    ECleft = DevGF.ECleft
    for jj in range(options.numchan):
        options.iSide, options.iChan = 0, jj+1
        writeWavefunction(options, geom, basis, ECleft[jj])
        if BC:
            Curr=calcCurrent(options, basis, DevGF.H, ECleft[jj])
            writeCurrent(options, geom, Curr)

    # Calculate eigenchannels from right
    if options.bothsides:
        ECright = DevGF.ECright
        for jj in range(options.numchan):
            options.iSide, options.iChan = 1, jj+1
            writeWavefunction(options, geom, basis, ECright[jj])
            if BC:
                Curr=calcCurrent(options, basis, DevGF.H, ECright[jj])
                writeCurrent(options, geom, Curr)

    # Calculate total "bond currents"
    if BC:
        Curr=-calcCurrent(options, basis, DevGF.H, DevGF.AL)
        options.iChan, options.iSide = 0, 0
        writeCurrent(options, geom, Curr)
        Curr=-calcCurrent(options, basis, DevGF.H, DevGF.AR)
        options.iSide = 1
        writeCurrent(options, geom, Curr)

    # Calculate eigenstates of device Hamiltonian (MPSH)
    if options.MolStates>0.0:
        try:
            import scipy.linalg as SLA
            ev, es = SLA.eigh(DevGF.H, DevGF.S)
            print 'EigenChannels: Eigenvalues (in eV) of computed molecular eigenstates:'
            print ev
            # Write eigenvalues to file
            fn = options.DestDir+'/'+options.systemlabel+'.EIGVAL'
            print 'EigenChannels: Writing', fn
            fnfile = open(fn, 'w')
            fnfile.write('# Device region = [%i,%i], units in eV\n'%(options.DeviceFirst, options.DeviceLast))
            for i, val in enumerate(ev):
                fnfile.write('%i %.8f\n'%(i, val))
            fnfile.close()
            # Compute selected eigenstates
            for ii, val in enumerate(ev):
                if N.abs(val)<options.MolStates:
                    fn=options.DestDir+'/'+options.systemlabel+'.S%.3i.E%.3f'%(ii, val)
                    writeWavefunction(options, geom, basis, es[:, ii], fn=fn)
        except:
            print 'You need to install scipy to solve the generalized eigenvalue problem'
            print 'for the molecular eigenstates in the nonorthogonal basis'

    CF.PrintMainFooter('EigenChannels')
Exemplo n.º 9
0
def GetOptions(argv, **kwargs):
    # if text string is specified, convert to list
    if isinstance(argv, basestring): argv = argv.split()

    import optparse as o

    d = """Script that calculates STM images using the Bardeen approximation outlined in PRB 93 115434 (2016) and PRB 96 085415 (2017). The script is divided into 3 parts:
1) Calculation of the scattering states at the Fermi-energy on the same real space grid as TranSiesta (real-space cutoff). These are saved in DestDir/SystemLabel.A[LR][0-99].nc files and are reused if found. NEEDS: TranSiesta calculation. 
2) Propagation of the scattering states from a surface (defined by a constant charge density) out into the vacuum region. After the x-y plane, where the average potential of the slice is maximum (the separation plane), is found, the potential is ascribed a constant value at this average. Saves the propagated wavefunctions at the separation plane in DestDir/[kpoint]/FD[kpoint].nc. NEEDS: TotalPotential.grid.nc and Rho.grid.nc. 
3) Conductance calculation where the tip/substrate wavefunctions are displaced to simulate the conductance at different tip-positions. The k averaged STM image and the STM images of individual k points are saved in DestDir/STMimage.nc.
"""

    p = o.OptionParser("usage: %prog [options] DestinationDirectory", description=d)
    p.add_option("-F", "--DeviceFirst", dest='DeviceFirst', default=0, type='int',
                 help="First device atom (SIESTA numbering) [TS.TBT.PDOSFrom]")
    p.add_option("-L", "--DeviceLast", dest='DeviceLast', default=0, type='int',
                 help="Last device atom (SIESTA numbering) [TS.TBT.PDOSTo]")
    p.add_option("-e", "--Energy", dest='energy', default=0.0, type='float',
                 help="Energy where scattering states are evaluated [%default eV]")
    p.add_option("--eta", dest="eta", help="Imaginary part added to all energies (device and leads) [%default eV]",
                 type='float', default=0.000001)
    p.add_option("-l", "--etaLead", dest="etaLead", help="Additional imaginary part added ONLY in the leads (surface GF) [%default eV]",
                 type='float', default=0.0)
    p.add_option("-f", "--fdf", dest='fn', default='./RUN.fdf', type='string',
                 help="Input fdf-file for TranSIESTA calculations [%default]")
    p.add_option("-s", "--iSpin", dest='iSpin', default=0, type='int',
                 help="Spin channel [%default]")
    p.add_option("-p", "--savePOS", dest='savePOS', default=False, action='store_true',
                 help="Save the individual solutions as .pos files")
    p.add_option("--shift", dest='shift', default=False, action='store_true',
                 help="Shift current 1/2 cell in x, y directions")

    # Electrode stuff
    p.add_option("--bulk", dest='UseBulk', default=-1, action='store_true',
                 help="Use bulk in electrodes. The Hamiltonian from the electrode calculation is inserted into the electrode region in the TranSIESTA cell [TS.UseBulkInElectrodes]")
    p.add_option("--nobulk", dest='UseBulk', default=-1, action='store_false',
                 help="Use only self-energies in the electrodes. The full Hamiltonian of the TranSIESTA cell is used in combination with self-energies for the electrodes [TS.UseBulkInElectrodes]")

    # Scale (artificially) the coupling to the electrodes
    p.add_option("--scaleSigL", dest="scaleSigL", help="Scale factor applied to Sigma_L [default=%default]",
                 type='float', default=1.0)
    p.add_option("--scaleSigR", dest="scaleSigR", help="Scale factor applied to Sigma_R [default=%default]",
                 type='float', default=1.0)

    p.add_option("-u", "--useSigNC", dest='signc', default=False, action='store_true',
                 help="Use SigNCfiles [%default]")

    # Use spectral matrices?
    p.add_option("--SpectralCutoff", dest="SpectralCutoff", help="Cutoff value for SpectralMatrix functions (for ordinary matrix representation set cutoff<=0.0) [default=%default]",
                 type='float', default=0.0)
    p.add_option("-n", "--nCPU", dest='nCPU', default=1, type='int',
                 help="Number of processors [%default]")

    # k-grid
    p.add_option("-x", "--Nk1", dest='Nk1', default=1, type='int',
                  help="k-points Nk1 along a1 [%default]")
    p.add_option("-y", "--Nk2", dest='Nk2', default=1, type='int',
                  help="k-points Nk2 along a2 [%default]")

    #FD calculation
    p.add_option("-r", "--rhoiso", dest='rhoiso', default=1e-3, type='float',
                 help="Density at the isosurface from which the localized-basis wave functions are propagated [default=%default Bohr^-3Ry^-1]")
    p.add_option("--ssp", dest='ShiftSeparationPlane', default=0, type='float',
                 help="Manually shift the separation plane (>0 means away from the substrate) [default=%default Ang]")
    p.add_option("--sc", dest='samplingscale', default=2, type='int',
                 help="Sampling scale of wave functions in lateral plane. 1 means same real-space resolution as used in TranSiesta, while 2 means doubling of the lateral lattice constants, etc. [default=%default]")
    p.add_option("-t", "--tolsolve", dest='tolsolve', default=1e-6, type='float',
                 help="Tolerance for the iterative linear solver (scipy.linalg.isolve.gmres) [default=%default]")
    p.add_option("--savelocwfs", dest='savelocwfs', default=False, action='store_true',
                 help="Save localized-basis wave functions.")

    (options, args) = p.parse_args(argv)

    # Get the last positional argument
    options.DestDir = VC.GetPositional(args, "You need to specify a destination directory!")
    # With this one can overwrite the logging information
    if "log" in kwargs:
        options.Logfile = kwargs["log"]
    else:
        options.Logfile = 'STM.log'
    options.kpoints = Kmesh.kmesh(options.Nk1, options.Nk2, 1, meshtype=['LIN', 'LIN', 'LIN'], invsymmetry=False)
    return options
Exemplo n.º 10
0
def main(options):
    dd = len(glob.glob(options.DestDir))
    if len(glob.glob('TotalPotential.grid.nc'))==0 and len(glob.glob('Rho.grid.nc'))==0:
        sys.exit('TotalPotential.nc and Rho.grid.nc not found! Add "SaveTotalPotential true" and "SaveRho true" to RUN.fdf')
    if len(glob.glob('TotalPotential.grid.nc'))==0:
        sys.exit('TotalPotential.nc not found! Add "SaveTotalPotential true" to RUN.fdf')
    if len(glob.glob('Rho.grid.nc'))==0:
        sys.exit('Rho.grid.nc not found! Add "SaveRho true" to RUN.fdf')

    CF.CreatePipeOutput(options.DestDir+'/'+options.Logfile)
    VC.OptionsCheck(options, 'STM')
    CF.PrintMainHeader('STM', options)

    ## Step 1: Calculate scattering states from L/R on TranSiesta real space grid.
    if glob.glob(options.DestDir+'/kpoints')!=[]: # Check previous k-points
        f, oldk = open(options.DestDir+'/kpoints', 'r'), []
        f.readline()
        for ii in f.readlines():
            oldk += [N.array(string.split(ii), N.float)]
        oldk = N.array(oldk)

    options.kpoints.mesh2file(options.DestDir+'/kpoints')
    doK = []
    for ikpoint in range(len(options.kpoints.k)):
        ikdir = options.DestDir+'/%i'%(ikpoint)
        if not os.path.isdir(ikdir):
            os.mkdir(ikdir)
        doK += [ikpoint]

    #Check if some k points are already done
    doK = []
    nokpts = len(options.kpoints.k)
    noFDcalcs = len(glob.glob('./'+options.DestDir+'/*/FDcurr*.nc'))
    for ii in range(nokpts):
        if len(glob.glob('./'+options.DestDir+'/'+str(ii)+'/FDcurr'+str(ii)+'.nc'))==1:
            pass
        else:
            doK += [ii]

    if noFDcalcs>nokpts-1:
        print('\nAll ('+str(nokpts)+') k points are already finished!\n')
    elif noFDcalcs>0 and noFDcalcs<nokpts:
        print(str(nokpts-len(doK))+' ('+str(N.round(100.*((1.*nokpts-len(doK))/nokpts), 1))+'%) k points already done. Will proceed with the rest.')
        print('You should perhaps remove the loc-basis states in the most recent k folder ')
        print('since some of these may not have been calculated before interuption.\n')
    else:
        if dd==1:
            print('No STM calculations found in existing directory '+str(options.DestDir)+'/. Starting from scratch.')
            print('(...by possibly using saved localized-basis states)\n')
        else:
            print('STM calculation starts.')
    args=[(options, ik) for ik in doK]
    tmp = CF.runParallel(calcTSWFPar, args, nCPU=options.nCPU)

    print('Calculating k-point averaged STM image')
    def ShiftOrigin(mat, x, y):
        Nx = N.shape(mat)[0]; Ny = N.shape(mat)[1]
        NewMat = N.zeros((Nx, Ny))
        for ii in range(Nx):
            for jj in range(Ny):
                NewMat[N.mod(ii+x, Nx), N.mod(jj+y, Ny)] = mat[ii, jj]
        return NewMat

    file = NC.Dataset('TotalPotential.grid.nc', 'r')
    steps = N.array(file.variables['cell'][:], N.float)
    theta = N.arccos(N.dot(steps[0], steps[1])/(LA.norm(steps[0])*LA.norm(steps[1])))

    currtmp = NC.Dataset('./'+options.DestDir+'/0/FDcurr0.nc', 'r')
    dimSTMimage = N.shape(currtmp.variables['Curr'][:, :])
    dim1 = dimSTMimage[0]
    dim2 = dimSTMimage[1]
    STMimage = N.zeros((dim1, dim2))
    tmpSTM = N.zeros((dim1*nokpts, dim2))
    for ii in range(nokpts):
        file = NC.Dataset('./'+options.DestDir+'/'+str(ii)+'/FDcurr'+str(ii)+'.nc', 'r')
        ikSTMimage = file.variables['Curr'][:, :]/(nokpts)
        STMimage += ShiftOrigin(ikSTMimage, dim1/2, dim2/2)
        tmpSTM[ii*dim1:(ii+1)*dim1, :] = ShiftOrigin(ikSTMimage, dim1/2, dim2/2)
    STMimagekpt = N.zeros((dim1*options.Nk1, dim2*options.Nk2))
    for ii in range(options.Nk1):
        for jj in range(options.Nk2):
            N1 = ii+1; N2 = options.Nk2-jj; kk = (ii+1)*options.Nk2-jj-1
            STMimagekpt[(N1-1)*dim1:N1*dim1, (N2-1)*dim2:N2*dim2]=tmpSTM[kk*dim1:(kk+1)*dim1, ::-1]

    tmp = open(options.systemlabel+'.XV').readlines()[4+options.DeviceFirst-1:4+options.DeviceLast]
    xyz = N.zeros((len(tmp), 4))
    for ii in range(len(tmp)):
        for jj in range(1, 5):
            tmp2 = tmp[ii].split()
            xyz[ii, jj-1] = ast.literal_eval(tmp2[jj])
            if jj>1:
                xyz[ii, jj-1] = xyz[ii, jj-1]*PC.Bohr2Ang

    drAtoms = N.zeros(len(xyz))
    for atomIdx in range(len(xyz)-1):
        drAtoms[atomIdx] = N.round((xyz[atomIdx+1][3]-xyz[atomIdx][3]), 3)
    TipHeight = N.max(drAtoms)

    n=writeNC.NCfile('./'+options.DestDir+'/STMimage.nc')
    n.write(STMimage, 'STMimage')
    n.write(theta, 'theta')
    n.write(options.kpoints.k, 'kpoints')
    n.write(options.Nk1, 'Nk1')
    n.write(options.Nk2, 'Nk2')
    n.write(STMimagekpt, 'STMkpoint')
    n.write(xyz, 'Geometry')
    n.write(TipHeight, 'TipHeight')
    n.close()

    CF.PrintMainFooter('STM')