Beispiel #1
0
def test():
    # 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)

    # radius that separates inner from outer region
    r0 = vdw_sphere_radius(atomlist, fac=2.0)
    # basis sets
    bs_core = AtomicBasisSet(atomlist, orbital_set="core")
    bs_valence = AtomicBasisSet(atomlist, orbital_set="valence")
    bs_continuum = AtomicScatteringBasisSet(atomlist, E, lmax=1)
    # combine basis functions from all basis sets
    bfs = bs_core.bfs + bs_valence.bfs + bs_continuum.bfs

    A, D, S = fvvm_matrix_elements(atomlist, bfs, veff, E, r0)

    # solve generalized eigenvalue problem
    #   A.C = b.D.C
    b, C = sla.eigh(A, D)

    return b, C
Beispiel #2
0
 def __init__(self, tddftb, name=""):
     self.tddftb = tddftb
     self.dftb = tddftb.dftb2
     self.name = name
     if self.dftb != None:
         self.bs = AtomicBasisSet(self.dftb.atomlist)
     self.ppb = 2.0
     # auxiliary basis of Gaussian flucuation functions F_A(r)
     self.bs_aux = AuxiliaryBasisSet(self.dftb.atomlist,
                                     self.dftb.hubbard_U)
 def __init__(self, tddftb, symmetry_group):
     self.tddftb = tddftb
     self.dftb = tddftb.dftb2
     if self.dftb != None:
         self.bs = AtomicBasisSet(self.dftb.atomlist)
     self.symmetry_group = symmetry_group
     # How do I choose the test points?
     npts = 10
     # random points in a box [-3,3]x[-3,3]x[-3,3] around the first atom
     pos_at1 = np.array(self.dftb.atomlist[0][1])  # position of 1st atom
     self.test_pts = pos_at1 + 3.0 * 2.0 * (np.random.rand(npts, 3) - 0.5)
    def __init__(self, tddftb, symmetry_group):
        self.tddftb = tddftb
        self.dftb2 = tddftb.dftb2
        if self.dftb2 != None:
            self.bs = AtomicBasisSet(self.dftb2.atomlist)
        self.symmetry_group = symmetry_group
        # How do I choose the test points?
        npts = 5
        # random points in a box [-3,3]x[-3,3]x[-3,3] around the first atom
        pos_at1 = np.array(self.dftb2.atomlist[0][1])  # position of 1st atom
        test_pts1 = pos_at1 + 3.0 * 2.0 * (np.random.rand(npts, 3) - 0.5)
        pos_atN = np.array(self.dftb2.atomlist[-1][1])  # position of last atom
        test_ptsN = pos_atN + 3.0 * 2.0 * (np.random.rand(npts, 3) - 0.5)
        self.test_pts = np.vstack((test_pts1, test_ptsN))
        # transform test points according to the symmetry operations
        # of the group
        xs = []
        self.npts = len(self.test_pts)
        # Put all test points into one long list so that we
        # can compute the values of the transition density
        # for all of the in one step
        for x in self.test_pts:
            # image of x under all symmetry transformations
            # e.g.  xts = {Id(x), sigma(x), ....}
            xts = self.symmetry_group.symmetry_equivalents(x)
            xs += xts
        self.xs = np.array(xs)
        # The coordinates of the transformed vectors belonging
        # to the the i-th test point can be indexed as
        # xs[i*ng:(i+1)*ng,:]

        # size of group - number of symmetry elements
        self.ng = self.symmetry_group.size()
        # number of basis functions
        self.nbf = len(self.bs.bfs)
        # evaluate basis functions on the grid of symmetry equivalent points
        grid = self.xs.transpose()
        self.bf_grid = []
        for bf in self.bs.bfs:
            self.bf_grid.append(bf.amp(*grid))
        self.bf_grid = np.array(self.bf_grid)
        ## classify molecular orbitals by symmetry
        #self.orbs_irreps = self.assign_orbital_symmetries(self.dftb2.orbs)
        #
        self.selected_irreps = ["B1U"]
def charge_fluctuation_functions(atom_name='h', confined=True):
    """
    create charge fluctuation functions for valence shells

    Parameters
    ----------
    atom_name     :   string with atom name

    Optional
    --------
    confined      :   controls where confined atoms (True) or free atoms (False)
                      are used

    Returns
    -------
    ls            :   list of angular momenta of the valence shells
    Fs            :   list of charge fluctuation functions for each shell
                      callable, Fs[i](x,y,z) evaluates the spherically averaged
                      charged fluctuation function for the shell with angular
                      momentum ls[i]
    """
    Zat = atomic_number(atom_name)
    atomlist = [(Zat, (0, 0, 0))]
    # load atomic valence orbitals
    basis = AtomicBasisSet(atomlist, confined=confined)
    # group basis functions by angular momentum
    ls = []
    shells = []  # contains list of basis functions for each shell
    for bf in basis.bfs:
        if len(ls) == 0 or bf.l > ls[-1]:
            # new shell begins
            ls.append(bf.l)
            # create new shell with current basis function as first element
            shells.append([bf])
        else:
            # append basis function to current shell
            shells[-1].append(bf)
    # define charge fluctuation function for each shell
    Fs = []  # list of charge fluctuation functions
    for l, shell in zip(ls, shells):
        assert len(shell) == 2 * l + 1
        Fs.append(spherically_averaged_density(shell, l))

    return ls, Fs
def onsite_integrals(atom_name='h', confined=True):
    """
    compute on-site Coulomb and exchange integrals for an atom

    Parameters
    ----------
    atom_name      :  name of atom, e.g. 'h', 'c', etc.

    Optional
    --------
    confined      :   controls where confined atoms (True) or free atoms (False)
                      are used
    """
    print(
        "computing on-site Coulomb and exchange integrals between valence orbitals of '%s'"
        % atom_name)
    Zat = atomic_number(atom_name)
    atomlist = [(Zat, (0, 0, 0))]
    basis = AtomicBasisSet(atomlist, confined=confined)
    density = AtomicDensitySuperposition(atomlist, confined=confined)
    #xc_functional = XCFunctionals.libXCFunctional("lda_x", "lda_c_pw")
    xc_functional = XCFunctionals.libXCFunctional("gga_x_pbe", "gga_c_pbe")

    for a, bfA in enumerate(basis.bfs):
        stra = "%d%s" % (bfA.n, angmom_to_xyz[(bfA.l, bfA.m)]
                         )  # name of valence orbital, e.g. 1s, 2px, etc.
        for b, bfB in enumerate(basis.bfs):
            strb = "%d%s" % (bfB.n, angmom_to_xyz[(bfB.l, bfB.m)])
            # Coulomb integral    (aa|(1/r12 + fxc[rho0])|bb)
            eri_aabb = electron_repulsion_integral(atomlist, bfA, bfA, bfB,
                                                   bfB, density, xc_functional)
            print("Coulomb  (%s,%s|{1/r12+f_xc[rho0]}|%s,%s)= %e" %
                  (stra, stra, strb, strb, eri_aabb))
            if a != b:
                # exchange integral    (ab|(1/r12 + fxc[rho0])|ab)
                eri_abab = electron_repulsion_integral(atomlist, bfA, bfB, bfA,
                                                       bfB, density,
                                                       xc_functional)
                print("exchange (%s,%s|{1/r12+f_xc[rho0]}|%s,%s)= %e" %
                      (stra, strb, stra, strb, eri_abab))
Beispiel #7
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])
Beispiel #8
0
    def __init__(self, atomlist, hubbard_U):
        """
        
        Parameters:
        ===========
        bfs: list of numeric basis functions
        bfs_aux: list of auxiliary basis functions
        """
        self.atomlist = atomlist
        self.bfs = AtomicBasisSet(atomlist).bfs
        self.bfs_aux = AuxiliaryBasisSet(atomlist, hubbard_U).bfs
        # create a list of points that should have the symmetry
        # of the molecule on which the true density should agree
        # with the model density
        Xs,Ys,Zs = [], [], []
        # atomic centers
        for Zi,posi in atomlist:
            x,y,z = posi
            Xs.append(x)
            Ys.append(y)
            Zs.append(z)
        # points between atoms
        for i,(Zi,posi) in enumerate(atomlist):
            for j,(Zj,posj) in enumerate(atomlist):
                if i == j:
                    continue
                Rij = np.array(posj)-np.array(posi)
                x,y,z = np.array(posi) + 0.33*Rij
                Xs.append(x)
                Ys.append(y)
                Zs.append(z)
                x,y,z = np.array(posi) - 0.33*Rij
                Xs.append(x)
                Ys.append(y)
                Zs.append(z)


        # points perpendicular to plane between 3 atoms
        for i,(Zi,posi) in enumerate(atomlist):
            for j,(Zj,posj) in enumerate(atomlist):
                if i == j:
                    continue
                for k,(Zk,posk) in enumerate(atomlist):
                    if i == k:
                        continue
                    if i == j:
                        continue
                    Rij = np.array(posj) - np.array(posi)
                    Rik = np.array(posk) - np.array(posi)
                    x,y,z = np.array(posi) + 0.25*np.cross(Rij, Rik)
                    Xs.append(x)
                    Ys.append(y)
                    Zs.append(z)

        Xs,Ys,Zs = np.mgrid[-6.0:6.0:30j,-6.0:6.0:30j,-6.0:6.0:30j]
        Xs = Xs.ravel()
        Ys = Ys.ravel()
        Zs = Zs.ravel()
        grid = np.array(Xs), np.array(Ys), np.array(Zs)

        # number of fit parameters
        self.Nfit = len(self.bfs_aux)
        assert self.Nfit % 4 == 0
        # number of basis functions
        self.Nbfs = len(self.bfs)
        # number of sample points
        self.Npts = len(Xs)
        assert self.Npts > self.Nfit
        # save grid to xyz file
        gridlist = atomlist[:]
        for i in range(0, self.Npts):
            gridlist.append( (1, (Xs[i], Ys[i], Zs[i])) )
        XYZ.write_xyz("/tmp/fitgrid.xyz", [gridlist])
        # evaluate all basis functions on the grid
        self.bf_grid = np.zeros((self.Npts, self.Nbfs))
        for m,bfm in enumerate(self.bfs):
            self.bf_grid[:,m] = bfm.amp(*grid)        
        # evaluate fit functions on the grid
        self.bf_aux_grid = np.zeros((self.Npts, self.Nfit))
        for m,bfm_aux in enumerate(self.bfs_aux):
            self.bf_aux_grid[:,m] = bfm_aux.amp(*grid)

        #
        M = self.bf_aux_grid
        self.M = M
         # constrained   C.x - Q = 0
        Id4 = np.eye(4)
        C = np.hstack([Id4 for i in range(0, self.Nfit/4)])
        Ct = C.transpose()
        #
        Mt = M.transpose()
        MtM = np.dot(Mt, M)
        MtMinv = la.inv(MtM)
        #
        self.A1 = np.dot(C,np.dot(MtMinv, Mt))
        self.A2 = np.dot(C,np.dot(MtMinv, Ct))
        self.A3 = np.dot(MtMinv, Ct)
        self.A4 = np.dot(MtMinv,Mt)
def transform_turbomole_orbitals(tm_dir, dyson_file, method="atomwise", selected_orbitals=None):
    """
    The molecular orbitals from a Turbomole calculation are transformed
    into the basis used by DFTB. Since in DFTB there are much less atomic orbitals
    this transformation is not bijective and does not conserve the normalization and
    orthogonality among the MOs. 

    Parameters:
    ===========
    tm_dir: directory with coord, basis and mos files
    dyson_file: transformed MO coefficients for DFTB are written to this file
    method: Method used to find the coefficients in the minimal basis
      'atomwise': overlaps are only calculated between orbitals on the same atom (very fast).
      'grid': the DFT orbital mo(r) and the minimal DFT atomic orbitals ao_i(r) are calculated 
         on a grid and the coefficients are obtained by minimizing the deviation
             error(c_i) = integral |mo(r) - sum_i c_i ao_i(r)|^2 dr
         in a least square sense (slower, but more accurate).
    selected_orbitals: list of indeces (starting at 1, e.g. '[1,2,3]') of orbitals that should be transformed.
      If not set all orbitals are transformed.
    """
    print "Reading geometry and basis from Turbomole directory %s" % tm_dir
    Data = Turbomole.parseTurbomole(tm_dir + "/coord")
    atomlist = Data["coord"]
    Data = Turbomole.parseTurbomole(tm_dir + "/basis")
    basis_data = Data["basis"]
    bs_gaussian = GaussianBasisSet(atomlist, basis_data)
    # load Turbomole orbitals
    try:
        Data = Turbomole.parseTurbomole(tm_dir + "/mos")
        orbe,orbs_turbomole = Data["scfmo"]
    except IOError:
        print "'mos' file not found! Maybe this is an open-shell calculation. Trying to read file 'alpha'"
        Data = Turbomole.parseTurbomole(tm_dir + "/alpha")
        orbe,orbs_turbomole = Data["uhfmo_alpha"]
    # Which orbitals should be transformed?
    if selected_orbitals == None:
        selected_mos = np.array(range(0, len(orbe)), dtype=int)
    else:
        selected_mos = np.array(eval(selected_orbitals), dtype=int)-1 
    nmo = len(selected_mos)
    # transform them to DFTB basis
    if method == "atomwise":
        T = bs_gaussian.getTransformation_GTO_to_DFTB()
        orbs = np.dot(T, orbs_turbomole[:,selected_mos])
    elif method == "grid":
        # define grid for integration
        (xmin,xmax),(ymin,ymax),(zmin,zmax) = Cube.get_bbox(atomlist, dbuff=7.0)
        dx,dy,dz = xmax-xmin,ymax-ymin,zmax-zmin
        ppb = 5.0 # Points per bohr
        nx,ny,nz = int(dx*ppb),int(dy*ppb),int(dz*ppb)
        x,y,z = np.mgrid[xmin:xmax:nx*1j, ymin:ymax:ny*1j, zmin:zmax:nz*1j]
        grid = (x,y,z)
        dV = dx/float(nx-1) * dy/float(ny-1) * dz/float(nz-1)
        # numerical atomic DFTB orbitals on the grid
        bs_atomic = AtomicBasisSet(atomlist)
        aos = [bf.amp(x,y,z) for bf in bs_atomic.bfs]
        nao = len(aos)
        # overlaps S2_mn = <m|n>
        S2 = np.zeros((nao,nao))
        for m in range(0, nao):
            S2[m,m] = np.sum(aos[m]*aos[m]*dV)
            for n in range(m+1,nao):
                S2[m,n] = np.sum(aos[m]*aos[n]*dV)
                S2[n,m] = S2[m,n]
        # DFT molecular orbitals on the grid
        mos = [Cube.orbital_amplitude(grid, bs_gaussian.bfs, orbs_turbomole[:,i]).real for i in selected_mos]
        # overlaps S1_mi = <m|i>, m is an atomic DFTB orbital, i a molecular DFT orbital
        S1 = np.zeros((nao,nmo))
        for m in range(0, nao):
            for i in range(0, nmo):
                S1[m,i] = np.sum(aos[m]*mos[i]*dV)
        # Linear regression leads to the matrix equation
        #   sum_n S2_mn c_ni = S1_mi
        # which has to be solved for the coefficients c_ni.
        orbs = la.solve(S2, S1)

    else:
        raise ValueError("Method should be 'atomwise' or 'grid' but not '%s'!" % method)
    # normalize orbitals, due to the incomplete transformation they are not necessarily
    # orthogonal
    dftb2 = DFTB2(atomlist)
    dftb2.setGeometry(atomlist)
    S = dftb2.getOverlapMatrix()
    for i in range(0, nmo):
        norm2 = np.dot(orbs[:,i], np.dot(S, orbs[:,i]))
        orbs[:,i] /= np.sqrt(norm2)
    # save orbitals 
    xyz_file = "geometry.xyz"
    XYZ.write_xyz(xyz_file, [atomlist])
    print "Molecular geometry was written to '%s'." % xyz_file
    ionization_energies = -orbe[selected_mos]
    save_dyson_orbitals(dyson_file, ["%d" % (i+1) for i in selected_mos], ionization_energies, orbs, mode="w")
    print "MO coefficients for DFTB were written to '%s'." % dyson_file
def onsite_integrals_unique(atom_name='h', confined=True):
    """
    compute the 5 unique on-site electron integrals for an atom.
    Only s- and p-valence orbitals are considered. The 5 integrals
    are

      (ss|ss)
      (sp|sp)   = (s px|s px) = (s py|s py) = (s pz|s pz)
      (ss|pp)   = (s s |pxpx) = (s s |pypy) = (s s |pzpz)
      (pp|pp)   = (pxpx|pxpx) = (pypy|pypy) = (pzpz|pzpz)
      (pp'|pp') = (pxpy|pxpy) = (pxpz|pxpz) = (pypz|pypz)

    Optional
    --------
    confined      :   controls where confined atoms (True) or free atoms (False)
                      are used

    Returns
    -------
    unique_integrals  :  numpy array with unique on-site integrals
                         [(ss|ss), (sp|sp), (ss|pp), (pp|pp), (pp'|pp')]

    References
    ----------
    [1] http://openmopac.net/manual/1c2e.html
    """
    print(
        "computing unique on-site electron integrals between valence orbitals of '%s'"
        % atom_name)
    print("only s- and p-functions are considered")
    Zat = atomic_number(atom_name)
    atomlist = [(Zat, (0, 0, 0))]
    basis = AtomicBasisSet(atomlist, confined=confined)
    density = AtomicDensitySuperposition(atomlist, confined=confined)
    #xc_functional = XCFunctionals.libXCFunctional("lda_x", "lda_c_pw")
    xc_functional = XCFunctionals.libXCFunctional("gga_x_pbe", "gga_c_pbe")

    unique_integrals = np.zeros(5)
    for a, bfA in enumerate(basis.bfs):
        for b, bfB in enumerate(basis.bfs):
            # choose index into unique_integrals to which the integrals is written
            if (bfA.l == 0 and bfB.l == 0):
                # (ss|ss)
                i = 0
            elif (bfA.l == 0 and bfB.l == 1 and bfB.m == 0):
                # (sp|sp) and (ss|pp)
                i = 1
            elif (bfA.l == 1 and bfA.m == 0 and bfB.l == 1 and bfB.m == 0):
                # (pp|pp)
                i = 3
            elif (bfA.l == 1 and bfA.m == 0 and bfB.l == 1 and bfB.m == 1):
                # (pp'|pp')
                i = 4
            else:
                continue

            # compute Coulomb integral    (ab|(1/r12 + fxc[rho0])|ab)
            eri_abab = electron_repulsion_integral(atomlist, bfA, bfB, bfA,
                                                   bfB, density, xc_functional)
            unique_integrals[i] = eri_abab

            if (i == 1):
                # in addition to (sp|sp) we also need to calculated (ss|pp)
                eri_aabb = electron_repulsion_integral(atomlist, bfA, bfA, bfB,
                                                       bfB, density,
                                                       xc_functional)
                unique_integrals[2] = eri_aabb
            elif (i == 4):
                # in addition to (pp'|pp') we also compute (pp|p'p') and verify the identity
                #  (pp|p'p') = (pp|pp) - 2 (pp'|pp')
                eri_aabb = electron_repulsion_integral(atomlist, bfA, bfA, bfB,
                                                       bfB, density,
                                                       xc_functional)
                assert (
                    eri_aabb -
                    (unique_integrals[3] - 2 * unique_integrals[4])) < 1.0e-5

    print("unique 1-center integrals (in eV) for atom '%s'" % atom_name)
    print("       (ss|ss)     (sp|sp)     (ss|pp)      (pp|pp)     (pp'|pp')")
    print("    %+.7f  %+.7f  %+.7f  %+.7f  %+.7f" %
          tuple(unique_integrals * hartree_to_eV))

    return unique_integrals
Beispiel #11
0
#!/usr/bin/env python
"""
This script demonstrates how to change the resolution of the multicenter 
grid.
"""

# routine for integration
from DFTB.MolecularIntegrals.Ints1e import kinetic
from DFTB.BasisSets import AtomicBasisSet

if __name__ == "__main__":
    # default settings for grid size
    from DFTB.MolecularIntegrals import settings
    # increase radial grid
    settings.radial_grid_factor = 10
    settings.lebedev_order = 23

    atomlist = [(6, (0, 0, 0))]
    basis = AtomicBasisSet(atomlist)

    # matrix elements of kinetic energy computed on a fine radial grid
    for a, bfA in enumerate(basis.bfs):
        for b, bfB in enumerate(basis.bfs):
            t = kinetic(atomlist, bfA, bfB)
            print "(%d|T|%d) = %e" % (a, b, t)
Beispiel #12
0
    def __init__(self, tddftb, parent=None):
        self.tddftb = tddftb

        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QVBoxLayout(self)
        #
        upperFrame = QtGui.QFrame()
        layout.addWidget(upperFrame)
        upperLayout = QtGui.QHBoxLayout(upperFrame)
        # table with MOs and Kohn-Sham energies
        tableFrame = QtGui.QFrame()
        upperLayout.addWidget(tableFrame)
        tableLayout = QtGui.QVBoxLayout(tableFrame)
        tableLayout.addWidget(QtGui.QLabel("Molecular Orbitals:"))
        self.tableMOs = QtGui.QTableWidget()
        self.tableMOs.setToolTip(
            "Select a row to highlight the respective orbital in the density of states"
        )
        self.tableMOs.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self.tableMOs.itemSelectionChanged.connect(self.plotDOS)
        tableLayout.addWidget(self.tableMOs)
        cubeFrame = QtGui.QFrame()
        tableLayout.addWidget(cubeFrame)
        cubeLayout = QtGui.QHBoxLayout(cubeFrame)
        self.ppbGrid = QtGui.QComboBox()
        self.ppbGrid.addItems(
            ["crude: 1.0", "coarse: 2.0", "medium: 3.0", "fine: 4.0"])
        self.ppbGrid.setCurrentIndex(1)
        self.ppbGrid.setToolTip(
            "The number of points per bohr determines the resolution of the grid on which the cubes are calculated"
        )
        self.ppbGrid.currentIndexChanged.connect(self.recalculateCubes)
        cubeLayout.addWidget(self.ppbGrid)

        calcCubesButton = QtGui.QPushButton("Calc. cubes")
        calcCubesButton.setToolTip("Calculate cubes for the selected orbitals")
        calcCubesButton.clicked.connect(self.calculateCubes)
        cubeLayout.addWidget(calcCubesButton)
        # MOs
        moFrame = QtGui.QFrame()
        upperLayout.addWidget(moFrame)
        moLayout = QtGui.QVBoxLayout(moFrame)
        self.moLabel = QtGui.QLabel("Ground State:")
        moLayout.addWidget(self.moLabel)
        moTab = QtGui.QTabWidget()
        moLayout.addWidget(moTab)
        # 3D cubes - MOs
        self.bs = AtomicBasisSet(self.tddftb.dftb2.atomlist)
        self.molecularOrbitalViewer = QCubeViewerWidget()
        self.molecularOrbitalViewer.setIsoValue(0.02)
        moTab.addTab(self.molecularOrbitalViewer, "3D Molecular Orbital")
        # 3D cubes - MOs
        # Mulliken Charges
        self.chargesViewer = QCubeViewerWidget()
        moTab.addTab(self.chargesViewer, "Mulliken Charges")
        atomlist = self.tddftb.dftb2.getGeometry()
        partial_charges = -self.tddftb.dftb2.getPartialCharges()
        self.chargesViewer.setGeometriesAndCharges([atomlist],
                                                   [partial_charges])
        self.chargesViewer.selectShowOptions(options=["charges"])
        # figure with DOS
        dosFrame = QtGui.QFrame()
        layout.addWidget(dosFrame)
        dosLayout = QtGui.QVBoxLayout(dosFrame)
        self.figDOS = Figure()
        self.canvasDOS = FigureCanvas(self.figDOS)
        dosLayout.addWidget(self.canvasDOS)
        NavigationToolbar(self.canvasDOS, dosFrame, coordinates=True)
        # controls
        controlFrame = QtGui.QFrame()
        dosLayout.addWidget(controlFrame)
        controlLayout = QtGui.QHBoxLayout(controlFrame)
        self.energyUnits = QtGui.QComboBox()

        self.energyUnits.addItems(["Hartree", "eV", "cm-1"])
        self.energyUnits.currentIndexChanged.connect(self.plotDOS)
        controlLayout.addWidget(QtGui.QLabel("units:"))
        controlLayout.addWidget(self.energyUnits)
        controlLayout.addWidget(QtGui.QLabel("broadening:"))
        self.broadening = QtGui.QLineEdit()
        self.broadening.setText("0.01")
        self.broadening.editingFinished.connect(self.plotDOS)
        self.broadening.setToolTip(
            "The sticks are convolved with a Gaussian to simulated a temperature broadened DOS"
        )
        controlLayout.addWidget(self.broadening)

        # load KS orbitals
        tab = self.tableMOs
        dftb = self.tddftb.dftb2
        orbe = dftb.getKSEnergies()
        f = dftb.getOccupation()
        self.H**O, self.LUMO = dftb.getFrontierOrbitals()
        tab.setRowCount(len(orbe))
        headers = [
            "N", "name", "occ.", "orb. en. / hartree", "orb. en. / eV", "Cube"
        ]
        tab.setColumnCount(len(headers))
        tab.setHorizontalHeaderLabels(headers)
        self.mo_names = []
        for i in range(0, len(orbe)):
            if i == self.H**O:
                name = "H**O"
            elif i == self.LUMO:
                name = "LUMO"
            else:
                name = ""
            self.mo_names.append(name)
            row = [str.rjust(str(i+1),5), \
                   str.rjust(name,5),
                   str.rjust(str(f[i]),5),
                   str.rjust("%.7f" % orbe[i],20), \
                   str.rjust("%.7f" % (orbe[i]*AtomicData.hartree_to_eV), 17) ]
            for j, r in enumerate(row):
                tab.setItem(i, j, QtGui.QTableWidgetItem("%s" % r))
        tab.resizeColumnsToContents()
        self.plotDOS()
        # cubes with MOs
        self.mo_cubes = {}
        # select H**O
        self.tableMOs.setCurrentCell(self.H**O, 0)
Beispiel #13
0
    def __init__(self, tddftb, parent=None):
        self.tddftb = tddftb

        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QVBoxLayout(self)
        #
        upperFrame = QtGui.QFrame()
        layout.addWidget(upperFrame)
        upperLayout = QtGui.QHBoxLayout(upperFrame)
        # table with excitation energies and states
        tableFrame = QtGui.QFrame()
        upperLayout.addWidget(tableFrame)
        tableLayout = QtGui.QVBoxLayout(tableFrame)
        tableLayout.addWidget(QtGui.QLabel("Excited States:"))
        self.tableStates = QtGui.QTableWidget()
        self.tableStates.setToolTip(
            "Select a row to highlight the respective state in the absorption spectrum"
        )
        self.tableStates.setSelectionBehavior(
            QtGui.QAbstractItemView.SelectRows)
        self.tableStates.itemSelectionChanged.connect(self.plotSpectrum)
        tableLayout.addWidget(self.tableStates)
        cubeFrame = QtGui.QFrame()
        tableLayout.addWidget(cubeFrame)
        cubeLayout = QtGui.QHBoxLayout(cubeFrame)
        self.ppbGrid = QtGui.QComboBox()
        self.ppbGrid.addItems(
            ["crude: 1.0", "coarse: 2.0", "medium: 3.0", "fine: 4.0"])
        self.ppbGrid.setCurrentIndex(1)
        self.ppbGrid.setToolTip(
            "The number of points per bohr determines the resolution of the grid on which the cubes are calculated"
        )
        self.ppbGrid.currentIndexChanged.connect(self.recalculateCubes)
        cubeLayout.addWidget(self.ppbGrid)

        calcCubesButton = QtGui.QPushButton("Calc. cubes")
        calcCubesButton.setToolTip(
            "Calculate cubes with the transition densities for the selected states"
        )
        calcCubesButton.clicked.connect(self.calculateCubes)
        cubeLayout.addWidget(calcCubesButton)
        # transition densities
        tdenseFrame = QtGui.QFrame()
        upperLayout.addWidget(tdenseFrame)
        tdenseLayout = QtGui.QVBoxLayout(tdenseFrame)
        self.tdenseLabel = QtGui.QLabel("Excitated State:")
        tdenseLayout.addWidget(self.tdenseLabel)
        tdenseTab = QtGui.QTabWidget()
        tdenseLayout.addWidget(tdenseTab)
        # 3D cubes - transition density
        self.bs = AtomicBasisSet(self.tddftb.dftb2.atomlist)
        self.transitionDensityViewer = QCubeViewerWidget()
        tdenseTab.addTab(self.transitionDensityViewer, "3D Transition Density")
        # 3D cubes - density difference
        self.bs_aux = AuxiliaryBasisSet(self.tddftb.dftb2.atomlist,
                                        self.tddftb.dftb2.hubbard_U)
        self.differenceDensityViewer = QCubeViewerWidget()
        tdenseTab.addTab(self.differenceDensityViewer, "3D Difference Density")
        # 2D occ-virt plane
        exvec2dFrame = QtGui.QFrame()
        exvec2dLayout = QtGui.QVBoxLayout(exvec2dFrame)
        self.figExvec = Figure()
        self.canvasExvec = FigureCanvas(self.figExvec)
        exvec2dLayout.addWidget(self.canvasExvec)
        tdenseTab.addTab(exvec2dFrame, "2D Excitation Vector")
        NavigationToolbar(self.canvasExvec, exvec2dFrame, coordinates=True)
        # atomic charges in the excited state
        self.chargesViewer = QCubeViewerWidget()
        tdenseTab.addTab(self.chargesViewer, "Partial Charges")
        atomlist = self.tddftb.dftb2.getGeometry()
        #        partial_charges = -self.tddftb.dftb2.getPartialCharges()
        #        self.chargesViewer.setGeometriesAndCharges([atomlist], [partial_charges])
        self.chargesViewer.selectShowOptions(options=["charges"])
        # transition charges between ground and excited state
        self.trans_chargesViewer = QCubeViewerWidget()
        tdenseTab.addTab(self.trans_chargesViewer, "Transition Charges")
        atomlist = self.tddftb.dftb2.getGeometry()
        #        # for S0->S0 transition, the transition charges are equal to the partial charges
        #        transition_charges = -self.tddftb.dftb2.getPartialCharges()
        #        self.trans_chargesViewer.setGeometriesAndCharges([atomlist], [transition_charges])
        self.trans_chargesViewer.selectShowOptions(
            options=["charges", "transition charges"])
        # figure with spectrum
        spectrumFrame = QtGui.QFrame()
        layout.addWidget(spectrumFrame)
        spectrumLayout = QtGui.QVBoxLayout(spectrumFrame)
        self.figSpectrum = Figure()
        self.canvasSpectrum = FigureCanvas(self.figSpectrum)
        spectrumLayout.addWidget(self.canvasSpectrum)
        NavigationToolbar(self.canvasSpectrum, spectrumFrame, coordinates=True)
        # controls
        controlFrame = QtGui.QFrame()
        spectrumLayout.addWidget(controlFrame)
        controlLayout = QtGui.QHBoxLayout(controlFrame)
        self.energyUnits = QtGui.QComboBox()

        self.energyUnits.addItems(["Hartree", "eV", "nm", "cm-1"])
        self.energyUnits.currentIndexChanged.connect(self.plotSpectrum)
        controlLayout.addWidget(QtGui.QLabel("units:"))
        controlLayout.addWidget(self.energyUnits)
        controlLayout.addWidget(QtGui.QLabel("broadening:"))
        self.broadening = QtGui.QLineEdit()
        self.broadening.setText("0.0")
        self.broadening.editingFinished.connect(self.plotSpectrum)
        self.broadening.setToolTip(
            "The stick spectrum is convolved with a Gaussian to simulated a temperature broadened spectrum"
        )
        controlLayout.addWidget(self.broadening)

        # load spectrum
        nr_dominant_ex = 2
        tab = self.tableStates
        tab.setRowCount(len(tddftb.Omega))
        headers = [
            "N", "Spin", "Sym", "exc. en. / hartree", "exc. en. / eV",
            "exc. en. / nm", "osc. strength", "Lambda diagn.", "Cube"
        ]
        tab.setColumnCount(len(headers))
        tab.setHorizontalHeaderLabels(headers)
        for I in range(0, len(tddftb.Omega)):
            row = [str.rjust(str(I+1),5), \
                               tddftb.multiplicity, \
                               str.rjust(tddftb.Irreps[I], 3), \
                               str.rjust("%.7f" % tddftb.Omega[I],20), \
                               str.rjust("%.7f" % (tddftb.Omega[I]*AtomicData.hartree_to_eV), 17), \
                               str.rjust("%.7f" % (AtomicData.hartree_to_nm / tddftb.Omega[I]), 17), \
                               str.rjust("%.7f" % tddftb.oscillator_strength[I], 12), \
                               str.rjust("%.4f" % tddftb.Lambda2[I], 7)]
            for j, r in enumerate(row):
                tab.setItem(I, j, QtGui.QTableWidgetItem("%s" % r))
        tab.resizeColumnsToContents()

        self.plotSpectrum()
        # cubes with transition densities and difference densities for each state if calculated
        self.tdense_cubes = {}
        self.difdense_cubes = {}
        self.partial_charges = {}  # partial charges on excited states
Beispiel #14
0
 def __init__(self, dftb, name=""):
     self.dftb = dftb
     self.name = name
     if self.dftb != None:
         self.bs = AtomicBasisSet(self.dftb.atomlist)
     self.ppb = 2.0
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"