knuckles_parse/records/
crystal.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4#[cfg(feature = "python")]
5use pyo3::prelude::*;
6
7#[cfg(feature = "python")]
8use knuckles_macro::pydefault;
9
10/// Represents a CRYST1 record containing crystallographic unit cell parameters.
11///
12/// This record specifies the unit cell parameters and space group for the crystal structure.
13/// It is essential for understanding the crystallographic context of the atomic coordinates.
14///
15/// # Fields
16///
17/// - `a`, `b`, `c`: Unit cell dimensions in Ångströms
18/// - `alpha`, `beta`, `gamma`: Unit cell angles in degrees
19/// - `space_group`: Space group symbol
20/// - `z`: Number of polymeric chains in the unit cell
21///
22/// # Example
23///
24/// ```rust
25/// use knuckles_parse::records::crystal::CrystalRecord;
26///
27/// let line = "CRYST1   52.000   58.600   61.900  90.00  90.00  90.00 P 21 21 21    8";
28/// let crystal = CrystalRecord::from(line);
29///
30/// assert_eq!(crystal.a, 52.0);
31/// assert_eq!(crystal.space_group, "P 21 21 21");
32/// ```
33#[derive(Debug, Clone)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
36#[cfg_attr(feature = "python", pydefault)]
37pub struct CrystalRecord {
38    /// Unit cell dimension a in Ångströms
39    pub a: f32,
40    /// Unit cell dimension b in Ångströms
41    pub b: f32,
42    /// Unit cell dimension c in Ångströms
43    pub c: f32,
44    /// Unit cell angle alpha in degrees
45    pub alpha: f32,
46    /// Unit cell angle beta in degrees
47    pub beta: f32,
48    /// Unit cell angle gamma in degrees
49    pub gamma: f32,
50    /// Space group symbol
51    pub space_group: String,
52    /// Number of polymeric chains in the unit cell
53    pub z: u32,
54}
55
56impl CrystalRecord {
57    /// Create a new CrystalRecord by parsing a CRYST1 line.
58    ///
59    /// Parses the unit cell parameters and space group information from the fixed-width
60    /// fields in the CRYST1 record.
61    ///
62    /// # Arguments
63    ///
64    /// * `str` - A CRYST1 line from a PDB file
65    ///
66    /// # Returns
67    ///
68    /// A new `CrystalRecord` with parsed crystallographic parameters.
69    pub fn new(str: &str) -> CrystalRecord {
70        CrystalRecord {
71            a: str[6..15].trim().parse().unwrap_or_default(),
72            b: str[15..24].trim().parse().unwrap_or_default(),
73            c: str[24..33].trim().parse().unwrap_or_default(),
74            alpha: str[33..40].trim().parse().unwrap_or_default(),
75            beta: str[40..47].trim().parse().unwrap_or_default(),
76            gamma: str[47..54].trim().parse().unwrap_or_default(),
77            space_group: str[55..66].trim().to_string(),
78            z: str[66..70].trim().parse().unwrap_or_default(),
79        }
80    }
81}
82
83impl From<&str> for CrystalRecord {
84    fn from(str: &str) -> Self {
85        CrystalRecord::new(str)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    #[test]
93    fn parse_crystal_line_test() {
94        const LINE: &str =
95            "CRYST1   52.000   58.600   61.900  90.00  90.00  90.00 P 21 21 21    8          ";
96        let record = CrystalRecord::new(LINE);
97        assert_eq!(record.a, 52.0);
98        assert_eq!(record.b, 58.6);
99        assert_eq!(record.c, 61.9);
100        assert_eq!(record.alpha, 90.0);
101        assert_eq!(record.beta, 90.0);
102        assert_eq!(record.gamma, 90.0);
103        assert_eq!(record.space_group, "P 21 21 21");
104        assert_eq!(record.z, 8);
105    }
106}