Пример #1
0
def check_dm(dm, overlap, eps=1e-4, occ_max=1.0):
    '''Check if the density matrix has eigenvalues in the proper range.

    Parameters
    ----------
    dm : np.ndarray, shape=(nbasis, nbasis), dtype=float
        The density matrix
    overlap : np.ndarray, shape=(nbasis, nbasis), dtype=float
        The overlap matrix
    eps : float
        The threshold on the eigenvalue inequalities.
    occ_max : float
        The maximum occupation.

    Raises
    ------
    ValueError
        When the density matrix has wrong eigenvalues.
    '''
    # construct natural orbitals
    orb = Orbitals(dm.shape[0])
    orb.derive_naturals(dm, overlap)
    if orb.occupations.min() < -eps:
        raise ValueError('The density matrix has eigenvalues considerably smaller than '
                         'zero. error=%e' % (orb.occupations.min()))
    if orb.occupations.max() > occ_max+eps:
        raise ValueError('The density matrix has eigenvalues considerably larger than '
                         'max. error=%e' % (orb.occupations.max()-1))
Пример #2
0
def get_naturalorbs(nbasis, dm1, orb_alpha):
    """
    Get the natural orbitals from the 1-DM
    """
    dm1 /= 2.0
    norb = Orbitals(nbasis)
    # Diagonalize and compute eigenvalues
    evals, evecs = eigh(dm1)
    # Reorder the matrix and the eigenvalues
    evecs1 = evecs.copy()
    evecs1[:] = evecs[:, ::-1]
    norb.occupations[::-1] = evals
    # Get the natural orbitals
    norb.coeffs[:] = np.dot(orb_alpha.coeffs, evecs1)
    norb.energies[:] = 0.0
    return norb, evecs
Пример #3
0
def test_integrals_wrapper_init():
    """
    Test the integral wrapper.
    """
    function = IntegralsWrapper
    mol = [1, 2]
    obasis = [2, 3, 1, 3]
    one_approx = [0, 1]
    two_approx = [0]
    pars = [0, 3]
    orbs = 'orbs'
    # Check mol
    assert_raises(TypeError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    mol = IOData(numbers=np.array([1]), coordinates=np.array([[0., 0., 0]]))
    # Check obasis
    assert_raises(TypeError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    obasis = get_gobasis(mol.coordinates, mol.numbers, '3-21G')
    # Check one_approx
    assert_raises(TypeError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    one_approx = ['man']
    # Check options for one_approx
    assert_raises(ValueError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    one_approx = ['standard']
    del function
    function = IntegralsWrapper
    # Check two_approx
    assert_raises(TypeError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    two_approx = ['nada']
    # Check for options two_approx
    assert_raises(ValueError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    two_approx = ['erf', 'gauss']
    # Check for pars
    assert_raises(TypeError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    pars = [[0.1], [1.0, 1.5]]
    # Check for orbs
    assert_raises(TypeError, function, mol, obasis, one_approx, two_approx,
                  pars, orbs)
    orbs = [Orbitals(obasis.nbasis)]
Пример #4
0
def check_vanadium_sc_hf(scf_solver):
    """Try to converge the SCF for the neutral vanadium atom with fixe fractional occupations.

    Parameters
    ----------
    scf_solver : one of the SCFSolver types in HORTON
                 A configured SCF solver that must be tested.
    """
    # vanadium atoms
    numbers = np.array([23])
    pseudo_numbers = numbers.astype(float)
    coordinates = np.zeros((1, 3), float)

    # Simple basis set
    obasis = get_gobasis(coordinates, numbers, 'def2-tzvpd')

    # Compute integrals
    olp = obasis.compute_overlap()
    kin = obasis.compute_kinetic()
    na = obasis.compute_nuclear_attraction(coordinates, pseudo_numbers)
    er = obasis.compute_electron_repulsion()

    # Setup of restricted HF Hamiltonian
    terms = [
        RTwoIndexTerm(kin, 'kin'),
        RDirectTerm(er, 'hartree'),
        RExchangeTerm(er, 'x_hf'),
        RTwoIndexTerm(na, 'ne'),
    ]
    ham = REffHam(terms)

    # Define fractional occupations of interest. (Spin-compensated case)
    occ_model = FixedOccModel(np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                        1.0, 1.0, 0.5]))

    # Allocate orbitals and make the initial guess
    orb_alpha = Orbitals(obasis.nbasis)
    guess_core_hamiltonian(olp, kin+na, orb_alpha)

    # SCF test
    check_solve(ham, scf_solver, occ_model, olp, kin, na, orb_alpha)
Пример #5
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
Пример #6
0
def load_mkl(filename):
    '''Load data from a Molekel file.

    Parameters
    ----------
    filename : str
        The filename of the mkl file.

    Returns
    -------
    results : dict
        Data loaded from file, with keys: ``coordinates``, ``numbers``, ``obasis``,
        ``orb_alpha``. It may also contain: ``orb_beta``, ``signs``.
    '''

    def helper_char_mult(f):
        return [int(word) for word in f.readline().split()]


    def helper_coordinates(f):
        numbers = []
        coordinates = []
        while True:
            line = f.readline()
            if len(line) == 0 or line.strip() == '$END':
                break
            words = line.split()
            numbers.append(int(words[0]))
            coordinates.append([float(words[1]), float(words[2]), float(words[3])])
        numbers = np.array(numbers, int)
        coordinates = np.array(coordinates)*angstrom
        return numbers, coordinates


    def helper_obasis(f, coordinates):
        shell_types = []
        shell_map = []
        nprims = []
        alphas = []
        con_coeffs = []

        center_counter = 0
        in_shell = False
        nprim = None
        while True:
            line = f.readline()
            lstrip = line.strip()
            if len(line) == 0 or lstrip == '$END':
                break
            if len(lstrip) == 0:
                continue
            if lstrip == '$$':
                center_counter += 1
                in_shell = False
            else:
                words = line.split()
                if len(words) == 2:
                    assert in_shell
                    alpha = float(words[0])
                    alphas.append(alpha)
                    con_coeffs.append(float(words[1]))
                    nprim += 1
                else:
                    if nprim is not None:
                        nprims.append(nprim)
                    shell_map.append(center_counter)
                    # always assume pure basis functions
                    shell_type = str_to_shell_types(words[1], pure=True)[0]
                    shell_types.append(shell_type)
                    in_shell = True
                    nprim = 0
        if nprim is not None:
            nprims.append(nprim)

        shell_map = np.array(shell_map)
        nprims = np.array(nprims)
        shell_types = np.array(shell_types)
        alphas = np.array(alphas)
        con_coeffs = np.array(con_coeffs)
        return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs)


    def helper_coeffs(f, nbasis):
        coeffs = []
        energies = []

        in_orb = 0
        while True:
            line = f.readline()
            lstrip = line.strip()
            if len(line) == 0 or lstrip == '$END':
                break
            if in_orb == 0:
                # read a1g line
                words = lstrip.split()
                ncol = len(words)
                assert ncol > 0
                for word in words:
                    assert word == 'a1g'
                cols = [np.zeros((nbasis,1), float) for icol in xrange(ncol)]
                in_orb = 1
            elif in_orb == 1:
                # read energies
                words = lstrip.split()
                assert len(words) == ncol
                for word in words:
                    energies.append(float(word))
                in_orb = 2
                ibasis = 0
            elif in_orb == 2:
                # read expansion coefficients
                words = lstrip.split()
                assert len(words) == ncol
                for icol in xrange(ncol):
                    cols[icol][ibasis] = float(words[icol])
                ibasis += 1
                if ibasis == nbasis:
                    in_orb = 0
                    coeffs.extend(cols)

        return np.hstack(coeffs), np.array(energies)


    def helper_occ(f):
        occs = []
        while True:
            line = f.readline()
            lstrip = line.strip()
            if len(line) == 0 or lstrip == '$END':
                break
            for word in lstrip.split():
                occs.append(float(word))
        return np.array(occs)


    charge = None
    spinmult = None
    numbers = None
    coordinates = None
    obasis = None
    coeff_alpha = None
    ener_alpha = None
    occ_alpha = None
    coeff_beta = None
    ener_beta = None
    occ_beta = None
    with open(filename) as f:
        while True:
            line = f.readline()
            if len(line) == 0:
                break
            line = line.strip()
            if line == '$CHAR_MULT':
                charge, spinmult = helper_char_mult(f)
            elif line == '$COORD':
                numbers, coordinates = helper_coordinates(f)
            elif line == '$BASIS':
                obasis = helper_obasis(f, coordinates)
            elif line == '$COEFF_ALPHA':
                coeff_alpha, ener_alpha = helper_coeffs(f, obasis.nbasis)
            elif line == '$OCC_ALPHA':
                occ_alpha = helper_occ(f)
            elif line == '$COEFF_BETA':
                coeff_beta, ener_beta = helper_coeffs(f, obasis.nbasis)
            elif line == '$OCC_BETA':
                occ_beta = helper_occ(f)

    if charge is None:
        raise IOError('Charge and multiplicity not found in mkl file.')
    if coordinates is None:
        raise IOError('Coordinates not found in mkl file.')
    if obasis is None:
        raise IOError('Orbital basis not found in mkl file.')
    if coeff_alpha is None:
        raise IOError('Alpha orbitals not found in mkl file.')
    if occ_alpha is None:
        raise IOError('Alpha occupation numbers not found in mkl file.')

    nelec = numbers.sum() - charge
    if coeff_beta is None:
        assert nelec % 2 == 0
        assert abs(occ_alpha.sum() - nelec) < 1e-7
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha/2
        orb_beta = None
    else:
        if occ_beta is None:
            raise IOError('Beta occupation numbers not found in mkl file while beta orbitals were present.')
        nalpha = int(np.round(occ_alpha.sum()))
        nbeta = int(np.round(occ_beta.sum()))
        assert nelec == nalpha+nbeta
        assert coeff_alpha.shape == coeff_beta.shape
        assert ener_alpha.shape == ener_beta.shape
        assert occ_alpha.shape == occ_beta.shape
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha
        orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1])
        orb_beta.coeffs[:] = coeff_beta
        orb_beta.energies[:] = ener_beta
        orb_beta.occupations[:] = occ_beta

    result = {
        'coordinates': coordinates,
        'orb_alpha': orb_alpha,
        'numbers': numbers,
        'obasis': obasis,
    }
    if orb_beta is not None:
        result['orb_beta'] = orb_beta
    _fix_molden_from_buggy_codes(result, filename)
    return result
Пример #7
0
def load_molden(filename):
    """Load data from a molden input file.

    Parameters
    ----------
    filename : str
        The filename of the molden input file.

    Returns
    -------
    results : dict
        Data loaded from file, with with: ``coordinates``, ``numbers``,
        ``pseudo_numbers``, ``obasis``, ``orb_alpha``, ``signs``. It may also contain:
        ``title``, ``orb_beta``.
    """
    def helper_coordinates(f, cunit):
        """Load element numbers and coordinates"""
        numbers = []
        pseudo_numbers = []
        coordinates = []
        while True:
            last_pos = f.tell()
            line = f.readline()
            if len(line) == 0:
                break
            words = line.split()
            if len(words) != 6:
                # Go back to previous line and stop
                f.seek(last_pos)
                break
            else:
                numbers.append(periodic[words[0]].number)
                pseudo_numbers.append(float(words[2]))
                coordinates.append(
                    [float(words[3]),
                     float(words[4]),
                     float(words[5])])
        numbers = np.array(numbers, int)
        pseudo_numbers = np.array(pseudo_numbers)
        coordinates = np.array(coordinates) * cunit
        return numbers, pseudo_numbers, coordinates

    def helper_obasis(f, coordinates, pure):
        """Load the orbital basis"""
        shell_types = []
        shell_map = []
        nprims = []
        alphas = []
        con_coeffs = []

        icenter = 0
        in_atom = False
        in_shell = False
        while True:
            last_pos = f.tell()
            line = f.readline()
            if len(line) == 0:
                break
            words = line.split()
            if len(words) == 0:
                in_atom = False
                in_shell = False
            elif len(words) == 2 and not in_atom:
                icenter = int(words[0]) - 1
                in_atom = True
                in_shell = False
            elif len(words) == 3:
                in_shell = True
                shell_map.append(icenter)
                shell_label = words[0].lower()
                shell_type = str_to_shell_types(shell_label,
                                                pure.get(shell_label,
                                                         False))[0]
                shell_types.append(shell_type)
                nprims.append(int(words[1]))
            elif len(words) == 2 and in_atom:
                assert in_shell
                alpha = float(words[0].replace('D', 'E'))
                alphas.append(alpha)
                con_coeff = float(words[1].replace('D', 'E'))
                con_coeffs.append(con_coeff)
            else:
                # done, go back one line
                f.seek(last_pos)
                break

        shell_map = np.array(shell_map)
        nprims = np.array(nprims)
        shell_types = np.array(shell_types)
        alphas = np.array(alphas)
        con_coeffs = np.array(con_coeffs)
        return GOBasis(coordinates, shell_map, nprims, shell_types, alphas,
                       con_coeffs)

    def helper_coeffs(f, nbasis):
        """Load the orbital coefficients"""
        coeff_alpha = []
        ener_alpha = []
        occ_alpha = []
        coeff_beta = []
        ener_beta = []
        occ_beta = []

        new_orb = True
        icoeff = nbasis
        while True:
            line = f.readline()
            if len(line) == 0 or '[' in line:
                break
            # prepare array with orbital coefficients
            if '=' in line:
                if line.startswith(' Ene='):
                    energy = float(line[5:])
                elif line.startswith(' Spin='):
                    spin = line[6:].strip()
                elif line.startswith(' Occup='):
                    occ = float(line[7:])
                new_orb = True
            else:
                if new_orb:
                    # store col, energy and occ
                    col = np.zeros((nbasis, 1))
                    if spin.lower() == 'alpha':
                        coeff_alpha.append(col)
                        ener_alpha.append(energy)
                        occ_alpha.append(occ)
                    else:
                        coeff_beta.append(col)
                        ener_beta.append(energy)
                        occ_beta.append(occ)
                    new_orb = False
                    if icoeff < nbasis:
                        raise IOError(
                            'Too little expansions coefficients in one orbital in molden file.'
                        )
                    icoeff = 0
                words = line.split()
                if icoeff >= nbasis:
                    raise IOError(
                        'Too many expansions coefficients in one orbital in molden file.'
                    )
                col[icoeff] = float(words[1])
                icoeff += 1
                assert int(words[0]) == icoeff

        coeff_alpha = np.hstack(coeff_alpha)
        ener_alpha = np.array(ener_alpha)
        occ_alpha = np.array(occ_alpha)
        if len(coeff_beta) == 0:
            coeff_beta = None
            ener_beta = None
            occ_beta = None
        else:
            coeff_beta = np.hstack(coeff_beta)
            ener_beta = np.array(ener_beta)
            occ_beta = np.array(occ_beta)
        return (coeff_alpha, ener_alpha, occ_alpha), (coeff_beta, ener_beta,
                                                      occ_beta)

    # First pass: scan the file for pure/cartesian modifiers.
    # Unfortunately, some program put this information _AFTER_ the basis
    # set specification.
    pure = {'d': False, 'f': False, 'g': False}
    with open(filename) as f:
        for line in f:
            line = line.lower()
            if line.startswith('[5d]') or line.startswith('[5d7f]'):
                pure['d'] = True
                pure['f'] = True
            elif line.lower().startswith('[7f]'):
                pure['f'] = True
            elif line.lower().startswith('[5d10f]'):
                pure['d'] = True
                pure['f'] = False
            elif line.lower().startswith('[9g]'):
                pure['g'] = True

    # Second pass: read all the other info.
    numbers = None
    coordinates = None
    obasis = None
    coeff_alpha = None
    ener_alpha = None
    occ_alpha = None
    coeff_beta = None
    ener_beta = None
    occ_beta = None
    title = None
    with open(filename) as f:
        line = f.readline()
        if line != '[Molden Format]\n':
            raise IOError('Molden header not found')
        while True:
            line = f.readline()
            if len(line) == 0:
                break
            line = line.strip()
            if line == '[Title]':
                title = f.readline().strip()
            elif line.startswith('[Atoms]'):
                if 'au' in line.lower():
                    cunit = 1.0
                elif 'angs' in line.lower():
                    cunit = angstrom
                numbers, pseudo_numbers, coordinates = helper_coordinates(
                    f, cunit)
            elif line == '[GTO]':
                obasis = helper_obasis(f, coordinates, pure)
            elif line == '[STO]':
                raise NotImplementedError(
                    'Slater-type orbitals are not supported in HORTON.')
            elif line == '[MO]':
                data_alpha, data_beta = helper_coeffs(f, obasis.nbasis)
                coeff_alpha, ener_alpha, occ_alpha = data_alpha
                coeff_beta, ener_beta, occ_beta = data_beta

    if coordinates is None:
        raise IOError('Coordinates not found in molden input file.')
    if obasis is None:
        raise IOError('Orbital basis not found in molden input file.')
    if coeff_alpha is None:
        raise IOError('Alpha orbitals not found in molden input file.')

    if coeff_beta is None:
        nalpha = int(np.round(occ_alpha.sum())) / 2
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha / 2
        orb_beta = None
    else:
        nalpha = int(np.round(occ_alpha.sum()))
        nbeta = int(np.round(occ_beta.sum()))
        assert coeff_alpha.shape == coeff_beta.shape
        assert ener_alpha.shape == ener_beta.shape
        assert occ_alpha.shape == occ_beta.shape
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha
        orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1])
        orb_beta.coeffs[:] = coeff_beta
        orb_beta.energies[:] = ener_beta
        orb_beta.occupations[:] = occ_beta

    permutation = _get_molden_permutation(obasis)

    # filter out ghost atoms
    mask = pseudo_numbers != 0
    coordinates = coordinates[mask]
    numbers = numbers[mask]
    pseudo_numbers = pseudo_numbers[mask]

    result = {
        'coordinates': coordinates,
        'orb_alpha': orb_alpha,
        'numbers': numbers,
        'obasis': obasis,
        'permutation': permutation,
        'pseudo_numbers': pseudo_numbers,
    }
    if title is not None:
        result['title'] = title
    if orb_beta is not None:
        result['orb_beta'] = orb_beta

    _fix_molden_from_buggy_codes(result, filename)

    return result
Пример #8
0
def load_atom_cp2k(filename):
    """Load data from a CP2K ATOM computation.

    Parameters
    ---------
    filename : str
        The name of the cp2k out file

    Returns
    -------
    results : dict
        Contains: ``obasis``, ``orb_alpha``, ``coordinates``, ``numbers``, ``energy``,
        ``pseudo_numbers``. May contain: ``orb_beta``.


    Notes
    -----
    This function assumes that the following subsections are present in the CP2K
    ATOM input file, in the section ``ATOM%PRINT``:

    .. code-block:: text

      &PRINT
        &POTENTIAL
        &END POTENTIAL
        &BASIS_SET
        &END BASIS_SET
        &ORBITALS
        &END ORBITALS
      &END PRINT
    """
    with open(filename) as f:
        # Find the element number
        number = None
        for line in f:
            if line.startswith(' Atomic Energy Calculation'):
                number = int(line[-5:-1])
                break
        if number is None:
            raise IOError(
                'Could not find atomic number in CP2K ATOM output: %s.' %
                filename)

        # Go to the all-electron basis set and read it.
        for line in f:
            if line.startswith(' All Electron Basis'):
                break
        ae_obasis = _read_cp2k_obasis(f)

        # Go to the pseudo basis set and read it.
        for line in f:
            if line.startswith(' Pseudopotential Basis'):
                break
        pp_obasis = _read_cp2k_obasis(f)

        # Search for (un)restricted
        restricted = None
        for line in f:
            if line.startswith(' METHOD    |'):
                if 'U' in line:
                    restricted = False
                    break
                elif 'R' in line:
                    restricted = True
                    break

        # Search for the core charge (pseudo number)
        pseudo_number = None
        for line in f:
            if line.startswith('          Core Charge'):
                pseudo_number = float(line[70:])
                assert pseudo_number == int(pseudo_number)
                break
            elif line.startswith(' Electronic structure'):
                pseudo_number = float(number)
                break
        if pseudo_number is None:
            raise IOError(
                'Could not find effective core charge in CP2K ATOM output:'
                ' %s' % filename)

        # Select the correct basis
        if pseudo_number == number:
            obasis = ae_obasis
        else:
            obasis = pp_obasis

        # Search for energy
        for line in f:
            if line.startswith(
                    ' Energy components [Hartree]           Total Energy ::'):
                energy = float(line[60:])
                break

        # Read orbital energies and occupations
        for line in f:
            if line.startswith(' Orbital energies'):
                break
        f.next()
        oe_alpha, oe_beta = _read_cp2k_occupations_energies(f, restricted)

        # Read orbital expansion coefficients
        line = f.next()
        if (line != " Atomic orbital expansion coefficients [Alpha]\n") and \
           (line != " Atomic orbital expansion coefficients []\n"):
            raise IOError(
                'Could not find orbital coefficients in CP2K ATOM output: '
                '%s' % filename)
        coeffs_alpha = _read_cp2k_orbital_coeffs(f, oe_alpha)

        if not restricted:
            line = f.next()
            if line != " Atomic orbital expansion coefficients [Beta]\n":
                raise IOError(
                    'Could not find beta orbital coefficient in CP2K ATOM '
                    'output: %s' % filename)
            coeffs_beta = _read_cp2k_orbital_coeffs(f, oe_beta)

        # Turn orbital data into a HORTON orbital expansions
        if restricted:
            norb, nel = _get_norb_nel(oe_alpha)
            assert nel % 2 == 0
            orb_alpha = Orbitals(obasis.nbasis, norb)
            orb_beta = None
            _fill_orbitals(orb_alpha, oe_alpha, coeffs_alpha,
                           obasis.shell_types, restricted)
        else:
            norb_alpha = _get_norb_nel(oe_alpha)[0]
            norb_beta = _get_norb_nel(oe_beta)[0]
            assert norb_alpha == norb_beta
            orb_alpha = Orbitals(obasis.nbasis, norb_alpha)
            orb_beta = Orbitals(obasis.nbasis, norb_beta)
            _fill_orbitals(orb_alpha, oe_alpha, coeffs_alpha,
                           obasis.shell_types, restricted)
            _fill_orbitals(orb_beta, oe_beta, coeffs_beta, obasis.shell_types,
                           restricted)

    result = {
        'obasis': obasis,
        'orb_alpha': orb_alpha,
        'coordinates': obasis.centers,
        'numbers': np.array([number]),
        'energy': energy,
        'pseudo_numbers': np.array([pseudo_number]),
    }
    if orb_beta is not None:
        result['orb_beta'] = orb_beta
    return result
Пример #9
0
def load_molden(filename):
    """Load data from a molden input file.

    Parameters
    ----------
    filename : str
        The filename of the molden input file.

    Returns
    -------
    results : dict
        Data loaded from file, with with: ``coordinates``, ``numbers``,
        ``pseudo_numbers``, ``obasis``, ``orb_alpha``, ``signs``. It may also contain:
        ``title``, ``orb_beta``.
    """

    def helper_coordinates(f, cunit):
        """Load element numbers and coordinates"""
        numbers = []
        pseudo_numbers = []
        coordinates = []
        while True:
            last_pos = f.tell()
            line = f.readline()
            if len(line) == 0:
                break
            words = line.split()
            if len(words) != 6:
                # Go back to previous line and stop
                f.seek(last_pos)
                break
            else:
                numbers.append(periodic[words[0]].number)
                pseudo_numbers.append(float(words[2]))
                coordinates.append([float(words[3]), float(words[4]), float(words[5])])
        numbers = np.array(numbers, int)
        pseudo_numbers = np.array(pseudo_numbers)
        coordinates = np.array(coordinates)*cunit
        return numbers, pseudo_numbers, coordinates


    def helper_obasis(f, coordinates, pure):
        """Load the orbital basis"""
        shell_types = []
        shell_map = []
        nprims = []
        alphas = []
        con_coeffs = []

        icenter = 0
        in_atom = False
        in_shell = False
        while True:
            last_pos = f.tell()
            line = f.readline()
            if len(line) == 0:
                break
            words = line.split()
            if len(words) == 0:
                in_atom = False
                in_shell = False
            elif len(words) == 2 and not in_atom:
                icenter = int(words[0])-1
                in_atom = True
                in_shell = False
            elif len(words) == 3:
                in_shell = True
                shell_map.append(icenter)
                shell_label = words[0].lower()
                shell_type = str_to_shell_types(shell_label, pure.get(shell_label, False))[0]
                shell_types.append(shell_type)
                nprims.append(int(words[1]))
            elif len(words) == 2 and in_atom:
                assert in_shell
                alpha = float(words[0].replace('D', 'E'))
                alphas.append(alpha)
                con_coeff = float(words[1].replace('D', 'E'))
                con_coeffs.append(con_coeff)
            else:
                # done, go back one line
                f.seek(last_pos)
                break

        shell_map = np.array(shell_map)
        nprims = np.array(nprims)
        shell_types = np.array(shell_types)
        alphas = np.array(alphas)
        con_coeffs = np.array(con_coeffs)
        return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs)


    def helper_coeffs(f, nbasis):
        """Load the orbital coefficients"""
        coeff_alpha = []
        ener_alpha = []
        occ_alpha = []
        coeff_beta = []
        ener_beta = []
        occ_beta = []

        new_orb = True
        icoeff = nbasis
        while True:
            line = f.readline()
            if len(line) == 0 or '[' in line:
                break
            # prepare array with orbital coefficients
            if '=' in line:
                if line.startswith(' Ene='):
                    energy = float(line[5:])
                elif line.startswith(' Spin='):
                    spin = line[6:].strip()
                elif line.startswith(' Occup='):
                    occ = float(line[7:])
                new_orb = True
            else:
                if new_orb:
                    # store col, energy and occ
                    col = np.zeros((nbasis, 1))
                    if spin.lower() == 'alpha':
                        coeff_alpha.append(col)
                        ener_alpha.append(energy)
                        occ_alpha.append(occ)
                    else:
                        coeff_beta.append(col)
                        ener_beta.append(energy)
                        occ_beta.append(occ)
                    new_orb = False
                    if icoeff < nbasis:
                        raise IOError('Too little expansions coefficients in one orbital in molden file.')
                    icoeff = 0
                words = line.split()
                if icoeff >= nbasis:
                    raise IOError('Too many expansions coefficients in one orbital in molden file.')
                col[icoeff] = float(words[1])
                icoeff += 1
                assert int(words[0]) == icoeff

        coeff_alpha = np.hstack(coeff_alpha)
        ener_alpha = np.array(ener_alpha)
        occ_alpha = np.array(occ_alpha)
        if len(coeff_beta) == 0:
            coeff_beta = None
            ener_beta = None
            occ_beta = None
        else:
            coeff_beta = np.hstack(coeff_beta)
            ener_beta = np.array(ener_beta)
            occ_beta = np.array(occ_beta)
        return (coeff_alpha, ener_alpha, occ_alpha), (coeff_beta, ener_beta, occ_beta)

    # First pass: scan the file for pure/cartesian modifiers.
    # Unfortunately, some program put this information _AFTER_ the basis
    # set specification.
    pure = {'d': False, 'f': False, 'g': False}
    with open(filename) as f:
        for line in f:
            line = line.lower()
            if line.startswith('[5d]') or line.startswith('[5d7f]'):
                pure['d'] = True
                pure['f'] = True
            elif line.lower().startswith('[7f]'):
                pure['f'] = True
            elif line.lower().startswith('[5d10f]'):
                pure['d'] = True
                pure['f'] = False
            elif line.lower().startswith('[9g]'):
                pure['g'] = True

    # Second pass: read all the other info.
    numbers = None
    coordinates = None
    obasis = None
    coeff_alpha = None
    ener_alpha = None
    occ_alpha = None
    coeff_beta = None
    ener_beta = None
    occ_beta = None
    title = None
    with open(filename) as f:
        line = f.readline()
        if line != '[Molden Format]\n':
            raise IOError('Molden header not found')
        while True:
            line = f.readline()
            if len(line) == 0:
                break
            line = line.strip()
            if line == '[Title]':
                title = f.readline().strip()
            elif line.startswith('[Atoms]'):
                if 'au' in line.lower():
                    cunit = 1.0
                elif 'angs' in line.lower():
                    cunit = angstrom
                numbers, pseudo_numbers, coordinates = helper_coordinates(f, cunit)
            elif line == '[GTO]':
                obasis = helper_obasis(f, coordinates, pure)
            elif line == '[STO]':
                raise NotImplementedError('Slater-type orbitals are not supported in HORTON.')
            elif line == '[MO]':
                data_alpha, data_beta = helper_coeffs(f, obasis.nbasis)
                coeff_alpha, ener_alpha, occ_alpha = data_alpha
                coeff_beta, ener_beta, occ_beta = data_beta

    if coordinates is None:
        raise IOError('Coordinates not found in molden input file.')
    if obasis is None:
        raise IOError('Orbital basis not found in molden input file.')
    if coeff_alpha is None:
        raise IOError('Alpha orbitals not found in molden input file.')

    if coeff_beta is None:
        nalpha = int(np.round(occ_alpha.sum()))/2
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha/2
        orb_beta = None
    else:
        nalpha = int(np.round(occ_alpha.sum()))
        nbeta = int(np.round(occ_beta.sum()))
        assert coeff_alpha.shape == coeff_beta.shape
        assert ener_alpha.shape == ener_beta.shape
        assert occ_alpha.shape == occ_beta.shape
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha
        orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1])
        orb_beta.coeffs[:] = coeff_beta
        orb_beta.energies[:] = ener_beta
        orb_beta.occupations[:] = occ_beta

    permutation = _get_molden_permutation(obasis)

    # filter out ghost atoms
    mask = pseudo_numbers != 0
    coordinates = coordinates[mask]
    numbers = numbers[mask]
    pseudo_numbers = pseudo_numbers[mask]

    result = {
        'coordinates': coordinates,
        'orb_alpha': orb_alpha,
        'numbers': numbers,
        'obasis': obasis,
        'permutation': permutation,
        'pseudo_numbers': pseudo_numbers,
    }
    if title is not None:
        result['title'] = title
    if orb_beta is not None:
        result['orb_beta'] = orb_beta

    _fix_molden_from_buggy_codes(result, filename)

    return result
Пример #10
0
def load_wfn(filename):
    """Load data from a WFN file.

    Parameters
    ----------
    filename : str
        The filename of the wfn file.

    Returns
    -------
    results : dict
        Data loaded from file, with keys ``title``, ``coordinates``, ``numbers``,
        ``energy``, ``obasis`` and ``orb_alpha``. May contain ``orb_beta``.
    """
    title, numbers, coordinates, centers, type_assignment, exponents, \
        mo_count, mo_occ, mo_energy, coefficients, energy = load_wfn_low(filename)
    permutation = get_permutation_basis(type_assignment)
    # permute arrays containing wfn data
    type_assignment = type_assignment[permutation]
    mask = get_mask(type_assignment)
    reduced_size = np.array(mask, int).sum()
    num_mo = coefficients.shape[1]
    alphas = np.empty(reduced_size)
    alphas[:] = exponents[permutation][mask]
    assert (centers == centers[permutation]).all()
    shell_map = centers[mask]
    # cartesian basis: {S:0, P:1, D:2, F:3, G:4, H:5}
    shell = {1: 0, 2: 1, 5: 2, 11: 3, 21: 4, 36: 5}
    shell_types = type_assignment[mask]
    shell_types = np.array([shell[i] for i in shell_types])
    assert shell_map.size == shell_types.size == reduced_size
    nprims = np.ones(reduced_size, int)
    con_coeffs = np.ones(reduced_size)
    # build basis set
    obasis = GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs)
    coefficients = coefficients[permutation]
    coefficients /= obasis.get_scales().reshape(-1, 1)
    # make the wavefunction
    if mo_occ.max() > 1.0:
        # close shell system
        orb_alpha = Orbitals(obasis.nbasis, coefficients.shape[1])
        orb_alpha.coeffs[:] = coefficients
        orb_alpha.energies[:] = mo_energy
        orb_alpha.occupations[:] = mo_occ / 2
        orb_beta = None
    else:
        # open shell system
        # counting the number of alpha and beta orbitals
        index = 1
        while index < num_mo and mo_energy[index] >= mo_energy[index - 1] and mo_count[index] == mo_count[index - 1] + 1:
            index += 1
        orb_alpha = Orbitals(obasis.nbasis, index)
        orb_alpha.coeffs[:] = coefficients[:, :index]
        orb_alpha.energies[:] = mo_energy[:index]
        orb_alpha.occupations[:] = mo_occ[:index]
        orb_beta = Orbitals(obasis.nbasis, num_mo - index)
        orb_beta.coeffs[:] = coefficients[:, index:]
        orb_beta.energies[:] = mo_energy[index:]
        orb_beta.occupations[:] = mo_occ[index:]

    result = {
        'title': title,
        'coordinates': coordinates,
        'orb_alpha': orb_alpha,
        'numbers': numbers,
        'obasis': obasis,
        'energy': energy,
    }
    if orb_beta is not None:
        result['orb_beta'] = orb_beta
    return result
Пример #11
0
def load_fchk(filename):
    '''Load from a formatted checkpoint file.

       **Arguments:**

       filename
            The filename of the Gaussian formatted checkpoint file.

       **Returns** a dictionary with: ``title``, ``coordinates``, ``numbers``,
       ``obasis``, ``orb_alpha``, ``permutation``, ``energy``,
       ``pseudo_numbers``, ``mulliken_charges``. The dictionary may also
       contain: ``npa_charges``, ``esp_charges``, ``orb_beta``, ``dm_full_mp2``,
       ``dm_spin_mp2``, ``dm_full_mp3``, ``dm_spin_mp3``, ``dm_full_cc``,
       ``dm_spin_cc``, ``dm_full_ci``, ``dm_spin_ci``, ``dm_full_scf``,
       ``dm_spin_scf``, ``polar``, ``dipole_moment``, ``quadrupole_moment``.
    '''
    from horton.gbasis.cext import GOBasis

    fchk = FCHKFile(filename, [
        "Number of electrons", "Number of independant functions",
        "Number of independent functions",
        "Number of alpha electrons", "Number of beta electrons",
        "Atomic numbers", "Current cartesian coordinates",
        "Shell types", "Shell to atom map", "Shell to atom map",
        "Number of primitives per shell", "Primitive exponents",
        "Contraction coefficients", "P(S=P) Contraction coefficients",
        "Alpha Orbital Energies", "Alpha MO coefficients",
        "Beta Orbital Energies", "Beta MO coefficients",
        "Total Energy", "Nuclear charges",
        'Total SCF Density', 'Spin SCF Density',
        'Total MP2 Density', 'Spin MP2 Density',
        'Total MP3 Density', 'Spin MP3 Density',
        'Total CC Density', 'Spin CC Density',
        'Total CI Density', 'Spin CI Density',
        'Mulliken Charges', 'ESP Charges', 'NPA Charges',
        'Polarizability', 'Dipole Moment', 'Quadrupole Moment',
    ])

    # A) Load the geometry
    numbers = fchk["Atomic numbers"]
    coordinates = fchk["Current cartesian coordinates"].reshape(-1,3)
    pseudo_numbers = fchk["Nuclear charges"]
    # Mask out ghost atoms
    mask = pseudo_numbers != 0.0
    numbers = numbers[mask]
    # Do not overwrite coordinates array, because it is needed to specify basis
    system_coordinates = coordinates[mask]
    pseudo_numbers = pseudo_numbers[mask]

    # B) Load the orbital basis set
    shell_types = fchk["Shell types"]
    shell_map = fchk["Shell to atom map"] - 1
    nprims = fchk["Number of primitives per shell"]
    alphas = fchk["Primitive exponents"]
    ccoeffs_level1 = fchk["Contraction coefficients"]
    ccoeffs_level2 = fchk.get("P(S=P) Contraction coefficients")

    my_shell_types = []
    my_shell_map = []
    my_nprims = []
    my_alphas = []
    con_coeffs = []
    counter = 0
    for i, n in enumerate(nprims):
        if shell_types[i] == -1:
            # Special treatment for SP shell type
            my_shell_types.append(0)
            my_shell_types.append(1)
            my_shell_map.append(shell_map[i])
            my_shell_map.append(shell_map[i])
            my_nprims.append(nprims[i])
            my_nprims.append(nprims[i])
            my_alphas.append(alphas[counter:counter+n])
            my_alphas.append(alphas[counter:counter+n])
            con_coeffs.append(ccoeffs_level1[counter:counter+n])
            con_coeffs.append(ccoeffs_level2[counter:counter+n])
        else:
            my_shell_types.append(shell_types[i])
            my_shell_map.append(shell_map[i])
            my_nprims.append(nprims[i])
            my_alphas.append(alphas[counter:counter+n])
            con_coeffs.append(ccoeffs_level1[counter:counter+n])
        counter += n
    my_shell_types = np.array(my_shell_types)
    my_shell_map = np.array(my_shell_map)
    my_nprims = np.array(my_nprims)
    my_alphas = np.concatenate(my_alphas)
    con_coeffs = np.concatenate(con_coeffs)
    del shell_map
    del shell_types
    del nprims
    del alphas

    obasis = GOBasis(coordinates, my_shell_map, my_nprims, my_shell_types, my_alphas, con_coeffs)

    # permutation of the orbital basis functions
    permutation_rules = {
      -9: np.arange(19),
      -8: np.arange(17),
      -7: np.arange(15),
      -6: np.arange(13),
      -5: np.arange(11),
      -4: np.arange(9),
      -3: np.arange(7),
      -2: np.arange(5),
       0: np.array([0]),
       1: np.arange(3),
       2: np.array([0, 3, 4, 1, 5, 2]),
       3: np.array([0, 4, 5, 3, 9, 6, 1, 8, 7, 2]),
       4: np.arange(15)[::-1],
       5: np.arange(21)[::-1],
       6: np.arange(28)[::-1],
       7: np.arange(36)[::-1],
       8: np.arange(45)[::-1],
       9: np.arange(55)[::-1],
    }
    permutation = []
    for shell_type in my_shell_types:
        permutation.extend(permutation_rules[shell_type]+len(permutation))
    permutation = np.array(permutation, dtype=int)

    result = {
        'title': fchk.title,
        'coordinates': system_coordinates,
        'numbers': numbers,
        'obasis': obasis,
        'permutation': permutation,
        'pseudo_numbers': pseudo_numbers,
    }

    # C) Load density matrices
    def load_dm(label):
        if label in fchk:
            dm = np.zeros((obasis.nbasis, obasis.nbasis))
            start = 0
            for i in xrange(obasis.nbasis):
                stop = start+i+1
                dm[i, :i+1] = fchk[label][start:stop]
                dm[:i+1, i] = fchk[label][start:stop]
                start = stop
            return dm

    # First try to load the post-hf density matrices.
    load_orbitals = True
    for key in 'MP2', 'MP3', 'CC', 'CI', 'SCF':
        dm_full = load_dm('Total %s Density' % key)
        if dm_full is not None:
            result['dm_full_%s' % key.lower()] = dm_full
        dm_spin = load_dm('Spin %s Density' % key)
        if dm_spin is not None:
            result['dm_spin_%s' % key.lower()] = dm_spin

    # D) Load the wavefunction
    # Handle small difference in fchk files from g03 and g09
    nbasis_indep = fchk.get("Number of independant functions") or \
                   fchk.get("Number of independent functions")
    if nbasis_indep is None:
        nbasis_indep = obasis.nbasis

    # Load orbitals
    nalpha = fchk['Number of alpha electrons']
    nbeta = fchk['Number of beta electrons']
    if nalpha < 0 or nbeta < 0 or nalpha+nbeta <= 0:
        raise ValueError('The file %s does not contain a positive number of electrons.' % filename)
    orb_alpha = Orbitals(obasis.nbasis, nbasis_indep)
    orb_alpha.coeffs[:] = fchk['Alpha MO coefficients'].reshape(nbasis_indep, obasis.nbasis).T
    orb_alpha.energies[:] = fchk['Alpha Orbital Energies']
    orb_alpha.occupations[:nalpha] = 1.0
    result['orb_alpha'] = orb_alpha
    if 'Beta Orbital Energies' in fchk:
        # UHF case
        orb_beta = Orbitals(obasis.nbasis, nbasis_indep)
        orb_beta.coeffs[:] = fchk['Beta MO coefficients'].reshape(nbasis_indep, obasis.nbasis).T
        orb_beta.energies[:] = fchk['Beta Orbital Energies']
        orb_beta.occupations[:nbeta] = 1.0
        result['orb_beta'] = orb_beta
    elif fchk['Number of beta electrons'] != fchk['Number of alpha electrons']:
        # ROHF case
        orb_beta = Orbitals(obasis.nbasis, nbasis_indep)
        orb_beta.coeffs[:] = fchk['Alpha MO coefficients'].reshape(nbasis_indep, obasis.nbasis).T
        orb_beta.energies[:] = fchk['Alpha Orbital Energies']
        orb_beta.occupations[:nbeta] = 1.0
        result['orb_beta'] = orb_beta
        # Delete dm_full_scf because it is known to be buggy
        result.pop('dm_full_scf')

    # E) Load properties
    result['energy'] = fchk['Total Energy']
    if 'Polarizability' in fchk:
        result['polar'] = triangle_to_dense(fchk['Polarizability'])
    if 'Dipole Moment' in fchk:
        result['dipole_moment'] = fchk['Dipole Moment']
    if 'Quadrupole Moment' in fchk:
        # Convert to HORTON ordering: xx, xy, xz, yy, yz, zz
        result['quadrupole_moment'] = fchk['Quadrupole Moment'][[0, 3, 4, 1, 5, 2]]

    # F) Load optional properties
    # Mask out ghost atoms from charges
    if 'Mulliken Charges' in fchk:
        result['mulliken_charges'] = fchk['Mulliken Charges'][mask]
    if 'ESP Charges' in fchk:
        result['esp_charges'] = fchk['ESP Charges'][mask]
    if 'NPA Charges' in fchk:
        result['npa_charges'] = fchk['NPA Charges'][mask]

    return result
Пример #12
0
    def __call__(self, ham, overlap, occ_model, *dms):
        '''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(dms):
            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(dms[i], overlap)
        occ_model.check_dms(overlap, *dms)

        # keep local variables as attributes for inspection/debugging by caller
        self._history = self.DIISHistoryClass(self.nvector, ham.ndm,
                                              ham.deriv_scale, overlap)
        self._focks = [np.zeros(overlap.shape) for i in xrange(ham.ndm)]
        self._orbs = [Orbitals(overlap.shape[0]) for i in xrange(ham.ndm)]

        if log.do_medium:
            log('Starting restricted closed-shell %s-SCF' % self._history.name)
            log.hline()
            log('Iter         Error        CN         Last nv Method          Energy       Change'
                )
            log.hline()

        converged = False
        counter = 0
        while self.maxiter is None or counter < self.maxiter:
            # Construct the Fock operator from scratch if the history is empty:
            if self._history.nused == 0:
                # feed the latest density matrices in the hamiltonian
                ham.reset(*dms)
                # Construct the Fock operators
                ham.compute_fock(*self._focks)
                # Compute the energy if needed by the history
                energy = ham.compute_energy() if self._history.need_energy \
                         else None
                # Add the current fock+dm pair to the history
                error = self._history.add(energy, dms, self._focks)

                # Screen logging
                if log.do_high:
                    log('          DIIS add')
                if error < self.threshold:
                    converged = True
                    break
                if log.do_high:
                    log.blank()
                if log.do_medium:
                    energy_str = ' ' * 20 if energy is None else '% 20.13f' % energy
                    log('%4i %12.5e                         %2i   %20s' %
                        (counter, error, self._history.nused, energy_str))
                if log.do_high:
                    log.blank()
                fock_interpolated = False
            else:
                energy = None
                fock_interpolated = True

            # Take a regular SCF step using the current fock matrix. Then
            # construct a new density matrix and fock matrix.
            for i in xrange(ham.ndm):
                self._orbs[i].from_fock(self._focks[i], overlap)
            occ_model.assign(*self._orbs)
            for i in xrange(ham.ndm):
                dms[i][:] = self._orbs[i].to_dm()
            ham.reset(*dms)
            energy = ham.compute_energy(
            ) if self._history.need_energy else None
            ham.compute_fock(*self._focks)

            # Add the current (dm, fock) pair to the history
            if log.do_high:
                log('          DIIS add')
            error = self._history.add(energy, dms, self._focks)

            # break when converged
            if error < self.threshold:
                converged = True
                break

            # Screen logging
            if log.do_high:
                log.blank()
            if log.do_medium:
                energy_str = ' ' * 20 if energy is None else '% 20.13f' % energy
                log('%4i %12.5e                         %2i   %20s' %
                    (counter, error, self._history.nused, energy_str))
            if log.do_high:
                log.blank()

            # get extra/intra-polated Fock matrix
            while True:
                # The following method writes the interpolated dms and focks
                # in-place.
                energy_approx, coeffs, cn, method, error = self._history.solve(
                    dms, self._focks)
                # if the error is small on the interpolated state, we have
                # converged to a solution that may have fractional occupation
                # numbers.
                if error < self.threshold:
                    converged = True
                    break
                #if coeffs[coeffs<0].sum() < -1:
                #    if log.do_high:
                #        log('          DIIS (coeffs too negative) -> drop %i and retry' % self._history.stack[0].identity)
                #    self._history.shrink()
                if self._history.nused <= 2:
                    break
                if coeffs[-1] == 0.0:
                    if log.do_high:
                        log('          DIIS (last coeff zero) -> drop %i and retry'
                            % self._history.stack[0].identity)
                    self._history.shrink()
                else:
                    break

            if False and len(coeffs) == 2:
                dms_tmp = [dm.copy() for dm in dms]
                import matplotlib.pyplot as pt
                xs = np.linspace(0.0, 1.0, 25)
                a, b = self._history._setup_equations()
                energies1 = []
                energies2 = []
                for x in xs:
                    x_coeffs = np.array([1 - x, x])
                    energies1.append(
                        np.dot(x_coeffs, 0.5 * np.dot(a, x_coeffs) - b))
                    self._history._build_combinations(x_coeffs, dms_tmp, None)
                    ham.reset(*dms_tmp)
                    energies2.append(ham.compute_energy())
                    print x, energies1[-1], energies2[-1]
                pt.clf()
                pt.plot(xs, energies1, label='est')
                pt.plot(xs, energies2, label='ref')
                pt.axvline(coeffs[1], color='k')
                pt.legend(loc=0)
                pt.savefig('diis_test_%05i.png' % counter)

            if energy_approx is not None:
                energy_change = energy_approx - min(
                    state.energy for state in self._history.stack)
            else:
                energy_change = None

            # log
            if log.do_high:
                self._history.log(coeffs)

            if log.do_medium:
                change_str = ' ' * 10 if energy_change is None else '% 12.7f' % energy_change
                log('%4i              %10.3e %12.7f %2i %s                      %12s'
                    % (counter, cn, coeffs[-1], self._history.nused, method,
                       change_str))

            if log.do_high:
                log.blank()

            if self.prune_old_states:
                # get rid of old states with zero coeff
                for i in xrange(self._history.nused):
                    if coeffs[i] == 0.0:
                        if log.do_high:
                            log('          DIIS insignificant -> drop %i' %
                                self._history.stack[0].identity)
                        self._history.shrink()
                    else:
                        break

            # counter
            counter += 1

        if log.do_medium:
            if converged:
                log('%4i %12.5e (converged)' % (counter, error))
            log.blank()

        if not self.skip_energy or self._history.need_energy:
            if not self._history.need_energy:
                ham.compute_energy()
            if log.do_medium:
                ham.log()

        if not converged:
            raise NoSCFConvergence

        return counter
Пример #13
0
def load_fchk(filename):
    '''Load from a formatted checkpoint file.

       **Arguments:**

       filename
            The filename of the Gaussian formatted checkpoint file.

       **Returns** a dictionary with: ``title``, ``coordinates``, ``numbers``,
       ``obasis``, ``orb_alpha``, ``permutation``, ``energy``,
       ``pseudo_numbers``, ``mulliken_charges``. The dictionary may also
       contain: ``npa_charges``, ``esp_charges``, ``orb_beta``, ``dm_full_mp2``,
       ``dm_spin_mp2``, ``dm_full_mp3``, ``dm_spin_mp3``, ``dm_full_cc``,
       ``dm_spin_cc``, ``dm_full_ci``, ``dm_spin_ci``, ``dm_full_scf``,
       ``dm_spin_scf``, ``polar``, ``dipole_moment``, ``quadrupole_moment``.
    '''
    from horton.gbasis.cext import GOBasis

    fchk = FCHKFile(filename, [
        "Number of electrons",
        "Number of independant functions",
        "Number of independent functions",
        "Number of alpha electrons",
        "Number of beta electrons",
        "Atomic numbers",
        "Current cartesian coordinates",
        "Shell types",
        "Shell to atom map",
        "Shell to atom map",
        "Number of primitives per shell",
        "Primitive exponents",
        "Contraction coefficients",
        "P(S=P) Contraction coefficients",
        "Alpha Orbital Energies",
        "Alpha MO coefficients",
        "Beta Orbital Energies",
        "Beta MO coefficients",
        "Total Energy",
        "Nuclear charges",
        'Total SCF Density',
        'Spin SCF Density',
        'Total MP2 Density',
        'Spin MP2 Density',
        'Total MP3 Density',
        'Spin MP3 Density',
        'Total CC Density',
        'Spin CC Density',
        'Total CI Density',
        'Spin CI Density',
        'Mulliken Charges',
        'ESP Charges',
        'NPA Charges',
        'Polarizability',
        'Dipole Moment',
        'Quadrupole Moment',
    ])

    # A) Load the geometry
    numbers = fchk["Atomic numbers"]
    coordinates = fchk["Current cartesian coordinates"].reshape(-1, 3)
    pseudo_numbers = fchk["Nuclear charges"]
    # Mask out ghost atoms
    mask = pseudo_numbers != 0.0
    numbers = numbers[mask]
    # Do not overwrite coordinates array, because it is needed to specify basis
    system_coordinates = coordinates[mask]
    pseudo_numbers = pseudo_numbers[mask]

    # B) Load the orbital basis set
    shell_types = fchk["Shell types"]
    shell_map = fchk["Shell to atom map"] - 1
    nprims = fchk["Number of primitives per shell"]
    alphas = fchk["Primitive exponents"]
    ccoeffs_level1 = fchk["Contraction coefficients"]
    ccoeffs_level2 = fchk.get("P(S=P) Contraction coefficients")

    my_shell_types = []
    my_shell_map = []
    my_nprims = []
    my_alphas = []
    con_coeffs = []
    counter = 0
    for i, n in enumerate(nprims):
        if shell_types[i] == -1:
            # Special treatment for SP shell type
            my_shell_types.append(0)
            my_shell_types.append(1)
            my_shell_map.append(shell_map[i])
            my_shell_map.append(shell_map[i])
            my_nprims.append(nprims[i])
            my_nprims.append(nprims[i])
            my_alphas.append(alphas[counter:counter + n])
            my_alphas.append(alphas[counter:counter + n])
            con_coeffs.append(ccoeffs_level1[counter:counter + n])
            con_coeffs.append(ccoeffs_level2[counter:counter + n])
        else:
            my_shell_types.append(shell_types[i])
            my_shell_map.append(shell_map[i])
            my_nprims.append(nprims[i])
            my_alphas.append(alphas[counter:counter + n])
            con_coeffs.append(ccoeffs_level1[counter:counter + n])
        counter += n
    my_shell_types = np.array(my_shell_types)
    my_shell_map = np.array(my_shell_map)
    my_nprims = np.array(my_nprims)
    my_alphas = np.concatenate(my_alphas)
    con_coeffs = np.concatenate(con_coeffs)
    del shell_map
    del shell_types
    del nprims
    del alphas

    obasis = GOBasis(coordinates, my_shell_map, my_nprims, my_shell_types,
                     my_alphas, con_coeffs)

    # permutation of the orbital basis functions
    permutation_rules = {
        -9: np.arange(19),
        -8: np.arange(17),
        -7: np.arange(15),
        -6: np.arange(13),
        -5: np.arange(11),
        -4: np.arange(9),
        -3: np.arange(7),
        -2: np.arange(5),
        0: np.array([0]),
        1: np.arange(3),
        2: np.array([0, 3, 4, 1, 5, 2]),
        3: np.array([0, 4, 5, 3, 9, 6, 1, 8, 7, 2]),
        4: np.arange(15)[::-1],
        5: np.arange(21)[::-1],
        6: np.arange(28)[::-1],
        7: np.arange(36)[::-1],
        8: np.arange(45)[::-1],
        9: np.arange(55)[::-1],
    }
    permutation = []
    for shell_type in my_shell_types:
        permutation.extend(permutation_rules[shell_type] + len(permutation))
    permutation = np.array(permutation, dtype=int)

    result = {
        'title': fchk.title,
        'coordinates': system_coordinates,
        'numbers': numbers,
        'obasis': obasis,
        'permutation': permutation,
        'pseudo_numbers': pseudo_numbers,
    }

    # C) Load density matrices
    def load_dm(label):
        if label in fchk:
            dm = np.zeros((obasis.nbasis, obasis.nbasis))
            start = 0
            for i in xrange(obasis.nbasis):
                stop = start + i + 1
                dm[i, :i + 1] = fchk[label][start:stop]
                dm[:i + 1, i] = fchk[label][start:stop]
                start = stop
            return dm

    # First try to load the post-hf density matrices.
    load_orbitals = True
    for key in 'MP2', 'MP3', 'CC', 'CI', 'SCF':
        dm_full = load_dm('Total %s Density' % key)
        if dm_full is not None:
            result['dm_full_%s' % key.lower()] = dm_full
        dm_spin = load_dm('Spin %s Density' % key)
        if dm_spin is not None:
            result['dm_spin_%s' % key.lower()] = dm_spin

    # D) Load the wavefunction
    # Handle small difference in fchk files from g03 and g09
    nbasis_indep = fchk.get("Number of independant functions") or \
                   fchk.get("Number of independent functions")
    if nbasis_indep is None:
        nbasis_indep = obasis.nbasis

    # Load orbitals
    nalpha = fchk['Number of alpha electrons']
    nbeta = fchk['Number of beta electrons']
    if nalpha < 0 or nbeta < 0 or nalpha + nbeta <= 0:
        raise ValueError(
            'The file %s does not contain a positive number of electrons.' %
            filename)
    orb_alpha = Orbitals(obasis.nbasis, nbasis_indep)
    orb_alpha.coeffs[:] = fchk['Alpha MO coefficients'].reshape(
        nbasis_indep, obasis.nbasis).T
    orb_alpha.energies[:] = fchk['Alpha Orbital Energies']
    orb_alpha.occupations[:nalpha] = 1.0
    result['orb_alpha'] = orb_alpha
    if 'Beta Orbital Energies' in fchk:
        # UHF case
        orb_beta = Orbitals(obasis.nbasis, nbasis_indep)
        orb_beta.coeffs[:] = fchk['Beta MO coefficients'].reshape(
            nbasis_indep, obasis.nbasis).T
        orb_beta.energies[:] = fchk['Beta Orbital Energies']
        orb_beta.occupations[:nbeta] = 1.0
        result['orb_beta'] = orb_beta
    elif fchk['Number of beta electrons'] != fchk['Number of alpha electrons']:
        # ROHF case
        orb_beta = Orbitals(obasis.nbasis, nbasis_indep)
        orb_beta.coeffs[:] = fchk['Alpha MO coefficients'].reshape(
            nbasis_indep, obasis.nbasis).T
        orb_beta.energies[:] = fchk['Alpha Orbital Energies']
        orb_beta.occupations[:nbeta] = 1.0
        result['orb_beta'] = orb_beta
        # Delete dm_full_scf because it is known to be buggy
        result.pop('dm_full_scf')

    # E) Load properties
    result['energy'] = fchk['Total Energy']
    if 'Polarizability' in fchk:
        result['polar'] = triangle_to_dense(fchk['Polarizability'])
    if 'Dipole Moment' in fchk:
        result['dipole_moment'] = fchk['Dipole Moment']
    if 'Quadrupole Moment' in fchk:
        # Convert to HORTON ordering: xx, xy, xz, yy, yz, zz
        result['quadrupole_moment'] = fchk['Quadrupole Moment'][[
            0, 3, 4, 1, 5, 2
        ]]

    # F) Load optional properties
    # Mask out ghost atoms from charges
    if 'Mulliken Charges' in fchk:
        result['mulliken_charges'] = fchk['Mulliken Charges'][mask]
    if 'ESP Charges' in fchk:
        result['esp_charges'] = fchk['ESP Charges'][mask]
    if 'NPA Charges' in fchk:
        result['npa_charges'] = fchk['NPA Charges'][mask]

    return result
Пример #14
0
def load_mkl(filename):
    '''Load data from a Molekel file.

    Parameters
    ----------
    filename : str
        The filename of the mkl file.

    Returns
    -------
    results : dict
        Data loaded from file, with keys: ``coordinates``, ``numbers``, ``obasis``,
        ``orb_alpha``. It may also contain: ``orb_beta``, ``signs``.
    '''
    def helper_char_mult(f):
        return [int(word) for word in f.readline().split()]

    def helper_coordinates(f):
        numbers = []
        coordinates = []
        while True:
            line = f.readline()
            if len(line) == 0 or line.strip() == '$END':
                break
            words = line.split()
            numbers.append(int(words[0]))
            coordinates.append(
                [float(words[1]),
                 float(words[2]),
                 float(words[3])])
        numbers = np.array(numbers, int)
        coordinates = np.array(coordinates) * angstrom
        return numbers, coordinates

    def helper_obasis(f, coordinates):
        shell_types = []
        shell_map = []
        nprims = []
        alphas = []
        con_coeffs = []

        center_counter = 0
        in_shell = False
        nprim = None
        while True:
            line = f.readline()
            lstrip = line.strip()
            if len(line) == 0 or lstrip == '$END':
                break
            if len(lstrip) == 0:
                continue
            if lstrip == '$$':
                center_counter += 1
                in_shell = False
            else:
                words = line.split()
                if len(words) == 2:
                    assert in_shell
                    alpha = float(words[0])
                    alphas.append(alpha)
                    con_coeffs.append(float(words[1]))
                    nprim += 1
                else:
                    if nprim is not None:
                        nprims.append(nprim)
                    shell_map.append(center_counter)
                    # always assume pure basis functions
                    shell_type = str_to_shell_types(words[1], pure=True)[0]
                    shell_types.append(shell_type)
                    in_shell = True
                    nprim = 0
        if nprim is not None:
            nprims.append(nprim)

        shell_map = np.array(shell_map)
        nprims = np.array(nprims)
        shell_types = np.array(shell_types)
        alphas = np.array(alphas)
        con_coeffs = np.array(con_coeffs)
        return GOBasis(coordinates, shell_map, nprims, shell_types, alphas,
                       con_coeffs)

    def helper_coeffs(f, nbasis):
        coeffs = []
        energies = []

        in_orb = 0
        while True:
            line = f.readline()
            lstrip = line.strip()
            if len(line) == 0 or lstrip == '$END':
                break
            if in_orb == 0:
                # read a1g line
                words = lstrip.split()
                ncol = len(words)
                assert ncol > 0
                for word in words:
                    assert word == 'a1g'
                cols = [np.zeros((nbasis, 1), float) for icol in xrange(ncol)]
                in_orb = 1
            elif in_orb == 1:
                # read energies
                words = lstrip.split()
                assert len(words) == ncol
                for word in words:
                    energies.append(float(word))
                in_orb = 2
                ibasis = 0
            elif in_orb == 2:
                # read expansion coefficients
                words = lstrip.split()
                assert len(words) == ncol
                for icol in xrange(ncol):
                    cols[icol][ibasis] = float(words[icol])
                ibasis += 1
                if ibasis == nbasis:
                    in_orb = 0
                    coeffs.extend(cols)

        return np.hstack(coeffs), np.array(energies)

    def helper_occ(f):
        occs = []
        while True:
            line = f.readline()
            lstrip = line.strip()
            if len(line) == 0 or lstrip == '$END':
                break
            for word in lstrip.split():
                occs.append(float(word))
        return np.array(occs)

    charge = None
    spinmult = None
    numbers = None
    coordinates = None
    obasis = None
    coeff_alpha = None
    ener_alpha = None
    occ_alpha = None
    coeff_beta = None
    ener_beta = None
    occ_beta = None
    with open(filename) as f:
        while True:
            line = f.readline()
            if len(line) == 0:
                break
            line = line.strip()
            if line == '$CHAR_MULT':
                charge, spinmult = helper_char_mult(f)
            elif line == '$COORD':
                numbers, coordinates = helper_coordinates(f)
            elif line == '$BASIS':
                obasis = helper_obasis(f, coordinates)
            elif line == '$COEFF_ALPHA':
                coeff_alpha, ener_alpha = helper_coeffs(f, obasis.nbasis)
            elif line == '$OCC_ALPHA':
                occ_alpha = helper_occ(f)
            elif line == '$COEFF_BETA':
                coeff_beta, ener_beta = helper_coeffs(f, obasis.nbasis)
            elif line == '$OCC_BETA':
                occ_beta = helper_occ(f)

    if charge is None:
        raise IOError('Charge and multiplicity not found in mkl file.')
    if coordinates is None:
        raise IOError('Coordinates not found in mkl file.')
    if obasis is None:
        raise IOError('Orbital basis not found in mkl file.')
    if coeff_alpha is None:
        raise IOError('Alpha orbitals not found in mkl file.')
    if occ_alpha is None:
        raise IOError('Alpha occupation numbers not found in mkl file.')

    nelec = numbers.sum() - charge
    if coeff_beta is None:
        assert nelec % 2 == 0
        assert abs(occ_alpha.sum() - nelec) < 1e-7
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha / 2
        orb_beta = None
    else:
        if occ_beta is None:
            raise IOError(
                'Beta occupation numbers not found in mkl file while beta orbitals were present.'
            )
        nalpha = int(np.round(occ_alpha.sum()))
        nbeta = int(np.round(occ_beta.sum()))
        assert nelec == nalpha + nbeta
        assert coeff_alpha.shape == coeff_beta.shape
        assert ener_alpha.shape == ener_beta.shape
        assert occ_alpha.shape == occ_beta.shape
        orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1])
        orb_alpha.coeffs[:] = coeff_alpha
        orb_alpha.energies[:] = ener_alpha
        orb_alpha.occupations[:] = occ_alpha
        orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1])
        orb_beta.coeffs[:] = coeff_beta
        orb_beta.energies[:] = ener_beta
        orb_beta.occupations[:] = occ_beta

    result = {
        'coordinates': coordinates,
        'orb_alpha': orb_alpha,
        'numbers': numbers,
        'obasis': obasis,
    }
    if orb_beta is not None:
        result['orb_beta'] = orb_beta
    _fix_molden_from_buggy_codes(result, filename)
    return result