Source code for schrodinger.application.jaguar.gui.theory_tab_widgets

import warnings
from collections import OrderedDict

from schrodinger.infra import mm
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets

from .ui import excited_state_widget_ui
from .ui import spin_treatment_widget_ui
from .utils import JaguarSettingWarning
from .utils import SpinTreatment
from .utils import validate_le_float_input

CONV_MIN = 1.e-12
CONV_MAX = 1.0


[docs]class SpinTreatmentWidget(QtWidgets.QWidget): """ This widget allows user to specify SCF spin treatment options. :ivar spinTreatmentChanged: A signal indicating that the type of spin treatment has changed. :vartype spinTreatmentChanged: `PyQt5.QtCore.pyqtSignal` """ spinTreatmentChanged = QtCore.pyqtSignal()
[docs] def __init__(self, parent): """ Initialize widget. :param parent: parent of this dialog. :type parent: QtCore.QObject """ super(SpinTreatmentWidget, self).__init__(parent) self.ui = spin_treatment_widget_ui.Ui_Widget() self.ui.setupUi(self) self.ui.btngrp.buttonClicked.connect(self.spinTreatmentChanged.emit)
[docs] def getMmJagKeywords(self): """ This function returns dictionary of mmjag keywords for this widget. :return: mmjag keywords dictionary :rtype: dict """ treatment = self.getSpinTreatment() return {mm.MMJAG_IKEY_IUHF: treatment.value}
[docs] def loadSettings(self, jag_input): """ Restore spin treatment settings from Jaguar handle. :param jag_input: The Jaguar settings to base the widget settings on :type jag_input: `schrodinger.application.jaguar.input.JaguarInput` """ iuhf = jag_input[mm.MMJAG_IKEY_IUHF] if iuhf == mm.MMJAG_IUHF_ON: self.ui.unrestricted_rb.setChecked(True) elif iuhf == mm.MMJAG_IUHF_OFF: self.ui.restricted_rb.setChecked(True) elif iuhf == mm.MMJAG_IUHF_AUTOMATIC: self.ui.automatic_rb.setChecked(True) else: err = "Unrecognized %s setting (%s)." % (mm.MMJAG_IKEY_IUHF, iuhf) warnings.warn(JaguarSettingWarning(err))
[docs] def getSpinTreatment(self): """ Return the current spin treatment setting :return: The current spin treatment setting :rtype: `SpinTreatment` """ if self.ui.restricted_rb.isChecked(): return SpinTreatment.Restricted elif self.ui.unrestricted_rb.isChecked(): return SpinTreatment.Unrestricted elif self.ui.automatic_rb.isChecked(): return SpinTreatment.Automatic
[docs] def unrestrictedAvailable(self): """ Does the current setting allow for an unrestricted spin treatment? :return: True if the spin treatment is currently set to unrestricted or automatic. False otherwise. :rtype: bool """ return self.getSpinTreatment().unrestrictedAvailable()
[docs]class ExcitedStateWidget(QtWidgets.QWidget): """ This widget allows user to specify 'Excited state' options. :ivar excited_state_changed: A signal indicating that the state of the 'Excited state' toggle has changed. :vartype excited_state_changed: `PyQt5.QtCore.pyqtSignal` """ CALCULATION_MODE = OrderedDict( (("Full linear response", mm.MMJAG_ITDA_OFF), ("Tamm-Dancoff approximation", mm.MMJAG_ITDA_ON))) excited_state_changed = QtCore.pyqtSignal()
[docs] def __init__(self, parent): """ Initialize widget. :param parent: parent of this dialog. :type parent: QtCore.QObject """ self.es_type_rev = {} super(ExcitedStateWidget, self).__init__(parent) self.ui = excited_state_widget_ui.Ui_Widget() self.ui.setupUi(self) self.ui.excited_state_cb.disabled_checkstate = False # setup validators self.ui.energy_conv_le.setValidator( QtGui.QDoubleValidator(CONV_MIN, CONV_MAX, 5, self)) self.ui.residual_conv_le.setValidator( QtGui.QDoubleValidator(CONV_MIN, CONV_MAX, 5, self)) # populate calculation mode combo box self.ui.excited_state_combo.addItemsFromDict(self.CALCULATION_MODE) self.ui.excited_state_cb.toggled.connect( self.excited_state_changed.emit) self.ui.excited_state_combo.currentIndexChanged.connect( self.excited_state_changed.emit) self.ui.es_type_combo.currentIndexChanged.connect( self.excited_state_changed.emit)
[docs] def populateExcitedStatesType(self, es_types): """ This function is called to populate combo box that contains excited states types. :param es_types: dictionary that contains names of excited states types and corresponding mmjag keywords. :type es_types: `collections.OrderedDict` """ self.ui.es_type_combo.addItemsFromDict(es_types) self.es_type_rev = {v: k for k, v in es_types.items()}
[docs] def enableExcitedStatesType(self, enable): """ This function is used to enable/disable combo box that defines excited states type and its label. :param enable: True or False to enable or disable widgets :type enable: bool """ self.ui.es_lbl.setEnabled(enable) self.ui.es_type_combo.setEnabled(enable)
[docs] def getExcitedState(self): """ Return whether excited state check box is checked or not :return: state of excited state check box :rtype: bool """ return self.ui.excited_state_cb.isChecked()
[docs] def enableExcitedState(self, enable): """ This function is used to enable/disable TDDFT check box and excited state combo box. When TDDFT check box is disabled it will also get unchecked. :param enable: True or False to enable or disable widgets :type enable: bool """ self.ui.excited_state_cb.setEnabled(enable) self.ui.excited_state_combo.setEnabled(enable)
[docs] def getMmJagKeywords(self): """ This function returns dictionary of mmjag keywords for this widget. :return: mmjag keywords dictionary :rtype: dict """ itddft = mm.MMJAG_ITDDFT_OFF itda = mm.MMJAG_ITDA_OFF num_states = None max_iter = None energy_conv = None residual_conv = None rsinglet = None rtriplet = None if self.ui.excited_state_cb.isChecked(): itddft = mm.MMJAG_ITDA_ON itda = self.ui.excited_state_combo.currentData() if self.ui.es_type_combo.isEnabled(): data = self.ui.es_type_combo.currentData() rsinglet, rtriplet = data num_states = self.ui.excited_states_sb.value() max_iter = self.ui.max_iter_sb.value() energy_conv = validate_le_float_input( self.ui.energy_conv_le, "Invalid input for energy convergence threshold field.") residual_conv = validate_le_float_input( self.ui.residual_conv_le, "Invalid input for residual convergence threshold field.") keywords = { mm.MMJAG_IKEY_ITDDFT: itddft, mm.MMJAG_IKEY_ITDA: itda, mm.MMJAG_IKEY_RSINGLET: rsinglet, mm.MMJAG_IKEY_RTRIPLET: rtriplet, mm.MMJAG_IKEY_NROOT: num_states, mm.MMJAG_IKEY_MITERTD: max_iter, mm.MMJAG_RKEY_ECONTD: energy_conv, mm.MMJAG_RKEY_RCONTD: residual_conv } return keywords
[docs] def loadSettings(self, jag_input): """ Restore Excited state settings from Jaguar handle. :param jag_input: The Jaguar settings to base the widget settings on :type jag_input: `schrodinger.application.jaguar.input.JaguarInput` """ tddft = (jag_input[mm.MMJAG_IKEY_ITDDFT] != mm.MMJAG_ITDDFT_OFF) self.ui.excited_state_cb.setChecked(tddft) self.ui.excited_state_combo.setCurrentMmJagData(jag_input, mm.MMJAG_IKEY_ITDA, "calculation method") rsinglet = jag_input[mm.MMJAG_IKEY_RSINGLET] rtriplet = jag_input[mm.MMJAG_IKEY_RTRIPLET] if rsinglet == 0 and rtriplet == 0: # There may be a better way to deal with this. These settings # correspond to disabled combo box. We assume that this is the # case and just set index to default (0). self.ui.es_type_combo.setCurrentIndex(0) else: data = (rsinglet, rtriplet) try: es_type = self.es_type_rev[data] self.ui.es_type_combo.setCurrentText(es_type) except KeyError: msg = ("Excited states type keywords (%s=%s, %s=%s) do not " "match any presets" % (mm.MMJAG_IKEY_RSINGLET, rsinglet, mm.MMJAG_IKEY_RTRIPLET, rtriplet)) warnings.warn(JaguarSettingWarning(msg)) num_states = jag_input[mm.MMJAG_IKEY_NROOT] if num_states < 1: err = ( "Number of excited states (%d) should be greater than zero." % num_states) warnings.warn(JaguarSettingWarning(err)) else: self.ui.excited_states_sb.setValue(num_states) max_iter = jag_input[mm.MMJAG_IKEY_MITERTD] if max_iter < 1: err = ("Maximum TDDFT(TDHF) iterations (%d) should be " "greater than zero." % max_iter) warnings.warn(JaguarSettingWarning(err)) else: self.ui.max_iter_sb.setValue(max_iter) energy_conv = jag_input[mm.MMJAG_RKEY_ECONTD] if energy_conv < CONV_MIN or energy_conv > CONV_MAX: err = ("Energy convergence threshold value (%s) is invalid." % str(energy_conv)) warnings.warn(JaguarSettingWarning(err)) else: self.ui.energy_conv_le.setText(str(energy_conv)) residual_conv = jag_input[mm.MMJAG_RKEY_RCONTD] if residual_conv < CONV_MIN or residual_conv > CONV_MAX: err = ("Residual convergence threshold value (%s) is invalid." % str(energy_conv)) warnings.warn(JaguarSettingWarning(err)) else: self.ui.residual_conv_le.setText(str(residual_conv))
[docs] def setExcitedStateCheckBoxText(self, txt): """ Set text for the excited state check box widget. This method is needed since we want to show different text in the Optimization task. :param txt: excited state check box text :type txt: str """ self.ui.excited_state_cb.setText(txt)
[docs]class HFExcitedStateWidget(ExcitedStateWidget): """ This widget differs from the parent only by some text, which is specific to HF theory level. """ EXCITED_STATE_TEXT = "Excited state (TDHF)" MAXIMUM_ITER_TEXT = "Maximum TDHF iterations:"
[docs] def __init__(self, parent): """ Initialize widget. :param parent: parent of this dialog. :type parent: QtCore.QObject """ super(HFExcitedStateWidget, self).__init__(parent) self.ui.excited_state_cb.setText(self.EXCITED_STATE_TEXT) self.ui.max_iter_lbl.setText(self.MAXIMUM_ITER_TEXT)
[docs]class SpinExcitedStateController(object): """ Controller to facilitate interaction between widget that defines spin treatment options, excited state widget and Hamiltonian widget. This controller is needed because excited states type combo box needs to be enabled or disabled depending on the state of spin treatment, excited state checkbox, and Hamiltonian combo box. This controller is also used to set and get mmjag keywords. """
[docs] def __init__(self, spin_widget, es_widget, h_widget): """ Initialize controller, which takes spin treatment widget and excited state widgets as arguments and establishes connection between the two. :param spin_widget: widget that defines sping treatment options :type spin_widget: `SpinRestrictedWidget` :param es_widget: widget that defines excited state options :type: es_widget: `ExcitedStateWidget` :param h_widget: combo box that defines Hamiltonian :type h_widget: `EnhancedComboBox` """ self.spin_widget = spin_widget self.es_widget = es_widget self.h_widget = h_widget spin_widget.spinTreatmentChanged.connect(self.checkExcitedStatesType) es_widget.excited_state_changed.connect(self.checkExcitedStatesType) h_widget.currentIndexChanged.connect(self.checkExcitedStatesType)
[docs] def checkExcitedStatesType(self): """ This function checks whether excited states type widgets should be disabled. """ so_zora = self.h_widget.currentData() == mm.MMJAG_RELHAM_ZORA_2C if so_zora: # when Spin-orbit ZORA is selected excited state type should # be disabled regardless of the other options. enable = False else: enable = (self.es_widget.getExcitedState() and not self.spin_widget.unrestrictedAvailable()) self.es_widget.enableExcitedStatesType(enable) # TDDFT should not be available if both 'Unrestricted' spin treatment # and Spin-orbit ZORA are selected. spin_treatment = self.spin_widget.getSpinTreatment() enable_tddft = not (so_zora and spin_treatment == SpinTreatment.Unrestricted) self.es_widget.enableExcitedState(enable_tddft)
[docs] def getMmJagKeywords(self): """ This function returns dictionary of mmjag keywords for both spin restricted and excited state widgets. :return: mmjag keywords dictionary :rtype: dict """ keywords = self.spin_widget.getMmJagKeywords() keywords.update(self.es_widget.getMmJagKeywords()) # if both Spin-orbit ZORA and TDDFT are selected set rgeneric option so_zora = self.h_widget.currentData() == mm.MMJAG_RELHAM_ZORA_2C if so_zora and self.es_widget.getExcitedState(): keywords[mm.MMJAG_IKEY_RGENERIC] = mm.MMJAG_RGENERIC_ON else: keywords[mm.MMJAG_IKEY_RGENERIC] = mm.MMJAG_RGENERIC_OFF return keywords
[docs] def loadSettings(self, jag_input): """ Convenience function that allows to specify both spin treatment and excited state options from a given jaguar handle. :param jag_input: The Jaguar settings to base the widget settings on :type jag_input: `schrodinger.application.jaguar.input.JaguarInput` """ for w in [self.spin_widget, self.es_widget]: w.loadSettings(jag_input)