Skip to content

Instantly share code, notes, and snippets.

@orbeckst
Last active November 10, 2025 08:20
Show Gist options
  • Select an option

  • Save orbeckst/e53813df6aa5b7d7a10910b2cad1cb43 to your computer and use it in GitHub Desktop.

Select an option

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
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")
Display the source blob
Display the rendered blob
Raw
{
"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