Skip to content

ENH: Speedup the DSSP constructor - #5503

Closed
BradyAJohnston wants to merge 1 commit into
MDAnalysis:developfrom
BradyAJohnston:enh-dssp-construction
Closed

BradyAJohnston wants to merge 1 commit into
MDAnalysis:developfrom
BradyAJohnston:enh-dssp-construction

Conversation

@BradyAJohnston

Copy link
Copy Markdown
Member

The DSSP() constructor's select_atoms was called on every single residue creating many atom groups and being a performance bottleneck especially on larger systems. Creating multiple atom groups has been replaced by numpy arrays providing a significant speedup for the constructor (~50x in my benchmarks).

Changes made in this Pull Request:

  • Numpy array operations instead of atom groups for constructor
  • _get_coords is now one gather instead of a per-residue loop that ran every frame
  • Some general cleanup of comments not needing to be inside brackets and unnecessary set(map(lambda...))).
  • Updated typing hints, using X | Y instead

LLM / AI generated code disclosure

LLMs or other AI-powered tools (beyond simple IDE use cases) were used in this contribution: no

I did have Claude write the benchmarking script (attached at bottom) but not the code or tests.

PR Checklist

  • Issue raised/referenced?
  • Tests updated/added?
  • Documentation updated/added?
  • package/CHANGELOG file updated?
  • Is your name in package/AUTHORS? (If it is not, add it!)
  • I have read and understand the current AI Policy
  • LLM/AI disclosure was updated.

Developers Certificate of Origin

I certify that I can submit this code contribution as described in the Developer Certificate of Origin, under the MDAnalysis LICENSE.


Benchmarks

Residues Construction: develop → branch Run, guess_hydrogens=True Run, guess_hydrogens=False
214 10.3 → 0.33 ms (31x) 2.40 → 1.43 (1.7x) 2.68 → 1.43 (1.9x)
856 42 → 0.90 ms (46x) 25.3 → 21.4 (1.2x) 26.7 → 21.4 (1.25x)
3,424 167 → 3.2 ms (52x) 330 → 316 (1.04x) 335 → 315 (1.07x)

Script

Details
"""Time DSSP construction and full runs, with and without guessed hydrogens.

Run it on develop and on this branch: the timings show the change, and the
checksums of the results must be the same on both.
"""

import hashlib
import time

import MDAnalysis as mda
import numpy as np
from MDAnalysis.analysis.dssp import DSSP
from MDAnalysis.coordinates.memory import MemoryReader
from MDAnalysisTests.datafiles import TPR, XTC

# of the N-terminal hydrogens (H1-H3) only H1 is named: guess_hydrogens=False
# accepts one hydrogen per residue after the first, so the later copies'
# N-termini would fail with all three
HYDROGEN_NAME = "H HN H1"


def best(fn, repeats):
    """Shortest time of ``repeats`` calls, and the last result"""
    times = []
    for _ in range(repeats):
        start = time.perf_counter()
        out = fn()
        times.append(time.perf_counter() - start)
    return min(times), out


def copies_of(protein, copies):
    """``copies`` of ``protein`` as one system, 100 A apart along x, with
    all frames of its trajectory"""
    merged = mda.Merge(*[protein] * copies)
    frames = np.array([protein.positions for _ in protein.universe.trajectory])
    coords = np.concatenate([frames] * copies, axis=1)
    coords[:, :, 0] += np.repeat(np.arange(copies) * 100.0, protein.n_atoms)
    merged.load_new(coords.astype(np.float32), format=MemoryReader)
    return merged


protein = mda.Universe(TPR, XTC).select_atoms("protein")
for copies in (1, 4, 16):
    u = copies_of(protein, copies)
    n_res, n_frames = u.residues.n_residues, u.trajectory.n_frames
    print(f"{n_res} residues, {n_frames} frames")
    for guess in (True, False):
        kwargs = dict(guess_hydrogens=guess, hydrogen_name=HYDROGEN_NAME)
        t_init, _ = best(lambda: DSSP(u, **kwargs), 5)
        t_run, run = best(lambda: DSSP(u, **kwargs).run(), 3)
        digest = hashlib.sha1(run.results.dssp_ndarray.tobytes()).hexdigest()
        print(
            f"  guess_hydrogens={guess!s:5}: construction {1e3 * t_init:7.2f} "
            f"ms, construction and run {1e3 * t_run / n_frames:7.2f} ms per "
            f"frame (results {digest[:12]})"
        )

@BradyAJohnston BradyAJohnston added this to the 2.12 milestone Oct 20, 2026
Comment thread package/CHANGELOG
DSSP by porting upstream PyDSSP 0.9.1 fix (Issue #4913)

Enhancements
* DSSP finds the hydrogens of all residues with one selection instead of

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be moved once 2.11 is released and this PR rebased.

@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 MDAnalysis | 🛠️ Build #35273111 | 📁 Comparing a7f7048 against latest (6cd1f7e)

  🔍 Preview build  

3 files changed
± index.html
± documentation_pages/analysis/wbridge_analysis.html
± _modules/MDAnalysis/analysis/dssp/dssp.html

@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.92%. Comparing base (6cd1f7e) to head (a7f7048).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #5503   +/-   ##
========================================
  Coverage    93.91%   93.92%           
========================================
  Files          184      184           
  Lines        22644    22656   +12     
  Branches      3219     3218    -1     
========================================
+ Hits         21267    21279   +12     
  Misses         917      917           
  Partials       460      460           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@BradyAJohnston BradyAJohnston mentioned this pull request Oct 20, 2026
7 tasks done
@BradyAJohnston

Copy link
Copy Markdown
Member Author

Closing in favor of #5504

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant