def __calcEigChan(self, A1, G2, Left, channels=10): # Calculate Eigenchannels using recipe from PRB # For right eigenchannels, A1=A2, G2=G1 !!! if isinstance(A1, MM.SpectralMatrix): ev, U = LA.eigh(MM.mm(A1.L, A1.R)) else: ev, U = LA.eigh(A1) # This small trick will remove all zero contribution vectors # and will diagonalize the tt matrix in the subspace where there # are values. idx = (ev > 0).nonzero()[0] ev = N.sqrt(ev[idx] / (2 * N.pi)) ev.shape = (1, -1) Utilde = ev * U[:, idx] nuo, nuoL, nuoR = self.nuo, self.nuoL, self.nuoR if Left: tt = MM.mm(MM.dagger(Utilde[nuo-nuoR:nuo, :]), 2*N.pi*G2, Utilde[nuo-nuoR:nuo, :]) else: tt = MM.mm(MM.dagger(Utilde[:nuoL, :]), 2*N.pi*G2, Utilde[:nuoL, :]) # Diagonalize (note that this is on a reduced tt matrix (no 0 contributing columns) evF, UF = LA.eigh(tt) EC = MM.mm(Utilde, UF[:, -channels:]).T return EC[::-1, :], evF[::-1] # reverse eigenvalues
def GetGradient(self, Atom, Axis): print('\nPhonons.GetGradient: Computing dH[%i,%i]' % (Atom, Axis)) # Read TSHS files TSHSm = SIO.HS(self.TSHS[Atom, Axis, -1]) TSHSm.setkpoint(self.kpoint, atype=self.atype) TSHSp = SIO.HS(self.TSHS[Atom, Axis, 1]) TSHSp.setkpoint(self.kpoint, atype=self.atype) # Use Fermi energy of equilibrium calculation as energy reference? if self.AbsEref: print('Computing gradient with absolute energy reference') for iSpin in range(self.nspin): TSHSm.H[iSpin, :, :] += (TSHSm.ef - self.TSHS0.ef) * TSHSm.S TSHSp.H[iSpin, :, :] += (TSHSp.ef - self.TSHS0.ef) * TSHSp.S # Compute direct gradient dH = (TSHSp.H - TSHSm.H) / (2 * self.Displ[Atom]) del TSHSm, TSHSp # Orbital range for the displaced atom: f, l = self.OrbIndx[Atom - 1] self.dSdij[:, f:l + 1] = self.dS[Axis, :, f:l + 1] # Apply Pulay-type corrections for iSpin in range(self.nspin): dH[iSpin, :, :] -= MM.mm(MM.dagger(self.dSdij), self.invS0H0[0, iSpin, :, :]) \ + MM.mm(self.invS0H0[1, iSpin, :, :], self.dSdij) self.dSdij[:, f:l + 1] = 0. # reset return dH
def orthogonalize(self): print('NEGF.GF.orthogonalize: Orthogonalizing device region quantities') self.OrthogonalDeviceRegion = True self.HNO = self.H.copy() # nonorthogonal device Hamiltonian (needed) # Device part Usi = MM.mysqrt(self.S) # Folded S Us = LA.inv(Usi) # Store transformation matrices self.Usi, self.Us = Usi, Us # Transform S and H self.S, self.H = MM.mm(Us, self.S, Us), MM.mm(Us, self.H, Us) # Sigmas/Gammas in pyTBT GF can be smaller than device region # First give them the shape of the device region nnL, nnR = len(self.SigL), len(self.SigR) S1, S2 = N.zeros(self.H.shape, N.complex), N.zeros(self.H.shape, N.complex) S1[0:nnL, 0:nnL], S2[-nnR:, -nnR:] = self.SigL, self.SigR # Resetting Sigmas to orthogonalized quantities self.SigL, self.SigR = MM.mm(Us, S1, Us), MM.mm(Us, S2, Us) # ... now the same for the Gammas G1, G2 = N.zeros(self.H.shape, N.complex), N.zeros(self.H.shape, N.complex) G1[0:nnL, 0:nnL], G2[-nnR:, -nnR:] = self.GamL, self.GamR # Resetting Gammas to orthogonalized quantities self.GamL, self.GamR = MM.mm(Us, G1, Us), MM.mm(Us, G2, Us) # Orthogonalize Greens functions self.Gr = MM.mm(Usi, self.Gr, Usi) self.Ga = MM.dagger(self.Gr)
def calcg0(self, ee, ispin=0, left=True): # Calculate surface Green's function # Euro Phys J B 62, 381 (2008) # Inverse of : NOTE, setup for "right" lead. # e-h00 -h01 ... # -h10 e-h00 ... h00, s00, h01, s01 = self.H[ispin, :, :], self.S, self.H01[ ispin, :, :], self.S01 NN, ee = len(h00), N.real(ee) + N.max([N.imag(ee), 1e-8]) * 1.0j if left: h01, s01 = MM.dagger(h01), MM.dagger(s01) # Solve generalized eigen-problem # ( e I - h00 , -I) (eps) (h01 , 0) (eps) # ( h10 , 0) (xi ) = lambda (0 , I) (xi ) a, b = N.zeros((2 * NN, 2 * NN), N.complex), N.zeros((2 * NN, 2 * NN), N.complex) a[0:NN, 0:NN] = ee * s00 - h00 a[0:NN, NN:2 * NN] = -N.eye(NN) a[NN:2 * NN, 0:NN] = MM.dagger(h01) - ee * MM.dagger(s01) b[0:NN, 0:NN] = h01 - ee * s01 b[NN:2 * NN, NN:2 * NN] = N.eye(NN) ev, evec = SLA.eig(a, b) # Select lambda <0 and the eps part of the evec ipiv = N.where(N.abs(ev) < 1.0)[0] ev, evec = ev[ipiv], N.transpose(evec[:NN, ipiv]) # Normalize evec norm = N.sqrt(N.diag(MM.mm(evec, MM.dagger(evec)))) evec = MM.mm(N.diag(1.0 / norm), evec) # E^+ Lambda_+ (E^+)^-1 --->>> g00 EP = N.transpose(evec) FP = MM.mm(EP, N.diag(ev), LA.inv(MM.mm(MM.dagger(EP), EP)), MM.dagger(EP)) g00 = LA.inv(ee * s00 - h00 - MM.mm(h01 - ee * s01, FP)) # Check! err = N.max(N.abs(g00-LA.inv(ee*s00-h00-\ MM.mm(h01-ee*s01, g00, MM.dagger(h01)-ee*MM.dagger(s01))))) if err > 1.0e-8 and left: print "WARNING: Lopez-scheme not-so-well converged for LEFT electrode at E = %.4f eV:" % ee, err if err > 1.0e-8 and not left: print "WARNING: Lopez-scheme not-so-well converged for RIGHT electrode at E = %.4f eV:" % ee, err return g00
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 calcTraces(options, GF1, GF2, basis, NCfile, ihw): # Calculate various traces over the electronic structure # Electron-phonon couplings ihw = int(ihw) M = N.array(NCfile.variables['He_ph'][ihw, options.iSpin, :, :], N.complex) try: M += 1.j*N.array(NCfile.variables['ImHe_ph'][ihw, options.iSpin, :, :], N.complex) except: print 'Warning: Variable ImHe_ph not found' # Calculation of intermediate quantity MARGLGM = MM.mm(M, GF1.ARGLG, M) MARGLGM2 = MM.mm(M, GF2.ARGLG, M) # LOE expressions in compact form t1 = MM.mm(MARGLGM, GF2.AR) t2 = MM.mm(MARGLGM2, GF1.AL) # Note that compared with Eq. (10) of PRB89, 081405 (2014) we here use # the definition B_lambda = MM.trace(t1-dagger(t2)), which in turn gives # ReB = MM.trace(t1).real-MM.trace(t2).real # ImB = MM.trace(t1).imag+MM.trace(t2).imag K23 = MM.trace(t1).imag+MM.trace(t2).imag K4 = MM.trace(MM.mm(M, GF1.ALT, M, GF2.AR)) aK23 = 2*(MM.trace(t1).real-MM.trace(t2).real) # asymmetric part # Non-Hilbert term defined here with a minus sign GF1.nHT[ihw] = NEGF.AssertReal(K23+K4, 'nHT[%i]'%ihw) GF1.HT[ihw] = NEGF.AssertReal(aK23, 'HT[%i]'%ihw) # Power, damping and current rates GF1.P1T[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.A, M, GF2.A)), 'P1T[%i]'%ihw) GF1.P2T[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.AL, M, GF2.AR)), 'P2T[%i]'%ihw) GF1.ehDampL[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.AL, M, GF2.AL)), 'ehDampL[%i]'%ihw) GF1.ehDampR[ihw] = NEGF.AssertReal(MM.trace(MM.mm(M, GF1.AR, M, GF2.AR)), 'ehDampR[%i]'%ihw) # Remains from older version (see before rev. 219): #GF.dGnout.append(EC.calcCurrent(options,basis,GF.HNO,mm(Us,-0.5j*(tmp1-dagger(tmp1)),Us))) #GF.dGnin.append(EC.calcCurrent(options,basis,GF.HNO,mm(Us,mm(G,MA1M,Gd)-0.5j*(tmp2-dagger(tmp2)),Us))) # NB: TF Should one use GF.HNO (nonorthogonal) or GF.H (orthogonalized) above? if options.LOEscale==0.0: # Check against original LOE-WBA formulation isym1 = MM.mm(GF1.ALT, M, GF2.AR, M) isym2 = MM.mm(MM.dagger(GF1.ARGLG), M, GF2.A, M) isym3 = MM.mm(GF1.ARGLG, M, GF2.A, M) isym = MM.trace(isym1)+1j/2.*(MM.trace(isym2)-MM.trace(isym3)) print 'LOE-WBA check: Isym diff', K23+K4-isym iasym1 = MM.mm(MM.dagger(GF1.ARGLG), M, GF2.AR-GF2.AL, M) iasym2 = MM.mm(GF1.ARGLG, M, GF2.AR-GF2.AL, M) iasym = MM.trace(iasym1)+MM.trace(iasym2) print 'LOE-WBA check: Iasym diff', aK23-iasym # Compute inelastic shot noise terms according to the papers # Haupt, Novotny & Belzig, PRB 82, 165441 (2010) and # Avriller & Frederiksen, PRB 86, 155411 (2012) # Zero-temperature limit TT = MM.mm(GF1.GammaL, GF1.AR) # this matrix has the correct shape for MM ReGr = (GF1.Gr+GF1.Ga)/2. tmp = MM.mm(GF1.Gr, M, ReGr, M, GF1.AR) tmp = tmp+MM.dagger(tmp) Tlambda0 = MM.mm(GF1.GammaL, tmp) tmp1 = MM.mm(M, GF1.AR, M) tmp2 = MM.mm(M, GF1.A, M, GF1.Gr, GF1.GammaR) tmp = tmp1+1j/2.*(MM.dagger(tmp2)-tmp2) Tlambda1 = MM.mm(GF1.GammaL, GF1.Gr, tmp, GF1.Ga) MARGL = MM.mm(M, GF1.AR, GF1.GammaL) tmp1 = MM.mm(MARGL, GF1.AR, M) tmp2 = MM.mm(MARGL, GF1.Gr, M, GF1.Gr, GF1.GammaR) tmp = tmp1+tmp2 tmp = tmp + MM.dagger(tmp) Qlambda = MM.mm(-GF1.Ga, GF1.GammaL, GF1.Gr, tmp) tmp = -2*TT OneMinusTwoT = tmp+N.identity(len(GF1.GammaL)) # Store relevant traces GF1.dIel[ihw] = NEGF.AssertReal(MM.trace(Tlambda0), 'dIel[%i]'%ihw) GF1.dIinel[ihw] = NEGF.AssertReal(MM.trace(Tlambda1), 'dIinel[%i]'%ihw) GF1.dSel[ihw] = NEGF.AssertReal(MM.trace(MM.mm(OneMinusTwoT, Tlambda0)), 'dSel[%i]'%ihw) GF1.dSinel[ihw] = NEGF.AssertReal(MM.trace(Qlambda+MM.mm(OneMinusTwoT, Tlambda1)), 'dSinel[%i]'%ihw)
def ComputePhononModes(self, FC, verbose=True): dyn = len(self.DynamicAtoms) FCtilde = N.zeros((dyn, 3, dyn, 3), N.complex) # Symmetrize and mass-scale for i, v in enumerate(self.DynamicAtoms): for j, w in enumerate(self.DynamicAtoms): FCtilde[i, :, j, :] = 0.5*(FC[i, :, w-1, :]+MM.dagger(FC[j, :, v-1, :]))\ /(self.Masses[i]*self.Masses[j])**0.5 # Solve eigenvalue problem with symmetric FCtilde FCtilde = FCtilde.reshape((3 * dyn, 3 * dyn), order='C') self.FCtilde = FCtilde evalue, evec = LA.eigh(FCtilde) #evalue,evec = LA.eig(FCtilde) evec = N.transpose(evec) evalue = N.array(evalue, N.complex) # Calculate frequencies const = PC.hbar2SI * (1e20 / (PC.eV2Joule * PC.amu2kg))**0.5 hw = const * evalue**0.5 # Units in eV for i in range(3 * dyn): # Real eigenvalues are defined as positive, imaginary eigenvalues as negative hw[i] = hw[i].real - abs(hw[i].imag) hw = hw.real # Normalize eigenvectors U = evec.copy() for i in range(3 * dyn): U[i] = U[i] / (N.dot(N.conjugate(U[i]), U[i])**0.5) # Sort in order descending mode energies #hw = hw[::-1] # reverse array #U = U[::-1] # reverse array indx = N.argsort(hw)[::-1] # reverse hw = hw[indx] U = U[indx] # Print mode frequencies if verbose: print('Phonons.CalcPhonons: Frequencies in meV:') for i in range(3 * dyn): print(('%.3f' % (1000 * hw[i])).rjust(9), end='') if (i - 5) % 6 == 0: print() if (i - 5) % 6 != 0: print() #print 'Phonons.CalcPhonons: Frequencies in cm^-1:' #for i in range(3*dyn): # print ('%.3f'%(hw[i]/PC.invcm2eV)).rjust(9), # if (i-5)%6 == 0: print #if (i-5)%6 != 0: print # Compute real displacement vectors Udisp = U.copy() for i in range(3 * dyn): # Eigenvectors after division by sqrt(mass) Udisp[:, i] = U[:, i] / self.Masses[i // 3]**.5 # Compute displacement vectors scaled for the characteristic length Ucl = N.zeros_like(U) Ucl[hw > 0, :] = PC.hbar2SI / N.sqrt( N.array(self.Masses).repeat(3).reshape(1, -1) * PC.amu2kg * hw[hw > 0].reshape(-1, 1) * PC.eV2Joule) * 1e10 * U[hw > 0, :] # Note that if we displace by the characteristic length # via E=1/2 <u|FC|u>, the energy should change by the # characteristic energy (which is hw/2), i.e., # np.diag(Ucl.dot(fcmat).dot(Ucl.T)).real[hw > 0] # should be identical to hw # Expand vectors to full geometry UU = N.zeros((len(hw), self.geom.natoms, 3), N.complex) UUdisp = N.zeros((len(hw), self.geom.natoms, 3), N.complex) UUcl = N.zeros((len(hw), self.geom.natoms, 3), N.complex) for i in range(len(hw)): for j, v in enumerate(self.DynamicAtoms): UU[i, v - 1, :] = [U[i, 3 * j], U[i, 3 * j + 1], U[i, 3 * j + 2]] UUdisp[i, v - 1, :] = [ Udisp[i, 3 * j], Udisp[i, 3 * j + 1], Udisp[i, 3 * j + 2] ] UUcl[i, v - 1, :] = [ Ucl[i, 3 * j], Ucl[i, 3 * j + 1], Ucl[i, 3 * j + 2] ] self.hw = hw self.U = U self.Udisp = Udisp self.Ucl = Ucl self.UU = UU self.UUdisp = UUdisp self.UUcl = UUcl
def calcGF(self, ee, kpoint, ispin=0, etaLead=0.0, useSigNCfiles=False, SpectralCutoff=0.0): "Calculate GF etc at energy ee and 2d k-point" nuo, nuoL, nuoR = self.nuo, self.nuoL, self.nuoR nuo0, nuoL0, nuoR0 = self.nuo0, self.nuoL0, self.nuoR0 FoldedL, FoldedR = self.FoldedL, self.FoldedR devSt, devEnd = self.DeviceOrbs[0], self.DeviceOrbs[1] # Determine whether electrode self-energies should be k-sampled or not try: mesh = self.elecL.mesh # a mesh was attached except: mesh = False # Calculate electrode self-energies if mesh: try: self.SigAvg # Averaged self-energies exist except: self.SigAvg = [False, -1] if self.SigAvg[0] == ee and self.SigAvg[1] == ispin: # We have already the averaged self-energies print('NEGF: Reusing sampled electrode self-energies', mesh.Nk, mesh.type, 'for ispin= %i e= %f'%(ispin, ee)) else: # k-sampling performed over folded electrode self-energies print('NEGF: Sampling electrode self-energies', mesh.Nk, mesh.type, 'for ispin= %i e= %f'%(ispin, ee)) self.calcSigLR(ee, mesh.k[0, :2], ispin, etaLead, useSigNCfiles, SpectralCutoff) AvgSigL = mesh.w[0, 0]*self.SigL AvgSigR = mesh.w[0, 0]*self.SigR for i in range(1, len(mesh.k)): self.calcSigLR(ee, mesh.k[i, :2], ispin, etaLead, useSigNCfiles, SpectralCutoff) AvgSigL += mesh.w[0, i]*self.SigL AvgSigR += mesh.w[0, i]*self.SigR # We now simply continue with the averaged self-energies self.SigL = AvgSigL self.SigR = AvgSigR self.SigAvg = [ee, ispin] else: # We sample k-points the usual way self.calcSigLR(ee, kpoint, ispin, etaLead, useSigNCfiles) # Ready to calculate Gr self.setkpoint(kpoint, ispin) eSmH = ee*self.S-self.H if FoldedL: eSmH[0:nuoL, 0:nuoL] = eSmH[0:nuoL, 0:nuoL]-self.SigL else: if self.Bulk: eSmH[0:nuoL, 0:nuoL] = self.SigL # SGF^1 else: eSmH[0:nuoL, 0:nuoL] = eSmH[0:nuoL, 0:nuoL]-self.SigL if FoldedR: eSmH[nuo-nuoR:nuo, nuo-nuoR:nuo] = eSmH[nuo-nuoR:nuo, nuo-nuoR:nuo]-self.SigR else: if self.Bulk: eSmH[nuo-nuoR:nuo, nuo-nuoR:nuo] = self.SigR # SGF^1 else: eSmH[nuo-nuoR:nuo, nuo-nuoR:nuo] = eSmH[nuo-nuoR:nuo, nuo-nuoR:nuo]-self.SigR self.Gr = LA.inv(eSmH) self.Ga = MM.dagger(self.Gr) # Calculate spectral functions if SpectralCutoff > 0.0: self.AL = MM.SpectralMatrix(MM.mm(self.Gr[:, 0:nuoL], self.GamL, self.Ga[0:nuoL, :]), cutoff=SpectralCutoff) tmp = MM.mm(self.GamL, self.Gr[0:nuoL, :]) self.ALT = MM.SpectralMatrix(MM.mm(self.Ga[:, 0:nuoL], tmp), cutoff=SpectralCutoff) self.AR = MM.SpectralMatrix(MM.mm(self.Gr[:, nuo-nuoR:nuo], self.GamR, self.Ga[nuo-nuoR:nuo, :]), cutoff=SpectralCutoff) self.ARGLG = MM.mm(self.AR.L, self.AR.R[:, 0:nuoL], tmp) self.A = self.AL+self.AR # transmission matrix AL.GamR self.TT = MM.mm(self.AL.R[:, nuo-nuoR:nuo], self.GamR, self.AL.L[nuo-nuoR:nuo, :]) else: self.AL = MM.mm(self.Gr[:, 0:nuoL], self.GamL, self.Ga[0:nuoL, :]) tmp = MM.mm(self.GamL, self.Gr[0:nuoL, :]) self.ALT = MM.mm(self.Ga[:, 0:nuoL], tmp) self.AR = MM.mm(self.Gr[:, nuo-nuoR:nuo], self.GamR, self.Ga[nuo-nuoR:nuo, :]) self.ARGLG = MM.mm(self.AR[:, 0:nuoL], tmp) self.A = self.AL+self.AR # transmission matrix AL.GamR self.TT = MM.mm(self.AL[nuo-nuoR:nuo, nuo-nuoR:nuo], self.GamR) print('NEGF.calcGF: Shape of transmission matrix (TT):', self.TT.shape) print('NEGF.calcGF: Energy and total transmission Tr[TT].real:', ee, N.trace(self.TT).real) # Write also the Gammas in the full space of Gr/Ga/A # (needed for the inelastic shot noise) self.GammaL = N.zeros(self.Gr.shape, N.complex) self.GammaL[0:nuoL, 0:nuoL] = self.GamL self.GammaR = N.zeros(self.Gr.shape, N.complex) self.GammaR[nuo-nuoR:nuo, nuo-nuoR:nuo] = self.GamR
def calcSigLR(self, ee, kpoint, ispin=0, etaLead=0.0, useSigNCfiles=False, SpectralCutoff=0.0): """ Calculate (folded) self-energy at energy ee and 2d k-point Uses SpectralMatrix format for the spectralfunction matrices, see Inelastica.math, if cutoff>0.0 """ nuoL, nuoR = self.nuoL, self.nuoR nuo0, nuoL0, nuoR0 = self.nuo0, self.nuoL0, self.nuoR0 FoldedL, FoldedR = self.FoldedL, self.FoldedR devSt, devEnd = self.DeviceOrbs[0], self.DeviceOrbs[1] # Calculate Sigma without folding self.setkpoint(kpoint, ispin) SigL0 = self.elecL.getSig(ee, kpoint, left=True, Bulk=self.Bulk, ispin=ispin, etaLead=etaLead, useSigNCfiles=useSigNCfiles) SigR0 = self.elecR.getSig(ee, kpoint, left=False, Bulk=self.Bulk, ispin=ispin, etaLead=etaLead, useSigNCfiles=useSigNCfiles) if FoldedL: # Fold down from nuoL0 to the device region # A11 A12 g11 g12 I 0 # A21 A22 * g21 g22 = 0 I -> # g22 = (A22-A21.A11^-1.A12)^-1 -> # Sigma = A21.A11^-1.A12 (tau=A12) devEndL = self.devEndL # Do folding eSmH = ee*self.S0-self.H0 eSmHmS = eSmH[0:devEndL, 0:devEndL].copy() if self.Bulk: eSmHmS[0:nuoL0, 0:nuoL0] = SigL0 # SGF^1 else: eSmHmS[0:nuoL0, 0:nuoL0] = eSmHmS[0:nuoL0, 0:nuoL0]-SigL0 tau = eSmHmS[0:devSt-1, devSt-1:devEndL].copy() taud = eSmHmS[devSt-1:devEndL, 0:devSt-1].copy() inv = LA.inv(eSmHmS[0:devSt-1, 0:devSt-1]) eSmHmS[devSt-1:devEndL, devSt-1:devEndL] = eSmHmS[devSt-1:devEndL, devSt-1:devEndL]-\ MM.mm(taud, inv, tau) self.SigL = eSmH[devSt-1:devEndL, devSt-1:devEndL]-eSmHmS[devSt-1:devEndL, devSt-1:devEndL] else: self.SigL = SigL0 self.GamL = 1.0j*(self.SigL-MM.dagger(self.SigL)) if self.Bulk and not FoldedL: # Reverse sign since SigL is really SGF^-1 self.GamL = -1.0*self.GamL AssertReal(N.diag(self.GamL), 'GamL') if FoldedR: # Fold down from nuoR0 to the device region devStR = self.devStR eSmH = ee*self.S0-self.H0 eSmHmS = eSmH[devStR-1:nuo0, devStR-1:nuo0].copy() tmpnuo = len(eSmHmS) if self.Bulk: eSmHmS[tmpnuo-nuoR0:tmpnuo, tmpnuo-nuoR0:tmpnuo] = SigR0 # SGF^1 else: eSmHmS[tmpnuo-nuoR0:tmpnuo, tmpnuo-nuoR0:tmpnuo] = eSmHmS[tmpnuo-nuoR0:tmpnuo, tmpnuo-nuoR0:tmpnuo]-SigR0 tau = eSmHmS[0:nuoR, nuoR:tmpnuo].copy() taud = eSmHmS[nuoR:tmpnuo, 0:nuoR].copy() inv = LA.inv(eSmHmS[nuoR:tmpnuo, nuoR:tmpnuo]) eSmHmS[0:nuoR, 0:nuoR] = eSmHmS[0:nuoR, 0:nuoR]-MM.mm(tau, inv, taud) self.SigR = eSmH[devStR-1:devEnd, devStR-1:devEnd]-eSmHmS[0:nuoR, 0:nuoR] else: self.SigR = SigR0 self.GamR = 1.0j*(self.SigR-MM.dagger(self.SigR)) if self.Bulk and not FoldedR: # Reverse sign since SigR is really SGF^-1 self.GamR = -1.0*self.GamR AssertReal(N.diag(self.GamR), 'GamR')
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): Log.CreatePipeOutput(options) #VC.OptionsCheck(options) Log.PrintMainHeader(options) try: fdf = glob.glob(options.onlyTSdir + '/RUN.fdf') TSrun = True except: fdf = glob.glob(options.FCwildcard + '/RUN.fdf') # This should be made an input flag TSrun = False SCDM = Supercell_DynamicalMatrix(fdf, TSrun) # Write high-symmetry path WritePath(options.DestDir + '/symmetry-path', SCDM.Sym.path, options.steps) # Write mesh k1, k2, k3 = ast.literal_eval(options.mesh) rvec = 2 * N.pi * N.array([SCDM.Sym.b1, SCDM.Sym.b2, SCDM.Sym.b3]) import Inelastica.physics.mesh as Kmesh # Full mesh kmesh = Kmesh.kmesh(2**k1, 2**k2, 2**k3, meshtype=['LIN', 'LIN', 'LIN'], invsymmetry=False) WriteKpoints(options.DestDir + '/mesh_%ix%ix%i' % tuple(kmesh.Nk), N.dot(kmesh.k, rvec)) # Mesh reduced by inversion symmetry kmesh = Kmesh.kmesh(2**k1, 2**k2, 2**k3, meshtype=['LIN', 'LIN', 'LIN'], invsymmetry=True) WriteKpoints(options.DestDir + '/mesh_%ix%ix%i_invsym' % tuple(kmesh.Nk), N.dot(kmesh.k, rvec)) # Evaluate electron k-points if options.kfile: # Prepare Hamiltonian etc in Gamma for whole supercell natoms = SIO.GetFDFlineWithDefault(fdf[0], 'NumberOfAtoms', int, -1, 'Error') SCDM.PrepareGradients(options.onlySdir, N.array([0., 0., 0.]), 1, natoms, AbsEref=False, atype=N.complex, TSrun=TSrun) SCDM.nao = SCDM.h0.shape[-1] SCDM.FirstOrb = SCDM.OrbIndx[0][0] # First atom = 1 SCDM.LastOrb = SCDM.OrbIndx[SCDM.Sym.basis.NN - 1][1] # Last atom = Sym.NN SCDM.rednao = SCDM.LastOrb + 1 - SCDM.FirstOrb # Read kpoints kpts, dk, klabels, kticks = ReadKpoints(options.kfile) if klabels: # Only write ascii if labels exist WriteKpoints(options.DestDir + '/kpoints', kpts, klabels) # Prepare netcdf ncfn = options.DestDir + '/Electrons.nc' ncf = NC4.Dataset(ncfn, 'w') # Grid ncf.createDimension('gridpts', len(kpts)) ncf.createDimension('vector', 3) grid = ncf.createVariable('grid', 'd', ('gridpts', 'vector')) grid[:] = kpts grid.units = '1/Angstrom' # Geometry ncf.createDimension('atoms', SCDM.Sym.basis.NN) xyz = ncf.createVariable('xyz', 'd', ('atoms', 'vector')) xyz[:] = SCDM.Sym.basis.xyz xyz.units = 'Angstrom' pbc = ncf.createVariable('pbc', 'd', ('vector', 'vector')) pbc.units = 'Angstrom' pbc[:] = [SCDM.Sym.a1, SCDM.Sym.a2, SCDM.Sym.a3] rvec1 = ncf.createVariable('rvec', 'd', ('vector', 'vector')) rvec1.units = '1/Angstrom (incl. factor 2pi)' rvec1[:] = rvec ncf.sync() # Loop over kpoints for i, k in enumerate(kpts): if i < 100: # Print only for the first 100 points ev, evec = SCDM.ComputeElectronStates(k, verbose=True, TSrun=TSrun) else: ev, evec = SCDM.ComputeElectronStates(k, verbose=False, TSrun=TSrun) # otherwise something simple if i % 100 == 0: print('%i out of %i k-points computed' % (i, len(kpts))) if i == 0: ncf.createDimension('nspin', SCDM.nspin) ncf.createDimension('orbs', SCDM.rednao) if options.nbands and options.nbands < SCDM.rednao: nbands = options.nbands else: nbands = SCDM.rednao ncf.createDimension('bands', nbands) evals = ncf.createVariable('eigenvalues', 'd', ('gridpts', 'nspin', 'bands')) evals.units = 'eV' evecsRe = ncf.createVariable( 'eigenvectors.re', 'd', ('gridpts', 'nspin', 'orbs', 'bands')) evecsIm = ncf.createVariable( 'eigenvectors.im', 'd', ('gridpts', 'nspin', 'orbs', 'bands')) # Check eigenvectors print('SupercellPhonons: Checking eigenvectors at', k) for j in range(SCDM.nspin): ev2 = N.diagonal( MM.mm(MM.dagger(evec[j]), SCDM.h0_k[j], evec[j])) print(' ... spin %i: Allclose=' % j, N.allclose(ev[j], ev2, atol=1e-5, rtol=1e-3)) ncf.sync() # Write to NetCDF evals[i, :] = ev[:, :nbands] evecsRe[i, :] = evec[:, :, :nbands].real evecsIm[i, :] = evec[:, :, :nbands].imag ncf.sync() # Include basis orbitals in netcdf file if SCDM.Sym.basis.NN == len(SCDM.OrbIndx): lasto = N.zeros(SCDM.Sym.basis.NN + 1, N.float) lasto[:SCDM.Sym.basis.NN] = SCDM.OrbIndx[:SCDM.Sym.basis.NN, 0] lasto[SCDM.Sym.basis.NN] = SCDM.OrbIndx[SCDM.Sym.basis.NN - 1, 1] + 1 else: lasto = SCDM.OrbIndx[:SCDM.Sym.basis.NN + 1, 0] orbbasis = SIO.BuildBasis(fdf[0], 1, SCDM.Sym.basis.NN, lasto) # Note that the above basis is for the geometry with an atom FC-moved in z. #print dir(orbbasis) #print orbbasis.xyz # Hence, this is not the correct geometry of the basis atoms! center = ncf.createVariable('orbcenter', 'i', ('orbs', )) center[:] = N.array(orbbasis.ii - 1, dtype='int32') center.description = 'Atom index (counting from 0) of the orbital center' nn = ncf.createVariable('N', 'i', ('orbs', )) nn[:] = N.array(orbbasis.N, dtype='int32') ll = ncf.createVariable('L', 'i', ('orbs', )) ll[:] = N.array(orbbasis.L, dtype='int32') mm = ncf.createVariable('M', 'i', ('orbs', )) mm[:] = N.array(orbbasis.M, dtype='int32') # Cutoff radius and delta Rc = ncf.createVariable('Rc', 'd', ('orbs', )) Rc[:] = orbbasis.coff Rc.units = 'Angstrom' delta = ncf.createVariable('delta', 'd', ('orbs', )) delta[:] = orbbasis.delta delta.units = 'Angstrom' # Radial components of the orbitals ntb = len(orbbasis.orb[0]) ncf.createDimension('ntb', ntb) rii = ncf.createVariable('rii', 'd', ('orbs', 'ntb')) rii[:] = N.outer(orbbasis.delta, N.arange(ntb)) rii.units = 'Angstrom' radialfct = ncf.createVariable('radialfct', 'd', ('orbs', 'ntb')) radialfct[:] = orbbasis.orb # Sort eigenvalues to connect crossing bands? if options.sorting: for i in range(SCDM.nspin): evals[:, i, :] = SortBands(evals[:, i, :]) # Produce nice plots if labels exist if klabels: if SCDM.nspin == 1: PlotElectronBands(options.DestDir + '/Electrons.agr', dk, evals[:, 0, :], kticks) elif SCDM.nspin == 2: PlotElectronBands(options.DestDir + '/Electrons.UP.agr', dk, evals[:, 0, :], kticks) PlotElectronBands(options.DestDir + '/Electrons.DOWN.agr', dk, evals[:, 1, :], kticks) ncf.close() if TSrun: # only electronic calculation # Ugly hack to get my old code to work again. -Magnus if options.FermiSurface == True: from . import BandStruct as BS options.fdfFile = 'RUN.fdf' options.eMin, options.eMax = -10, 10 options.NNk = 101 BS.general = options BS.main() return SCDM.Sym.path # Compute phonon eigenvalues if options.qfile: SCDM.SymmetrizeFC(options.radius) SCDM.SetMasses() qpts, dq, qlabels, qticks = ReadKpoints(options.qfile) if qlabels: # Only write ascii if labels exist WriteKpoints(options.DestDir + '/qpoints', qpts, qlabels) # Prepare netcdf ncfn = options.DestDir + '/Phonons.nc' ncf = NC4.Dataset(ncfn, 'w') # Grid ncf.createDimension('gridpts', len(qpts)) ncf.createDimension('vector', 3) grid = ncf.createVariable('grid', 'd', ('gridpts', 'vector')) grid[:] = qpts grid.units = '1/Angstrom' # Geometry ncf.createDimension('atoms', SCDM.Sym.basis.NN) xyz = ncf.createVariable('xyz', 'd', ('atoms', 'vector')) xyz[:] = SCDM.Sym.basis.xyz xyz.units = 'Angstrom' pbc = ncf.createVariable('pbc', 'd', ('vector', 'vector')) pbc.units = 'Angstrom' pbc[:] = [SCDM.Sym.a1, SCDM.Sym.a2, SCDM.Sym.a3] rvec1 = ncf.createVariable('rvec', 'd', ('vector', 'vector')) rvec1.units = '1/Angstrom (incl. factor 2pi)' rvec1[:] = rvec ncf.sync() # Loop over q for i, q in enumerate(qpts): if i < 100: # Print only for the first 100 points hw, U = SCDM.ComputePhononModes_q(q, verbose=True) else: hw, U = SCDM.ComputePhononModes_q(q, verbose=False) # otherwise something simple if i % 100 == 0: print('%i out of %i q-points computed' % (i, len(qpts))) if i == 0: ncf.createDimension('bands', len(hw)) ncf.createDimension('displ', len(hw)) evals = ncf.createVariable('eigenvalues', 'd', ('gridpts', 'bands')) evals.units = 'eV' evecsRe = ncf.createVariable('eigenvectors.re', 'd', ('gridpts', 'bands', 'displ')) evecsIm = ncf.createVariable('eigenvectors.im', 'd', ('gridpts', 'bands', 'displ')) # Check eigenvectors print('SupercellPhonons.Checking eigenvectors at', q) tmp = MM.mm(N.conjugate(U), SCDM.FCtilde, N.transpose(U)) const = PC.hbar2SI * (1e20 / (PC.eV2Joule * PC.amu2kg))**0.5 hw2 = const * N.diagonal(tmp)**0.5 # Units in eV print(' ... Allclose=', N.allclose(hw, N.absolute(hw2), atol=1e-5, rtol=1e-3)) ncf.sync() # Write only AXSF files for the first q-point PH.WriteAXSFFiles(options.DestDir + '/q%i_re.axsf' % i, SCDM.Sym.basis.xyz, SCDM.Sym.basis.anr, hw, U.real, 1, SCDM.Sym.basis.NN) PH.WriteAXSFFiles(options.DestDir + '/q%i_im.axsf' % i, SCDM.Sym.basis.xyz, SCDM.Sym.basis.anr, hw, U.imag, 1, SCDM.Sym.basis.NN) PH.WriteFreqFile(options.DestDir + '/q%i.freq' % i, hw) evals[i] = hw evecsRe[i] = U.real evecsIm[i] = U.imag ncf.sync() # Sort eigenvalues to connect crossing bands? if options.sorting: evals = SortBands(evals) # Produce nice plots if labels exist if qlabels: PlotPhononBands(options.DestDir + '/Phonons.agr', dq, N.array(evals[:]), qticks) ncf.close() # Compute e-ph couplings if options.kfile and options.qfile: SCDM.ReadGradients() ncf = NC4.Dataset(options.DestDir + '/EPH.nc', 'w') ncf.createDimension('kpts', len(kpts)) ncf.createDimension('qpts', len(qpts)) ncf.createDimension('modes', len(hw)) ncf.createDimension('nspin', SCDM.nspin) ncf.createDimension('bands', SCDM.rednao) ncf.createDimension('vector', 3) kgrid = ncf.createVariable('kpts', 'd', ('kpts', 'vector')) kgrid[:] = kpts qgrid = ncf.createVariable('qpts', 'd', ('qpts', 'vector')) qgrid[:] = qpts evalfkq = ncf.createVariable('evalfkq', 'd', ('kpts', 'qpts', 'nspin', 'bands')) # First (second) band index n (n') is the initial (final) state, i.e., # Mkq(k,q,mode,spin,n,n') := < n',k+q | dV_q(mode) | n,k > MkqAbs = ncf.createVariable( 'Mkqabs', 'd', ('kpts', 'qpts', 'modes', 'nspin', 'bands', 'bands')) GkqAbs = ncf.createVariable( 'Gkqabs', 'd', ('kpts', 'qpts', 'modes', 'nspin', 'bands', 'bands')) ncf.sync() # Loop over k-points for i, k in enumerate(kpts): kpts[i] = k # Compute initial electronic states evi, eveci = SCDM.ComputeElectronStates(k, verbose=True) # Loop over q-points for j, q in enumerate(qpts): # Compute phonon modes hw, U = SCDM.ComputePhononModes_q(q, verbose=True) # Compute final electronic states evf, evecf = SCDM.ComputeElectronStates(k + q, verbose=True) evalfkq[i, j, :] = evf # Compute electron-phonon couplings m, g = SCDM.ComputeEPHcouplings_kq( k, q) # (modes,nspin,bands,bands) # Data to file # M (modes,spin,i,l) = m(modes,k,j) init(i,j) final(k,l) # 0 1 2 0,1 0 1 # ^-------^ # ^----------------------^ for ispin in range(SCDM.nspin): evecfd = MM.dagger(evecf[ispin]) # (bands,bands) M = N.tensordot(N.tensordot(m[:, ispin], eveci[ispin], axes=[2, 0]), evecfd, axes=[1, 1]) G = N.tensordot(N.tensordot(g[:, ispin], eveci[ispin], axes=[2, 0]), evecfd, axes=[1, 1]) MkqAbs[i, j, :, ispin] = N.absolute(M) GkqAbs[i, j, :, ispin] = N.absolute(G) ncf.sync() ncf.close() return SCDM.Sym.path
def ComputePhononModes(self, FC, verbose=True): dyn = len(self.DynamicAtoms) FCtilde = N.zeros((dyn, 3, dyn, 3), N.complex) # Symmetrize and mass-scale for i, v in enumerate(self.DynamicAtoms): for j, w in enumerate(self.DynamicAtoms): FCtilde[i, :, j, :] = 0.5*(FC[i, :, w-1, :]+MM.dagger(FC[j, :, v-1, :]))\ /(self.Masses[i]*self.Masses[j])**0.5 # Solve eigenvalue problem with symmetric FCtilde FCtilde = FCtilde.reshape((3 * dyn, 3 * dyn), order='C') self.FCtilde = FCtilde evalue, evec = LA.eigh(FCtilde) #evalue,evec = LA.eig(FCtilde) evec = N.transpose(evec) evalue = N.array(evalue, N.complex) # Calculate frequencies const = PC.hbar2SI * (1e20 / (PC.eV2Joule * PC.amu2kg))**0.5 hw = const * evalue**0.5 # Units in eV for i in range(3 * dyn): # Real eigenvalues are defined as positive, imaginary eigenvalues as negative hw[i] = hw[i].real - abs(hw[i].imag) hw = hw.real # Normalize eigenvectors U = evec.copy() for i in range(3 * dyn): U[i] = U[i] / (N.dot(N.conjugate(U[i]), U[i])**0.5) # Sort in order descending mode energies #hw = hw[::-1] # reverse array #U = U[::-1] # reverse array indx = N.argsort(hw)[::-1] # reverse hw = hw[indx] U = U[indx] # Print mode frequencies if verbose: print 'Phonons.CalcPhonons: Frequencies in meV:' for i in range(3 * dyn): print string.rjust('%.3f' % (1000 * hw[i]), 9), if (i - 5) % 6 == 0: print if (i - 5) % 6 != 0: print #print 'Phonons.CalcPhonons: Frequencies in cm^-1:' #for i in range(3*dyn): # print string.rjust('%.3f'%(hw[i]/PC.invcm2eV),9), # if (i-5)%6 == 0: print #if (i-5)%6 != 0: print # Compute real displacement vectors Udisp = U.copy() for i in range(3 * dyn): # Eigenvectors after division by sqrt(mass) Udisp[:, i] = U[:, i] / self.Masses[i / 3]**.5 # Compute displacement vectors scaled for the characteristic length Ucl = N.empty_like(U) for j in range(3 * dyn): for i in range(3 * dyn): # Eigenvectors after multiplication by characteristic length if hw[j] > 0: Ucl[j, i] = U[j, i] * (1. / (self.Masses[i / 3] * (hw[j] / (2 * PC.Rydberg2eV)))**.5) else: # Characteristic length not defined for non-postive frequency Ucl[j, i] = U[j, i] * 0.0 # Expand vectors to full geometry UU = N.zeros((len(hw), self.geom.natoms, 3), N.complex) UUdisp = N.zeros((len(hw), self.geom.natoms, 3), N.complex) UUcl = N.zeros((len(hw), self.geom.natoms, 3), N.complex) for i in range(len(hw)): for j, v in enumerate(self.DynamicAtoms): UU[i, v - 1, :] = [U[i, 3 * j], U[i, 3 * j + 1], U[i, 3 * j + 2]] UUdisp[i, v - 1, :] = [ Udisp[i, 3 * j], Udisp[i, 3 * j + 1], Udisp[i, 3 * j + 2] ] UUcl[i, v - 1, :] = [ Ucl[i, 3 * j], Ucl[i, 3 * j + 1], Ucl[i, 3 * j + 2] ] self.hw = hw self.U = U self.Udisp = Udisp self.Ucl = Ucl self.UU = UU self.UUdisp = UUdisp self.UUcl = UUcl
def main(options): """ Main routine to compute elastic transmission probabilities etc. Parameters ---------- options : an ``options`` instance """ Log.CreatePipeOutput(options) VC.OptionsCheck(options) Log.PrintMainHeader(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', 'w') 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', 'w') 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', 'w') 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', 'w') 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', 'w') 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) Log.PrintMainFooter(options)