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]]] |
|---|---|
| Units | nanometer |
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')
Note
We could also use molsysmt.basic.view() with viewer='nglview' to instantiate the viewer.
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]]] |
|---|---|
| Units | nanometer |
msm.structure.get_center(view, selection='molecule_index==0')
| Magnitude | [[[0.44929119636363635 0.485347455 -0.022366771363636358]]] |
|---|---|
| Units | nanometer |
msm.structure.get_center(view, selection='molecule_index==1')
| Magnitude | [[[0.9492911963636362 0.485347455 -0.022366771363636358]]] |
|---|---|
| Units | nanometer |
msm.structure.get_distances(view1, center_of_atoms=True, molecular_system_2=view2, center_of_atoms_2=True)
| Magnitude | [[[0.4999999999999998]]] |
|---|---|
| Units | nanometer |
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] |
|---|---|
| Units | elementary_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