Exemple #1
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)
Exemple #2
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
Exemple #3
0
class QMolecularOrbitalWidget(QtGui.QWidget):
    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)

    def plotDOS(self):
        self.figDOS.clf()
        ax = self.figDOS.add_subplot(111)

        units = self.energyUnits.itemText(self.energyUnits.currentIndex())
        broadening = abs(float(self.broadening.text()))
        ax.set_title("Density of States (DOS)")
        ax.set_xlabel("Kohn-Sham Energy / %s" % units, fontsize=15)
        ax.set_ylabel("DOS / arbitrary units", fontsize=15)

        ens = self.tddftb.dftb2.getKSEnergies()
        dos = 1.0 * np.ones(ens.shape)
        # stick spectrum
        ens_out = convert_energy(ens, "Hartree", units)
        ax.vlines(ens_out,
                  np.zeros(ens_out.shape),
                  dos,
                  lw=2,
                  color="black",
                  picker=5)

        # selected states are highlighted
        selected = self.tableMOs.selectionModel().selectedRows()
        for s in selected:
            i = s.row()
            ax.vlines(ens_out[i], [0.0], dos[i], lw=3, color="green")
            ax.text(ens_out[i], dos[i], "%s %s" % (i + 1, self.mo_names[i]))

        def onpick(event):
            self.tableMOs.setCurrentCell(event.ind[0], 0)

        self.figDOS.canvas.mpl_connect('pick_event', onpick)

        if broadening > 0.0:
            ens_broadened, spec = broadened_spectrum(ens, dos, broadening)
            ens_broadened_out = convert_energy(ens_broadened, "Hartree", units)
            scale = spec.max() / dos.max() / 1.1
            x, y = ens_broadened_out, spec / scale
            ax.plot(x, y, lw=1, color="blue")

            x_occ = x[x <= ens_out[self.H**O]]
            y_occ = y[x <= ens_out[self.H**O]]
            x_virt = x[x >= ens_out[self.LUMO]]
            y_virt = y[x >= ens_out[self.LUMO]]

            ax.fill_between(x_occ, 0, y_occ, alpha=0.5, color="blue")
            ax.fill_between(x_virt, 0, y_virt, alpha=0.5, color="red")

            ax.annotate('',
                        xy=(ens_out[self.H**O], 1.1),
                        xycoords='data',
                        xytext=(ens_out[self.LUMO], 1.1),
                        textcoords='data',
                        arrowprops={'arrowstyle': '<->'})
            ax.annotate(
                'gap = %4.3f' % (ens_out[self.LUMO] - ens_out[self.H**O]),
                xy=((ens_out[self.H**O] + ens_out[self.LUMO]) / 2.0, 1.1),
                xycoords='data',
                xytext=(0, 5),
                textcoords='offset points')

        self.canvasDOS.draw()

        self.showMullikenCharges()
        if len(selected) > 0:
            i = selected[0].row()
            self.moLabel.setText("Molecular Orbital: %s %s" %
                                 (i + 1, self.mo_names[i]))
            if i in self.mo_cubes:
                mo_cube = self.mo_cubes[i]
                self.molecularOrbitalViewer.setCubes([mo_cube])
            else:
                print("No cube for molecular orbital %d" % (i + 1))

    def showMullikenCharges(self):
        atomlist = self.tddftb.dftb2.getGeometry()
        dq0 = self.tddftb.dftb2.getPartialCharges()
        # internally charges are measured in units of e,
        charges = -dq0
        self.chargesViewer.setGeometriesAndCharges([atomlist], [charges])

    def calculateCubes(self):
        selected = self.tableMOs.selectionModel().selectedRows()
        for s in selected:
            i = s.row()
            if not (i in self.mo_cubes):
                # need to calculated cube
                print("compute cube for molecular orbital %d ..." % (i + 1),
                      end=' ')
                mo_cube = self._computeMO(i)
                print("...done")
                self.mo_cubes[i] = mo_cube

                self.tableMOs.setItem(i,
                                      self.tableMOs.columnCount() - 1,
                                      QtGui.QTableWidgetItem("Y"))
                self.tableMOs.item(i,
                                   self.tableMOs.columnCount() -
                                   1).setBackground(QtGui.QColor(255, 0, 0))
        self.plotDOS()

    def recalculateCubes(self):
        # delete cubes
        for i in list(self.mo_cubes.keys()):
            self.tableMOs.setItem(i,
                                  self.tableMOs.columnCount() - 1,
                                  QtGui.QTableWidgetItem(""))
            self.tableMOs.item(i,
                               self.tableMOs.columnCount() - 1).setBackground(
                                   QtGui.QColor(255, 255, 255))
        self.mo_cubes = {}
        delattr(Cube.orbital_amplitude,
                "cached_grid")  # cached grid has wrong resolution
        self.calculateCubes()

    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
Exemple #4
0
class QSpectrumWidget(QtGui.QWidget):
    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

    def plotSpectrum(self):
        self.figSpectrum.clf()
        ax = self.figSpectrum.add_subplot(111)

        units = self.energyUnits.itemText(self.energyUnits.currentIndex())
        broadening = abs(float(self.broadening.text()))
        ax.set_title("Absorption Spectrum")
        ax.set_xlabel("Excitation Energy / %s" % units, fontsize=15)
        ax.set_ylabel("Oscillator strength", fontsize=15)

        ens = self.tddftb.Omega
        osz = self.tddftb.oscillator_strength
        # stick spectrum
        ens_out = convert_energy(ens, "Hartree", units)
        ax.vlines(ens_out,
                  np.zeros(ens_out.shape),
                  osz,
                  lw=2,
                  color="black",
                  picker=5)

        # selected states are highlighted
        selected = self.tableStates.selectionModel().selectedRows()
        for s in selected:
            I = s.row()
            ax.vlines(ens_out[I], [0.0], osz[I], lw=3, color="green")
            ax.text(
                ens_out[I], osz[I], "%s (%s$_{%d}$)" %
                (self.tddftb.Irreps[I], self.tddftb.multiplicity, I + 1))

        def onpick(event):
            self.tableStates.setCurrentCell(event.ind[0], 0)

        self.figSpectrum.canvas.mpl_connect('pick_event', onpick)

        if broadening > 0.0:
            ens_broadened, spec = broadened_spectrum(ens, osz, broadening)
            ens_broadened_out = convert_energy(ens_broadened, "Hartree", units)
            scale = spec.max() / osz.max() / 1.1
            ax.plot(ens_broadened_out, spec / scale, lw=1, color="blue")

        self.canvasSpectrum.draw()

        if len(selected) > 0:
            I = selected[0].row()
            self.tdenseLabel.setText(
                "Excited State: %s (%s%d)" %
                (self.tddftb.Irreps[I], self.tddftb.multiplicity, I + 1))
            self.showPartialCharges(I)
            if I in self.tdense_cubes:
                tdense_cube = self.tdense_cubes[I]
                self.transitionDensityViewer.setCubes([tdense_cube])
                difdense_cube = self.difdense_cubes[I]
                self.differenceDensityViewer.setCubes([difdense_cube])
            else:
                print(
                    "No cube for transition and difference density of state %d"
                    % (I + 1))
            # transition densities in occ-virt plane
            self.plotExcitationCoefficients2D(I)
        self.canvasExvec.draw()

    def plotExcitationCoefficients2D(self, I):
        self.figExvec.clf()
        ax = self.figExvec.add_subplot(111)
        ax.set_title("$C^{%d}_{(o \\to v)}$" % (I + 1))
        ax.set_xlabel("occupied orbital")
        ax.set_ylabel("virtual orbital")
        active_occupied_orbs, active_virtual_orbs = self.tddftb.getActiveOrbitals(
        )
        xlabel_positions = np.arange(0, len(active_occupied_orbs))
        xlabels = [str(o + 1) for o in active_occupied_orbs]
        xlabels[-1] = "H**O %s" % xlabels[-1]
        ylabel_positions = np.arange(0, len(active_virtual_orbs))
        ylabels = [str(v + 1) for v in active_virtual_orbs]
        ylabels[0] = "LUMO %s" % ylabels[0]
        ax.set_xticks(xlabel_positions)
        ax.set_xticklabels(xlabels, rotation=45, fontsize=10)
        ax.set_yticks(ylabel_positions)
        ax.set_yticklabels(ylabels, fontsize=10)
        self.Cexvec = self.tddftb._getExcitationCoefficients()[I, :, :]
        image = ax.imshow(self.Cexvec.transpose(),
                          interpolation='none',
                          origin='lower',
                          picker=True,
                          vmin=-1.0,
                          vmax=1.0)
        self.figExvec.colorbar(image, ax=ax)

        def onpick(event):
            x, y = event.mouseevent.xdata, event.mouseevent.ydata
            o, v = int(np.floor(x + 0.5)), int(np.floor(y + 0.5))
            if self.Cexvec[o, v] < -0.5:
                color = "white"
            else:
                color = "black"
            print("%s -> %s    %s" %
                  (active_occupied_orbs[o] + 1, active_virtual_orbs[v] + 1,
                   self.Cexvec[o, v]))
            ax.text(o - 0.4,
                    v - 0.25,
                    "$%d \\to %d$\n%+3.3f" %
                    (active_occupied_orbs[o] + 1, active_virtual_orbs[v] + 1,
                     self.Cexvec[o, v]),
                    color=color,
                    fontsize=12)
            self.canvasExvec.draw()

        self.figExvec.canvas.mpl_connect('pick_event', onpick)

    def showPartialCharges(self, I):
        atomlist = self.tddftb.dftb2.getGeometry()
        # particle-hole charges
        print("compute particle-hole charges for excited state %d" % (I + 1))
        particle_charges, hole_charges = self.tddftb.ParticleHoleCharges(I)
        # partial charges on ground state
        dq0 = self.tddftb.dftb2.getPartialCharges()
        # partial charges on excited state
        dqI = particle_charges + hole_charges + dq0
        self.partial_charges[I] = dqI
        # internally charges are measured in units of e,
        charges = -dqI
        self.chargesViewer.setGeometriesAndCharges([atomlist], [charges])
        print(
            "compute transition charges between ground and excited state %d" %
            (I + 1))
        transition_charges = self.tddftb.TransitionChargesState(I)
        self.trans_chargesViewer.setGeometriesAndCharges([atomlist],
                                                         [transition_charges])

    def calculateCubes(self):
        selected = self.tableStates.selectionModel().selectedRows()
        for s in selected:
            I = s.row()
            if not (I in self.tdense_cubes):
                self.showPartialCharges(I)
                # need to calculated cube
                print(
                    "compute transition and difference density for state %d ..."
                    % (I + 1),
                    end=' ')
                tdense_cube, difdense_cube = self._computeTransitionAndDifferenceDensity(
                    I)
                print("...done")
                self.tdense_cubes[I] = tdense_cube
                self.difdense_cubes[I] = difdense_cube

                self.tableStates.setItem(I,
                                         self.tableStates.columnCount() - 1,
                                         QtGui.QTableWidgetItem("Y"))
                self.tableStates.item(I,
                                      self.tableStates.columnCount() -
                                      1).setBackground(QtGui.QColor(255, 0, 0))
        self.plotSpectrum()

    def recalculateCubes(self):
        # delete cubes
        for I in list(self.tdense_cubes.keys()):
            self.tableStates.setItem(I,
                                     self.tableStates.columnCount() - 1,
                                     QtGui.QTableWidgetItem(""))
            self.tableStates.item(I,
                                  self.tableStates.columnCount() -
                                  1).setBackground(QtGui.QColor(255, 255, 255))
        self.tdense_cubes = {}
        self.difdense_cubes = {}
        self.calculateCubes()

    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

        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
Exemple #5
0
    def __init__(self, ):
        super(Main, self).__init__()
        self.ui = loadUiWidget(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         "window2.ui"))
        self.setCentralWidget(self.ui)
        self.canvases = []
        self.trajdirs = []

        self.dt = 0.0
        self.geometries = []
        self.particle_hole_charges = []

        self.ui.trajSelection.itemSelectionChanged.connect(
            self.changeTrajectory)

        # load Trajectories
        dyndir = "./"
        if len(sys.argv) > 1:
            dyndir = sys.argv[1]
        self.ui.dirBrowser.setText(dyndir)
        self.ui.dirBrowser.returnPressed.connect(self.fetchURL)
        self.ui.openButton.clicked.connect(self.openDirectory)

        # save movie
        self.ui.movieButton.clicked.connect(self.renderMovie)
        self.stop = False
        # plots
        import matplotlib.pyplot as plt
        cmap = plt.get_cmap('gnuplot')
        self.colors = ['black', 'red', 'green', 'blue', 'orange', 'm'
                       ] + [cmap(f) for f in np.linspace(0.4, 1.0, 10)]

        self.energiesAxis = self.addTab(self.ui.energiesLayout)
        self.coefficientsAxis = self.addTab(self.ui.coefficientsLayout)
        self.couplingsAxis = self.addTab(self.ui.couplingsLayout)
        self.currEnergyAxis = self.addTab(self.ui.currEnergyLayout)
        self.currStateAxis = self.addTab(self.ui.currStateLayout)
        self.populationsAxis = self.addTab(self.ui.populationsLayout)
        self.axes = [
            self.energiesAxis, self.coefficientsAxis, self.couplingsAxis,
            self.currEnergyAxis, self.currStateAxis
        ]

        self.objects = []

        # Window with geometries
        self.geometryViewer = QCubeViewerWidget()
        self.geometryViewer.setAnimation(True)
        #self.geometryViewer.hideIsoControls()
        self.geometryViewer.selectShowOptions(options=["charges"])
        self.ui.viewerWidgetLayout.addWidget(self.geometryViewer)
        """
        self.glWidget = self.newGLWidget()
        self.mol = Avogadro.molecules.addMolecule()
        self.objects.append( self.mol )
        self.glWidget.molecule = self.mol
        self.ui.viewerWidgetLayout.addWidget(Avogadro.toPyQt(self.glWidget))
        """
        #
        self.ui.animationSlider.valueChanged.connect(self.showMolecules)

        self.loadTrajectories(dyndir)

        #
        self.pictureAorDiab = QtGui.QComboBox()
        self.pictureAorDiab.addItems(["adiabatic", "local diabatic"])
        self.pictureAorDiab.setToolTip(
            "Choose whether energies and couplings should be shown in the adiabatic or the local diabatic picture."
        )
        self.pictureAorDiab.currentIndexChanged.connect(self.switchPicture)
        self.picture = self.pictureAorDiab.currentText()
        self.ui.gridLayout.addWidget(self.pictureAorDiab, 1, 0)
Exemple #6
0
class Main(QtGui.QMainWindow):
    def __init__(self, ):
        super(Main, self).__init__()
        self.ui = loadUiWidget(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         "window2.ui"))
        self.setCentralWidget(self.ui)
        self.canvases = []
        self.trajdirs = []

        self.dt = 0.0
        self.geometries = []
        self.particle_hole_charges = []

        self.ui.trajSelection.itemSelectionChanged.connect(
            self.changeTrajectory)

        # load Trajectories
        dyndir = "./"
        if len(sys.argv) > 1:
            dyndir = sys.argv[1]
        self.ui.dirBrowser.setText(dyndir)
        self.ui.dirBrowser.returnPressed.connect(self.fetchURL)
        self.ui.openButton.clicked.connect(self.openDirectory)

        # save movie
        self.ui.movieButton.clicked.connect(self.renderMovie)
        self.stop = False
        # plots
        import matplotlib.pyplot as plt
        cmap = plt.get_cmap('gnuplot')
        self.colors = ['black', 'red', 'green', 'blue', 'orange', 'm'
                       ] + [cmap(f) for f in np.linspace(0.4, 1.0, 10)]

        self.energiesAxis = self.addTab(self.ui.energiesLayout)
        self.coefficientsAxis = self.addTab(self.ui.coefficientsLayout)
        self.couplingsAxis = self.addTab(self.ui.couplingsLayout)
        self.currEnergyAxis = self.addTab(self.ui.currEnergyLayout)
        self.currStateAxis = self.addTab(self.ui.currStateLayout)
        self.populationsAxis = self.addTab(self.ui.populationsLayout)
        self.axes = [
            self.energiesAxis, self.coefficientsAxis, self.couplingsAxis,
            self.currEnergyAxis, self.currStateAxis
        ]

        self.objects = []

        # Window with geometries
        self.geometryViewer = QCubeViewerWidget()
        self.geometryViewer.setAnimation(True)
        #self.geometryViewer.hideIsoControls()
        self.geometryViewer.selectShowOptions(options=["charges"])
        self.ui.viewerWidgetLayout.addWidget(self.geometryViewer)
        """
        self.glWidget = self.newGLWidget()
        self.mol = Avogadro.molecules.addMolecule()
        self.objects.append( self.mol )
        self.glWidget.molecule = self.mol
        self.ui.viewerWidgetLayout.addWidget(Avogadro.toPyQt(self.glWidget))
        """
        #
        self.ui.animationSlider.valueChanged.connect(self.showMolecules)

        self.loadTrajectories(dyndir)

        #
        self.pictureAorDiab = QtGui.QComboBox()
        self.pictureAorDiab.addItems(["adiabatic", "local diabatic"])
        self.pictureAorDiab.setToolTip(
            "Choose whether energies and couplings should be shown in the adiabatic or the local diabatic picture."
        )
        self.pictureAorDiab.currentIndexChanged.connect(self.switchPicture)
        self.picture = self.pictureAorDiab.currentText()
        self.ui.gridLayout.addWidget(self.pictureAorDiab, 1, 0)

    def switchPicture(self):
        self.picture = self.pictureAorDiab.currentText()
        if self.picture == "adiabatic":
            self.ui.tabWidget.setTabText(0, "Adiab. Energies")
            self.ui.tabWidget.setTabText(2, "Nonadiab. Couplings")
        else:
            self.ui.tabWidget.setTabText(0, "Local Diab. Energies")
            self.ui.tabWidget.setTabText(2, "Local Diab. Coupl.")
        self.changeTrajectory()

    def openDirectory(self):
        dyndir = QtGui.QFileDialog.getExistingDirectory()
        self.ui.dirBrowser.setText(dyndir)
        self.fetchURL()

    def fetchURL(self):
        dyndir = str(self.ui.dirBrowser.text())
        print "TOP DIRECTORY: %s" % dyndir
        self.loadTrajectories(dyndir)

    def loadTrajectories(self, dyndir):
        dirs = glob.glob(os.path.join(dyndir, "*TRAJ*/"))
        if len(dirs) == 0:
            # open a single trajectory
            if os.path.isfile(os.path.join(dyndir, "dynamics.xyz")):
                self.ui.trajSelection.addItem("%d" % len(self.trajdirs))
                self.trajdirs.append(dyndir)
        else:
            dirs.sort()
            # remove previous trajectories
            self.trajdirs = []
            for i, trajdir in enumerate(dirs):
                if os.path.isfile(os.path.join(trajdir, "dynamics.xyz")):
                    print "Load trajectory form %s" % trajdir
                    self.ui.trajSelection.addItem("%d" % i)
                    self.trajdirs.append(trajdir)

            self.plotPopulations()

            for ax in self.axes:
                ax.clear()
                ax.set_xlim((-1.0, 1.0))
                ax.set_ylim((-1.0, 1.0))
                ax.text(-0.75, 0.2, "Click on a trajectory", fontsize=30)
                ax.text(-0.75, -0.2, "    on the left", fontsize=30)
            for canvas in self.canvases:
                canvas.draw()

    def plotPopulations(self):
        tmin = 0.0
        tmax = 0.0
        Nst = 0
        Nt = 0
        data_list = []
        for i, trajdir in enumerate(self.trajdirs):
            state_file = os.path.join(trajdir, "state.dat")
            try:
                data = np.loadtxt(state_file)
            except IOError as e:
                print e
                continue
            Nst = max(data[:, 1].max() + 1, Nst)
            tmin = min(data[:, 0].min(), tmin)
            tmax = max(data[:, 0].max(), tmax)
            Nt = max(len(data[:, 0]), Nt)
            data_list.append(data)
        Nst = int(Nst)
        Nt = int(Nt)
        print "%d electronic states" % Nst
        print "%d time steps between %s and %s" % (Nt, tmin, tmax)
        pop = np.zeros((Nt, Nst + 1))
        pop[:, 0] = np.linspace(tmin, tmax, Nt)  # time axis in fs
        Ntraj = [0 for t in range(0, Nt)
                 ]  # count trajectories available at each time step
        for i, data in enumerate(data_list):
            # only consider trajectories that finished nicely
            Nt_i = data.shape[0]
            if Nt_i != Nt:
                print "Trajectory %d has only %d time steps" % (i, Nt_i)
            for t in range(0, Nt_i):
                st = data[t, 1]
                pop[t, st + 1] += 1
                Ntraj[t] += 1.0
        print "%s trajectories" % Ntraj[0]
        # divide populations by the number of trajectories
        for t in range(0, Nt):
            pop[t, 1:] /= float(Ntraj[t])

        self.populationsAxis.clear()
        self.populationsAxis.set_xlabel("Time / fs", fontsize=15)
        self.populationsAxis.set_ylabel("Populations", fontsize=15)
        for i in range(0, Nst):
            self.populationsAxis.plot(pop[:, 0],
                                      pop[:, 1 + i],
                                      lw=2,
                                      color=self.colors[i],
                                      label="State %d" % i)

    def addTab(self, layout):
        fig = Figure()
        ax = fig.add_subplot(111)
        ax.set_xlim((-1.0, 1.0))
        ax.set_ylim((-1.0, 1.0))
        ax.text(-0.75, 0.2, "Click on a trajectory", fontsize=30)
        ax.text(-0.75, -0.2, "    on the left", fontsize=30)

        canvas = FigureCanvas(fig)
        self.canvases.append(canvas)
        layout.addWidget(canvas)
        canvas.draw()
        toolbar = NavigationToolbar(canvas, canvas, coordinates=True)
        return ax

    def newGLWidget(self):
        glWidget = Avogadro.GLWidget()
        glWidget.loadDefaultEngines()
        glWidget.quality = 4
        toolGroup = Avogadro.ToolGroup()
        tool = Avogadro.PluginManager.instance.tool('Navigate', None)
        if tool:
            toolGroup.append(tool)
            self.objects.append(tool)
        glWidget.toolGroup = toolGroup
        self.objects.append(glWidget)
        self.objects.append(toolGroup)
        return glWidget

    def changeTrajectory(self):
        selected = self.ui.trajSelection.selectedItems()
        trajs = map(int, [s.text() for s in selected])
        self.geometries = []
        self.nsteps = 0
        print "Selected: %s" % map(str, trajs)
        print "Plot trajectories %s" % trajs
        for ax in self.axes:
            ax.clear()
        for t in trajs:
            trajdir = self.trajdirs[t]
            print trajdir
            try:
                self.plotTrajectory(trajdir, t)
                if os.path.isfile(
                        os.path.join(trajdir, "particle_hole_charges.xyz")):
                    geoms, ph_charges = read_xyz_datafields(
                        os.path.join(trajdir, "particle_hole_charges.xyz"))
                else:
                    geoms = XYZ.read_xyz(os.path.join(trajdir, "dynamics.xyz"))
                    ph_charges = None
                self.geometries.append(geoms)
                self.particle_hole_charges.append(ph_charges)
                self.nsteps = max(len(self.geometries[-1]), self.nsteps)
            except (ValueError, IOError) as e:
                print "Unable to load %s" % trajdir
                print "ERROR: %s" % e
        for canvas in self.canvases:
            canvas.draw()

        self.ui.animationSlider.setMinimum(0)
        self.ui.animationSlider.setMaximum(self.nsteps)
        self.ui.animationTime.setText("Time: %7.2f fs" % 0.0)
        self.geometryViewer.clear()
        self.showMolecules()

    def showMolecules(self):
        tstep = self.ui.animationSlider.value()
        self.ui.animationTime.setText("Time: %7.2f fs" % (tstep * self.dt))
        molecules = []
        charge_lists = []
        for geoms, ph_charges in zip(self.geometries,
                                     self.particle_hole_charges):
            geom_time = geoms[min(tstep, len(geoms) - 1)]
            if type(ph_charges) == type(None):
                ph_charges_times = np.zeros((len(geom_time), 2))
            else:
                ph_charges_times = ph_charges[min(tstep, len(ph_charges) - 1)]
                charge_lists.append(-ph_charges_times)
            molecules.append(geom_time)
            # change sign of charges, internally charges are represented in units of e-!
        if len(charge_lists) == 0:
            self.geometryViewer.setGeometries(molecules)
        else:
            self.geometryViewer.setGeometriesAndCharges(
                molecules, charge_lists)

    def abortCurrentAction(self):
        self.stop = True
        #raise AbortException()
    def renderMovie(self):
        if len(self.geometries) == 0:
            # no trajectories
            return
        # ask for filename and start and end frames
        (ok, path, start, end, step) = SaveMovieDialog.getDirnameAndFrames()
        if ok == False:
            return
        steps = range(0, self.nsteps)
        self.ui.movieButton.setText("Stop")
        self.ui.movieButton.clicked.disconnect()
        self.ui.movieButton.clicked.connect(self.abortCurrentAction)

        dirname = os.path.dirname(path)
        basename = os.path.basename(path)
        if basename == '':
            basename = "movie.png"
        name, suffix = os.path.splitext(basename)
        print "Frames will be stored in %s" % os.path.join(
            dirname, "%s_######.png" % name)
        for i in steps[start:end:step]:
            print "render frame %d" % i
            self.ui.animationSlider.setValue(i)
            image_file = os.path.join(dirname, "%s_%06d.png" % (name, i))
            self.geometryViewer.savefig(image_file)
            if self.stop == True:
                print "stop rendering!"
                self.stop = False
                break

        self.ui.movieButton.setText("Movie...")
        self.ui.movieButton.clicked.disconnect()
        self.ui.movieButton.clicked.connect(self.renderMovie)

    def plotTrajectory(self, trajdir, trajID):
        # load adiabatic energies
        energy_files = glob.glob(os.path.join(trajdir, "energy_*.dat"))
        states = []
        energies_list = []
        for f in energy_files:
            states.append(int(f[-15:-4].split("_")[1]))
            data = np.loadtxt(f)
            times = data[:, 0]
            energies_list.append(data[:, 1])
        Nst = len(states)
        Nt = len(times)
        energies = np.zeros((Nt, Nst))
        indx = np.argsort(states)
        for i in range(0, Nst):
            energies[:, i] = energies_list[indx[i]]
        # plot levels
        if self.picture == "adiabatic":
            print "adiabatic levels ...",
            self.energiesAxis.set_xlabel("Time / fs", fontsize=17)
            self.energiesAxis.set_ylabel("Adiab. Energy / eV", fontsize=17)
            for i in range(0, Nst):
                self.energiesAxis.plot(times,
                                       energies[:, i] * 27.211,
                                       lw=2,
                                       color=self.colors[i],
                                       label="State %d" % i)
            print "...done"
        else:
            print "local diabatic levels ..."
            # local diabatic picture
            ld_energy_file = os.path.join(trajdir,
                                          "local_diabatic_energies.dat")
            self.energiesAxis.set_xlabel("Time / fs", fontsize=17)
            self.energiesAxis.set_ylabel("Local Diab. Energy / eV",
                                         fontsize=17)
            try:
                data = np.loadtxt(ld_energy_file)
                for i in range(0, Nst):
                    self.energiesAxis.plot(data[:, 0],
                                           data[:, 1 + i] * 27.211,
                                           lw=2,
                                           color=self.colors[i],
                                           label="State %d" % i)
                print "...done"
            except IOError as e:
                print "No diabatic energies"
                print e
        # plot current state
        print "current states ...",
        curr_states = np.loadtxt(os.path.join(trajdir, "state.dat"))
        curr_energy = []
        state_changes = [0]
        st_last = curr_states[0, 1]
        for i in range(0, Nt):
            st = curr_states[i, 1]
            if st_last != st:
                state_changes.append(i)
                st_last = st
            curr_energy.append(energies[i, st])
        state_changes.append(i)
        curr_energy = np.array(curr_energy)
        # show current energy
        # ... as a dashed brown line
        self.energiesAxis.plot(times,
                               curr_energy * 27.211,
                               ls="-.",
                               lw=3,
                               color="brown",
                               label="Curr. State")
        # ... or as circles
        #self.energiesAxis.plot(times[::15], (curr_energy*27.211)[::15], 'o', color='brown', markersize=13, mfc='none', label="Curr. State")
        print "...done"
        # time-step in fs
        self.dt = times[1] - times[0]
        # couplings
        if self.picture == "adiabatic":
            # non-adiabatic couplings
            print "non-adiabatic couplings...",
            self.couplingsAxis.set_xlabel("Time / fs", fontsize=15)
            self.couplingsAxis.set_ylabel("Scalar Non-adiab. Coupling",
                                          fontsize=15)
            for i in range(0, Nst):
                for j in range(i + 1, Nst):
                    try:
                        data = np.loadtxt(
                            os.path.join(
                                trajdir,
                                "nonadiabatic" + str(i) + str(j) + ".dat"))
                        self.couplingsAxis.plot(
                            data[:, 0],
                            data[:, 1],
                            lw=2,
                            label="Non-adiab. coupling %d-%d" % (i, j))
                    except IOError as e:
                        print e
            print "...done"
        else:
            # local diabatic couplings
            print "local diabatic coupling...",
            ld_coupling_file = os.path.join(trajdir,
                                            "local_diabatic_couplings.dat")
            self.couplingsAxis.set_xlabel("Time / fs", fontsize=15)
            self.couplingsAxis.set_ylabel("Scalar Local Diab. Coupling",
                                          fontsize=15)
            try:
                data = np.loadtxt(ld_coupling_file)
                c = 1
                for i in range(0, Nst):
                    for j in range(i + 1, Nst):
                        self.couplingsAxis.plot(
                            data[:, 0],
                            data[:, c],
                            lw=2,
                            label="Local diab. coupling %d-%d" % (i, j))
                        c += 1
                print "...done"
            except IOError as e:
                print "No diabatic couplings"
                print e

        # coefficients
        print "coefficients ...",
        self.coefficientsAxis.set_xlabel("Time / fs", fontsize=15)
        self.coefficientsAxis.set_ylabel(
            "Electronic Coefficients $\\vert C_i \\vert^2$", fontsize=15)
        for i in range(0, Nst):
            try:
                data = np.loadtxt(
                    os.path.join(trajdir, "coeff_" + str(i) + ".dat"))
                self.coefficientsAxis.plot(data[:, 0],
                                           data[:, 1],
                                           lw=2,
                                           color=self.colors[i],
                                           label="$\\vert C_%d \\vert^2$" % i)
            except IOError as e:
                print e
        print "...done"
        # current energy
        print "current energy ...",
        self.currEnergyAxis.set_xlabel("Time / fs", fontsize=15)
        self.currEnergyAxis.set_ylabel("Current Energy / eV", fontsize=15)
        try:
            data = np.loadtxt(os.path.join(trajdir, "curr_energy.dat"))
            self.currEnergyAxis.plot(data[:, 0],
                                     data[:, 1] * 27.211,
                                     lw=2,
                                     color="black",
                                     label="kinetic")
            self.currEnergyAxis.plot(data[:, 0],
                                     data[:, 2] * 27.211,
                                     lw=2,
                                     color="red",
                                     label="potential")
            if data.shape[1] > 3:
                self.currEnergyAxis.plot(data[:, 0],
                                         data[:, 3] * 27.211,
                                         lw=2,
                                         color="green",
                                         label="total")
            legend = self.currEnergyAxis.get_legend()
            if legend == None:
                handles, labels = self.currEnergyAxis.get_legend_handles_labels(
                )
                self.currEnergyAxis.legend(handles, labels, loc=1)
        except IOError as e:
            print e
        print "...done"
        # current states as blocks
        print "current state blocks ...",
        self.currStateAxis.set_yticks(range(0, len(self.trajdirs)))
        self.currStateAxis.set_xlabel("Time / fs", fontsize=15)
        self.currStateAxis.set_ylabel("Trajectory", fontsize=15)
        state = curr_states[0, 1]
        for i in range(1, len(state_changes)):
            xi = times[state_changes[i - 1]]
            yi = trajID - 0.45
            width = times[state_changes[i]] - times[state_changes[i - 1]]
            st = int(curr_states[state_changes[i - 1], 1])
            self.currStateAxis.add_patch(
                patches.Rectangle((xi, yi), width, 0.90,
                                  color=self.colors[st]))
            self.currStateAxis.text(xi + 0.5 * width, yi + 0.25, "%d" % st)
        xmin, xmax = self.currStateAxis.get_xlim()
        self.currStateAxis.set_xlim((min(times[0], xmin), max(times[-1],
                                                              xmax)))
        ymin, ymax = self.currStateAxis.get_ylim()
        self.currStateAxis.set_ylim((min(ymin,
                                         trajID - 1), max(ymax, trajID + 1)))
        print "...done"
Exemple #7
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)
Exemple #8
0
class Main(QtGui.QMainWindow):
    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)

    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"

    def searchPKE(self):
        self.photo_kinetic_energy = slako_tables_scattering.energies[
            self.pke_slider.value()]
        self.pke_label.setText("PKE: %6.4f eV" %
                               (self.photo_kinetic_energy * 27.211))
        #self.pke_label.update()
    @busy
    def changePKE(self):
        self.photo_kinetic_energy = slako_tables_scattering.energies[
            self.pke_slider.value()]
        self.pke_label.setText("PKE: %6.4f eV" %
                               (self.photo_kinetic_energy * 27.211))
        self.loadContinuum()
        self.plotContinuumOrbital()
        self.plotMFPAD()
        self.plotAveragedPAD()

    @busy
    def selectBoundOrbital(self):
        self.plotBoundOrbital()
        self.plotContinuumOrbital()
        self.plotMFPAD()
        self.deletePADTable()
        self.plotAveragedPAD()

    def plotBoundOrbital(self):
        selected = self.orbitalSelection.selectedItems()
        assert len(selected) == 1
        selected_orbital = self.orbital_dict[str(selected[0].text())]

        self.mo_bound = self.bound_orbs[:, selected_orbital]
        # shift geometry so that the expectation value of the dipole operator vanishes
        # dipole matrix
        dipole = np.tensordot(self.mo_bound,
                              np.tensordot(self.tddftb.dftb2.D,
                                           self.mo_bound,
                                           axes=(1, 0)),
                              axes=(0, 0))
        print "expectation value of dipole: %s" % dipole
        # shift molecule R -> R - dipole
        self.atomlist = MolCo.transform_molecule(
            self.tddftb.dftb2.getGeometry(), (0, 0, 0), -dipole)
        # load bound basis functions
        self.bs_bound = AtomicBasisSet(self.atomlist)
        # plot selected orbital
        (xmin, xmax), (ymin, ymax), (zmin, zmax) = Cube.get_bbox(self.atomlist,
                                                                 dbuff=5.0)
        dx, dy, dz = xmax - xmin, ymax - ymin, zmax - zmin
        ppb = 3.0  # Points per bohr
        nx, ny, nz = int(dx * ppb), int(dy * ppb), int(dz * ppb)
        x, y, z = np.mgrid[xmin:xmax:nx * 1j, ymin:ymax:ny * 1j,
                           zmin:zmax:nz * 1j]
        grid = (x, y, z)
        amplitude_bound = Cube.orbital_amplitude(grid,
                                                 self.bs_bound.bfs,
                                                 self.mo_bound,
                                                 cache=False)

        bound_cube = CubeData()
        bound_cube.data = amplitude_bound.real
        bound_cube.grid = grid
        bound_cube.atomlist = self.atomlist

        self.boundOrbitalViewer.setCubes([bound_cube])

    def 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 picker_callback(self, picker):
        for actors in self.efield_actors:
            if picker.actor in actors:
                mlab = self.continuumOrbitalViewer.visualization.scene.mlab
                self.selected = "arrow"
                w = 1.0
                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)
                break
        else:
            #
            if self.selected != None:
                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.epol = np.array(picker.pick_position)
                self.plotContinuumOrbital()
                self.plotMFPAD()
                self.selected = None

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

    @busy
    def activateAveragedPAD(self, s):
        print "s = %s" % s
        self.loadContinuum()
        self.plotAveragedPAD()

    @busy
    def plotAveragedPAD(self):
        if self.activate_average.isChecked() == False:
            for fig in [self.AvgPADfig1D, self.AvgPADfig2D]:
                fig.clf()
                ax = fig.add_subplot(111)
                ax.set_xlim((0.0, 1.0))
                ax.set_ylim((0.0, 1.0))
                ax.text(0.2, 0.6, "click checkbox to", fontsize=20)
                ax.text(0.2, 0.3, "activate averaging", fontsize=20)
                #self.AvgPADfig1D.draw()
        else:
            pad, betas = self.orientation_averaging.averaged_pad(self.mo_bound)

            # 1D plot
            # cut along any phi
            self.AvgPADfig1D.clf()
            ax1d = self.AvgPADfig1D.add_subplot(111, projection='polar')
            nx, ny = pad.shape
            thetas = np.linspace(0.0, np.pi, nx)
            for i in range(0, ny):
                ax1d.plot(thetas, pad[:, i], color="black")
                ax1d.plot(thetas + np.pi, pad[::-1, i], color="black")
            # plot PAD = sigma/4pi * (1 + beta2 * P2(cos(theta))) in read
            pad2 = betas[0] / (4.0 * np.pi) * (1.0 + betas[2] * 0.5 *
                                               (3 * np.cos(thetas)**2 - 1.0))
            print "max(pad) / max(pad2) = %s" % (pad.max() / pad2.max())
            ax1d.plot(thetas, pad2, color="red", ls="-.")
            ax1d.plot(thetas + np.pi, pad2[::-1], color="red", ls="-.")

            self.AvgPADCanvas1D.draw()
            # 2D plot
            self.AvgPADfig2D.clf()
            ax2d = self.AvgPADfig2D.add_subplot(111)
            image = ax2d.imshow(np.fliplr(pad.transpose()),
                                extent=[0.0, np.pi, 0.0, 2 * np.pi],
                                aspect=0.5,
                                origin='lower')
            ax2d.set_xlim((0.0, np.pi))
            ax2d.set_ylim((0.0, 2 * np.pi))
            ax2d.set_xlabel("$\\theta$")
            ax2d.set_ylabel("$\phi$")
            self.AvgPADfig2D.colorbar(image)
            self.AvgPADCanvas2D.draw()
            # Table
            n = self.avgpadTable.rowCount()
            self.avgpadTable.insertRow(n)
            self.avgpadTable.setItem(
                n, 0,
                QtGui.QTableWidgetItem("%6.4f" %
                                       (self.photo_kinetic_energy * 27.211)))
            for i, b in enumerate(betas):
                if i == 0:
                    # sigma
                    self.avgpadTable.setItem(
                        n, i + 1, QtGui.QTableWidgetItem("%e" % betas[i]))
                else:
                    self.avgpadTable.setItem(
                        n, i + 1, QtGui.QTableWidgetItem("%6.4f" % betas[i]))

    def deletePADTable(self):
        n = self.avgpadTable.rowCount()
        for i in range(0, n):
            self.avgpadTable.removeRow(0)

    def scanPADTable(self):
        if self.activate_average.isChecked() == False:
            self.statusBar().showMessage(
                "You have to activate averaging first before performing a scan!"
            )
            return
        self.deletePADTable()
        # scan through all PKE's and fill table
        print "SCAN"
        nskip = len(slako_tables_scattering.energies) / int(
            self.settings.getOption("Scan", "nr. points"))
        nskip = max(1, nskip)
        print "nskip = %s" % nskip
        for i, pke in enumerate(slako_tables_scattering.energies):
            if i % nskip != 0:
                continue
            print "*** PKE = %s Hartree ***" % pke
            self.pke_slider.setValue(i)
            self.changePKE()

    def getPADTable(self):
        m, n = self.avgpadTable.rowCount(), self.avgpadTable.columnCount()
        pad_data = np.zeros((m, n))
        for i in range(0, m):
            for j in range(0, n):
                item = self.avgpadTable.item(i, j)
                if item is not None:
                    pad_data[i, j] = float(item.text())
        return pad_data

    def savePADTable(self):
        data_file = QtGui.QFileDialog.getSaveFileName(self, 'Save Table', '',
                                                      '*')[0]
        pad_data = self.getPADTable()
        if str(data_file) != "":
            fh = open(data_file, "w")
            print >> fh, "# PKE/eV     sigma    beta1   beta2   beta3   beta4"
            np.savetxt(fh, pad_data)
            fh.close()
            print "Wrote table with betas to %s" % data_file

    def plotPADTable(self):
        pad_data = self.getPADTable()
        self.plot_window = FigurePopup(pad_data[:, 0], pad_data[:, 1],
                                       pad_data[:, 3])

    def editSettings(self):
        self.settings_window = SettingsPopup(self.settings)