MDAnalysis Integration
High-performance SASA analysis compatible with MDAnalysis' AnalysisBase pattern.
Installation#
pip install MDAnalysis
# or
uv add MDAnalysisImport#
from zsasa.mdanalysis import SASAAnalysis, compute_sasaOverview#
3.4x faster than mdsasa-bolt on real MD data with controllable thread count.
SASAAnalysis#
Class-based API following MDAnalysis conventions.
class SASAAnalysis:
"""SASA analysis for MDAnalysis trajectories."""
def __init__(
self,
universe_or_atomgroup: Universe | AtomGroup,
select: str = "all",
) -> NoneParameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
universe_or_atomgroup |
Universe or AtomGroup |
required | MDAnalysis object to analyze |
select |
str |
"all" |
Atom selection string |
Attributes:
| Attribute | Type | Description |
|---|---|---|
atomgroup |
AtomGroup |
The atoms being analyzed |
results |
Results |
Results object (after run()) |
n_frames |
int |
Number of frames analyzed |
times |
NDArray[float64] |
Frame times |
frames |
NDArray[int64] |
Frame indices |
run()#
def run(
self,
start: int = 0,
stop: int | None = None,
step: int = 1,
*,
probe_radius: float = 1.4,
n_points: int = 960,
algorithm: Literal["sr", "lr"] = "sr",
n_slices: int = 20,
n_threads: int = 0,
chunk_size: int | None = None,
store_atom_areas: bool = True,
use_bitmask: bool = False,
bitmask_correction: bool = False,
bitmask_correction_coeff: float | None = None,
) -> SASAAnalysisParameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
start |
int |
0 |
First frame to analyze (not negative) |
stop |
int | None |
None |
Stop before this frame (not negative; None = run through the last frame) |
step |
int |
1 |
Step between frames (at least 1; 0 or a negative value raises ValueError before any frame is read) |
probe_radius |
float |
1.4 |
Probe radius in Å |
n_points |
int |
960 |
Test points per atom (SR) |
algorithm |
"sr" or "lr" |
"sr" |
Algorithm to use |
n_slices |
int |
20 |
Slices per atom (LR) |
n_threads |
int |
0 |
Threads (0 = auto) |
chunk_size |
int | None |
None |
Frames per native batch (must be positive). None processes all selected frames in one batch; a smaller value lowers peak memory |
store_atom_areas |
bool |
True |
Keep per-atom SASA in results.atom_area. With False, only the totals and per-residue sums are kept and results.atom_area is None |
use_bitmask |
bool |
False |
Use bitmask LUT optimization (SR only, n_points must be 1..1024) |
bitmask_correction |
bool |
False |
Experimental exposed-fraction correction for bitmask quantization bias; requires use_bitmask=True |
bitmask_correction_coeff |
float | None |
None |
Override the experimental correction coefficient (None uses library default) |
Returns: self for method chaining.
Results#
After calling run(), results are available in the results attribute:
| Attribute | Type | Description |
|---|---|---|
atom_area |
NDArray[float32] | None |
Per-atom SASA, shape (n_frames, n_atoms); None when run() was called with store_atom_areas=False |
residue_area |
NDArray[float32] |
Per-residue SASA, shape (n_frames, n_residues) |
total_area |
NDArray[float32] |
Total SASA, shape (n_frames,) |
mean_total_area |
float |
Mean total SASA across all frames |
Units: All SASA values are in Ų (matching MDAnalysis conventions).
Radii#
The radius of each atom comes from the MDAnalysis van der Waals table (MDAnalysis.guesser.tables.vdwradii; MDAnalysis.topology.tables.vdwradii in MDAnalysis releases before 2.8, which zsasa falls back to) and the atom's element. An element missing from that table (for example Fe, which MDAnalysis does not list) gets 2.0 Å.
The element is the element attribute when the topology has one (for example a PDB file with an element column). Without it (for example GRO files, or PDB files without an element column), it is inferred from the atom type and name together with the residue name:
- A name or type that starts with
CL,BR,FE,ZN,MG,MN,NI,CU,LIorALis that element (CL1is chlorine,ZNis zinc). - A two-letter symbol that is also an ordinary atom name (
CA,CD,NA,HG,SE,CO, ...) is that element only when the residue is the ion itself (atomCAin residueCA, atomNAin residueNA+). The alpha carbonCAofALA, theCDofGLNand theNAnitrogen ofHEMstay carbon, carbon and nitrogen, andHGofSERstays hydrogen. - CHARMM ion names (
SOD,POT,CLA,CAL,CES) give sodium, potassium, chlorine, calcium and caesium. - The type and the name are both read, and a two-letter reading from either wins: a parser may guess the type of an ion from its name and get it wrong (MDAnalysis types the calcium ion
CAas carbon).
The atom mass is not used. MDAnalysis guesses masses from the same types when a topology has none, so a guessed mass cannot confirm a guessed type.
Anything else takes the first letter of the type, or the name when there is no type, and carbon when there is neither.
Example#
import MDAnalysis as mda
from zsasa.mdanalysis import SASAAnalysis
# Load trajectory
u = mda.Universe("topology.pdb", "trajectory.xtc")
# Analyze protein only
sasa = SASAAnalysis(u, select="protein")
sasa.run(start=0, stop=100, step=10)
# Access results
print(f"Mean SASA: {sasa.results.mean_total_area:.2f} Ų")
print(f"Per-frame: {sasa.results.total_area}")
print(f"Per-residue shape: {sasa.results.residue_area.shape}")
print(f"Per-atom shape: {sasa.results.atom_area.shape}")compute_sasa (function)#
Convenience function for simple use cases.
def compute_sasa(
universe_or_atomgroup: Universe | AtomGroup,
*,
select: str = "all",
start: int = 0,
stop: int | None = None,
step: int = 1,
probe_radius: float = 1.4,
n_points: int = 960,
algorithm: Literal["sr", "lr"] = "sr",
n_slices: int = 20,
n_threads: int = 0,
chunk_size: int | None = None,
mode: Literal["atom", "residue", "total"] = "atom",
use_bitmask: bool = False,
bitmask_correction: bool = False,
bitmask_correction_coeff: float | None = None,
) -> NDArray[np.float32]chunk_size has the same meaning as in run(). Per-atom areas are only kept for mode="atom", so "residue" and "total" need less memory.
Returns: SASA values in Ų.
| Mode | Shape |
|---|---|
"atom" |
(n_frames, n_atoms) |
"residue" |
(n_frames, n_residues) |
"total" |
(n_frames,) |
Example:
from zsasa.mdanalysis import compute_sasa
import MDAnalysis as mda
u = mda.Universe("topology.pdb", "trajectory.xtc")
# Simple one-liner
total_sasa = compute_sasa(u, select="protein", mode="total")Key Advantages#
- Controllable parallelism: Set exact thread count with
n_threads - Higher precision: f64 by default
- Efficient scaling: 5.2x speedup from 1→8 threads
- MDAnalysis compatible: Works with selections, AtomGroups