# Copyright 2016 Autodesk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""" This module contains methods to display 3D information about the assignment of force field
parameters to a biomolecule.
This helps users to visualize errors that occur when trying to assign a forcefield,
such as unrecognized residues, missing atoms, etc.
NOTE:
This is currently tied to ambertools and tleap! It will need to be made generic if/when
another method for assigning forcefields is added.
"""
import cgi
import collections
import re
import ipywidgets as ipy
from moldesign import units as u
[docs]class ParameterizationDisplay(ipy.Box):
def __init__(self, errormessages, molin, molout=None):
self.molin = molin
self.molout = molout
self.msg = errormessages
self.status = ipy.HTML('<h4>Forcefield assignment: %s</h4>' %
('Success' if molout else 'FAILED'))
self.listdesc = ipy.HTML('<b>Errors / warnings:</b>')
self.errorlist = ipy.Select(options=collections.OrderedDict((e.short, e) for e in self.msg))
self.errmsg = ipy.HTML('-')
self.viewer = self.molin.draw3d()
self.viewer.ribbon(opacity=0.7)
if self.errorlist.value is not None:
self.switch_display({'old': self.errorlist.value, 'new': self.errorlist.value})
self.errorlist.observe(self.switch_display, 'value')
children = (self.status,
ipy.HBox([self.viewer, ipy.VBox([self.listdesc, self.errorlist])]),
self.errmsg)
super(ParameterizationDisplay, self).__init__(children=children)
[docs] def switch_display(self, d):
old = d['old']
old.unshow(self.viewer)
self.errmsg.value = '-'
new = d['new']
new.show(self.viewer)
self.errmsg.value = new.desc
[docs]class ForceFieldMessage(object):
pass
[docs]class UnknownAtom(ForceFieldMessage):
def __init__(self, message, residue, atom):
self.message = message
self.residue = residue
self.atom = atom
self.desc = 'ERROR: Atom name "%s" was not found in the "%s" template<br>%s' % (
self.atom.name, self.residue.resname, self.atom) + '<p>TLeap message:<i>%s</i>' % self.message
self.short = 'ERR: %s: unknown atom name "%s" for residue %s' % (self.atom,
self.atom.name,
self.atom.residue.resname)
[docs] def show(self, viewer):
viewer.licorice(atoms=self.residue.atoms, render=False)
viewer.vdw(atoms=[self.atom])
[docs] def unshow(self, viewer):
viewer.ribbon(opacity=0.7)
[docs]class MissingAtom(ForceFieldMessage):
def __init__(self, message, residue, atom):
self.message = message
self.residue = residue
self.atom = atom
self.desc = 'INFO: Atom %s in %s (chain %s) was added to the system using the "%s" template' % (
self.atom.name, self.residue.name, self.residue.chain.name, self.residue.resname) + \
'<p>TLeap message:<i>%s</i>' % self.message
self.short = 'INFO: Missing heavy atom %s (index %d)' % (self.atom, self.atom.index)
[docs] def show(self, viewer):
viewer.licorice(atoms=self.residue.atoms, render=False)
viewer.vdw(atoms=[self.atom])
[docs] def unshow(self, viewer):
viewer.ribbon(opacity=0.7)
[docs]class UnknownResidue(ForceFieldMessage):
def __init__(self, message, residue):
self.message = message
self.residue = residue
self._label = None
self.desc = ('ERROR: Residue type "%s" was not found in residue templates<br>%s' % (self.residue.resname, self.residue)
+ '<p>TLeap message:<i>%s</i>' % self.message)
self.short = 'ERR: %s: unknown res type "%s"' % (self.residue, self.residue.resname)
[docs] def show(self, viewer):
viewer.licorice(opacity=1.0, atoms=self.residue.atoms, render=False)
self._label = viewer.draw_label(position=self.residue.com, text=self.residue.name)
[docs] def unshow(self, viewer):
viewer.ribbon(opacity=0.7, render=False)
if self._label: viewer.remove(self._label)
self._label = None
[docs]class UnusualBond(ForceFieldMessage):
def __init__(self, message, atoms, residues):
self.message = message
self.atoms = atoms
self.residues = residues
self._has_atoms = (self.atoms[0] is not None) and (self.atoms[1] is not None)
self._shape = None
if self._has_atoms:
self.desc = 'WARNING: Unusual distance between {a1} and {a2}: {d:.3f}'.format(
d=self.atoms[0].distance(self.atoms[1]), a1=self.atoms[0], a2=self.atoms[1]) \
+ '<p>TLeap message:<br><i>%s</i>' % cgi.escape(self.message)
self.short = 'WARN: Unusual dist: {} - {} = ({:.1f})'.format(self.atoms[0],
self.atoms[1],
self.atoms[0].distance(self.atoms[1]))
else:
self.short = 'WARN: Unusual bond - atoms not shown'
self.desc = 'TLeap message:<br><i>%s</i>' % cgi.escape(self.message)
[docs] def show(self, viewer):
if self._has_atoms:
res_opacity = 0.7
else:
res_opacity = 0.9
viewer.licorice(opacity=res_opacity, atoms=self.residues[0], render=False)
viewer.licorice(opacity=res_opacity, atoms=self.residues[1], render=False)
if self._has_atoms:
self._shape = viewer.draw_cylinder(start=self.atoms[0].position,
end=self.atoms[1].position,
radius=0.1 * u.angstrom,
opacity=1.0,
color='red')
else:
viewer.render()
[docs] def unshow(self, viewer):
viewer.ribbon(opacity=0.7, render=False)
if self._shape: viewer.remove(self._shape)
self._shape = None