Working with NGLView#

MolSysMT handles nglview.NGLWidget instances as a native molecular system form. Any MolSysMT function can accept NGLView widget instances directly as input systems.

NGLWidget Form#

Let’s create an nglview.NGLWidget demo instance to explore how MolSysMT queries and operates on it:

import molsysmt as msm
import nglview as nv
view = nv.demo()
view

We can query system attributes and topology directly from the nglview.NGLWidget instance using molsysmt.basic.info() and molsysmt.basic.get():

msm.info(view)
form n_atoms n_groups n_components n_chains n_molecules n_entities n_proteins n_structures
nglview.NGLWidget 5547 349 1 1 1 1 1 1
msm.get(view, element='group', selection=[81, 82, 83], name=True)
['VAL', 'ALA', 'ASH']
msm.get(view, element='system', n_structures=True)
1
msm.get(view, element='atom', selection='atom_name=="CA"', coordinates=True)
Magnitude
[[[3.7219999999999995 4.478 1.1199999999999999]  [3.9679999999999995 4.531 1.4119999999999997]  [3.8679999999999994 4.228 1.6149999999999998]  ...  [3.5289999999999995 3.638 8.024]  [3.324 3.9369999999999994 7.933999999999999]  [3.147 4.02 8.261]]]
Unitsnanometer

We can also make selections and convert selection strings to NGLView syntax:

msm.select(view, selection='atom_name=="CA" and group_name=="LYS"')
[226, 1053, 1075, 2235, 3652, 3851, 3898, 4965, 5214, 5405]
msm.select(view, selection='atom_name=="CA" and group_name=="LYS"', to_syntax='nglview')
'@226,1053,1075,2235,3652,3851,3898,4965,5214,5405'
msm.select(view, element='group', selection='group_name=="LYS"', to_syntax='nglview')
'16,66,67,141,231,245,248,311,325,339'

Furthermore, MolSysMT converts nglview.NGLWidget instances to other forms seamlessly:

msm.convert(view, to_form='string:amino_acids_3')
'AceMetAsnGlyThrGluGlyProAsnPheTyrValProPheSerAsnLysThrGlyValValArgSerProPheGluAlaProGlnTyrTyrLeuAlaGluProTrpGlnPheSerMetLeuAlaAlaTyrMetPheLeuLeuIleMetLeuGlyPheProIleAsnPheLeuThrLeuTyrValThrValGlnHisLysLysLeuArgThrProLeuAsnTyrIleLeuLeuAsnLeuAlaValAlaAshLeuPheMetValPheGlyGlyPheThrThrThrLeuTyrThrSerLeuHisGlyTyrPheValPheGlyProThrGlyCysAsnLeuGluGlyPhePheAlaThrLeuGlyGlyGlhIleAlaLeuTrpSerLeuValValLeuAlaIleGluArgTyrValValValCysLysProMetSerAsnPheArgPheGlyGluAsnHisAlaIleMetGlyValAlaPheThrTrpValMetAlaLeuAlaCysAlaAlaProProLeuValGlyTrpSerArgTyrIleProGluGlyMetGlnCysSerCysGlyIleAspTyrTyrThrProHisGluGluThrAsnAsnGluSerPheValIleTyrMetPheValValHisPheIleIleProLeuIleValIlePhePheCysTyrGlyGlnLeuValPheThrValLysGluAlaAlaAlaGlnGlnGlnGluSerAlaThrThrGlnLysAlaGluLysGluValThrArgMetValIleIleMetValIleAlaPheLeuIleCysTrpLeuProTyrAlaGlyValAlaPheTyrIlePheThrHisGlnGlySerAspPheGlyProIlePheMetThrIleProAlaPhePheAlaLrtThrSerAlaValTyrAsnProValIleTyrIleMetMetAsnLysGlnPheArgAsnCysMetValThrThrLeuYplYplGlyLysAsnProLeuGlyAspAspGluAlaSerThrThrValSerLysThrGluThrSerGlnValAlaProAla'
openmm_Topology = msm.convert(view, to_form='openmm.Topology')
msm.info(openmm_Topology)
form n_atoms n_groups n_components n_chains n_molecules n_entities n_small_molecules n_peptides n_proteins n_structures
openmm.Topology 5547 349 123 1 6 6 3 2 1 None

Contact Maps#

Given a trajectory view, we can compute contact maps between all C-alpha atoms across frames using molsysmt.structure.get_contacts():

import molsysmt as msm
import nglview as nv

view = msm.convert([nv.datafiles.GRO, nv.datafiles.XTC], to_form='nglview.NGLWidget')
msm.info(view)
form n_atoms n_groups n_components n_chains n_molecules n_entities n_proteins n_structures
nglview.NGLWidget 5547 349 1 1 1 1 1 51
view
contact_map = msm.structure.get_contacts(view, selection='molecule_type=="protein" and atom_name=="CA"',
                                         threshold='12 angstroms')
contact_map[10]
array([[ True,  True,  True, ..., False, False, False],
       [ True,  True,  True, ..., False, False, False],
       [ True,  True,  True, ..., False, False, False],
       ...,
       [False, False, False, ...,  True,  True,  True],
       [False, False, False, ...,  True,  True,  True],
       [False, False, False, ...,  True,  True,  True]], shape=(348, 348))
CA_labels = msm.get_label(view, selection='molecule_type=="protein" and atom_name=="CA"',
                          string='{group_name}-{group_id}')
CA_labels[10]
'VAL-11'

We can render an interactive animated contact map with Plotly:

import plotly.express as px

fig_plotly = px.imshow(contact_map, x=CA_labels, y=CA_labels, animation_frame=0,
                       labels={'x': 'Residue', 'y': 'Residue', 'animation_frame': 'Frame'},
                       color_continuous_scale='Blues')
fig_plotly

Distances Between Views#

We can compute geometric distances across separate NGLView view instances:

import molsysmt as msm
import nglview as nv

molsys_A = msm.build.build_peptide('AceAlaNme')
molsys_B = msm.build.build_peptide('AceAlaNme')
molsys_B = msm.structure.translate(molsys_B, translation='[0.5, 0.0, 0.0] nm')

view1 = msm.convert(molsys_A, to_form='nglview.NGLWidget')
view2 = msm.convert(molsys_B, to_form='nglview.NGLWidget')
msm.info(view1)
form n_atoms n_groups n_components n_chains n_molecules n_entities n_peptides n_structures
nglview.NGLWidget 22 3 1 1 1 1 1 1
msm.info(view2)
form n_atoms n_groups n_components n_chains n_molecules n_entities n_peptides n_structures
nglview.NGLWidget 22 3 1 1 1 1 1 1

We can merge both views into a combined NGLView visualization:

view = msm.merge([view1, view2])
view.clear()
view.add_licorice()
view
msm.structure.get_distances(view, selection='molecule_index==0', selection_2='molecule_index==1', center_of_atoms=True)
Magnitude
[[[0.46026935818432885 0.37379559007041957 0.3312328322709677   0.3189887091806808 0.45195497766176096 0.5735343102419436   0.41635060931320395 0.3261498224080957 0.5370359648123084   0.6042574477828384 0.6282222620033407 0.595397510163581   0.7206080829237674 0.6622379735108516 0.5378103419171005   0.44900688798385124 0.6658709622656408 0.7402293801674724   0.7223583921238095 0.6542869553601989 0.79240385528605   0.7873638707787478]]]
Unitsnanometer
msm.structure.get_center(view, selection='molecule_index==0')
Magnitude
[[[0.44929119636363635 0.485347455 -0.022366771363636358]]]
Unitsnanometer
msm.structure.get_center(view, selection='molecule_index==1')
Magnitude
[[[0.9492911963636362 0.485347455 -0.022366771363636358]]]
Unitsnanometer
msm.structure.get_distances(view1, center_of_atoms=True, molecular_system_2=view2, center_of_atoms_2=True)
Magnitude
[[[0.4999999999999998]]]
Unitsnanometer

Color by Property#

We can color macromolecular structures in NGLView based on physical properties such as residue partial charges:

molsys = msm.convert('181L', selection='molecule_type=="protein"')
charge_groups = msm.physchem.get_charge(molsys, element='group', selection='molecule_type=="protein"')
charge_groups
Magnitude
[0.0 0.0 0.0 0.0 -1.0 0.0 0.0 1.0 0.0 -1.0 -1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 -1.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 -1.0 0.0 -1.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 -1.0 -1.0 0.0 -1.0 1.0 0.0 0.0 0.0 0.0 -1.0 0.0 -1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 -1.0 0.0 0.0 -1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 -1.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 1.0]
Unitselementary_charge
view = msm.view(molsys, viewer='nglview')
msm.third_party.nglview.set_color_by_value(view, values=charge_groups, selection='molecule_type=="protein"', cmap='bwr')
view