Working with NGLView#

MolSysMT can handle nglview.NGLWidget objects in the same way that it handles other molecular systems: it is a “form” of molecular systems. As such, any MolSysMT tool can accept NGLView views as input systems.

The nglview.NGLWidget as any other molecular system’s form#

Before we show some examples of how MolSysMT works with NGLView native objects, let’s create an nglview.NGLWidget to play with. Let’s work with the demo system from NGLView:

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

Now that we have an nglview.NGLWidget object, let’s see if MolSysMT can get an information summary of the molecular system straight from it as first test:

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

Attributes such as atom names, number of structures or atom coordinates can be retrieved with the function molsysmt.basic.get():

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

Element’s selections can also be made on NGLView native objects with MolSysMT:

msm.select(view, selection='atom_name=="CA" and group_name=="LYS"')
[226, 1053, 1075, 2235, 3652, 3851, 3898, 4965, 5214, 5405]

And in addition, MolSysMT can help you to get selection strings to be used in NGLView:

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:A 66:A 67:A 141:A 231:A 245:A 248:A 311:A 325:A 339:A'

Finally we can wonder, can MolSysMT convert an nglview.NGLWidget to other molecular system forms? Have a look to this couple of examples:

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_proteins n_structures
openmm.Topology 5547 349 123 1 123 123 3 1 None

Now that it has been demonstrated that MolSysMT recognizes nglview.NGLWidget objects as any other molecular form. Let’s show a couple of examples more appealing than the previous cells.

Example 1: Getting contact maps from a trajectory view#

Given the visualization of a molecular dynamics trajectory, let’s compute the contact maps between all alpha carbons from all frames.

First, the view needs to be produced by NGLView. This let’s do it straight with MolSysMT:

import molsysmt as msm
import nglview as nv
view = msm.convert([nv.datafiles.GRO, nv.datafiles.XTC], to_form='nglview.NGLWidget')

Note

We could have used the function :func:molsysmt.basic.view instead also. Have a look to User guide > Tools > Basic > View.

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

Let’s get the contact maps from the 51 structures with a 12 angstroms threshold:

contact_map = msm.structure.get_contacts(view, selection='molecule_type=="protein" and atom_name=="CA"',
                                         threshold='12 angstroms')

Each contact map is a boolean matrix:

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]])

We can make with Plotly an animated representation of these contact maps. But before, let’s get the x and y ticks labels for our contacts:

CA_labels = msm.get_label(view, selection='molecule_type=="protein" and atom_name=="CA"',
                          string='{group_name}-{group_id}')
CA_labels[10]
'VAL-11'

Time to show how the CA contacts evolves:

import plotly.express as px

fig = px.imshow(~contact_map, animation_frame=0, binary_string=True, height=600, origin='lower')

fig.update_layout(
    xaxis = dict(
        tickmode = 'array',
        tickvals = list(range(0,contact_map.shape[1],25)),
        ticktext = CA_labels[::25],
        tickangle = 45
    ),
    yaxis = dict(
        tickmode = 'array',
        tickvals = list(range(0,contact_map.shape[1],25)),
        ticktext = CA_labels[::25],
        tickangle = 45
    )
)

fig.show()

Example 2: Getting distances between geometric centers of different views#

As second example let’s suppose we have two peptides:

import molsysmt as msm
import nglview as nv
molsys_A = msm.build.build_peptide('AceAlaNME')
molsys_A = msm.structure.center(molsys_A)
molsys_B = msm.build.build_peptide('AceProNME')
molsys_B = msm.structure.center(molsys_B)
molsys_B = msm.structure.translate(molsys_B, translation='[1.0, 0.0, 0.0] nm')

But each peptide has its own visualization. Notice that this time the views are produced with a different approach than before:

view1 = nv.show_molsysmt(molsys_A)
view2 = nv.show_molsysmt(molsys_B)
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 26 3 1 1 1 1 1 1

If we are interested in the distance between the geometrical center of each peptide, we can merge the views in a new one to extract the observable from a unique molecular system:

view = msm.merge([view1, view2])
view.clear()
view.add_licorice()
view
msm.structure.get_distances(view, selection='molecule_index==0', center_of_atoms=True,
                            selection_2='molecule_index==1', center_of_atoms_2=True)
Magnitude
[[[1.0]]]
Unitsnanometer

It can be proved looking at the geometric centers that the distance is correct:

msm.structure.get_center(view, selection='molecule_index==0')
Magnitude
[[[-5.551115123125783e-17 2.5232341468753557e-17 -1.8924256101565167e-18]]]
Unitsnanometer
msm.structure.get_center(view, selection='molecule_index==1')
Magnitude
[[[1.0 4.5369690910162646e-17 6.4051328343759035e-18]]]
Unitsnanometer

Or we can do it from both views, without merging them in a new one:

msm.structure.get_distances(view1, center_of_atoms=True,
                            molecular_system_2=view2, center_of_atoms_2=True)
Magnitude
[[[1.0]]]
Unitsnanometer

Example 3: Coloring atoms or residues based on scalar values from a list#

In this example, we will see how to easily color the residues of a protein according to their charge values using MolSysMT. With just a few lines of code, we can map any list of scalar values—such as partial charges—onto a molecular representation, enabling intuitive visual analysis of physicochemical properties.

molecular_system = msm.basic.convert('181L', selection='molecule_type=="protein"')
charge_groups = msm.physchem.get_charge(molecular_system, element='group', definition='physical_pH7')
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(molecular_system, viewer='NGLView')
msm.thirds.nglview.set_color_by_value(view, values=charge_groups, cmap='bwr_r')
view