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 # 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: warnings.warn('Phonons.GetFileLists: No TSHS file found in %s' % self.directory)
def GetOptions(argv): """ Returns an instance of ``options`` for the ``Phonons`` module Parameters ---------- argv : string For example `-c test_dir`, which gives instructions to compute not only vibrational modes and frequencies, but also the corresponding electron-vibration couplings and to place the results in the output directory `test_dir`. """ # if text string is specified, convert to list if isinstance(argv, VC.string_types): argv = argv.split() import argparse p = argparse.ArgumentParser( description= 'Methods to calculate vibrations and e-ph couplings from SIESTA output' ) p.add_argument('DestDir', help='Destination directory') p.add_argument( '-f', '--fdf', dest='fdf', default='./RUN.fdf', type=str, help='Input fdf-file for SIESTA/TranSIESTA calculation [%(default)s]') p.add_argument('-c', '--CalcCoupl', dest='CalcCoupl', action='store_true', default=False, help='Calculate e-ph couplings [default=%(default)s]') p.add_argument('-r', '--Restart', dest='Restart', action='store_true', default=False, help='Restart from a previous run [default=%(default)s]') p.add_argument( '--CheckPointNetCDF', dest='CheckPointNetCDF', type=str, default='None', help='Old NetCDF file used for restart [default=%(default)s]') p.add_argument( '-s', '--SinglePrec', dest='SinglePrec', action='store_true', default=False, help= 'Calculate e-ph couplings using single precision arrays [default=%(default)s]' ) p.add_argument( '-F', '--DeviceFirst', dest='DeviceFirst', type=int, default=1, help= 'First device atom index (in the electronic basis) [default=%(default)s]' ) p.add_argument( '-L', '--DeviceLast', dest='DeviceLast', type=int, default=0, help= 'Last device atom index (in the electronic basis) [default=NumberOfAtoms]' ) p.add_argument('--FCfirst', dest='FCfirst', type=int, default=1, help='First FC atom index [default=%(default)s]') p.add_argument('--FClast', dest='FClast', type=int, default=0, help='Last FC atom index [default=NumberOfAtoms]') p.add_argument( '--EPHfirst', dest='EPHfirst', type=int, default=1, help= 'First atom index for which the e-ph. couplings are evaluated [default=FCfirst]' ) p.add_argument( '--EPHlast', dest='EPHlast', type=int, default=0, help= 'Last atom index for which the e-ph. couplings are evaluated [default=FClast]' ) p.add_argument( '--PBCFirst', dest='PBCFirst', type=int, default=1, help= 'For eliminating interactions through periodic boundary conditions in z-direction [default=%(default)s]' ) p.add_argument( '--PBCLast', dest='PBCLast', type=int, default=0, help= 'For eliminating interactions through periodic boundary conditions in z-direction [default=NumberOfAtoms]' ) p.add_argument('--FCwildcard', dest='FCwildcard', type=str, default='./FC*', help='Wildcard for FC directories [default=%(default)s]') p.add_argument('--OSdir', dest='onlySdir', type=str, default='./OSrun', help='Location of OnlyS directory [default=%(default)s]') p.add_argument( '-a', '--AbsoluteEnergyReference', dest='AbsEref', action='store_true', default=False, help= 'Use an absolute energy reference (Fermi energy of equilibrium structure) for displaced Hamiltonians (e.g., when eF is not well-defined) instead of the instantaneous Fermi energy for the displaced geometries, cf. Eq.(17) in PRB 75, 205413 (2007) [default=%(default)s]' ) p.add_argument( '-i', '--Isotopes', dest='Isotopes', default='[]', help= 'String, formatted as a list [[i1,m1],...], where the mass of atom index i1 (SIESTA numbering) will be set to m1. Alternatively, the argument can be a file with the string [default=%(default)s]' ) p.add_argument( '-x', '--k1', dest='k1', default=0.0, type=float, help='k-point along a1 where e-ph couplings are evaluated [%(default)s]' ) p.add_argument( '-y', '--k2', dest='k2', default=0.0, type=float, help='k-point along a2 where e-ph couplings are evaluated [%(default)s]' ) p.add_argument( '-z', '--k3', dest='k3', default=0.0, type=float, help='k-point along a3 where e-ph couplings are evaluated [%(default)s]' ) p.add_argument( '-g', '--WriteGradients', dest='WriteGradients', action='store_true', default=False, help='Write real-space gradients dH/dR to NetCDF [default=%(default)s]' ) options = p.parse_args(argv) # Set module name options.module = 'Phonons' # k-point options.kpoint = N.array([options.k1, options.k2, options.k3], N.float) del options.k1, options.k2, options.k3 # Determine array type for H,S,dH,... options.GammaPoint = N.dot(options.kpoint, options.kpoint) < 1e-7 if options.GammaPoint: if options.SinglePrec: options.atype = N.float32 else: options.atype = N.float64 else: if options.SinglePrec: options.atype = N.complex64 else: options.atype = N.complex128 # Check if we need to set the last atom(s) if options.FClast * options.DeviceLast * options.EPHlast * options.PBCLast == 0: # We need NumberOfAtoms fdf = glob.glob(options.FCwildcard + '/' + options.fdf) natoms = SIO.GetFDFlineWithDefault(fdf[0], 'NumberOfAtoms', int, 1000, 'Phonons') if options.FClast == 0: options.FClast = natoms if options.DeviceLast == 0: options.DeviceLast = natoms if options.EPHlast == 0: options.EPHlast = natoms if options.PBCLast == 0: options.PBCLast = natoms # Dynamic atoms options.DynamicAtoms = list(range(options.FCfirst, options.FClast + 1)) # EPH atoms - set only different from options.DynamicAtoms if a subset is specified if options.EPHfirst >= options.FCfirst and options.EPHlast <= options.FClast: options.EPHAtoms = list(range(options.EPHfirst, options.EPHlast + 1)) else: options.EPHAtoms = options.DynamicAtoms del options.EPHfirst, options.EPHlast del options.FCfirst, options.FClast # PBCFirst/PBCLast if options.PBCFirst < options.DeviceFirst: options.PBCFirst = options.DeviceFirst if options.PBCLast > options.DeviceLast: options.PBCLast = options.DeviceLast # Isotopes specified in separate file? if os.path.isfile(options.Isotopes): f = open(options.Isotopes) s = '' for line in f.readlines(): s += line.replace('\n', '') options.Isotopes = s options.Isotopes = ast.literal_eval(options.Isotopes) return options
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 get_elec_vars(lr): # Look up old format first TSHS = SIO.GetFDFlineWithDefault(opts.fn, 'TS.HSFile' + lr, str, '', exe) NA1 = SIO.GetFDFlineWithDefault(opts.fn, 'TS.ReplicateA1' + lr, int, 1, exe) NA2 = SIO.GetFDFlineWithDefault(opts.fn, 'TS.ReplicateA2' + lr, int, 1, exe) # default semi-inf direction semiinf = 2 # Proceed looking up new format, which precedes belec = 'TS.Elec.' + lr print('Looking for new electrode format in: %%block {}'.format(belec)) # Default replication stuff TSHS = SIO.GetFDFlineWithDefault(opts.fn, belec + '.TSHS', str, TSHS, exe) NA1 = SIO.GetFDFlineWithDefault(opts.fn, belec + '.Bloch.A1', int, NA1, exe) NA2 = SIO.GetFDFlineWithDefault(opts.fn, belec + '.Bloch.A2', int, NA2, exe) NA3 = SIO.GetFDFlineWithDefault(opts.fn, belec + '.Bloch.A3', int, 1, exe) # Overwrite block block = SIO.GetFDFblock(opts.fn, KeyWord=belec) for line in block: print(line) # Lower-case, FDF is case-insensitive key = line[0].lower() if key in ['tshs', 'tshs-file', 'hs', 'hs-file']: TSHS = line[1] elif key in [ 'replicate-a', 'rep-a', 'replicate-a1', 'rep-a1', 'bloch-a1' ]: NA1 = int(line[1]) elif key in [ 'replicate-b', 'rep-b', 'replicate-a2', 'rep-a2', 'bloch-a2' ]: NA2 = int(line[1]) elif key in [ 'replicate-c', 'rep-c', 'replicate-a3', 'rep-a3', 'bloch-a3' ]: NA3 = int(line[1]) elif key in ['replicate', 'rep', 'bloch']: # We have *at least* 2 integers NA1 = int(line[1]) NA2 = int(line[2]) NA3 = int(line[3]) elif key in ['semi-inf-direction', 'semi-inf-dir', 'semi-inf']: # This is lower-case checked axis = line[1][1:].lower() if 'a' == axis or 'a1' == axis: semiinf = 0 elif 'b' == axis or 'a2' == axis: semiinf = 1 elif 'c' == axis or 'a3' == axis: semiinf = 2 # Simple check of input, this may be overwritten later if semiinf != 2 and NA1 * NA2 * NA3 > 1: raise ValueError( ("Cannot provide semi-infinite directions " "other than A3-direction with repetition.")) if TSHS[0] != '/': # path is relative TSHS = opts.head + '/' + TSHS return TSHS, NA1, NA2, semiinf
def OptionsCheck(opts): """ Generic routine for adjusting most used options for routines. I.e. Inelastica/EigenChannels/pyTBT. """ import Inelastica.io.siesta as SIO import os import os.path as osp # Module name exe = opts.module # Destination directory if not osp.isdir(opts.DestDir): print('\n' + exe + ': Creating folder {0}'.format(opts.DestDir)) os.mkdir(opts.DestDir) if not osp.isfile(opts.fn): raise IOError("FDF-file not found: " + opts.fn) # Read SIESTA files opts.head = osp.split(opts.fn)[0] if opts.head == '': # set filepath if missing opts.head = '.' print(exe + ": Reading keywords from {0} \n".format(opts.fn)) opts.systemlabel = SIO.GetFDFlineWithDefault(opts.fn, 'SystemLabel', str, 'siesta', exe) opts.TSHS = '%s/%s.TSHS' % (opts.head, opts.systemlabel) # These first keys can be used, but they are superseeded by keys in the TS.Elec.<> block # Hence if they are read in first it will do it in correct order. if opts.UseBulk < 0: # Note NRP: # in principle this is now a per-electrode setting which # may be useful for certain systems... opts.UseBulk = SIO.GetFDFlineWithDefault(opts.fn, 'TS.UseBulkInElectrodes', bool, True, exe) opts.UseBulk = SIO.GetFDFlineWithDefault(opts.fn, 'TS.Elecs.Bulk', bool, opts.UseBulk, exe) def get_elec_vars(lr): # Look up old format first TSHS = SIO.GetFDFlineWithDefault(opts.fn, 'TS.HSFile' + lr, str, '', exe) NA1 = SIO.GetFDFlineWithDefault(opts.fn, 'TS.ReplicateA1' + lr, int, 1, exe) NA2 = SIO.GetFDFlineWithDefault(opts.fn, 'TS.ReplicateA2' + lr, int, 1, exe) # default semi-inf direction semiinf = 2 # Proceed looking up new format, which precedes belec = 'TS.Elec.' + lr print('Looking for new electrode format in: %%block {}'.format(belec)) # Default replication stuff TSHS = SIO.GetFDFlineWithDefault(opts.fn, belec + '.TSHS', str, TSHS, exe) NA1 = SIO.GetFDFlineWithDefault(opts.fn, belec + '.Bloch.A1', int, NA1, exe) NA2 = SIO.GetFDFlineWithDefault(opts.fn, belec + '.Bloch.A2', int, NA2, exe) NA3 = SIO.GetFDFlineWithDefault(opts.fn, belec + '.Bloch.A3', int, 1, exe) # Overwrite block block = SIO.GetFDFblock(opts.fn, KeyWord=belec) for line in block: print(line) # Lower-case, FDF is case-insensitive key = line[0].lower() if key in ['tshs', 'tshs-file', 'hs', 'hs-file']: TSHS = line[1] elif key in [ 'replicate-a', 'rep-a', 'replicate-a1', 'rep-a1', 'bloch-a1' ]: NA1 = int(line[1]) elif key in [ 'replicate-b', 'rep-b', 'replicate-a2', 'rep-a2', 'bloch-a2' ]: NA2 = int(line[1]) elif key in [ 'replicate-c', 'rep-c', 'replicate-a3', 'rep-a3', 'bloch-a3' ]: NA3 = int(line[1]) elif key in ['replicate', 'rep', 'bloch']: # We have *at least* 2 integers NA1 = int(line[1]) NA2 = int(line[2]) NA3 = int(line[3]) elif key in ['semi-inf-direction', 'semi-inf-dir', 'semi-inf']: # This is lower-case checked axis = line[1][1:].lower() if 'a' == axis or 'a1' == axis: semiinf = 0 elif 'b' == axis or 'a2' == axis: semiinf = 1 elif 'c' == axis or 'a3' == axis: semiinf = 2 # Simple check of input, this may be overwritten later if semiinf != 2 and NA1 * NA2 * NA3 > 1: raise ValueError( ("Cannot provide semi-infinite directions " "other than A3-direction with repetition.")) if TSHS[0] != '/': # path is relative TSHS = opts.head + '/' + TSHS return TSHS, NA1, NA2, semiinf # Look up electrode block block = SIO.GetFDFblock(opts.fn, KeyWord='TS.Elecs') if len(block) == 0: # Did not find the electrode block, defaults to old naming scheme opts.fnL, opts.NA1L, opts.NA2L, opts.semiinfL = get_elec_vars('Left') opts.fnR, opts.NA1R, opts.NA2R, opts.semiinfR = get_elec_vars('Right') elif len(block) == 2: # NB: The following assumes that the left electrode is the first in the block! opts.fnL, opts.NA1L, opts.NA2L, opts.semiinfL = get_elec_vars( block[0][0]) opts.fnR, opts.NA1R, opts.NA2R, opts.semiinfR = get_elec_vars( block[1][0]) else: print(block) raise IOError('Currently only two electrodes are supported') # Read in number of buffer atoms opts.buffer, L, R = SIO.GetBufferAtomsList(opts.TSHS, opts.fn) opts.bufferL = L opts.bufferR = R if 'maxBias' in opts.__dict__: # Bias range opts.maxBias = abs(opts.maxBias) opts.minBias = -abs(opts.maxBias) # Device region if opts.DeviceFirst <= 0: opts.DeviceFirst = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.PDOSFrom', int, 1, exe) opts.DeviceFirst -= L if opts.DeviceLast <= 0: opts.DeviceLast = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.PDOSTo', int, 1e10, exe) opts.DeviceLast -= L opts.NumberOfAtoms = SIO.GetFDFlineWithDefault(opts.fn, 'NumberOfAtoms', int, 1e10, exe) opts.NumberOfAtoms -= L + R if opts.DeviceLast < opts.DeviceFirst: print( exe + ' error: DeviceLast<DeviceFirst not allowed. Setting DeviceLast=DeviceFirst' ) opts.DeviceLast = opts.DeviceFirst opts.DeviceAtoms = [ max(opts.DeviceFirst, 1), min(opts.DeviceLast, opts.NumberOfAtoms) ] # Voltage opts.voltage = SIO.GetFDFlineWithDefault(opts.fn, 'TS.Voltage', float, 0.0, exe) ############# # Here comes some specifics related to different executables: ############# if "VfracL" in opts.__dict__: if opts.VfracL < 0.0 or opts.VfracL > 1.0: raise RuntimeError( 'Option VfracL must be a value in the range [0,1].') if "PhononNetCDF" in opts.__dict__: if not osp.isfile(opts.PhononNetCDF): raise IOError("Electron-phonon coupling NetCDF file not found: " + opts.PhononNetCDF) if "eta" in opts.__dict__: if opts.eta < 0: raise RuntimeError("eta must be a posivite number") if "etaLead" in opts.__dict__: if opts.etaLead < 0: raise RuntimeError("etaLead must be a posivite number") if "PhExtDamp" in opts.__dict__: if opts.PhExtDamp < 0: raise RuntimeError("PhExtDamp must be a posivite number") if "biasPoints" in opts.__dict__: if opts.biasPoints < 6: raise AssertionError("BiasPoints must be larger than 5") if "iSpin" in opts.__dict__: if not opts.iSpin in [0, 1]: raise AssertionError("Spin must be either 0 or 1") if "Emin" in opts.__dict__: if opts.Emin == 1e10: opts.Emin = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.Emin', float, 0.0, 'pyTBT') if "Emax" in opts.__dict__: if opts.Emax == 1e10: opts.Emax = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.Emax', float, 1.0, 'pyTBT') if "NPoints" in opts.__dict__: if opts.NPoints <= 0: opts.NPoints = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.NPoints', int, 1, 'pyTBT') # Create list of energies try: opts.Elist # Do not overwrite if some Elist is already specified except: if opts.NPoints == 1: opts.Elist = _np.array((opts.Emin, ), _np.float) else: # Linspace is just what we need opts.Elist = _np.linspace(opts.Emin, opts.Emax, opts.NPoints)
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