Source code for schrodinger.application.prime.gui.analyze_energy
from collections import OrderedDict
import schrodinger
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.structutils import analyze
from schrodinger.ui.qt import widgetmixins
from schrodinger.ui.qt.appframework2 import af2
from schrodinger.ui.qt.appframework2 import settings
from .ui import analyze_energy_ui
from .ui import environment_options_ui
maestro = schrodinger.get_maestro()
try:
from schrodinger.application.prime.packages import Prime
# PXerror is imported here so it can be reimported in modules calling
# AnalyzeEnergyWidget.runSingleStruc(), as it will be raised for failed jobs
from schrodinger.application.prime.packages.PXutil import PXerror
except ImportError:
Prime = None
PXerror = None
[docs]class AnalyzeEnergyWidget(widgetmixins.MessageBoxMixin, QtWidgets.QWidget):
SOLVATION = OrderedDict(
(("VSGB", "vsgb"), ("Vacuum", "vac"), ("Chloroform", "chloroform")))
[docs] def __init__(self, parent=None):
super(AnalyzeEnergyWidget, self).__init__(parent)
self.ui = analyze_energy_ui.Ui_Form()
self.ui.setupUi(self)
self.setup()
[docs] def setup(self):
self.options_dialog = AnalyzeEnergyOptions(self)
self.ui.solvation_combo.addItemsFromDict(self.SOLVATION)
self.ui.options_btn.clicked.connect(self.options_dialog.open)
[docs] def reset(self):
self.ui.solvation_combo.setCurrentIndex(0)
self.ui.force_field_selector.update()
self.options_dialog.reset()
def _checkProtPrep(self, struc):
"""
Make sure that the protein contains hydrogens. If it doesn't, tell the
user to run the Protein Preparation Wizard.
:param struc: The structure to check
:type struc: `schrodinger.structure.Structure`
:return: True if the protein contains hydrogens. False otherwise.
:rtype: bool
"""
if analyze.hydrogens_present(struc):
return True
msg = ("Force field check failed. Please make sure the structure has "
"hydrogens and valid bond bond orders and atom charges. Would "
"you like to use the Protein Preparation Wizard to clean up "
"your structure?")
if self.question(msg, title="Error", yes_text="Open PrepWizard"):
maestro.command("pythonrunbuiltin prepwizard_gui.panel")
return False
def _checkMembrane(self, struc):
"""
Make sure that there is membrane data present if the user has specified
the membrane environment.
:param struc: The structure to check
:type struc: `schrodinger.structure.Structure`
:return: False if a membrane environment is selected but the structure
does not contain membrane data. True otherwise.
:rtype: bool
"""
key = self.options_dialog.MEMBRANE_ENVIRON
if self.options_dialog.getAliasedValue(key):
prop_suffixes = ((i, j) for i in (1, 2) for j in "xyz")
prop_names = ("r_psp_Memb%i_%s" % (i, j) for i, j in prop_suffixes)
return all(prop in struc.property for prop in prop_names)
else:
return True
[docs] def validateWorkspace(self):
"""
Make sure that the workspace structure is acceptable input for energy
analysis. If it isn't, display an error dialog explaining why.
:return: True if the structure is acceptable. False otherwise.
:rtype: bool
"""
status = self._workspaceValidation()
if isinstance(status, bool):
return status
elif isinstance(status, str):
self.error(status)
return False
[docs] def sanitizeCustomOPLSDir(self):
"""
Sanitize the forcefield selector's custom OPLS dir choice allowing it to
be reset to not using one if it could not be 'sanitized'.
:return: whether the validated custom OPLS dir should be used.
:rtype: bool
"""
ffs = self.ui.force_field_selector
return ffs.sanitizeCustomOPLSDir(allow_default_dialog=True)
[docs] def getCustomOPLSDir(self):
"""
Return OPLSDIR suitable for passing to jobcontrol through -OPLSDIR
or None if usage of a custom directory is not selected.
:rtype: str
:returns: string of OPLS dir or None custom forcefield not selected
"""
ffs = self.ui.force_field_selector
return ffs.getCustomOPLSDIR()
def _workspaceValidation(self):
"""
Make sure that the workspace structure is acceptable input for energy
analysis
:return: If the structure is acceptable, return True. If the structure
is not acceptable, returns either: An error message to display to
the user or False if nothing needs to be displayed to the user
:rtype: bool or str
"""
try:
struc = maestro.get_included_entry()
except RuntimeError:
return "Only a single entry may be included in the workspace"
if not self._checkProtPrep(struc):
return False
elif not self._checkMembrane(struc):
return ("The workspace structure is missing the required membrane "
"properties.")
else:
return True
[docs] def runSingleStruc(self, struc):
"""
Run energy analysis on the specified structure and load the result into
the project table
:param struc: The structure to analyze
:type struc: `schrodinger.structure.Structure`
:raise `PXerror`: If the energy analysis fails
"""
arg_list = self.getArgList()
prime_out = Prime.run_prime("analyze_energy", "ENERGY", struc, arg_list)
new_struc = prime_out[0]
proj = maestro.project_table_get()
row = proj.importStructure(new_struc, wsreplace=True)
row.title += " - Prime analyzed"
def _getArgs(self):
"""
Get the appropriate Prime arguments
:return: A dictionary of Prime arguments
:rtype: dict
"""
dlg = self.options_dialog
dlg_settings = dlg.getAliasedSettings()
yesno = lambda x: "yes" if x else "no"
dielectric = dlg_settings[dlg.DIELECTRIC]
crystal = dlg_settings[dlg.CRYSTAL_ENVIRON]
membrane = dlg_settings[dlg.MEMBRANE_ENVIRON]
solvation = self.ui.solvation_combo.currentData()
ff = self.ui.force_field_selector.getSelectionForceField()
ff = ff.replace("_", "")
args = {
"USE_CRYSTAL_SYMMETRY": yesno(crystal),
"USE_RANDOM_SEED": "no",
"OPLS_VERSION": ff,
"EXT_DIEL": str(dielectric),
"USE_MEMBRANE": yesno(membrane),
"HOST": "localhost"
}
if solvation != "vsgb":
args["SGB_MOD"] = solvation
return args
[docs] def getArgList(self):
"""
:return: the list of arguments for a `Prime.run_prime` call
"""
args = self._getArgs()
arg_list = []
list(map(arg_list.extend, args.items()))
return arg_list
[docs]class AnalyzeEnergyOptions(settings.BaseOptionsPanel):
DEFAULT_ENVIRON = "default_environ"
MEMBRANE_ENVIRON = "membrane_environ"
CRYSTAL_ENVIRON = "crystal_environ"
DIELECTRIC = "dielectric"
[docs] def setPanelOptions(self):
super(AnalyzeEnergyOptions, self).setPanelOptions()
self.title = "Analyze Energy - Options"
self.ui = environment_options_ui.Ui_Form()
self.help_topic = 'PRIME_ANALYZE_ENERGY_OPTIONS'
[docs] def setup(self):
super(AnalyzeEnergyOptions, self).setup()
self.ui.set_up_membrane_btn.clicked.connect(self.setUpMembrane)
self.ui.dielectric_le.setValidator(QtGui.QDoubleValidator(self))
self.setAlias(self.DEFAULT_ENVIRON, self.ui.default_rb)
self.setAlias(self.MEMBRANE_ENVIRON, self.ui.membrane_rb)
self.setAlias(self.CRYSTAL_ENVIRON, self.ui.unit_cell_rb)
self.setAlias(self.DIELECTRIC, self.ui.dielectric_le)
@af2.validator()
def checkDielectric(self):
"""
Make sure that the dielectric line edit has a valid value
"""
if self.ui.dielectric_le.hasAcceptableInput():
return True
else:
return (False, "Invalid dielectric value.")
[docs] def setUpMembrane(self):
"""
Launch the Membrane GUI panel in response to the user clicking the "Set
Up Membrane" button
"""
maestro.command("pythonrunbuiltin membrane_gui.panel")