Esempio n. 1
0
def improve_continuum_orbital(atomlist,
                              phi0,
                              potential_ee,
                              E,
                              max_iter=100,
                              thresh=1.0e-6):
    """
    improves an initial guess phi0 for a continuum orbital with energy
    E by repeatedly computing orbital corrections

         phi_{i+1} = a_i * phi_{i} + b_i * dphi_{i}

    The orbital correction delta_phi is obtained as the solution of
    the inhomogeneous Schroedinger equation

        (H-E) dphi  = -(H-E) phi
                  i             i

    The Schroedinger equation is solved approximately by replacing the
    effective potential with a spherical average around each atom. The
    best linear combination of phi and the correction is chosen. The
    coefficients a_i and b_i minimize the expectation value of the residual
                  2
         R = (H-E)
                                     2
       a ,b  = argmin  <phi   |(H-E)  |phi   >
        i  i               i+1            i+1

    Parameters
    ==========
    atomlist      :  list of tuples (Zat,[x,y,z]) with atomic numbers
                     and positions
    phi0          :  callable, phi0(x,y,z) evaluates the initial orbital
    potential_ee  :  callable, potential_ee(x,y,z) evaluates the effective
                     molecular Kohn-Sham potential WITHOUT the nuclear attraction,
                     Vee = Vcoul + Vxc
    E             :  float, energy of continuum orbital

    Optional
    ========
    max_iter   :  int, maximum number of orbital correction
    thresh     :  float, the iteration is stopped as soon as the weight
                  of the orbital correction falls below this threshold:
                        2
                     b_i  <= thresh

    Returns
    =======
    phi        :  callable, phi(x,y,z) evaluates the improved orbital

    """
    # attraction potential between nuclei and electrons
    nuclear_potential = nuclear_potential_func(atomlist)

    # effective potential (electron-electron interaction + nuclei-electrons)
    def potential(x, y, z):
        return potential_ee(x, y, z) + nuclear_potential(x, y, z)

    print("orbital corrections...")

    phi = phi0
    for i in range(0, max_iter):
        residual = residual_ee_func(atomlist, phi, potential_ee, E)

        def source(x, y, z):
            return -residual(x, y, z)

        # orbital correction
        delta_phi = inhomogeneous_schroedinger(atomlist, potential, source, E)

        a, b = variational_mixture_continuum(atomlist, phi, delta_phi,
                                             potential_ee, E)

        # The error is estimated as the norm^2 of the orbital correction:
        #   error = <delta_phi|delta_phi>
        # For large radii, we cannot expect the solution to be correct since
        # the density of points is far too low. Therefore we exclude all points
        # outside the radius `rlarge` from the integration.
        rlarge = 10.0

        def error_density(x, y, z):
            err = abs(delta_phi(x, y, z))**2
            r = np.sqrt(x**2 + y**2 + z**2)
            err[rlarge < r] = 0.0
            return err

        error = integral(atomlist, error_density)
        print("  iteration i= %d   |dphi|^2= %e   b^2= %e  (threshold= %e )" %
              (i + 1, error, b**2, thresh))
        # The solution is converged, when the weight of the
        # correction term b**2 is small enough.
        if b**2 < thresh:
            print("CONVERGED")
            break

        # next approximation for phi
        phi_next = add_two_functions(atomlist, phi, delta_phi, a, b)

        ### DEBUG
        import matplotlib.pyplot as plt
        plt.clf()
        # plot cuts along z-axis
        r = np.linspace(-40.0, 40.0, 10000)
        x = 0.0 * r
        y = 0.0 * r
        z = r

        plt.title("Iteration i=%d" % (i + 1))
        plt.xlabel("z / bohr")
        plt.ylim((-0.6, +0.6))

        plt.plot(r, phi(x, y, z), label=r"$\phi$")
        plt.plot(r, delta_phi(x, y, z), ls="--", label=r"$\Delta \phi$")
        plt.plot(r, phi_next(x, y, z), label=r"$a \phi + b \Delta \phi$")
        plt.plot(r, residual(x, y, z), ls="-.", label="residual $(H-E)\phi$")
        plt.legend()
        plt.savefig("/tmp/iteration_%3.3d.png" % (i + 1))
        #        plt.show()

        ###

        # prepare for next iteration
        phi = phi_next
    else:
        msg = "Orbital corrections did not converge in '%s' iterations!" % max_iter
        print("WARNING: %s" % msg)
        #raise RuntimeError(msg)
    return phi
Esempio n. 2
0
def test_lcao_continuum():
    import matplotlib.pyplot as plt

    # bond length in bohr
    dist = 2.0
    # positions of protons
    posH1 = (0.0, 0.0, -dist / 2.0)
    posH2 = (0.0, 0.0, +dist / 2.0)

    atomlist = [(1, posH1), (1, posH2)]

    # Set resolution of multicenter grid
    settings.radial_grid_factor = 20
    settings.lebedev_order = 23

    # energy of continuum orbital
    E = 1.0

    # same functional as used in the calculation of pseudo orbitals
    xc = XCFunctionals.libXCFunctional(Parameters.pseudo_orbital_x,
                                       Parameters.pseudo_orbital_c)
    dft = BasissetFreeDFT(atomlist, xc)

    print("initial orbital guess from DFTB calculation")
    orbitals = dft.getOrbitalGuess()

    norb = len(orbitals)
    # all orbitals are doubly occupied
    nelec = 2 * norb

    bound_orbitals = dft.getOrbitalGuess()

    # effective potential
    rho = density_func(bound_orbitals)
    veff = effective_potential_func(atomlist, rho, xc, nelec=nelec)

    ps = AtomicPotentialSet(atomlist)

    r = np.linspace(-15.0, 15.0, 10000)
    x = 0.0 * r
    y = 0.0 * r
    z = r

    for lmax in [0, 1, 2, 3]:
        bs = AtomicScatteringBasisSet(atomlist, E, lmax=lmax)

        #test_AO_basis(atomlist, bs, ps, E)

        R = residual2_matrix(atomlist, veff, ps, bs)
        S = continuum_overlap(bs.bfs, E)
        print("continuum overlap")
        print(S)
        print("residual^2 matrix")
        print(R)

        eigvals, eigvecs = sla.eigh(R, S)
        print(eigvals)
        print("eigenvector belonging to lowest eigenvalue")
        print(eigvecs[:, 0])

        # LCAO continuum orbitals
        continuum_orbitals = orbital_transformation(atomlist, bs.bfs, eigvecs)

        # improve continuum orbital by adding a correction term
        #
        #    phi = phi0 + dphi
        #
        # The orbital correction dphi is the solution of the inhomogeneous
        # Schroedinger equation
        #
        #   (H-E)dphi = -(H-E)phi0
        #
        print("orbital correction...")
        phi0 = continuum_orbitals[0]

        phi = improve_continuum_orbital(atomlist, phi0, veff, E)
        exit(-1)

        residual_0 = residual_func(atomlist, phi0, veff, E)

        def source(x, y, z):
            return -residual_0(x, y, z)

        delta_phi = inhomogeneous_schroedinger(atomlist, veff, source, E)
        residual_d = residual_func(atomlist, delta_phi, veff, E)

        a, b = variational_mixture_continuum(atomlist, phi0, delta_phi, veff,
                                             E)

        phi = add_two_functions(atomlist, phi0, delta_phi, a, b)
        residual = residual_func(atomlist, phi, veff, E)

        plt.plot(r, 1.0 / np.sqrt(2.0) * bs.bfs[0](x, y, z), label=r"AO")
        plt.plot(r, phi0(x, y, z), label=r"$\phi_0$")
        plt.plot(r, delta_phi(x, y, z), label=r"$\Delta \phi$")
        plt.plot(r, phi(x, y, z), label=r"$\phi_0 + \Delta \phi$")
        plt.legend()
        plt.show()
        """
        dphi = delta_phi(x,y,z)
        imin = np.argmin(abs(r-1.0))
        dphi[abs(r) < 1.0] = dphi[imin] - (dphi[abs(r) < 1.0] - dphi[imin])
        plt.plot(r, dphi, label=r"$\Delta \phi$")
        """
        plt.plot(r, residual_0(x, y, z), label=r"$(H-E) \phi_0$")
        plt.plot(r, residual_d(x, y, z), label=r"$(H-E)\Delta \phi$")
        plt.plot(r,
                 residual(x, y, z),
                 label=r"$(H-E)(a \phi_0 + b \Delta \phi)$")
        plt.plot(r,
                 a * residual_0(x, y, z) + b * residual_d(x, y, z),
                 ls="-.",
                 label=r"$(H-E)(a \phi_0 + b \Delta \phi)$ (separate)")

        plt.legend()
        plt.show()

        averaged_angular_distribution(atomlist, bound_orbitals,
                                      continuum_orbitals, E)

        # save continuum MOs to cubefiles
        for i, phi in enumerate(continuum_orbitals):

            def func(grid, dV):
                x, y, z = grid
                return phi(x, y, z)

            Cube.function_to_cubefile(
                atomlist,
                func,
                filename="/tmp/cmo_lmax_%2.2d_orb%4.4d.cube" % (lmax, i),
                ppb=5.0)
        #

        for i, phi in enumerate(continuum_orbitals):
            residual = residual_func(atomlist, phi, veff, E)
            delta_e = energy_correction(atomlist,
                                        residual,
                                        phi,
                                        method="Becke")
            print(" orbital %d   energy <%d|H-E|%d> = %e" % (i, i, i, delta_e))

            l, = plt.plot(r,
                          phi(x, y, z),
                          label=r"$\phi_{%d}$ ($l_{max}$ = %d)" % (i, lmax))
            plt.plot(r,
                     residual(x, y, z),
                     ls="-.",
                     label=r"$(H-E)\phi_{%d}$" % i,
                     color=l.get_color())

        plt.legend()
        plt.show()