ak_core/utils/
atom_info.rs

1use once_cell::sync::Lazy;
2use std::num::{ParseFloatError, ParseIntError};
3
4use crate::AtomicNumber;
5
6const DATA: &str = include_str!("atom_info.csv");
7
8pub struct AtomInfo {
9    pub number: AtomicNumber,
10    pub symbol: String,
11    pub covalent_radius: f64,
12    pub vdw_radius: f64,
13}
14
15pub struct PeriodicTable {
16    info_arr: [AtomInfo; 110],
17}
18
19impl PeriodicTable {
20    pub fn from_data() -> Result<Self, String> {
21        let mut info = Vec::new();
22
23        for (i, line) in DATA.lines().enumerate() {
24            if i == 0 || line.trim().is_empty() {
25                continue; // skip header/empty
26            }
27            let parts: Vec<&str> = line.split(',').collect();
28            if parts.len() != 4 {
29                return Err(format!("bad row {}: {}", i + 1, line));
30            }
31            let number_u8: u8 = parts[0]
32                .parse::<u8>()
33                .map_err(|e: ParseIntError| e.to_string())?;
34            let number: AtomicNumber = AtomicNumber::new(number_u8).expect("Invalid atomic number");
35            let symbol = parts[1].to_string();
36            let covalent_radius: f64 = parts[2]
37                .parse()
38                .map_err(|e: ParseFloatError| e.to_string())?;
39            let vdw_radius: f64 = parts[3]
40                .parse()
41                .map_err(|e: ParseFloatError| e.to_string())?;
42
43            info.push(AtomInfo {
44                number,
45                symbol,
46                covalent_radius,
47                vdw_radius,
48            });
49        }
50
51        let info_arr: [AtomInfo; 110] = info
52            .try_into()
53            .map_err(|v: Vec<AtomInfo>| format!("expected 110 rows, got {}", v.len()))?;
54
55        Ok(Self { info_arr })
56    }
57
58    pub fn get(&self, z: AtomicNumber) -> &AtomInfo {
59        self.info_arr
60            .get((z.get() - 1) as usize)
61            .expect("Mismatched index due")
62    }
63
64    pub fn get_by_symbol(&self, symbol: &str) -> &AtomInfo {
65        self.info_arr
66            .iter()
67            .find(|f| f.symbol == symbol)
68            .expect("Unknown element symbol")
69    }
70}
71
72pub static PERIODIC_TABLE: Lazy<PeriodicTable> =
73    Lazy::new(|| PeriodicTable::from_data().expect("Failed to build periodic table"));
74
75#[cfg(test)]
76mod test {
77
78    use crate::AtomicNumber;
79    use crate::utils::atom_info::PERIODIC_TABLE;
80
81    #[test]
82    fn get_from_table() {
83        let hydrogen_data = PERIODIC_TABLE.get(AtomicNumber::new(1).unwrap());
84        assert_eq!(hydrogen_data.number.get(), 1);
85        assert_eq!(hydrogen_data.symbol, String::from("H"));
86    }
87
88    #[test]
89    fn get_by_symbol() {
90        let hydrogen_data = PERIODIC_TABLE.get_by_symbol("H");
91        assert_eq!(hydrogen_data.number.get(), 1);
92        assert_eq!(hydrogen_data.symbol, String::from("H"));
93    }
94}