コード例 #1
0
def solve_poisson(grid: TransformedGrid, rho: np.ndarray) -> np.ndarray:
    """Solve the radial poisson equation for a spherically symmetric density."""
    norm = grid.integrate(4 * np.pi * grid.points**2 * rho)
    int1 = grid.antiderivative(grid.points * rho)
    int1 -= int1[0]
    int2 = grid.antiderivative(int1)
    int2 -= int2[0]
    pot = -(4 * np.pi) * int2
    alpha = (norm - pot[-1]) / grid.points[-1]
    pot += alpha * grid.points
    pot /= grid.points
    return pot
コード例 #2
0
ファイル: test_tinygrid.py プロジェクト: thonmaker/CompFizz
def test_tf_grid_exp():
    # pylint: disable=redefined-outer-name
    def transform(x, np):
        return 10 * np.arctanh((1 + x) / 2)**2

    grid = TransformedGrid(transform, 201)
    fnvals = np.exp(-grid.points)
    fnvalsa = grid.antiderivative(fnvals)
    fnvalsa += -1 - fnvalsa[0]
    fnvalsd = grid.derivative(fnvals)
    assert_allclose(grid.integrate(fnvals), 1.0, atol=1e-13, rtol=0)
    assert_allclose(fnvalsa, -fnvals, atol=1e-7, rtol=0)
    assert_allclose(fnvalsd, -fnvals, atol=1e-7, rtol=0)
コード例 #3
0
ファイル: test_tinygrid.py プロジェクト: thonmaker/CompFizz
def test_tf_grid_hydrogen_few():
    # pylint: disable=redefined-outer-name
    def transform(x, np):
        left = 1e-2
        right = 1e3
        alpha = np.log(right / left)
        return left * (np.exp(alpha * (1 + x) / 2) - 1)

    grid = TransformedGrid(transform, 101)

    # Solutions of the radial equation (U=R/r)
    psi_1s = np.sqrt(4 * np.pi) * grid.points * np.exp(-grid.points) / np.sqrt(
        np.pi)
    psi_2s = np.sqrt(4 * np.pi) * grid.points * np.exp(-grid.points / 2) / \
        np.sqrt(2 * np.pi) / 4 * (2 - grid.points)

    # Check norms with vectorization
    norms = grid.integrate(np.array([psi_1s, psi_2s])**2)
    assert_allclose(norms, 1.0, atol=1e-14, rtol=0)

    # Check norms and energies
    for eps, psi in [(-0.5, psi_1s), (-0.125, psi_2s)]:
        ekin = grid.integrate(-psi * grid.derivative(grid.derivative(psi)) / 2)
        assert_allclose(ekin, -eps, atol=1e-11, rtol=0)
        epot = grid.integrate(-psi**2 / grid.points)
        assert_allclose(epot, 2 * eps, atol=1e-14, rtol=0)

    dot = grid.integrate(psi_1s * psi_2s)
    assert_allclose(dot, 0.0, atol=1e-15, rtol=0)
コード例 #4
0
    def __init__(self,
                 grid: TransformedGrid,
                 alphamin: float = 1e-6,
                 alphamax: float = 1e7,
                 nbasis: int = 80):
        """Initialize a basis.

        Parameters
        ----------
        grid
            The radial integration grid.
        alphamin
            The lowest Gaussian exponent.
        alphamax
            The highest Gaussian exponent.
        nbasis
            The number of basis functions.

        """
        self.grid = grid
        self.alphas = 10**np.linspace(np.log10(alphamin), np.log10(alphamax),
                                      nbasis)
        self.fnvals = np.exp(
            -np.outer(self.alphas, grid.points**2)) * grid.points
        self.normalizations = np.sqrt(np.sqrt(self.alphas))**3 * np.sqrt(
            np.sqrt(2 / np.pi) * 8)
        self.fnvals *= self.normalizations[:, np.newaxis]
        assert_allclose(np.sqrt(grid.integrate(self.fnvals**2)),
                        1.0,
                        atol=7e-14,
                        rtol=0)
コード例 #5
0
ファイル: test_tinygrid.py プロジェクト: thonmaker/CompFizz
def test_tf_grid_exp_vectorized():
    # pylint: disable=redefined-outer-name
    def transform(x, np):
        return 15 * np.arctanh((1 + x) / 2)**2

    grid = TransformedGrid(transform, 201)
    exponents = np.array([1.0, 0.5, 2.0])
    fnsvals = np.exp(-np.outer(exponents, grid.points))
    fnsvalsa = grid.antiderivative(fnsvals)
    fnsvalsa += (-1 / exponents - fnsvalsa[:, 0])[:, np.newaxis]
    fnsvalsd = grid.derivative(fnsvals)
    assert_allclose(grid.integrate(fnsvals), 1 / exponents, atol=1e-13, rtol=0)
    assert_allclose(fnsvalsa,
                    -fnsvals / exponents[:, np.newaxis],
                    atol=1e-7,
                    rtol=0)
    assert_allclose(fnsvalsd,
                    -fnsvals * exponents[:, np.newaxis],
                    atol=1e-7,
                    rtol=0)
    fns_other = np.exp(-np.outer([1.1, 1.2, 0.8], grid.points))
    integrals2 = np.array([[grid.integrate(fn1 * fn2) for fn2 in fns_other]
                           for fn1 in fnsvals])
    assert_allclose(grid.integrate(fnsvals, fns_other),
                    integrals2,
                    atol=1e-14,
                    rtol=0)
コード例 #6
0
def setup_grid(npoint: int = 256) -> TransformedGrid:
    """Create a suitable grid for integration and differentiation."""
    # pylint: disable=redefined-outer-name
    def transform(x: np.ndarray, np) -> np.ndarray:
        """Transform from [-1, 1] to [0, big_radius]."""
        left = 1e-3
        right = 1e4
        alpha = np.log(right / left)
        return left * (np.exp(alpha * (1 + x) / 2) - 1)
    return TransformedGrid(transform, npoint)
コード例 #7
0
def scf_atom(atnum: float, occups: List[np.ndarray], grid: TransformedGrid,
             basis: Basis, nscf: int = 25, mixing: float = 0.5) \
        -> Tuple[np.ndarray, List[Tuple[np.ndarray, np.ndarray]]]:
    """Perform a self-consistent field atomic calculation.

    Parameters
    ----------
    atnum
        The nuclear charge.
    occups
        Occupation numbers, see interpret_econf.
    grid
        A radial grid.
    basis
        The radial orbital basis.
    nscf
        The number of SCF cycles.
    mixing
        The SCF mixing parameter. 1 means the old Fock operator is not mixed in.

    Returns
    -------
    energies
        The atomic energy and its contributions.
    eps_orbs_u
        A list of tuples of (orbital energy, orbital coefficients). One tuple
        for each angular momentum quantum number. The orbital coefficients
        represent the radial solutions U = R/r.

    """
    # Fock operators from previous iteration, used for mixing.
    focks_old = []
    # Volume element in spherical coordinates
    vol = 4 * np.pi * grid.points**2

    nelec = np.concatenate(occups).sum()
    maxangqn = len(occups) - 1
    print("Occupation numbers per ang. mom.  {:}".format(occups))
    print("Number of electrons               {:8.1f}".format(nelec))
    print("Maximum ang. mol. quantum number  {:8d}".format(maxangqn))
    print()
    print("Number of SCF iterations          {:8d}".format(nscf))
    print("Mixing parameter                  {:8.3f}".format(mixing))
    print()

    # pylint: disable=redefined-outer-name
    def excfunction(rho, np):
        """Compute the exchange(-correlation) energy density."""
        clda = (3 / 4) * (3.0 / np.pi)**(1 / 3)
        return -clda * rho**(4 / 3)

    print(
        " It           Total         Rad Kin         Ang Kin         Hartree "
        "             XC             Ext")
    print(
        "=== =============== =============== =============== =============== "
        "=============== ===============")

    # SCF cycle
    # For the first iteration, the density is set to zero to obtain the core guess.
    rho = np.zeros(len(grid.points))
    vhartree = np.zeros(len(grid.points))
    vxc = np.zeros(len(grid.points))
    for iscf in range(nscf):
        # A) Solve for each angular momentum the radial Schrodinger equation.
        # orbitals energy and radial orbitals: U = R/r
        eps_orbs_u = []
        energy_ext = 0.0
        energy_kin_rad = 0.0
        energy_kin_ang = 0.0
        # Hartree and XC potential are the same for all angular momenta.
        jxc = basis.pot(vhartree + vxc)
        for angqn in range(maxangqn + 1):
            # The new fock matrix.
            fock = basis.kin_rad + atnum * basis.ext + jxc
            if angqn > 0:
                angmom_factor = (angqn * (angqn + 1)) / 2
                fock += basis.kin_ang * angmom_factor
            # Mix with the old fock matrix, if available.
            if iscf == 0:
                fock_mix = fock
                focks_old.append(fock)
            else:
                fock_mix = mixing * fock + (1 - mixing) * focks_old[angqn]
                focks_old[angqn] = fock_mix
            # Solve for the occupied orbitals.
            evals, evecs = eigh(fock_mix,
                                basis.olp,
                                eigvals=(0, len(occups[angqn]) - 1))
            eps_orbs_u.append((evals, evecs))
            # Compute the kinetic energy contributions using the orbitals.
            energy_kin_rad += np.einsum('i,ji,jk,ki', occups[angqn], evecs,
                                        basis.kin_rad, evecs)
            energy_ext += atnum * np.einsum('i,ji,jk,ki', occups[angqn], evecs,
                                            basis.ext, evecs)
            if angqn > 0:
                energy_kin_ang += np.einsum('i,ji,jk,ki', occups[angqn], evecs,
                                            basis.kin_ang,
                                            evecs) * angmom_factor

        # B) Build the density and derived quantities.
        # Compute the total density.
        rho = build_rho(occups, eps_orbs_u, grid, basis)
        # Check the total number of electrons.
        assert_allclose(grid.integrate(rho * vol), nelec, atol=1e-10, rtol=0)
        # Solve the Poisson problem for the new density.
        vhartree = solve_poisson(grid, rho)
        energy_hartree = 0.5 * grid.integrate(vhartree * rho * vol)
        # Compute the exchange-correlation potential and energy density.
        exc, vxc = xcfunctional(rho, excfunction)
        energy_xc = grid.integrate(exc * vol)
        # Compute the total energy.
        energy = energy_kin_rad + energy_kin_ang + energy_hartree + energy_xc + energy_ext
        print("{:3d} {:15.6f} {:15.6f} {:15.6f} {:15.6f} {:15.6f} {:15.6f}".
              format(iscf, energy, energy_kin_rad, energy_kin_ang,
                     energy_hartree, energy_xc, energy_ext))

    # Assemble return values
    energies = np.array([
        energy, energy_kin_rad, energy_kin_ang, energy_hartree, energy_xc,
        energy_ext
    ])
    return energies, eps_orbs_u