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}