import warnings
from collections import OrderedDict
from schrodinger.infra import mm
from schrodinger.Qt import QtWidgets
from .. import ui
from ..utils import JaguarSettingWarning
from .base_tab import BaseTab
[docs]def different(value1, value2):
"""
Return True if the given float values are different
from each other (tiny differences are not considered).
"""
return abs(value1 - value2) > 1e-10
[docs]class SolventTab(BaseTab):
"""
A base class for the Solvent tab (used in the pKa panel) and the standard
Solvation tab.
"""
NAME = "Solvent"
HELP_TOPIC = "JAGUAR_TOPIC_SOLVATION_FOLDER"
def _warnAboutOtherSolventTypes(self, jag_input):
"""
Warn if any keywords for the "other" solvent type are set, since they
will be ignored.
:param jag_input: The Jaguar settings to check
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
NON_ALLOWED_KEYS = (mm.MMJAG_RKEY_EPSOUT, mm.MMJAG_RKEY_RADPRB)
bad_keys = [
key for key in NON_ALLOWED_KEYS if jag_input.isNonDefault(key)
]
if bad_keys and jag_input[mm.MMJAG_SKEY_SOLVENT] != "other":
plural_s = "s" if len(bad_keys) > 1 else ""
msg = (
"Custom solvent keyword%s %s specified but solvent is not set "
"to other (solvent != other). These keywords will be ignored." %
(plural_s, " and ".join(bad_keys)))
warnings.warn(JaguarSettingWarning(msg))
[docs]class SolventPkaTab(SolventTab):
"""
The Solvent tab for the Pka panel.
"""
SOLVENTS = {"Water": mm.MMJAG_SOLVENT_WATER, "DMSO": mm.MMJAG_SOLVENT_DMSO}
UI_MODULES = (ui.solvent_pka_tab_ui,)
[docs] def setup(self):
super().setup()
self.ui.solvent_combo.addItemsFromDict(self.SOLVENTS)
[docs] def getMmJagKeywords(self):
keywords = super().getMmJagKeywords()
solv_type = self.ui.solvent_combo.currentData()
keywords[mm.MMJAG_SKEY_SOLVENT] = solv_type
ipka_solv_opt = mm.MMJAG_IPKA_SOLV_OPT_OFF
if self.ui.use_solvation_cb.isChecked():
ipka_solv_opt = mm.MMJAG_IPKA_SOLV_OPT_ON
keywords[mm.MMJAG_IKEY_IPKA_SOLV_OPT] = ipka_solv_opt
# Explicitly clear any dielectric and probe radius settings, since
# they're incompatible with the solvents selectable in this tab
keywords[mm.MMJAG_RKEY_EPSOUT] = None
keywords[mm.MMJAG_RKEY_RADPRB] = None
return keywords
[docs] def loadSettings(self, jag_input):
solv_type = jag_input[mm.MMJAG_SKEY_SOLVENT]
solv_type = solv_type.replace('_', ' ')
try:
self.ui.solvent_combo.setCurrentData(solv_type)
except ValueError:
msg = "Unrecognized solvent type %s=%s" % (mm.MMJAG_SKEY_SOLVENT,
solv_type)
warnings.warn(JaguarSettingWarning(msg))
self._warnAboutOtherSolventTypes(jag_input)
ipka_solv_opt = jag_input[mm.MMJAG_IKEY_IPKA_SOLV_OPT]
ipka_solv_opt_bool = ipka_solv_opt == mm.MMJAG_IPKA_SOLV_OPT_ON
self.ui.use_solvation_cb.setChecked(ipka_solv_opt_bool)
[docs]class SolvationTab(SolventTab):
"""
The Solvation tab, which includes solvent model and gas-phase reference
energy
"""
NAME = "Solvation"
UI_MODULES = (ui.solvent_tab_ui,)
NO_SOLVENT_MODEL = "None"
PBF_MODEL = "PBF"
PCM_MODEL = "PCM"
SM6_MODEL = "SM6"
SM8_MODEL = "SM8"
# SM6 and SM8 models are not available yet, so we will only show
# the first two items at this time. NOTE - when they are implemented,
# remove their special handling in the SolvationTabNoOptimized class.
SOLVENT_MODELS = OrderedDict([
(NO_SOLVENT_MODEL, mm.MMJAG_ISOLV_OFF),
(PBF_MODEL, mm.MMJAG_ISOLV_PBF),
(PCM_MODEL, mm.MMJAG_ISOLV_PCM),
#(SM6_MODEL, mm.MMJAG_ISOLV_SM6),
#(SM8_MODEL, mm.MMJAG_ISOLV_SM8),
])
CPCM_PCM_MODEL = "CPCM"
COSMO_PCM_MODEL = "COSMO"
SSVPE_PCM_MODEL = "SS(v)PE"
PCM_MODELS = OrderedDict([(CPCM_PCM_MODEL, mm.MMJAG_PCM_MODEL_CPCM),
(COSMO_PCM_MODEL, mm.MMJAG_PCM_MODEL_COSMO),
(SSVPE_PCM_MODEL, mm.MMJAG_PCM_MODEL_SSVPE)])
BONDI_PCM_RADII = "Bondi"
UFF_PCM_RADII = "UFF"
KLAMT_PCM_RADII = "Klamt"
PCM_RADII = OrderedDict([(BONDI_PCM_RADII, mm.MMJAG_PCM_RADII_BONDI),
(UFF_PCM_RADII, mm.MMJAG_PCM_RADII_UFF),
(KLAMT_PCM_RADII, mm.MMJAG_PCM_RADII_KLAMT)])
[docs] def setup(self):
super().setup()
self.ui.solvent_model_combo.addItemsFromDict(self.SOLVENT_MODELS)
self.ui.solvent_model_combo.currentIndexChanged.connect(
self.solventModelChanged)
self.ui.pcm_model_combo.addItemsFromDict(self.PCM_MODELS)
self.ui.pcm_radii_combo.addItemsFromDict(self.PCM_RADII)
self.gas_phase_group = QtWidgets.QButtonGroup(self)
self.gas_phase_group.addButton(self.ui.optimized_structure_rb)
self.gas_phase_group.addButton(self.ui.input_structure_rb)
self.ui.solvent_chooser_btn.solventChanged.connect(
self.ui.solvent_le.setText)
self.solventModelChanged()
[docs] def solventModelChanged(self):
"""
Called when a new item is selected in the solvent model type menu.
"""
isolv = self.ui.solvent_model_combo.currentText()
enable = isolv in (self.PBF_MODEL, self.PCM_MODEL, self.SM8_MODEL)
self.ui.solvent_lbl.setEnabled(enable)
self.ui.solvent_le.setEnabled(enable)
self.ui.solvent_chooser_btn.setEnabled(enable)
if not enable:
self.ui.solvent_chooser_btn.setSolvent(mm.MMJAG_SOLVENT_WATER)
visible = (isolv == self.PCM_MODEL)
self.ui.pcm_frame.setVisible(visible)
visible = isolv in (self.PBF_MODEL, self.PCM_MODEL)
self.ui.gas_phase_frame.setVisible(visible)
[docs] def getMmJagKeywords(self):
keywords = {}
keywords.update(self._getSolventModelAndTypeKeyword())
keywords.update(self._getPCMKeywords())
keywords.update(self._getGasPhaseKeywords())
return keywords
def _getSolventModelAndTypeKeyword(self):
"""
Get the solvent model keyword
:return: A dictionary of keywords
:rtype: dict
"""
isolv = self.ui.solvent_model_combo.currentData()
solvent_type = self.ui.solvent_chooser_btn.getSolvent()
solvent_type = solvent_type.replace(' ', '_')
return {mm.MMJAG_IKEY_ISOLV: isolv, mm.MMJAG_SKEY_SOLVENT: solvent_type}
def _getPCMKeywords(self):
"""
Get PCM keywords when applicable.
:return: A dictionary of keywords
:rtype: dict
"""
keywords = {}
isolv = self.ui.solvent_model_combo.currentText()
if isolv != self.PCM_MODEL:
keywords[mm.MMJAG_SKEY_PCM_MODEL] = None
keywords[mm.MMJAG_SKEY_PCM_RADII] = None
keywords[mm.MMJAG_IKEY_PBF_AFTER_PCM] = None
return keywords
pcm_model = self.ui.pcm_model_combo.currentData()
keywords[mm.MMJAG_SKEY_PCM_MODEL] = pcm_model
pcm_radii = self.ui.pcm_radii_combo.currentData()
keywords[mm.MMJAG_SKEY_PCM_RADII] = pcm_radii
pbf_after_pcm = mm.MMJAG_PBF_AFTER_PCM_OFF
if self.ui.pbf_optimization_cb.isChecked():
pbf_after_pcm = mm.MMJAG_PBF_AFTER_PCM_ON
keywords[mm.MMJAG_IKEY_PBF_AFTER_PCM] = pbf_after_pcm
return keywords
def _getGasPhaseKeywords(self):
"""
Get the gas phase reference energy keywords
:return: A dictionary of keywords
:rtype: dict
"""
keywords = {}
isolv = self.ui.solvent_model_combo.currentText()
if isolv not in (self.PBF_MODEL, self.PCM_MODEL):
keywords[mm.MMJAG_IKEY_NOGAS] = None
return keywords
if self.ui.optimized_structure_rb.isChecked():
nogas = mm.MMJAG_NOGAS_OFF
elif self.ui.input_structure_rb.isChecked():
nogas = mm.MMJAG_NOGAS_ZMAT
keywords[mm.MMJAG_IKEY_NOGAS] = nogas
return keywords
[docs] def loadSettings(self, jag_input):
self._checkSolventSettings(jag_input)
self._warnAboutOtherSolventTypes(jag_input)
self._loadSolventModelAndTypeSettings(jag_input)
self._loadPCMSettings(jag_input)
self._loadGasPhaseSettings(jag_input)
def _checkSolventSettings(self, jag_input):
"""
Make sure that the solvent settings are consistent. Issue warnings for
any inconsistencies.
:param jag_input: The Jaguar settings to check
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
SETTING_KEYS = (mm.MMJAG_RKEY_EPSOUT, mm.MMJAG_RKEY_RADPRB)
SOLVENT_KEYS = ((mm.MMJAG_SKEY_SOLVENT, mm.MMJAG_IKEY_NOGAS) +
SETTING_KEYS)
if jag_input[mm.MMJAG_IKEY_ISOLV] not in (mm.MMJAG_ISOLV_PBF,
mm.MMJAG_ISOLV_PCM,
mm.MMJAG_ISOLV_SM8):
bad_keys = [
key for key in SOLVENT_KEYS if jag_input.isNonDefault(key)
]
if bad_keys:
msg = ("Solvent settings specified but solvation not set to "
"PBF, PCM, or SM8 (%s != %i, %i, or %i). The following "
"keys will be ignored: %s." %
(mm.MMJAG_IKEY_ISOLV, mm.MMJAG_ISOLV_PBF,
mm.MMJAG_ISOLV_PCM, mm.MMJAG_ISOLV_SM8,
", ".join(bad_keys)))
warnings.warn(JaguarSettingWarning(msg))
return
if jag_input[mm.MMJAG_SKEY_SOLVENT] != "other":
bad_keys = [
key for key in SETTING_KEYS if jag_input.isNonDefault(key)
]
if bad_keys:
msg = ("Solvent settings specified but solvent not set to "
"other (%s != other). The following keys will be "
"ignored: %s." %
(mm.MMJAG_SKEY_SOLVENT, ", ".join(bad_keys)))
warnings.warn(JaguarSettingWarning(msg))
def _loadSolventModelAndTypeSettings(self, jag_input):
"""
Load the solvent model and solvent type settings. If solvent model is
not PBF, PCM, or SM8, then a solvent type of water is used regardless of
the `jag_input` setting.
:param jag_input: The Jaguar settings to load
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
self.ui.solvent_model_combo.setCurrentMmJagData(jag_input,
mm.MMJAG_IKEY_ISOLV,
"solvent model")
if jag_input[mm.MMJAG_IKEY_ISOLV] in (mm.MMJAG_ISOLV_PBF,
mm.MMJAG_ISOLV_PCM,
mm.MMJAG_ISOLV_SM8):
solvent = jag_input[mm.MMJAG_SKEY_SOLVENT]
solvent = solvent.replace('_', ' ')
try:
self.ui.solvent_chooser_btn.setSolvent(solvent)
except ValueError:
msg = ("Unrecognized solvent type "
f"{mm.MMJAG_SKEY_SOLVENT}={solvent}")
warnings.warn(JaguarSettingWarning(msg))
else:
self.ui.solvent_chooser_btn.setSolvent(mm.MMJAG_SOLVENT_WATER)
def _loadPCMSettings(self, jag_input):
"""
Load PCM settings.
"""
self.ui.pcm_model_combo.setCurrentMmJagData(jag_input,
mm.MMJAG_SKEY_PCM_MODEL,
"pcm model")
self.ui.pcm_radii_combo.setCurrentMmJagData(jag_input,
mm.MMJAG_SKEY_PCM_RADII,
"pcm radii")
pbf_after_pcm = jag_input[mm.MMJAG_IKEY_PBF_AFTER_PCM]
self.ui.pbf_optimization_cb.setChecked(
pbf_after_pcm == mm.MMJAG_PBF_AFTER_PCM_ON)
def _loadGasPhaseSettings(self, jag_input):
"""
Load the gas phase reference energy settings
:param jag_input: The Jaguar settings to load
:type jag_input: `schrodinger.application.jaguar.input.JaguarInput`
"""
if (jag_input[mm.MMJAG_IKEY_ISOLV] != mm.MMJAG_ISOLV_PBF):
return
nogas = jag_input[mm.MMJAG_IKEY_NOGAS]
# NOGAS_DEFAULT == -1, and NOGAS_OFF == 0, but the default behavior is
# NOGAS_OFF.
if nogas == mm.MMJAG_NOGAS_OFF or nogas == mm.MMJAG_NOGAS_DEFAULT:
self.ui.optimized_structure_rb.setChecked(True)
elif nogas == mm.MMJAG_NOGAS_ZMAT:
self.ui.input_structure_rb.setChecked(True)
else:
msg = ("Gas-phase reference energy setting not recognized (%s=%f)" %
(mm.MMJAG_IKEY_NOGAS, nogas))
warnings.warn(JaguarSettingWarning(msg))
[docs]class SolvationTabNoOptimized(SolvationTab):
"""
A solvation tab that doesn't allow "Optimized gas-phase structure".
"""
[docs] def setup(self):
super(SolvationTabNoOptimized, self).setup()
self.ui.optimized_structure_rb.setText("Input gas-phase structure")
self.ui.input_structure_rb.hide()
self.ui.pbf_optimization_cb.hide()
# SM6 and SM8 models are only available for jobs without
# optimization (SPE & Rigid CS), so add them here.
self.ui.solvent_model_combo.addItem(self.SM6_MODEL, mm.MMJAG_ISOLV_SM6)
self.ui.solvent_model_combo.addItem(self.SM8_MODEL, mm.MMJAG_ISOLV_SM8)
def _loadGasPhaseSettings(self, jag_input):
if (jag_input[mm.MMJAG_IKEY_ISOLV] == mm.MMJAG_ISOLV_PBF and
jag_input[mm.MMJAG_IKEY_NOGAS] == mm.MMJAG_NOGAS_ZMAT):
msg = ("%s=%i not allowed for single point calculations." %
(mm.MMJAG_IKEY_NOGAS, jag_input[mm.MMJAG_IKEY_NOGAS]))
warnings.warn(JaguarSettingWarning(msg))
else:
super(SolvationTabNoOptimized,
self)._loadGasPhaseSettings(jag_input)