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
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
#!/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:]])
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')
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
def AssertReal(x, label): VC.Check("zero-imaginary-part", abs(x.imag), "Imaginary part too large in quantity %s" % label) return x.real
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
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')
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
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')