use crate::nuclidedata::index::{NAME,SYMBOL,SYMBOL_INDEX};
use crate::mmodel::mass_model;
use crate::constant::*;
use crate::traits::{ChemElement};
use crate::nuclidedata::spinparity::SPIN_PARITY;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Nuclide {
idx: usize,
}
impl std::fmt::Display for Nuclide {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (z, n) = self.isotope();
write!(f, "{}-{}", SYMBOL[z - 1], n)
}
}
impl std::str::FromStr for Nuclide{
type Err = &'static str;
fn from_str(input: &str) -> Result<Self,Self::Err>{
match Nuclide::new(input){
Some(x) => Ok(x),
None => Err("Parsing error"),
}
}
}
impl Nuclide {
pub fn new(input: &str) -> Option<Nuclide> {
let [symbol, nucleons_str]: [&str; 2] = input.split('-')
.collect::<Vec<&str>>()
.try_into()
.ok()?;
let nucleons = nucleons_str.parse::<usize>().ok()?;
let z_offset = SYMBOL.iter().position(|&element_str| element_str == symbol)?;
let (start, a_min, a_max) = SYMBOL_INDEX[z_offset];
(a_min..=a_max).contains(&nucleons)
.then_some(Nuclide {idx: start + nucleons - a_min})
}
pub fn from_nucleons_unchecked(protons: usize, neutrons: usize) -> Self{
let (start, a_min, _) = SYMBOL_INDEX[protons - 1];
let a = protons + neutrons;
Nuclide {idx: start + a - a_min}
}
pub fn from_nucleons(protons: usize, neutrons: usize) -> Option<Self> {
if protons == 0 {
return None;
}
let (start, a_min, a_max) = *SYMBOL_INDEX.get(protons - 1)?;
let a = protons + neutrons;
(a_min..=a_max).contains(&a)
.then_some(Nuclide {idx: start + a - a_min})
}
pub fn assign(idx: usize) -> Self {
Self { idx }
}
pub fn change(&mut self, idx: usize) {
self.idx = idx;
}
pub fn create(z: usize, n: usize) -> (f64, f64) {
let b_e = mass_model(z + n, z);
(
(z as f64 * PROTONMASS + n as f64 * NEUTRONMASS) - (b_e / 931.36808885),
b_e,
)
}
pub fn nuclide_index(&self) -> usize {
self.idx
}
pub fn isotope(&self) -> (usize, usize) {
let z = self.atomic_num() as usize;
let (start, a_min, _) = SYMBOL_INDEX[z - 1];
let a = self.idx - start + a_min;
(z, a)
}
pub fn element_name(&self) -> String {
NAME[self.atomic_num() as usize - 1].to_string()
}
pub fn proton_neutron(&self) -> (usize, usize) {
let (z, a) = self.isotope();
(z, a - z)
}
pub fn neutron_separation(&self) -> f64 {
let (z, n) = self.proton_neutron();
mass_model(z + n, z) - mass_model(z + n - 1, z)
}
pub fn proton_separation(&self) -> f64 {
let (z, n) = self.proton_neutron();
mass_model(z + n, z) - mass_model(z + n - 1, z - 1)
}
pub fn isotope_list(&self) -> Vec<Self> {
let proton = self.atomic_num() as usize;
let start = SYMBOL_INDEX[proton - 1].0;
let delta = SYMBOL_INDEX[proton - 1].2 - SYMBOL_INDEX[proton - 1].1;
let mut n_vector = vec![];
for i in 0..delta + 1 {
n_vector.push(Nuclide::assign(start + i))
}
n_vector
}
pub fn mirror(&self) -> Option<Self> {
let (z, n) = self.proton_neutron();
Nuclide::from_nucleons(n, z)
}
pub fn list() -> Vec<Self> {
(0..NUCLIDE_COUNT)
.map(Nuclide::assign)
.collect::<Vec<Self>>()
}
pub fn isobar_list(&self) -> Vec<Self> {
let table = Nuclide::list();
let mut isobars = vec![];
let a = self.proton_neutron().0 + self.proton_neutron().1;
for i in table{
let (z, n) = i.proton_neutron();
if (z + n) == a {
isobars.push(i)
}
}
isobars
}
pub fn isotone_list(&self) -> Vec<Self> {
let n = self.proton_neutron().1;
let mut n_vector = vec![];
for (idx, el) in SYMBOL_INDEX.iter().enumerate() {
let n_lo = el.1 - (idx + 1);
let n_hi = el.2 - (idx + 1);
if n >= n_lo && n <= n_hi {
n_vector.push(Nuclide::from_nucleons_unchecked(idx + 1, n))
}
}
n_vector
}
pub fn spin_parity(&self) -> (i8, i8) {
SPIN_PARITY[self.idx]
}
}