{ "cells": [ { "cell_type": "markdown", "id": "90e5b4f1", "metadata": {}, "source": [ "# TMol How-To Guide — Metals and Nucleic Acids\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/03_metals_and_nucleic_acids.ipynb)" ] }, { "cell_type": "markdown", "id": "3b1d67bf", "metadata": {}, "source": [ "## 3. Metals and Nucleic Acids" ] }, { "cell_type": "markdown", "id": "ff851d1b", "metadata": {}, "source": [ "Metal ions and nucleic acids both score correctly with no special setup -- this notebook is mostly worked examples confirming that. It assumes the basics from [tmol_how_to_guide.ipynb](01_tmol_how_to_guide.ipynb) and scoring/relax from [02_scoring_and_optimization.ipynb](02_scoring_and_optimization.ipynb)." ] }, { "cell_type": "markdown", "id": "dee83c2c", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "markdown", "id": "caef9b13", "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": "b36860f6", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:35:04.669883Z", "iopub.status.busy": "2026-10-20T01:35:04.669733Z", "iopub.status.idle": "2026-10-20T01:35:11.080209Z", "shell.execute_reply": "2026-10-20T01:35:11.079326Z" }, "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": "e27082f6", "metadata": {}, "source": [ "### Metals" ] }, { "cell_type": "markdown", "id": "502eca81", "metadata": {}, "source": [ "#### Handled by default" ] }, { "cell_type": "markdown", "id": "3f5462a1", "metadata": {}, "source": [ "Metal ions (ZN, CA, MG, FE, ...) are ordinary residue types in the default chemical database, not ligands -- detecting a metal ion and building its coordinating bonds needs no `prepare_ligands` flag. A `MetalCoordination` energy term scoring that geometry is included in `beta2016_score_function` by default, at weight 1.0." ] }, { "cell_type": "markdown", "id": "4b2a863b", "metadata": {}, "source": [ "We use a small curated fixture from TMol's own test suite -- a tetrahedral zinc site from [3KS3](https://www.rcsb.org/structure/3KS3) (carbonic anhydrase II) -- rather than fetching the full deposited structure, since it's already cropped to the site of interest." ] }, { "cell_type": "code", "execution_count": 2, "id": "0e6ddef6", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:35:11.082807Z", "iopub.status.busy": "2026-10-20T01:35:11.082420Z", "iopub.status.idle": "2026-10-20T01:35:16.538453Z", "shell.execute_reply": "2026-10-20T01:35:16.537862Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "HIS is seen only at a chain terminus and N, ND1, NE2 could each carry its other connection; it is read as the conventional backbone through N. A copy of the residue in a chain would settle it.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Unrecognized 3lc GOL\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "n_poses=1, max_n_blocks=258\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": [ "metal_pose_stack = pose_stack_from_file(data_path(\"metal_fixtures\", \"zn_tetrahedral_3ks3.cif.zst\"), device)\n", "print(f\"n_poses={metal_pose_stack.n_poses}, max_n_blocks={metal_pose_stack.max_n_blocks}\")\n", "\n", "viewer = tmol.view(metal_pose_stack)\n", "viewer.show()\n" ] }, { "cell_type": "markdown", "id": "05861dc4", "metadata": {}, "source": [ "#### Determined Coordination - TODO" ] }, { "cell_type": "markdown", "id": "9dbed3ea", "metadata": {}, "source": [ "TODO - I dont really understand what determined coordination is, but this is clearly too complicated. Claude feedback below:\n", "\n", "There is no single \"report coordination\" function; the real pattern is to find the metal's block type (it declares coordination sites via `tmol.database.chemical.site_connections`), then walk `pose_stack.inter_residue_connections64` to see which residue fills each site." ] }, { "cell_type": "code", "execution_count": 3, "id": "af4601e1", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:35:16.544417Z", "iopub.status.busy": "2026-10-20T01:35:16.544247Z", "iopub.status.idle": "2026-10-20T01:35:45.576540Z", "shell.execute_reply": "2026-10-20T01:35:45.575864Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/runner/work/tmol/tmol/tmol/io/_atomworks_reader.py:893: UserWarning: Keeping one residue per site: dropping C:202 CU\n", " array = one_residue_per_site(array)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Unrecognized 3lc ACY\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Unrecognized 3lc SO4\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "A 201 CU1 CU (trigonal):\n", " site 0: A 44 HIS ND1\n", " site 1: A 46 HIS NE2\n", " site 2: A 118 HIS NE2\n", "A 203 ZN ZN (tetrahedral):\n", " site 0: A 61 HIS ND1\n", " site 1: A 69 HIS ND1\n", " site 2: A 81 ASP OD1\n", " site 3: A 78 HIS ND1\n", "A 204 NA NA (irregular):\n", " site 0: A 90 ASP OD1\n", " site 1: open\n", " site 2: open\n", " site 3: open\n", " site 4: open\n", " site 5: open\n", " site 6: open\n", " site 7: open\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "A 201 CU1: metal_coordination 1.784 -> 0.400\n", "A 203 ZN: metal_coordination 1.082 -> 0.466\n", "A 204 NA: metal_coordination 21.387 -> 0.018\n" ] } ], "source": [ "def metal_sites(pose_stack, pose, block):\n", " \"\"\"(metal atom, geometry, donor labels) for each metal in a block; None marks an open site.\"\"\"\n", " bt = pose_stack.block_type(pose, block)\n", " result = []\n", " for site in bt.metal_sites:\n", " donors = []\n", " for conn_name in site.site_connections:\n", " conn = bt.connection_to_cidx[conn_name]\n", " partner, partner_conn = pose_stack.inter_residue_connections64[pose, block, conn].tolist()\n", " if partner < 0:\n", " donors.append(None)\n", " continue\n", " partner_bt = pose_stack.block_type(pose, partner)\n", " atom = partner_bt.atoms[partner_bt.ordered_connection_atoms[partner_conn]].name\n", " donors.append(f\"{residue_label(pose_stack, pose, partner)} {atom}\")\n", " result.append((site.metal_atom, site.geometry, donors))\n", " return result\n", "\n", "cif_text = fetch_cif(\"3F7L\")\n", "pose_stack = pose_stack_from_file(io.StringIO(cif_text), device)\n", "metals = metal_blocks(pose_stack, 0)\n", "\n", "# Coordination found at construction\n", "for block in metals:\n", " for metal_atom, geometry, donors in metal_sites(pose_stack, 0, block):\n", " print(f\"{residue_label(pose_stack, 0, block)} {metal_atom} ({geometry}):\")\n", " for site, donor in enumerate(donors):\n", " print(f\" site {site}: {donor or 'open'}\")\n", "\n", "# Relax, then compare metal_coordination energy per metal\n", "score_function = beta2016_score_function(device)\n", "before = block_energies(score_function, pose_stack, ScoreType.metal_coordination, 0, metals)\n", "torch.manual_seed(SEED)\n", "relaxed = tmol.fast_relax(\n", " pose_stack.clone(),\n", " score_function,\n", " PackerPalette(),\n", " tmol.CartesianMoveMap(),\n", " tmol.FoldForest.reasonable_fold_forest(pose_stack),\n", " num_repeats=1,\n", ")\n", "after = block_energies(score_function, relaxed, ScoreType.metal_coordination, 0, metals)\n", "for block, e0, e1 in zip(metals, before, after):\n", " print(f\"{residue_label(pose_stack, 0, block)}: metal_coordination {e0:.3f} -> {e1:.3f}\")\n" ] }, { "cell_type": "markdown", "id": "addc8e65", "metadata": {}, "source": [ "Relax it (using the ligand-extended parameter database, since the glycerol needs it) and read off the `metal_coordination` term." ] }, { "cell_type": "code", "execution_count": 4, "id": "6cf861b4", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:35:45.578110Z", "iopub.status.busy": "2026-10-20T01:35:45.577944Z", "iopub.status.idle": "2026-10-20T01:36:38.960503Z", "shell.execute_reply": "2026-10-20T01:36:38.959803Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "metal_coordination energy: 0.28\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", "
pre-relax
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
metal_coordination energy: 0.28
\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. relaxed
\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": [ "metal_score_function = beta2016_score_function(device, param_db=param_db)\n", "metal_fold_forest = tmol.FoldForest.reasonable_fold_forest(metal_pose_stack)\n", "\n", "torch.manual_seed(SEED)\n", "metal_relaxed = tmol.fast_relax(\n", " metal_pose_stack.clone(), metal_score_function, PackerPalette(), tmol.CartesianMoveMap(), metal_fold_forest,\n", " num_repeats=1,\n", ")\n", "\n", "metal_scorer = metal_score_function.render_whole_pose_scoring_module(metal_relaxed)\n", "weighted_terms = metal_scorer(metal_relaxed.coords, sum_terms=False, apply_weights=True)\n", "metal_coordination_index = metal_score_function.all_score_types().index(ScoreType.metal_coordination)\n", "print(f\"metal_coordination energy: {float(weighted_terms[metal_coordination_index, 0].detach()):.2f}\")\n", "\n", "display(tmol.overlay_view(\n", " {\"original\": metal_pose_stack, \"relaxed\": metal_relaxed},\n", " notes={\n", " \"original\": \"pre-relax\",\n", " \"relaxed\": f\"metal_coordination energy: {float(weighted_terms[metal_coordination_index, 0].detach()):.2f}\",\n", " },\n", "))\n" ] }, { "cell_type": "markdown", "id": "3584483a", "metadata": {}, "source": [ "#### Metal cluster example" ] }, { "cell_type": "markdown", "id": "f2fc7b19", "metadata": {}, "source": [ "Multi-ion cofactors (iron-sulfur clusters, and similar) are supported the same way -- still no `prepare_ligands` flag, no cluster-specific API. Here a [2Fe-2S] cluster from a ferredoxin ([6E6R](https://www.rcsb.org/structure/6E6R)), each iron coordinated by two cysteines." ] }, { "cell_type": "code", "execution_count": 5, "id": "f6b7f9e6", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:36:38.975602Z", "iopub.status.busy": "2026-10-20T01:36:38.975431Z", "iopub.status.idle": "2026-10-20T01:36:40.445530Z", "shell.execute_reply": "2026-10-20T01:36:40.444777Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "n_poses=1, max_n_blocks=57\n", "metal_coordination energy: 37.32\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": [ "fes_pose_stack = pose_stack_from_file(data_path(\"metal_fixtures\", \"fes_ferredoxin_6e6r.cif.zst\"), device)\n", "fes_score_function = beta2016_score_function(device, param_db=param_db)\n", "fes_scorer = fes_score_function.render_whole_pose_scoring_module(fes_pose_stack)\n", "fes_weighted_terms = fes_scorer(fes_pose_stack.coords, sum_terms=False, apply_weights=True)\n", "print(f\"n_poses={fes_pose_stack.n_poses}, max_n_blocks={fes_pose_stack.max_n_blocks}\")\n", "print(f\"metal_coordination energy: {float(fes_weighted_terms[metal_coordination_index, 0]):.2f}\")\n", "\n", "viewer = tmol.view(fes_pose_stack)\n", "viewer.show()\n" ] }, { "cell_type": "markdown", "id": "677df319", "metadata": {}, "source": [ "### Nucleic acids" ] }, { "cell_type": "markdown", "id": "43ad8084", "metadata": {}, "source": [ "#### Handled by default" ] }, { "cell_type": "markdown", "id": "a72d61bb", "metadata": {}, "source": [ "DNA and RNA residues are likewise ordinary chemistry in the default database. An `NaTorsion` term scoring their backbone, sugar-pucker, and glycosidic torsions is included in `beta2016_score_function` by default; it's an exact zero on protein-only poses, so mixed protein/NA structures score correctly with no special handling either." ] }, { "cell_type": "markdown", "id": "9690bce8", "metadata": {}, "source": [ "#### Example" ] }, { "cell_type": "markdown", "id": "d5965205", "metadata": {}, "source": [ "[1HDD](https://www.rcsb.org/structure/1HDD), an engrailed homeodomain bound to its DNA operator, gives us both an NA torsion energy and a protein-DNA interface in one structure." ] }, { "cell_type": "code", "execution_count": 6, "id": "fc6f91d1", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:36:40.448988Z", "iopub.status.busy": "2026-10-20T01:36:40.448721Z", "iopub.status.idle": "2026-10-20T01:37:24.434718Z", "shell.execute_reply": "2026-10-20T01:37:24.433976Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "ARG is seen only at a chain terminus and N, NE, NH1, NH2 could each carry its other connection; it is read as the conventional backbone through N. A copy of the residue in a chain would settle it.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "n_poses=1, max_n_blocks=156\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": [ "na_torsion energy: 34.96\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", "
pre-relax
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
na_torsion energy: 34.96
\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. relaxed
\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(\"1HDD\")\n", "dna_pose_stack = pose_stack_from_file(io.StringIO(dna_cif_text), device)\n", "print(f\"n_poses={dna_pose_stack.n_poses}, max_n_blocks={dna_pose_stack.max_n_blocks}\")\n", "\n", "viewer = tmol.view(dna_pose_stack)\n", "viewer.show()\n", "\n", "na_score_function = beta2016_score_function(device, param_db=param_db)\n", "na_fold_forest = tmol.FoldForest.reasonable_fold_forest(dna_pose_stack)\n", "\n", "torch.manual_seed(SEED)\n", "dna_relaxed = tmol.fast_relax(\n", " dna_pose_stack.clone(), na_score_function, PackerPalette(), tmol.CartesianMoveMap(), na_fold_forest,\n", " num_repeats=1,\n", ")\n", "dna_scorer = na_score_function.render_whole_pose_scoring_module(dna_relaxed)\n", "dna_weighted_terms = dna_scorer(dna_relaxed.coords, sum_terms=False, apply_weights=True)\n", "na_torsion_index = na_score_function.all_score_types().index(ScoreType.na_torsion)\n", "print(f\"na_torsion energy: {float(dna_weighted_terms[na_torsion_index, 0].detach()):.2f}\")\n", "\n", "display(tmol.overlay_view(\n", " {\"original\": dna_pose_stack, \"relaxed\": dna_relaxed},\n", " notes={\n", " \"original\": \"pre-relax\",\n", " \"relaxed\": f\"na_torsion energy: {float(dna_weighted_terms[na_torsion_index, 0].detach()):.2f}\",\n", " },\n", "))\n" ] }, { "cell_type": "markdown", "id": "e41f9605", "metadata": {}, "source": [ "The protein-DNA interface energy reuses the block-pair scoring technique from [02_scoring_and_optimization.ipynb](02_scoring_and_optimization.ipynb), with a `polymer_type`-based mask standing in for a chain mask -- selections like this are covered in general in [05_selections.ipynb](05_selections.ipynb)." ] }, { "cell_type": "code", "execution_count": 7, "id": "f3df871b", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:37:24.451185Z", "iopub.status.busy": "2026-10-20T01:37:24.450933Z", "iopub.status.idle": "2026-10-20T01:37:24.480893Z", "shell.execute_reply": "2026-10-20T01:37:24.480188Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "protein blocks: 114, DNA blocks: 42\n", "protein-DNA interface score: -205.33\n" ] } ], "source": [ "pbt = dna_relaxed.packed_block_types\n", "protein_mask = torch.zeros((1, dna_relaxed.max_n_blocks), dtype=torch.bool)\n", "dna_mask = torch.zeros((1, dna_relaxed.max_n_blocks), dtype=torch.bool)\n", "for block in range(dna_relaxed.max_n_blocks):\n", " bt_ind = int(dna_relaxed.block_type_ind64[0, block])\n", " if bt_ind < 0:\n", " continue\n", " polymer_type = pbt.active_block_types[bt_ind].properties.polymer.polymer_type\n", " protein_mask[0, block] = polymer_type == \"amino_acid\"\n", " dna_mask[0, block] = polymer_type == \"nucleic_acid\"\n", "\n", "dna_block_scorer = na_score_function.render_block_pair_scoring_module(dna_relaxed)\n", "dna_block_pair = dna_block_scorer(dna_relaxed.coords)[0].detach()\n", "interface_score = (\n", " dna_block_pair[protein_mask[0]][:, dna_mask[0]].sum()\n", " + dna_block_pair[dna_mask[0]][:, protein_mask[0]].sum()\n", ")\n", "print(f\"protein blocks: {int(protein_mask.sum())}, DNA blocks: {int(dna_mask.sum())}\")\n", "print(f\"protein-DNA interface score: {float(interface_score):.2f}\")\n" ] }, { "cell_type": "markdown", "id": "964f7737", "metadata": {}, "source": [ "The same recipe works unchanged for RNA. [1EHT](https://www.rcsb.org/structure/1EHT) is a theophylline-binding RNA aptamer; here the interesting \"other side\" is its bound ligand rather than a second polymer chain, covered in [04_ligands_and_ncaas.ipynb](04_ligands_and_ncaas.ipynb), so we report just the NA torsion energy." ] }, { "cell_type": "code", "execution_count": 8, "id": "8fca7f30", "metadata": { "execution": { "iopub.execute_input": "2026-10-20T01:37:24.482958Z", "iopub.status.busy": "2026-10-20T01:37:24.482684Z", "iopub.status.idle": "2026-10-20T01:37:50.108679Z", "shell.execute_reply": "2026-10-20T01:37:50.108026Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "G 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": "stderr", "output_type": "stream", "text": [ "Unrecognized 3lc TEP\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "n_poses=1, max_n_blocks=33\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": [ "na_torsion energy: 62.66\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", "
pre-relax
\n", "
\n", "

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

\n", "
\n", "\n", "
\n", "
\n", "
na_torsion energy: 62.66
\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. relaxed
\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": [ "rna_cif_text = fetch_cif(\"1EHT\")\n", "rna_pose_stack = pose_stack_from_file(io.StringIO(rna_cif_text), device)\n", "print(f\"n_poses={rna_pose_stack.n_poses}, max_n_blocks={rna_pose_stack.max_n_blocks}\")\n", "\n", "viewer = tmol.view(rna_pose_stack)\n", "viewer.show()\n", "\n", "torch.manual_seed(SEED)\n", "rna_relaxed = tmol.fast_relax(\n", " rna_pose_stack.clone(), na_score_function, PackerPalette(), tmol.CartesianMoveMap(),\n", " tmol.FoldForest.reasonable_fold_forest(rna_pose_stack), num_repeats=1,\n", ")\n", "rna_scorer = na_score_function.render_whole_pose_scoring_module(rna_relaxed)\n", "rna_weighted_terms = rna_scorer(rna_relaxed.coords, sum_terms=False, apply_weights=True)\n", "print(f\"na_torsion energy: {float(rna_weighted_terms[na_torsion_index, 0].detach()):.2f}\")\n", "\n", "display(tmol.overlay_view(\n", " {\"original\": rna_pose_stack, \"relaxed\": rna_relaxed},\n", " notes={\n", " \"original\": \"pre-relax\",\n", " \"relaxed\": f\"na_torsion energy: {float(rna_weighted_terms[na_torsion_index, 0].detach()):.2f}\",\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 }