Ejemplo n.º 1
0
    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
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
    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])
Ejemplo n.º 6
0
    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"
Ejemplo n.º 7
0
    (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()
Ejemplo n.º 12
0
 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,
Ejemplo n.º 14
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)
Ejemplo n.º 15
0
        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
Ejemplo n.º 16
0
    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()
Ejemplo n.º 17
0
    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"
Ejemplo n.º 19
0
    # 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()
Ejemplo n.º 21
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()
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
Ejemplo n.º 23
0
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()
Ejemplo n.º 24
0
    
    # 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"