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)