import warnings
from collections import OrderedDict
from itertools import combinations
from itertools import permutations
import schrodinger
from schrodinger.infra import mm
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import Qt
from schrodinger.ui.qt.decorators import suppress_signals
from .. import ui
from .. import utils as gui_utils
from ..utils import JaguarSettingWarning
from . import coordinates
from .transition_state_tab import ActiveTransitionStateTab
maestro = schrodinger.get_maestro()
try:
from schrodinger.maestro import markers
except ImportError:
markers = None
DISTANCE_COORD = "Distance"
COORDINATE_TYPES = OrderedDict(
(("Cartesian - X", mm.MMJAG_COORD_CART_X), ("Cartesian - Y",
mm.MMJAG_COORD_CART_Y),
("Cartesian - Z", mm.MMJAG_COORD_CART_Z), ("Cartesian - XYZ",
mm.MMJAG_COORD_CART_XYZ),
(DISTANCE_COORD, mm.MMJAG_COORD_DISTANCE), ("Angle", mm.MMJAG_COORD_ANGLE),
("Dihedral", mm.MMJAG_COORD_TORSION)))
IP_472_OFF, IP_472_ON = list(range(1, 3))
INITIAL_NOPS_VAL = 10
[docs]class OptimizationTab(coordinates.CoordinateTab):
NAME = "Optimization"
UI_MODULES = (ui.optimization_tab_ui,)
HELP_TOPIC = "JAGUAR_TOPIC_OPTIMIZATION_FOLDER"
CONVERGENCE_CRITERIA = OrderedDict(
(("Default", mm.MMJAG_IACCG_ACCURATE), ("Loose", mm.MMJAG_IACCG_QUICK),
("Tight", mm.MMJAG_IACCG_TIGHT), ("Custom", mm.MMJAG_IACCG_CUSTOM)))
INITIAL_HESSIAN = OrderedDict(
(("Schlegel guess", mm.MMJAG_INHESS_SCHLEGEL),
("Fischer-Almlof guess", mm.MMJAG_INHESS_FISCHER),
("Unit Hessian", mm.MMJAG_INHESS_UNIT), ("Quantum-mechanical",
mm.MMJAG_INHESS_QM)))
COORDINATE_OPTS = OrderedDict((("Redundant internal", mm.MMJAG_INTOPT_RED),
("Cartesian", mm.MMJAG_INTOPT_CART),
("Z-matrix", mm.MMJAG_INTOPT_ZMAT)))
# Map that has constraint coordinate type as a key and a value
# that is a tuple that contains two variables: bool to indicate
# whether button should be visible and the text that should be
# shown on the button.
ALL_CONSTRAINTS_BUTTON = {
mm.MMJAG_COORD_CART_X: (True, "Add Selected Atoms"),
mm.MMJAG_COORD_CART_Y: (True, "Add Selected Atoms"),
mm.MMJAG_COORD_CART_Z: (True, "Add Selected Atoms"),
mm.MMJAG_COORD_CART_XYZ: (True, "Add Selected Atoms"),
mm.MMJAG_COORD_DISTANCE: (True, "Add All Atom Pairs"),
mm.MMJAG_COORD_ANGLE: (True, "Add All Bond Angles"),
mm.MMJAG_COORD_TORSION: (True, "Add All Torsions")
}
SETS_IACCG_CUSTOM = True
[docs] def setup(self):
super().setup()
self.picker = coordinates.CoordinatePicker(COORDINATE_TYPES,
self.ui.pick_cb,
self.ui.coord_type_combo,
self.ui.pick_combo)
# create validators
double_validator = QtGui.QDoubleValidator()
double_validator.setDecimals(3)
for widget in [
self.ui.energy_le, self.ui.gradient_rms_le,
self.ui.max_disp_rms_le
]:
widget.setValidator(double_validator)
# add data to combo boxes
self.ui.convergence_combo.addItemsFromDict(self.CONVERGENCE_CRITERIA)
self.ui.hessian_combo.addItemsFromDict(self.INITIAL_HESSIAN)
self.ui.coordinates_combo.addItemsFromDict(self.COORDINATE_OPTS)
self.ui.custom_frame.setVisible(False)
# setup coordinate table
self.model = ConstraintCoordinatesModel(self)
self.proxy = ConstraintCoordinatesProxyModel(self)
self.proxy.setSourceModel(self.model)
self.ui.tableView.setModel(self.proxy)
self.ui.tableView.setItemDelegateForColumn(
2, ConstraintCoordinatesDelegate(self))
self.ui.tableView.selectionModel().selectionChanged.connect(
self._highlightSelectedMarkers)
# create connections
self.model.dataChanged.connect(self.proxy.dataChanged)
self.picker.pickCompleted.connect(self.pickCompleted)
self.ui.delete_btn.clicked.connect(self.deleteCurrentRow)
self.ui.delete_all_btn.clicked.connect(self.deleteAllRows)
self.ui.convergence_combo.currentIndexChanged.connect(
self.convergenceTypeChanged)
self.ui.coord_type_combo.currentIndexChanged.connect(
self.updateAllConstraintsPB)
self.ui.all_constraints_btn.clicked.connect(self.addConsButtonPressed)
self.updateAllConstraintsPB()
# Since the mm.MMJAG_RKEY_NOPS_OPT_SWITCH must always be reset to
# INITIAL_NOPS_VAL inside getMmJagKeywords, unless the user has
# modified it, we must track the value of
# mm.MMJAG_RKEY_NOPS_OPT_SWITCH to not overwrite user modified value.
self.nops_opt_switch_value = INITIAL_NOPS_VAL
[docs] def getMmJagKeywords(self):
"""
This function returns dictionary of mmjag keywords for this tab.
Since this tab does not set any keywords it returns an empty
dictionary.
:return: mmjag keywords dictionary
:rtype: dict
"""
keywords = {}
keywords[mm.MMJAG_IKEY_MAXITG] = self.ui.max_steps_sb.value()
keywords.update(self.getConvergenceKeywords())
hessian = self.ui.hessian_combo.currentData()
keywords[mm.MMJAG_IKEY_INHESS] = hessian
coord_opt = self.ui.coordinates_combo.currentData()
keywords[mm.MMJAG_IKEY_INTOPT] = coord_opt
# FIXME there is no constant in mmjag_keywords for this option.
ip_472 = IP_472_OFF
if self.ui.save_geom_cb.isChecked():
ip_472 = IP_472_ON
keywords[mm.MMJAG_IKEY_IP472] = ip_472
if self.ui.use_nops_cb.isChecked():
keywords[mm.MMJAG_RKEY_NOPS_OPT_SWITCH] = self.nops_opt_switch_value
else:
keywords[mm.MMJAG_RKEY_NOPS_OPT_SWITCH] = None
return keywords
[docs] def getConvergenceKeywords(self):
"""
Get keywords from convergence criteria widgets.
:return: A dictionary of keywords
:rtype: dict
"""
keywords = {}
conv = self.ui.convergence_combo.currentData()
if conv != mm.MMJAG_IACCG_CUSTOM or self.SETS_IACCG_CUSTOM:
keywords[mm.MMJAG_IKEY_IACCG] = conv
keywords[mm.MMJAG_RKEY_GCONV2] = None
keywords[mm.MMJAG_RKEY_GCONV6] = None
keywords[mm.MMJAG_RKEY_GCONV7] = None
if conv == mm.MMJAG_IACCG_CUSTOM:
energy_change = gui_utils.validate_le_float_input(
self.ui.energy_le, "Invalid input for energy change field.")
keywords[mm.MMJAG_RKEY_GCONV7] = energy_change
gradient_rms = gui_utils.validate_le_float_input(
self.ui.gradient_rms_le,
"Invalid input for gradient RMS field.")
keywords[mm.MMJAG_RKEY_GCONV2] = gradient_rms
max_disp_rms = gui_utils.validate_le_float_input(
self.ui.max_disp_rms_le,
"Invalid input for maximal displacement ")
keywords[mm.MMJAG_RKEY_GCONV6] = max_disp_rms
return keywords
[docs] def loadSettings(self, jag_input):
"""
Restore scan coordinates settings from Jaguar handle.
:param jag_input: The Jaguar settings to base the tab settings on
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
self._resetDefaults()
max_steps = jag_input[mm.MMJAG_IKEY_MAXITG]
self.ui.max_steps_sb.setValue(max_steps)
self.loadConvergenceKeywords(jag_input)
self.ui.hessian_combo.setCurrentMmJagData(jag_input,
mm.MMJAG_IKEY_INHESS,
"hessian")
self.ui.coordinates_combo.setCurrentMmJagData(jag_input,
mm.MMJAG_IKEY_INTOPT,
"coordinates")
save_geom = jag_input[mm.MMJAG_IKEY_IP472]
save_geom_bool = save_geom == 2
self.ui.save_geom_cb.setChecked(save_geom_bool)
self.loadConstraintCoordinates(jag_input)
try:
nops_opt_switch_val = jag_input[mm.MMJAG_RKEY_NOPS_OPT_SWITCH]
except mm.MmException:
msg = ('Unrecognized %s setting. "Use NOPS optimization switch" '
'will not be modified.' % (mm.MMJAG_RKEY_NOPS_OPT_SWITCH))
warnings.warn(JaguarSettingWarning(msg))
return
# See PANEL-5767 for details on how 1.0 was chosen.
if nops_opt_switch_val > 1.0:
self.ui.use_nops_cb.setChecked(True)
self.nops_opt_switch_value = nops_opt_switch_val
else:
self.ui.use_nops_cb.setChecked(False)
self.nops_opt_switch_value = INITIAL_NOPS_VAL
[docs] def loadConvergenceKeywords(self, jag_input):
"""
Load the convergence criteria settings.
:param jag_input: The Jaguar settings to load.
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
conv = jag_input.getValue(mm.MMJAG_IKEY_IACCG)
if conv != mm.MMJAG_IACCG_CUSTOM or self.SETS_IACCG_CUSTOM:
self.ui.convergence_combo.setCurrentMmJagData(
jag_input, mm.MMJAG_IKEY_IACCG, "convergence")
if self.ui.convergence_combo.currentData() == mm.MMJAG_IACCG_CUSTOM:
# Custom convergence options
energy_change = jag_input[mm.MMJAG_RKEY_GCONV7]
self.ui.energy_le.setText(str(energy_change))
gradient_rms = jag_input[mm.MMJAG_RKEY_GCONV2]
self.ui.gradient_rms_le.setText(str(gradient_rms))
max_disp_rms = jag_input[mm.MMJAG_RKEY_GCONV6]
self.ui.max_disp_rms_le.setText(str(max_disp_rms))
[docs] def loadConstraintCoordinates(self, jag_input):
"""
Load constraint coordinates from Jaguar handle.
:param jag_input: The Jaguar handle where tab settings are saved.
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
# check that there is just single entry in workspace
num_constraint = jag_input.constraintCount()
if num_constraint == 0:
return
try:
st = maestro.get_included_entry()
except RuntimeError as err:
msg = ("There should be exactly one entry included in workspace. "
"Constraints can not be loaded.")
warnings.warn(JaguarSettingWarning(msg))
refresh_markers = False
for constraint in jag_input.constraints():
if constraint:
(coord_type, atoms, value) = constraint
self.addCoordinate(st, atoms, coord_type, value)
refresh_markers = True
if refresh_markers:
self.refreshMarkers.emit()
[docs] def saveSettings(self, jag_input, eid=None):
"""
Save constraint coordinate settings in jaguar handle.
See parent class for argument documentation
"""
for coord in self.model.coords:
jag_input.setConstraint(coord.coordinate_type, coord.atom_indices,
coord.target_value)
[docs] def deleteCurrentRow(self):
"""
This function is called to delete row which is currently
selected from the coordinates table.
"""
selected = self.ui.tableView.selectedIndexes()
if len(selected) == 0:
return
selected_row = selected[0].row()
atoms = self._getAtomsForRow(selected_row)
self._emitCoordinateDeleted(atoms)
self.model.removeRow(selected_row)
self.ui.tableView.clearSelection()
if self.model.rowCount() == 0:
self.ui.delete_btn.setDisabled(True)
self.ui.delete_all_btn.setDisabled(True)
[docs] def deleteAllRows(self):
"""
This function is called to delete all rows from the coordinates table.
"""
self.ui.tableView.clearSelection()
self.model.reset()
self._marker_count.clear()
self.allCoordinatesDeleted.emit()
self.ui.delete_btn.setDisabled(True)
self.ui.delete_all_btn.setDisabled(True)
[docs] def addCoordinate(self, st, atoms, coordinate_type, target_value=None):
"""
Add new coordinate row.
:param st: structure
:type st: `schrodinger.structure.Structure`
:param atoms: atom indices
:type atoms: list
:param coordinate_type: coordinate type
:type coordinate_type: int
:param target_value: target coordinate value
:type target_value: float
"""
err = self._determineIfConstraintsAddable()
if err is not None:
self.error(err)
elif self.model.addCoordinate(st, atoms, coordinate_type, target_value):
self._emitCoordinateAdded(atoms, coordinate_type)
# select row that was just added
last_row = self.model.rowCount() - 1
self.ui.tableView.selectRow(last_row)
self.ui.delete_btn.setEnabled(True)
self.ui.delete_all_btn.setEnabled(True)
[docs] def convergenceTypeChanged(self, index):
"""
This function is called when convergence type is changed. If 'Custom'
is selected extra options frame is shown. Otherwise, it is hidden.
:param index: convergence type combo box index
:type index: int
"""
visible = False
if self.ui.convergence_combo.currentData() == mm.MMJAG_IACCG_CUSTOM:
visible = True
self.ui.custom_frame.setVisible(visible)
[docs] def pickCompleted(self, atoms):
"""
This slot is called when required number of atoms for the current
coordinate type has been picked.
:param atoms: list of atom indices
:type atoms: list
"""
try:
st = maestro.get_included_entry()
except RuntimeError as err:
self.warning(str(err))
return
coord_type = self.ui.coord_type_combo.currentData()
self.addCoordinate(st, atoms, coord_type)
[docs] def updateAllConstraintsPB(self):
"""
This function is called when coordinate type is changed to
update text and visibility of the button that adds all
constraints.
"""
coord_type = self.ui.coord_type_combo.currentData()
visible, text = self.ALL_CONSTRAINTS_BUTTON[coord_type]
self.ui.all_constraints_btn.setVisible(visible)
self.ui.all_constraints_btn.setText(text)
[docs] def getAllAtomPairs(self, st):
"""
This function returns a list of all possible atom pairs in a
given structure.
:param st: structure
:type st: `schrodinger.structure.Structure`
:return: list that contains pairs of atom indices
:rtype: list
"""
atoms = [
[at1.index, at2.index] for at1, at2 in combinations(st.atom, 2)
]
return atoms
[docs] def getAllAngles(self, st):
"""
This function returns list that contains lists of atoms for each
bond angle in a given structure.
:param st: structure
:type st: `schrodinger.structure.Structure`
:return: list that contains lists of atom indices for each bond angle
:rtype: list
"""
atoms = []
for at2 in st.atom:
for at1, at3 in permutations(at2.bonded_atoms, 2):
atoms.append([at1.index, at2.index, at3.index])
return atoms
[docs] def getAllTorsions(self, st):
"""
This function returns list that contains lists of atoms for each
torsion angle in a given structure.
:param st: structure
:type st: `schrodinger.structure.Structure`
:return: list that contains lists of atom indices for each torsion
:rtype: list
"""
atoms = []
for bond in st.bond:
at2 = bond.atom1
at3 = bond.atom2
for at1 in at2.bonded_atoms:
for at4 in at3.bonded_atoms:
if at1 != at3 and at4 != at2:
atoms.append(
[at1.index, at2.index, at3.index, at4.index])
return atoms
[docs]class ConstraintCoordinateColumns():
"""
Constants for table columns.
"""
NAMES = ('Atom Indices', 'Coordinate', 'Type', 'Target Value')
NUM_COLS = len(NAMES)
(INDICES, COORD_NAME, COORD_TYPE, TARGET_VAL) = list(range(NUM_COLS))
[docs]class ConstraintCoordinateData(coordinates.CoordinateData):
"""
This class stores all data for a single constraint coordinate.
:ivar st: ct structure for which coordinates are defined
:vartype st: `schrodinger.structure.Structure`
:ivar atom_indices: indices of atoms, which define this coordinate
:vartype atom_indices: list
:ivar coordinate_name: name of this coordinate based on atom indices
:vartype coordinate_name: str
:ivar coordinate_type: coordinate type
:vartype coordinate_type: int
:ivar target_value: target value of this coordinate
:vartype target_value: float
"""
[docs] def __init__(self, st, atoms, coordinate_type, target_value=None):
"""
Initialize coordinates data given a structure, set of atom indices and
coordinate type.
:param st: structure
:type st: `schrodinger.structure.Structure`
:param atoms: atom indices
:type atoms: list
:param coordinate_type: coordinate type
:type coordinate_type: int
:param target_value: target coordinate value
:type target_value: float
"""
super(ConstraintCoordinateData, self).__init__(st, atoms,
coordinate_type)
self.target_value = target_value
self.coordinate_name = self._getCoordinateName()
[docs]class ConstraintCoordinatesDelegate(QtWidgets.QItemDelegate):
"""
This delegate is used to validate values entered in 'target value'
column.
"""
COLUMN = ConstraintCoordinateColumns()
[docs] def createEditor(self, parent, option, index):
"""
This function returns an editor widget (QLineEdit) for 'target value'
column.
:param parent: parent widget
:type parent: `QtWidgets.QWidget`
:param option: not used, but kept for compatibility
:type option: `QtWidgets.QStyleOptionViewItem`
:param index: model index
:type index: `QtCore.QModelIndex`
:return: QLineEdit editor widget
:rtype: `QtWidgets.QLineEdit`
"""
editor = QtWidgets.QLineEdit(parent)
double_validator = QtGui.QDoubleValidator(editor)
# Here model is a proxy model which is responsible for column
# visibility. So, 1 is the index of coordinate type column.
coord_type_index = index.model().index(index.row(), 1)
coord_type = index.model().data(coord_type_index, Qt.DisplayRole)
if coord_type == DISTANCE_COORD:
double_validator.setBottom(0.0)
editor.setValidator(double_validator)
return editor
[docs] def setEditorData(self, editor, index):
"""
This function read data from model, converts it to text and sets it
in the editor widget.
:param editor: editor widget
:type editor: `QtWidgets.QLineEdit`
:param index: model index
:type index: `QtCore.QModelIndex`
"""
value = index.model().data(index, Qt.EditRole)
s = ""
if value is not None:
s = "%.3f" % value
editor.setText(s)
[docs] def setModelData(self, editor, model, index):
"""
This function reads text from QLineEdit and writes it to the model
:param editor: editor widget
:type editor: `QtWidgets.QLineEdit`
:param model: data model
:type model: `QtCore.QAbstractItemModel`
:param index: model index
:type index: `QtCore.QModelIndex`
"""
try:
value = float(editor.text())
except ValueError:
value = ""
model.setData(index, value)
[docs]class ConstraintCoordinatesProxyModel(QtCore.QSortFilterProxyModel):
"""
A proxy model that allows to hide columns.
"""
COLUMN = ConstraintCoordinateColumns()
[docs] def __init__(self, parent):
super(ConstraintCoordinatesProxyModel, self).__init__(parent)
self.visible_columns = (self.COLUMN.COORD_NAME, self.COLUMN.COORD_TYPE,
self.COLUMN.TARGET_VAL)
# maintain Qt4 dynamicSortFilter default
self.setDynamicSortFilter(False)
[docs] def filterAcceptsColumn(self, column, index):
"""
Modified from the parent class to define columns that
should be visible.
:param column: the column index
:type column: int
:param index: Unused, but kept for PyQt compatibility
:type index: `QModelIndex`
"""
return column in self.visible_columns
[docs]class ConstraintCoordinatesModel(coordinates.CoordinatesModel):
"""
A model to store scan tab coordinates data.
"""
COLUMN = ConstraintCoordinateColumns()
[docs] def addCoordinate(self, st, atoms, coordinate_type, target_value=None):
"""
Add new coordinate row.
:param st: structure
:type st: `schrodinger.structure.Structure`
:param atoms: atom indices
:type atoms: list
:param coordinate_type: coordinate type
:type coordinate_type: int
:param target_value: target coordinate value
:type target_value: float
"""
if self.checkNewCoordinate(atoms, coordinate_type):
new_row_num = len(self.coords)
self.beginInsertRows(QtCore.QModelIndex(), new_row_num, new_row_num)
new_coord = ConstraintCoordinateData(st, atoms, coordinate_type,
target_value)
self.coords.append(new_coord)
self.endInsertRows()
return True
return False
[docs] def appendFromModel(self, model):
"""
Append the rows in the given model to this model
:type: `ConstraintCoordinatesModel`
:param: The model to append rows from
"""
# This method is used by the Jaguar Multistage panel when copying one
# stage into another
for coord in model.coords:
self.addCoordinate(coord.st,
coord.atom_indices,
coord.coordinate_type,
target_value=coord.target_value)
[docs] def flags(self, index):
"""
Returns flags for the specified cell. Whether it is editable or not.
:param index: The index to retrieve flags for.
:type index: `PyQt5.QtCore.QModelIndex`
"""
col = index.column()
if col == self.COLUMN.TARGET_VAL:
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
else:
return Qt.ItemIsSelectable | Qt.ItemIsEnabled
[docs] def data(self, index, role=Qt.DisplayRole):
"""
Retrieve the requested data
:param index: The index to retrieve data for
:type index: `PyQt5.QtCore.QModelIndex`
:param role: The role to retrieve data for
:type role: int
:return: The requested data
"""
if not index.isValid():
return
if index.row() >= len(self.coords):
return
if role == Qt.TextAlignmentRole:
return Qt.AlignLeft
elif role == Qt.DisplayRole or role == Qt.EditRole:
row = index.row()
col = index.column()
coord = self.coords[row]
if col == self.COLUMN.INDICES:
return coord.atom_indices
elif col == self.COLUMN.COORD_NAME:
return coord.coordinate_name
elif col == self.COLUMN.COORD_TYPE:
type_text = gui_utils.find_key_for_value(
COORDINATE_TYPES, coord.coordinate_type)
return type_text
elif col == self.COLUMN.TARGET_VAL:
return coord.target_value
[docs] def setData(self, index, value, role=Qt.EditRole):
"""
Modify coordinate values.
:param index: the index of table cell
:type index: `QtCore.QModelIndex`
:param value: new value
:type value: str
:param role: The role to set data for.
:type role: int
"""
if index.isValid() and role == Qt.EditRole:
row = index.row()
col = index.column()
coord = self.coords[row]
if col != self.COLUMN.TARGET_VAL:
return False
if value == "":
value = None
else:
try:
value = float(value)
except:
value = 0.0
coord.target_value = value
self.dataChanged.emit(index, index)
return True
else:
return super(ConstraintCoordinatesModel,
self).setData(index, value, role)
[docs]class ActiveOptimizationTab(OptimizationTab):
"""
Tab used in place of optimization tab in Transition State Search panel
:cvar activeConstraintsToggled: A signal emitted when the active constraints check
box is toggled
:vartype activeConstraintsToggled: `PyQt5.QtCore.pyqtSignal`
"""
activeConstraintsToggled = QtCore.pyqtSignal(bool)
[docs] def setup(self):
"""Setup UI elements"""
super().setup()
self.ui.active_constraints_chk = QtWidgets.QCheckBox(
"Set constraints as active constraints")
self.ui.constraint_layout.addWidget(self.ui.active_constraints_chk)
self.ui.active_constraints_chk.setEnabled(False)
self.ui.active_constraints_chk.toggled.connect(
self.activeConstraintsToggled.emit)
self.ts_search_method = ActiveTransitionStateTab.SEARCH_STANDARD
[docs] def saveSettings(self, jag_input, eid=None):
"""
Save per-atom settings to Jaguar input.
:param jag_input: A JaguarInput object to save settings to
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
:param eid: the eid to save settings for, always None because
Transition State Search panel uses a MultiStructureTab
:type eid: str or None
"""
if self.ui.active_constraints_chk.isChecked():
for coord in self.model.coords:
jag_input.setActiveCoord(coord.coordinate_type,
coord.atom_indices)
else:
for coord in self.model.coords:
jag_input.setConstraint(coord.coordinate_type,
coord.atom_indices, coord.target_value)
[docs] def loadConstraintCoordinates(self, jag_input):
"""
Load constraint coordinates from Jaguar handle.
:param jag_input: The Jaguar handle where tab settings are saved.
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
super().loadConstraintCoordinates(jag_input)
self.enableActiveChkIfConstraintsAdded()
if jag_input.anyConstraintsActive():
self.ui.active_constraints_chk.setChecked(True)
if not jag_input.allConstraintsActive():
msg = ("Found mix of active and non-active constraints. "
"Setting all constraints to active")
warnings.warn(JaguarSettingWarning(msg))
else:
self.ui.active_constraints_chk.setChecked(False)
[docs] def enableActiveChkIfConstraintsAdded(self):
"""
Enable the active constraints check box if there are constraints
added to the list or disable it if there are not.
"""
if self.ts_search_method == ActiveTransitionStateTab.SEARCH_STANDARD:
self.ui.active_constraints_chk.setEnabled(
bool(self.model.rowCount()))
[docs] def addCoordinate(self, st, atoms, coordinate_type, target_value=None):
"""
Add a coordinate and enable active checkbox if successfully added
"""
super().addCoordinate(st, atoms, coordinate_type, target_value)
self.enableActiveChkIfConstraintsAdded()
[docs] def deleteAllRows(self):
"""
Delete all constraint rows and disable active checkbox
"""
super().deleteAllRows()
self.enableActiveChkIfConstraintsAdded()
[docs] def deleteCurrentRow(self):
"""
Delete current row and disable active checkbox if no more rows remain
"""
super().deleteCurrentRow()
self.enableActiveChkIfConstraintsAdded()
[docs] def onSearchMethodChanged(self, method_id):
"""
Enable or disable active constraints toggle depending on the search
method selection in the Transition State tab.
:param method_id: button id corresponding to a search method selected
in the Transition State tab.
:type method_id: int
"""
self.ts_search_method = method_id
visible = method_id == ActiveTransitionStateTab.SEARCH_STANDARD
self.ui.active_constraints_chk.setVisible(visible)
with suppress_signals(self.ui.active_constraints_chk):
if not visible:
self.ui.active_constraints_chk.setChecked(False)
self.enableActiveChkIfConstraintsAdded()
[docs]class NoCustomIACCGOptimizationTab(OptimizationTab):
"""
Subclass of OptimizationTab that does not set the
iaccg keyword
"""
SETS_IACCG_CUSTOM = False