def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename) except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace('\n', '') self.name = s.split()[1] self.xc = f.readline().replace('\n', '').split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): l = f.readline().split() E = float(l[0]) me = [float(l[1]), float(l[2]), float(l[3])] self.append(LrTDDFTExcitation(e=E, m=me)) if fh is None: f.close() # update own variables self.istart = self.Om.fullkss.istart self.jend = self.Om.fullkss.jend
def forced_update(self): """Recalc yourself.""" if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' else: Om = ApmB name = 'LrTDDFThyb' self.kss = KSSingles(calculator=self.calculator, nspins=self.nspins, eps=self.eps, istart=self.istart, jend=self.jend, energy_range=self.energy_range, txt=self.txt) self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name
def update(self, calculator=None, nspins=None, eps=0.001, istart=0, jend=None, energy_range=None, xc=None, derivative_level=None, numscale=0.001): changed = False if self.calculator != calculator or \ self.nspins != nspins or \ self.eps != eps or \ self.istart != istart or \ self.jend != jend : changed = True if not changed: return self.calculator = calculator self.nspins = nspins self.eps = eps self.istart = istart self.jend = jend self.xc = xc self.derivative_level = derivative_level self.numscale = numscale self.kss = KSSingles(calculator=calculator, nspins=nspins, eps=eps, istart=istart, jend=jend, energy_range=energy_range, txt=self.txt) if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' else: Om = ApmB name = 'LrTDDFThyb' self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name
def singlets_triplets(self): """Split yourself into singlet and triplet transitions""" assert(self.fullkss.npspins == 2) assert(self.fullkss.nvspins == 1) # strip kss from down spins skss = KSSingles() tkss = KSSingles() map = [] for ij, ks in enumerate(self.fullkss): if ks.pspin == ks.spin: skss.append((ks + ks) / sqrt(2)) tkss.append((ks - ks) / sqrt(2)) map.append(ij) nkss = len(skss) # define the singlet and the triplet omega-matrixes sOm = OmegaMatrix(kss=skss) sOm.full = np.empty((nkss, nkss)) tOm = OmegaMatrix(kss=tkss) tOm.full = np.empty((nkss, nkss)) for ij in range(nkss): for kl in range(nkss): sOm.full[ij, kl] = (self.full[map[ij], map[kl]] + self.full[map[ij], nkss + map[kl]]) tOm.full[ij, kl] = (self.full[map[ij], map[kl]] - self.full[map[ij], nkss + map[kl]]) return sOm, tOm
def forced_update(self): """Recalc yourself.""" nonselfconsistent_xc = None if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' # nonselfconsistent_xc = HybridXC('PBE0', alpha=5.0) else: Om = ApmB name = 'LrTDDFThyb' self.kss = KSSingles(calculator=self.calculator, nspins=self.nspins, nonselfconsistent_xc=nonselfconsistent_xc, eps=self.eps, istart=self.istart, jend=self.jend, energy_range=self.energy_range, txt=self.txt) self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name
def get_map(self, istart=None, jend=None, energy_range=None): """Return the reduction map for the given requirements""" self.istart = istart self.jend = jend if istart is None and jend is None and energy_range is None: return None, self.fullkss # reduce the matrix print('# diagonalize: %d transitions original' % len(self.fullkss), file=self.txt) if energy_range is None: if istart is None: istart = self.kss.istart if self.fullkss.istart > istart: raise RuntimeError('istart=%d has to be >= %d' % (istart, self.kss.istart)) if jend is None: jend = self.kss.jend if self.fullkss.jend < jend: raise RuntimeError('jend=%d has to be <= %d' % (jend, self.kss.jend)) else: try: emin, emax = energy_range except: emax = energy_range emin = 0. emin /= Hartree emax /= Hartree map = [] kss = KSSingles() for ij, k in zip(range(len(self.fullkss)), self.fullkss): if energy_range is None: if k.i >= istart and k.j <= jend: kss.append(k) map.append(ij) else: if k.energy >= emin and k.energy < emax: kss.append(k) map.append(ij) kss.update() print('# diagonalize: %d transitions now' % len(kss), file=self.txt) return map, kss
def get_map(self, istart=None, jend=None, energy_range=None): """Return the reduction map for the given requirements""" self.istart = istart self.jend = jend if istart is None and jend is None and energy_range is None: return None, self.fullkss # reduce the matrix print >> self.txt,'# diagonalize: %d transitions original'\ % len(self.fullkss) if energy_range is None: if istart is None: istart = self.kss.istart if self.fullkss.istart > istart: raise RuntimeError('istart=%d has to be >= %d' % (istart, self.kss.istart)) if jend is None: jend = self.kss.jend if self.fullkss.jend < jend: raise RuntimeError('jend=%d has to be <= %d' % (jend, self.kss.jend)) else: try: emin, emax = energy_range except: emax = energy_range emin = 0. emin /= Hartree emax /= Hartree map= [] kss = KSSingles() for ij, k in zip(range(len(self.fullkss)), self.fullkss): if energy_range is None: if k.i >= istart and k.j <= jend: kss.append(k) map.append(ij) else: if k.energy >= emin and k.energy < emax: kss.append(k) map.append(ij) kss.update() print >> self.txt, '# diagonalize: %d transitions now' % len(kss) return map, kss
class LrTDDFT(ExcitationList): """Linear Response TDDFT excitation class Input parameters: calculator: the calculator object after a ground state calculation nspins: number of spins considered in the calculation Note: Valid only for unpolarised ground state calculation eps: Minimal occupation difference for a transition (default 0.001) istart: First occupied state to consider jend: Last unoccupied state to consider xc: Exchange-Correlation approximation in the Kernel derivative_level: 0: use Exc, 1: use vxc, 2: use fxc if available filename: read from a file """ def __init__( self, calculator=None, nspins=None, eps=0.001, istart=0, jend=None, energy_range=None, xc=None, derivative_level=1, numscale=0.00001, txt=None, filename=None, finegrid=2, force_ApmB=False, # for tests eh_comm=None # parallelization over eh-pairs ): self.nspins = None self.istart = None self.jend = None if isinstance(calculator, str): ExcitationList.__init__(self, None, txt) return self.read(calculator) else: ExcitationList.__init__(self, calculator, txt) if filename is not None: return self.read(filename) self.filename = None self.calculator = None self.eps = None self.xc = None self.derivative_level = None self.numscale = numscale self.finegrid = finegrid self.force_ApmB = force_ApmB if eh_comm is None: eh_comm = mpi.serial_comm elif isinstance(eh_comm, (mpi.world.__class__, mpi.serial_comm.__class__)): # Correct type already. pass else: # world should be a list of ranks: eh_comm = mpi.world.new_communicator(np.asarray(eh_comm)) self.eh_comm = eh_comm if calculator is not None: calculator.converge_wave_functions() if calculator.density.nct_G is None: calculator.set_positions() self.update(calculator, nspins, eps, istart, jend, energy_range, xc, derivative_level, numscale) def analyse(self, what=None, out=None, min=0.1): """Print info about the transitions. Parameters: 1. what: I list of excitation indicees, None means all 2. out : I where to send the output, None means sys.stdout 3. min : I minimal contribution to list (0<min<1) """ if what is None: what = range(len(self)) elif isinstance(what, int): what = [what] if out is None: out = sys.stdout for i in what: print >> out, str(i) + ':', self[i].analyse(min=min) def update(self, calculator=None, nspins=None, eps=0.001, istart=0, jend=None, energy_range=None, xc=None, derivative_level=None, numscale=0.001): changed = False if self.calculator != calculator or \ self.nspins != nspins or \ self.eps != eps or \ self.istart != istart or \ self.jend != jend : changed = True if not changed: return self.calculator = calculator self.nspins = nspins self.eps = eps self.istart = istart self.jend = jend self.xc = xc self.derivative_level = derivative_level self.numscale = numscale self.kss = KSSingles(calculator=calculator, nspins=nspins, eps=eps, istart=istart, jend=jend, energy_range=energy_range, txt=self.txt) if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' else: Om = ApmB name = 'LrTDDFThyb' self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name ## self.diagonalize() def diagonalize(self, istart=None, jend=None, energy_range=None): self.istart = istart self.jend = jend self.Om.diagonalize(istart, jend, energy_range) # remove old stuff while len(self): self.pop() for j in range(len(self.Om.kss)): self.append(LrTDDFTExcitation(self.Om, j)) def get_Om(self): return self.Om def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename) except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace('\n', '') self.name = s.split()[1] self.xc = f.readline().replace('\n', '').split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): l = f.readline().split() E = float(l[0]) me = [float(l[1]), float(l[2]), float(l[3])] self.append(LrTDDFTExcitation(e=E, m=me)) if fh is None: f.close() # update own variables self.istart = self.Om.fullkss.istart self.jend = self.Om.fullkss.jend def singlets_triplets(self): """Split yourself into a singlet and triplet object""" slr = LrTDDFT(None, self.nspins, self.eps, self.istart, self.jend, self.xc, self.derivative_level, self.numscale) tlr = LrTDDFT(None, self.nspins, self.eps, self.istart, self.jend, self.xc, self.derivative_level, self.numscale) slr.Om, tlr.Om = self.Om.singlets_triplets() for lr in [slr, tlr]: lr.kss = lr.Om.fullkss return slr, tlr def single_pole_approximation(self, i, j): """Return the excitation according to the single pole approximation. See e.g.: Grabo et al, Theochem 501 (2000) 353-367 """ for ij, kss in enumerate(self.kss): if kss.i == i and kss.j == j: return sqrt(self.Om.full[ij][ij]) * Hartree return self.Om.full[ij][ij] / kss.energy * Hartree def __str__(self): string = ExcitationList.__str__(self) string += '# derived from:\n' string += self.kss.__str__() return string def write(self, filename=None, fh=None): """Write current state to a file. 'filename' is the filename. If the filename ends in .gz, the file is automatically saved in compressed gzip format. 'fh' is a filehandle. This can be used to write into already opened files. """ if mpi.rank == mpi.MASTER: if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename, 'wb') except: f = open(filename, 'w') else: f = open(filename, 'w') else: f = fh f.write('# ' + self.name + '\n') xc = self.xc if xc is None: xc = 'RPA' if self.calculator is not None: xc += ' ' + self.calculator.get_xc_functional() f.write(xc + '\n') f.write('%g %d %g %d' % (self.eps, int(self.derivative_level), self.numscale, int(self.finegrid)) + '\n') self.kss.write(fh=f) self.Om.write(fh=f) if len(self): f.write('# Eigenvalues\n') istart = self.istart if istart is None: istart = self.kss.istart jend = self.jend if jend is None: jend = self.kss.jend f.write('%d %d %d' % (len(self), istart, jend) + '\n') for ex in self: f.write(ex.outstring()) f.write('# Eigenvectors\n') for ex in self: for w in ex.f: f.write('%g ' % w) f.write('\n') if fh is None: f.close()
], cell=(a, a, c)) calc = GPAW(xc=xc, nbands=3, spinpol=False, eigensolver='rmm-diis', txt=txt) H2.set_calculator(calc) H2.get_potential_energy() gsname = exname = 'rraman' rr = ResonantRaman(H2, KSSingles, gsname=gsname, exname=exname, exkwargs={ 'eps': 0.0, 'jend': 1 }) rr.run() # check size kss = KSSingles('rraman-d0.010.eq.ex.gz') assert (len(kss) == 1) rr = ResonantRaman( H2, KSSingles, gsname=gsname, exname=exname, verbose=True, ) rr.summary(omega=5, method='frederiksen')
nbands=4, kpts=(1, 2, 2), mode=mode, eigensolver=eigensolver, txt=txt) else: name = 'zero bc' calc = GPAW(h=0.25, nbands=4, mode=mode, eigensolver=eigensolver, txt=txt) Be.set_calculator(calc) Be.get_potential_energy() kss = KSSingles(calc) # all s->p transitions at the same energy [Ha] and # oscillator_strength for ks in kss: equal(ks.get_energy(), kss[0].get_energy(), 1.e-4) equal(ks.get_oscillator_strength()[0], kss[0].get_oscillator_strength()[0], 1.e-3) energy[name] = np.array([ks.get_energy() * Hartree for ks in kss]).mean() osz[name] = np.array([ks.get_oscillator_strength()[0] for ks in kss]).sum() parprint(kss) # I/O kss.write('kss.dat')
def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename) except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace('\n','') self.name = s.split()[1] self.xc = f.readline().replace('\n','').split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): l = f.readline().split() E = float(l[0]) me = [float(l[1]), float(l[2]), float(l[3])] self.append(LrTDDFTExcitation(e=E, m=me)) if fh is None: f.close() # update own variables self.istart = self.Om.fullkss.istart self.jend = self.Om.fullkss.jend
kpts=(1, 2, 2), mode=mode, symmetry='off', eigensolver=eigensolver, txt=txt) else: name = 'zero_bc' calc = GPAW(h=0.25, nbands=4, mode=mode, eigensolver=eigensolver, txt=txt) Be.set_calculator(calc) Be.get_potential_energy() kss = KSSingles(calc, eps=0.9) # all s->p transitions at the same energy [Ha] and # oscillator_strength for ks in kss: equal(ks.get_energy(), kss[0].get_energy(), 5.e-3) equal(ks.get_oscillator_strength()[0], kss[0].get_oscillator_strength()[0], 5.e-3) equal(ks.get_oscillator_strength()[0], ks.get_oscillator_strength()[1:].sum() / 3, 1.e-15) for c in range(3): equal(ks.get_oscillator_strength()[1 + c], ks.get_dipole_tensor()[c, c], 1.e-15) energy[name] = np.array([ks.get_energy() * Hartree for ks in kss]).mean() osz[name] = np.array([ks.get_oscillator_strength()[0] for ks in kss]).sum()
energy = {} osz = {} for pbc in [False, True]: Be.set_pbc(pbc) if pbc: name = 'periodic' calc = GPAW(h=0.25, nbands=4, kpts=(1,2,2), mode=mode, eigensolver=eigensolver, txt=txt) else: name = 'zero bc' calc = GPAW(h=0.25, nbands=4, mode=mode, eigensolver=eigensolver, txt=txt) Be.set_calculator(calc) Be.get_potential_energy() kss = KSSingles(calc) # all s->p transitions at the same energy [Ha] and # oscillator_strength for ks in kss: equal(ks.get_energy(), kss[0].get_energy(), 1.e-4) equal(ks.get_oscillator_strength()[0], kss[0].get_oscillator_strength()[0], 1.e-3) energy[name] = np.array( [ks.get_energy() * Hartree for ks in kss]).mean() osz[name] = np.array( [ks.get_oscillator_strength()[0] for ks in kss]).sum() parprint(kss) # I/O kss.write('kss.dat')
class LrTDDFT(ExcitationList): """Linear Response TDDFT excitation class Input parameters: calculator: the calculator object after a ground state calculation nspins: number of spins considered in the calculation Note: Valid only for unpolarised ground state calculation eps: Minimal occupation difference for a transition (default 0.001) istart: First occupied state to consider jend: Last unoccupied state to consider xc: Exchange-Correlation approximation in the Kernel derivative_level: 0: use Exc, 1: use vxc, 2: use fxc if available filename: read from a file """ def __init__(self, calculator=None, **kwargs): self.timer = Timer() self.set(**kwargs) if isinstance(calculator, str): ExcitationList.__init__(self, None, self.txt) self.filename = calculator else: ExcitationList.__init__(self, calculator, self.txt) if self.filename is not None: return self.read(self.filename) if self.eh_comm is None: self.eh_comm = mpi.serial_comm elif isinstance(self.eh_comm, (mpi.world.__class__, mpi.serial_comm.__class__)): # Correct type already. pass else: # world should be a list of ranks: self.eh_comm = mpi.world.new_communicator(np.asarray(eh_comm)) if calculator is not None and calculator.initialized: if calculator.wfs.kpt_comm.size > 1: err_txt = "Spin parallelization with Linear response " err_txt += "TDDFT. Use parallel = {'domain' : 'domain_only'} " err_txt += "calculator parameter." raise NotImplementedError(err_txt) if self.xc == 'GS': self.xc = calculator.hamiltonian.xc.name calculator.converge_wave_functions() if calculator.density.nct_G is None: spos_ac = calculator.initialize_positions() calculator.wfs.initialize(calculator.density, calculator.hamiltonian, spos_ac) self.update(calculator) def set(self, **kwargs): defaults = { 'nspins' : None, 'eps' : 0.001, 'istart' : 0, 'jend' : None, 'energy_range' : None, 'xc' : 'GS', 'derivative_level' : 1, 'numscale' : 0.00001, 'txt' : None, 'filename' : None, 'finegrid' : 2, 'force_ApmB' : False, # for tests 'eh_comm' : None # parallelization over eh-pairs } changed = False for key, value in defaults.items(): if hasattr(self, key): value = getattr(self, key) # do not overwrite setattr(self, key, kwargs.pop(key, value)) if value != getattr(self, key): changed = True for key in kwargs: raise KeyError('Unknown key ' + key) return changed def set_calculator(self, calculator): self.calculator = calculator # self.force_ApmB = parameters['force_ApmB'] self.force_ApmB = None # XXX def analyse(self, what=None, out=None, min=0.1): """Print info about the transitions. Parameters: 1. what: I list of excitation indicees, None means all 2. out : I where to send the output, None means sys.stdout 3. min : I minimal contribution to list (0<min<1) """ if what is None: what = range(len(self)) elif isinstance(what, int): what = [what] if out is None: out = sys.stdout for i in what: print >> out, str(i) + ':', self[i].analyse(min=min) def update(self, calculator=None, **kwargs): changed = self.set(**kwargs) if calculator is not None: changed = True self.set_calculator(calculator) if not changed: return self.forced_update() def forced_update(self): """Recalc yourself.""" nonselfconsistent_xc = None if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' # nonselfconsistent_xc = HybridXC('PBE0', alpha=5.0) else: Om = ApmB name = 'LrTDDFThyb' self.kss = KSSingles(calculator=self.calculator, nspins=self.nspins, nonselfconsistent_xc=nonselfconsistent_xc, eps=self.eps, istart=self.istart, jend=self.jend, energy_range=self.energy_range, txt=self.txt) self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name def diagonalize(self, istart=None, jend=None, energy_range=None, TDA=False): self.timer.start('diagonalize') self.timer.start('omega') self.Om.diagonalize(istart, jend, energy_range, TDA) self.timer.stop('omega') # remove old stuff self.timer.start('clean') while len(self): self.pop() self.timer.stop('clean') print >> self.txt, 'LrTDDFT digonalized:' self.timer.start('build') for j in range(len(self.Om.kss)): self.append(LrTDDFTExcitation(self.Om, j)) print >> self.txt, ' ', str(self[-1]) self.timer.stop('build') self.timer.stop('diagonalize') def get_Om(self): return self.Om def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename) except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace('\n','') self.name = s.split()[1] self.xc = f.readline().replace('\n','').split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): self.append(LrTDDFTExcitation(string=f.readline())) # load the eigenvectors f.readline() for i in range(n): values = f.readline().split() weights = [float(val) for val in values] self[i].f = np.array(weights) self[i].kss = self.kss if fh is None: f.close() # update own variables self.istart = self.Om.fullkss.istart self.jend = self.Om.fullkss.jend def singlets_triplets(self): """Split yourself into a singlet and triplet object""" slr = LrTDDFT(None, nspins=self.nspins, eps=self.eps, istart=self.istart, jend=self.jend, xc=self.xc, derivative_level=self.derivative_level, numscale=self.numscale) tlr = LrTDDFT(None, nspins=self.nspins, eps=self.eps, istart=self.istart, jend=self.jend, xc=self.xc, derivative_level=self.derivative_level, numscale=self.numscale) slr.Om, tlr.Om = self.Om.singlets_triplets() for lr in [slr, tlr]: lr.kss = lr.Om.fullkss return slr, tlr def single_pole_approximation(self, i, j): """Return the excitation according to the single pole approximation. See e.g.: Grabo et al, Theochem 501 (2000) 353-367 """ for ij, kss in enumerate(self.kss): if kss.i == i and kss.j == j: return sqrt(self.Om.full[ij][ij]) * Hartree return self.Om.full[ij][ij] / kss.energy * Hartree def __str__(self): string = ExcitationList.__str__(self) string += '# derived from:\n' string += self.Om.kss.__str__() return string def write(self, filename=None, fh=None): """Write current state to a file. 'filename' is the filename. If the filename ends in .gz, the file is automatically saved in compressed gzip format. 'fh' is a filehandle. This can be used to write into already opened files. """ if mpi.rank == mpi.MASTER: if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename,'wb') except: f = open(filename, 'w') else: f = open(filename, 'w') else: f = fh f.write('# ' + self.name + '\n') xc = self.xc if xc is None: xc = 'RPA' if self.calculator is not None: xc += ' ' + self.calculator.get_xc_functional() f.write(xc + '\n') f.write('%g %d %g %d' % (self.eps, int(self.derivative_level), self.numscale, int(self.finegrid)) + '\n') self.kss.write(fh=f) self.Om.write(fh=f) if len(self): f.write('# Eigenvalues\n') istart = self.istart if istart is None: istart = self.kss.istart jend = self.jend if jend is None: jend = self.kss.jend f.write('%d %d %d'%(len(self), istart, jend) + '\n') for ex in self: f.write(ex.outstring()) f.write('# Eigenvectors\n') for ex in self: for w in ex.f: f.write('%g '%w) f.write('\n') if fh is None: f.close()
def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename, 'rt') except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace('\n', '') self.name = s.split()[1] self.xc = f.readline().replace('\n', '').split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: self.diagonalized = True # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): self.append(LrTDDFTExcitation(string=f.readline())) # load the eigenvectors f.readline() for i in range(n): values = f.readline().split() weights = [float(val) for val in values] self[i].f = np.array(weights) self[i].kss = self.kss if fh is None: f.close()
class LrTDDFT(ExcitationList): """Linear Response TDDFT excitation class Input parameters: calculator: the calculator object after a ground state calculation nspins: number of spins considered in the calculation Note: Valid only for unpolarised ground state calculation eps: Minimal occupation difference for a transition (default 0.001) istart: First occupied state to consider jend: Last unoccupied state to consider xc: Exchange-Correlation approximation in the Kernel derivative_level: 0: use Exc, 1: use vxc, 2: use fxc if available filename: read from a file """ default_parameters = { 'nspins': None, 'eps': 0.001, 'istart': 0, 'jend': sys.maxsize, 'energy_range': None, 'xc': 'GS', 'derivative_level': 1, 'numscale': 0.00001, 'txt': None, 'filename': None, 'finegrid': 2, 'force_ApmB': False, # for tests 'eh_comm': None} # parallelization over eh-pairs def __init__(self, calculator=None, **kwargs): self.timer = Timer() self.diagonalized = False changed = self.set(**kwargs) if isinstance(calculator, str): ExcitationList.__init__(self, None, self.txt) self.filename = calculator else: ExcitationList.__init__(self, calculator, self.txt) if self.filename is not None: self.read(self.filename) if set(['istart', 'jend', 'energy_range']) & set(changed): # the user has explicitely demanded these self.diagonalize() return if self.eh_comm is None: self.eh_comm = mpi.serial_comm elif isinstance(self.eh_comm, (mpi.world.__class__, mpi.serial_comm.__class__)): # Correct type already. pass else: # world should be a list of ranks: self.eh_comm = mpi.world.new_communicator( np.asarray(self.eh_comm)) if calculator is not None and calculator.initialized: if not isinstance(calculator.wfs, FDWaveFunctions): raise RuntimeError( 'Linear response TDDFT supported only in real space mode') if calculator.wfs.kd.comm.size > 1: err_txt = 'Spin parallelization with Linear response ' err_txt += "TDDFT. Use parallel={'domain': world.size} " err_txt += 'calculator parameter.' raise NotImplementedError(err_txt) if self.xc == 'GS': self.xc = calculator.hamiltonian.xc.name if calculator.parameters.mode != 'lcao': calculator.converge_wave_functions() if calculator.density.nct_G is None: spos_ac = calculator.initialize_positions() calculator.wfs.initialize(calculator.density, calculator.hamiltonian, spos_ac) self.update(calculator) def set(self, **kwargs): """Change parameters.""" changed = [] for key, value in LrTDDFT.default_parameters.items(): if hasattr(self, key): value = getattr(self, key) # do not overwrite setattr(self, key, kwargs.pop(key, value)) if value != getattr(self, key): changed.append(key) for key in kwargs: raise KeyError('Unknown key ' + key) return changed def set_calculator(self, calculator): self.calculator = calculator # self.force_ApmB = parameters['force_ApmB'] self.force_ApmB = None # XXX def analyse(self, what=None, out=None, min=0.1): """Print info about the transitions. Parameters: 1. what: I list of excitation indicees, None means all 2. out : I where to send the output, None means sys.stdout 3. min : I minimal contribution to list (0<min<1) """ if what is None: what = range(len(self)) elif isinstance(what, numbers.Integral): what = [what] if out is None: out = sys.stdout for i in what: print(str(i) + ':', self[i].analyse(min=min), file=out) def update(self, calculator=None, **kwargs): changed = self.set(**kwargs) if calculator is not None: changed = True self.set_calculator(calculator) if not changed: return self.forced_update() def forced_update(self): """Recalc yourself.""" if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' else: Om = ApmB name = 'LrTDDFThyb' self.kss = KSSingles(calculator=self.calculator, nspins=self.nspins, eps=self.eps, istart=self.istart, jend=self.jend, energy_range=self.energy_range, txt=self.txt) self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name def diagonalize(self, **kwargs): self.set(**kwargs) self.timer.start('diagonalize') self.timer.start('omega') self.Om.diagonalize(self.istart, self.jend, self.energy_range) self.timer.stop('omega') self.diagonalized = True # remove old stuff self.timer.start('clean') while len(self): self.pop() self.timer.stop('clean') print('LrTDDFT digonalized:', file=self.txt) self.timer.start('build') for j in range(len(self.Om.kss)): self.append(LrTDDFTExcitation(self.Om, j)) print(' ', str(self[-1]), file=self.txt) self.timer.stop('build') self.timer.stop('diagonalize') def get_Om(self): return self.Om def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename, 'rt') except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace('\n', '') self.name = s.split()[1] self.xc = f.readline().replace('\n', '').split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: self.diagonalized = True # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): self.append(LrTDDFTExcitation(string=f.readline())) # load the eigenvectors f.readline() for i in range(n): values = f.readline().split() weights = [float(val) for val in values] self[i].f = np.array(weights) self[i].kss = self.kss if fh is None: f.close() def singlets_triplets(self): """Split yourself into a singlet and triplet object""" slr = LrTDDFT(None, nspins=self.nspins, eps=self.eps, istart=self.istart, jend=self.jend, xc=self.xc, derivative_level=self.derivative_level, numscale=self.numscale) tlr = LrTDDFT(None, nspins=self.nspins, eps=self.eps, istart=self.istart, jend=self.jend, xc=self.xc, derivative_level=self.derivative_level, numscale=self.numscale) slr.Om, tlr.Om = self.Om.singlets_triplets() for lr in [slr, tlr]: lr.kss = lr.Om.fullkss return slr, tlr def single_pole_approximation(self, i, j): """Return the excitation according to the single pole approximation. See e.g.: Grabo et al, Theochem 501 (2000) 353-367 """ for ij, kss in enumerate(self.kss): if kss.i == i and kss.j == j: return sqrt(self.Om.full[ij][ij]) * Hartree return self.Om.full[ij][ij] / kss.energy * Hartree def __str__(self): string = ExcitationList.__str__(self) string += '# derived from:\n' string += self.Om.kss.__str__() return string def write(self, filename=None, fh=None): """Write current state to a file. 'filename' is the filename. If the filename ends in .gz, the file is automatically saved in compressed gzip format. 'fh' is a filehandle. This can be used to write into already opened files. """ if self.calculator is None: rank = mpi.world.rank else: rank = self.calculator.wfs.world.rank if rank == 0: if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename, 'wt') except: f = open(filename, 'w') else: f = open(filename, 'w') else: f = fh f.write('# ' + self.name + '\n') xc = self.xc if xc is None: xc = 'RPA' if self.calculator is not None: xc += ' ' + self.calculator.get_xc_functional() f.write(xc + '\n') f.write('%g %d %g %d' % (self.eps, int(self.derivative_level), self.numscale, int(self.finegrid)) + '\n') self.kss.write(fh=f) self.Om.write(fh=f) if len(self): f.write('# Eigenvalues\n') istart = self.istart if istart is None: istart = self.kss.istart jend = self.jend if jend is None: jend = self.kss.jend f.write('%d %d %d' % (len(self), istart, jend) + '\n') for ex in self: f.write(ex.outstring()) f.write('# Eigenvectors\n') for ex in self: for w in ex.f: f.write('%g ' % w) f.write('\n') if fh is None: f.close() mpi.world.barrier() def __getitem__(self, i): if not self.diagonalized: self.diagonalize() return list.__getitem__(self, i) def __iter__(self): if not self.diagonalized: self.diagonalize() return list.__iter__(self) def __len__(self): if not self.diagonalized: self.diagonalize() return list.__len__(self)
def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith(".gz"): try: import gzip f = gzip.open(filename) except: f = open(filename, "r") else: f = open(filename, "r") self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace("\n", "") self.name = s.split()[1] self.xc = f.readline().replace("\n", "").split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == "LrTDDFT": self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != "# Eigenvalues\n": # go back to previous position f.seek(p) else: # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): self.append(LrTDDFTExcitation(string=f.readline())) # load the eigenvectors f.readline() for i in range(n): values = f.readline().split() weights = [float(val) for val in values] self[i].f = np.array(weights) self[i].kss = self.kss if fh is None: f.close() # update own variables self.istart = self.Om.fullkss.istart self.jend = self.Om.fullkss.jend
class LrTDDFT(ExcitationList): """Linear Response TDDFT excitation class Input parameters: calculator: the calculator object after a ground state calculation nspins: number of spins considered in the calculation Note: Valid only for unpolarised ground state calculation eps: Minimal occupation difference for a transition (default 0.001) istart: First occupied state to consider jend: Last unoccupied state to consider xc: Exchange-Correlation approximation in the Kernel derivative_level: 0: use Exc, 1: use vxc, 2: use fxc if available filename: read from a file """ def __init__(self, calculator=None, nspins=None, eps=0.001, istart=0, jend=None, energy_range=None, xc=None, derivative_level=1, numscale=0.00001, txt=None, filename=None, finegrid=2, force_ApmB=False, # for tests eh_comm=None # parallelization over eh-pairs ): self.nspins = None self.istart = None self.jend = None if isinstance(calculator, str): ExcitationList.__init__(self, None, txt) return self.read(calculator) else: ExcitationList.__init__(self, calculator, txt) if filename is not None: return self.read(filename) self.filename = None self.calculator = None self.eps = None self.xc = None self.derivative_level = None self.numscale = numscale self.finegrid = finegrid self.force_ApmB = force_ApmB if eh_comm is None: eh_comm = mpi.serial_comm elif isinstance(eh_comm, (mpi.world.__class__, mpi.serial_comm.__class__)): # Correct type already. pass else: # world should be a list of ranks: eh_comm = mpi.world.new_communicator(np.asarray(eh_comm)) self.eh_comm = eh_comm if calculator is not None: calculator.converge_wave_functions() if calculator.density.nct_G is None: calculator.set_positions() self.update(calculator, nspins, eps, istart, jend, energy_range, xc, derivative_level, numscale) def analyse(self, what=None, out=None, min=0.1): """Print info about the transitions. Parameters: 1. what: I list of excitation indicees, None means all 2. out : I where to send the output, None means sys.stdout 3. min : I minimal contribution to list (0<min<1) """ if what is None: what = range(len(self)) elif isinstance(what, int): what = [what] if out is None: out = sys.stdout for i in what: print >> out, str(i) + ':', self[i].analyse(min=min) def update(self, calculator=None, nspins=None, eps=0.001, istart=0, jend=None, energy_range=None, xc=None, derivative_level=None, numscale=0.001): changed = False if self.calculator != calculator or \ self.nspins != nspins or \ self.eps != eps or \ self.istart != istart or \ self.jend != jend : changed = True if not changed: return self.calculator = calculator self.nspins = nspins self.eps = eps self.istart = istart self.jend = jend self.xc = xc self.derivative_level = derivative_level self.numscale = numscale self.kss = KSSingles(calculator=calculator, nspins=nspins, eps=eps, istart=istart, jend=jend, energy_range=energy_range, txt=self.txt) if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' else: Om = ApmB name = 'LrTDDFThyb' self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name ## self.diagonalize() def diagonalize(self, istart=None, jend=None, energy_range=None): self.istart = istart self.jend = jend self.Om.diagonalize(istart, jend, energy_range) # remove old stuff while len(self): self.pop() for j in range(len(self.Om.kss)): self.append(LrTDDFTExcitation(self.Om,j)) def get_Om(self): return self.Om def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename) except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None # get my name s = f.readline().replace('\n','') self.name = s.split()[1] self.xc = f.readline().replace('\n','').split()[0] values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 self.kss = KSSingles(filehandle=f) if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.Kss(self.kss) # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): l = f.readline().split() E = float(l[0]) me = [float(l[1]), float(l[2]), float(l[3])] self.append(LrTDDFTExcitation(e=E, m=me)) if fh is None: f.close() # update own variables self.istart = self.Om.fullkss.istart self.jend = self.Om.fullkss.jend def singlets_triplets(self): """Split yourself into a singlet and triplet object""" slr = LrTDDFT(None, self.nspins, self.eps, self.istart, self.jend, self.xc, self.derivative_level, self.numscale) tlr = LrTDDFT(None, self.nspins, self.eps, self.istart, self.jend, self.xc, self.derivative_level, self.numscale) slr.Om, tlr.Om = self.Om.singlets_triplets() for lr in [slr, tlr]: lr.kss = lr.Om.fullkss return slr, tlr def single_pole_approximation(self, i, j): """Return the excitation according to the single pole approximation. See e.g.: Grabo et al, Theochem 501 (2000) 353-367 """ for ij, kss in enumerate(self.kss): if kss.i == i and kss.j == j: return sqrt(self.Om.full[ij][ij]) * Hartree return self.Om.full[ij][ij] / kss.energy * Hartree def __str__(self): string = ExcitationList.__str__(self) string += '# derived from:\n' string += self.kss.__str__() return string def write(self, filename=None, fh=None): """Write current state to a file. 'filename' is the filename. If the filename ends in .gz, the file is automatically saved in compressed gzip format. 'fh' is a filehandle. This can be used to write into already opened files. """ if mpi.rank == mpi.MASTER: if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename,'wb') except: f = open(filename, 'w') else: f = open(filename, 'w') else: f = fh f.write('# ' + self.name + '\n') xc = self.xc if xc is None: xc = 'RPA' if self.calculator is not None: xc += ' ' + self.calculator.get_xc_functional() f.write(xc + '\n') f.write('%g %d %g %d' % (self.eps, int(self.derivative_level), self.numscale, int(self.finegrid)) + '\n') self.kss.write(fh=f) self.Om.write(fh=f) if len(self): f.write('# Eigenvalues\n') istart = self.istart if istart is None: istart = self.kss.istart jend = self.jend if jend is None: jend = self.kss.jend f.write('%d %d %d'%(len(self), istart, jend) + '\n') for ex in self: f.write(ex.outstring()) f.write('# Eigenvectors\n') for ex in self: for w in ex.f: f.write('%g '%w) f.write('\n') if fh is None: f.close()
def read(self, filename=None, fh=None): """Read myself from a file""" timer = self.timer timer.start('name') if fh is None: if filename.endswith('.gz'): try: import gzip f = gzip.open(filename, 'rt') except: f = open(filename, 'r') else: f = open(filename, 'r') self.filename = filename else: f = fh self.filename = None timer.stop('name') timer.start('header') # get my name s = f.readline().strip() self.name = s.split()[1] self.xc = XC(f.readline().strip().split()[0]) values = f.readline().split() self.eps = float(values[0]) if len(values) > 1: self.derivative_level = int(values[1]) self.numscale = float(values[2]) self.finegrid = int(values[3]) else: # old writing style, use old defaults self.numscale = 0.001 timer.stop('header') timer.start('init_kss') self.kss = KSSingles(filehandle=f) timer.stop('init_kss') timer.start('init_obj') if self.name == 'LrTDDFT': self.Om = OmegaMatrix(kss=self.kss, filehandle=f, txt=self.txt) else: self.Om = ApmB(kss=self.kss, filehandle=f, txt=self.txt) self.Om.fullkss = self.kss timer.stop('init_obj') timer.start('read diagonalized') # check if already diagonalized p = f.tell() s = f.readline() if s != '# Eigenvalues\n': # go back to previous position f.seek(p) else: self.diagonalized = True # load the eigenvalues n = int(f.readline().split()[0]) for i in range(n): self.append(LrTDDFTExcitation(string=f.readline())) # load the eigenvectors timer.start('read eigenvectors') f.readline() for i in range(n): self[i].f = np.array([float(x) for x in f.readline().split()]) self[i].kss = self.kss timer.stop('read eigenvectors') timer.stop('read diagonalized') if fh is None: f.close()