Last active
November 10, 2025 08:20
-
-
Save orbeckst/e53813df6aa5b7d7a10910b2cad1cb43 to your computer and use it in GitHub Desktop.
Example for how to add a custom TopologyAttribute (for secondary structure) in MDAnalysis
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import numpy as np | |
| import MDAnalysis as mda | |
| # (1) define the custom SecondaryStructure attribute | |
| from MDAnalysis.core.topologyattrs import ResidueAttr | |
| # Just *defining* this class registers it with MDAnalysis | |
| # and makes "ss" available for Universe.add_TopologyAttr() | |
| class SecondaryStructure(ResidueAttr): | |
| """Per-reside secondary structure identifier. | |
| - H: helix (any) | |
| - E: sheet | |
| - -: other/no structure | |
| """ | |
| attrname = "ss" | |
| singular = "ss" | |
| per_object = "residue" | |
| dtype = "U1" | |
| @staticmethod | |
| def _gen_initial_values(na, nr, ns): | |
| # initialize with "-" for each residue | |
| return np.full(nr, "-", dtype="U1") | |
| # load your universe from file "PDB"; here we are using a test file | |
| # but you just use your own filename and remove the next line | |
| from MDAnalysisTests.datafiles import PDB # REMOVE WHEN USING YOUR OWN FILE | |
| u = mda.Universe(PDB) | |
| # add the custom TopologyAttr; automatically initialize with '-' | |
| u.add_TopologyAttr("ss") | |
| # (2) get secondary structure for the protein and set the 'ss' attribute | |
| from MDAnalysis.analysis.dssp import DSSP | |
| protein = u.select_atoms("protein") | |
| # set protein secondary structure attribute to value from first frame | |
| dssp = DSSP(protein).run(frames=[0]) | |
| protein.residues.ss = dssp.results.dssp[0] | |
| # (3) We can now select on the new attribute 'ss': | |
| helices = u.select_atoms("ss == H") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "id": "6878b31f-3192-4ff1-96c1-f24deb6a2a1d", | |
| "metadata": {}, | |
| "source": [ | |
| "# Selecting on secondary structure in MDAnalysis" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "1f66be0e-ffc7-48f3-9257-c2e11654c30f", | |
| "metadata": {}, | |
| "source": [ | |
| "1. compute secondary structure using DSSP\n", | |
| "2. create a new TopologyAttribute *ss*\n", | |
| "3. assign DSSP results to *ss*\n", | |
| "4. select" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "e1f2e817-6e01-4e27-a5b8-84def1fec60f", | |
| "metadata": {}, | |
| "source": [ | |
| "## Packages" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 68, | |
| "id": "2c5e00da-0876-4877-b89a-db79d60db53e", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "2.10.0\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "import MDAnalysis as mda\n", | |
| "from MDAnalysisTests.datafiles import PDB\n", | |
| "\n", | |
| "print(mda.__version__)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 69, | |
| "id": "4f29b1e2-ebff-4c5f-801a-34352cb05591", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from MDAnalysis.analysis.dssp import DSSP" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "9733ebba-392a-470b-8430-a85b74bb963a", | |
| "metadata": {}, | |
| "source": [ | |
| "## Universe" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 72, | |
| "id": "592e676c-df34-4745-81c6-2b3128e57bfc", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "u = mda.Universe(PDB)\n", | |
| "u.guess_TopologyAttrs(context='default', to_guess=['elements'])\n", | |
| "\n", | |
| "protein = u.select_atoms(\"protein\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "ff6c568d-a692-4a31-a3e5-7bfba86f5357", | |
| "metadata": {}, | |
| "source": [ | |
| "## Secondary structure with DSSP" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "aaf0d0fe-27d2-4bec-91a8-3ad8a312ffb6", | |
| "metadata": {}, | |
| "source": [ | |
| "Secondary structure for initial frame (even if it's the only frame, `frames=[0]` is safe):" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 73, | |
| "id": "e1de17d4-7968-4846-b32c-3727fb7df61e", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stderr", | |
| "output_type": "stream", | |
| "text": [ | |
| "/Users/oliver/miniforge3/envs/mda314/lib/python3.14/site-packages/MDAnalysis/analysis/base.py:562: UserWarning: Reader has no dt information, set to 1.0 ps\n", | |
| " self.times[idx] = ts.time\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "dssp = DSSP(protein).run(frames=[0])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "a174f418-e077-4508-ac49-19c6d61af1c4", | |
| "metadata": {}, | |
| "source": [ | |
| "Get assignment for frame 0:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 74, | |
| "id": "dc425b7f-d542-43cc-b21d-2da0bbe39012", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array(['-', '-', 'E', 'E', 'E', 'E', 'E', '-', '-', '-', '-', '-', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', '-', '-',\n", | |
| " 'E', 'E', 'E', '-', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', '-', '-', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', '-', '-', '-', '-', '-', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', '-', '-', '-', 'H', 'H', 'H', '-',\n", | |
| " '-', '-', 'E', 'E', 'E', 'E', '-', '-', '-', '-', '-', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', '-', '-', '-', '-', '-',\n", | |
| " 'E', 'E', 'E', 'E', 'E', 'E', '-', '-', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', '-', '-', 'E', 'E', '-', '-', '-', '-', '-', '-',\n", | |
| " '-', '-', 'E', 'E', '-', '-', '-', 'E', '-', '-', '-', '-', '-',\n", | |
| " '-', 'E', '-', '-', '-', '-', '-', '-', 'E', '-', '-', '-', '-',\n", | |
| " 'H', 'H', 'H', '-', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', '-', '-', '-', 'E', 'E', 'E', 'E',\n", | |
| " 'E', 'E', '-', '-', '-', '-', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', '-'], dtype='<U1')" | |
| ] | |
| }, | |
| "execution_count": 74, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "dssp.results.dssp[0]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "b297e95a-d2ee-40c4-b655-581373a7e2c6", | |
| "metadata": {}, | |
| "source": [ | |
| "## New TopologyAttr\n", | |
| "\n", | |
| "attributes are attached to a topology – see [User Guide: The Topology System](https://userguide.mdanalysis.org/stable/topology_system.html)\n", | |
| "\n", | |
| "secondary structure is a *residue* level attribute\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 75, | |
| "id": "bb8725eb-5c44-4aa3-a00f-690de23d53e3", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from MDAnalysis.core.topologyattrs import ResidueAttr\n", | |
| "import numpy as np" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 76, | |
| "id": "1a0e5639-9e03-472d-a662-42962dc33ab4", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Just *defining* this class registers it with MDAnalysis\n", | |
| "# and makes \"ss\" available for Universe.add_TopologyAttr()\n", | |
| "\n", | |
| "class SecondaryStructure(ResidueAttr):\n", | |
| " \"\"\"Per-residue secondary structure identifier.\n", | |
| "\n", | |
| " - H: helix (any)\n", | |
| " - E: sheet\n", | |
| " - -: other/no structure\n", | |
| " \"\"\"\n", | |
| " attrname = \"ss\"\n", | |
| " singular = \"ss\"\n", | |
| " per_object = \"residue\"\n", | |
| " dtype = \"U1\"\n", | |
| "\n", | |
| " @staticmethod\n", | |
| " def _gen_initial_values(na, nr, ns):\n", | |
| " # initialize with \"-\" for each residue\n", | |
| " return np.full(nr, \"-\", dtype=\"U1\")\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "dce3fc75-bca0-4738-845e-00b2eadfcfd9", | |
| "metadata": {}, | |
| "source": [ | |
| "We add it with" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 77, | |
| "id": "d2ff31a7-5836-428d-b60b-b6d6254c5566", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "u.add_TopologyAttr(\"ss\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "95b976a0-faf3-4ec0-a49f-a613c67bfcc1", | |
| "metadata": {}, | |
| "source": [ | |
| "Now fill the attribute with the DSSP results" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 78, | |
| "id": "cace0816-43d5-4678-a6e4-420785d1a815", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "protein.residues.ss = dssp.results.dssp[0]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "edbd99cf-6aab-4732-a76b-2f591a86afb0", | |
| "metadata": {}, | |
| "source": [ | |
| "## Select all helices" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 79, | |
| "id": "77c84824-a85f-4017-ba6f-f0d58b2d8e1c", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "helices = u.select_atoms(\"ss H\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 80, | |
| "id": "afcbd323-53fe-4003-8276-5ee00d431da2", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array(['H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',\n", | |
| " 'H', 'H', 'H', 'H', 'H', 'H'], dtype='<U1')" | |
| ] | |
| }, | |
| "execution_count": 80, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "helices.residues.ss" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "4d781db4-147a-4426-92f0-4ced12661e0f", | |
| "metadata": {}, | |
| "source": [ | |
| "## Select beta sheets" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 66, | |
| "id": "26b62d71-b7a2-431d-85c1-88dc87c062cd", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "sheets = u.residues[u.residues.ss == \"E\"].atoms" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "4520fc57-0b9d-4ebc-b1be-03526ef1ac4b", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "091f10a4-8194-4847-802a-d03eb0ceecac", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python 3 (ipykernel)", | |
| "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.14.0" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment