def test():
    R = 2.0
    Za = 1.0
    Zb = 1.0
    """
    nc2 = 200
    energy_range = np.linspace(-35.0, 20.0, nc2) * 2.0/R**2
    Lsep = SeparationConstants(R, Za, Zb)
    Lsep.tabulate_separation_constants(energy_range, nmax=10, mmax=3)
    Lsep.load_separation_constants()

    discrete_spectrum = DiscreteSpectrum(Lsep)
    #discrete_spectrum.tabulate_discrete_energies()
    #discrete_spectrum.load_discrete_energies()
    discrete_spectrum.binding_curve()
    #Lfunc = Lsep.L_interpolated(0,0)
    #xbound_eigenenergies(R, Za, Zb, energy_range, Lfunc)
    """

    wfn = DimerWavefunctions(R, Za, Zb)
    atomlist = [(1, (0, 0, -R / 2.0)), (1, (0, 0, +R / 2.0))]

    m, n, q = 0, 0, 0
    trig = 'cos'
    energy, (Rfunc1, Sfunc1,
             Pfunc1), wavefunction1 = wfn.getBoundOrbital(m, n, trig, q)
    Cube.function_to_cubefile(
        atomlist,
        wavefunction1,
        filename="/tmp/h2+_bound_orbital_%d_%d_%s_%d.cube" % (m, n, trig, q))
    """
    m,n = 0,1
    trig = 'cos'
    Es = np.linspace(0.01, 15.0, 2)
    phase_shifts = []
    transition_dipoles = []
    for E in Es:
        Delta, (Rfunc2,Sfunc2,Pfunc2),wavefunction2 = wfn.getContinuumOrbital(m,n,trig,E)
        phase_shifts.append(Delta)
        tdip = wfn.transition_dipoles(Rfunc1,Sfunc1,Pfunc1, Rfunc2,Sfunc2,Pfunc2)
        transition_dipoles.append(tdip)
    plt.cla()

    plt.xlabel("Energy / Hartree")
    plt.ylabel("Phase Shift")
    plt.plot(Es, phase_shifts)

    plt.cla()
    transition_dipoles = np.array(transition_dipoles)
    xyz2label = ["X", "Y", "Z"]
    for xyz in range(0, 3):
        plt.plot(Es, transition_dipoles[:,xyz], label="%s" % xyz2label[xyz])
    plt.legend()
    plt.ioff()
    plt.savefig("transition_dipoles.png")
    plt.show()
    """
    m, n, E = 0, 1, 1.0 / 27.211
    trig = 'cos'
    Delta, (Rfunc2, Sfunc2,
            Pfunc2), wavefunction2 = wfn.getContinuumOrbital(m, n, trig, E)
    Cube.function_to_cubefile(
        atomlist,
        wavefunction2,
        filename="/tmp/h2+_continuum_orbital_%d_%d_%s.cube" %
        (m, n, str(E).replace(".", "p")),
        dbuff=15.0)

    plt.ioff()
    plt.show()
def h2_ion_averaged_pad_scan(energy_range,
                             data_file,
                             npts_r=60,
                             rmax=300.0,
                             lebedev_order=23,
                             radial_grid_factor=3,
                             units="eV-Mb",
                             tdip_threshold=1.0e-5):
    """
    compute the photoelectron angular distribution for an ensemble of istropically
    oriented H2+ ions.

    Parameters
    ----------
    energy_range    : numpy array with photoelectron kinetic energies (PKE)
                      for which the PAD should be calculated
    data_file       : path to file, a table with PKE, SIGMA and BETA is written

    Optional
    --------
    npts_r          : number of radial grid points for integration on interval [rmax,rmax+2pi/k]
    rmax            : large radius at which the continuum orbitals can be matched 
                      with the asymptotic solution
    lebedev_order   : order of Lebedev grid for angular integrals
    radial_grid_factor 
                    : factor by which the number of grid points is increased
                      for integration on the interval [0,+inf]
    units           : units for energies and photoionization cross section in output, 'eV-Mb' (eV and Megabarn) or 'a.u.'
    tdip_threshold  : continuum orbitals |f> are neglected if their transition dipole moments mu = |<i|r|f>| to the
                      initial orbital |i> are below this threshold.
    """
    print ""
    print "*******************************************"
    print "*  PHOTOELECTRON ANGULAR DISTRIBUTIONS    *"
    print "*******************************************"
    print ""
    # bond length in bohr
    R = 2.0
    # two protons
    Za = 1.0
    Zb = 1.0
    atomlist = [(1, (0, 0, -R / 2.0)), (1, (0, 0, +R / 2.0))]

    # determine the radius of the sphere where the angular distribution is calculated. It should be
    # much larger than the extent of the molecule
    (xmin, xmax), (ymin, ymax), (zmin, zmax) = Cube.get_bbox(atomlist,
                                                             dbuff=0.0)
    dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin
    # increase rmax by the size of the molecule
    rmax += max([dx, dy, dz])
    Npts = max(int(rmax), 1) * 50
    print "Radius of sphere around molecule, rmax = %s bohr" % rmax
    print "Points on radial grid, Npts = %d" % Npts

    # load separation constants or tabulate them if the file 'separation_constants.dat'
    # does not exist
    Lsep = SeparationConstants(R, Za, Zb)
    try:
        Lsep.load_separation_constants()
    except IOError as err:
        nmax = 10
        mmax = 3
        print "generating separation constants..."
        nc2 = 200
        energy_range = np.linspace(-35.0, 20.0, nc2) * 2.0 / R**2
        Lsep.tabulate_separation_constants(energy_range, nmax=nmax, mmax=mmax)
        Lsep.load_separation_constants()

    ### DEBUG
    #Lsep.mmax = 1
    #Lsep.nmax = 1
    ###

    #
    wfn = DimerWavefunctions(R, Za, Zb)
    # compute the bound orbitals without any radial nor angular nodes,
    # the 1sigma_g orbital
    m, n, q = 0, 0, 0
    trig = 'cos'
    energy, (Rfunc1, Sfunc1,
             Pfunc1), wavefunction1 = wfn.getBoundOrbital(m, n, trig, q)
    print "Energy: %s Hartree" % energy
    bound_orbital = WrapperWavefunction(wavefunction1)

    ### DEBUG
    # save cube file for initial bound orbital
    Cube.function_to_cubefile(atomlist,
                              wavefunction1,
                              filename="/tmp/h2+_bound_orbital_%d_%d_%d.cube" %
                              (m, n, q),
                              dbuff=10.0)
    ###

    # compute PADs for all energies
    pad_data = []
    print "  SCAN"

    print "  Writing table with PAD to %s" % data_file
    # table headers
    header = ""
    header += "# npts_r: %s  rmax: %s" % (npts_r, rmax) + '\n'
    header += "# lebedev_order: %s  radial_grid_factor: %s" % (
        lebedev_order, radial_grid_factor) + '\n'
    if units == "eV-Mb":
        header += "# PKE/eV     SIGMA/Mb       BETA2" + '\n'
    else:
        header += "# PKE/Eh     SIGMA/bohr^2   BETA2" + '\n'

    # save table
    access_mode = MPI.MODE_WRONLY | MPI.MODE_CREATE
    fh = MPI.File.Open(comm, data_file, access_mode)
    if rank == 0:
        # only process 0 write the header
        fh.Write_ordered(header)
    else:
        fh.Write_ordered('')

    for i, energy in enumerate(energy_range):
        if i % size == rank:
            print "    PKE = %6.6f Hartree  (%4.4f eV)" % (
                energy, energy * AtomicData.hartree_to_eV)

            # continuum orbitals at a given energy
            continuum_orbitals = []
            # quantum numbers of continuum orbitals (m,n,trig)
            quantum_numbers = []
            phase_shifts = []
            # transition dipoles
            nc = 2 * (Lsep.mmax + 1) * (
                Lsep.nmax +
                1) - 1  # number of continuum orbitals at each energy
            dipoles_RSP = np.zeros((1, nc, 3))
            #
            print "Continuum orbital         m           n                     P                  phase shift (in \pi rad)"
            print "                    # nodes in P                 cos(m*pi) or sin(m*pi)                                "
            print "-------------------------------------------------------------------------------------------------------"
            ic = 0  # enumerate continuum orbitals
            for m in range(0, Lsep.mmax + 1):
                if m > 0:
                    # for m > 0, there are two solutions for P(phi): sin(m*phi) and cos(m*phi)
                    Psolutions = ['cos', 'sin']
                else:
                    Psolutions = ['cos']
                for trig in Psolutions:
                    for n in range(0, Lsep.nmax + 1):
                        Delta, (
                            Rfunc2, Sfunc2,
                            Pfunc2), wavefunction2 = wfn.getContinuumOrbital(
                                m, n, trig, energy)
                        #print "energy= %s eV   m= %d n= %d  phase shift= %s \pi rad" % (energy*AtomicData.hartree_to_eV, m,n,Delta / np.pi)
                        print "      %3.1d             %2.1d        %2.1d                  %s                         %8.6f" % (
                            ic, m, n, trig, Delta)
                        continuum_orbital = WrapperWavefunction(wavefunction2)
                        continuum_orbitals.append(continuum_orbital)
                        quantum_numbers.append((m, n, trig))
                        phase_shifts.append(Delta)
                        ### DEBUG
                        # save cube file for continuum orbital
                        Cube.function_to_cubefile(
                            atomlist,
                            wavefunction2,
                            filename="/tmp/h2+_continuum_orbital_%d_%d_%s.cube"
                            % (m, n, trig),
                            dbuff=10.0)
                        ###

                        # compute transition dipoles exploiting the factorization
                        # of the wavefunction as R(xi)*S(eta)*P(phi). These integrals
                        # are probably more accurate then those using Becke's integration scheme
                        dipoles_RSP[0, ic, :] = wfn.transition_dipoles(
                            Rfunc1, Sfunc1, Pfunc1, Rfunc2, Sfunc2, Pfunc2)
                        ic += 1

            print ""

            # transition dipoles between bound and free orbitals
            dipoles = transition_dipole_integrals(
                atomlist, [bound_orbital],
                continuum_orbitals,
                radial_grid_factor=radial_grid_factor,
                lebedev_order=lebedev_order)
            # Continuum orbitals with vanishing transition dipole moments to the initial orbital
            # do not contribute to the PAD. Filter out those continuum orbitals |f> for which
            # mu^2 = |<i|r|f>|^2  <  threshold
            mu = np.sqrt(np.sum(dipoles[0, :, :]**2, axis=-1))
            dipoles_important = dipoles[0, mu > tdip_threshold, :]
            continuum_orbitals_important = [
                continuum_orbitals[i]
                for i in range(0, len(continuum_orbitals))
                if mu[i] > tdip_threshold
            ]
            quantum_numbers_important = [
                quantum_numbers[i] for i in range(0, len(continuum_orbitals))
                if mu[i] > tdip_threshold
            ]
            phase_shifts_important = [[
                phase_shifts[i]
            ] for i in range(0, len(continuum_orbitals))
                                      if mu[i] > tdip_threshold]

            print " %d of %d continuum orbitals have non-vanishing transition dipoles " \
                % (len(continuum_orbitals_important), len(continuum_orbitals))
            print " to initial orbital (|<i|r|f>| > %e)" % tdip_threshold

            print "  H2+ Quantum Numbers"
            print "  ==================="
            row_labels = [
                "orb. %2.1d" % a
                for a in range(0, len(quantum_numbers_important))
            ]
            col_labels = ["M", "N", "Trig"]
            txt = annotated_matrix(np.array(quantum_numbers_important),
                                   row_labels,
                                   col_labels,
                                   format="%s")
            print txt

            print "  Phase Shifts (in units of pi)"
            print "  ============================="
            row_labels = [
                "orb. %2.1d" % a for a in range(0, len(phase_shifts_important))
            ]
            col_labels = ["Delta"]
            txt = annotated_matrix(np.array(phase_shifts_important) / np.pi,
                                   row_labels,
                                   col_labels,
                                   format="  %8.6f  ")
            print txt

            print "  Transition Dipoles"
            print "  =================="
            print "  threshold = %e" % tdip_threshold
            row_labels = [
                "orb. %2.1d" % a
                for a in range(0, len(quantum_numbers_important))
            ]
            col_labels = ["X", "Y", "Z"]
            txt = annotated_matrix(dipoles_important, row_labels, col_labels)
            print txt

            # compare transition dipoles from two integration methods
            print "check transition dipoles between bound and continuum orbitals"
            ic = 0  # enumerate continuum orbitals
            for m in range(0, Lsep.mmax + 1):
                if m > 0:
                    # for m > 0, there are two solutions for P(phi): sin(m*phi) and cos(m*phi)
                    Psolutions = ['cos', 'sin']
                else:
                    Psolutions = ['cos']
                for trig in Psolutions:
                    for n in range(0, Lsep.nmax + 1):
                        print "continuum orbital %d  (m= %d  n= %d  trig= %s)" % (
                            ic, m, n, trig)
                        print " from factorization R*S*P :  %s" % dipoles_RSP[
                            0, ic, :]
                        print " Becke's integration      :  %s" % dipoles[
                            0, ic, :]

                        ic += 1
            print ""

            #
            Cs, LMs = asymptotic_Ylm(continuum_orbitals_important,
                                     energy,
                                     rmax=rmax,
                                     npts_r=npts_r,
                                     lebedev_order=lebedev_order)

            # expand product of continuum orbitals into spherical harmonics of order L=0,2
            Amo, LMs = angular_product_distribution(
                continuum_orbitals_important,
                energy,
                rmax=rmax,
                npts_r=npts_r,
                lebedev_order=lebedev_order)

            # compute PAD for ionization from orbital 0 (the bound orbital)
            sigma, beta = photoangular_distribution(dipoles_important, Amo,
                                                    LMs, energy)

            if units == "eV-Mb":
                energy *= AtomicData.hartree_to_eV
                # convert cross section sigma from bohr^2 to Mb
                sigma *= AtomicData.bohr2_to_megabarn

            pad_data.append([energy, sigma, beta])
            # save row with PAD for this energy to table
            row = "%10.6f   %10.6e  %+10.6e" % tuple(pad_data[-1]) + '\n'
            fh.Write_ordered(row)

    fh.Close()

    print "  Photoelectron Angular Distribution"
    print "  =================================="
    print "  units: %s" % units
    row_labels = [" " for en in energy_range]
    col_labels = ["energy", "sigma", "beta2"]
    txt = annotated_matrix(np.array(pad_data).real, row_labels, col_labels)
    print txt

    print "FINISHED"
    m,n,q = 0,0,0
    trig = 'cos'
    energy,(Rfunc1,Sfunc1,Pfunc1),wavefunction1 = wfn.getBoundOrbital(m,n,trig,q)
    print "Energy: %s Hartree" % energy
    """
    # compute the continuum orbital with PKE=5 eV
    m, n, E = 0, 1, 5.0 / 27.211
    trig = 'cos'
    Delta, (Rfunc2, Sfunc2,
            Pfunc2), wavefunction2 = wfn.getContinuumOrbital(m, n, trig, E)
    print "Phase shift: %s" % Delta

    Cube.function_to_cubefile(
        atomlist,
        wavefunction2,
        filename="/tmp/h2+_continuum_orbital_%d_%d_%s.cube" %
        (m, n, str(E).replace(".", "p")),
        dbuff=15.0,
        ppb=2.5)

    from DFTB.Scattering import PAD
    PAD.asymptotic_density(wavefunction2, 20.0, E)

    plt.show()

    # build LCAO of two atomic s-waves with PKE=5 eV
    from DFTB.Scattering.SlakoScattering import AtomicScatteringBasisSet
    bs = AtomicScatteringBasisSet(atomlist, E)
    lcao_continuum = np.array([
        +1.0,
        0.0,
Exemple #4
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()