rust-sasa-python 0.2.1__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -138,7 +138,7 @@ jobs:
138
138
  name: Release
139
139
  runs-on: ubuntu-latest
140
140
  needs: [linux, windows, macos, sdist, test]
141
- if: github.ref_name != 'radical'
141
+ if: startsWith(github.ref, 'refs/tags/v')
142
142
  steps:
143
143
  - uses: actions/download-artifact@v4
144
144
  - name: Publish to PyPI
@@ -659,8 +659,9 @@ dependencies = [
659
659
 
660
660
  [[package]]
661
661
  name = "rust-sasa-python"
662
- version = "0.2.1"
662
+ version = "0.2.3"
663
663
  dependencies = [
664
+ "nalgebra",
664
665
  "pdbtbx",
665
666
  "pyo3",
666
667
  "rust-sasa",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rust-sasa-python"
3
- version = "0.2.1"
3
+ version = "0.2.3"
4
4
  edition = "2024"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -12,3 +12,4 @@ crate-type = ["cdylib"]
12
12
  pdbtbx = "0.12.0"
13
13
  pyo3 = "0.25.1"
14
14
  rust-sasa = "0.3.2"
15
+ nalgebra = "0.33.2"
@@ -84,6 +84,78 @@ atom_results = sasa.calculate_atom_sasa("protein.pdb")
84
84
  chain_results = sasa.calculate_chain_sasa("protein.pdb")
85
85
  ```
86
86
 
87
+ ### Low-level Functions
88
+
89
+ For advanced use cases where you need to calculate SASA for custom atom sets:
90
+
91
+ **`calculate_sasa_internal(atoms_in: list[tuple[tuple[float, float, float], float, int]], probe_radius: float, n_points: int) -> list[float]`**
92
+
93
+ Calculate SASA for a set of atoms directly without PDB file parsing.
94
+
95
+ **Parameters:**
96
+ - `atoms_in`: List of tuples, each containing:
97
+ - Atom coordinates as `(x, y, z)` tuple of floats
98
+ - Atom radius as float
99
+ - Atom index as int
100
+ - `probe_radius`: Probe sphere radius in Ångströms
101
+ - `n_points`: Number of points for surface sampling
102
+
103
+ **Returns:**
104
+ - List of SASA values (floats) corresponding to each input atom
105
+
106
+ **Example:**
107
+ ```python
108
+ import rust_sasa_python as sasa
109
+
110
+ # Define atoms: [(coordinates, radius, index), ...]
111
+ atoms = [
112
+ ((0.0, 0.0, 0.0), 1.7, 0), # Carbon atom
113
+ ((2.0, 0.0, 0.0), 1.2, 1), # Hydrogen atom
114
+ ((0.0, 2.0, 0.0), 1.55, 2), # Nitrogen atom
115
+ ]
116
+
117
+ # Calculate SASA
118
+ sasa_values = sasa.calculate_sasa_internal(atoms, probe_radius=1.4, n_points=100)
119
+ print(f"SASA values: {sasa_values}")
120
+ ```
121
+
122
+ **`calculate_sasa_internal_at_residue_level(atoms_in: list[tuple[tuple[float, float, float], float, int]], probe_radius: float, n_points: int) -> list[ResidueResult]`**
123
+
124
+ Calculate SASA at the residue level for a set of atoms directly without PDB file parsing. This function groups atoms by residue ID and calculates the average SASA per atom within each residue.
125
+
126
+ **Parameters:**
127
+ - `atoms_in`: List of tuples, each containing:
128
+ - Atom coordinates as `(x, y, z)` tuple of floats
129
+ - Atom radius as float
130
+ - Residue ID as int (used to group atoms into residues)
131
+ - `probe_radius`: Probe sphere radius in Ångströms
132
+ - `n_points`: Number of points for surface sampling
133
+
134
+ **Returns:**
135
+ - List of `ResidueResult` objects with:
136
+ - `chain_id`: Always "UNK" (unknown)
137
+ - `residue_name`: Always "UNK" (unknown)
138
+ - `residue_number`: The residue ID from input
139
+ - `sasa`: Average SASA per atom in the residue
140
+
141
+ **Example:**
142
+ ```python
143
+ import rust_sasa_python as sasa
144
+
145
+ # Define atoms with residue grouping
146
+ # Residue 1: two atoms, Residue 2: one atom
147
+ atoms = [
148
+ ((0.0, 0.0, 0.0), 1.7, 1), # Carbon in residue 1
149
+ ((1.5, 0.0, 0.0), 1.2, 1), # Hydrogen in residue 1
150
+ ((0.0, 3.0, 0.0), 1.55, 2), # Nitrogen in residue 2
151
+ ]
152
+
153
+ # Calculate residue-level SASA
154
+ residues = sasa.calculate_sasa_internal_at_residue_level(atoms, probe_radius=1.4, n_points=100)
155
+ for residue in residues:
156
+ print(f"Residue {residue.residue_number}: {residue.sasa:.2f} Ų (avg per atom)")
157
+ ```
158
+
87
159
  ## Usage Examples
88
160
 
89
161
  ### Basic Usage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rust-sasa-python
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -51,7 +51,7 @@ print(f"Polar SASA: {result.polar:.2f}")
51
51
  print(f"Non-polar SASA: {result.non_polar:.2f}")
52
52
  ```
53
53
 
54
- See [DOCS](DOCS.md) for more information and API reference.
54
+ See [DOCS](https://github.com/maxall41/rust-sasa-python/blob/main/DOCS.md) for more information and API reference.
55
55
 
56
56
  ## Contributing
57
57
 
@@ -35,7 +35,7 @@ print(f"Polar SASA: {result.polar:.2f}")
35
35
  print(f"Non-polar SASA: {result.non_polar:.2f}")
36
36
  ```
37
37
 
38
- See [DOCS](DOCS.md) for more information and API reference.
38
+ See [DOCS](https://github.com/maxall41/rust-sasa-python/blob/main/DOCS.md) for more information and API reference.
39
39
 
40
40
  ## Contributing
41
41
 
@@ -132,3 +132,20 @@ def calculate_chain_sasa(pdb_path: str) -> List[Chain]:
132
132
  List[Chain]: List of chain-level SASA results
133
133
 
134
134
  """
135
+
136
+ def calculate_sasa_internal(
137
+ atoms_in: list[tuple[tuple[float, float, float], float, int]],
138
+ probe_radius: float,
139
+ n_points: int,
140
+ ) -> list[float]:
141
+ """Calculate SASA for a set of atoms.
142
+
143
+ Args:
144
+ atoms_in: List of atom coordinates, radii, and indices
145
+ probe_radius: Probe radius
146
+ n_points: Number of points on the sphere
147
+
148
+ Returns:
149
+ List[float]: List of SASA values
150
+
151
+ """
@@ -1,8 +1,11 @@
1
+ use nalgebra::Point3;
1
2
  use pdbtbx;
2
3
  use pyo3::exceptions::PyRuntimeError;
3
4
  use pyo3::prelude::*;
4
5
  use rust_sasa::options::SASAOptions;
5
- use rust_sasa::{AtomLevel, ChainLevel, ProteinLevel, ResidueLevel};
6
+ use rust_sasa::{Atom, SASAProcessor, calculate_sasa_internal as calculate_sasa_internal_internal};
7
+ use rust_sasa::{AtomLevel, ChainLevel, ProteinLevel, ResidueLevel}; // As in long long
8
+ use std::collections::HashMap;
6
9
 
7
10
  #[pyclass]
8
11
  #[derive(Clone)]
@@ -33,7 +36,7 @@ pub struct Residue {
33
36
  #[pyo3(get)]
34
37
  pub residue_name: String,
35
38
  #[pyo3(get)]
36
- pub residue_number: i32,
39
+ pub residue_number: u32,
37
40
  #[pyo3(get)]
38
41
  pub sasa: f32,
39
42
  }
@@ -193,7 +196,7 @@ impl SASACalculator {
193
196
  residue_results.push(Residue {
194
197
  chain_id: result.chain_id,
195
198
  residue_name: result.name,
196
- residue_number: result.serial_number as i32,
199
+ residue_number: result.serial_number as u32,
197
200
  sasa: result.value,
198
201
  });
199
202
  }
@@ -291,6 +294,62 @@ pub fn calculate_chain_sasa(pdb_path: String) -> PyResult<Vec<Chain>> {
291
294
  SASACalculator::new(pdb_path).calculate_chain()
292
295
  }
293
296
 
297
+ #[pyfunction]
298
+ pub fn calculate_sasa_internal(
299
+ atoms_in: Vec<((f32, f32, f32), f32, usize)>,
300
+ probe_radius: f32,
301
+ n_points: usize,
302
+ ) -> PyResult<Vec<f32>> {
303
+ let atoms: Vec<Atom> = atoms_in
304
+ .into_iter()
305
+ .map(|(pos, radius, index)| Atom {
306
+ position: Point3::new(pos.0, pos.1, pos.2),
307
+ id: index,
308
+ parent_id: None,
309
+ radius,
310
+ })
311
+ .collect();
312
+ Ok(calculate_sasa_internal_internal(
313
+ atoms.as_slice(),
314
+ probe_radius,
315
+ n_points,
316
+ true,
317
+ ))
318
+ }
319
+
320
+ #[pyfunction]
321
+ fn calculate_sasa_internal_at_residue_level(
322
+ atoms_in: Vec<((f32, f32, f32), f32, usize)>,
323
+ probe_radius: f32,
324
+ n_points: usize,
325
+ ) -> PyResult<Vec<Residue>> {
326
+ let atom_sasa = calculate_sasa_internal(atoms_in.clone(), probe_radius, n_points).unwrap();
327
+
328
+ let mut residue_groups: HashMap<usize, Vec<f32>> = HashMap::new();
329
+
330
+ for (i, atom) in atoms_in.iter().enumerate() {
331
+ let parent_id = atom.2;
332
+ residue_groups
333
+ .entry(parent_id)
334
+ .or_insert_with(Vec::new)
335
+ .push(atom_sasa[i]);
336
+ }
337
+
338
+ let mut residue_sasa = Vec::new();
339
+ for (parent_id, sasa_values) in residue_groups {
340
+ let local_sum = sasa_values.iter().sum::<f32>();
341
+
342
+ residue_sasa.push(Residue {
343
+ chain_id: "UNK".to_string(),
344
+ residue_name: "UNK".to_string(),
345
+ residue_number: parent_id as u32,
346
+ sasa: local_sum / sasa_values.len() as f32,
347
+ });
348
+ }
349
+
350
+ Ok(residue_sasa)
351
+ }
352
+
294
353
  #[pymodule]
295
354
  fn rust_sasa_python(m: &Bound<'_, PyModule>) -> PyResult<()> {
296
355
  m.add_class::<SASACalculator>()?;
@@ -303,6 +362,11 @@ fn rust_sasa_python(m: &Bound<'_, PyModule>) -> PyResult<()> {
303
362
  m.add_function(wrap_pyfunction!(calculate_residue_sasa, m)?)?;
304
363
  m.add_function(wrap_pyfunction!(calculate_atom_sasa, m)?)?;
305
364
  m.add_function(wrap_pyfunction!(calculate_chain_sasa, m)?)?;
365
+ m.add_function(wrap_pyfunction!(calculate_sasa_internal, m)?)?;
366
+ m.add_function(wrap_pyfunction!(
367
+ calculate_sasa_internal_at_residue_level,
368
+ m
369
+ )?)?;
306
370
 
307
371
  Ok(())
308
372
  }
@@ -84,7 +84,7 @@ wheels = [
84
84
 
85
85
  [[package]]
86
86
  name = "rust-sasa-python"
87
- version = "0.2.0"
87
+ version = "0.2.1"
88
88
  source = { editable = "." }
89
89
 
90
90
  [package.optional-dependencies]