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 readHS(): # Setup H, S and self-energies global HS fn = glob.glob('*.TSHS') if len(fn) > 1: print "ERROR: BandStruct: More than one .TSHS file ... which to choose???" sys.exit(1) if len(fn) < 1: print "ERROR: BandStruct: No .TSHS file ???" sys.exit(1) HS = SIO.HS(fn=fn[0])
def __init__(self, fn, NA1, NA2, voltage=0.0, UseF90helpers=True): self.path = os.path.split(os.path.abspath(fn))[0] self.HS = SIO.HS(fn, UseF90helpers=UseF90helpers) # An electrode HS self.hash = myHash([self.HS, NA1, NA2, voltage]) SavedSig.add_hsfile(self.path) if self.HS.gamma: raise IOError("Are you trying to sneak a Gamma point electrode calculation past me?") self.NA1 = NA1 self.NA2 = NA2 self.kpoint = N.array([1e10, 1e10], N.float) self.voltage = voltage self.scaling = 1.0 # Default scale factor for coupling to device
def __init__(self, onlySdir, kpoint, atype=N.complex): print('Phonons.GetOnlyS: Reading from: ' + onlySdir) onlySfiles = glob.glob(onlySdir + '/*.onlyS*') onlySfiles.sort() if len(onlySfiles) < 1: sys.exit('Phonons.GetOnlyS: No .onlyS file found!') if len(onlySfiles) != 6: sys.exit('Phonons.GetOnlyS: Wrong number of onlyS files found!') else: onlyS = {} Displ = {} for file in onlySfiles: thisHS = SIO.HS(file) thisHS.setkpoint(kpoint, atype=atype) S = thisHS.S del thisHS nao = len(S) // 2 S0 = S[0:nao, 0:nao].copy() dmat = S[0:nao, nao:nao * 2].copy() if file.endswith('_1.onlyS'): onlyS[0, -1] = dmat elif file.endswith('_2.onlyS'): onlyS[0, 1] = dmat elif file.endswith('_3.onlyS'): onlyS[1, -1] = dmat elif file.endswith('_4.onlyS'): onlyS[1, 1] = dmat elif file.endswith('_5.onlyS'): onlyS[2, -1] = dmat elif file.endswith('_6.onlyS'): onlyS[2, 1] = dmat # Loop over the 6 doubled geometries and determine the displacement for i in range(1, 7): thisd = 1e10 xyz = N.array(SIO.Getxyz(onlySdir + '/RUN_%i.fdf' % i)) #for j in range(1, len(xyz)): # thisd = min(thisd, (N.dot(xyz[0]-xyz[j], xyz[0]-xyz[j]))**.5) j = len(xyz) // 2 v = xyz[0] - xyz[j] thisd = N.dot(v, v)**.5 Displ[(i - 1) // 2, 1 - 2 * (i % 2)] = thisd print('Phonons.GetOnlyS: OnlyS-displacement (min) = %.5f Ang' % thisd) # Construct dS array self.S0 = S0 self.dS = N.empty((3, ) + dmat.shape, dtype=dmat.dtype) for j in range(3): self.dS[j] = (onlyS[j, 1] - onlyS[j, -1]) / (Displ[j, -1] + Displ[j, 1]) self.Displ = Displ
def PrepareGradients(self, onlySdir, kpoint, DeviceFirst, DeviceLast, AbsEref, atype, TSrun=False): print('\nPhonons.PrepareGradients: Setting up various arrays') self.atype = atype self.kpoint = kpoint self.OrbIndx, nao = self.FCRs[0].GetOrbitalIndices() self.TSHS0 = SIO.HS(self.TSHS[0]) self.TSHS0.setkpoint(kpoint, atype=atype) if not TSrun: OS = OSrun(onlySdir, kpoint, atype=atype) self.dS = OS.dS # OS.S0 and TSHS0.S should be identical, but with some versions/compilations # of TranSIESTA this is NOT always the case away from k=0 (GammaPoint is OK). # It appears to be a bug in TranSIESTA 3.2 and 4.0b affecting runs # with TS.onlyS=True, i.e., the quick evaluations in the OSrun folder if not N.allclose(OS.S0, self.TSHS0.S): sys.exit( 'Inconsistency detected with your .onlyS files. Perhaps a bug in your TranSIESTA version/compilation or overlaps beyond first neighbor cells.' ) self.invS0H0 = N.empty((2, ) + self.TSHS0.H.shape, dtype=self.TSHS0.H.dtype) #invS0 = LA.inv(OS.S0) # <--- This choice was used in rev. 324-397 invS0 = LA.inv( self.TSHS0.S) # Reverting to the matrix used up to rev. 323 self.nspin = len(self.TSHS0.H) for iSpin in range(self.nspin): self.invS0H0[0, iSpin, :, :] = MM.mm(invS0, self.TSHS0.H[iSpin, :, :]) self.invS0H0[1, iSpin, :, :] = MM.mm(self.TSHS0.H[iSpin, :, :], invS0) del invS0 # don't re-create the array every time... too expensive self.dSdij = N.zeros((nao, nao), atype) # Take Device region self.DeviceAtoms = list(range(DeviceFirst, DeviceLast + 1)) first, last = self.OrbIndx[DeviceFirst - 1][0], self.OrbIndx[DeviceLast - 1][1] self.h0 = self.TSHS0.H[:, first:last + 1, first:last + 1] self.s0 = self.TSHS0.S[first:last + 1, first:last + 1] self.DeviceFirst = DeviceFirst self.DeviceLast = DeviceLast self.AbsEref = AbsEref
def main(): if not SIO.F90imported: print "To test the F90 routines you better compile them first" kuk print 'TESTING reading routines:\n' # Test readTSHS routines for file in ['Self-energy-FCC111/ELEC-1x1/Au3D_BCA.TSHS', 'sample.onlyS']: HS1 = SIO.HS('../TestCalculations/' + file, UseF90helpers=False) HS2 = SIO.HS('../TestCalculations/' + file, UseF90helpers=True) compare_H(HS1, HS2) # Test readTSHS routines new vs. old for file in [ 'Self-energy-FCC100/ELEC-1x1/ABAB.TSHS', 'Self-energy-FCC111/ELEC-1x1/Au3D_BCA.TSHS' ]: HS1 = SIO.HS('../TestCalculations/' + file, UseF90helpers=True) HS2 = SIO.HS('../TestCalculations/' + file.replace('.TSHS', '_NEW.TSHS'), UseF90helpers=True) compare_H(HS1, HS2, not_checks={'xij': False}) # Test removeUnitCellXij print 'TESTING removeUnitCellXij method' elec1 = NEGF.ElectrodeSelfEnergy( '../TestCalculations/Self-energy-FCC111/ELEC-1x1/Au3D_BCA.TSHS', 1, 1, UseF90helpers=True) elec2 = NEGF.ElectrodeSelfEnergy( '../TestCalculations/Self-energy-FCC111/ELEC-1x1/Au3D_BCA.TSHS', 1, 1, UseF90helpers=False) maxerr = N.max(abs(elec1.HS.xij - elec2.HS.xij)) print "Maximum difference between Xij :", maxerr for ii in range(10): k = N.array(RA.random(3), N.float) print " Checking k-point: %f,%f,%f" % (k[0], k[1], k[2]) elec1.HS.kpoint = N.zeros((3, ), N.float) # To ensure it is calculated! elec1.HS.setkpoint(k, UseF90helpers=True) H1, S1 = elec1.HS.H.copy(), elec1.HS.S.copy() elec2.HS.kpoint = N.zeros((3, ), N.float) # To ensure it is calculated! elec2.HS.setkpoint(k, UseF90helpers=False) H2, S2 = elec2.HS.H.copy(), elec2.HS.S.copy() tmp1 = N.max(abs(H1 - H2)) tmp2 = N.max(abs(S1 - S2)) print "Max difference kpointhelper: ", max(tmp1, tmp2) if maxerr > 1e-9: print "ERROR!" kuk ee = RA.random(1) + 0.0001j elec1.semiinf = 2 elec2.semiinf = 2 SGF1 = elec1.getSig(ee, k[0:2].copy(), UseF90helpers=True) SGF2 = elec2.getSig(ee, k[0:2].copy(), UseF90helpers=False) SGFerr = N.max(abs(SGF1 - SGF2)) print "Max difference for self-energy: ", SGFerr maxerr = max(tmp1, tmp2, SGFerr, maxerr) if maxerr > 1e-9: print "ERROR!" kuk # SurfaceGF print '\nTESTING pyTBT.surfaceGF method (1x1 vs 3x3)' elecF90 = NEGF.ElectrodeSelfEnergy( '../TestCalculations/Self-energy-FCC111/ELEC-1x1/Au3D_BCA.TSHS', 3, 3, UseF90helpers=True) elecNoF90 = NEGF.ElectrodeSelfEnergy( '../TestCalculations/Self-energy-FCC111/ELEC-1x1/Au3D_BCA.TSHS', 3, 3, UseF90helpers=False) for ii in range(10): k = N.array(RA.random(3), N.float) print " Checking k-point: %f,%f,%f" % (k[0], k[1], k[2]) elecF90.HS.kpoint = N.zeros((3, ), N.float) # To ensure it is calculated! elecF90.HS.setkpoint(k, UseF90helpers=True) H1, S1 = elecF90.HS.H.copy(), elecF90.HS.S.copy() elecNoF90.HS.kpoint = N.zeros((3, ), N.float) # To ensure it is calculated! elecNoF90.HS.setkpoint(k, UseF90helpers=False) H2, S2 = elecNoF90.HS.H.copy(), elecNoF90.HS.S.copy() tmp1 = N.max(abs(H1 - H2)) tmp2 = N.max(abs(S1 - S2)) print "Max difference kpointhelper: ", max(tmp1, tmp2) if maxerr > 1e-9: print "ERROR!" kuk ee = RA.random(1) + 0.0001j elecF90.semiinf = 2 elecNoF90.semiinf = 2 SGFf90 = elecF90.getSig(ee, k[0:2].copy(), UseF90helpers=True) SGF = elecNoF90.getSig(ee, k[0:2].copy(), UseF90helpers=False) SGFerr = N.max(abs(SGFf90 - SGF)) print "Max difference for self-energy: ", SGFerr maxerr = max(tmp1, tmp2, SGFerr, maxerr) if maxerr > 1e-9: print "ERROR!" kuk print "Maximum error : ", maxerr print print "###############################################################" print "###############################################################" if maxerr > 1e-9: print "ERROR!" kuk else: print "Tests passed for remove Xij, expansion_SE, readTSHS, and setkpoint!" print "###############################################################" print "###############################################################" print
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 main(options, kpoint, ikpoint): Ef = SIO.HS(options.systemlabel + '.TSHS').ef / PC.Rydberg2eV kpt = ikpoint pathkpt = './' + options.DestDir + '/' + str(kpt) + '/' print 'k-point: ' + str(ikpoint) + '/' print '(k1,k2): (' + str(kpoint[0]) + ',' + str(kpoint[1]) + ')' print 'Fermi energy (' + str(options.systemlabel) + '.TSHS):', N.round( Ef, 4), 'Ry =', N.round(Ef * PC.Rydberg2eV, 4), 'eV' #Determine substrate layers and tip height posZMol, posZTip = LayersAndTipheight(options, kpoint, ikpoint) #Read total potential, loc-basis states, lattice constants etc. tmp = readDFT(options, kpoint, pathkpt, posZMol, posZTip) Subwfs, Tipwfs, Vsub, Vtip = tmp[0], tmp[1], tmp[2], tmp[3] scSize, ucSize, Max, dS, theta = tmp[4], tmp[5], tmp[6], tmp[7], tmp[8] SubChans, TipChans, SubPot, TipPot, SubRho, TipRho, MeshCutoff = tmp[ 9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15] Nx, Ny, Nz = scSize[0], scSize[1], scSize[2] NN = Nx * Ny * Nz a1, a2, a3 = ucSize[0], ucSize[1], ucSize[2] if options.samplingscale != 1: print '\nReal-space sampling in xy plane coarser by factor', options.samplingscale, '...' tmp = sampling(options, Nx, Ny, Nz, Subwfs, Tipwfs, SubChans, TipChans, SubPot, TipPot, SubRho, TipRho) Nx, Ny, Nz = tmp[0], tmp[1], tmp[2] NN = Nx * Ny * Nz a1, a2 = a1 * options.samplingscale, a2 * options.samplingscale ucSize = [a1, a2, a3] Subwfs, Tipwfs, Vsub, Vtip, SubRho, TipRho = tmp[3], tmp[4], tmp[ 5], tmp[6], tmp[7], tmp[8] print 'New lattice in xy plane obtained (corresponding to ' + str( MeshCutoff / options.samplingscale**2) + ' Ry):' print ' [Nx Ny Nz] = [' + str(Nx), str(Ny), str(Nz) + ']' print ' [a1 a2 a3] = [' + str(N.round( a1 * PC.Bohr2Ang, 4)), str( N.round(a2 * PC.Bohr2Ang, 4)), str(N.round(a3 * PC.Bohr2Ang, 4)) + '] Ang' else: print '\nReal-space sampling omitted (same grid as TranSiesta).\n' #Substrate part print '\nCalculation from substrate side:' WFs, Chans, rho = Subwfs, SubChans, SubRho inda, indb = reindexing(options, Nx, Ny, Nz, NN, WFs, Chans, rho, ucSize, theta) Pot = Vsub Ham = Hamiltonian(options, a1, a2, a3, Nx, Ny, Nz, NN, Pot, theta, kpoint) Tau, Hb = SplitHam(Ham, inda, indb) ChansL = Chans propSubModes = LinearSolve(options, WFs, inda, indb, Ef, Tau, Hb, NN, Nx, Ny, Nz, Chans) #Tip part print '\nCalculation from tip side:' WFs, Chans, rho = Tipwfs, TipChans, TipRho inda, indb = reindexing(options, Nx, Ny, Nz, NN, WFs, Chans, rho, ucSize, theta) Pot = Vtip Ham = Hamiltonian(options, a1, a2, a3, Nx, Ny, Nz, NN, Pot, theta, kpoint) Tau, Hb = SplitHam(Ham, inda, indb) ChansR = Chans propTipModes = LinearSolve(options, WFs, inda, indb, Ef, Tau, Hb, NN, Nx, Ny, Nz, Chans) #Combine sub and tip to find conductance STMimage, Currmat = Current(options, propSubModes, propTipModes, Nx, Ny, Nz, Max,\ ucSize, ChansL, ChansR, dS, kpoint, pathkpt, ikpoint) STMimage = STMimage[:Nx, :Ny]