Workspace API¶
Structure hierarchy¶
Molecular structures are organized like so:
- Workspace
- Complex
- Molecule
- Chain
- Residue
- Atom
- Bond
A complex is a group of molecules and has a position and rotation. In Nanome, the user can switch between the molecules of a complex using the frame slider, in the information menu.
Index¶
Each molecular structure has an index available as a base property.
This index is a unique identifier for structures uploaded to Nanome. However, if a structure hasn’t been added to Nanome’s workspace yet, its index will be -1
To access this index:
if my_structure.index == -1:
Logs.message("This structure hasn't been uploaded to Nanome")
Deep / Shallow¶
Nanome has two molecular structure transmission mode: Deep and Shallow. Their goal is to make data transmission faster by requesting only the data needed.
- Deep mode will request/send the structure and its entire content. E.g. a molecule in deep mode will contain its name and any other property it might have + all its chains, residues, atoms and bonds
- Shallow mode will request/send only the structure itself. E.g. a molecule in shallow mode will only contain its name and any other property it might have
Whether a command requests one mode or the other is described in this documentation.
Coordinate Spaces¶
When dealing with structures and objects in Nanome, there are 2 coordinate spaces to be aware of: global/workspace and local/complex.
- Global (Workspace) coordinate space is used for positioning and rotating objects in the Nanome Workspace relative to each other
- Local (Complex) coordinate space is used for positioning atoms within a complex
For example, when a complex is loaded into Nanome, the atom positions are all in the local coordinate space of the complex.
Moving and rotating this complex in the workspace will not affect the positions of the atoms within it. Consequently, if you have
2 complexes next to each other rotated differently and export both complexes to a file, the atom positions will not be relative to
each other from their original complex positions. In order to treat atom positions as relative to each other, you must first convert
their positions from local space to global space (or convert the positions of one complex into global space and back into local space
of the other complex). This can be done by using transformation matrices to multiply against the atom positions, where the matrices
come from Complex.get_complex_to_workspace_matrix
and Complex.get_workspace_to_complex_matrix
.
@async_callback
async def on_run(self):
complex1, complex2 = await self.request_complexes([1, 2])
c1_to_global_mat = complex1.get_complex_to_workspace_matrix()
global_to_c2_mat = complex2.get_workspace_to_complex_matrix()
for atom in complex1.atoms:
global_pos = c1_to_global_mat * atom.position
atom.position = global_to_c2_mat * global_pos
complex1.io.to_sdf('complex1.sdf')
complex2.io.to_sdf('complex2.sdf')
In the above example, the atoms of complex1 are converted from local space to global space, and then back to local space of complex2. This makes it possible to pass the resulting sdf into a different program (such as docking) and have the atoms positions be relative to each other as they were positioned inside Nanome.
Common Operations¶
Load PDB/SDF/MMCIF as a Nanome Complex¶
from nanome.api.structure import Complex
pdb_path = '/path/to/file.pdb'
sdf_path = '/path/to/file.sdf'
mmcif_path = '/path/to/file.mmcif'
comp = Complex.io.from_pdb(path=pdb_file)
comp = Complex.io.from_sdf(path=sdf_file)
comp = Complex.io.from_mmcif(path=mmcif_file)
Export Nanome Complex as PDB/SDF/MMCIF¶
from nanome.api.structure import Complex
pdb_path = '/path/to/file.pdb'
sdf_path = '/path/to/file.sdf'
mmcif_path = '/path/to/file.mmcif'
comp = Complex()
comp.io.to_pdb(path=pdb_path)
comp.io.to_sdf(path=sdf_path)
comp.io.to_mmcif(path=mmcif_path)
Request entire workspace in deep mode¶
@async_callback
async def on_run(self):
workspace = await self.request_workspace()
Request all complexes in the workspace in shallow mode¶
@async_callback
async def on_run(self):
shallow_complexes = await self.request_complex_list()
Request a list of specific complexes in deep mode¶
@async_callback
async def on_run(self):
deep_complexes = await self.request_complexes([1, 6, 5]) # Requests complexes with ID 1, 6 and 5
Update workspace to match exactly¶
@async_callback
async def on_run(self):
workspace = await self.request_workspace()
# ...
# Do something with workspace
# ...
self.update_workspace(workspace)
Add to workspace¶
@async_callback
async def on_run(self):
# ...
# Create new complexes
# ...
self.add_to_workspace([new_complex1, new_complex2])
Remove from workspace¶
@async_callback
async def on_run(self):
# ...
# Get list of complexes to remove
# ...
self.remove_from_workspace(complexes_to_remove)
Update specific structures¶
In shallow mode:
@async_callback
async def on_run(self):
shallow_complexes = await self.request_complex_list()
# ...
# Do something with shallow structures, i.e. move them, rename them
# ...
self.update_structures_shallow([complex, atom, residue])
In deep mode:
@async_callback
async def on_run(self):
deep_complexes = await self.request_complexes([1, 6, 5])
# ...
# Do something with deep structures, i.e. move them, rename them
# ...
self.update_structures_deep([complex])