use crate::Nuclide;
use crate::traits::ChemElement;
use crate::nuclidedata::index::SYMBOL;
use crate::nuclidedata::elemental::*;
use crate::nuclidedata::ionization::IONIZATION_ENERGIES;
#[derive(Clone)]
pub struct NuclideFraction {
pub fractions: Vec<(Nuclide, f64)>
}
impl std::fmt::Display for NuclideFraction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let symbol = self.first().element_name();
write!(f, "{}-[{:?}]", symbol, self.fractions)
}
}
impl NuclideFraction {
pub fn from_nucleon_fractions(protons: u8, nucleon_fractions: &[(u16, f64)]) -> Option<NuclideFraction> {
if nucleon_fractions.is_empty() {
return None;
}
nucleon_fractions.iter()
.map(|&(nucleons, abundance)| {
let p = protons.into();
let n = Into::<usize>::into(nucleons) - p;
Nuclide::from_nucleons(p, n).map(|nucl| (nucl, abundance))
})
.collect::<Option<Vec<_>>>()
.map(|fractions| NuclideFraction {fractions})
}
pub fn from_nucleon_fractions_unchecked(protons: u8, neutron_fractions: &[(u16, f64)]) -> NuclideFraction {
let fractions: Vec<_> = neutron_fractions.iter()
.map(|&(nucleons, abundance)| {
let p = protons.into();
let n = Into::<usize>::into(nucleons) - p;
let nuclide = Nuclide::from_nucleons_unchecked(p, n);
(nuclide, abundance)
})
.collect();
NuclideFraction {fractions}
}
pub fn from_natural_abundancies(protons: u8) -> NuclideFraction {
Element::from_protons(protons).abundant_nuclides()
}
fn first(&self) -> &Nuclide {
&self.fractions.first().unwrap().0
}
fn weighted_property<F: Fn(&Nuclide) -> f64>(&self, map: F) -> f64 {
if self.fractions.len() == 1 {
map(self.first())
} else {
self.fractions.iter()
.map(|(nuclide, abundancy)| abundancy * map(nuclide))
.sum()
}
}
}
impl ChemElement for NuclideFraction {
fn atomic_num(&self) -> u64 {
self.first().atomic_num()
}
fn am(&self) -> f64 {
self.weighted_property(Nuclide::am)
}
fn electron_affinity(&self) -> f64 {
self.first().electron_affinity()
}
fn ionization_energies(&self, level: usize) -> Option<f64> {
self.first().ionization_energies(level)
}
fn electronegativity(&self) -> f64 {
self.first().electronegativity()
}
fn mullikan_en(&self) -> f64 {
self.first().mullikan_en()
}
fn allen_en(&self) -> f64 {
self.first().allen_en()
}
fn pauling_en(&self) -> f64 {
self.first().pauling_en()
}
fn covalent_radii(&self, bond: usize) -> Option<f64> {
self.first().covalent_radii(bond)
}
fn ionic_radii(&self) -> f64 {
self.first().ionic_radii()
}
fn vdr_crystal(&self) -> f64 {
self.first().vdr_crystal()
}
fn vdr_isolated(&self) -> f64 {
self.first().vdr_isolated()
}
}
#[rustfmt::skip]
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum Element {
H=1, He , Li , Be , B , C , N , O , F ,
Ne , Na , Mg , Al , Si , P , S , Cl , Ar,
K , Ca , Sc , Ti , V , Cr , Mn , Fe , Co,
Ni , Cu , Zn , Ga , Ge , As , Se , Br , Kr,
Rb , Sr , Y , Zr , Nb , Mo , Tc , Ru , Rh,
Pd , Ag , Cd , In , Sn , Sb , Te , I , Xe,
Cs , Ba , La , Ce , Pr , Nd , Pm , Sm , Eu,
Gd , Tb , Dy , Ho , Er , Tm , Yb , Lu , Hf,
Ta , W , Re , Os , Ir , Pt , Au , Hg , Tl,
Pb , Bi , Po , At , Rn , Fr , Ra , Ac , Th,
Pa , U , Np , Pu , Am , Cm , Bk , Cf , Es,
Fm , Md , No , Lr , Rf , Db , Sg , Bh , Hs,
Mt , Ds , Rg , Cn , Nh , Fl , Mc , Lv , Ts,
Og ,
}
impl std::fmt::Display for Element {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.symbol())
}
}
#[rustfmt::skip]
const ABUNDANCE_LOOKUP_TABLE: [u16; 119] = [
0, 2, 4, 6, 7, 9, 12,
14, 17, 18, 21, 22, 25,
26, 29, 30, 34, 36, 39,
42, 48, 49, 54, 56, 60,
61, 65, 66, 71, 73, 78,
80, 85, 86, 92, 94, 100,
102, 106, 107, 112, 113,
120, 121, 128, 129, 135,
137, 145, 147, 157, 159,
167, 168, 177, 178, 185,
187, 191, 192, 199, 200,
207, 209, 216, 217, 224,
225, 231, 232, 239, 241,
247, 249, 254, 256, 263,
265, 271, 272, 279, 281,
285, 286, 287, 288, 289,
290, 291, 292, 293, 294,
297, 298, 299, 300, 301,
302, 303, 304, 305, 306,
307, 308, 309, 310, 311,
312, 313, 314, 315, 316,
317, 318, 319, 320, 321,
322, 323,
];
const ABUNDANCE_TABLE: [(u16, f64); 323] = [
(1, 0.999885), (2, 0.000115), (3, 0.00000134), (4, 0.99999866), (6, 0.0759), (7, 0.9241), (9, 1.0), (10, 0.199), (11, 0.801), (12, 0.9893), (13, 0.0107), (14, 0.0), (14, 0.99636), (15, 0.00364), (16, 0.99757), (17, 0.00038), (18, 0.00205), (19, 1.0), (20, 0.9048), (21, 0.0027), (22, 0.0925), (23, 1.0), (24, 0.7899), (25, 0.1000), (26, 0.1101), (27, 1.0), (28, 0.92223), (29, 0.04685), (30, 0.03092), (31, 1.0), (32, 0.9499), (33, 0.0075), (34, 0.0425), (36, 0.0001), (35, 0.7576), (37, 0.2424), (36, 0.003336), (38, 0.000629), (40, 0.996035), (39, 0.932581), (40, 0.000117), (41, 0.067302), (40, 0.96941), (42, 0.00647), (43, 0.00135), (44, 0.02086), (46, 0.00004), (48, 0.00187), (45, 1.0), (46, 0.0825), (47, 0.0744), (48, 0.7372), (49, 0.0541), (50, 0.0518), (50, 0.00250), (51, 0.99750), (50, 0.04345), (52, 0.83789), (53, 0.09501), (54, 0.02365), (55, 1.0), (54, 0.05845), (56, 0.91754), (57, 0.02119), (58, 0.00282), (59, 1.0), (58, 0.68077), (60, 0.26223), (61, 0.011399), (62, 0.036346), (64, 0.009255), (63, 0.6915), (65, 0.3085), (64, 0.4917), (66, 0.2773), (67, 0.0404), (68, 0.1845), (70, 0.0061), (69, 0.60108), (71, 0.39892), (70, 0.2057), (72, 0.2745), (73, 0.0775), (74, 0.3650), (76, 0.0773), (75, 1.0), (74, 0.0089), (76, 0.0937), (77, 0.0763), (78, 0.2377), (80, 0.4961), (82, 0.0873), (79, 0.5069), (81, 0.4931), (78, 0.00355), (80, 0.02286), (82, 0.11593), (83, 0.11500), (84, 0.56987), (86, 0.17279), (85, 0.7217), (87, 0.2783), (84, 0.0056), (86, 0.0986), (87, 0.0700), (88, 0.8258), (89, 1.0), (90, 0.5145), (91, 0.1122), (92, 0.1715), (94, 0.1738), (96, 0.0280), (93, 1.0), (92, 0.1453), (94, 0.0915), (95, 0.1584), (96, 0.1667), (97, 0.0960), (98, 0.2439), (100, 0.0982), (98, 1.0), (96, 0.0554), (98, 0.0187), (99, 0.1276), (100, 0.1260), (101, 0.1706), (102, 0.3155), (104, 0.1862), (103, 1.0), (102, 0.0102), (104, 0.1114), (105, 0.2233), (106, 0.2733), (108, 0.2646), (110, 0.1172), (107, 0.51839), (109, 0.48161), (106, 0.0125), (108, 0.0089), (110, 0.1249), (111, 0.1280), (112, 0.2413), (113, 0.1222), (114, 0.2873), (116, 0.0749), (113, 0.0429), (115, 0.9571), (112, 0.0097), (114, 0.0066), (115, 0.0034), (116, 0.1454), (117, 0.0768), (118, 0.2422), (119, 0.0859), (120, 0.3258), (122, 0.0463), (124, 0.0579), (121, 0.5721), (123, 0.4279), (120, 0.0009), (122, 0.0255), (123, 0.0089), (124, 0.0474), (125, 0.0707), (126, 0.1884), (128, 0.3174), (130, 0.3408), (127, 1.0), (124, 0.000952), (126, 0.000890), (128, 0.019102), (129, 0.264006), (130, 0.040710), (131, 0.212324), (132, 0.269086), (134, 0.104357), (136, 0.088573), (133, 1.0), (130, 0.00106), (132, 0.00101), (134, 0.02417), (135, 0.06592), (136, 0.07854), (137, 0.11232), (138, 0.71698), (138, 0.0008881), (139, 0.9991119), (136, 0.00185), (138, 0.00251), (140, 0.88450), (142, 0.11114), (141, 1.0), (142, 0.27152), (143, 0.12174), (144, 0.23798), (145, 0.08293), (146, 0.17189), (148, 0.05756), (150, 0.05638), (145, 1.0), (144, 0.0307), (147, 0.1499), (148, 0.1124), (149, 0.1382), (150, 0.0738), (152, 0.2675), (154, 0.2275), (151, 0.4781), (153, 0.5219), (152, 0.0020), (154, 0.0218), (155, 0.1480), (156, 0.2047), (157, 0.1565), (158, 0.2484), (160, 0.2186), (159, 1.0), (156, 0.00056), (158, 0.00095), (160, 0.02329), (161, 0.18889), (162, 0.25475), (163, 0.24896), (164, 0.28260), (165, 1.0), (162, 0.00139), (164, 0.01601), (166, 0.33503), (167, 0.22869), (168, 0.26978), (170, 0.14910), (169, 1.0), (168, 0.00123), (170, 0.02982), (171, 0.1409), (172, 0.2168), (173, 0.16103), (174, 0.32026), (176, 0.12996), (175, 0.97401), (176, 0.02599), (174, 0.0016), (176, 0.0526), (177, 0.1860), (178, 0.2728), (179, 0.1362), (180, 0.3508), (180, 0.0001201), (181, 0.9998799), (180, 0.0012), (182, 0.2650), (183, 0.1431), (184, 0.3064), (186, 0.2843), (185, 0.3740), (187, 0.6260), (184, 0.0002), (186, 0.0159), (187, 0.0196), (188, 0.1324), (189, 0.1615), (190, 0.2626), (192, 0.4078), (191, 0.373), (193, 0.627), (190, 0.00012), (192, 0.00782), (194, 0.3286), (195, 0.3378), (196, 0.2521), (198, 0.07356), (197, 1.0), (196, 0.0015), (198, 0.0997), (199, 0.1687), (200, 0.2310), (201, 0.1318), (202, 0.2986), (204, 0.0687), (203, 0.2952), (205, 0.7048), (204, 0.014), (206, 0.241), (207, 0.221), (208, 0.524), (209, 1.0), (209, 1.0), (210, 1.0), (222, 1.0), (223, 1.0), (226, 1.0), (227, 1.0), (232, 1.0), (231, 1.0), (234, 0.000054), (235, 0.007204), (238, 0.992742), (237, 1.0), (244, 1.0), (243, 1.0), (247, 1.0), (247, 1.0), (251, 1.0), (252, 1.0), (257, 1.0), (258, 1.0), (259, 1.0), (262, 1.0), (267, 1.0), (268, 1.0), (271, 1.0), (272, 1.0), (270, 1.0), (276, 1.0), (281, 1.0), (280, 1.0), (285, 1.0), (286, 1.0), (290, 1.0), (290, 1.0), (293, 1.0), (294, 1.0), (294, 1.0), ];
impl Element {
pub const fn protons(&self) -> u8 {
*self as u8
}
pub const fn from_protons(protons: u8) -> Element {
if protons == 0 || protons > Element::Og.protons() {
panic!("Invalid number of protons for an element");
}
unsafe { std::mem::transmute(protons) }
}
pub const fn symbol(&self) -> &'static str {
SYMBOL[*self as usize - 1]
}
pub fn abundant_nuclides(&self) -> NuclideFraction {
NuclideFraction::from(*self)
}
pub fn iter() -> impl Iterator<Item = Element> {
(1..=Element::Og.protons()).map(Element::from_protons)
}
}
impl ChemElement for Element {
fn atomic_num(&self) -> u64 {
*self as u64
}
fn am(&self) -> f64 {
NuclideFraction::from(*self).am()
}
fn electron_affinity(&self) -> f64 {
ELECTRON_AFFINITY[*self as usize - 1]
}
fn ionization_energies(&self, level: usize) -> Option<f64> {
let z = self.atomic_num();
if z > 110 || level == 0 || level > z as usize {
return None
}
Some(
IONIZATION_ENERGIES[((((z * (z + 1)) >> 1)
- z)
+ level as u64
- 1) as usize],
)
}
fn electronegativity(&self) -> f64 {
THERMOCHEMICAL_ELECTRO_NEGATIVE[*self as usize - 1]
}
fn mullikan_en(&self) -> f64 {
(self.ionization_energies(1).unwrap() + ELECTRON_AFFINITY[*self as usize - 1])
* 1.97E-3
+ 0.19
}
fn allen_en(&self) -> f64 {
ALLEN_ELECTRO[*self as usize - 1]
}
fn pauling_en(&self) -> f64 {
PAULING_ELECTRO[*self as usize - 1]
}
fn covalent_radii(&self, bond: usize) -> Option<f64> {
if bond > 0 && bond < 4 {
Some(COVALENT_RADII[(*self as usize - 1) * 3 + bond - 1])
} else {
None
}
}
fn ionic_radii(&self) -> f64 {
IONIC_RADII[*self as usize - 1]
}
fn vdr_crystal(&self) -> f64 {
VAN_DER_WAAL_CRYSTAL[*self as usize - 1]
}
fn vdr_isolated(&self) -> f64 {
VAN_DER_WAAL_ISOLATED[*self as usize - 1]
}
}
impl From<Element> for NuclideFraction {
fn from(element: Element) -> Self {
let z = element as usize;
let start = ABUNDANCE_LOOKUP_TABLE[z - 1] as usize;
let end = ABUNDANCE_LOOKUP_TABLE[z] as usize;
NuclideFraction::from_nucleon_fractions(
element as u8,
&ABUNDANCE_TABLE[start..end]
).expect("Valid abundance table for these elements")
}
}