4. Visualisation¶
This notebook showcases different ways of visualizing lig-prot and prot-prot interactions, either with atomistic details or simply at the residue level.
This is a work in progress…
[1]:
import MDAnalysis as mda
import prolif as plf
# load topology
u = mda.Universe(plf.datafiles.TOP)
lig = u.select_atoms("resname LIG")
prot = u.select_atoms("protein")
[2]:
# create RDKit-like molecules for visualisation
lmol = plf.Molecule.from_mda(lig)
pmol = plf.Molecule.from_mda(prot)
[3]:
# get lig-prot interactions with atom info
fp = plf.Fingerprint(["HBDonor", "HBAcceptor", "Cationic", "PiStacking"])
fp.run(u.trajectory, lig, prot, return_atoms=True)
df = fp.to_dataframe()
df.T
[3]:
Frame | 0 | ||
---|---|---|---|
ligand | protein | interaction | |
LIG1.G | ASP129.A | HBDonor | [52, 10] |
Cationic | [13, 10] | ||
VAL201.A | HBAcceptor | [28, 6] | |
SER212.A | HBDonor | [44, 2] | |
PHE331.B | PiStacking | [3, 9] |
4.1. py3Dmol (3Dmol.js)¶
With py3Dmol we can easily display the interactions.
For interactions involving a ring (pi-cation, pi-stacking…etc.) ProLIF returns the index of one of the ring atoms, but for visualisation having the centroid of the ring looks nicer. We’ll start by writing a function to find the centroid, given the index of one of the ring atoms.
[4]:
from rdkit import Chem
from rdkit import Geometry
def get_ring_centroid(mol, index):
# find ring using the atom index
Chem.SanitizeMol(mol, Chem.SanitizeFlags.SANITIZE_SETAROMATICITY)
ri = mol.GetRingInfo()
for r in ri.AtomRings():
if index in r:
break
else:
raise ValueError("No ring containing this atom index was found in the given molecule")
# get centroid
coords = mol.xyz[list(r)]
ctd = plf.utils.get_centroid(coords)
return Geometry.Point3D(*ctd)
Finally, the actual visualisation code. The API of py3Dmol is exactly the same as the GLViewer class of 3Dmol.js, for which the documentation can be found here.
[5]:
import py3Dmol
colors = {
"HBAcceptor": "blue",
"HBDonor": "red",
"Cationic": "green",
"PiStacking": "purple",
}
hover_func = """
function(atom,viewer) {
if(!atom.label) {
atom.label = viewer.addLabel(atom.interaction,
{position: atom, backgroundColor: 'mintcream', fontColor:'black'});
}
}"""
unhover_func = """
function(atom,viewer) {
if(atom.label) {
viewer.removeLabel(atom.label);
delete atom.label;
}
}"""
v = py3Dmol.view(650, 600)
v.removeAllModels()
models = {}
mid = -1
for i, row in df.T.iterrows():
lresid, presid, interaction = i
lindex, pindex = row[0]
lres = lmol[lresid]
pres = pmol[presid]
# set model ids for reusing later
if lresid not in models.keys():
mid += 1
v.addModel(Chem.MolToMolBlock(lres), "sdf")
model = v.getModel()
model.setStyle({}, {"stick": {"colorscheme": "cyanCarbon"}})
models[lresid] = mid
if presid not in models.keys():
mid += 1
v.addModel(Chem.MolToMolBlock(pres), "sdf")
model = v.getModel()
model.setStyle({}, {"stick": {}})
models[presid] = mid
# get coordinates for both points of the interaction
if interaction in ["PiStacking", "EdgeToFace", "FaceToFace", "PiCation"]:
p1 = get_ring_centroid(lres, lindex)
else:
p1 = lres.GetConformer().GetAtomPosition(lindex)
if interaction in ["PiStacking", "EdgeToFace", "FaceToFace", "CationPi"]:
p2 = get_ring_centroid(pres, pindex)
else:
p2 = pres.GetConformer().GetAtomPosition(pindex)
# add interaction line
v.addCylinder({"start": dict(x=p1.x, y=p1.y, z=p1.z),
"end": dict(x=p2.x, y=p2.y, z=p2.z),
"color": colors[interaction],
"radius": .15,
"dashed": True,
"fromCap": 1,
"toCap": 1,
})
# add label when hovering the middle of the dashed line by adding a dummy atom
c = Geometry.Point3D(*plf.utils.get_centroid([p1, p2]))
mid = models[lresid]
model = v.getModel(mid)
model.addAtoms([{"elem": 'Z',
"x": c.x, "y": c.y, "z": c.z,
"interaction": interaction}])
model.setStyle({"interaction": interaction}, {"clicksphere": {"radius": .5}})
model.setHoverable(
{"interaction": interaction}, True,
hover_func, unhover_func)
v.zoomTo()
v.show()
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
jupyter labextension install jupyterlab_3dmol
4.2. RDKit¶
This script is given as a starting point for visualising the interactions with RDKit. It’s definitely not ideal for 3D visualisation as the drawing ends up being crowded and quite unreadable very quickly. I’m sure there’s a better way to do this but it will do as a code snippet.
[6]:
from rdkit.Chem import Draw
from IPython import display
colors = {
"HBAcceptor": (0, 1, 1),
"HBDonor": (1, .7,.3),
"Cationic": (0, 1, 0),
"PiStacking": (.5, 0, .5),
}
d = Draw.MolDraw2DSVG(650, 600)
opts = Draw.MolDrawOptions()
opts.fixedBondLength = 25
d.SetDrawOptions(opts)
displayed = []
for i, row in df.T.iterrows():
lresid, presid, interaction = i
lindex, pindex = row[0]
lres = lmol[lresid]
pres = pmol[presid]
if lresid not in displayed:
displayed.append(lresid)
d.DrawMolecule(lres)
if presid not in displayed:
displayed.append(presid)
d.DrawMolecule(pres)
if interaction in ["PiStacking", "EdgeToFace", "FaceToFace", "PiCation"]:
p1 = get_ring_centroid(lres, lindex)
else:
p1 = lres.GetConformer().GetAtomPosition(lindex)
if interaction in ["PiStacking", "EdgeToFace", "FaceToFace", "CationPi"]:
p2 = get_ring_centroid(pres, pindex)
else:
p2 = pres.GetConformer().GetAtomPosition(pindex)
p1 = Geometry.Point2D(p1)
p2 = Geometry.Point2D(p2)
d.DrawWavyLine(p1, p2, colors[interaction], colors[interaction])
# display
d.FinishDrawing()
display.SVG(d.GetDrawingText())
[6]: