1use crate::error::NereidsError;
9use crate::types::Isotope;
10
11pub fn element_symbol(z: u32) -> Option<&'static str> {
15 endf_mat::element_symbol(z)
16}
17
18pub fn element_name(z: u32) -> Option<&'static str> {
20 endf_mat::element_name(z)
21}
22
23pub fn parse_isotope_str(s: &str) -> Option<Isotope> {
27 let parts: Vec<&str> = s.split('-').collect();
28 if parts.len() != 2 {
29 return None;
30 }
31 let symbol = parts[0].trim();
32 let a: u32 = parts[1].trim().parse().ok()?;
33 let z = symbol_to_z(symbol)?;
34 Isotope::new(z, a).ok()
35}
36
37pub fn symbol_to_z(symbol: &str) -> Option<u32> {
39 endf_mat::symbol_to_z(symbol)
40}
41
42pub fn natural_abundance(isotope: &Isotope) -> Option<f64> {
47 endf_mat::natural_abundance(isotope.z(), isotope.a())
48}
49
50pub fn natural_isotopes(z: u32) -> Vec<(Isotope, f64)> {
55 endf_mat::natural_isotopes(z)
56 .into_iter()
57 .filter_map(|(a, frac)| Isotope::new(z, a).ok().map(|iso| (iso, frac)))
58 .collect()
59}
60
61pub fn known_isotopes(z: u32) -> Vec<Isotope> {
67 endf_mat::known_isotopes(z)
68 .into_iter()
69 .filter_map(|a| Isotope::new(z, a).ok())
70 .collect()
71}
72
73pub fn has_endf_evaluation(z: u32, a: u32) -> bool {
75 endf_mat::has_endf_evaluation(z, a)
76}
77
78pub fn za_from_isotope(isotope: &Isotope) -> u32 {
80 endf_mat::za(isotope.z(), isotope.a())
81}
82
83pub fn isotope_from_za(za: u32) -> Result<Isotope, NereidsError> {
91 Isotope::new(endf_mat::z_from_za(za), endf_mat::a_from_za(za))
92}
93
94pub fn isotope_to_string(isotope: &Isotope) -> String {
96 match element_symbol(isotope.z()) {
97 Some(sym) => format!("{}-{}", sym, isotope.a()),
98 None => format!("Z{}-{}", isotope.z(), isotope.a()),
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_element_symbol() {
108 assert_eq!(element_symbol(1), Some("H"));
109 assert_eq!(element_symbol(92), Some("U"));
110 assert_eq!(element_symbol(26), Some("Fe"));
111 assert_eq!(element_symbol(0), Some("n"));
112 }
113
114 #[test]
115 fn test_parse_isotope_str() {
116 let u238 = parse_isotope_str("U-238").unwrap();
117 assert_eq!(u238.z(), 92);
118 assert_eq!(u238.a(), 238);
119
120 let fe56 = parse_isotope_str("Fe-56").unwrap();
121 assert_eq!(fe56.z(), 26);
122 assert_eq!(fe56.a(), 56);
123
124 assert!(parse_isotope_str("invalid").is_none());
125 }
126
127 #[test]
128 fn test_za_roundtrip() {
129 let iso = Isotope::new(92, 238).unwrap();
130 let za = za_from_isotope(&iso);
131 assert_eq!(za, 92238);
132 let back = isotope_from_za(za).unwrap();
133 assert_eq!(back, iso);
134 }
135
136 #[test]
137 fn test_isotope_from_za_natural_element_returns_error() {
138 let result = isotope_from_za(26000);
140 assert!(result.is_err());
141 assert!(result.unwrap_err().to_string().contains("must be positive"));
142 }
143
144 #[test]
145 fn test_isotope_from_za_invalid_z_greater_than_a() {
146 let result = isotope_from_za(999001);
149 assert!(result.is_err());
150 assert!(result.unwrap_err().to_string().contains("cannot exceed"));
151 }
152
153 #[test]
154 fn test_natural_abundance() {
155 let u238 = Isotope::new(92, 238).unwrap();
156 let abund = natural_abundance(&u238).unwrap();
157 assert!((abund - 0.992742).abs() < 1e-6);
158 }
159
160 #[test]
161 fn test_natural_isotopes() {
162 let fe_isotopes = natural_isotopes(26);
163 assert_eq!(fe_isotopes.len(), 4);
164 let total: f64 = fe_isotopes.iter().map(|(_, a)| a).sum();
165 assert!((total - 1.0).abs() < 0.001);
166 }
167
168 #[test]
169 fn test_isotope_to_string() {
170 let iso = Isotope::new(92, 238).unwrap();
171 assert_eq!(isotope_to_string(&iso), "U-238");
172 }
173
174 #[test]
175 fn test_known_isotopes_plutonium() {
176 let pu = known_isotopes(94);
177 assert!(!pu.is_empty());
178 assert!(pu.iter().any(|iso| iso.a() == 239));
179 }
180
181 #[test]
182 fn test_known_isotopes_synthetic_element() {
183 assert!(natural_isotopes(43).is_empty());
185 let tc = known_isotopes(43);
186 assert!(!tc.is_empty());
187 }
188
189 #[test]
190 fn test_known_isotopes_superset_of_natural() {
191 let natural: Vec<Isotope> = natural_isotopes(26)
192 .into_iter()
193 .map(|(iso, _)| iso)
194 .collect();
195 let known = known_isotopes(26);
196 for iso in &natural {
197 assert!(known.contains(iso));
198 }
199 assert!(known.iter().any(|iso| iso.a() == 55));
201 }
202
203 #[test]
204 fn test_has_endf_evaluation() {
205 assert!(has_endf_evaluation(94, 239));
206 assert!(!has_endf_evaluation(94, 999));
207 }
208}