Example #1
0
class ExcitedState(GPAW):

    def __init__(self, lrtddft=None, index=0, d=0.001, txt=None,
                 parallel=0, communicator=None, name=None, restart=None):
        """ExcitedState object.
        parallel: Can be used to parallelize the numerical force calculation
        over images.
        """

        self.timer = Timer()
        self.atoms = None
        if isinstance(index, int):
            self.index = UnconstraintIndex(index)
        else:
            self.index = index

        self.results = {}
        self.results['forces'] = None
        self.results['energy'] = None
        if communicator is None:
            try:
                communicator = lrtddft.calculator.wfs.world
            except:
                communicator = mpi.world
        self.world = communicator

        if restart is not None:
            self.read(restart)
            if txt is None:
                self.txt = self.lrtddft.txt
            else:
                self.txt = convert_string_to_fd(txt, self.world)

        if lrtddft is not None:
            self.lrtddft = lrtddft
            self.calculator = self.lrtddft.calculator
            self.atoms = self.calculator.atoms
            self.parameters = self.calculator.parameters
            if txt is None:
                self.txt = self.lrtddft.txt
            else:
                self.txt = convert_string_to_fd(txt, self.world)

        self.d = d
        self.parallel = parallel
        self.name = name

        self.log = GPAWLogger(self.world)
        self.log.fd = self.txt
        self.reader = None
        self.calculator.log.fd = self.txt
        self.log('#', self.__class__.__name__, __version__)
        self.log('#', self.index)
        if name:
            self.log('name=' + name)
        self.log('# Force displacement:', self.d)
        self.log

    def __del__(self):
        self.timer.write(self.log.fd)

    def set(self, **kwargs):
        self.calculator.set(**kwargs)

    def set_positions(self, atoms):
        """Update the positions of the atoms."""

        self.atoms = atoms.copy()
        self.results['forces'] = None
        self.results['energy'] = None

    def write(self, filename, mode=''):

        try:
            os.makedirs(filename)
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise

        self.calculator.write(filename=filename + '/' + filename, mode=mode)
        self.lrtddft.write(filename=filename + '/' + filename + '.lr.dat.gz',
                           fh=None)

        f = open(filename + '/' + filename + '.exst', 'w')
        f.write('# ' + self.__class__.__name__ + __version__ + '\n')
        f.write('Displacement: {0}'.format(self.d) + '\n')
        f.write('Index: ' + self.index.__class__.__name__ + '\n')
        for k, v in self.index.__dict__.items():
            f.write('{0}, {1}'.format(k, v) + '\n')
        f.close()

        mpi.world.barrier()

    def read(self, filename):

        self.lrtddft = LrTDDFT(filename + '/' + filename + '.lr.dat.gz')
        self.atoms, self.calculator = restart(
            filename + '/' + filename, communicator=self.world)
        E0 = self.calculator.get_potential_energy()

        f = open(filename + '/' + filename + '.exst', 'r')
        f.readline()
        self.d = f.readline().replace('\n', '').split()[1]
        indextype = f.readline().replace('\n', '').split()[1]
        if indextype == 'UnconstraintIndex':
            iex = int(f.readline().replace('\n', '').split()[1])
            self.index = UnconstraintIndex(iex)
        else:
            direction = f.readline().replace('\n', '').split()[1]
            if direction in [str(0), str(1), str(2)]:
                direction = int(direction)
            else:
                direction = None

            val = f.readline().replace('\n', '').split()
            if indextype == 'MinimalOSIndex':

                self.index = MinimalOSIndex(float(val[1]), direction)
            else:
                emin = float(val[2])
                emax = float(val[3].replace(']', ''))
                self.index = MaximalOSIndex([emin, emax], direction)

        index = self.index.apply(self.lrtddft)
        self.results['energy'] = E0 + self.lrtddft[index].energy * Hartree
        self.lrtddft.set_calculator(self.calculator)

    def calculation_required(self, atoms, quantities):
        if len(quantities) == 0:
            return False

        if self.atoms is None:
            return True

        elif (len(atoms) != len(self.atoms) or
              (atoms.get_atomic_numbers() !=
               self.atoms.get_atomic_numbers()).any() or
              (atoms.get_initial_magnetic_moments() !=
               self.atoms.get_initial_magnetic_moments()).any() or
              (atoms.get_cell() != self.atoms.get_cell()).any() or
              (atoms.get_pbc() != self.atoms.get_pbc()).any()):
            return True
        elif (atoms.get_positions() !=
              self.atoms.get_positions()).any():
            return True

        for quantity in ['energy', 'forces']:
            if quantity in quantities:
                quantities.remove(quantity)
                if self.results[quantity] is None:
                    return True
        return len(quantities) > 0

    def check_state(self, atoms, tol=1e-15):
        system_changes = GPAW.check_state(self.calculator, atoms, tol)
        return system_changes

    def get_potential_energy(self, atoms=None, force_consistent=None):
        """Evaluate potential energy for the given excitation."""

        if atoms is None:
            atoms = self.atoms

        if self.calculation_required(atoms, ['energy']):
            self.results['energy'] = self.calculate(atoms)

        return self.results['energy']

    def calculate(self, atoms):
        """Evaluate your energy if needed."""
        self.set_positions(atoms)

        self.calculator.calculate(atoms)
        E0 = self.calculator.get_potential_energy()
        atoms.set_calculator(self)

        if hasattr(self, 'density'):
            del(self.density)
        self.lrtddft.forced_update()
        self.lrtddft.diagonalize()

        index = self.index.apply(self.lrtddft)

        energy = E0 + self.lrtddft[index].energy * Hartree

        self.log('--------------------------')
        self.log('Excited state')
        self.log(self.index)
        self.log('Energy:   {0}'.format(energy))
        self.log()

        return energy

    def get_forces(self, atoms=None, save=False):
        """Get finite-difference forces
        If save = True, restartfiles for every displacement are given
        """
        if atoms is None:
            atoms = self.atoms

        if self.calculation_required(atoms, ['forces']):
            atoms.set_calculator(self)

            # do the ground state calculation to set all
            # ranks to the same density to start with
            E0 = self.calculate(atoms)

            finite = FiniteDifference(
                atoms=atoms,
                propertyfunction=atoms.get_potential_energy,
                save=save,
                name="excited_state", ending='.gpw',
                d=self.d, parallel=self.parallel)
            F_av = finite.run()

            self.set_positions(atoms)
            self.results['energy'] = E0
            self.results['forces'] = F_av
            if self.txt:
                self.log('Excited state forces in eV/Ang:')
                symbols = self.atoms.get_chemical_symbols()
                for a, symbol in enumerate(symbols):
                    self.log(('%3d %-2s %10.5f %10.5f %10.5f' %
                              ((a, symbol) +
                               tuple(self.results['forces'][a]))))
        return self.results['forces']

    def forces_indexn(self, index):
        """ If restartfiles are created from the force calculation,
        this function allows the calculation of forces for every
        excited state index.
        """
        atoms = self.atoms

        def reforce(self, name):
            excalc = ExcitedState(index=index, restart=name)
            return excalc.get_potential_energy()

        fd = FiniteDifference(
            atoms=atoms, save=True,
            propertyfunction=self.atoms.get_potential_energy,
            name="excited_state", ending='.gpw',
            d=self.d, parallel=0)
        atoms.set_calculator(self)

        return fd.restart(reforce)

    def get_stress(self, atoms):
        """Return the stress for the current state of the Atoms."""
        raise NotImplementedError

    def initialize_density(self, method='dipole'):
        if hasattr(self, 'density') and self.density.method == method:
            return

        gsdensity = self.calculator.density
        lr = self.lrtddft
        self.density = ExcitedStateDensity(
            gsdensity.gd, gsdensity.finegd, lr.kss.npspins,
            gsdensity.charge,
            method=method, redistributor=gsdensity.redistributor)
        index = self.index.apply(self.lrtddft)
        self.density.initialize(self.lrtddft, index)
        self.density.update(self.calculator.wfs)

    def get_pseudo_density(self, **kwargs):
        """Return pseudo-density array."""
        method = kwargs.pop('method', 'dipole')
        self.initialize_density(method)
        return GPAW.get_pseudo_density(self, **kwargs)

    def get_all_electron_density(self, **kwargs):
        """Return all electron density array."""
        method = kwargs.pop('method', 'dipole')
        self.initialize_density(method)
        return GPAW.get_all_electron_density(self, **kwargs)