Skip to content

Repository files navigation

volumizer discovers and annotates occluded volumes in proteins including:

  • cavities: Volumes within a protein that do not make any contacts with bulk solvent. Useful for e.g. carrying cargo.
  • pockets: Volumes on the protein surface that make a single contact with bulk solvent. Useful for e.g. ligand binding or catalysis.
  • pores: Volumes connecting two bulk solvent surfaces. Useful for e.g. filtering solutes.

The core classifier also identifies hubs (volumes connecting more than two bulk-solvent surfaces), but CLI-written annotation outputs omit hubs by default. Pass --include-hubs to restore hub emission in CLI JSON/CIF outputs.

Example Identified Volume

Here is shown an example pore identified and annotated (red) in PDB 4JPN (green). image

The same pore volume annotated (red) shown as a slice through the protein (grey). image

The dataframe output shows volume/dimensinos of each occluded volume in the structure

id type volume x y z
0 0 pore 38286.0 108.221 38.574 36.310
1 0 pocket 189.0 8.214 5.628 0.000
2 1 pocket 162.0 6.635 3.843 2.840
3 2 pocket 162.0 7.298 4.002 2.557
4 3 pocket 162.0 10.757 2.701 0.000
5 4 pocket 135.0 6.000 6.000 0.000

Installation

Install from PyPI:

pip install volumizer

This installs the portable Python wheel and CLI (volumizer). Optional local C helpers and the optional Rust native extension are not bundled in the default wheel.

Linux Clone Quickstart

On a fresh Linux machine, the shortest repository-local setup is:

  1. Install uv, Node.js + npm, and optionally build-essential if you want the local C helpers.
  2. Run:
bash scripts/bootstrap_linux.sh
  1. Verify the CLI:
uv run --python 3.11 volumizer --version
  1. Build and serve the local gallery from a run summary:
./gallery /path/to/run.summary.json

The bootstrap script installs the Python dependencies, compiles the optional local C helpers when cc is available, installs the npm packages, and downloads the Playwright Chromium build needed for thumbnail rendering. Use --no-gallery to skip the Node/Playwright setup or --with-native to also build the optional Rust backend.

Developing the Volumizer Package

biotite==0.37.0 does not support Python 3.14. Use Python 3.10 or 3.11 with uv.

If you want to develop the package:

  1. uv python install 3.11
  2. uv sync --python 3.11 --group test
  3. bash src/compile_c_libs.sh
  4. uv run --python 3.11 pytest

Optional native scaffold (Phase 1):

  1. uv sync --python 3.11 --group test --group native
  2. Install Rust toolchain (cargo, rustc).
  3. uv run --python 3.11 maturin develop --manifest-path native/Cargo.toml

Backend selection:

  • VOLUMIZER_BACKEND=python (default): use Python implementation (and local C helpers only when src/voxel.so and src/fib_sphere.so are present)
  • VOLUMIZER_BACKEND=auto: use native if importable, otherwise Python mode
  • VOLUMIZER_BACKEND=native: require native module and fail if unavailable

Usage

CLI

The package now includes a CLI entrypoint: volumizer.

Print CLI version:

volumizer --version

Analyze a local structure file:

Control load behavior with assembly policy (biological default, asymmetric, auto):

volumizer analyze --input my_structure.cif --output-dir out --assembly-policy auto
volumizer analyze --input my_structure.cif --output-dir out

Analyze one PDB ID (downloaded from RCSB as CIF):

volumizer analyze --pdb-id 4JPN --output-dir out

Analyze cluster representatives for an identity threshold:

volumizer cluster --cluster-identity 30 --max-structures 25 --output-dir out

Resume a previous run (skip entries that already have both output files):

volumizer cluster --cluster-identity 30 --max-structures 25 --output-dir out --resume

Preview selection/filtering without downloading structures or running analysis:

volumizer cluster --cluster-identity 30 --max-structures 25 --output-dir out --dry-run

Write a reproducible manifest for selected cluster representatives:

volumizer cluster --cluster-identity 30 --max-structures 25 --output-dir out --write-manifest out/cluster.manifest.json

Replay that exact set later:

volumizer analyze --manifest out/cluster.manifest.json --output-dir out-rerun

Replay only failed structures from a previous summary:

volumizer analyze --from-summary out/run.summary.json --only failed --output-dir out-retry

Emit a replayable manifest for failures during a run:

volumizer cluster --cluster-identity 30 --output-dir out --failures-manifest out/failed.manifest.json

Retry only those failures later:

volumizer analyze --manifest out/failed.manifest.json --output-dir out-retry

Enable periodic human-readable progress + ETA updates:

volumizer cluster --cluster-identity 30 --max-structures 100 --output-dir out --jobs 8 --progress-interval 15

Deterministically shard a large cluster run across workers (example: shard 1 of 4):

volumizer cluster --cluster-identity 30 --output-dir out --num-shards 4 --shard-index 1

Include hubs in CLI-written annotation outputs:

volumizer analyze --input my_structure.cif --output-dir out --include-hubs

Inspect metadata cache entries:

volumizer cache inspect --metadata-cache out/entry_metadata_cache.json

Clear only negative cache entries (e.g. permanent 404 metadata failures):

volumizer cache clear-negative --metadata-cache out/entry_metadata_cache.json

Legacy compatibility:

  • Existing flag-only invocations still work (volumizer --input ..., volumizer --cluster-identity ...) and are auto-routed to analyze or cluster.

Cluster filtering defaults:

  • Methods: X-ray + cryo-EM (RCSB method labels X-RAY DIFFRACTION and ELECTRON MICROSCOPY)
  • Override with --cluster-method (repeatable), e.g. --cluster-method xray --cluster-method neutron
  • Disable method filtering with --cluster-allow-all-methods
  • Optional resolution gate with --cluster-max-resolution, e.g. --cluster-max-resolution 3.0
  • Residue-count gate defaults to --cluster-max-residues 20000
  • Parallel workers for metadata/download + analysis with --jobs, e.g. --jobs 8
  • Deterministic run partitioning with --num-shards <N> --shard-index <K> to split representative lists across machines/jobs (0 <= K < N; both flags required)
  • Retry transient network errors with --retries and --retry-delay
  • Structure-load policy for analyze and cluster can be set with --assembly-policy biological|asymmetric|auto
  • Cluster metadata cache defaults to <output-dir>/entry_metadata_cache.json; override with --metadata-cache or disable with --no-metadata-cache
  • Cache stores both successful entry metadata and permanent metadata failures (e.g. HTTP 404) to avoid repeated failed fetches on later runs
  • Checkpointing defaults to <output-dir>/run.checkpoint.json; override with --checkpoint or disable with --no-checkpoint
  • Structured progress events can be written with --progress-jsonl <path> (JSON Lines format)
  • Human-readable progress + ETA updates are emitted every --progress-interval seconds (default 30, set <= 0 to disable)
  • Cluster runs can emit selected/rejected structure manifests with --write-manifest <path>
  • Analyze runs can replay any manifest with --manifest <path> (entries support pdb_id and/or input_path)
  • Analyze runs can also replay structures from a prior run.summary.json using --from-summary <path> --only failed|skipped|planned|all
  • Any run can emit a failed-entry manifest via --failures-manifest <path> for direct retry with analyze --manifest <path>
  • CLI-written annotation outputs omit hubs by default; pass --include-hubs to include them

CLI outputs:

  • <label>.annotated.cif: cleaned input plus volume pseudo-atoms (hubs omitted by default)
  • <label>.annotation.json: web-friendly volume data payload (hubs omitted by default)
  • run.summary.json: run configuration and per-structure status
  • <path from --failures-manifest> (optional): replayable manifest containing failed structure inputs

Local Gallery Web App

For local browsing of modest indexed datasets, the repository now includes a small FastAPI app plus a static gallery UI.

Install the extra local web dependencies:

uv sync --python 3.11 --group test --group web
npm ci
npm run gallery:install-browser

Build the gallery index from a run summary:

uv run --python 3.11 python scripts/build_gallery_index.py --summary out/run.summary.json --db data/gallery.db

Render cached x/y/z thumbnails for indexed hits:

uv run --python 3.11 python scripts/render_gallery_thumbnails.py --db data/gallery.db --render-root data/renders --jobs 4 --render-backend auto

Both the thumbnail renderer and the browser UI now use locally installed Mol* assets from node_modules/molstar by default, so the gallery no longer depends on runtime CDN fetches. Override the asset location with MOLSTAR_ASSET_ROOT if needed. Optional profiling output is available via --timing-jsonl <path>. The default --axis-render-mode compatibility path reuses one Mol* browser/page/viewer context while loading axis-specific atom-filtered structures for x/y/z; --axis-render-mode fast keeps one unclipped structure loaded and captures full-structure x/y/z views.

Serve the local gallery:

uv run --python 3.11 python scripts/serve_gallery.py --db data/gallery.db --host 127.0.0.1 --port 8000

Then open http://127.0.0.1:8000.

For the shortest end-to-end path from the repository root, use:

./gallery /path/to/run.summary.json

If Chromium is not installed yet, run bash scripts/bootstrap_linux.sh or npm run gallery:install-browser. If you want to browse the indexed data before rendering thumbnails, use:

./gallery /path/to/run.summary.json --skip-thumbnails

If you want to retry rows whose thumbnails previously failed to render without rerendering successful rows, use:

./gallery /path/to/run.summary.json --include-failed

If you want to rerender thumbnails for one or more specific gallery entries, use:

uv run --python 3.11 python scripts/rerender_gallery_targets.py --db data/gallery.db 9bq2

Current web-app scope:

  • GET /api/runs, GET /api/hits, GET /api/hits/{structure_id}, GET /api/hits/{structure_id}/viewer-data
  • static browser UI for filtering and browsing hits
  • file-serving endpoints for annotated CIF, annotation JSON, and cached PNG thumbnails
  • Mol* detail viewer embedded in the browser page

Using the test file tests/pdbs/4jpn.pdb try out the following:

Volumize a PDB and Save the Volumized PDB and DataFrame

Performing end-to-end loading, cleaning, volumizing, and saving is done with a single convenience function:

from volumizer import volumizer

volumizer.volumize_pdb_and_save("my_input.pdb", "volumized_pdb.pdb", "volumized_df.json")

Load a PDB as a Biotite Structure, Clean, Volumize, and Save Output

If you want access to the individual end-products: volume dataframe, the input structure after cleaning, and the structure of the volumes:

from volumizer import volumizer, pdb

pdb_structure = pdb.load_structure("my_input.pdb")
volumes_df, cleaned_structure, volumes_structure = volumizer.volumize_structure(pdb_structure)

# take the cleaned input and annotated volumes and convert them to a PDB format string and then save
#  modify the `deliminator` to suit your visualization preference
#  e.g. the default "END" allows Pymol to load the resulting PDB file as two separate objects, one for the cleaned input, and one for the volumes
pdb_lines = pdb.make_volumized_pdb_lines([cleaned_structure, volumes_structure], deliminator="END")
pdb.save_pdb_lines(pdb_lines, "volumized_pdb.pdb")

volumes_df.to_json("volumized_df.json")

Changing Resolution, and Beyond

If you are interested in additional control over the volumizing method:

  • the resolution of the voxels can be changed
  • cleaning can be skipped

Note: the default voxel resolution is 3.0 Angstroms, which gives sensible results in the majority of cases. Higher resolutions especially < 2.0 Angstroms will often find small paths through a protein structure, making e.g. cavities look like pores, etc. Lower resolutions are faster to compute, but may begin to under-estimate the true volume of solvent occluded elements.

Note: by default all residues that make L- or D- peptide bonds are retained through cleaning (e.g. Non-canonicals are kept, even if they are heteroatoms in PDB structure). By constrast all non-covalently attached residues are removed. Currently glycan residues are also removed as they make non-peptide bonds, below is shown an example of how would would retain glycans.

from volumizer import volumizer, pdb, utils

utils.set_resolution(2.0)

pdb_structure = pdb.load_structure("my_input.pdb")
cleaned_structure = volumizer.prepare_pdb_structure(pdb_structure)  # skip this if you want to keep the exact input structure
volumes_df, volumes_structure = volumizer.annotate_structure_volumes(cleaned_structure)

# take the cleaned input and annotated volumes and convert them to a PDB format string and then save
#  modify the `deliminator` to suit your visualization preference
#  e.g. the default "END" allows Pymol to load the resulting PDB file as two separate objects, one for the cleaned input, and one for the volumes
pdb_lines = pdb.make_volumized_pdb_lines([cleaned_structure, volumes_structure], deliminator="END")
pdb.save_pdb_lines(pdb_lines, "volumized_pdb.pdb")

volumes_df.to_json("volumized_df.json")

How It Works

volumizer identifies hydrated volumes in a protein structure that are not fully solvent exposed, e.g. a binding pocket. It then computes the volume and dimensions of these and outputs that information along with an annotated version of the input PDB showing where these volumes are (which can be visualized in e.g. Pymol, Chimerax, etc.).

Identifying hydrated volumes

  1. A large voxel-grid is built around the atoms of the protein or other structure supplied.
  2. All voxels within a van der Waals radius of a protein or other atom is flagged as being non-solvent
  3. Remaining solvent atoms are then broken into two groups: bulk solvent and occluded volumes This is done by tracing a vector along each ordinal axis from a given voxel and if 2 or more of these vectors would cross a non-solvent voxel, then the query voxel is identified as an occluded volume to be further analyzed otherwise it is considered bulk solvent The intention is to identify points on the grid that are outside the protein as bulk solvent
  4. All occluded volume voxels are then grouped into a number of continuous volumes
  5. For each continous volume the number of distinct surfaces that contact bulk solvent voxels is computed and used to indicate the volume type: 0 surfaces interacting with solvent: cavity 1 surface interacting with solvent: pocket 2 surfaces interacting with solvent: pore 3+ surfaces interacting with solvent: hub

Annotations

Pandas DataFrame

Annotations are given as a pandas data frame saved as a .json file. The annotation lists all hydrated volumes ordered by total volume, giving the type of volume, and dimensions.

For CLI-written outputs, hub rows are omitted by default unless --include-hubs is passed. The underlying library classifier still recognizes hubs.

PDB File

The input PDB file will be annotated by adding atoms to represent the hydrated volumes. The ATOM entries contain several points of information about the volume from which they come:

Type of volume: the residue name encodes the type of volume in 3-letter code

  • OCC for occluded
  • CAV for cavity
  • POK for pocket
  • POR for pore
  • HUB for hub when hub emission is enabled

Surface of the hydrated volume that interacts with bulk solvent: this is indicated by a B-factor of 50.0, whereas the remainder of the volume (that does not interact with the bulk solvent) has a value of 0.0.

All atoms of a particular volume are grouped under the same residue number.

About

A tool to find cavities, pockets, and pores within a protein structure at variable resolution

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages