voronota-ltr

Crates.iovoronota-ltr
lib.rsvoronota-ltr
version0.1.0
created_at2026-01-22 12:47:53.515115+00
updated_at2026-01-22 12:47:53.515115+00
descriptionVoronota-LT is an alternative version of Voronota for constructing tessellation-derived atomic contact areas and volumes.
homepagehttps://www.voronota.com/expansion_lt
repositoryhttps://github.com/mlund/voronota-ltr
max_upload_size
id2061475
size999,720
Mikael Lund (mlund)

documentation

README

CI

Voronota-LT in Rust

Experimental, native Rust port of voronota-lt originally written in C++. Replaces the previous bindings crate. Computes radical Voronoi tessellation of atomic balls constrained inside a solvent-accessible surface. Outputs inter-atom contact areas, solvent accessible surface (SAS) areas, and volumes.

Features

  • Pure, safe Rust
  • Basic radical tessellation (stateless)
  • Updateable tessellation for incremental updates (stateful)
  • Periodic boundaries
  • Per atom SASA and volume
  • Groupings to avoid internal contacts
  • Parallel processing using Rayon - see benchmarks below
  • PDB, mmCIF, and XYZR input formats with auto-detection
  • Unit-tests and benchmarks carried over from the C++ side
  • Based on Voronota-LT v1.1.479 (f5ad92de4e9723ab767db3e5035c0e7532f31595)

Installation

The port can be used either as a library for other projects, or as a basic CLI tool:

cargo install voronota-ltr

Examples

use voronota_ltr::{Ball, Results, compute_tessellation};

let balls = vec![
    Ball::new(0.0, 0.0, 0.0, 1.5),
    Ball::new(3.0, 0.0, 0.0, 1.5),
    Ball::new(1.5, 2.5, 0.0, 1.5),
];

let result = compute_tessellation(&balls, 1.4, None, None);

// Per-ball SAS areas and volumes (indexed by ball)
let sas_areas: Vec<f64> = result.sas_areas();
let volumes: Vec<f64> = result.volumes();

// Total SAS area
let total_sas: f64 = result.total_sas_area();

// Detailed contact and cell data
for contact in &result.contacts {
    println!("Contact {}-{}: area={:.2}", contact.id_a, contact.id_b, contact.area);
}

With periodic boundary conditions

use voronota_ltr::{Ball, PeriodicBox, compute_tessellation};

let balls = vec![Ball::new(0.0, 0.0, 0.0, 1.5)];
let pbox = PeriodicBox::from_corners((0.0, 0.0, 0.0), (10.0, 10.0, 10.0));

let result = compute_tessellation(&balls, 1.4, Some(&pbox), None);

Updateable tessellation

For simulations where only a few spheres change position each step, UpdateableTessellation provides efficient incremental updates:

use voronota_ltr::{Ball, UpdateableTessellation};

let mut balls = vec![
    Ball::new(0.0, 0.0, 0.0, 1.0),
    Ball::new(2.0, 0.0, 0.0, 1.0),
    Ball::new(4.0, 0.0, 0.0, 1.0),
];

// Create with backup support for undo
let mut tess = UpdateableTessellation::with_backup();
tess.init(&balls, 1.0, None);

// Move first sphere
balls[0].x += 0.1;
tess.update_with_changed(&balls, &[0]);  // Only recompute affected contacts

// Get results
let summary = tess.summary();
println!("Total contacts: {}", summary.contacts.len());

// Undo last update
tess.restore();

CLI

Supports PDB, mmCIF, and XYZR input formats (auto-detected from extension or content):

# PDB input
voronota-ltr -i structure.pdb --probe 1.4

# mmCIF input
voronota-ltr -i structure.cif --probe 1.4

# XYZR input (last 4 columns: x y z radius)
voronota-ltr -i atoms.xyzr --probe 1.4

# Save JSON output to file
voronota-ltr -i structure.pdb -o results.json

# Quiet mode (suppress log messages)
voronota-ltr -i structure.pdb -q -o results.json

# Exclude heteroatoms (HETATM records)
voronota-ltr -i structure.pdb --exclude-heteroatoms

# Include hydrogen atoms (excluded by default)
voronota-ltr -i structure.pdb --include-hydrogens

# Custom radii file (format: residue atom radius per line)
voronota-ltr -i structure.pdb --radii-file custom_radii.txt

# With periodic boundary conditions
voronota-ltr -i atoms.xyzr --periodic-box-corners 0 0 0 100 100 100

Loading JSON output in Python

import json

with open("results.json") as f:
    data = json.load(f)

# Per-ball data (indexed by ball)
sas_areas = data["sas_areas"]
volumes = data["volumes"]

# Totals
total_sasa = data["total_sas_area"]
total_volume = data["total_volume"]
total_contact_area = data["total_contact_area"]

# Contact details
for contact in data["contacts"]:
    print(f"Contact {contact['id_a']}-{contact['id_b']}: area={contact['area']:.2f}")

Benchmarks

Run benchmarks with cargo bench. Performance on Apple M4 processor (10 cores) - the speedup is relative to single threaded runs:

Dataset Balls C++ (OpenMP) Rust (Rayon) C++ Speedup Rust Speedup
balls_cs_1x1 100 179 µs 79 µs 0.4x 0.8x
balls_2zsk 3545 14 ms 12 ms 5.1x 5.9x
balls_3dlb 9745 38 ms 30 ms 5.0x 5.9x

License

MIT License

Copyright (c) 2026 Kliment Olechnovic and Mikael Lund

Commit count: 87

cargo fmt