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 test_averaged_asymptotic_density():
    from DFTB.LR_TDDFTB import LR_TDDFTB
    from DFTB import XYZ
    from DFTB.Scattering import slako_tables_scattering
    from DFTB.Scattering import PAD

    # 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, 0.0, 1.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):
        print "IMO = %s" % imo
        import time
        time.sleep(1)
        mo_bound = bound_orbs[:, mo_indx[imo]]
        # CONTINUUM ORBITALS at energy E
        for E in slako_tables_scattering.energies[-2:-1]:
            bs = AtomicScatteringBasisSet(atomlist, E)
            SKT_bf, SKT_ff = load_slako_scattering(atomlist, E)
            Dipole = ScatteringDipoleMatrix(atomlist, valorbs, SKT_bf)

            PAD.averaged_asymptotic_density(mo_bound, Dipole, bs, 20.0, E)
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 test_photoangular_distribution():
    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("./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)

    H**O, LUMO = tddftb.dftb2.getFrontierOrbitals()
    bound_orbs = tddftb.dftb2.getKSCoefficients()

    # according to Koopman's theorem the electron is ionized from the H**O
    mo_indx = range(0, len(bound_orbs))
    nmo = len(mo_indx)
    energies = [[] for i in range(0, nmo)]
    sigmas = [[] for i in range(0, nmo)]
    betas = [[] for i in range(0, nmo)]
    tdip2s = [[] for i in range(0, nmo)]
    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:

            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))
            #

            scat_lambdas, scat_orbs = sla.eigh(HmE2)
            print "PKE = %s" % E
            print "lambdas = %s" % scat_lambdas

            # photoangular distribution averaged over isotropically oriented ensemble of molecules
            Dipole = ScatteringDipoleMatrix(atomlist, valorbs, SKT_bf)
            sigma = 0.0
            beta = 0.0
            tdip2 = 0.0
            olap = 0.0
            for i in range(0, len(scat_lambdas)):
                if abs(scat_lambdas[i]) > 1.0e-10:
                    print "%d  lambda = %s" % (i, scat_lambdas[i])
                    mo_scatt = scat_orbs[:, i]
                    sigma_i, beta_i = angular_distribution(
                        atomlist, valorbs, mo_bound, mo_scatt, Dipole)
                    sigma += sigma_i
                    beta += sigma_i * beta_i

                    tdip_i = np.zeros(3, dtype=complex)
                    for xyz in range(0, 3):
                        tdip_i[xyz] += np.dot(
                            mo_bound, np.dot(Dipole[:, :, xyz], mo_scatt))
                    tdip2 += np.sum(abs(tdip_i)**2)

            print "tdip2 = %s" % tdip2
            beta /= sigma

            ####
            #sigma_test, beta_test = angular_distribution_old2(atomlist, valorbs, mo_bound, mo_scatt, Dipole)
            #assert abs(sigma-sigma_test) < 1.0e-10
            #assert abs(beta-beta_test) < 1.0e-10
            ####
            print "E= %s   sigma=%s   beta= %s" % (E, sigma, beta)
            energies[imo].append(E)
            sigmas[imo].append(sigma.real)
            betas[imo].append(beta.real)
            tdip2s[imo].append(tdip2)

    energies = np.array(energies)
    sigmas = np.array(sigmas)
    betas = np.array(betas)

    from matplotlib import pyplot as plt
    # total cross section
    plt.xlabel("PKE / eV")
    plt.ylabel("total photoionization cross section $\sigma$")
    for imo in range(0, nmo):
        plt.plot(energies[imo] * 27.211, sigmas[imo], lw=2)
        #plt.plot(energies[imo]*27.211, tdip2s[imo], lw=2, ls="-.")
    plt.show()
    # anisotropy
    plt.cla()

    plt.ylim((-1.0, 2.0))
    plt.xlabel("PKE / eV")
    plt.ylabel("anisotropy $\\beta_2$")
    for imo in range(0, nmo):
        plt.plot(energies[imo] * 27.211, betas[imo], lw=2, label="%d" % imo)
    plt.legend()
    plt.show()

    return energies, sigmas, betas
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")
    #test_dipole_prepared_continuum()
    test_averaged_asymptotic_density()
    #test_photoangular_distribution()
    exit(-1)

    #atomlist = [(8,(0,0,0))]
    atomlist = [(1, (0, 0, 0)), (1, (0, 0, 3.0))]

    energies = []
    sigmas = []
    betas = []
    for E in np.linspace(0.001, 5.0 / 27.211, 20):

        SKT_bf, SKT_ff = load_slako_scattering(atomlist, E)
        #print SKT
        valorbs, radial_val = load_pseudo_atoms(atomlist)

        Dipole = ScatteringDipoleMatrix(atomlist, valorbs, SKT_bf)
        #print Dipole
        mos_bound = np.array([1.0, 1.0])
        #mos_bound = np.array([1.0,-1.0])

        mos_bound /= la.norm(mos_bound)

        mos_scatt = np.array([0.0, 1.0, 1.0, 1.0] + [0.0, 1.0, 1.0, 1.0])
        #mos_scatt = np.array([0.0,  1.0,1.0,1.0] + [0.0,  1.0,1.0,1.0])
        mos_scatt /= la.norm(mos_scatt)
        #sigma, beta = angular_distribution_old(atomlist, valorbs, mos_bound, mos_scatt, Dipole)
        #print "OLD E= %s   sigma=%s   beta= %s" % (E,sigma, beta)
        sigma, beta = angular_distribution(atomlist, valorbs, mos_bound,
                                           mos_scatt, Dipole)
Пример #7
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()
Пример #8
0
def build_water_cluster(atomlist_template, orbital_names, phases):
    """
    build a cluster orbital as a linear combination of monomer orbitals with the correct orientation.
    The position and orientation of the water molecules is taken from the template. 
    
    Parameters:
    ===========
    atomlist_template: molecular geometry for cluster with n water molecules
    orbital_names: list of orbital names ('1b1', '3a1', '1b2' or '2a1') for each of the n water molecules
    phases: list of +1 or -1's for each orbital

    Returns:
    ========
    water_cluster: molecular geometry of the cluster
    orb_cluster: cluster orbital, that is a linear combination of the monomer orbitals.
    """
    # water geometry in bohr
    water_std = [(1, (0.0,  0.8459947982381987,  -1.4473477675908173)),
                 (8, (0.0, -0.21392561795490195,  0.0               )),
                 (1, (0.0,  0.8459947982381987,   1.4473477675908173))]
    valorbs, radial_val = load_pseudo_atoms(water_std)

    # Orbitals for H2O monomer in standard orientation:
    # molecular plane = yz-plane, oxygen lies on the negative y-axis, H-H bond is parallel to z-axis
        #                  H1-1s   O-2s    O-2py   O-2pz   O-2px   H2-1s  
    monomer_orbitals = {
        "1b1": np.array([ 0.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000]),
        "3a1": np.array([ 0.7816,-0.6842, 0.6989, 0.0000, 0.0000, 0.7816]),
        "1b2": np.array([-0.7264, 0.0000, 0.0000, 0.9429, 0.0000, 0.7264]),
        "2a1": np.array([ 0.1730, 0.8662, 0.0393, 0.0000, 0.0000, 0.1730])
    }

    # First the individual water molecules in the template are identified and their positions
    # and orientations are extracted.
    fragments = MolecularGraph.disconnected_fragments(atomlist_template)
    assert len(fragments) == len(orbital_names) == len(phases), "For each water fragment in the cluster you need to specify one fragment orbital ('1b1', '3a1', '1b2' or '2a1') and its phase"
    orb_cluster = []    # list of MO coefficients
    water_cluster = []  # list of atoms
    for i,water in enumerate(fragments):
        # the Euler angles (a,b,g) specify the orientation of the i-th water molecule
        water_std_i, (a,b,g), cm = MolCo.molecular_frame_transformation(water)
        print "WATER STANDARD"
        for (Zi,posi) in water_std:
            print "  %s   %8.6f  %8.6f  %8.6f" % (Zi, posi[0], posi[1], posi[2])
        print "WATER STANDARD %d" % i
        for (Zi,posi) in water_std_i:
            print "  %s   %8.6f  %8.6f  %8.6f" % (Zi, posi[0], posi[1], posi[2])
        # The desired orbital is placed on the i-th water molecule and is
        # rotated to match the orientation of the molecule.
        orb_monomer = monomer_orbitals[orbital_names[i]]
        # rotate orbital
        orb_monomer_rot = OrbitalRotations.rotate_orbitals(water_std, valorbs, orb_monomer, (a,b,g))
        # add orbital with desired phase to cluster MOs
        orb_cluster += list(phases[i] * orb_monomer_rot)
        # rotate geometry
        water_rot = MolCo.transform_molecule(water_std, (a,b,g), cm)
        XYZ.write_xyz("/tmp/water_std.xyz", [water_std, water_rot])
        water_cluster += water_rot
    # Assuming that the overlap between orbitals on different water molecules is negligible,
    # we can normalize the cluster orbital by dividing through sqrt(number of water molecules)
    n = np.sum(abs(np.array(phases))) # a phase of 0 indicates no orbital
    orb_cluster /= np.sqrt(n)
    
    return water_cluster, orb_cluster
Пример #9
0
    def __init__(self, xyz_file, dyson_file=None):
        super(Main, self).__init__()
        self.settings = Settings({
            "Continuum Orbital": {
                "Ionization transitions":
                [0, ["only intra-atomic", "inter-atomic"]]
            },
            "Averaging": {
                "Euler angle grid points": 5,
                "polar angle grid points": 1000,
                "sphere radius Rmax": 300.0,
            },
            "Scan": {
                "nr. points": 20
            },
            "Cube": {
                "extra space / bohr": 15.0,
                "points per bohr": 3.0
            }
        })
        # perform DFTB calculation

        # BOUND ORBITAL = H**O
        self.atomlist = XYZ.read_xyz(xyz_file)[0]
        # shift molecule to center of mass
        print "shift molecule to center of mass"
        pos = XYZ.atomlist2vector(self.atomlist)
        masses = AtomicData.atomlist2masses(self.atomlist)
        pos_com = MolCo.shift_to_com(pos, masses)
        self.atomlist = XYZ.vector2atomlist(pos_com, self.atomlist)

        self.tddftb = LR_TDDFTB(self.atomlist)
        self.tddftb.setGeometry(self.atomlist, charge=0)
        options = {"nstates": 1}
        try:
            self.tddftb.getEnergies(**options)
        except DFTB.Solver.ExcitedStatesNotConverged:
            pass

        self.valorbs, radial_val = load_pseudo_atoms(self.atomlist)

        if dyson_file == None:
            # Kohn-Sham orbitals are taken as Dyson orbitals
            self.H**O, self.LUMO = self.tddftb.dftb2.getFrontierOrbitals()
            self.bound_orbs = self.tddftb.dftb2.getKSCoefficients()
            self.orbe = self.tddftb.dftb2.getKSEnergies()
            orbital_names = []
            norb = len(self.orbe)
            for o in range(0, norb):
                if o < self.H**O:
                    name = "occup."
                elif o == self.H**O:
                    name = "H**O"
                elif o == self.LUMO:
                    name = "LUMO "
                else:
                    name = "virtual"
                name = name + "  " + str(o).rjust(4) + (
                    "   %+10.3f eV" % (self.orbe[o] * 27.211))
                orbital_names.append(name)
            initially_selected = self.H**O
        else:
            # load coefficients of Dyson orbitals from file
            names, ionization_energies, self.bound_orbs = load_dyson_orbitals(
                dyson_file)
            self.orbe = np.array(ionization_energies) / 27.211
            orbital_names = []
            norb = len(self.orbe)
            for o in range(0, norb):
                name = names[o] + "  " + str(o).rjust(4) + (
                    "   %4.2f eV" % (self.orbe[o] * 27.211))
                orbital_names.append(name)
            initially_selected = 0

        self.photo_kinetic_energy = slako_tables_scattering.energies[0]
        self.epol = np.array([15.0, 0.0, 0.0])

        # Build Graphical User Interface
        main = QtGui.QWidget()
        mainLayout = QtGui.QHBoxLayout(main)
        #
        selectionFrame = QtGui.QFrame()
        selectionFrame.setSizePolicy(QtGui.QSizePolicy.Fixed,
                                     QtGui.QSizePolicy.Preferred)
        mainLayout.addWidget(selectionFrame)
        selectionLayout = QtGui.QVBoxLayout(selectionFrame)
        #
        label = QtGui.QLabel(selectionFrame)
        label.setText("Select bound MO:")
        selectionLayout.addWidget(label)

        # bound orbitals
        self.orbitalSelection = QtGui.QListWidget(selectionFrame)
        self.orbitalSelection.itemSelectionChanged.connect(
            self.selectBoundOrbital)
        norb = len(self.orbe)
        self.orbital_dict = {}
        for o in range(0, norb):
            name = orbital_names[o]
            self.orbital_dict[name] = o
            item = QtGui.QListWidgetItem(name, self.orbitalSelection)
            if o == initially_selected:
                selected_orbital_item = item
            selectionLayout.addWidget(self.orbitalSelection)

        ### VIEWS
        center = QtGui.QWidget()
        mainLayout.addWidget(center)
        centerLayout = QtGui.QGridLayout(center)
        #
        boundFrame = QtGui.QFrame()

        centerLayout.addWidget(boundFrame, 1, 1)
        boundLayout = QtGui.QVBoxLayout(boundFrame)
        # "Bound Orbital"
        label = QtGui.QLabel(boundFrame)
        label.setText("Bound Orbital")
        boundLayout.addWidget(label)
        #
        self.boundOrbitalViewer = QCubeViewerWidget(boundFrame)
        boundLayout.addWidget(self.boundOrbitalViewer)

        # continuum orbital
        continuumFrame = QtGui.QFrame()
        centerLayout.addWidget(continuumFrame, 1, 2)
        continuumLayout = QtGui.QVBoxLayout(continuumFrame)
        # "Dipole-Prepared Continuum Orbital"
        label = QtGui.QLabel(continuumFrame)
        label.setText("Dipole-Prepared Continuum Orbital")
        continuumLayout.addWidget(label)

        self.continuumOrbitalViewer = QCubeViewerWidget(continuumFrame)
        continuumLayout.addWidget(self.continuumOrbitalViewer)

        self.efield_objects = []
        self.efield_actors = []
        self.selected = None
        # picker
        self.picker = self.continuumOrbitalViewer.visualization.scene.mayavi_scene.on_mouse_pick(
            self.picker_callback)
        self.picker.tolerance = 0.01

        # PHOTO KINETIC ENERGY
        sliderFrame = QtGui.QFrame(continuumFrame)
        continuumLayout.addWidget(sliderFrame)
        sliderLayout = QtGui.QHBoxLayout(sliderFrame)
        # label
        self.pke_label = QtGui.QLabel()
        self.pke_label.setText("PKE: %6.4f eV" %
                               (self.photo_kinetic_energy * 27.211))
        sliderLayout.addWidget(self.pke_label)
        # Slider for changing the PKE
        self.pke_slider = QtGui.QSlider(QtCore.Qt.Horizontal)
        self.pke_slider.setMinimum(0)
        self.pke_slider.setMaximum(len(slako_tables_scattering.energies) - 1)
        self.pke_slider.setValue(0)
        self.pke_slider.sliderReleased.connect(self.changePKE)
        self.pke_slider.valueChanged.connect(self.searchPKE)
        sliderLayout.addWidget(self.pke_slider)

        #

        # molecular frame photoangular distribution
        mfpadFrame = QtGui.QFrame()
        centerLayout.addWidget(mfpadFrame, 2, 1)
        mfpadLayout = QtGui.QVBoxLayout(mfpadFrame)
        mfpadLayout.addWidget(QtGui.QLabel("Molecular Frame PAD"))
        mfpadTabs = QtGui.QTabWidget()
        mfpadLayout.addWidget(mfpadTabs)
        # 2D map
        mfpadFrame2D = QtGui.QFrame()
        mfpadTabs.addTab(mfpadFrame2D, "2D")
        mfpadLayout2D = QtGui.QVBoxLayout(mfpadFrame2D)
        self.MFPADfig2D = Figure()
        self.MFPADCanvas2D = FigureCanvas(self.MFPADfig2D)
        mfpadLayout2D.addWidget(self.MFPADCanvas2D)
        self.MFPADCanvas2D.draw()
        NavigationToolbar(self.MFPADCanvas2D, mfpadFrame2D, coordinates=True)
        # 3D
        mfpadFrame3D = QtGui.QFrame()
        mfpadTabs.addTab(mfpadFrame3D, "3D")
        mfpadLayout3D = QtGui.QVBoxLayout(mfpadFrame3D)
        self.MFPADfig3D = Figure()
        self.MFPADCanvas3D = FigureCanvas(self.MFPADfig3D)
        mfpadLayout3D.addWidget(self.MFPADCanvas3D)
        self.MFPADCanvas3D.draw()
        NavigationToolbar(self.MFPADCanvas3D, mfpadFrame3D, coordinates=True)

        # orientation averaged photoangular distribution
        avgpadFrame = QtGui.QFrame()
        centerLayout.addWidget(avgpadFrame, 2, 2)
        avgpadLayout = QtGui.QVBoxLayout(avgpadFrame)
        self.activate_average = QtGui.QCheckBox("Orientation Averaged PAD")
        self.activate_average.setToolTip(
            "Check this box to start averaging of the molecular frame PADs over all orientations. This can take a while."
        )
        self.activate_average.setCheckState(QtCore.Qt.Unchecked)
        self.activate_average.stateChanged.connect(self.activateAveragedPAD)
        avgpadLayout.addWidget(self.activate_average)

        avgpadTabs = QtGui.QTabWidget()
        avgpadLayout.addWidget(avgpadTabs)
        # 1D map
        avgpadFrame1D = QtGui.QFrame()
        avgpadTabs.addTab(avgpadFrame1D, "1D")
        avgpadLayout1D = QtGui.QVBoxLayout(avgpadFrame1D)
        self.AvgPADfig1D = Figure()
        self.AvgPADCanvas1D = FigureCanvas(self.AvgPADfig1D)
        avgpadLayout1D.addWidget(self.AvgPADCanvas1D)
        self.AvgPADCanvas1D.draw()
        NavigationToolbar(self.AvgPADCanvas1D, avgpadFrame1D, coordinates=True)
        # 2D map
        avgpadFrame2D = QtGui.QFrame()
        avgpadFrame2D.setToolTip(
            "The averaged PAD should have no phi-dependence anymore. A phi-dependence is a sign of incomplete averaging."
        )
        avgpadTabs.addTab(avgpadFrame2D, "2D")
        avgpadLayout2D = QtGui.QVBoxLayout(avgpadFrame2D)
        self.AvgPADfig2D = Figure()
        self.AvgPADCanvas2D = FigureCanvas(self.AvgPADfig2D)
        avgpadLayout2D.addWidget(self.AvgPADCanvas2D)
        self.AvgPADCanvas2D.draw()
        NavigationToolbar(self.AvgPADCanvas2D, avgpadFrame2D, coordinates=True)
        # Table
        avgpadFrameTable = QtGui.QFrame()
        avgpadTabs.addTab(avgpadFrameTable, "Table")
        avgpadLayoutTable = QtGui.QVBoxLayout(avgpadFrameTable)
        self.avgpadTable = QtGui.QTableWidget(0, 6)
        self.avgpadTable.setToolTip(
            "Activate averaging and move the PKE slider above to add a new row with beta values. After collecting betas for different energies you can save the table or plot a curve beta(PKE) for the selected orbital."
        )
        self.avgpadTable.setHorizontalHeaderLabels(
            ["PKE / eV", "sigma", "beta1", "beta2", "beta3", "beta4"])
        avgpadLayoutTable.addWidget(self.avgpadTable)
        # Buttons
        buttonFrame = QtGui.QFrame()
        avgpadLayoutTable.addWidget(buttonFrame)
        buttonLayout = QtGui.QHBoxLayout(buttonFrame)
        deleteButton = QtGui.QPushButton("Delete")
        deleteButton.setToolTip("clear table")
        deleteButton.clicked.connect(self.deletePADTable)
        buttonLayout.addWidget(deleteButton)
        buttonLayout.addSpacing(3)
        scanButton = QtGui.QPushButton("Scan")
        scanButton.setToolTip(
            "fill table by scanning automatically through all PKE values")
        scanButton.clicked.connect(self.scanPADTable)
        buttonLayout.addWidget(scanButton)
        saveButton = QtGui.QPushButton("Save")
        saveButton.setToolTip("save table as a text file")
        saveButton.clicked.connect(self.savePADTable)
        buttonLayout.addWidget(saveButton)
        plotButton = QtGui.QPushButton("Plot")
        plotButton.setToolTip("plot beta2 column as a function of PKE")
        plotButton.clicked.connect(self.plotPADTable)
        buttonLayout.addWidget(plotButton)
        """
        # DOCKS
        self.setDockOptions(QtGui.QMainWindow.AnimatedDocks | QtGui.QMainWindow.AllowNestedDocks)
        #
        selectionDock = QtGui.QDockWidget(self)
        selectionDock.setWidget(selectionFrame)
        selectionDock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
        self.addDockWidget(QtCore.Qt.DockWidgetArea(1), selectionDock)
        #
        boundDock = QtGui.QDockWidget(self)
        boundDock.setWidget(boundFrame)
        boundDock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
        boundDock.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
        self.addDockWidget(QtCore.Qt.DockWidgetArea(2), boundDock)
        # 
        continuumDock = QtGui.QDockWidget(self)
        continuumDock.setWidget(continuumFrame)
        continuumDock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
        continuumDock.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
        self.addDockWidget(QtCore.Qt.DockWidgetArea(2), continuumDock)
        """
        self.setCentralWidget(main)

        self.status_bar = QtGui.QStatusBar(main)
        self.setStatusBar(self.status_bar)
        self.default_message = "Click on the tip of the green arrow in the top right figure to change the orientation of the E-field"
        self.statusBar().showMessage(self.default_message)

        # Menu bar
        menubar = self.menuBar()
        exitAction = QtGui.QAction('&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit program')
        exitAction.triggered.connect(exit)
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAction)

        settingsMenu = menubar.addMenu('&Edit')
        settingsAction = QtGui.QAction('&Settings...', self)
        settingsAction.setStatusTip('Edit settings')
        settingsAction.triggered.connect(self.editSettings)
        settingsMenu.addAction(settingsAction)

        self.loadContinuum()
        # select H**O
        selected_orbital_item.setSelected(True)