Beispiel #1
0
def convergence_error_commutator(ham, lf, overlap, *dms):
    '''Compute the commutator error

       **Arguments:**

       ham
            A Hamiltonian instance.

       lf
            The linalg factory to be used.

       overlap
            The overlap operator.

       dm1, dm2, ...
            A list of density matrices. The numbers of dms must match ham.ndm.

       **Returns:** The commutator error. This measure (not this function) is
       also used in some SCF algorithms to check for convergence.
    '''
    if len(dms) != ham.ndm:
        raise TypeError('Expecting %i density matrices, got %i.' %
                        (ham.ndm, len(dms)))
    ham.reset(*dms)
    focks = [lf.create_two_index() for i in xrange(ham.ndm)]
    ham.compute_fock(*focks)
    error = 0.0
    work = lf.create_two_index()
    commutator = lf.create_two_index()
    errorsq = 0.0
    for i in xrange(ham.ndm):
        compute_commutator(dms[i], focks[i], overlap, work, commutator)
        errorsq += commutator.contract_two('ab,ab', commutator)
    return errorsq**0.5
Beispiel #2
0
def convergence_error_commutator(ham, lf, overlap, *dms):
    '''Compute the commutator error

       **Arguments:**

       ham
            A Hamiltonian instance.

       lf
            The linalg factory to be used.

       overlap
            The overlap operator.

       dm1, dm2, ...
            A list of density matrices. The numbers of dms must match ham.ndm.

       **Returns:** The commutator error. This measure (not this function) is
       also used in some SCF algorithms to check for convergence.
    '''
    if len(dms) != ham.ndm:
        raise TypeError('Expecting %i density matrices, got %i.' % (ham.ndm, len(dms)))
    ham.reset(*dms)
    focks = [lf.create_two_index() for i in xrange(ham.ndm)]
    ham.compute_fock(*focks)
    error = 0.0
    work = lf.create_two_index()
    commutator = lf.create_two_index()
    errorsq = 0.0
    for i in xrange(ham.ndm):
        compute_commutator(dms[i], focks[i], overlap, work, commutator)
        errorsq += commutator.contract_two('ab,ab', commutator)
    return errorsq**0.5
Beispiel #3
0
    def _build_combinations(self, coeffs, dms_output, focks_output):
        '''Construct a linear combination of density/fock matrices

           **Arguments:**

           coeffs
                The linear mixing coefficients for the previous SCF states.

           dms_output
                A list of output density matrices. (Ignored if None)

           focks_output
                A list of output density matrices. (Ignored if None)

           **Returns:** the commutator error, only when both dms_output and
           focks_output are given.
        '''
        if dms_output is not None:
            if len(dms_output) != self.ndm:
                raise TypeError('The number of density matrices must match the ndm parameter.')
            for i in xrange(self.ndm):
                dms_stack = [self.stack[j].dms[i] for j in xrange(self.nused)]
                self._linear_combination(coeffs, dms_stack, dms_output[i])
        if focks_output is not None:
            if len(focks_output) != self.ndm:
                raise TypeError('The number of Fock matrices must match the ndm parameter.')
            for i in xrange(self.ndm):
                focks_stack = [self.stack[j].focks[i] for j in xrange(self.nused)]
                self._linear_combination(coeffs, focks_stack, focks_output[i])
        if not (dms_output is None or focks_output is None):
            errorsq = 0.0
            for i in xrange(self.ndm):
                compute_commutator(dms_output[i], focks_output[i], self.overlap, self.work, self.commutator)
                errorsq += self.commutator.contract_two('ab,ab', self.commutator)
            return errorsq**0.5
Beispiel #4
0
    def assign(self, identity, energy, dms, focks):
        '''Assign a new state.

           **Arguments:**

           identity
                A unique id for the new state.

           energy
                The energy of the new state.

           dm
                The density matrix of the new state.

           fock
                The Fock matrix of the new state.
        '''
        self.identity = identity
        self.energy = energy
        self.normsq = 0.0
        for i in xrange(self.ndm):
            self.dms[i].assign(dms[i])
            self.focks[i].assign(focks[i])
            compute_commutator(dms[i], focks[i], self.overlap, self.work, self.commutators[i])
            self.normsq += self.commutators[i].contract_two('ab,ab', self.commutators[i])
Beispiel #5
0
def convergence_error_commutator(ham, overlap, *dms):
    '''Compute the commutator error

    Parameters
    ----------
    ham
        A Hamiltonian instance.
    overlap
        The overlap operator.
    dm1, dm2, ...
        A list of density matrices. The numbers of dms must match ham.ndm.

    Returns
    -------
    error: float
        The commutator error. This measure (not this function) is also used in some SCF
        algorithms to check for convergence.
    '''
    if len(dms) != ham.ndm:
        raise TypeError('Expecting %i density matrices, got %i.' %
                        (ham.ndm, len(dms)))
    ham.reset(*dms)
    focks = [np.zeros(dms[0].shape) for i in xrange(ham.ndm)]
    ham.compute_fock(*focks)
    error = 0.0
    errorsq = 0.0
    for i in xrange(ham.ndm):
        commutator = compute_commutator(dms[i], focks[i], overlap)
        errorsq += np.einsum('ab,ab', commutator, commutator)
    return errorsq**0.5
Beispiel #6
0
def convergence_error_commutator(ham, overlap, *dms):
    '''Compute the commutator error

    Parameters
    ----------
    ham
        A Hamiltonian instance.
    overlap
        The overlap operator.
    dm1, dm2, ...
        A list of density matrices. The numbers of dms must match ham.ndm.

    Returns
    -------
    error: float
        The commutator error. This measure (not this function) is also used in some SCF
        algorithms to check for convergence.
    '''
    if len(dms) != ham.ndm:
        raise TypeError('Expecting %i density matrices, got %i.' % (ham.ndm, len(dms)))
    ham.reset(*dms)
    focks = [np.zeros(dms[0].shape) for i in xrange(ham.ndm)]
    ham.compute_fock(*focks)
    error = 0.0
    errorsq = 0.0
    for i in xrange(ham.ndm):
        commutator = compute_commutator(dms[i], focks[i], overlap)
        errorsq += np.einsum('ab,ab', commutator, commutator)
    return errorsq**0.5
Beispiel #7
0
    def assign(self, identity, energy, dms, focks):
        '''Assign a new state.

           **Arguments:**

           identity
                A unique id for the new state.

           energy
                The energy of the new state.

           dm
                The density matrix of the new state.

           fock
                The Fock matrix of the new state.
        '''
        self.identity = identity
        self.energy = energy
        self.normsq = 0.0
        for i in xrange(self.ndm):
            self.dms[i][:] = dms[i]
            self.focks[i][:] = focks[i]
            self.commutators[i][:] = compute_commutator(
                dms[i], focks[i], self.overlap)
            self.normsq += np.einsum('ab,ab', self.commutators[i],
                                     self.commutators[i])
Beispiel #8
0
    def assign(self, identity, energy, dms, focks):
        '''Assign a new state.

           **Arguments:**

           identity
                A unique id for the new state.

           energy
                The energy of the new state.

           dm
                The density matrix of the new state.

           fock
                The Fock matrix of the new state.
        '''
        self.identity = identity
        self.energy = energy
        self.normsq = 0.0
        for i in xrange(self.ndm):
            self.dms[i][:] = dms[i]
            self.focks[i][:] = focks[i]
            self.commutators[i][:] = compute_commutator(dms[i], focks[i], self.overlap)
            self.normsq += np.einsum('ab,ab', self.commutators[i], self.commutators[i])
Beispiel #9
0
    def __call__(self, ham, overlap, occ_model, *dm0s):
        '''Find a self-consistent set of density matrices.

           **Arguments:**

           ham
                An effective Hamiltonian.

           overlap
                The overlap operator.

           occ_model
                Model for the orbital occupations.

           dm1, dm2, ...
                The initial density matrices. The number of dms must match
                ham.ndm.
        '''
        # Some type checking
        if ham.ndm != len(dm0s):
            raise TypeError(
                'The number of initial density matrices does not match the Hamiltonian.'
            )

        # Check input density matrices.
        for i in xrange(ham.ndm):
            check_dm(dm0s[i], overlap)
        occ_model.check_dms(overlap, *dm0s)

        if log.do_medium:
            log('Starting SCF with optimal damping. ham.ndm=%i' % ham.ndm)
            log.hline()
            log(' Iter               Energy         Error      Mixing')
            log.hline()

        fock0s = [np.zeros(overlap.shape) for i in xrange(ham.ndm)]
        fock1s = [np.zeros(overlap.shape) for i in xrange(ham.ndm)]
        dm1s = [np.zeros(overlap.shape) for i in xrange(ham.ndm)]
        orbs = [Orbitals(overlap.shape[0]) for i in xrange(ham.ndm)]
        work = np.zeros(dm0s[0].shape)
        commutator = np.zeros(dm0s[0].shape)
        converged = False
        counter = 0
        mixing = None
        error = None
        while self.maxiter is None or counter < self.maxiter:
            # feed the latest density matrices in the hamiltonian
            ham.reset(*dm0s)
            # Construct the Fock operators in point 0
            ham.compute_fock(*fock0s)
            # Compute the energy in point 0
            energy0 = ham.compute_energy()

            if log.do_medium:
                if mixing is None:
                    log('%5i %20.13f' % (counter, energy0))
                else:
                    log('%5i %20.13f  %12.5e  %10.5f' %
                        (counter, energy0, error, mixing))

            # go to point 1 by diagonalizing the fock matrices
            for i in xrange(ham.ndm):
                orbs[i].from_fock(fock0s[i], overlap)
            # Assign new occupation numbers.
            occ_model.assign(*orbs)
            # Construct the density matrices
            for i in xrange(ham.ndm):
                dm1s[i][:] = orbs[i].to_dm()

            # feed the latest density matrices in the hamiltonian
            ham.reset(*dm1s)
            # Compute the fock matrices in point 1
            ham.compute_fock(*fock1s)
            # Compute the energy in point 1
            energy1 = ham.compute_energy()

            # Compute the derivatives of the energy at point 0 and 1 for a
            # linear interpolation of the density matrices
            deriv0 = 0.0
            deriv1 = 0.0
            for i in xrange(ham.ndm):
                deriv0 += np.einsum('ab,ab', fock0s[i], dm1s[i])
                deriv0 -= np.einsum('ab,ab', fock0s[i], dm0s[i])
                deriv1 += np.einsum('ab,ab', fock1s[i], dm1s[i])
                deriv1 -= np.einsum('ab,ab', fock1s[i], dm0s[i])
            deriv0 *= ham.deriv_scale
            deriv1 *= ham.deriv_scale

            # find the lambda that minimizes the cubic polynomial in the range [0,1]
            if log.do_high:
                log('           E0: % 10.5e     D0: % 10.5e' %
                    (energy0, deriv0))
                log('        E1-E0: % 10.5e     D1: % 10.5e' %
                    (energy1 - energy0, deriv1))
            mixing = find_min_cubic(energy0, energy1, deriv0, deriv1)

            if self.debug:
                check_cubic(ham, dm0s, dm1s, energy0, energy1, deriv0, deriv1)

            # compute the mixed density and fock matrices (in-place in dm0s and fock0s)
            for i in xrange(ham.ndm):
                dm0s[i][:] *= 1 - mixing
                dm0s[i][:] += dm1s[i] * mixing
                fock0s[i][:] *= 1 - mixing
                fock0s[i][:] += fock1s[i] * mixing

            # Compute the convergence criterion.
            errorsq = 0.0
            for i in xrange(ham.ndm):
                commutator = compute_commutator(dm0s[i], fock0s[i], overlap)
                errorsq += np.einsum('ab,ab', commutator, commutator)
            error = errorsq**0.5
            if error < self.threshold:
                converged = True
                break
            elif mixing == 0.0:
                raise NoSCFConvergence(
                    'The ODA algorithm made a zero step without reaching convergence.'
                )

            # counter
            counter += 1

        if log.do_medium:
            ham.log()

        if not converged:
            raise NoSCFConvergence

        return counter
Beispiel #10
0
    def __call__(self, ham, lf, overlap, occ_model, *dm0s):
        '''Find a self-consistent set of density matrices.

           **Arguments:**

           ham
                An effective Hamiltonian.

           lf
                The linalg factory to be used.

           overlap
                The overlap operator.

           occ_model
                Model for the orbital occupations.

           dm1, dm2, ...
                The initial density matrices. The number of dms must match
                ham.ndm.
        '''
        # Some type checking
        if ham.ndm != len(dm0s):
            raise TypeError('The number of initial density matrices does not match the Hamiltonian.')

        # Check input density matrices.
        for i in xrange(ham.ndm):
            check_dm(dm0s[i], overlap, lf)
        occ_model.check_dms(overlap, *dm0s)

        if log.do_medium:
            log('Starting SCF with optimal damping. ham.ndm=%i' % ham.ndm)
            log.hline()
            log(' Iter               Energy         Error      Mixing')
            log.hline()

        fock0s = [lf.create_two_index() for i in xrange(ham.ndm)]
        fock1s = [lf.create_two_index() for i in xrange(ham.ndm)]
        dm1s = [lf.create_two_index() for i in xrange(ham.ndm)]
        exps = [lf.create_expansion() for i in xrange(ham.ndm)]
        work = dm0s[0].new()
        commutator = dm0s[0].new()
        converged = False
        counter = 0
        mixing = None
        error = None
        while self.maxiter is None or counter < self.maxiter:
            # feed the latest density matrices in the hamiltonian
            ham.reset(*dm0s)
            # Construct the Fock operators in point 0
            ham.compute_fock(*fock0s)
            # Compute the energy in point 0
            energy0 = ham.compute_energy()

            if log.do_medium:
                if mixing is None:
                    log('%5i %20.13f' % (counter, energy0))
                else:
                    log('%5i %20.13f  %12.5e  %10.5f' % (counter, energy0, error, mixing))

            # go to point 1 by diagonalizing the fock matrices
            for i in xrange(ham.ndm):
                exps[i].from_fock(fock0s[i], overlap)
            # Assign new occupation numbers.
            occ_model.assign(*exps)
            # Construct the density matrices
            for i in xrange(ham.ndm):
                exps[i].to_dm(dm1s[i])

            # feed the latest density matrices in the hamiltonian
            ham.reset(*dm1s)
            # Compute the fock matrices in point 1
            ham.compute_fock(*fock1s)
            # Compute the energy in point 1
            energy1 = ham.compute_energy()

            # Compute the derivatives of the energy at point 0 and 1 for a
            # linear interpolation of the density matrices
            deriv0 = 0.0
            deriv1 = 0.0
            for i in xrange(ham.ndm):
                deriv0 += fock0s[i].contract_two('ab,ab', dm1s[i])
                deriv0 -= fock0s[i].contract_two('ab,ab', dm0s[i])
                deriv1 += fock1s[i].contract_two('ab,ab', dm1s[i])
                deriv1 -= fock1s[i].contract_two('ab,ab', dm0s[i])
            deriv0 *= ham.deriv_scale
            deriv1 *= ham.deriv_scale

            # find the lambda that minimizes the cubic polynomial in the range [0,1]
            if log.do_high:
                log('           E0: % 10.5e     D0: % 10.5e' % (energy0, deriv0))
                log('        E1-E0: % 10.5e     D1: % 10.5e' % (energy1-energy0, deriv1))
            mixing = find_min_cubic(energy0, energy1, deriv0, deriv1)

            if self.debug:
                check_cubic(ham, dm0s, dm1s, energy0, energy1, deriv0, deriv1)

            # compute the mixed density and fock matrices (in-place in dm0s and fock0s)
            for i in xrange(ham.ndm):
                dm0s[i].iscale(1-mixing)
                dm0s[i].iadd(dm1s[i], mixing)
                fock0s[i].iscale(1-mixing)
                fock0s[i].iadd(fock1s[i], mixing)

            # Compute the convergence criterion.
            errorsq = 0.0
            for i in xrange(ham.ndm):
                compute_commutator(dm0s[i], fock0s[i], overlap, work, commutator)
                errorsq += commutator.contract_two('ab,ab', commutator)
            error = errorsq**0.5
            if error < self.threshold:
                converged = True
                break
            elif mixing == 0.0:
                raise NoSCFConvergence('The ODA algorithm made a zero step without reaching convergence.')

            # counter
            counter += 1

        if log.do_medium:
            ham.log()

        if not converged:
            raise NoSCFConvergence

        return counter