{ "cells": [ { "cell_type": "markdown", "id": "b572dd07", "metadata": {}, "source": [ "# TMol How-To Guide — Advanced Kinematics\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/RosettaCommons/tmol/blob/main/docs/tutorial/07_advanced_kinematics.ipynb)" ] }, { "cell_type": "markdown", "id": "0417440f", "metadata": {}, "source": [ "## 7. Advanced kinematics" ] }, { "cell_type": "markdown", "id": "d59f62a6", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "id": "c6153ddf", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "markdown", "id": "f3faed33", "metadata": {}, "source": [ "Wheel install, imports, seeding, and device selection live in [notebook_setup.py](https://github.com/RosettaCommons/tmol/blob/main/docs/tutorial/notebook_setup.py)." ] }, { "cell_type": "code", "execution_count": 1, "id": "fa5b577b", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T00:49:09.453984Z", "iopub.status.busy": "2026-10-20T00:49:09.453720Z", "iopub.status.idle": "2026-10-20T00:51:18.530624Z", "shell.execute_reply": "2026-10-20T00:51:18.523947Z" }, "tags": [ "collapse-code" ] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Environment variable CCD_MIRROR_PATH not set. Will not be able to use function requiring this variable. To set it you may:\n", " (1) add the line 'export VAR_NAME=path/to/variable' to your .bashrc or .zshrc file\n", " (2) set it in your current shell with 'export VAR_NAME=path/to/variable'\n", " (3) write it to a .env file in the root of the atomworks.io repository\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Environment variable PDB_MIRROR_PATH not set. Will not be able to use function requiring this variable. To set it you may:\n", " (1) add the line 'export VAR_NAME=path/to/variable' to your .bashrc or .zshrc file\n", " (2) set it in your current shell with 'export VAR_NAME=path/to/variable'\n", " (3) write it to a .env file in the root of the atomworks.io repository\n" ] } ], "source": [ "try:\n", " import google.colab # noqa: F401\n", "except ImportError:\n", " IN_COLAB = False\n", "else:\n", " IN_COLAB = True\n", "\n", "if IN_COLAB:\n", " from urllib.request import urlretrieve\n", "\n", " urlretrieve(\n", " \"https://raw.githubusercontent.com/RosettaCommons/tmol/\"\n", " \"main/docs/tutorial/notebook_setup.py\",\n", " \"notebook_setup.py\",\n", " )\n", "\n", "from notebook_setup import * # noqa: F401,F403\n", "\n", "device, param_db = setup()\n" ] }, { "cell_type": "markdown", "id": "2831cd17", "metadata": {}, "source": [ "### Named torsions" ] }, { "cell_type": "markdown", "id": "4445647a", "metadata": {}, "source": [ "#### Querying and setting by name: protein, NA" ] }, { "cell_type": "markdown", "id": "ad6cf395", "metadata": {}, "source": [ "`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](https://www.rcsb.org/structure/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`." ] }, { "cell_type": "code", "execution_count": 2, "id": "bd96f17e", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T00:51:18.546360Z", "iopub.status.busy": "2026-10-20T00:51:18.545888Z", "iopub.status.idle": "2026-10-20T00:51:58.952920Z", "shell.execute_reply": "2026-10-20T00:51:58.952164Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "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.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "torsion names: ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'chi1', 'nu0', 'nu1', 'nu4']\n", "alpha: -62.6 deg\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n" ] }, { "data": { "application/3dmoljs_load.v0": "
\n

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

\n
\n", "text/html": [ "
\n", "

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

\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "alpha after setting to 180: -180.0 deg\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n" ] }, { "data": { "text/html": [ "\n", "
\n", " \n", "
\n", " \n", "
\n", "
alpha=-62.6 deg
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
alpha=-180.0 deg
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
original vs. alpha rotated
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dna_cif_text = fetch_cif(\"1BNA\")\n", "dna_pose_stack = pose_stack_from_file(io.StringIO(dna_cif_text), device, param_db=param_db)\n", "\n", "dna_block = 2\n", "print(\"torsion names:\", tmol.get_torsion_names(dna_pose_stack, 0, dna_block))\n", "before_alpha = tmol.get_named_torsions(dna_pose_stack, 0, dna_block, 'alpha')\n", "print(f\"alpha: {before_alpha:.1f} deg\")\n", "\n", "viewer = tmol.view(dna_pose_stack)\n", "viewer.show()\n", "\n", "rotated_dna = tmol.set_named_torsions(dna_pose_stack, 0, dna_block, \"alpha\", 180.0)\n", "after_alpha = tmol.get_named_torsions(rotated_dna, 0, dna_block, 'alpha')\n", "print(f\"alpha after setting to 180: {after_alpha:.1f} deg\")\n", "\n", "display(tmol.overlay_view(\n", " {\"original\": dna_pose_stack, \"alpha rotated\": rotated_dna},\n", " notes={\"original\": f\"alpha={before_alpha:.1f} deg\", \"alpha rotated\": f\"alpha={after_alpha:.1f} deg\"},\n", "))\n" ] }, { "cell_type": "markdown", "id": "3fc63e9c", "metadata": {}, "source": [ "#### \"Autogenerated\" named torsions for ligands" ] }, { "cell_type": "markdown", "id": "a339d341", "metadata": {}, "source": [ "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](https://www.rcsb.org/structure/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." ] }, { "cell_type": "code", "execution_count": 3, "id": "630e4b9d", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T00:51:58.958359Z", "iopub.status.busy": "2026-10-20T00:51:58.957963Z", "iopub.status.idle": "2026-10-20T00:52:03.470471Z", "shell.execute_reply": "2026-10-20T00:52:03.469666Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ligand torsion names: ['chi1', 'chi2', 'chi3', 'chi4', 'chi5', 'chi6', 'chi7', 'chi8', 'chi9', 'chi10', 'chi11', 'chi12', 'chi13']\n", "chi1: -61.0 deg\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n" ] }, { "data": { "application/3dmoljs_load.v0": "
\n

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

\n
\n", "text/html": [ "
\n", "

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

\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import biotite.structure as struc\n", "import biotite.structure.io\n", "from tmol.io import pose_stack_from_atom_array\n", "ligand_structure = biotite.structure.io.load_structure(\n", " str(data_path(\"protein_ligand_test\", \"ace.tmol.nomin.cif\")), model=1, include_bonds=True\n", ")\n", "if isinstance(ligand_structure, struc.AtomArrayStack):\n", " ligand_structure = ligand_structure[0]\n", "\n", "ligand_pose_stack = pose_stack_from_atom_array(\n", " ligand_structure, device, prepare_ligands=True,\n", " ligand_params_files=[str(data_path(\"protein_ligand_test\", \"ace.xtal-lig.mmff94.tmol\"))], no_optH=True, param_db=param_db,\n", ")\n", "ligand_block = next(\n", " block for block in range(ligand_pose_stack.max_n_blocks)\n", " if ligand_pose_stack.block_type(0, block).name3 == \"LG1\"\n", ")\n", "ligand_torsion_names = tmol.get_torsion_names(ligand_pose_stack, 0, ligand_block)\n", "print(f\"ligand torsion names: {ligand_torsion_names}\")\n", "print(f\"chi1: {tmol.get_named_torsions(ligand_pose_stack, 0, ligand_block, 'chi1'):.1f} deg\")\n", "\n", "viewer = tmol.view(ligand_pose_stack)\n", "viewer.show()\n" ] }, { "cell_type": "markdown", "id": "8bf6b7e8", "metadata": {}, "source": [ "### FoldForest basic concepts" ] }, { "cell_type": "markdown", "id": "6c59b1b3", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "id": "8d551c41", "metadata": {}, "source": [ "#### Manipulating a FoldForest explicitly" ] }, { "cell_type": "markdown", "id": "96f7ab88", "metadata": {}, "source": [ "`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:" ] }, { "cell_type": "code", "execution_count": 4, "id": "12dcc4cb", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T00:52:03.496258Z", "iopub.status.busy": "2026-10-20T00:52:03.495992Z", "iopub.status.idle": "2026-10-20T00:52:04.752948Z", "shell.execute_reply": "2026-10-20T00:52:04.751728Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n" ] }, { "data": { "application/3dmoljs_load.v0": "
\n

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

\n
\n", "text/html": [ "
\n", "

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

\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "N->C fold forest matches the automatic one: True\n" ] } ], "source": [ "peptide = tmol.extended_pose_stack_from_sequences(\"AKLFG\", device=device)\n", "\n", "viewer = tmol.view(peptide)\n", "viewer.show()\n", "\n", "last_block = peptide.max_n_blocks - 1\n", "\n", "n_to_c_edges = np.zeros((1, 2, 4), dtype=np.int64)\n", "n_to_c_edges[0, 0] = [tmol.EdgeType.root_jump, -1, 0, -1]\n", "n_to_c_edges[0, 1] = [tmol.EdgeType.polymer, 0, last_block, -1]\n", "n_to_c_fold_forest = tmol.FoldForest.from_edges(n_to_c_edges)\n", "print(\"N->C fold forest matches the automatic one:\", (n_to_c_edges == tmol.FoldForest.reasonable_fold_forest(peptide).edges).all())\n" ] }, { "cell_type": "markdown", "id": "1a046cc6", "metadata": {}, "source": [ "Rooting the same peptide at its *last* residue instead, with the polymer edge running backward, gives a C-to-N tree." ] }, { "cell_type": "code", "execution_count": 5, "id": "beab7ef7", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T00:52:04.756610Z", "iopub.status.busy": "2026-10-20T00:52:04.755980Z", "iopub.status.idle": "2026-10-20T00:52:04.762454Z", "shell.execute_reply": "2026-10-20T00:52:04.761730Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "built a C->N fold forest with 2 edges\n" ] } ], "source": [ "c_to_n_edges = np.zeros((1, 2, 4), dtype=np.int64)\n", "c_to_n_edges[0, 0] = [tmol.EdgeType.root_jump, -1, last_block, -1]\n", "c_to_n_edges[0, 1] = [tmol.EdgeType.polymer, last_block, 0, -1]\n", "c_to_n_fold_forest = tmol.FoldForest.from_edges(c_to_n_edges)\n", "print(\"built a C->N fold forest with\", int(c_to_n_fold_forest.n_edges[0]), \"edges\")\n" ] }, { "cell_type": "markdown", "id": "3adea02b", "metadata": {}, "source": [ "`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." ] }, { "cell_type": "code", "execution_count": 6, "id": "4381738f", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T00:52:04.764620Z", "iopub.status.busy": "2026-10-20T00:52:04.764301Z", "iopub.status.idle": "2026-10-20T00:52:04.870698Z", "shell.execute_reply": "2026-10-20T00:52:04.868922Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "phi: target -61.0, N->C tree -61.0, C->N tree -61.0\n", "psi: target -43.0, N->C tree -43.0, C->N tree -43.0\n", "N->C tree leaves the N-terminal residue fixed: max move 0.00000 A\n", "C->N tree leaves the C-terminal residue fixed: max move 0.00000 A\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n", "/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.\n", " record = np.char.array(np.where(array.hetero, \"HETATM\", \"ATOM\"))\n" ] }, { "data": { "text/html": [ "\n", "
\n", " \n", "
\n", " \n", "
\n", "
unmoved peptide
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
N-terminus fixed, max move 0.00000 A
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
C-terminus fixed, max move 0.00000 A
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
original vs. N->C tree vs. C->N tree
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "middle_block = 2\n", "targets = {\"phi\": -61.0, \"psi\": -43.0}\n", "\n", "moved_n_to_c = tmol.set_named_torsions(peptide, 0, middle_block, list(targets), list(targets.values()))\n", "moved_c_to_n = tmol.set_named_torsions(\n", " peptide, 0, middle_block, list(targets), list(targets.values()), fold_forest=c_to_n_fold_forest\n", ")\n", "\n", "for name, target in targets.items():\n", " print(\n", " f\"{name}: target {target:.1f}, N->C tree {tmol.get_named_torsions(moved_n_to_c, 0, middle_block, name):.1f}, \"\n", " f\"C->N tree {tmol.get_named_torsions(moved_c_to_n, 0, middle_block, name):.1f}\"\n", " )\n", "\n", "first_block_offset = int(peptide.block_coord_offset64[0, 1])\n", "last_block_offset = int(peptide.block_coord_offset64[0, last_block])\n", "moved_before = (moved_n_to_c.coords[0, :first_block_offset] - peptide.coords[0, :first_block_offset]).norm(dim=-1)\n", "moved_after = (moved_c_to_n.coords[0, last_block_offset:] - peptide.coords[0, last_block_offset:]).norm(dim=-1)\n", "print(f\"N->C tree leaves the N-terminal residue fixed: max move {float(torch.nan_to_num(moved_before).max()):.5f} A\")\n", "print(f\"C->N tree leaves the C-terminal residue fixed: max move {float(torch.nan_to_num(moved_after).max()):.5f} A\")\n", "\n", "display(tmol.overlay_view(\n", " {\"original\": peptide, \"N->C tree\": moved_n_to_c, \"C->N tree\": moved_c_to_n},\n", " notes={\n", " \"original\": \"unmoved peptide\",\n", " \"N->C tree\": f\"N-terminus fixed, max move {float(torch.nan_to_num(moved_before).max()):.5f} A\",\n", " \"C->N tree\": f\"C-terminus fixed, max move {float(torch.nan_to_num(moved_after).max()):.5f} A\",\n", " },\n", "))\n" ] } ], "metadata": { "accelerator": "GPU", "colab": { "gpuType": "T4" }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.12" }, "tmol": { "gpu_smoke": true } }, "nbformat": 4, "nbformat_minor": 5 }