TMol How-To Guide — Advanced Kinematics#

Open In Colab

7. Advanced kinematics#

Named torsions (phi/psi/chi, already used since section 1) exist for nucleic acids and ligands too. The FoldForest that section 2 treated as a black box is itself just a small, directly-constructible data structure — both get covered in more depth here.

Setup#

Wheel install, imports, seeding, and device selection live in notebook_setup.py.

[1]:
try:
    import google.colab  # noqa: F401
except ImportError:
    IN_COLAB = False
else:
    IN_COLAB = True

if IN_COLAB:
    from urllib.request import urlretrieve

    urlretrieve(
        "https://raw.githubusercontent.com/RosettaCommons/tmol/"
        "main/docs/tutorial/notebook_setup.py",
        "notebook_setup.py",
    )

from notebook_setup import *  # noqa: F401,F403

device, param_db = setup()

Environment variable CCD_MIRROR_PATH not set. Will not be able to use function requiring this variable. To set it you may:
  (1) add the line 'export VAR_NAME=path/to/variable' to your .bashrc or .zshrc file
  (2) set it in your current shell with 'export VAR_NAME=path/to/variable'
  (3) write it to a .env file in the root of the atomworks.io repository
Environment variable PDB_MIRROR_PATH not set. Will not be able to use function requiring this variable. To set it you may:
  (1) add the line 'export VAR_NAME=path/to/variable' to your .bashrc or .zshrc file
  (2) set it in your current shell with 'export VAR_NAME=path/to/variable'
  (3) write it to a .env file in the root of the atomworks.io repository

Named torsions#

Querying and setting by name: protein, NA#

tmol.get_torsion_names/get_named_torsions/set_named_torsions are generic – they work on any residue type’s declared torsions, protein or nucleic acid alike. 1BNA (B-form DNA, from section 3) declares the standard backbone torsions (alpha through zeta), the glycosidic chi1, and the sugar-pucker torsions nu0/nu1/nu4.

[2]:
dna_cif_text = fetch_cif("1BNA")
dna_pose_stack = pose_stack_from_file(io.StringIO(dna_cif_text), device, param_db=param_db)

dna_block = 2
print("torsion names:", tmol.get_torsion_names(dna_pose_stack, 0, dna_block))
before_alpha = tmol.get_named_torsions(dna_pose_stack, 0, dna_block, 'alpha')
print(f"alpha: {before_alpha:.1f} deg")

viewer = tmol.view(dna_pose_stack)
viewer.show()

rotated_dna = tmol.set_named_torsions(dna_pose_stack, 0, dna_block, "alpha", 180.0)
after_alpha = tmol.get_named_torsions(rotated_dna, 0, dna_block, 'alpha')
print(f"alpha after setting to 180: {after_alpha:.1f} deg")

display(tmol.overlay_view(
    {"original": dna_pose_stack, "alpha rotated": rotated_dna},
    notes={"original": f"alpha={before_alpha:.1f} deg", "alpha rotated": f"alpha={after_alpha:.1f} deg"},
))

DG is seen only at a chain terminus and N1, N2 could each carry its other connection; none of them is a conventional backbone, so it cannot be told apart. A copy of the residue in a chain would settle it.
torsion names: ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'chi1', 'nu0', 'nu1', 'nu4']
alpha: -62.6 deg
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))

3Dmol.js failed to load for some reason. Please check your browser console for error messages.

alpha after setting to 180: -180.0 deg
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))

“Autogenerated” named torsions for ligands#

A prepared ligand’s rotatable bonds become named torsions too, continuing the chi{n} numbering scheme – not the same names its chemistry would suggest (phi/psi don’t apply), just chi1, chi2, … in whatever order the bonds were discovered. 3PTB’s benzamidine is too small and rigid to have any (zero named torsions); the “ace” ligand fixture from section 4’s splitting example is larger and has several.

[3]:
import biotite.structure as struc
import biotite.structure.io
from tmol.io import pose_stack_from_atom_array
ligand_structure = biotite.structure.io.load_structure(
    str(data_path("protein_ligand_test", "ace.tmol.nomin.cif")), model=1, include_bonds=True
)
if isinstance(ligand_structure, struc.AtomArrayStack):
    ligand_structure = ligand_structure[0]

ligand_pose_stack = pose_stack_from_atom_array(
    ligand_structure, device, prepare_ligands=True,
    ligand_params_files=[str(data_path("protein_ligand_test", "ace.xtal-lig.mmff94.tmol"))], no_optH=True, param_db=param_db,
)
ligand_block = next(
    block for block in range(ligand_pose_stack.max_n_blocks)
    if ligand_pose_stack.block_type(0, block).name3 == "LG1"
)
ligand_torsion_names = tmol.get_torsion_names(ligand_pose_stack, 0, ligand_block)
print(f"ligand torsion names: {ligand_torsion_names}")
print(f"chi1: {tmol.get_named_torsions(ligand_pose_stack, 0, ligand_block, 'chi1'):.1f} deg")

viewer = tmol.view(ligand_pose_stack)
viewer.show()

ligand torsion names: ['chi1', 'chi2', 'chi3', 'chi4', 'chi5', 'chi6', 'chi7', 'chi8', 'chi9', 'chi10', 'chi11', 'chi12', 'chi13']
chi1: -61.0 deg
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))

3Dmol.js failed to load for some reason. Please check your browser console for error messages.

FoldForest basic concepts#

A FoldForest is a small set of directed edges describing how a kinematic tree connects a structure’s blocks – tmol.kinematics.EdgeType.polymer (a contiguous run of blocks), .jump (an arbitrary connection between two residues), and .root_jump (an edge from the tree’s virtual root into one residue, start=-1). tmol.FoldForest.reasonable_fold_forest(pose_stack) builds one automatically, rooting each chain at its first residue – an N-to-C tree per chain, which every example so far has used implicitly.

Manipulating a FoldForest explicitly#

FoldForest.from_edges builds one from a padded [pose, edge, 4] array of (edge_type, start, end, jump_index) tuples. A small peptide, rooted at its first residue as usual:

[4]:
peptide = tmol.extended_pose_stack_from_sequences("AKLFG", device=device)

viewer = tmol.view(peptide)
viewer.show()

last_block = peptide.max_n_blocks - 1

n_to_c_edges = np.zeros((1, 2, 4), dtype=np.int64)
n_to_c_edges[0, 0] = [tmol.EdgeType.root_jump, -1, 0, -1]
n_to_c_edges[0, 1] = [tmol.EdgeType.polymer, 0, last_block, -1]
n_to_c_fold_forest = tmol.FoldForest.from_edges(n_to_c_edges)
print("N->C fold forest matches the automatic one:", (n_to_c_edges == tmol.FoldForest.reasonable_fold_forest(peptide).edges).all())

/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))

3Dmol.js failed to load for some reason. Please check your browser console for error messages.

N->C fold forest matches the automatic one: True

Rooting the same peptide at its last residue instead, with the polymer edge running backward, gives a C-to-N tree.

[5]:
c_to_n_edges = np.zeros((1, 2, 4), dtype=np.int64)
c_to_n_edges[0, 0] = [tmol.EdgeType.root_jump, -1, last_block, -1]
c_to_n_edges[0, 1] = [tmol.EdgeType.polymer, last_block, 0, -1]
c_to_n_fold_forest = tmol.FoldForest.from_edges(c_to_n_edges)
print("built a C->N fold forest with", int(c_to_n_fold_forest.n_edges[0]), "edges")

built a C->N fold forest with 2 edges

set_named_torsions takes an optional fold_forest= to move along a tree other than the pose’s default – it moves whichever side of the torsion’s bond is downstream of the root, so the two trees reach the same torsion value by moving opposite ends of the chain.

[6]:
middle_block = 2
targets = {"phi": -61.0, "psi": -43.0}

moved_n_to_c = tmol.set_named_torsions(peptide, 0, middle_block, list(targets), list(targets.values()))
moved_c_to_n = tmol.set_named_torsions(
    peptide, 0, middle_block, list(targets), list(targets.values()), fold_forest=c_to_n_fold_forest
)

for name, target in targets.items():
    print(
        f"{name}: target {target:.1f}, N->C tree {tmol.get_named_torsions(moved_n_to_c, 0, middle_block, name):.1f}, "
        f"C->N tree {tmol.get_named_torsions(moved_c_to_n, 0, middle_block, name):.1f}"
    )

first_block_offset = int(peptide.block_coord_offset64[0, 1])
last_block_offset = int(peptide.block_coord_offset64[0, last_block])
moved_before = (moved_n_to_c.coords[0, :first_block_offset] - peptide.coords[0, :first_block_offset]).norm(dim=-1)
moved_after = (moved_c_to_n.coords[0, last_block_offset:] - peptide.coords[0, last_block_offset:]).norm(dim=-1)
print(f"N->C tree leaves the N-terminal residue fixed: max move {float(torch.nan_to_num(moved_before).max()):.5f} A")
print(f"C->N tree leaves the C-terminal residue fixed: max move {float(torch.nan_to_num(moved_after).max()):.5f} A")

display(tmol.overlay_view(
    {"original": peptide, "N->C tree": moved_n_to_c, "C->N tree": moved_c_to_n},
    notes={
        "original": "unmoved peptide",
        "N->C tree": f"N-terminus fixed, max move {float(torch.nan_to_num(moved_before).max()):.5f} A",
        "C->N tree": f"C-terminus fixed, max move {float(torch.nan_to_num(moved_after).max()):.5f} A",
    },
))

phi: target -61.0, N->C tree -61.0, C->N tree -61.0
psi: target -43.0, N->C tree -43.0, C->N tree -43.0
N->C tree leaves the N-terminal residue fixed: max move 0.00000 A
C->N tree leaves the C-terminal residue fixed: max move 0.00000 A
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))
/home/runner/work/tmol/tmol/.venv/lib/python3.12/site-packages/biotite/structure/io/pdb/file.py:629: DeprecationWarning: The chararray class is deprecated and will be removed in a future release. Use an ndarray with a string or bytes dtype instead.
  record = np.char.array(np.where(array.hetero, "HETATM", "ATOM"))

Download this notebook