1use crate::nuclidedata::index::{NAME,SYMBOL,SYMBOL_INDEX};
2use crate::mmodel::mass_model;
3use crate::constant::*;
4use crate::traits::{ChemElement};
5use crate::nuclidedata::spinparity::SPIN_PARITY;
6
7
8
9
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13pub struct Nuclide {
14 idx: usize,
15}
16
17impl std::fmt::Display for Nuclide {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 let (z, n) = self.isotope();
20 write!(f, "{}-{}", SYMBOL[z - 1], n)
21 }
22}
23
24impl std::str::FromStr for Nuclide{
25
26 type Err = &'static str;
27
28 fn from_str(input: &str) -> Result<Self,Self::Err>{
29 match Nuclide::new(input){
30 Some(x) => Ok(x),
31 None => Err("Parsing error"),
32 }
33 }
34
35 }
36
37impl Nuclide {
38
39pub fn new(input: &str) -> Option<Nuclide> {
48 let [symbol, nucleons_str]: [&str; 2] = input.split('-')
49 .collect::<Vec<&str>>()
50 .try_into()
51 .ok()?;
52
53 let nucleons = nucleons_str.parse::<usize>().ok()?;
54
55 let z_offset = SYMBOL.iter().position(|&element_str| element_str == symbol)?;
56 let (start, a_min, a_max) = SYMBOL_INDEX[z_offset];
57 (a_min..=a_max).contains(&nucleons)
58 .then_some(Nuclide {idx: start + nucleons - a_min})
59 }
60
61 pub fn from_nucleons_unchecked(protons: usize, neutrons: usize) -> Self{
64 let (start, a_min, _) = SYMBOL_INDEX[protons - 1];
65 let a = protons + neutrons;
66 Nuclide {idx: start + a - a_min}
67 }
68
69 pub fn from_nucleons(protons: usize, neutrons: usize) -> Option<Self> {
72 if protons == 0 {
73 return None;
74 }
75
76 let (start, a_min, a_max) = *SYMBOL_INDEX.get(protons - 1)?;
77 let a = protons + neutrons;
78 (a_min..=a_max).contains(&a)
79 .then_some(Nuclide {idx: start + a - a_min})
80 }
81
82 pub fn assign(idx: usize) -> Self {
84 Self { idx }
85 }
86
87 pub fn change(&mut self, idx: usize) {
89 self.idx = idx;
90 }
91
92 pub fn create(z: usize, n: usize) -> (f64, f64) {
94 let b_e = mass_model(z + n, z);
95 (
96 (z as f64 * PROTONMASS + n as f64 * NEUTRONMASS) - (b_e / 931.36808885),
97 b_e,
98 )
99 }
100
101 pub fn nuclide_index(&self) -> usize {
104 self.idx
105 }
106
107 pub fn isotope(&self) -> (usize, usize) {
109 let z = self.atomic_num() as usize;
110 let (start, a_min, _) = SYMBOL_INDEX[z - 1];
111 let a = self.idx - start + a_min;
112 (z, a)
113 }
114
115 pub fn element_name(&self) -> String {
117 NAME[self.atomic_num() as usize - 1].to_string()
118 }
119
120 pub fn proton_neutron(&self) -> (usize, usize) {
122 let (z, a) = self.isotope();
123 (z, a - z)
124 }
125
126 pub fn neutron_separation(&self) -> f64 {
128 let (z, n) = self.proton_neutron();
129 mass_model(z + n, z) - mass_model(z + n - 1, z)
130 }
131
132 pub fn proton_separation(&self) -> f64 {
134 let (z, n) = self.proton_neutron();
135 mass_model(z + n, z) - mass_model(z + n - 1, z - 1)
136 }
137
138 pub fn isotope_list(&self) -> Vec<Self> {
140 let proton = self.atomic_num() as usize;
141 let start = SYMBOL_INDEX[proton - 1].0;
142 let delta = SYMBOL_INDEX[proton - 1].2 - SYMBOL_INDEX[proton - 1].1;
143 let mut n_vector = vec![];
144 for i in 0..delta + 1 {
145 n_vector.push(Nuclide::assign(start + i))
146 }
147 n_vector
148 }
149
150 pub fn mirror(&self) -> Option<Self> {
152 let (z, n) = self.proton_neutron();
153 Nuclide::from_nucleons(n, z)
154 }
155 pub fn list() -> Vec<Self> {
167 (0..NUCLIDE_COUNT)
168 .map(Nuclide::assign)
169 .collect::<Vec<Self>>()
170 }
171
172 pub fn isobar_list(&self) -> Vec<Self> {
174 let table = Nuclide::list();
175 let mut isobars = vec![];
176 let a = self.proton_neutron().0 + self.proton_neutron().1;
177 for i in table{
178 let (z, n) = i.proton_neutron();
179 if (z + n) == a {
180 isobars.push(i)
181 }
182 }
183 isobars
184 }
185
186 pub fn isotone_list(&self) -> Vec<Self> {
188 let n = self.proton_neutron().1;
189
190 let mut n_vector = vec![];
191 for (idx, el) in SYMBOL_INDEX.iter().enumerate() {
192 let n_lo = el.1 - (idx + 1);
193 let n_hi = el.2 - (idx + 1);
194 if n >= n_lo && n <= n_hi {
195 n_vector.push(Nuclide::from_nucleons_unchecked(idx + 1, n))
196 }
197 }
198 n_vector
199 }
200
201 pub fn spin_parity(&self) -> (i8, i8) {
203 SPIN_PARITY[self.idx]
204 }
205
206
207
208}
209