Source code for schrodinger.application.matsci.cgforcefieldgui

"""
GUI items for use with coarse grain force fields

Copyright Schrodinger, LLC. All rights reserved.
"""

from collections import namedtuple

from schrodinger.application.matsci import cgforcefield as cgff
from schrodinger.application.matsci import coarsegrain
from schrodinger.application.matsci import parserutils
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.ui.qt import swidgets

INSTALLED_FF_LOCATION_TEXT = 'Installation'
LOCAL_FF_LOCATION_TEXT = 'Local'
FF_LOCATION_TEXTS = [INSTALLED_FF_LOCATION_TEXT, LOCAL_FF_LOCATION_TEXT]
FF_LOCATION_TEXT_TO_TYPE_DICT = dict(
    list(zip(FF_LOCATION_TEXTS, parserutils.CG_FF_LOCATION_TYPES)))
FF_LOCATION_TYPE_TO_TEXT_DICT = dict(
    list(zip(parserutils.CG_FF_LOCATION_TYPES, FF_LOCATION_TEXTS)))

# define LABEL for UNITS
MASS_LABEL = 'Mass/(g mol%s)' % swidgets.SUPER_MINUS1
FC_LABEL = lambda x, y: '%s/(kcal mol%s%s)' % (x, swidgets.SUPER_MINUS1, y)
EA_LABEL = lambda x: '%seq%s/%s' % (swidgets.GL_theta, x, swidgets.DEGREES)
ANG_INV2 = ' %s%s' % (swidgets.ANGSTROM, swidgets.SUPER_MINUS2)
RAD_INV2 = ' rad%s' % swidgets.SUPER_MINUS2

# parameter data, averageable is whether or not an average value of
# this parameter can be obtained from a structure and database_parameter_key
# is the key to use for indexing parameter data for this parameter in the
# database, using values specifies that the parameter can only take on
# specific string values
ParameterData = namedtuple('ParameterData', [
    'label', 'minimum', 'maximum', 'default', 'step_size', 'averageable',
    'database_parameter_key', 'tip', 'decimals', 'values', 'builder_opts'
])
# yapf: disable
SITE_MASS_DATA = ParameterData(
    MASS_LABEL, 1.00, 1000.00, coarsegrain.DEFAULT_MASS, 1.00, True,
    cgff.SITE_MASS_KEY, None, 2, None, None)
SITE_MARTINI_MASS_DATA = ParameterData(
    MASS_LABEL, 1.00, 1000.00, coarsegrain.DEFAULT_MARTINI_MASS, 1.00, True,
    cgff.SITE_MASS_KEY, None, 2, None, None)
# yapf: enable
SITE_CHARGE_DATA = ParameterData(
    'Charge', -10.00, 10.00, 0.00, 1.00, True, cgff.SITE_CHARGE_KEY,
    'Specify the charge for the site.  Note that if a %s potential is being\n'
    'used in the Nonbond tab that the charges are required to be zero.' %
    (coarsegrain.DISSIPATIVE_PARTICLE), 2, None, None)
# yapf: disable
SITE_MARTINI_SUBTYPE_DATA = ParameterData('Martini Type', None, None,
    coarsegrain.DEFAULT_MARTINI_SUBTYPE, None, False, cgff.SITE_MARTINI_SUBTYPE_KEY,
    'Specify the Martini type.  Martini types are designated using a "Q" for a\n'
    'charged type, a "P" for a polar type, an "N" for a nonpolar type, and a\n'
    '"C" for an apolar type.  Both "Q" and "N" types are followed by a "da"\n'
    'meaning hydrogen bond donor and acceptor, a "d" meaning donor, an "a"\n'
    'meaning acceptor, or a "0" meaning neither.  Both "P" and "C" types are\n'
    'followed by a number [1, 5] indicating increasing degrees of polarity.\n'
    'Martini types beginning with an "S" are for ring types and the type "BP4" is\n'
    'an antifreeze type.  The "CNP" type is a carbon nanoparticle type which\n'
    'uses the parameterization given in Monticelli, JCTC 2012, 8, 1370.\n'
    'The "Custom" type is meant to be used when the user is not sure what\n'
    'type should be assigned.  Among other things Martini types signify that\n'
    'a shifted Lennard-Jones nonbond potential should use specific epsilon\n'
    'and sigma values depending on the pairing Martini type.  More about\n'
    'Martini types can be found in the Martini 2.0 publication, Marrink,\n'
    'et. al., JPCB 111 7812 2007, and/or the Martini homepage,\n'
    'http://cgmartini.nl/.', None, coarsegrain.MARTINI_SUBTYPES, None)
# yapf: enable
#max bond length must be 99 Angstrom (MATSCI-3414)
BOND_EQ_LENGTH_DATA = ParameterData(u'Req/%s' % swidgets.ANGSTROM, 0.00, 99.00,
                                    4.00, 1.00, True, cgff.BOND_EQ_LENGTH_KEY,
                                    None, 2, None, coarsegrain.BOND_LENGTH)
#force constant must have three decimals (MATSCI-3402)
BOND_FORCE_CONSTANT_DATA = ParameterData(FC_LABEL('k', ANG_INV2), 0.001,
                                         1000.00, 20.00, 10.00, False,
                                         cgff.BOND_FORCE_CONSTANT_KEY, None, 4,
                                         None, coarsegrain.FORCE_CONSTANT)

ANGLE_EQ_ANGLE_DATA = ParameterData(EA_LABEL(''), 0.01, 180.00, 150.00, 10.00,
                                    True, cgff.ANGLE_EQ_ANGLE_KEY, None, 2,
                                    None, coarsegrain.MEAN_ANGLE)
ANGLE_HARMONIC_FORCE_CONSTANT_DATA = ParameterData(
    FC_LABEL('k', RAD_INV2), 0.01, 500.00, 5.00, 5.00, False,
    cgff.ANGLE_HARMONIC_FORCE_CONSTANT_KEY, None, 3, None,
    coarsegrain.FORCE_CONSTANT)
# yapf: disable
ANGLE_TRIGONOMETRIC_FORCE_CONSTANT_DATA = ParameterData(
    FC_LABEL('k', ''), 0.010, 500.000, 5.975, 5.000, False,
    cgff.ANGLE_TRIGONOMETRIC_FORCE_CONSTANT_KEY, None, 3, None, coarsegrain.FORCE_CONSTANT)
DIHEDRAL_V1_DATA = ParameterData(
    FC_LABEL('V%s' % swidgets.SUB_ONE, ''), -50.00, 50.00, 0.00, 1.00, False,
    cgff.DIHEDRAL_V1_KEY, None, 3, None, None)
DIHEDRAL_V2_DATA = ParameterData(
    FC_LABEL('V%s' % swidgets.SUB_TWO, ''), -50.00, 50.00, 0.00, 1.00, False,
    cgff.DIHEDRAL_V2_KEY, None, 3, None, None)
DIHEDRAL_V3_DATA = ParameterData(
    FC_LABEL('V%s' % swidgets.SUB_THREE, ''), -50.00, 50.00, 0.00, 1.00, False,
    cgff.DIHEDRAL_V3_KEY, None, 3, None, None)
DIHEDRAL_V4_DATA = ParameterData(
    FC_LABEL('V%s' % swidgets.SUB_FOUR, ''), -50.00, 50.00, 0.00, 1.00, False,
    cgff.DIHEDRAL_V4_KEY, None, 3, None, None)
# yapf: enable
DIHEDRAL_EQ_ANGLE_1_DATA = ParameterData(EA_LABEL('%s' % swidgets.SUB_ONE),
                                         -180.00, 180.00, 0.00, 10.00, True,
                                         cgff.DIHEDRAL_EQ_ANGLE_1_KEY, None, 2,
                                         None, None)
# yapf: disable
DIHEDRAL_FORCE_CONSTANT_1_DATA = ParameterData(
    FC_LABEL('k%s' %swidgets.SUB_ONE, ''), 0.010, 500.000, 2.390, 5.000, False,
    cgff.DIHEDRAL_FORCE_CONSTANT_1_KEY, None, 3, None, None)
# yapf: enable
DIHEDRAL_MULT_1_DATA = ParameterData(u'multiplicity n%s' % swidgets.SUB_ONE, 1,
                                     6, 1, 1, False, cgff.DIHEDRAL_MULT_1_KEY,
                                     None, None, None, None)
DIHEDRAL_EQ_ANGLE_2_DATA = ParameterData(EA_LABEL('%s' % swidgets.SUB_TWO),
                                         -180.00, 180.00, 0.00, 10.00, False,
                                         cgff.DIHEDRAL_EQ_ANGLE_2_KEY, None, 2,
                                         None, None)
DIHEDRAL_FORCE_CONSTANT_2_DATA = ParameterData(
    FC_LABEL('k%s' % swidgets.SUB_TWO, ''), 0.00, 500.00, 0.00, 5.00, False,
    cgff.DIHEDRAL_FORCE_CONSTANT_2_KEY, None, 3, None, None)
DIHEDRAL_MULT_2_DATA = ParameterData(u'multiplicity n%s' % swidgets.SUB_TWO, 1,
                                     6, 1, 1, False, cgff.DIHEDRAL_MULT_2_KEY,
                                     None, None, None, None)
DIHEDRAL_EQ_ANGLE_3_DATA = ParameterData(EA_LABEL('%s' % swidgets.SUB_THREE),
                                         -180.00, 180.00, 0.00, 10.00, False,
                                         cgff.DIHEDRAL_EQ_ANGLE_3_KEY, None, 2,
                                         None, None)
DIHEDRAL_FORCE_CONSTANT_3_DATA = ParameterData(
    FC_LABEL('k%s' % swidgets.SUB_THREE, ''), 0.00, 500.00, 0.00, 5.00, False,
    cgff.DIHEDRAL_FORCE_CONSTANT_3_KEY, None, 3, None, None)
DIHEDRAL_MULT_3_DATA = ParameterData(u'multiplicity n%s' % swidgets.SUB_THREE,
                                     1, 6, 1, 1, False,
                                     cgff.DIHEDRAL_MULT_3_KEY, None, None, None,
                                     None)
DIHEDRAL_EQ_ANGLE_4_DATA = ParameterData(EA_LABEL('%s' % swidgets.SUB_FOUR),
                                         -180.00, 180.00, 0.00, 10.00, False,
                                         cgff.DIHEDRAL_EQ_ANGLE_4_KEY, None, 2,
                                         None, None)
DIHEDRAL_FORCE_CONSTANT_4_DATA = ParameterData(
    FC_LABEL('k%s' % swidgets.SUB_FOUR, ''), 0.00, 500.00, 0.00, 5.00, False,
    cgff.DIHEDRAL_FORCE_CONSTANT_4_KEY, None, 3, None, None)
DIHEDRAL_MULT_4_DATA = ParameterData(u'multiplicity n%s' % swidgets.SUB_FOUR, 1,
                                     6, 1, 1, False, cgff.DIHEDRAL_MULT_4_KEY,
                                     None, None, None, None)
IMPROPER_EQ_ANGLE_DATA = ParameterData(EA_LABEL(''), -180.00, 180.00, 0.00,
                                       10.00, True, cgff.IMPROPER_EQ_ANGLE_KEY,
                                       None, 2, None, None)
# yapf: disable
IMPROPER_FORCE_CONSTANT_DATA = ParameterData(
    FC_LABEL('k', RAD_INV2), 0.010, 500.000, 23.901, 5.000, False,
    cgff.IMPROPER_FORCE_CONSTANT_KEY, None, 3, None, None)
# yapf: enable
VDW_M_PARAMETER_DATA = ParameterData('m exponent', 2, 12, 12, 1, False,
                                     cgff.VDW_M_PARAMETER_KEY, None, None, None,
                                     None)
VDW_N_PARAMETER_DATA = ParameterData('n exponent', 1, 11, 6, 1, False,
                                     cgff.VDW_N_PARAMETER_KEY, None, None, None,
                                     None)
VDW_EPSILON_DATA = ParameterData(FC_LABEL(swidgets.GL_epsilon,
                                          ''), 0.050, 10.000,
                                 coarsegrain.DEFAULT_EPSILON, 0.050, False,
                                 cgff.VDW_EPSILON_KEY, None, 3, None, None)
VDW_SIGMA_DATA = ParameterData(
    u'%s/%s' % (swidgets.GL_sigma, swidgets.ANGSTROM), 2.00, 20.00, 4.50, 0.10,
    False, cgff.VDW_SIGMA_KEY, None, 2, None, None)
# yapf: disable
VDW_A_PARAMETER_DATA = ParameterData(
    FC_LABEL('a', ANG_INV2), 0.0100, 1000.0000, coarsegrain.DEFAULT_A_PARAMETER,
    0.0500, False, cgff.VDW_A_PARAMETER_KEY, None, 4, None, coarsegrain.FORCE_CONSTANT)
VDW_SHIFTED_LJ_SIGMA_DATA = ParameterData(
    u'%s/%s' % (swidgets.GL_sigma, swidgets.ANGSTROM), None, None,
    str(coarsegrain.MARTINI_1_SIGMA), None, False, cgff.VDW_SIGMA_KEY,
    None, None, [str(i) for i in coarsegrain.MARTINI_SIGMAS], None)
# yapf: enable


[docs]class CGForceFieldSelector(swidgets.SFrame): """ A set of widgets to allow selecting the coarse-grained force field """ # Emitted when the force field selection changes forceFieldChanged = QtCore.pyqtSignal(str)
[docs] def __init__(self, label='Force field:', description=True, stretch=True, layout_type=swidgets.HORIZONTAL, default_ff_location_type=parserutils.LOCAL_CG_FF_LOCATION_TYPE, allow_enc=False, **kwargs): """ Create a CGForceFieldSelector instance The force field picker combobox is automatically populated based on the force field files available. :type description: bool :param description: If True, include a button that pops up a dialog with the decscription for the chosen force field :type stretch: bool :param stretch: Whether to add a stretch in the layout after this widget :type layout_type: int :param layout_type: Whether the widgets lay out horizontally or vertically. Should be swidgets.HORIZONTAL or swidgets.VERTICAL :type default_ff_location_type: str :param default_ff_location_type: specifies the default location to which to look for force field files, one of parserutils.INSTALLED_CG_FF_LOCATION_TYPE or parserutils.LOCAL_CG_FF_LOCATION_TYPE :type allow_enc: bool :param allow_enc: whether to allow encrypted force field files All other keyword args, including layout, are passed to the SFrame parent class """ swidgets.SFrame.__init__(self, layout_type=layout_type, **kwargs) self.allow_enc = allow_enc # Force field picker self.combo = swidgets.SLabeledComboBox( label, layout=self.mylayout, command=self.forceFieldChanged.emit, nocall=True, stretch=False) self.combo.setSizeAdjustPolicy(self.combo.AdjustToContents) # Show description self.description_button = swidgets.SPushButton( 'Description...', layout=self.mylayout, command=self.showDescription) if not description: self.description_button.hide() # Location swidgets.SLabel('Location:', layout=self.mylayout) default_index = parserutils.CG_FF_LOCATION_TYPES.index( default_ff_location_type) installed_tip = ('Select from force field files located in a\n' 'standard directory of the Schrodinger installation.') local_tip = ('Select from force field files located in the\n' 'local directory: %s.') % cgff.FF_PARAMETERS_LOCAL_PATH item_tips = [installed_tip, local_tip] self.location_rbg = swidgets.SRadioButtonGroup( labels=FF_LOCATION_TEXTS, layout=self.mylayout, default_index=default_index, tips=item_tips, command=self.loadFFCombo, nocall=True) self.loadFFCombo() if stretch: self.mylayout.addStretch()
[docs] def numForceFields(self): """ Get the number of currently allowed force fields :rtype: int :return: The number of force fields loaded into the combobox """ return self.combo.count()
[docs] def showDescription(self): """ Pop up a dialog with the description field from the force field """ path = self.getSelectedPath() data = cgff.load_force_field_parameters(path) description = data.get(cgff.DESCRIPTION_INTERNAL_KEY) if not description: description = 'No description available' self.info('Description', description)
[docs] def info(self, caption, msg): """ Pop up an informational dialog with the given message :type caption: str :param caption: The title of the dialog :type msg: str :param msg: The message to display """ QtWidgets.QMessageBox.information(self, caption, msg)
[docs] def getSelectedName(self): """ Get the currently-selected force field name :rtype: str :return: The name of the selected force field """ return self.combo.currentText()
[docs] def getSelectedPath(self): """ Get the path to the currently-selected force field file :rtype: str :return: The path to the selected force field file """ return self.combo.currentData()
[docs] def getSelectedLocationType(self): """ Get the currently-selected location type. :rtype: str :return: the location type """ location_text = self.location_rbg.checkedText() return FF_LOCATION_TEXT_TO_TYPE_DICT[location_text]
[docs] def reset(self): """ Reset all widgets and reload the available force fields """ self.location_rbg.reset() self.loadFFCombo()
[docs] def loadFFCombo(self): """ Find all available force fields and load them into the combobox """ location_type = self.getSelectedLocationType() allffdata = cgff.get_all_force_field_paths(allow_enc=self.allow_enc) ffdata = allffdata.get(location_type, {}) self.combo.clear() self.combo.addItemsFromDict(ffdata) self.description_button.setEnabled(bool(ffdata)) self.forceFieldChanged.emit(self.getSelectedName())
[docs] def getCommandLineFlags(self): """ Gets command line flags for the coarse-grain forcefield file. :rtype: list(str) :return: The command line flags needed to specify the coarse-grain forcefield """ cmd = [] cmd += [parserutils.FLAG_CGFFLD, self.getSelectedName()] cmd += [ parserutils.FLAG_CGFFLD_LOCATION_TYPE, self.getSelectedLocationType() ] return cmd