def SetupCGrun(templateCGrun, newCGrun, NewContactSeparation, AtomsPerLayer, IndexShift=0, ListL=None, ListR=None, ZmatRanges=None, RotationAngle=None, RotationCenter=None, RotationAxis=None, RotationSubset=None, overwrite=False, PBStemplate=None, PBSsubs=None, submitJob=False): """ templateCGrun : Path+foldername to a template CGrun folder which contain the necessary SIESTA files and a structure which is to be modified for a new electrode separation (this function writes a new STRUCT.fdf file) newCGrun : Path+foldername for the CGrun to be created NewContactSeparation : The new electrode separation in Ang (defined as the z-distance over the device between two electrode layers which has not been relaxed) AtomsPerLayer : Number of atoms in one electrode layer in a cross-section wrt. the transport direction IndexShift : Number of atoms which is periodically translated from left to right during the formation of the new STRUCT.fdf from the template *.XV file ListL/ListR : These atom indices (SIESTA numbering) are forced to be a part of the electrodes and hence not affected by stretching RotationAngle/ : Rotate whole CG geometry (or only RotationSubset if specified) RotationCenter/ an angle RotationAngle (in degrees) around RotationAxis vector through RotationAxis/ atom index RotationCenter (SIESTA numbering). These manipulations RotationSubset are implemented BEFORE electrode separation is adjusted. overwrite : (True/False) Specifies if one is allowed to write in existing directories PBStemplate : Path+foldername to a template RUN.pbs file for PBS queueing PBSsubs : A list of string substitutions to be applied to the template PBS script in order to generate a new PBS script (e.g., PBSsubs=[['JOBNAME','newjobname'],...]' will replace any JOBNAME string with newjobname) submitJob : (True/False) Submit to batch queue via qsub command? """ # Make new directories head = os.path.split(newCGrun)[0] if not os.path.isdir(head): print '\nSetupRuns.SetupCGrun: Creating folder %s' % head os.mkdir(head) if not os.path.isdir(newCGrun): print '\nSetupRuns.SetupCGrun: Creating folder %s' % newCGrun os.mkdir(newCGrun) elif not overwrite: print '\nSetupRuns.SetupCGrun: %s already exists. No further actions taken.'\ %newCGrun return '' # Quit script else: print '\nSetupRuns.SetupCGrun: %s already exists. OVERWRITING FILES!!!'\ %newCGrun # Copy template files CopyInputFiles(templateCGrun, newCGrun, ['.fdf', '.vps', '.psf', 'pbs']) # Read relaxed geometry XVfiles = glob.glob(templateCGrun + '/*.XV*') if len(XVfiles) == 1: geom = MG.Geom(XVfiles[0]) elif len(XVfiles) > 1: print 'More than one XV file was found in folder %s:' % templateCGrun for i, xvfile in enumerate(XVfiles): print ' No. %i :' % i, xvfile select = raw_input(' ... select file:') geom = MG.Geom(XVfiles[int(select)]) else: print 'No XV file was found in folder %s:' % templateCGrun raw_input(' ... Continue reading geometry from RUN.fdf?') geom = MG.Geom(templateCGrun + '/RUN.fdf') # Rotate via indexshift? if IndexShift > 0: print 'SetupRuns.SetupCGrun: Applying IndexShift =', IndexShift for ii in range(IndexShift): geom.xyz[0][2] += geom.pbc[2][2] geom.addAtom(geom.xyz[0], geom.snr[0], geom.anr[0]) geom.rmAtom(0) # Rotation? if RotationAngle and RotationCenter and RotationAxis: print 'SetupRuns.SetupCGrun: Rotation applied:' print ' ... Rotation angle =', RotationAngle, ' deg' print ' ... Rotation center = atom %i (SIESTA numbering)' % RotationCenter print ' ... Rotation axis =', RotationAxis if RotationSubset: print ' ... RotationSubset =', RotationSubset, ' (SIESTA numbering)' # Change from SIESTA numbering to Python indices RotationSubset = [x - 1 for x in RotationSubset] center = N.array(geom.xyz[RotationCenter - 1]) geom.rotate(RotationAxis, RotationAngle, center, RotateSubset=RotationSubset) # Adjust electrode separation if NewContactSeparation: geom.stretch2NewContactSeparation(NewContactSeparation, AtomsPerLayer, ListL=ListL, ListR=ListR) if not ZmatRanges: geom.writeFDF(newCGrun + '/STRUCT.fdf') else: geom.writeFDFZmat(newCGrun + '/STRUCT.fdf', ZmatRanges) geom.writeXYZ(newCGrun + '/STRUCT.xyz') geom.writeXYZ(newCGrun + '/STRUCT2.xyz', rep=[2, 2, 2]) # PBS files MakePBS(PBStemplate, newCGrun + '/RUN.pbs', PBSsubs, submitJob, rtype='TS')
def SetupTSrun(CGrun, templateTSrun, newTSrun, AtomsPerLayer, BlockSize, LayersLeft, LayersRight, DeviceLayerInclLeft=0, DeviceLayerInclRight=0, IndexShift=0, AddLeftList=[], AddRightList=[], ListL=None, ListR=None, NewContactSeparation=None, RotationAngle=None, RotationCenter=None, RotationAxis=None, RotationSubset=None, overwrite=False, PBStemplate=None, PBSsubs=None, submitJob=False): """ CGrun : Path+foldername to a relaxed structure CGrun folder on which to run a TranSIESTA calculation. This folder must contain an *.XV file templateTSrun : Path+foldername to an existing TSrun folder which contains all relevant electrode calculations and *.fdf files except for STRUCT.fdf newTSrun : Path+foldername for the TSrun folder to be created AtomsPerLayer : Number of atoms in the electrodes in a cross section along the transport direction BlockSize : Number of atoms in the electrode block from CGrun/*.XV file LayersLeft/Right : Number of blocks to be pasted to the new enlarged STRUCT.fdf. DeviceLayerInclLeft : As default TBTRANS takes all relaxed atoms to be the device If DeviceLayerInclLeft is larger than 0 then TBTRANS includes the specified number of electode atomic planes into the device region. DeviceLayerInclRight : See DeviceLayerInclLeft IndexShift : Number of atoms which is periodically translated from left to right contact BEFORE eventual electrode layers are pasted. AddLeftList : Add list of atoms to the left side AddRightList : Add list of atoms to the right side ListL/ListR : These atom indices (SIESTA numbering) are forced to be a part of the electrodes and hence not affected by stretching NewContactSeparation : (Optional) value to set a different electrode separation than in the CG geometry RotationAngle/ : Rotate whole CG geometry (or only RotationSubset if specified) RotationCenter/ an angle RotationAngle (in degrees) around RotationAxis vector through RotationAxis/ atom index RotationCenter (SIESTA numbering). These manipulations RotationSubset are implemented BEFORE electrode separation is adjusted. overwrite : (True/False) Specifies if one is allowed to write in existing directories PBStemplate : Path+foldername to a template RUN.pbs file for PBS queueing PBSsubs : A list of string substitutions to be applied to the template PBS script in order to generate a new PBS script (e.g., PBSsubs=[['JOBNAME','newjobname'],...]' will replace any JOBNAME string with newjobname) submitJob : (True/False) Submit to batch queue via qsub command? """ # Make new directory if not os.path.isdir(newTSrun): print '\nSetupRuns.SetupTSrun: Creating folder %s' % newTSrun os.mkdir(newTSrun) elif not overwrite: print '\nSetupRuns.SetupTSrun: %s already exists. No further actions taken.'\ %newTSrun return '' # Quit script else: print '\nSetupRuns.SetupTSrun: %s already exists. OVERWRITING FILES!!!'\ %newTSrun # Copy all sub-directories for elm in glob.glob(templateTSrun + '/*'): tail = os.path.split(elm)[1] if os.path.isdir(elm): CopyTree(elm, newTSrun + '/' + tail, overwrite=overwrite) # Copy template files CopyInputFiles(templateTSrun, newTSrun, ['.fdf', '.vps', '.psf', '.pbs']) # Read relaxed geometry XVfiles = glob.glob(CGrun + '/*.XV*') if len(XVfiles) == 1: geom = MG.Geom(XVfiles[0]) elif len(XVfiles) > 1: print 'More than one XV file was found in folder %s:' % CGrun for i, xvfile in enumerate(XVfiles): print ' No. %i :' % i, xvfile select = raw_input(' ... select file:') geom = MG.Geom(XVfiles[int(select)]) else: print 'No XV file was found in folder %s:' % CGrun raw_input(' ... Continue reading geometry from RUN.fdf?') geom = MG.Geom(CGrun + '/RUN.fdf') # Rotate via indexshift? if IndexShift > 0: print 'SetupRuns.SetupTSrun: Applying IndexShift =', IndexShift for ii in range(IndexShift): geom.xyz[0][2] += geom.pbc[2][2] geom.addAtom(geom.xyz[0], geom.snr[0], geom.anr[0]) geom.rmAtom(0) # Overwrite STRUCT files if RotationAngle and RotationCenter and RotationAxis: print 'SetupRuns.SetupTSrun: Rotation applied:' print ' ... Rotation angle =', RotationAngle, ' deg' print ' ... Rotation center = atom %i (SIESTA numbering)' % RotationCenter print ' ... Rotation axis =', RotationAxis if RotationSubset: print ' ... RotationSubset =', RotationSubset, ' (SIESTA numbering)' # Change from SIESTA numbering to Python indices RotationSubset = [x - 1 for x in RotationSubset] center = N.array(geom.xyz[RotationCenter - 1]) geom.rotate(RotationAxis, RotationAngle, center, RotateSubset=RotationSubset) # Paste electrode layers via BlockSize specifications geom.PasteElectrodeLayers(BlockSize, AtomsPerLayer, LayersLeft, LayersRight) # Change contact separation? if NewContactSeparation: geom.stretch2NewContactSeparation(NewContactSeparation, AtomsPerLayer, ListL=ListL, ListR=ListR) # Add electrode atoms to the left if len(AddLeftList) > 0: dz = AddLeftList[AtomsPerLayer, 2] - AddLeftList[0, 2] tmp = N.array(geom.xyz) minz = min(tmp[:, 2]) maxz = max(AddLeftList[:, 2]) for ii in reversed(range(len(AddLeftList))): tmp = list(AddLeftList[ii, :] + (-maxz + minz - dz) * N.array([0.0, 0.0, 1.0], N.float)) geom.prependAtom(tmp, geom.snr[0], geom.anr[0]) geom.pbc[2][2] += len(AddLeftList) / AtomsPerLayer * dz # Add electrode atoms to the right if len(AddRightList) > 0: dz = AddRightList[AtomsPerLayer, 2] - AddRightList[0, 2] tmp = N.array(geom.xyz) maxz = tmp[0, 2] - dz + geom.pbc[2][2] minz = min(AddRightList[:, 2]) for ii in range(len(AddRightList)): geom.addAtom( list(AddRightList[ii, :] + (maxz - minz + dz) * N.array([0, 0, 1], N.float)), geom.snr[0], geom.anr[0]) geom.pbc[2][2] += len(AddRightList) / AtomsPerLayer * dz # Write structure to files geom.writeFDF(newTSrun + '/STRUCT.fdf') geom.writeXYZ(newTSrun + '/STRUCT.xyz') geom.writeXYZ(newTSrun + '/STRUCT2.xyz', rep=[2, 2, 2]) # Find device atoms geom.findContactsAndDevice(AtomsPerLayer) PDOSfirst = min(geom.deviceList) - DeviceLayerInclLeft * AtomsPerLayer PDOSlast = max(geom.deviceList) + DeviceLayerInclRight * AtomsPerLayer # Prepend lines to TBTRANS.fdf for elm in glob.glob(newTSrun + '/TBTRANS.fdf'): if os.path.isfile(elm): print 'SetupRuns.SetupTSrun: Prepending PDOS = [%i,%i] to %s' \ %(PDOSfirst, PDOSlast, elm) f = open(elm, 'r') lines = f.readlines() f.close() f = open(elm, 'w') f.write('### Lines appended %s \n' % time.ctime()) f.write('TS.TBT.PDOSFrom %i\n' % PDOSfirst) f.write('TS.TBT.PDOSTo %i\n\n' % PDOSlast) for line in lines: f.write(line) f.close() # PBS files MakePBS(PBStemplate, newTSrun + '/RUN.pbs', PBSsubs, submitJob, rtype='TS')
def calcTSWF(options, ikpoint): kpoint = options.kpoints.k[ikpoint] def calcandwrite(A, txt, kpoint, ikpoint): if isinstance(A, MM.SpectralMatrix): A = A.full() A = A*PC.Rydberg2eV # Change to 1/Ryd ev, U = LA.eigh(A) Utilde = N.empty(U.shape, U.dtype) for jj, val in enumerate(ev): # Problems with negative numbers if val < 0: val = 0 Utilde[:, jj] = N.sqrt(val/(2*N.pi))*U[:, jj] indx2 = N.where(abs(abs(ev) > 1e-4))[0] # Pick non-zero states ev = ev[indx2] Utilde = Utilde[:, indx2] indx = ev.real.argsort()[::-1] fn = options.DestDir+'/%i/'%(ikpoint)+options.systemlabel+'.%s'%(txt) path = './'+options.DestDir+'/' #FermiEnergy = SIO.HS(options.systemlabel+'.TSHS').ef def noWfs(side): tmp = len(glob.glob(path+str(ikpoint)+'/'+options.systemlabel+'.A'+side+'*')) return tmp if noWfs('L') == 0 or noWfs('R') == 0 and len(glob.glob(path+str(ikpoint)+'/FD*')) == 0: print('Calculating localized-basis states from spectral function %s ...'%(txt)) tlb = time.clock() calcWF2(options, geom, options.DeviceAtoms, basis, Utilde[:, indx], [N1, N2, N3, minN3, maxN3], Fold=True, k=kpoint, fn=fn) times = N.round(time.clock()-tlb, 2) timem = N.round(times/60, 2) print('Finished in '+str(times)+' s = '+str(timem)+' min') if noWfs('L') > 0 and noWfs('R') > 0 and len(glob.glob(path+str(ikpoint)+'/FD*')) == 0 and str('%s'%(txt)) == str('AR'): print('\nLocalized-basis states are calculated in k point '+str(ikpoint)+'/.') print('------------------------------------------------------') print('Finite-difference calculation of vacuum states starts!') timeFD = time.clock() STMFD.main(options, kpoint, ikpoint) print('FD calculation in k-point folder '+str(ikpoint)+'/ done in '+str(N.round((time.clock()-timeFD)/60, 2))+' min.') print('------------------------------------------------------') if options.savelocwfs == False: os.system('rm -f '+path+str(ikpoint)+'/'+options.systemlabel+'*') #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, 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 file = NC.Dataset('TotalPotential.grid.nc', 'r') N1, N2, N3 = len(file.dimensions['n1']), len(file.dimensions['n2']), len(file.dimensions['n3']) cell = file.variables['cell'][:] file.close() #Find device region in a3 axis U = LA.inv(N.array([cell[0]/N1, cell[1]/N2, cell[2]/N3]).transpose()) gridindx = N.dot(geom.xyz[options.DeviceAtoms[0]-1:options.DeviceAtoms[1]]/PC.Bohr2Ang, U) minN3, maxN3 = N.floor(N.min(gridindx[:, 2])).astype(N.int), N.ceil(N.max(gridindx[:, 2])).astype(N.int) if not N.allclose(geom.pbc, cell*PC.Bohr2Ang): print('Error: TotalPotential.grid.nc has different cell compared to geometry') sys.exit(1) print('\nk-point folder '+str(ikpoint)+'/') print('Checking/calculating localized-basis states ...') calcandwrite(DevGF.AL, 'AL', kpoint, ikpoint) calcandwrite(DevGF.AR, 'AR', kpoint, ikpoint)
def SetupFCrun(CGrun, newFCrun, FCfirst, FClast, displacement=0.02, overwrite=False, PBStemplate=None, PBSsubs=None, submitJob=False): """ CGrun : Path+foldername to a relaxed structure CGrun folder on which to perform a FCrun calculation. This folder must contain an *.XV file newFCrun : Path+foldername for the FCrun to be created FCfirst : Number of the first atom to displace (SIESTA numbering) FClast : Number of the last atom to displace (SIESTA numbering) displacement : Displacement overwrite : (True/False) Specifies if one is allowed to write in existing directories PBStemplate : Path+foldername to a template RUN.pbs file for PBS queueing PBSsubs : A list of string substitutions to be applied to the template PBS script in order to generate a new PBS script (e.g., PBSsubs=[['JOBNAME','newjobname'],...]' will replace any JOBNAME string with newjobname) submitJob : (True/False) Submit to batch queue via qsub command? """ # Make new directory if not os.path.isdir(newFCrun): print '\nSetupRuns.SetupFCrun: Creating folder %s' % newFCrun os.mkdir(newFCrun) elif not overwrite: print '\nSetupRuns.SetupFCrun: %s already exists. No further actions taken.'\ %newFCrun return '' # Quit script else: print '\nSetupRuns.SetupFCrun: %s already exists. OVERWRITING FILES!!!'\ %newFCrun # Copy template files CopyInputFiles(CGrun, newFCrun, ['.fdf', '.vps', '.psf', '.DM', '.XV', '.pbs', '.TSDE']) # Read relaxed geometry and overwrite STRUCT files XVfiles = glob.glob(CGrun + '/*.XV*') if len(XVfiles) == 1: geom = MG.Geom(XVfiles[0]) elif len(XVfiles) > 1: print 'More than one XV file was found in folder %s:' % CGrun for i, xvfile in enumerate(XVfiles): print ' No. %i :' % i, xvfile select = raw_input(' ... select file:') geom = MG.Geom(XVfiles[int(select)]) else: print 'No XV file was found in folder %s:' % CGrun raw_input(' ... Continue reading geometry from RUN.fdf?') geom = MG.Geom(CGrun + '/RUN.fdf') geom.writeFDF(newFCrun + '/STRUCT.fdf') geom.writeXYZ(newFCrun + '/STRUCT.xyz') geom.writeXYZ(newFCrun + '/STRUCT2.xyz', rep=[2, 2, 2]) # Prepend lines to RUN.fdf for elm in glob.glob(newFCrun + '/RUN.fdf'): if os.path.isfile(elm): print 'SetupRuns.SetupFCrun: Modifying %s' % elm f = open(elm, 'r') lines = f.readlines() f.close() f = open(elm, 'w') f.write('### Lines appended %s \n' % time.ctime()) f.write('MD.TypeOfRun FC\n') f.write('MD.FCfirst %i\n' % FCfirst) f.write('MD.FClast %i\n' % FClast) f.write('TS.HS.Save True\n') f.write('TS.SaveHS True\n') f.write('MD.FCDispl %.8f Ang\n' % displacement) for line in lines: f.write(line) f.close() # PBS files MakePBS(PBStemplate, newFCrun + '/RUN.pbs', PBSsubs, submitJob, rtype='TS')
def main(options): """ Main routine to compute eigenchannel scattering states Parameters ---------- options : an ``options`` instance """ Log.CreatePipeOutput(options) VC.OptionsCheck(options) Log.PrintMainHeader(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' Log.PrintMainFooter(options)