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,
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()