Ejemplo n.º 1
0
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()
Ejemplo n.º 2
0
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()
Ejemplo n.º 3
0
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()
Ejemplo n.º 4
0
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)