def BuildOSstruct(infile, outfile, axes=[0, 1, 2], direction=[-1, 1], displacement=0.04 * PC.Bohr2Ang): print('BuildOSstruct: displacement = %.6f Ang' % displacement) geom = MG.Geom(infile) for i in axes: for displdir in direction: new = MG.Geom(infile) displ = [0., 0., 0.] displ[i] = displdir * displacement new.move(displ) geom.addGeom(new) geom.writeFDF(outfile)
def FindElectrodeSep(directory, AtomsPerLayer): for xvfile in glob.glob(directory + '/*.XV*'): print(xvfile) g = MG.Geom(xvfile) g.findContactsAndDevice(AtomsPerLayer) DeviceFirst, DeviceLast = g.deviceList[0], g.deviceList[-1] return g.ContactSeparation, DeviceFirst, DeviceLast
def __init__(self, runfdf): self.fdf = runfdf self.directory, self.tail = os.path.split(runfdf) self.systemlabel = SIO.GetFDFlineWithDefault(runfdf, 'SystemLabel', str, 'siesta', 'Phonons') # Read geometry self.geom = MG.Geom(runfdf) # Compare with XV file corrected for last displacement XV = self.directory + '/%s.XV' % self.systemlabel geomXV = MG.Geom(XV) natoms = self.geom.natoms # Determine TSHS files files = glob.glob(self.directory + '/%s*.TSHS' % self.systemlabel) # Build dictionary over TSHS files and corresponding displacement amplitudes self.TSHS = {} try: self.TSHS[0] = files[0] # Equilibrium TSHS except: sys.exit('Phonons.GetFileLists: No TSHS file found in %s' % self.directory)
def checkDone(self): if self.fixed==True: self.FDFgeom = MG.Geom(self.dir+"/RUN.fdf") self.XVgeom = readxv(self.dir) self.forces = SIO.ReadForces(self.dir+"/RUN.out") self.forces = self.forces[-len(self.XVgeom.xyz):] self.Ftot = self.forces self.converged = True return True else: SIO.CheckTermination(self.dir+"/RUN.out") self.FDFgeom = MG.Geom(self.dir+"/RUN.fdf") done = False try: self.XVgeom = readxv(self.dir) self.forces = SIO.ReadForces(self.dir+"/RUN.out") print len(self.forces[0]) if N.allclose(self.XVgeom.xyz, self.FDFgeom.xyz, 1e-6): done=True except: done=False return done
def checkDone(self): if self.fixed == True: self.FDFgeom = MG.Geom(self.dirr + "/" + opts.fn) self.XVgeom = readxv(self.dirr) self.forces = SIO.ReadForces(self.dirr + "/RUN.out") self.forces = self.forces[-len(self.XVgeom.xyz):] self.F = N.array(self.forces) * 0 self.v = N.array(self.forces) * 0 self.converged = True return True else: done = SIO.CheckTermination(self.dirr + "/RUN.out") if done: try: self.FDFgeom = MG.Geom(self.dirr + "/" + opts.fn) self.XVgeom = readxv(self.dirr) self.forces = SIO.ReadForces(self.dirr + "/RUN.out") if N.allclose(self.XVgeom.xyz, self.FDFgeom.xyz, 1e-6): done = True except: done = False return done
def readxv(dirpath): global geom # Read geometry from first .XV file found in dirpath fns=glob.glob(dirpath+'/*.XV') if len(fns)>1: print "ERROR: NEB: More than one .XV file in dir:%s"%dirpath sys.exit(1) elif len(fns)<1: return None print('Reading geometry from "%s" file' % fns[0]) geom = MG.Geom(fns[0]) return geom
def cutlayers(infile, nalayer, nl, nr, outfile, ord=None): """ cut down some layers for md simulation infile input STRUCT.fdf file nalayer number of atoms per layer nl nl layers from left nr nr layers from left ord atom lists in new order """ print "reading ", infile geom = MG.Geom(infile) xyz = geom.xyz snr = geom.snr anr = geom.anr pbc = geom.pbc if ord is not None: anr, xyz = reordxyz(anr, xyz, ord) zs = [a[2] for a in xyz] olen = max(zs) - min(zs) na = len(xyz) nal = nl * nalayer nar = nr * nalayer if (nal + nar >= na): print "Cuting too many atoms" sys.exit(0) nna = int(na - nal - nar) nxyz = [xyz[nal + i] for i in range(nna)] nsnr = [snr[nal + i] for i in range(nna)] nanr = [anr[nal + i] for i in range(nna)] nzs = [a[2] for a in nxyz] nlen = max(nzs) - min(nzs) pbc[2][2] = pbc[2][2] - (olen - nlen) geom.xyz = nxyz geom.pbc = pbc geom.snr = nsnr geom.anr = nanr geom.natoms = nna geom.writeFDF(outfile) geom.writeXYZ(os.path.splitext(outfile)[0] + ".xyz") return geom
def __init__(self, dirr, restart, iistep, initial=None, final=None): self.dirr = dirr self.converged, self.Fmax = False, 0.0 self.ii = iistep self.fixed = (iistep == 0) or (iistep == opts.NNEB + 1) if restart or self.fixed: self.FDFgeom = MG.Geom(self.dirr + "/" + opts.fn) self.XVgeom = readxv(self.dirr) self.v = N.array(self.FDFgeom.xyz) * 0 if not restart and not self.fixed: os.makedirs(dirr) SUR.CopyInputFiles(opts.initial+"/CGrun/", dirr,\ ['.fdf', '.vps', '.psf']) # Interpolate ixyz, fxyz = N.array(initial.XVgeom.xyz), N.array(final.XVgeom.xyz) mix = float(iistep) / (opts.NNEB + 1.0) xyz = (1 - mix) * ixyz + mix * fxyz self.FDFgeom = copy.copy(initial.XVgeom) self.FDFgeom.xyz = [xyz[ii, :] for ii in range(len(xyz))] self.FDFgeom.writeFDF(self.dirr + "/STRUCT.fdf") self.v = N.array(self.FDFgeom.xyz) * 0 # Append lines to RUN.fdf elm = dirr + "/" + opts.fn 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 CG\n") f.write("MD.NumCGsteps 0\n") f.write("MD.UseSaveXV False\n") f.write("MD.UseSaveCG False\n") f.write('# end of lines appended %s \n' % time.ctime()) for line in lines: f.write(line) f.close() self.done = self.checkDone() const = SIO.GetFDFblock(dirr + "/" + opts.fn, "GeometryConstraints") if opts.const2 != None: self.const = [[opts.const2[0], opts.const2[0]]] else: self.const = [] for ii in const: self.const += [[int(ii[2]) - 1, int(ii[4]) - 1]]
def readxv(): global geom # Read geometry from first .XV file found in dir fns = glob.glob('*.XV') if len(fns) > 1: print "ERROR: BandStruct: More than one .XV file ... which geometry to choose???" sys.exit(1) elif len(fns) < 1: print "ERROR: BandStruct: Error ... No .XV file found!" sys.exit(1) print('Reading geometry from "%s" file' % fns[0]) geom = MG.Geom(fns[0]) geom.sym = SYM.Symmetry(fns[0], onlyLatticeSym=True) if geom.sym.NNbasis != geom.natoms: print "ERROR: Siesta cell does not contain one unit cell" sys.exit(1)
def RunTBT(TSrun, Emin, Emax, NPoints, NumKxy_A1=1, NumKxy_A2=1, AtomsPerLayer=0, DeviceLayerInclLeft=0, DeviceLayerInclRight=0, PBStemplate=None, PBSsubs=None, submitJob=False): """ 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? """ # Remove old Green's functions for elm in glob.glob(TSrun + '/*GF'): print('SetupRuns.RunTBT: Removing *.GF') if os.path.isfile(elm): print(' Deleting %s' % elm) os.remove(elm) # Determine Device PDOS? if AtomsPerLayer > 0: for elm in glob.glob(TSrun + '/*XV'): if os.path.isfile(elm): geom = MG.Geom(elm) geom.findContactsAndDevice(AtomsPerLayer) PDOSfirst = min( geom.deviceList) - DeviceLayerInclLeft * AtomsPerLayer PDOSlast = max( geom.deviceList) + DeviceLayerInclRight * AtomsPerLayer else: PDOSfirst, PDOSlast = 0, 0 # Prepend lines to TBTRANS.fdf tbtfile = TSrun + '/TBTRANS.fdf' f = open(tbtfile, 'r') lines = f.readlines() f.close() f = open(tbtfile, 'w') f.write('### Lines appended %s \n' % time.ctime()) if PDOSfirst > 0 and PDOSlast > 0: print('SetupRuns.RunTBT: Prepending PDOS = [%i,%i] to %s' \ %(PDOSfirst, PDOSlast, tbtfile)) f.write('TS.TBT.PDOSFrom %i\n' % PDOSfirst) f.write('TS.TBT.PDOSTo %i\n' % PDOSlast) print('SetupRuns.RunTBT: Writing Emin=%f, Emax=%f, NPoints=%i to %s' \ %(Emin, Emax, NPoints, tbtfile)) f.write('TS.TBT.Emin %f eV\n' % Emin) f.write('TS.TBT.Emax %f eV\n' % Emax) f.write('TS.TBT.NPoints %i\n' % NPoints) f.write('TS.NumKxy_A1 %i\n' % NumKxy_A1) f.write('TS.NumKxy_A2 %i\n' % NumKxy_A2) f.write('pyTBT.K_A1 %i\n' % NumKxy_A1) f.write('pyTBT.K_A2 %i\n\n' % NumKxy_A2) for line in lines: f.write(line) f.close() # PBS files MakePBS(PBStemplate, newTSrun + '/RUN.pyTBT.pbs', PBSsubs, submitJob, rtype='PY')
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', '.slurm', '.TSHS']) # 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 = input(' ... select file:') geom = MG.Geom(XVfiles[int(select)]) else: print('No XV file was found in folder %s:' % templateCGrun) 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 SetupFCrun(CGrun, newFCrun, FCfirst, FClast, displacement=0.02, overwrite=False, PBStemplate=None, PBSsubs=None, submitJob=False, main_fdf="RUN.fdf"): """ 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', '.slurm', '.TSDE', '.TSHS' ]) # 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 = input(' ... select file:') geom = MG.Geom(XVfiles[int(select)]) else: print('No XV file was found in folder %s:' % CGrun) 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]) if os.path.isfile(CGrun + "/STRUCT.fdf"): copy_chemical_info(CGrun + "/STRUCT.fdf", newFCrun + "/STRUCT.fdf") # Prepend lines to RUN.fdf elm = newFCrun + '/' + main_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) f.write('%include STRUCT.fdf\n') f.writelines(lines) f.close() else: print("{} was not found, so it cannot be edited." "Rerun with main_fdf set correctly.".format(elm)) # PBS files MakePBS(PBStemplate, newFCrun + '/RUN.pbs', PBSsubs, submitJob, rtype='TS')
def __init__(self, runfdf): self.fdf = runfdf self.directory, self.tail = os.path.split(runfdf) self.systemlabel = SIO.GetFDFlineWithDefault(runfdf, 'SystemLabel', str, 'siesta', 'Phonons') FCfirst = SIO.GetFDFlineWithDefault(runfdf, 'MD.FCfirst', int, 0, 'Phonons') FClast = SIO.GetFDFlineWithDefault(runfdf, 'MD.FClast', int, 0, 'Phonons') # Finite-displacement amplitude ampl, unit = SIO.GetFDFline(runfdf, KeyWord='MD.FCDispl') if unit.upper() == 'ANG': self.Displ = float(ampl) elif unit.upper() == 'BOHR': self.Displ = float(ampl) * PC.Bohr2Ang print('Displacement = %.6f Ang' % self.Displ) # Read geometry self.geom = MG.Geom(runfdf) # Compare with XV file corrected for last displacement XV = self.directory + '/%s.XV' % self.systemlabel geomXV = MG.Geom(XV) geomXV.xyz[FClast - 1, 2] -= self.Displ if not N.allclose(geomXV.xyz, self.geom.xyz): sys.exit('Error: Geometries %s and %s should differ ONLY by displacement of atom %s in z'\ %(runfdf, XV, FClast)) # Set up FC[i,a,j,b]: Force constant (eV/A^2) from moved atom i, axis a to atom j, axis b natoms = self.geom.natoms self.m = N.zeros((FClast - FCfirst + 1, 3, natoms, 3), N.float) self.p = N.zeros((FClast - FCfirst + 1, 3, natoms, 3), N.float) fc = N.array( SIO.ReadFCFile(self.directory + '/%s.FC' % self.systemlabel)) for i in range(FClast - FCfirst + 1): for j in range(3): self.m[i, j] = fc[2 * (3 * i + j) * natoms:(2 * (3 * i + j) + 1) * natoms] self.p[i, j] = fc[(2 * (3 * i + j) + 1) * natoms:(2 * (3 * i + j) + 2) * natoms] # Correct force constants for the moved atom # Cf. Eq. (13) in Frederiksen et al. PRB 75, 205413 (2007) for i in range(FClast - FCfirst + 1): for j in range(3): self.m[i, j, FCfirst - 1 + i, :] = 0.0 self.m[i, j, FCfirst - 1 + i, :] = -N.sum(self.m[i, j], axis=0) self.p[i, j, FCfirst - 1 + i, :] = 0.0 self.p[i, j, FCfirst - 1 + i, :] = -N.sum(self.p[i, j], axis=0) self.DynamicAtoms = list(range(FCfirst, FClast + 1)) # Determine TSHS files files = glob.glob(self.directory + '/%s*.TSHS' % self.systemlabel) files.sort() if self.directory + '/%s.TSHS' % self.systemlabel in files: # If present, ignore this file which does not origninate from the FCrun files.remove(self.directory + '/%s.TSHS' % self.systemlabel) if (FClast - FCfirst + 1) * 6 + 1 != len(files): warnings.warn( 'Phonons.GetFileLists: WARNING - Inconsistent number of *.TSHS files in %s' % self.directory) return # Build dictionary over TSHS files and corresponding displacement amplitudes self.TSHS = {} self.TSHS[0] = files[0] # Equilibrium TSHS for i, v in enumerate(self.DynamicAtoms): for j in range(3): # Shifted TSHS files (atom,axis,direction) self.TSHS[v, j, -1] = files[1 + 6 * i + 2 * j] self.TSHS[v, j, 1] = files[1 + 6 * i + 2 * j + 1]
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) return DevGF
from __future__ import print_function import glob import os import Inelastica.MakeGeom as MG import numpy as N import numpy.linalg as LA # Find geometries fns = glob.glob('NEB*/CGrun/RUN.fdf') # Fix order indx = [int(ii.split('_')[1].split('/')[0]) for ii in fns] fns = [fns[ii] for ii in N.argsort(indx)] geoms = [MG.Geom(ii) for ii in fns] xyz = [N.array(ii.xyz) for ii in geoms] # Find bond length d = LA.norm(xyz[0][37] - xyz[0][36]) # Move in z to keep bond length for ii in xyz: ii[37, 2] = ii[36, 2] + N.sqrt(d**2 - (ii[36, 0] - ii[37, 0])**2 - (ii[36, 1] - ii[37, 1])**2) # Redistribute points xyz, nxyz = N.array(xyz), N.array(xyz) # Two copies
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', '.slurm']) # 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 = input(' ... select file:') geom = MG.Geom(XVfiles[int(select)]) else: print('No XV file was found in folder %s:' % CGrun) 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(list(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 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
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)