def _computeMO(self, i): cube = CubeData() atomlist = self.tddftb.dftb2.getGeometry() (xmin, xmax), (ymin, ymax), (zmin, zmax) = Cube.get_bbox(atomlist, dbuff=5.0) dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin ppb_text = self.ppbGrid.itemText(self.ppbGrid.currentIndex()) ppb = float(ppb_text.split()[1]) # points per bohr nx, ny, nz = int(dx * ppb), int(dy * ppb), int(dz * ppb) x, y, z = np.mgrid[xmin:xmax:nx * 1j, ymin:ymax:ny * 1j, zmin:zmax:nz * 1j] grid = (x, y, z) # orbs = self.tddftb.dftb2.getKSCoefficients() moGrid = Cube.orbital_amplitude(grid, self.bs.bfs, orbs[:, i], cache=True) mo_cube = CubeData() mo_cube.data = moGrid.real mo_cube.grid = grid mo_cube.atomlist = atomlist return mo_cube
def test_dipole_prepared_continuum(): """ see G. Fronzoni, M. Stener, S. Furlan, P. Decleva Chemical Physics 273 (2001) 117-133 """ from DFTB.LR_TDDFTB import LR_TDDFTB from DFTB import XYZ from DFTB.Scattering import slako_tables_scattering # BOUND ORBITAL = H**O atomlist = XYZ.read_xyz("./water.xyz")[0] tddftb = LR_TDDFTB(atomlist) tddftb.setGeometry(atomlist, charge=0) options = {"nstates": 1} tddftb.getEnergies(**options) valorbs, radial_val = load_pseudo_atoms(atomlist) H**O, LUMO = tddftb.dftb2.getFrontierOrbitals() bound_orbs = tddftb.dftb2.getKSCoefficients() # polarization direction of E-field epol = np.array([0.0, 1.0, 0.0]) # according to Koopman's theorem the electron is ionized from the H**O mo_indx = range(0, len(bound_orbs)) nmo = len(mo_indx) for imo in range(0, nmo): mo_bound = bound_orbs[:, mo_indx[imo]] # CONTINUUM ORBITALS at energy E for E in slako_tables_scattering.energies: bs = AtomicScatteringBasisSet(atomlist, E) SKT_bf, SKT_ff = load_slako_scattering(atomlist, E) Dipole = ScatteringDipoleMatrix(atomlist, valorbs, SKT_bf) # projection of dipoles onto polarization direction Dipole_projected = np.zeros((Dipole.shape[0], Dipole.shape[1])) for xyz in [0, 1, 2]: Dipole_projected += Dipole[:, :, xyz] * epol[xyz] # unnormalized coefficients of dipole-prepared continuum orbitals mo_scatt = np.dot(mo_bound, Dipole_projected) nrm2 = np.dot(mo_scatt, mo_scatt) # normalized coefficients mo_scatt /= np.sqrt(nrm2) Cube.orbital2grid(atomlist, bs.bfs, mo_scatt, \ filename="/tmp/scattering_orbital_%d_to_%s.cube" % (imo, str(E).replace(".", "p")), dbuff=25.0) delattr(Cube.orbital_amplitude, "cached_grid")
def wavefunction(grid, dV): # evaluate orbital amp = Cube.orbital_amplitude(grid, bs.bfs, scat_orbs[:, i], cache=False) return amp
def _computeTransitionAndDifferenceDensity(self, I): Ptrans = self.tddftb.TransitionDensityMatrix(I) P0, PI = self.tddftb.ExcitedDensityMatrix(I) Pdif = PI - P0 cube = CubeData() atomlist = self.tddftb.dftb2.getGeometry() (xmin, xmax), (ymin, ymax), (zmin, zmax) = Cube.get_bbox(atomlist, dbuff=5.0) dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin ppb_text = self.ppbGrid.itemText(self.ppbGrid.currentIndex()) ppb = float(ppb_text.split()[1]) # points per bohr #print "points per bohr: %s" % ppb nx, ny, nz = int(dx * ppb), int(dy * ppb), int(dz * ppb) x, y, z = np.mgrid[xmin:xmax:nx * 1j, ymin:ymax:ny * 1j, zmin:zmax:nz * 1j] grid = (x, y, z) # transition density tdenseGrid = Cube.electron_density(grid, self.bs.bfs, Ptrans) tdense_cube = CubeData() tdense_cube.data = tdenseGrid tdense_cube.grid = grid tdense_cube.atomlist = atomlist ## approximate difference density based on particle and hole charges #dqI = self.partial_charges[I] #difdenseGrid = self.bs_aux.partial_density(dqI, 0.0*self.tddftb.dftb2.ddip, x,y,z) # exact difference density difdenseGrid = Cube.electron_density(grid, self.bs.bfs, Pdif) difdense_cube = CubeData() difdense_cube.data = difdenseGrid difdense_cube.grid = grid difdense_cube.atomlist = atomlist return tdense_cube, difdense_cube
def plotBoundOrbital(self): selected = self.orbitalSelection.selectedItems() assert len(selected) == 1 selected_orbital = self.orbital_dict[str(selected[0].text())] self.mo_bound = self.bound_orbs[:, selected_orbital] # shift geometry so that the expectation value of the dipole operator vanishes # dipole matrix dipole = np.tensordot(self.mo_bound, np.tensordot(self.tddftb.dftb2.D, self.mo_bound, axes=(1, 0)), axes=(0, 0)) print "expectation value of dipole: %s" % dipole # shift molecule R -> R - dipole self.atomlist = MolCo.transform_molecule( self.tddftb.dftb2.getGeometry(), (0, 0, 0), -dipole) # load bound basis functions self.bs_bound = AtomicBasisSet(self.atomlist) # plot selected orbital (xmin, xmax), (ymin, ymax), (zmin, zmax) = Cube.get_bbox(self.atomlist, dbuff=5.0) dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin ppb = 3.0 # Points per bohr nx, ny, nz = int(dx * ppb), int(dy * ppb), int(dz * ppb) x, y, z = np.mgrid[xmin:xmax:nx * 1j, ymin:ymax:ny * 1j, zmin:zmax:nz * 1j] grid = (x, y, z) amplitude_bound = Cube.orbital_amplitude(grid, self.bs_bound.bfs, self.mo_bound, cache=False) bound_cube = CubeData() bound_cube.data = amplitude_bound.real bound_cube.grid = grid bound_cube.atomlist = self.atomlist self.boundOrbitalViewer.setCubes([bound_cube])
def loadContinuum(self): E = self.photo_kinetic_energy k = np.sqrt(2 * E) wavelength = 2.0 * np.pi / k # 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(self.atomlist, dbuff=0.0) dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin Rmax0 = self.settings.getOption("Averaging", "sphere radius Rmax") Rmax = max([dx, dy, dz]) + Rmax0 Npts = max(int(Rmax), 1) * 50 print "Radius of sphere around molecule, Rmax = %s bohr" % Rmax print "Points on radial grid, Npts = %d" % Npts self.bs_free = AtomicScatteringBasisSet(self.atomlist, E, rmin=0.0, rmax=Rmax + 2 * wavelength, Npts=Npts) self.SKT_bf, SKT_ff = load_slako_scattering(self.atomlist, E) if self.settings.getOption("Continuum Orbital", "Ionization transitions") == "inter-atomic": inter_atomic = True else: inter_atomic = False print "inter-atomic transitions: %s" % inter_atomic self.Dipole = ScatteringDipoleMatrix(self.atomlist, self.valorbs, self.SKT_bf, inter_atomic=inter_atomic).real # if self.activate_average.isChecked(): print "ORIENTATION AVERAGING" npts_euler = self.settings.getOption("Averaging", "Euler angle grid points") npts_theta = self.settings.getOption("Averaging", "polar angle grid points") self.orientation_averaging = PAD.OrientationAveraging_small_memory( self.Dipole, self.bs_free, Rmax, E, npts_euler=npts_euler, npts_theta=npts_theta) else: print "NO AVERAGING"
(opts, args) = parser.parse_args() if len(args) < 6: print usage exit(-1) # input cube files Mx_cube_file = args[0] My_cube_file = args[1] Mz_cube_file = args[2] # output cube files Ax_cube_file = args[3] Ay_cube_file = args[4] Az_cube_file = args[5] # load cube files with magnetic dipole density print "load magnetic dipole density from cube files..." atomlist, origin, axes, Mx = Cube.readCube(Mx_cube_file) atomlist, origin, axes, My = Cube.readCube(My_cube_file) atomlist, origin, axes, Mz = Cube.readCube(Mz_cube_file) print "compute vector potential..." Ax,Ay,Az = vector_potential_Poisson(atomlist, origin, axes, Mx, My, Mz, poisson_solver=opts.solver, conv_eps=opts.conv_eps, maxiter=opts.maxiter) # save vector potential Cube.writeCube(Ax_cube_file, atomlist, origin, axes, Ax) Cube.writeCube(Ay_cube_file, atomlist, origin, axes, Ay) Cube.writeCube(Az_cube_file, atomlist, origin, axes, Az) print "x-,y- and z-components of vector potential saved to '%s', '%s' and '%s'" % (Ax_cube_file, Ay_cube_file, Az_cube_file)
def f(x, y, z): # evaluate orbital grid = (x, y, z) amp = Cube.orbital_amplitude(grid, bs.bfs, mo, cache=False) return amp
def test_scattering_orbitals(): from DFTB.LR_TDDFTB import LR_TDDFTB from DFTB import XYZ atomlist = XYZ.read_xyz("h2.xyz")[0] tddftb = LR_TDDFTB(atomlist) tddftb.setGeometry(atomlist, charge=0) options = {"nstates": 1} tddftb.getEnergies(**options) valorbs, radial_val = load_pseudo_atoms(atomlist) E = 5.0 / 27.211 bs = AtomicScatteringBasisSet(atomlist, E) print bs.bfs SKT_bf, SKT_ff = load_slako_scattering(atomlist, E) S_bb, H0_bb = tddftb.dftb2._constructH0andS() S_bf, H0_bf = ScatteringHamiltonianMatrix(atomlist, valorbs, SKT_bf) # invS_bb = la.inv(S_bb) # (H-E*S)^t . Id . (H-E*S) HmE2 = np.dot(H0_bf.conjugate().transpose(), np.dot(invS_bb, H0_bf)) \ - E * np.dot( S_bf.conjugate().transpose(), np.dot(invS_bb, H0_bf)) \ - E * np.dot(H0_bf.conjugate().transpose(), np.dot(invS_bb, S_bf)) \ + E**2 * np.dot(S_bf.conjugate().transpose(), np.dot(invS_bb, S_bf)) Scont = continuum_flux(atomlist, SKT_bf) S2 = np.dot(S_bf.conjugate().transpose(), np.dot(la.inv(S_bb), S_bf)) """ # H2 = np.dot(H0_bf.transpose(), np.dot(la.inv(S_bb), H0_bf)) S2 = np.dot( S_bf.transpose(), np.dot(la.inv(S_bb), S_bf)) print "H2" print H2 print "S2" print S2 scat_orbe2, scat_orbs = sla.eig(H2) #, S2) print "PKE = %s" % E print "Energies^2 = %s" % scat_orbe2 scat_orbe = np.sqrt(scat_orbe2) sort_indx = np.argsort(scat_orbe) scat_orbe = scat_orbe[sort_indx] scat_orbs = scat_orbs[:,sort_indx] print "Energies of scattering orbitals: %s" % scat_orbe orbE = np.argmin(abs(scat_orbe-E)) """ assert np.sum(abs(HmE2.conjugate().transpose() - HmE2)) < 1.0e-10 assert np.sum(abs(S2.conjugate().transpose() - S2)) < 1.0e-10 lambdas, scat_orbs = sla.eigh(HmE2) print "lambdas = %s" % lambdas from DFTB.Scattering import PAD for i in range(0, len(lambdas)): if abs(lambdas[i]) > 1.0e-8: print "%d lambda = %s" % (i, lambdas[i]) ### def wavefunction(grid, dV): # evaluate orbital amp = Cube.orbital_amplitude(grid, bs.bfs, scat_orbs[:, i], cache=False) return amp PAD.asymptotic_density(wavefunction, 20, E) ### for (flm, l, m) in classify_lm(bs, scat_orbs[:, i]): if abs(flm).max() > 1.0e-4: print " %s %s %s" % (l, m, abs(flm)) Cube.orbital2grid(atomlist, bs.bfs, scat_orbs[:,i], \ filename="/tmp/scattering_orbital_%d.cube" % i, dbuff=25.0) delattr(Cube.orbital_amplitude, "cached_grid")
def atomic_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 atomic 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 "" Z = 1 atomlist = [(Z, (0.0, 0.0, 0.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 bound pseudoatoms basis = AtomicBasisSet(atomlist, confined=False) bound_orbital = basis.bfs[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 (n,l,m) quantum_numbers = [] phase_shifts = [] print "compute atomic continuum orbitals ..." valorbs, radial_val, phase_shifts_val = load_pseudo_atoms_scattering( atomlist, energy, rmin=0.0, rmax=2 * rmax, Npts=100000, lmax=3) pos = np.array([0.0, 0.0, 0.0]) for indx, (n, l, m) in enumerate(valorbs[Z]): continuum_orbital = AtomicBasisFunction( Z, pos, n, l, m, radial_val[Z][indx], 0) continuum_orbitals.append(continuum_orbital) quantum_numbers.append((n, l, m)) phase_shifts.append(phase_shifts_val[Z][indx]) # 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 " Quantum Numbers" print " ===============" row_labels = [ "orb. %2.1d" % a for a in range(0, len(quantum_numbers_important)) ] col_labels = ["N", "L", "M"] 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 # 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"
def averaged_pad_scan(xyz_file, dyson_file, selected_orbitals, npts_euler, npts_theta, nskip, inter_atomic, sphere_radius): molecule_name = os.path.basename(xyz_file).replace(".xyz", "") atomlist = XYZ.read_xyz(xyz_file)[-1] # shift molecule to center of mass print "shift molecule to center of mass" pos = XYZ.atomlist2vector(atomlist) masses = AtomicData.atomlist2masses(atomlist) pos_com = MolCo.shift_to_com(pos, masses) atomlist = XYZ.vector2atomlist(pos_com, atomlist) # compute molecular orbitals with DFTB tddftb = LR_TDDFTB(atomlist) tddftb.setGeometry(atomlist, charge=0) options={"nstates": 1} try: tddftb.getEnergies(**options) except DFTB.Solver.ExcitedStatesNotConverged: pass valorbs, radial_val = load_pseudo_atoms(atomlist) if dyson_file == None: print "tight-binding Kohn-Sham orbitals are taken as Dyson orbitals" H**O, LUMO = tddftb.dftb2.getFrontierOrbitals() bound_orbs = tddftb.dftb2.getKSCoefficients() if selected_orbitals == None: # all orbitals selected_orbitals = range(0,bound_orbs.shape[1]) else: selected_orbitals = eval(selected_orbitals, {}, {"H**O": H**O+1, "LUMO": LUMO+1}) print "Indeces of selected orbitals (counting from 1): %s" % selected_orbitals orbital_names = ["orb_%s" % o for o in selected_orbitals] selected_orbitals = np.array(selected_orbitals, dtype=int)-1 # counting from 0 dyson_orbs = bound_orbs[:,selected_orbitals] ionization_energies = -tddftb.dftb2.getKSEnergies()[selected_orbitals] else: print "coeffients for Dyson orbitals are read from '%s'" % dyson_file orbital_names, ionization_energies, dyson_orbs = load_dyson_orbitals(dyson_file) ionization_energies = np.array(ionization_energies) / AtomicData.hartree_to_eV print "" print "*******************************************" print "* PHOTOELECTRON ANGULAR DISTRIBUTIONS *" print "*******************************************" print "" # 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 Rmax = max([dx,dy,dz]) + sphere_radius Npts = max(int(Rmax),1) * 50 print "Radius of sphere around molecule, Rmax = %s bohr" % Rmax print "Points on radial grid, Npts = %d" % Npts nr_dyson_orbs = len(orbital_names) # compute PADs for all selected orbitals for iorb in range(0, nr_dyson_orbs): print "computing photoangular distribution for orbital %s" % orbital_names[iorb] data_file = "betas_" + molecule_name + "_" + orbital_names[iorb] + ".dat" pad_data = [] print " SCAN" nskip = max(1, nskip) # save table fh = open(data_file, "w") print " Writing table with betas to %s" % data_file print>>fh, "# ionization from orbital %s IE = %6.3f eV" % (orbital_names[iorb], ionization_energies[iorb]*AtomicData.hartree_to_eV) print>>fh, "# inter_atomic: %s npts_euler: %s npts_theta: %s rmax: %s" % (inter_atomic, npts_euler, npts_theta, Rmax) print>>fh, "# PKE/eV sigma beta1 beta2 beta3 beta4" for i,E in enumerate(slako_tables_scattering.energies): if i % nskip != 0: continue print " PKE = %6.6f Hartree (%4.4f eV)" % (E, E*AtomicData.hartree_to_eV) k = np.sqrt(2*E) wavelength = 2.0 * np.pi/k bs_free = AtomicScatteringBasisSet(atomlist, E, rmin=0.0, rmax=Rmax+2*wavelength, Npts=Npts) SKT_bf, SKT_ff = load_slako_scattering(atomlist, E) Dipole = ScatteringDipoleMatrix(atomlist, valorbs, SKT_bf, inter_atomic=inter_atomic).real orientation_averaging = PAD.OrientationAveraging_small_memory(Dipole, bs_free, Rmax, E, npts_euler=npts_euler, npts_theta=npts_theta) pad,betasE = orientation_averaging.averaged_pad(dyson_orbs[:,iorb]) pad_data.append( [E*AtomicData.hartree_to_eV] + list(betasE) ) # save PAD for this energy print>>fh, "%10.6f %10.6e %+10.6e %+10.6f %+10.6e %+10.6e" % tuple(pad_data[-1]) fh.flush() fh.close()
def wavefunction(grid, dV): # evaluate continuum orbital amp = Cube.orbital_amplitude(grid, bs.bfs, mo_scatt, cache=False) return amp
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,
default="charges", help= "The charges can be saved to the file either as a single column ('charges') or together with the atomic positions in the xyz-format ('xyz')" ) (opts, args) = parser.parse_args() if len(args) < 2: print(usage) exit(-1) esp_cube_file = args[0] chg_file = args[1] # load cube file with electrostatic potential (esp) print("load ESP from cube file...") atomlist, origin, axes, data = Cube.readCube(esp_cube_file) if opts.fit_centers != "": print("loading fit centers from '%s'" % opts.fit_centers) atomlist = XYZ.read_xyz(opts.fit_centers)[0] # extract positions of points and values of ESP points, epot = Cube.get_points_and_values(origin, axes, data) # fit point charges chelpg = CHELPG() chelpg.setFitPoints(points, epot) chelpg.setAtomicCenters(atomlist, charge=0) partial_charges = chelpg.fitESP() # save point charges if opts.format == 'charges': np.savetxt(chg_file, partial_charges)
dest="nuclear_potential", type=int, help= "Should the nuclear potential be added to the electrostatic potential (0: no, 1: yes) [default: %default]", default=1) (opts, args) = parser.parse_args() if len(args) < 2: print usage exit(-1) rho_cube_file = args[0] pot_cube_file = args[1] # load cube file with electronic density print "load density from cube file..." atomlist, origin, axes, rho = Cube.readCube(rho_cube_file) print "compute electrostatic potential..." pot = electrostatic_potential_Poisson( atomlist, origin, axes, rho, poisson_solver=opts.solver, conv_eps=opts.conv_eps, maxiter=opts.maxiter, nuclear_potential=opts.nuclear_potential) # save potential Cube.writeCube(pot_cube_file, atomlist, origin, axes, pot) print "electrostatic potential saved to '%s'" % pot_cube_file
def plotMFPAD(self): Rmax = 80.0 npts = 30 E = self.photo_kinetic_energy k = np.sqrt(2 * E) wavelength = 2.0 * np.pi / k # spherical grid rs, thetas, phis = np.mgrid[Rmax:(Rmax + wavelength):30j, 0.0:np.pi:npts * 1j, 0.0:2 * np.pi:npts * 1j] # transformed into cartesian coordinates xs = rs * np.sin(thetas) * np.cos(phis) ys = rs * np.sin(thetas) * np.sin(phis) zs = rs * np.cos(thetas) grid = (xs, ys, zs) amplitude_continuum = Cube.orbital_amplitude(grid, self.bs_free.bfs, self.mo_free, cache=False) # integrate continuum orbital along radial-direction for 1 wavelength wfn2 = abs(amplitude_continuum)**2 # dr = wavelength / 30.0 wfn2_angular = np.sum(wfn2 * dr, axis=0) # SPHERICAL PLOT self.MFPADfig3D.clf() xs = wfn2_angular * np.sin(thetas[0, :, :]) * np.cos(phis[0, :, :]) ys = wfn2_angular * np.sin(thetas[0, :, :]) * np.sin(phis[0, :, :]) zs = wfn2_angular * np.cos(thetas[0, :, :]) ax = self.MFPADfig3D.add_subplot(111, projection='3d') rmax = wfn2_angular.max() * 1.5 ax.set_xlim((-rmax, rmax)) ax.set_ylim((-rmax, rmax)) ax.set_zlim((-rmax, rmax)) ax.set_xlabel("x") ax.set_ylabel("y") ax.set_zlabel("z") # draw efield arrow_vec = wfn2_angular.max() * 1.2 * self.epol / la.norm(self.epol) arrow = Arrow3D([0.0, arrow_vec[0]], [0.0, arrow_vec[1]], [0.0, arrow_vec[2]], color=(0.0, 1.0, 0.0), mutation_scale=20, lw=2, arrowstyle="-|>") ax.add_artist(arrow) ax.text(arrow_vec[0], arrow_vec[1], arrow_vec[2], "E-field", color=(0.0, 1.0, 0.0)) ax.plot_surface(xs, ys, zs, rstride=1, cstride=1) ax.scatter(xs, ys, zs, color="k", s=20) ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) ax.w_zaxis.set_ticks([]) self.MFPADCanvas3D.draw() # 2D PLOT self.MFPADfig2D.clf() ax = self.MFPADfig2D.add_subplot(111) image = ax.imshow(np.fliplr(wfn2_angular.transpose()), extent=[0.0, np.pi, 0.0, 2 * np.pi], aspect=0.5, origin='lower') ax.set_xlim((0.0, np.pi)) ax.set_ylim((0.0, 2 * np.pi)) ax.set_xlabel("$\\theta$") ax.set_ylabel("$\phi$") self.MFPADfig2D.colorbar(image) # show piercing points of E-field vector # ... coming out of the plane r = la.norm(self.epol) th_efield = np.arccos(self.epol[2] / r) phi_efield = np.arctan2(self.epol[1], self.epol[0]) + np.pi print "th, phi = %s %s" % (th_efield, phi_efield) ax.plot([th_efield], [phi_efield], "o", markersize=10, color=(0.0, 1.0, 0.0)) ax.text(th_efield, phi_efield, "E-field", color=(0.0, 1.0, 0.0), ha="center", va="top") # ... going into the plane th_efield = np.arccos(-self.epol[2] / r) phi_efield = np.arctan2(-self.epol[1], -self.epol[0]) + np.pi print "- th, phi = %s %s" % (th_efield, phi_efield) ax.plot([th_efield], [phi_efield], "x", markersize=10, color=(0.0, 1.0, 0.0)) self.MFPADCanvas2D.draw()
def plotContinuumOrbital(self): dbuff = self.settings["Cube"]["extra space / bohr"] ppb = self.settings["Cube"]["points per bohr"] (xmin, xmax), (ymin, ymax), (zmin, zmax) = Cube.get_bbox(self.atomlist, dbuff=dbuff) dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin nx, ny, nz = int(dx * ppb), int(dy * ppb), int(dz * ppb) x, y, z = np.mgrid[xmin:xmax:nx * 1j, ymin:ymax:ny * 1j, zmin:zmax:nz * 1j] grid = (x, y, z) # plot continuum orbital # projection of dipoles onto polarization direction Dipole_projected = np.zeros( (self.Dipole.shape[0], self.Dipole.shape[1])) # normalize polarization epol_unit = self.epol / np.sqrt(np.dot(self.epol, self.epol)) print "Polarization direction of E-field:" print "E (normalized) = %s" % epol_unit for xyz in [0, 1, 2]: Dipole_projected += self.Dipole[:, :, xyz] * epol_unit[xyz] #print "Dipole projected" #print Dipole_projected # unnormalized coefficients of dipole-prepared continuum orbitals self.mo_free = np.dot(self.mo_bound, Dipole_projected) nrm2 = np.dot(self.mo_free.conjugate(), self.mo_free) #print "nrm2 = %s" % nrm2 # normalized coefficients self.mo_free /= np.sqrt(nrm2) amplitude_continuum = Cube.orbital_amplitude(grid, self.bs_free.bfs, self.mo_free, cache=False) continuum_cube = CubeData() continuum_cube.data = amplitude_continuum.real continuum_cube.grid = grid continuum_cube.atomlist = self.atomlist self.continuumOrbitalViewer.setCubes([continuum_cube]) # plot E-field for o in self.efield_objects: o.remove() mlab = self.continuumOrbitalViewer.visualization.scene.mlab # self.efield_arrow = mlab.quiver3d(0,0,0, self.epol[0], self.epol[1], self.epol[2], self.efield_arrow = mlab.quiver3d(0, 0, 0, float(self.epol[0]), float(self.epol[1]), float(self.epol[2]), color=(0.0, 1.0, 0.0), scale_factor=1.0, mode='arrow', resolution=20, figure=self.continuumOrbitalViewer. visualization.scene.mayavi_scene) self.efield_text = mlab.text(self.epol[0], self.epol[1], "E-field", z=self.epol[2], figure=self.continuumOrbitalViewer. visualization.scene.mayavi_scene) self.efield_text.actor.set(text_scale_mode='none', width=0.05, height=0.1) self.efield_text.property.set(justification='centered', vertical_justification='centered') self.efield_head = mlab.points3d([self.epol[0]], [self.epol[1]], [self.epol[2]], scale_factor=0.5, mode='cube', resolution=20, color=(0.0, 1.0, 0.0), figure=self.continuumOrbitalViewer. visualization.scene.mayavi_scene) self.efield_head.glyph.glyph_source.glyph_source.center = [0, 0, 0] self.efield_outline = mlab.outline(line_width=3, figure=self.continuumOrbitalViewer. visualization.scene.mayavi_scene) self.efield_outline.outline_mode = 'cornered' w = 0.1 self.efield_outline.bounds = (self.epol[0] - w, self.epol[0] + w, self.epol[1] - w, self.epol[1] + w, self.epol[2] - w, self.epol[2] + w) self.efield_objects = [ self.efield_arrow, self.efield_text, self.efield_head, self.efield_outline ] self.efield_actors = [self.efield_head.actor.actors]
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"
# input cube files # ... for magnetic dipole density Mx_cube_file = args[0] My_cube_file = args[1] Mz_cube_file = args[2] # ... vector potential Ax_cube_file = args[3] Ay_cube_file = args[4] Az_cube_file = args[5] # output file dat_file = args[6] # load cube files components of magnetic dipole density print "load magnetic dipole densities from cube files..." atomlist, origin, axes, Mx = Cube.readCube(Mx_cube_file) atomlist, origin, axes, My = Cube.readCube(My_cube_file) atomlist, origin, axes, Mz = Cube.readCube(Mz_cube_file) dx = la.norm(axes[0]) dy = la.norm(axes[1]) dz = la.norm(axes[2]) # volume element, assuming the axes are orthogonal dV = dx * dy * dz # find total magnetic dipole moment mtot = np.array([np.sum(Mx * dV), np.sum(My * dV), np.sum(Mz * dV)]) # load cube files for components of vector potential print "load magnetic vector potential from cube files..." atomlist, origin, axes, Ax_data = Cube.readCube(Ax_cube_file) atomlist, origin, axes, Ay_data = Cube.readCube(Ay_cube_file)
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 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()
def transform_turbomole_orbitals(tm_dir, dyson_file, method="atomwise", selected_orbitals=None): """ The molecular orbitals from a Turbomole calculation are transformed into the basis used by DFTB. Since in DFTB there are much less atomic orbitals this transformation is not bijective and does not conserve the normalization and orthogonality among the MOs. Parameters: =========== tm_dir: directory with coord, basis and mos files dyson_file: transformed MO coefficients for DFTB are written to this file method: Method used to find the coefficients in the minimal basis 'atomwise': overlaps are only calculated between orbitals on the same atom (very fast). 'grid': the DFT orbital mo(r) and the minimal DFT atomic orbitals ao_i(r) are calculated on a grid and the coefficients are obtained by minimizing the deviation error(c_i) = integral |mo(r) - sum_i c_i ao_i(r)|^2 dr in a least square sense (slower, but more accurate). selected_orbitals: list of indeces (starting at 1, e.g. '[1,2,3]') of orbitals that should be transformed. If not set all orbitals are transformed. """ print "Reading geometry and basis from Turbomole directory %s" % tm_dir Data = Turbomole.parseTurbomole(tm_dir + "/coord") atomlist = Data["coord"] Data = Turbomole.parseTurbomole(tm_dir + "/basis") basis_data = Data["basis"] bs_gaussian = GaussianBasisSet(atomlist, basis_data) # load Turbomole orbitals try: Data = Turbomole.parseTurbomole(tm_dir + "/mos") orbe,orbs_turbomole = Data["scfmo"] except IOError: print "'mos' file not found! Maybe this is an open-shell calculation. Trying to read file 'alpha'" Data = Turbomole.parseTurbomole(tm_dir + "/alpha") orbe,orbs_turbomole = Data["uhfmo_alpha"] # Which orbitals should be transformed? if selected_orbitals == None: selected_mos = np.array(range(0, len(orbe)), dtype=int) else: selected_mos = np.array(eval(selected_orbitals), dtype=int)-1 nmo = len(selected_mos) # transform them to DFTB basis if method == "atomwise": T = bs_gaussian.getTransformation_GTO_to_DFTB() orbs = np.dot(T, orbs_turbomole[:,selected_mos]) elif method == "grid": # define grid for integration (xmin,xmax),(ymin,ymax),(zmin,zmax) = Cube.get_bbox(atomlist, dbuff=7.0) dx,dy,dz = xmax-xmin,ymax-ymin,zmax-zmin ppb = 5.0 # Points per bohr nx,ny,nz = int(dx*ppb),int(dy*ppb),int(dz*ppb) x,y,z = np.mgrid[xmin:xmax:nx*1j, ymin:ymax:ny*1j, zmin:zmax:nz*1j] grid = (x,y,z) dV = dx/float(nx-1) * dy/float(ny-1) * dz/float(nz-1) # numerical atomic DFTB orbitals on the grid bs_atomic = AtomicBasisSet(atomlist) aos = [bf.amp(x,y,z) for bf in bs_atomic.bfs] nao = len(aos) # overlaps S2_mn = <m|n> S2 = np.zeros((nao,nao)) for m in range(0, nao): S2[m,m] = np.sum(aos[m]*aos[m]*dV) for n in range(m+1,nao): S2[m,n] = np.sum(aos[m]*aos[n]*dV) S2[n,m] = S2[m,n] # DFT molecular orbitals on the grid mos = [Cube.orbital_amplitude(grid, bs_gaussian.bfs, orbs_turbomole[:,i]).real for i in selected_mos] # overlaps S1_mi = <m|i>, m is an atomic DFTB orbital, i a molecular DFT orbital S1 = np.zeros((nao,nmo)) for m in range(0, nao): for i in range(0, nmo): S1[m,i] = np.sum(aos[m]*mos[i]*dV) # Linear regression leads to the matrix equation # sum_n S2_mn c_ni = S1_mi # which has to be solved for the coefficients c_ni. orbs = la.solve(S2, S1) else: raise ValueError("Method should be 'atomwise' or 'grid' but not '%s'!" % method) # normalize orbitals, due to the incomplete transformation they are not necessarily # orthogonal dftb2 = DFTB2(atomlist) dftb2.setGeometry(atomlist) S = dftb2.getOverlapMatrix() for i in range(0, nmo): norm2 = np.dot(orbs[:,i], np.dot(S, orbs[:,i])) orbs[:,i] /= np.sqrt(norm2) # save orbitals xyz_file = "geometry.xyz" XYZ.write_xyz(xyz_file, [atomlist]) print "Molecular geometry was written to '%s'." % xyz_file ionization_energies = -orbe[selected_mos] save_dyson_orbitals(dyson_file, ["%d" % (i+1) for i in selected_mos], ionization_energies, orbs, mode="w") print "MO coefficients for DFTB were written to '%s'." % dyson_file
def atomic_pz_orbital(Z, data_file): atomlist = [(Z, (0.0, 0.0, 0.0))] # compute molecular orbitals with DFTB print "compute molecular orbitals with tight-binding DFT" tddftb = LR_TDDFTB(atomlist) tddftb.setGeometry(atomlist, charge=0) options = {"nstates": 1} try: tddftb.getEnergies(**options) except DFTB.Solver.ExcitedStatesNotConverged: pass valorbs, radial_val = load_pseudo_atoms(atomlist) bound_orbs = tddftb.dftb2.getKSCoefficients() # order of orbitals s, py,pz,px, dxy,dyz,dz2,dzx,dx2y2, so the pz-orbital has index 2 mo_bound = bound_orbs[:, 2] tdipole_data = [] for iE, E in enumerate(slako_tables_scattering.energies): print "PKE = %s" % E try: SKT_bf, SKT_ff = load_slako_scattering(atomlist, E) except ImportError: break Dipole = ScatteringDipoleMatrix(atomlist, valorbs, SKT_bf).real # dipole between bound orbital and the continuum AO basis orbitals dipole_bf = np.tensordot(mo_bound, Dipole, axes=(0, 0)) tdip_pz_to_s = dipole_bf[0, 2] # points along z-axis tdip_pz_to_dyz = dipole_bf[5, 1] # points along y-axis tdip_pz_to_dz2 = dipole_bf[6, 2] # points along z-axis tdip_pz_to_dzx = dipole_bf[7, 0] # points along x-axis tdipole_data.append( [E * AtomicData.hartree_to_eV] + [tdip_pz_to_s, tdip_pz_to_dyz, tdip_pz_to_dz2, tdip_pz_to_dzx]) #### # 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 Rmax = max([dx, dy, dz]) + 500.0 print "Radius of sphere around molecule, Rmax = %s bohr" % Rmax k = np.sqrt(2 * E) wavelength = 2.0 * np.pi / k print "wavelength = %s" % wavelength valorbsE, radial_valE = load_pseudo_atoms_scattering(atomlist, E, rmin=0.0, rmax=Rmax + 2 * wavelength, Npts=90000) # Plot radial wavefunctions import matplotlib.pyplot as plt plt.ion() r = np.linspace(0.0, Rmax + 2 * wavelength, 5000) for i, (Zi, posi) in enumerate(atomlist): for indx, (ni, li, mi) in enumerate(valorbsE[Zi]): # only plot the dz2 continuum orbital if li == 2 and mi == 0 and (iE % 10 == 0): R_spl = radial_valE[Zi][indx] radial_orbital_wfn = R_spl(r) plt.plot(r, radial_orbital_wfn, label="Z=%s n=%s l=%s m=%s E=%s" % (Zi, ni, li, mi, E)) plt.plot(r, np.sin(k * r) / r, ls="-.") #plt.plot(r, np.sin(k*r + 1.0/k * np.log(2*k*r))/r, ls="--", lw=2) plt.plot(r, np.array([ float(mpmath.coulombf(li, -1.0 / k, k * rx)) / rx for rx in r ]), ls="--", lw=2, label="CoulombF l=%s E=%s" % (li, E)) plt.draw() #### # save table fh = open(data_file, "w") print >> fh, "# PKE/eV pz->s pz->dyz pz->dz2 pz->dzx" np.savetxt(fh, tdipole_data) fh.close() print "Wrote table with transition dipoles to %s" % data_file # show radial wavefunctions plt.ioff() plt.show()
# Which axes should be integrated out? assert len(axes_str) == 2 ax2index = {"x": 0, "y": 1, "z": 2} axes_integrate = [ax2index[s] for s in axes_str] # Which axis remains? axis_remains = None for i in range(0,3): if not (i in axes_integrate): axis_remains = i break print "axes %s are integrated out" % str(axes_integrate) print "remaining axis: %s" % axis_remains # load the cube file atomlist, origin, axes, data = Cube.readCube(cube_file) axes = [np.array(ax) for ax in axes] x0 = np.array(origin) nx,ny,nz = data.shape ns = [nx,ny,nz] # remaining coordinate is called u du = la.norm(axes[axis_remains]) print "du = %s" % du nu = ns[axis_remains] u = np.linspace(origin[axis_remains], nu*du, nu) # shift axis so that the center is located at u=0 u = u-u[len(u)/2] # rho(u), 1D curve after integrating rho_u = np.zeros(ns[axis_remains])
def averaged_pad_scan(res, initial_orbital, energy_range, data_file, npts_euler=5, npts_theta=50, npts_r=60, rmax=300.0, lmax=1, lebedev_order=23, radial_grid_factor=3, units="eV-Mb"): """ compute the photoelectron angular distribution for an ensemble of istropically oriented molecules. Parameters ---------- res : instance of G09ResultsDFT, contains basis set, MO coefficients of bound orbitals initial_orbital : index (starting from 0) of occupied orbital from which the electron is ionized, only the alpha orbitals are used 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 BETAs is written Optional -------- npts_euler : number of grid points N for numerical integration over molecular orientations for each Euler angle a,b,c npts_theta : number of grid points for theta angle. The molecular frame PAD is computed on the rotated grid for each orientation and averaged. 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 lmax : maximal angular momentum of atomic continuum orbitals and 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.' """ # convert geometry to atomlist format atomlist = [] for i in range(0, res.nat): atomlist.append((res.atomic_numbers[i], res.coordinates[:, i])) print "" print "*******************************************" print "* PHOTOELECTRON ANGULAR DISTRIBUTIONS *" print "*******************************************" print "" # 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 #assert res.nelec_alpha == res.nelec_beta # create wavefunction of bound initial orbital # MO coefficients of initial orbital orbs_initial = res.orbs_alpha[:, initial_orbital] # bound_orbital = GaussianMolecularOrbital(res.basis, orbs_initial) # compute PADs for all energies pad_data = [] print " SCAN" print " Writing table with betas to %s" % data_file # table headers header = "" header += "# formatted checkpoint file: %s" % fchk_file + '\n' header += "# initial orbital: %s" % initial_orbital + '\n' header += "# npts_euler: %s npts_theta: %s npts_r: %s rmax: %s lmax: %s" % ( npts_euler, npts_theta, npts_r, rmax, lmax) + '\n' header += "# lebedev_order: %s radial_grid_factor: %s" % ( lebedev_order, radial_grid_factor) + '\n' if units == "eV-Mb": header += "# PKE/eV SIGMA/Mb BETA1 BETA2 BETA3 BETA4" + '\n' else: header += "# PKE/Eh SIGMA/bohr^2 BETA1 BETA2 BETA3 BETA4" + '\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) # molecular continuum orbitals at energy E continuum_orbitals, phase_shifts, lms = variational_kohn( atomlist, energy, lmax=lmax, rmax=rmax, npts_r=npts_r, radial_grid_factor=radial_grid_factor, lebedev_order=lebedev_order) # 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) ### DEBUG analyze_dipoles_angmom(dipoles, lms) ### # compute PAD for ionization from orbital 0 (the bound orbital) betasE = orientation_averaged_pad(dipoles[0, :, :], continuum_orbitals, energy, rmax=rmax, npts_euler=npts_euler, npts_r=npts_r, npts_theta=npts_theta) if units == "eV-Mb": energy *= AtomicData.hartree_to_eV # convert cross section sigma from bohr^2 to Mb betasE[0] *= AtomicData.bohr2_to_megabarn pad_data.append([energy] + list(betasE)) # save row with PAD for this energy to table row = "%10.6f %10.6e %+10.6e %+10.6f %+10.6e %+10.6e" % tuple( pad_data[-1]) + '\n' fh.Write_ordered(row) fh.Close() print "FINISHED"