Nuclide/nstruct/
core.rs

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/// Efficient representation of nuclide
12#[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
39/// Initializes a new Nuclide from  a string representation.
40/// Currently the input must be of the form {Symbol}-{Nucleon count}
41/// ```
42///         use ::Nuclide::Nuclide;
43///          
44///         let u235 = Nuclide::new("U-235").unwrap();
45///         assert_eq!(u235.to_string(),"U-235");
46/// ```
47    pub 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    /// In : proton, neutron
62    /// Out: Nuclide  
63    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    /// In: proton, neutron 
70    /// Returns None if the Nuclide doesn't exist
71    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    /// Construct a nuclide from the unique index. Avoid direct use as no checks are performed to ensure that it is valid
83    pub fn assign(idx: usize) -> Self {
84        Self { idx }
85    }
86    
87   /// Transforms a nuclide from the unique index.
88    pub fn change(&mut self, idx: usize) {
89        self.idx = idx;
90    }
91    
92    /// Returns the approximate mass and binding energy of a nuclide, theorectical or real, using the DZ-10 mass model.
93    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    /// Returns the underlying unique value. Can be used in conjunction with "assign" and "change" to rapidly create or
102    /// convert nuclides without decay
103    pub fn nuclide_index(&self) -> usize {
104        self.idx
105    }
106
107    /// Returns the atomic number and the nucleon count
108    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    ///Returns the element name.     
116    pub fn element_name(&self) -> String {
117        NAME[self.atomic_num() as usize - 1].to_string()
118    }
119
120    ///Returns the proton and neutron count
121    pub fn proton_neutron(&self) -> (usize, usize) {
122        let (z, a) = self.isotope();
123        (z, a - z)
124    }
125
126    /// Approximate neutron separation energy
127    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    /// Approximate proton separation energy
133    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    /// returns a vector of all isotopes of the element
139    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    /// Returns the nuclide (if it exists) that has swapped proton-neutron count
151    pub fn mirror(&self) -> Option<Self> {
152        let (z, n) = self.proton_neutron();
153        Nuclide::from_nucleons(n, z)
154    }
155    /*
156      isobar = permutations of z+1,n-1 and z-1,n+1
157
158    Iterate through the symbollist
159
160
161    z-(z-i) n+(z-i)
162
163    check that n+(z-i) is valid for the point z-(z-i)
164      */
165    /// Produces an iterator of all nuclides sorted by atomic number, e.g all hydrogen isotopes, all helium isotopes, ...
166    pub fn list() -> Vec<Self> {
167        (0..NUCLIDE_COUNT)
168            .map(Nuclide::assign)
169            .collect::<Vec<Self>>()
170    }
171    
172    /// Produces a list of all nuclides that share the same atomic number as the selected nuclide
173    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    /// Produces a list of nuclides that share the same number of neutrons
187    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    ///Returns the isospin and parity in the form of a i8 pair, one of which is negatively signed for - parity
202    pub fn spin_parity(&self) -> (i8, i8) {
203        SPIN_PARITY[self.idx]
204    }
205    
206
207    
208}
209