├── Pion ├── photon.rs ├── lib.rs ├── quark.rs ├── meson.rs ├── baryon.rs └── lepton.rs ├── Nuclide ├── src │ ├── nstruct.rs │ ├── nuclidedata.rs │ ├── decay.rs │ ├── constant.rs │ ├── rng.rs │ ├── decay │ │ ├── types.rs │ │ ├── decayimpl.rs │ │ ├── dhelper.rs │ │ └── type.rs │ ├── lib.rs │ ├── nstruct │ │ ├── element.rs │ │ ├── core.rs │ │ └── isotope.rs │ ├── estruct.rs │ ├── traits.rs │ ├── isomer.rs │ ├── atom.rs │ ├── particle.rs │ ├── mmodel.rs │ ├── nuclidedata │ │ ├── index.rs │ │ ├── lang.rs │ │ ├── elemental.rs │ │ └── spinparity.rs │ └── element.rs ├── tests │ ├── element.rs │ └── nuclidetest.rs ├── Cargo.lock ├── Cargo.toml ├── examples │ ├── standard_atomic_mass.rs │ └── molecule.rs └── update.rs ├── LICENSE └── README.md /Pion/photon.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | pub struct Photon(f64); 3 | 4 | 5 | -------------------------------------------------------------------------------- /Nuclide/src/nstruct.rs: -------------------------------------------------------------------------------- 1 | mod core; 2 | mod element; 3 | mod isotope; 4 | 5 | pub use crate::nstruct::core::Nuclide; 6 | -------------------------------------------------------------------------------- /Nuclide/src/nuclidedata.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod atomic_mass; 2 | pub(crate) mod decay_chain; 3 | pub(crate) mod elemental; 4 | pub(crate) mod half_life; 5 | pub(crate) mod index; 6 | pub(crate) mod ionization; 7 | pub(crate) mod spinparity; 8 | pub(crate) mod lang; 9 | 10 | -------------------------------------------------------------------------------- /Nuclide/src/decay.rs: -------------------------------------------------------------------------------- 1 | //! Decay modes. Each struct represents a decay mode. 2 | 3 | 4 | pub(crate) mod internal; 5 | pub(crate) mod types; 6 | pub(crate) mod decayimpl; 7 | pub(crate) mod dhelper; 8 | 9 | pub use types::*; 10 | pub use decayimpl::DecayMode; 11 | 12 | pub(crate) use dhelper::is_mode_recorded; 13 | -------------------------------------------------------------------------------- /Nuclide/tests/element.rs: -------------------------------------------------------------------------------- 1 | use ::Nuclide::element::{Element, NuclideFraction}; 2 | 3 | #[test] 4 | fn elements_have_valid_nuclide_fractions() { 5 | ::Nuclide::Nuclide::new("S-34").expect("S-34 valid"); 6 | 7 | for e in Element::iter() { 8 | dbg!(e); 9 | // No panics in NuclideFraction construction 10 | e.abundant_nuclides(); 11 | } 12 | } 13 | 14 | // TODO more tests 15 | 16 | -------------------------------------------------------------------------------- /Nuclide/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "Nuclide" 7 | version = "0.1.8" 8 | dependencies = [ 9 | "Pion", 10 | ] 11 | 12 | [[package]] 13 | name = "Pion" 14 | version = "0.0.1" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "af05ecc58c3f8c00986e54ac61305122c84eb29da88d81516c10833f73f1a10c" 17 | -------------------------------------------------------------------------------- /Pion/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | mod baryon; 4 | mod lepton; 5 | mod quark; 6 | mod photon; 7 | mod meson; 8 | 9 | pub use crate::quark::Quark; 10 | pub use crate::quark::AntiQuark; 11 | 12 | pub use crate::baryon::Baryon; 13 | pub use crate::baryon::AntiBaryon; 14 | 15 | pub use crate::lepton::Lepton; 16 | pub use crate::lepton::AntiLepton; 17 | 18 | pub use crate::meson::Meson; 19 | pub use crate::meson::AntiMeson; 20 | 21 | 22 | -------------------------------------------------------------------------------- /Nuclide/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Nuclide" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["J.A Sory jasory@rust-cas.org"] 6 | description = "Database and simple modeling of all known nuclides" 7 | keywords = ["atom","nuclide","isotope","elements","nuclear-physics"] 8 | homepage = "https://rust-cas.org/physics/datasheets/nuclide.html" 9 | repository = "https://github.com/JASory/Iridium" 10 | license = "MIT" 11 | categories = ["science"] 12 | readme = "README.md" 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | Pion = "0.0.2" 17 | -------------------------------------------------------------------------------- /Nuclide/src/constant.rs: -------------------------------------------------------------------------------- 1 | pub(crate) const PROTONMASS: f64 = 1.007276466621f64; 2 | pub(crate) const NEUTRONMASS: f64 = 1.00866491588f64; 3 | pub(crate) const ELECTRONMASS: f64 = 5.48579909070E-4; 4 | pub(crate) const ALPHAMASS: f64 = 4.001506179127f64; 5 | pub(crate) const TRITONMASS: f64 = 3.015500980600021; 6 | pub(crate) const DEUTERONMASS: f64 = 2.013553389273434; 7 | 8 | pub const NEUTRINOMASS: f64 = 1.288738236E-10; 9 | 10 | pub(crate) const NUCLIDE_COUNT : usize = 3585; 11 | 12 | #[allow(non_upper_case_globals)] 13 | pub(crate) const DALTON_MeV: f64 = 931.49410242; 14 | 15 | pub(crate) const _DALTON_KG : f64 = 1.6605390666E-27; 16 | 17 | #[allow(non_upper_case_globals)] 18 | pub(crate) const MeV_DALTON: f64 = 0.0010735441023212314; 19 | 20 | pub(crate) const FLOAT_64 : f64 = 18446744073709552000f64; 21 | -------------------------------------------------------------------------------- /Nuclide/examples/standard_atomic_mass.rs: -------------------------------------------------------------------------------- 1 | use ::Nuclide::Nuclide; 2 | use ::Nuclide::ChemElement; 3 | 4 | /* 5 | Nuclide does not have a standard atomic weight for the elements however it can be trivially computed for a sample 6 | given a list of the nuclides known to exist and a vector of the distribution ratios 7 | */ 8 | 9 | fn standard_atomic_mass(isos: &[Nuclide], dist: &[f64]) -> f64{ 10 | let mut mass = 0f64; 11 | for i in 0..isos.len(){ 12 | mass+=isos[i].am()*dist[i]; 13 | } 14 | mass 15 | } 16 | 17 | 18 | fn main(){ 19 | let hydro = vec![Nuclide::new("H-1").unwrap(), Nuclide::new("H-2").unwrap(), Nuclide::new("H-3").unwrap()]; 20 | let distr = vec![0.9998,0.000156,1E-18]; 21 | println!("The standard atomic mass of hydrogen is {:?} ", standard_atomic_mass(&hydro[..], &distr[..])); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 JASory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Nuclide/src/rng.rs: -------------------------------------------------------------------------------- 1 | /* 2 | PRNG for decay chain 3 | */ 4 | 5 | 6 | // Possible improvements 7 | // The higher bits of x are going to be identical in most cases so some copying/XOR-ing of 8 | // lower bits into higher positions could improve entropy 9 | #[inline(always)] 10 | fn default_xor() -> u64{ 11 | let mut x = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64; 12 | 13 | x ^= x.wrapping_shr(12); 14 | x ^= x.wrapping_shl(25); 15 | x ^= x.wrapping_shr(27); 16 | x.wrapping_mul(0x2545F4914F6CDD1D) 17 | } 18 | 19 | #[allow(unreachable_code)] 20 | pub(crate) fn rand() -> u64 { 21 | 22 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 23 | { 24 | if is_x86_feature_detected!("rdrand"){// USE RDRAND if possible 25 | let mut x: u64 = 0; 26 | unsafe { core::arch::x86_64::_rdrand64_step(&mut x) }; 27 | return x; 28 | }// If processor is x86 and does not support RDRAND use xor shift 29 | return default_xor() 30 | } 31 | 32 | {// All other architectures use xor shift 33 | default_xor() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Nuclide/examples/molecule.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Molecular mass example as an application for Nuclide 3 | */ 4 | 5 | use ::Nuclide::Nuclide; 6 | use ::Nuclide::ChemElement; 7 | 8 | struct Water { 9 | oxygen : Nuclide, 10 | hydroone : Nuclide, 11 | hydrotwo : Nuclide, 12 | } 13 | 14 | impl Water{ 15 | 16 | fn new(oxygen: Nuclide, hydroone: Nuclide, hydrotwo: Nuclide) -> Self{ 17 | Self {oxygen, hydroone, hydrotwo} 18 | } 19 | 20 | fn mass(&self) -> f64{ 21 | self.oxygen.am()+ self.hydroone.am() + self.hydrotwo.am() 22 | } 23 | 24 | 25 | 26 | } 27 | 28 | fn main(){ 29 | 30 | let protium = Nuclide::new("H-1").unwrap(); 31 | let deuterium = Nuclide::new("H-2").unwrap(); 32 | let tritium = Nuclide::new("H-3").unwrap(); 33 | let oxy = Nuclide::new("O-16").unwrap(); 34 | let h_oxy = Nuclide::new("O-18").unwrap(); 35 | 36 | let potable = Water::new(oxy, protium, protium); 37 | let hdo = Water::new(oxy, deuterium, protium); 38 | let heavy_water = Water::new(oxy, deuterium, deuterium); 39 | let tritiated_water = Water::new(oxy, tritium, tritium); 40 | let super_heavy_water = Water::new(h_oxy, deuterium, deuterium); 41 | 42 | println!("Some masses of different types of \"water\" \n "); 43 | println!("Regular water H2O : {} daltons", potable.mass()); 44 | println!("Semi-heavy water HDO : {} daltons", hdo.mass()); 45 | println!("heavy water D2O : {} daltons", heavy_water.mass()); 46 | println!("Tritiated water T2O : {} daltons", tritiated_water.mass()); 47 | println!("Super heavy water D2O-18 : {} daltons", super_heavy_water.mass()); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Nuclide/src/decay/types.rs: -------------------------------------------------------------------------------- 1 | /// Emit K protons 2 | pub struct ProtonEmission; 3 | 4 | /// Emit K neutrons 5 | pub struct NeutronEmission; 6 | 7 | /// Emit K Alpha particles 8 | pub struct AlphaEmission; 9 | /// Emit Alpha particle and K neutrons 10 | pub struct AlphaNeutron; 11 | /// Emit Neutron and Deuteron 12 | pub struct NeutronDeuteron; 13 | /// Emit Neutron and Triton 14 | pub struct NeutronTriton; 15 | /// K Electron captures (EC) 16 | pub struct ElectronCapture; 17 | /// K electron emissions (B-) 18 | pub struct ElectronEmission; 19 | /// Electron emission and K neutrons (B- Kn) 20 | pub struct ElectronNeutron; 21 | /// Electron emission and K protons (B- Kp) 22 | pub struct ElectronProton; 23 | /// Electron emission and Alpha particle (B- A) 24 | pub struct ElectronAlpha; 25 | /// Electron emission and Deuteron particle (B- D) 26 | pub struct ElectronDeuteron; 27 | /// Electron emission and Triton particle (B- T) 28 | pub struct ElectronTriton; 29 | /// Electron emission and Spontaneous fission 30 | pub struct ElectronFission; 31 | ///K Positron emissions (KB+) 32 | pub struct PositronEmission; 33 | /// Positron emission and K protons (B+ Kp) 34 | pub struct PositronProton; 35 | /// Positron emission and Alpha particle 36 | pub struct PositronAlpha; 37 | /// Positron emission and Spontaneous fission 38 | pub struct PositronFission; 39 | /// Spontaneous fission 40 | pub struct SpontaneousFission; 41 | /// Decay from nuclide index 42 | pub struct ClusterDecay; 43 | /// Decay two clusters 44 | pub struct DoubleCluster; 45 | /// Full set of possible decay modes 46 | pub struct TotalDecay; 47 | 48 | -------------------------------------------------------------------------------- /Pion/quark.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug,Copy,Clone)] 2 | pub enum Quark{ 3 | Up, 4 | Down, 5 | Charm, 6 | Strange, 7 | Top, 8 | Bottom, 9 | } 10 | 11 | #[derive(Debug,Copy,Clone)] 12 | pub enum AntiQuark{ 13 | Up, 14 | Down, 15 | Charm, 16 | Strange, 17 | Top, 18 | Bottom, 19 | } 20 | 21 | 22 | impl Quark{ 23 | 24 | pub const fn rest_mass(&self)->f64{ 25 | match self { 26 | Quark::Up => 1.7f64, 27 | Quark::Down => 4.1f64, 28 | Quark::Charm => 1270f64, 29 | Quark::Strange => 101f64, 30 | Quark::Top => 172000f64, 31 | Quark::Bottom => 41900f64, 32 | } 33 | } 34 | 35 | pub fn charge(&self)->f64 { 36 | match self { 37 | Quark::Up => 2.0/3.0, 38 | Quark::Down => -1.0/3.0, 39 | Quark::Charm => 2.0/3.0, 40 | Quark::Strange => -1.0/3.0, 41 | Quark::Top => 2.0/3.0, 42 | Quark::Bottom => -1.0/3.0, 43 | } 44 | } 45 | } 46 | 47 | impl AntiQuark{ 48 | 49 | pub const fn rest_mass(&self)->f64{ 50 | match self { 51 | AntiQuark::Up => 1.7f64, 52 | AntiQuark::Down => 4.1f64, 53 | AntiQuark::Charm => 1270f64, 54 | AntiQuark::Strange => 101f64, 55 | AntiQuark::Top => 172000f64, 56 | AntiQuark::Bottom => 41900f64, 57 | } 58 | } 59 | 60 | pub fn charge(&self)->f64 { 61 | match self { 62 | AntiQuark::Up => 2.0/3.0, 63 | AntiQuark::Down => -1.0/3.0, 64 | AntiQuark::Charm => 2.0/3.0, 65 | AntiQuark::Strange => -1.0/3.0, 66 | AntiQuark::Top => 2.0/3.0, 67 | AntiQuark::Bottom => -1.0/3.0, 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Nuclide/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | Nuclide is a simple modeling and data library for nuclear isotopes. It is meant to be a midpoint between 4 | basic periodic table libraries and advanced research software like GEANT4. 5 | 6 | Notable capabilities of this library are listed below. 7 | 8 | - Data on 3585 nuclides, including : Atomic mass, half-life (total and partial), decay mode 9 | - Decay modeling for all unstable nuclides with total energies and particles emitted 10 | - Approximation of the mass and binding energy of theorectical nuclides 11 | - Elemental data including : ionization levels, various atomic radii and electronegativity measures 12 | - Hardcoded data. Unlike more advanced libraries, all data is hardcoded or computed resulting in a condensed standalone library. It is believed by the author to be the largest standalone nuclide library in any language 13 | - Some multi-lingual support. Elements can be identified/named by their name in English,Spanish,French,Russian,German,and Polish 14 | 15 | Some features that are absent by design 16 | 17 | - Data on macroscopic properties, like melting point or abundance, are beyond the scope of this library which intends to model individual atoms. 18 | - Error bounds. Introduce levels of complexity that are beyond the intention of the library. For accurate proper modeling look for more 19 | specialized libraries. 20 | - Data on discovery and other metainformation. Not considered useful in application. 21 | 22 | 23 | 24 | */ 25 | #![allow(non_snake_case)] 26 | 27 | mod traits; 28 | pub(crate) mod nuclidedata; 29 | pub(crate) mod nstruct; 30 | pub mod decay; 31 | mod particle; 32 | mod rng; 33 | mod mmodel; 34 | pub(crate) mod constant; 35 | pub(crate) mod element; 36 | 37 | pub use crate::traits::{ChemElement,Isotope}; 38 | pub use crate::nstruct::Nuclide; 39 | pub use crate::particle::Particle; 40 | pub use crate::element::{Element,NuclideFraction}; 41 | pub use crate::nuclidedata::lang::Lang; 42 | -------------------------------------------------------------------------------- /Pion/meson.rs: -------------------------------------------------------------------------------- 1 | use crate::Quark; 2 | use crate::AntiQuark; 3 | 4 | // Currently only supports pion and kaon 5 | #[derive(Debug,Copy,Clone)] 6 | pub enum Meson{ 7 | Pion(f64), 8 | Kaon(f64), 9 | } 10 | 11 | ///Currently only supports anti-pion and anti-kaon 12 | #[derive(Debug,Copy,Clone)] 13 | pub enum AntiMeson{ 14 | Pion(f64), 15 | Kaon(f64), 16 | } 17 | 18 | impl Meson{ 19 | 20 | /// Construct pion or kaon using quarks and energy 21 | pub const fn new(x: Quark, y: AntiQuark, ke: f64)->Meson{ 22 | match (x,y) { 23 | 24 | (Quark::Up, AntiQuark::Down) => return Meson::Pion(ke), 25 | _ => return Meson::Kaon(ke), 26 | 27 | } 28 | } 29 | 30 | pub const fn rest_mass(&self)->f64{ 31 | match self{ 32 | Meson::Pion(_) => 139.6f64, 33 | Meson::Kaon(_) => 135.0f64, 34 | } 35 | } 36 | 37 | pub fn rest_mass_kg(&self)->f64{ 38 | self.rest_mass()*1.79E-30 39 | } 40 | 41 | pub const fn charge(&self)->f64{ 42 | match self{ 43 | Meson::Pion(_) => 1f64, 44 | Meson::Kaon(_) => 1f64, 45 | } 46 | } 47 | 48 | } 49 | 50 | 51 | impl AntiMeson{ 52 | // construct 53 | pub const fn new(x: AntiQuark, y: Quark, ke: f64)->AntiMeson{ 54 | match (x,y) { 55 | 56 | (AntiQuark::Up, Quark::Down) => return AntiMeson::Pion(ke), 57 | _ => return AntiMeson::Kaon(ke), 58 | 59 | } 60 | } 61 | /// Rest mass 62 | pub const fn rest_mass(&self)->f64{ 63 | match self{ 64 | AntiMeson::Pion(_) => 139.6f64, 65 | AntiMeson::Kaon(_) => 135.0f64, 66 | } 67 | } 68 | 69 | pub fn rest_mass_kg(&self)->f64{ 70 | self.rest_mass()*1.79E-30 71 | } 72 | 73 | pub const fn charge(&self)->f64{ 74 | match self{ 75 | AntiMeson::Pion(_) => -1f64, 76 | AntiMeson::Kaon(_) => -1f64, 77 | } 78 | } 79 | 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Nuclide/src/decay/decayimpl.rs: -------------------------------------------------------------------------------- 1 | use crate::decay::{types::*,internal::InternalDecay}; 2 | use crate::nuclidedata::decay_chain::DECAY_CHAIN; 3 | 4 | // Modes of decay 5 | pub trait DecayMode : InternalDecay{} 6 | 7 | impl DecayMode for TotalDecay{} 8 | impl DecayMode for ProtonEmission{} 9 | impl DecayMode for NeutronEmission{} 10 | impl DecayMode for AlphaEmission{} 11 | impl DecayMode for AlphaNeutron{} 12 | impl DecayMode for NeutronDeuteron{} 13 | impl DecayMode for NeutronTriton{} 14 | impl DecayMode for ElectronCapture{} 15 | impl DecayMode for ElectronEmission{} 16 | impl DecayMode for ElectronNeutron{} 17 | impl DecayMode for ElectronProton{} 18 | impl DecayMode for ElectronAlpha{} 19 | impl DecayMode for ElectronDeuteron{} 20 | impl DecayMode for ElectronTriton{} 21 | impl DecayMode for ElectronFission{} 22 | impl DecayMode for PositronEmission{} 23 | impl DecayMode for PositronProton{} 24 | impl DecayMode for PositronAlpha{} 25 | impl DecayMode for PositronFission{} 26 | impl DecayMode for SpontaneousFission{} 27 | impl DecayMode for ClusterDecay{} 28 | impl DecayMode for DoubleCluster{} 29 | 30 | 31 | // Given an u64 representation of the decay modes it returns the corresponding u64 for 32 | pub(crate) fn decayindex(idx: usize) -> Option{ 33 | let modes = DECAY_CHAIN[idx].to_be_bytes(); 34 | let d_mode = T::decay_index(); 35 | if d_mode == 254{ 36 | return Some(u64::MAX) 37 | } 38 | if modes[0] == d_mode{ 39 | return Some(DECAY_CHAIN[idx-5]) 40 | } 41 | if modes[1] == d_mode{ 42 | return Some(DECAY_CHAIN[idx-4]) 43 | } 44 | if modes[2] == d_mode{ 45 | return Some(DECAY_CHAIN[idx-3]) 46 | } 47 | if modes[3] == d_mode{ 48 | return Some(DECAY_CHAIN[idx-2]) 49 | } 50 | if modes[4] == d_mode{ 51 | return Some(DECAY_CHAIN[idx-1]) 52 | } 53 | None 54 | } 55 | -------------------------------------------------------------------------------- /Nuclide/src/nstruct/element.rs: -------------------------------------------------------------------------------- 1 | use crate::traits::ChemElement; 2 | use crate::nstruct::core::Nuclide; 3 | use crate::nuclidedata::atomic_mass::ATOMIC_MASS; 4 | use crate::nuclidedata::elemental::*; 5 | use crate::nuclidedata::ionization::IONIZATION_ENERGIES; 6 | 7 | //use crate::constant::*; 8 | use crate::nuclidedata::index::SYMBOL_INDEX; 9 | 10 | 11 | 12 | impl ChemElement for Nuclide{ 13 | fn atomic_num(&self) -> u64 { 14 | SYMBOL_INDEX.partition_point(|&tup| tup.0 <= self.nuclide_index()) as u64 15 | } 16 | 17 | /// Returns the atomic mass in daltons 18 | fn am(&self) -> f64 { 19 | ATOMIC_MASS[self.nuclide_index()] 20 | } 21 | 22 | fn electron_affinity(&self) -> f64 { 23 | ELECTRON_AFFINITY[self.atomic_num() as usize - 1] 24 | } 25 | 26 | fn ionization_energies(&self, level: usize) -> Option { 27 | if self.atomic_num() > 110 || level == 0 || level > self.atomic_num() as usize { 28 | return None; 29 | } 30 | 31 | Some( 32 | IONIZATION_ENERGIES[((((self.atomic_num() * (self.atomic_num() + 1)) >> 1) 33 | - self.atomic_num()) 34 | + level as u64 35 | - 1) as usize], 36 | ) 37 | } 38 | 39 | fn electronegativity(&self) -> f64 { 40 | THERMOCHEMICAL_ELECTRO_NEGATIVE[self.atomic_num() as usize - 1] 41 | } 42 | 43 | ///Returns the Mullikan, or absolute, electronegativity in kj/mol 44 | fn mullikan_en(&self) -> f64 { 45 | (self.ionization_energies(1).unwrap() + ELECTRON_AFFINITY[self.atomic_num() as usize - 1]) 46 | * 1.97E-3 47 | + 0.19 48 | } 49 | 50 | ///Allen Electronegativity 51 | fn allen_en(&self) -> f64 { 52 | ALLEN_ELECTRO[self.atomic_num() as usize - 1] 53 | } 54 | 55 | ///Pauling Electronegativity. A poor fit for experimental values, however it is here for completeness 56 | fn pauling_en(&self) -> f64 { 57 | PAULING_ELECTRO[self.atomic_num() as usize - 1] 58 | } 59 | 60 | /// Radius in Single-double-and-triple covalent bonds 61 | fn covalent_radii(&self, bond: usize) -> Option { 62 | if bond > 0 && bond < 4 { 63 | Some(COVALENT_RADII[(self.atomic_num() as usize - 1) * 3 + bond - 1]) 64 | } else { 65 | None 66 | } 67 | } 68 | 69 | fn ionic_radii(&self) -> f64 { 70 | IONIC_RADII[self.atomic_num() as usize - 1] 71 | } 72 | 73 | 74 | fn vdr_crystal(&self) -> f64 { 75 | VAN_DER_WAAL_CRYSTAL[self.atomic_num() as usize - 1] 76 | } 77 | 78 | 79 | fn vdr_isolated(&self) -> f64 { 80 | VAN_DER_WAAL_ISOLATED[self.atomic_num() as usize - 1] 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Pion/baryon.rs: -------------------------------------------------------------------------------- 1 | use crate::AntiQuark; 2 | use crate::Quark; 3 | 4 | ///Currently only supports proton and neutron 5 | #[derive(Debug,Copy,Clone)] 6 | pub enum Baryon{ 7 | Proton(f64), 8 | Neutron(f64), 9 | } 10 | /// Currently only supports anti-proton and anti-neutron 11 | #[derive(Debug,Copy,Clone)] 12 | pub enum AntiBaryon{ 13 | Proton(f64), 14 | Neutron(f64), 15 | } 16 | 17 | 18 | impl Baryon{ 19 | ///Compose proton or neutron using quarks 20 | pub const fn new(x: Quark, y: Quark, z: Quark, ke: f64)->Baryon{ 21 | 22 | match (x,y,z) { 23 | 24 | (Quark::Up, Quark::Up, Quark::Down) => return Baryon::Proton(ke), 25 | _ => return Baryon::Neutron(ke), 26 | 27 | } 28 | } 29 | 30 | pub const fn charge(&self)->f64{ 31 | match self{ 32 | Baryon::Proton(_) => 1f64, 33 | Baryon::Neutron(_) => 0f64, 34 | } 35 | } 36 | // MeV 37 | pub const fn rest_mass(&self)->f64{ 38 | 39 | match self{ 40 | Baryon::Proton(_) => 938.27231, 41 | Baryon::Neutron(_) => 939.5656, 42 | } 43 | } 44 | 45 | pub fn rest_mass_kg(&self)->f64{ 46 | self.rest_mass()*1.79E-30 47 | } 48 | 49 | pub const fn kinetic(&self)-> f64{ 50 | match self { 51 | Baryon::Proton(x) => *x, 52 | Baryon::Neutron(x) => *x, 53 | } 54 | } 55 | //relativistic momentum 56 | pub fn momentum(&self)->f64{ 57 | self.rest_mass()*self.kinetic() / (1.0 - (self.kinetic()*self.kinetic())/89875517873681764f64).sqrt() 58 | } 59 | 60 | } 61 | 62 | 63 | impl AntiBaryon{ 64 | 65 | pub const fn new(x: AntiQuark, y: AntiQuark, z: AntiQuark, ke: f64)->AntiBaryon{ 66 | 67 | match (x,y,z) { 68 | 69 | (AntiQuark::Up, AntiQuark::Up, AntiQuark::Down) => return AntiBaryon::Proton(ke), 70 | _ => return AntiBaryon::Neutron(ke), 71 | 72 | } 73 | } 74 | 75 | pub const fn charge(&self)->f64{ 76 | match self{ 77 | AntiBaryon::Proton(_) => 1f64, 78 | AntiBaryon::Neutron(_) => 0f64, 79 | } 80 | } 81 | /// MeV 82 | pub const fn rest_mass(&self)->f64{ 83 | 84 | match self{ 85 | AntiBaryon::Proton(_) => 938.27231, 86 | AntiBaryon::Neutron(_) => 939.5656, 87 | } 88 | } 89 | 90 | pub fn rest_mass_kg(&self)->f64{ 91 | self.rest_mass()*1.79E-30 92 | } 93 | 94 | pub const fn kinetic(&self)-> f64{ 95 | match self { 96 | AntiBaryon::Proton(x) => *x, 97 | AntiBaryon::Neutron(x) => *x, 98 | } 99 | } 100 | //relativistic momentum 101 | pub fn momentum(&self)->f64{ 102 | self.rest_mass()*self.kinetic() / (1.0 - (self.kinetic()*self.kinetic())/89875517873681764f64).sqrt() 103 | } 104 | 105 | } 106 | 107 | -------------------------------------------------------------------------------- /Nuclide/src/estruct.rs: -------------------------------------------------------------------------------- 1 | use crate::nstruct::Nuclide; 2 | use crate::decay::decayimpl::DecayMode; 3 | use crate::particle::Particle; 4 | 5 | use crate::traits::{ChemElement,Isotope}; 6 | 7 | 8 | /// Currently identical to Nuclide 9 | #[allow(dead_code)] 10 | #[derive(Clone, Debug)] 11 | pub struct EnergeticNuclide { 12 | gs: Nuclide, 13 | lvl: usize, 14 | } 15 | 16 | impl EnergeticNuclide { 17 | pub fn new(x: &str) -> Option { 18 | Nuclide::new(x).map(|x| Self { gs: x, lvl: 0usize }) 19 | } 20 | } 21 | 22 | impl ChemElement for EnergeticNuclide{ 23 | fn atomic_num(&self) -> u64 { 24 | self.gs.atomic_num() 25 | } 26 | 27 | 28 | fn am(&self) -> f64 { 29 | self.gs.am() 30 | } 31 | 32 | fn electron_affinity(&self) -> f64 { 33 | self.gs.electron_affinity() 34 | } 35 | 36 | 37 | fn ionization_energies(&self, level: usize) -> Option { 38 | self.gs.ionization_energies(level) 39 | } 40 | 41 | fn electronegativity(&self) -> f64 { 42 | self.gs.electronegativity() 43 | } 44 | 45 | fn mullikan_en(&self) -> f64 { 46 | self.gs.mullikan_en() 47 | } 48 | 49 | fn allen_en(&self) -> f64 { 50 | self.gs.allen_en() 51 | } 52 | 53 | fn pauling_en(&self) -> f64 { 54 | self.gs.pauling_en() 55 | } 56 | 57 | fn covalent_radii(&self, bond: usize) -> Option { 58 | self.gs.covalent_radii(bond) 59 | } 60 | 61 | fn ionic_radii(&self) -> f64 { 62 | self.gs.ionic_radii() 63 | } 64 | 65 | fn vdr_crystal(&self) -> f64 { 66 | self.gs.vdr_crystal() 67 | } 68 | 69 | fn vdr_isolated(&self) -> f64 { 70 | self.gs.vdr_isolated() 71 | } 72 | 73 | } 74 | 75 | impl Isotope for EnergeticNuclide{ 76 | 77 | fn mass_deficit(&self) -> f64 { 78 | self.gs.mass_deficit() 79 | } 80 | 81 | fn binding_energy(&self) -> f64 { 82 | self.gs.binding_energy() 83 | } 84 | 85 | fn half_life(&self) -> f64 { 86 | self.gs.half_life::() 87 | } 88 | 89 | fn mean_lifetime(&self) -> f64 { 90 | self.gs.mean_lifetime::() 91 | } 92 | 93 | fn decay_string(&self) -> String { 94 | self.gs.decay_string() 95 | } 96 | 97 | fn branching_ratio(&self) -> f64{ 98 | self.gs.branching_ratio::() 99 | } 100 | 101 | fn decay_constant(&self) -> f64 { 102 | self.gs.decay_constant::() 103 | } 104 | 105 | fn decay_probability(&self, time: f64) -> f64{ 106 | self.gs.decay_probability::(time) 107 | } 108 | 109 | fn decay_time(&self, time: f64) -> bool { 110 | self.gs.decay_time::(time) 111 | } 112 | 113 | fn daughter(&self) -> Option{ 114 | self.gs.daughter::().map(|x| EnergeticNuclide{gs:x,lvl: 0usize}) 115 | } 116 | 117 | fn daughter_energetic(&mut self) -> (f64,Vec){ 118 | self.gs.daughter_energetic::() 119 | } 120 | 121 | fn decay_q(&self) -> f64{ 122 | self.gs.decay_q::() 123 | } 124 | 125 | fn decay(&mut self, time: f64) -> (f64, Vec) { 126 | self.gs.decay::(time) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Pion/lepton.rs: -------------------------------------------------------------------------------- 1 | 2 | #[derive(Debug,Copy,Clone)] 3 | pub enum Lepton{ 4 | Electron(f64), 5 | Muon(f64), 6 | Tau(f64), 7 | ElectronNeutrino(f64), 8 | MuNeutrino(f64), 9 | TauNeutrino(f64), 10 | } 11 | #[derive(Debug,Copy,Clone)] 12 | pub enum AntiLepton{ 13 | Electron(f64), 14 | Muon(f64), 15 | Tau(f64), 16 | ElectronNeutrino(f64), 17 | MuNeutrino(f64), 18 | TauNeutrino(f64), 19 | } 20 | 21 | impl Lepton{ 22 | ///rest mass in MeV 23 | pub const fn rest_mass(&self)->f64{ 24 | 25 | match self { 26 | Lepton::Electron(_) => 0.510998950, 27 | Lepton::Muon(_) => 105.6583755, 28 | Lepton::Tau(_) => 1776.86, 29 | Lepton::ElectronNeutrino(_) => 0f64, 30 | Lepton::MuNeutrino(_) => 0f64, 31 | Lepton::TauNeutrino(_) => 0f64, 32 | } 33 | } 34 | 35 | 36 | 37 | pub fn rest_mass_kg(&self)->f64{ 38 | self.rest_mass()*1.79E-30 39 | } 40 | 41 | pub const fn kinetic(&self)->f64{ 42 | match self { 43 | Lepton::Electron(x) => *x, 44 | Lepton::Muon(x) => *x, 45 | Lepton::Tau(x) => *x, 46 | Lepton::ElectronNeutrino(x) => *x, 47 | Lepton::MuNeutrino(x) => *x, 48 | Lepton::TauNeutrino(x) => *x, 49 | } 50 | } 51 | 52 | 53 | 54 | pub fn momentum(&self)->f64{ 55 | self.rest_mass()*self.kinetic() / (1.0 - (self.kinetic()*self.kinetic())/89875517873681764f64).sqrt() 56 | } 57 | 58 | pub const fn charge(&self)->f64{ 59 | match self { 60 | Lepton::Electron(_) => -1f64, 61 | Lepton::Muon(_) => -1f64, 62 | Lepton::Tau(_) => -1f64, 63 | Lepton::ElectronNeutrino(_) => 0f64, 64 | Lepton::MuNeutrino(_) => 0f64, 65 | Lepton::TauNeutrino(_) => 0f64, 66 | } 67 | } 68 | } 69 | 70 | 71 | impl AntiLepton{ 72 | ///rest mass in MeV 73 | pub const fn rest_mass(&self)->f64{ 74 | 75 | match self { 76 | AntiLepton::Electron(_) => 0.510998950, 77 | AntiLepton::Muon(_) => 105.6583755, 78 | AntiLepton::Tau(_) => 1776.86, 79 | AntiLepton::ElectronNeutrino(_) => 0f64, 80 | AntiLepton::MuNeutrino(_) => 0f64, 81 | AntiLepton::TauNeutrino(_) => 0f64, 82 | } 83 | } 84 | 85 | 86 | 87 | pub fn rest_mass_kg(&self)->f64{ 88 | self.rest_mass()*1.79E-30 89 | } 90 | 91 | pub const fn kinetic(&self)->f64{ 92 | match self { 93 | AntiLepton::Electron(x) => *x, 94 | AntiLepton::Muon(x) => *x, 95 | AntiLepton::Tau(x) => *x, 96 | AntiLepton::ElectronNeutrino(x) => *x, 97 | AntiLepton::MuNeutrino(x) => *x, 98 | AntiLepton::TauNeutrino(x) => *x, 99 | } 100 | } 101 | 102 | pub const fn charge(&self)->f64{ 103 | match self { 104 | AntiLepton::Electron(_) => 1f64, 105 | AntiLepton::Muon(_) => 1f64, 106 | AntiLepton::Tau(_) => 1f64, 107 | AntiLepton::ElectronNeutrino(_) => 0f64, 108 | AntiLepton::MuNeutrino(_) => 0f64, 109 | AntiLepton::TauNeutrino(_) => 0f64, 110 | } 111 | } 112 | 113 | pub fn momentum(&self)->f64{ 114 | self.rest_mass()*self.kinetic() / (1.0 - (self.kinetic()*self.kinetic())/89875517873681764f64).sqrt() 115 | } 116 | } 117 | 118 | -------------------------------------------------------------------------------- /Nuclide/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::particle::Particle; 2 | use crate::decay::decayimpl::DecayMode; 3 | 4 | /// General chemical element properties 5 | pub trait ChemElement: Clone{ 6 | /// Atomic number 7 | fn atomic_num(&self) -> u64; 8 | /// Atomic mass in Daltons 9 | fn am(&self) -> f64; 10 | /// Electron affinity in kj/mol 11 | fn electron_affinity(&self) -> f64; 12 | ///Returns the ionization energies for all known levels. Values are in kj/mol 13 | fn ionization_energies(&self, level: usize) -> Option; 14 | /// Returns Oganov-Tantardini values, the current best evaluation 15 | fn electronegativity(&self) -> f64; 16 | /// Mullikan electronegativity 17 | fn mullikan_en(&self) -> f64; 18 | /// Allen electronegativity 19 | fn allen_en(&self) -> f64; 20 | /// Pauling electronegativity 21 | fn pauling_en(&self) -> f64; 22 | /// Covalent radii of the first three bonds 23 | fn covalent_radii(&self, bond: usize) -> Option; 24 | /// Ionic radii 25 | fn ionic_radii(&self) -> f64; 26 | /// Van der Waal radius in crystalline structure 27 | fn vdr_crystal(&self) -> f64; 28 | /// Van der Waal radius in isolated atoms 29 | fn vdr_isolated(&self) -> f64; 30 | 31 | } 32 | 33 | /// Nuclear properties that vary between isotopes 34 | pub trait Isotope: ChemElement{ 35 | 36 | /// Mass defect or the difference between the empirical mass and the mass of the constituents, in Daltons 37 | fn mass_deficit(&self) -> f64; 38 | 39 | /// Binding energy in MeV 40 | fn binding_energy(&self) -> f64; 41 | 42 | /// Half-life of nuclide/isomer in seconds. TotalDecay mode returns the half-life, all other modes return the partial half-life 43 | /// # Nan 44 | /// Particle is unstable but the selected decay mode is not supported 45 | /// # Inf 46 | /// Particle is stable 47 | fn half_life(&self) -> f64; 48 | 49 | /// The mean lifetime of nuclide in seconds 50 | fn mean_lifetime(&self) -> f64; 51 | 52 | /// Returns the probable decay modes as a string 53 | fn decay_string(&self) -> String; 54 | 55 | /// Returns the daughter with decay energy 56 | fn daughter_energetic(&mut self) -> (f64, Vec); 57 | 58 | /// Returns the daughter nuclide 59 | /// # None 60 | /// If nuclide has not been observed to decay by the mode, returns None 61 | fn daughter(&self) -> Option; 62 | 63 | /// Returns the daughter nuclide,regardless of whether it has been observed 64 | /// # None 65 | /// If impossible to decay by the provided mode 66 | fn daughter_theoretical(&self) -> Option; 67 | 68 | /// Probability of the provided Decay mode being taken 69 | /// # NAN 70 | /// If Decay Mode is not observed return NAN 71 | fn branching_ratio(&self) -> f64; 72 | 73 | /// Decay constant in seconds of nuclide/isomer. TotalDecay mode returns the decay constant, all other modes return the partial decay constant 74 | /// # Nan 75 | /// Particle is unstable but the selected decay mode is not supported 76 | /// # Inf 77 | /// Particle is stable 78 | fn decay_constant(&self) -> f64; 79 | 80 | /// Returns the probability of the nuclide to decay after the time in seconds provided 81 | fn decay_probability(&self, time: f64) -> f64; 82 | 83 | /// Checks if nuclide probably decay in the selected time. 84 | fn decay_time(&self, time: f64) -> bool; 85 | 86 | /// Continously performs decay throughout the time selected, collecting all particles into a vector with decay energies. 87 | fn decay(&mut self, time: f64) -> (f64, Vec); 88 | 89 | /// Q-value (total energy) of a nuclear decay, regardless of whether it is observed 90 | /// # NAN 91 | /// Returns NAN if this decay mode results in a nonexistent nuclide 92 | fn decay_q(&self) -> f64; 93 | } 94 | -------------------------------------------------------------------------------- /Nuclide/src/isomer.rs: -------------------------------------------------------------------------------- 1 | use crate::nuclidedata::nuclidestruct::Nuclide; 2 | use crate::decay::decayimpl::DecayMode; 3 | use crate::particle::Particle; 4 | 5 | use crate::atom::Atom; 6 | 7 | 8 | /// Currently identical to Nuclide 9 | #[allow(dead_code)] 10 | #[derive(Clone, Debug)] 11 | pub struct Isomer { 12 | gs: Nuclide, 13 | lvl: usize, 14 | } 15 | 16 | impl Isomer { 17 | pub fn new(x: &str) -> Option { 18 | Nuclide::new(x).map(|x| Self { gs: x, lvl: 0usize }) 19 | } 20 | } 21 | 22 | impl Atom for Isomer { 23 | fn atomic_num(&self) -> u64 { 24 | self.gs.atomic_num() 25 | } 26 | 27 | 28 | fn am(&self) -> f64 { 29 | self.gs.am() 30 | } 31 | 32 | fn am_kg(&self) -> f64 { 33 | self.gs.am_kg() 34 | } 35 | 36 | //fn am_ev(&self) -> f64; 37 | 38 | fn mass_deficit(&self) -> f64 { 39 | self.gs.mass_deficit() 40 | } 41 | 42 | fn mass_deficit_kg(&self) -> f64 { 43 | self.gs.mass_deficit_kg() 44 | } 45 | 46 | fn mass_deficit_j(&self) -> f64 { 47 | self.gs.mass_deficit_j() 48 | } 49 | 50 | fn mass_deficit_ev(&self) -> f64 { 51 | self.gs.mass_deficit_ev() 52 | } 53 | 54 | fn binding_energy(&self) -> f64 { 55 | self.gs.binding_energy() 56 | } 57 | 58 | fn binding_energy_j(&self) -> f64 { 59 | self.gs.binding_energy_j() 60 | } 61 | 62 | fn spin_parity(&self) -> (i8, i8) { 63 | self.gs.spin_parity() 64 | } 65 | 66 | fn electron_affinity(&self) -> f64 { 67 | self.gs.electron_affinity() 68 | } 69 | 70 | fn electron_affinity_ev(&self) -> f64 { 71 | self.gs.electron_affinity() 72 | } 73 | 74 | fn ionization_energies(&self, level: usize) -> Option { 75 | self.gs.ionization_energies(level) 76 | } 77 | 78 | fn ionization_energies_ev(&self, level: usize) -> Option { 79 | self.gs.ionization_energies(level) 80 | } 81 | 82 | fn electronegativity(&self) -> f64 { 83 | self.gs.electronegativity() 84 | } 85 | 86 | fn mullikan_en(&self) -> f64 { 87 | self.gs.mullikan_en() 88 | } 89 | 90 | fn allen_en(&self) -> f64 { 91 | self.gs.allen_en() 92 | } 93 | 94 | fn pauling_en(&self) -> f64 { 95 | self.gs.pauling_en() 96 | } 97 | 98 | fn covalent_radii(&self, bond: usize) -> Option { 99 | self.gs.covalent_radii(bond) 100 | } 101 | 102 | fn ionic_radii(&self) -> f64 { 103 | self.gs.ionic_radii() 104 | } 105 | 106 | fn vdr_crystal(&self) -> f64 { 107 | self.gs.vdr_crystal() 108 | } 109 | 110 | fn vdr_isolated(&self) -> f64 { 111 | self.gs.vdr_isolated() 112 | } 113 | 114 | fn half_life(&self) -> f64 { 115 | self.gs.half_life::() 116 | } 117 | 118 | fn mean_lifetime(&self) -> f64 { 119 | self.gs.mean_lifetime::() 120 | } 121 | 122 | fn decay_mode(&self) -> String { 123 | self.gs.decay_mode() 124 | } 125 | 126 | fn decay_constant(&self) -> f64 { 127 | self.gs.decay_constant::() 128 | } 129 | 130 | fn decay_probability(&self, time: f64) -> f64{ 131 | self.gs.decay_probability::(time) 132 | } 133 | 134 | fn decay_time(&self, time: f64) -> bool { 135 | self.gs.decay_time::(time) 136 | } 137 | 138 | fn daughter(&self) -> Option{ 139 | self.gs.daughter::().map(|x| Isomer{gs:x,lvl: 0usize}) 140 | } 141 | 142 | fn daughter_energetic(&mut self) -> (f64,Vec){ 143 | self.gs.daughter_energetic::() 144 | } 145 | 146 | fn decay_q(&self) -> f64{ 147 | self.gs.decay_q::() 148 | } 149 | 150 | fn decay(&mut self, time: f64) -> (f64, Vec) { 151 | self.gs.decay::(time) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Nuclide/src/atom.rs: -------------------------------------------------------------------------------- 1 | use crate::particle::Particle; 2 | use crate::decay::decayimpl::DecayMode; 3 | 4 | /// Shared trait for atoms 5 | pub trait Atom: Clone { 6 | /// Atomic number 7 | fn atomic_num(&self) -> u64; 8 | 9 | /// Atomic mass in Daltons 10 | fn am(&self) -> f64; 11 | /// Atomic mass in kilograms 12 | fn am_kg(&self) -> f64; 13 | 14 | //fn am_ev(&self) -> f64; 15 | /// Mass defect or the difference between the empirical mass and the mass of the constituents, in Daltons 16 | fn mass_deficit(&self) -> f64; 17 | // Mass defect in Kilograms 18 | fn mass_deficit_kg(&self) -> f64; 19 | /// Mass defect in Joules 20 | fn mass_deficit_j(&self) -> f64; 21 | /// Mass defect in MeV fix these, same as binding energy 22 | fn mass_deficit_ev(&self) -> f64; 23 | 24 | fn binding_energy(&self) -> f64; 25 | 26 | fn binding_energy_j(&self) -> f64; 27 | /// Spin as a i8 pair 28 | fn spin_parity(&self) -> (i8, i8); 29 | /// Electron affinity in kj/mol 30 | fn electron_affinity(&self) -> f64; 31 | /// Electron affinity in MeV 32 | fn electron_affinity_ev(&self) -> f64; 33 | ///Returns the ionization energies for all known levels. Values are in kj/mol 34 | fn ionization_energies(&self, level: usize) -> Option; 35 | ///Returns the ionization energies for all known levels. Values are in MeV 36 | fn ionization_energies_ev(&self, level: usize) -> Option; 37 | /// Returns Oganov-Tantardini values, the current best evaluation 38 | fn electronegativity(&self) -> f64; 39 | /// Mullikan electronegativity 40 | fn mullikan_en(&self) -> f64; 41 | /// Allen electronegativity 42 | fn allen_en(&self) -> f64; 43 | /// Pauling electronegativity 44 | fn pauling_en(&self) -> f64; 45 | /// Covalent radii of the first three bonds 46 | fn covalent_radii(&self, bond: usize) -> Option; 47 | /// Ionic radii 48 | fn ionic_radii(&self) -> f64; 49 | /// Van der Waal radius in crystalline structure 50 | fn vdr_crystal(&self) -> f64; 51 | /// Van der Waal radius in isolated atoms 52 | fn vdr_isolated(&self) -> f64; 53 | 54 | /// Half-life of nuclide/isomer. TotalDecay mode returns the half-life, all other modes return the partial half-life 55 | /// # Nan 56 | /// Particle is unstable but the selected decay mode is not supported 57 | /// # Inf 58 | /// Particle is stable 59 | fn half_life(&self) -> f64; 60 | /// The mean lifetime of nuclide/isomer 61 | fn mean_lifetime(&self) -> f64; 62 | /// Returns the probable decay modes as a string 63 | fn decay_mode(&self) -> String; 64 | 65 | fn daughter_energetic(&mut self) -> (f64, Vec); 66 | 67 | /// Returns the daughter nuclide 68 | /// # None 69 | /// If nuclide has not been observed to decay by the mode, returns None 70 | fn daughter(&self) -> Option; 71 | 72 | /// Decay constant in seconds of nuclide/isomer. TotalDecay mode returns the decay constant, all other modes return the partial decay constant 73 | /// # Nan 74 | /// Particle is unstable but the selected decay mode is not supported 75 | /// # Inf 76 | /// Particle is stable 77 | fn decay_constant(&self) -> f64; 78 | /// Returns the probability of the nuclide to decay after the time in seconds provided 79 | fn decay_probability(&self, time: f64) -> f64; 80 | 81 | /// Checks if nuclide/isomer would decay in the selected time 82 | fn decay_time(&self, time: f64) -> bool; 83 | 84 | /// Continously performs decay throughout the time selected, collecting all particles into a vector with decay energies. 85 | fn decay(&mut self, time: f64) -> (f64, Vec); 86 | 87 | /// Q-value (total energy) of a nuclear decay, regardless of whether it is observed 88 | /// # NAN 89 | /// Returns NAN if this decay mode results in a nonexistent nuclide 90 | fn decay_q(&self) -> f64; 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Nuclide/src/particle.rs: -------------------------------------------------------------------------------- 1 | use Pion::AntiBaryon; 2 | use Pion::Baryon; 3 | use Pion::AntiLepton; 4 | use Pion::Lepton; 5 | //use Pion::photon::Photon; 6 | 7 | use crate::nstruct::Nuclide; 8 | 9 | /// Generalized representation of potential decay particles 10 | #[derive(Debug, Clone)] 11 | pub enum Particle { 12 | /// Photon and frequency 13 | Photon(f64), 14 | /// Leptons including Electrons, Neutrinos, and AntiNeutrinos 15 | Lepton(Lepton), 16 | /// AntiLeptons 17 | AntiLepton(AntiLepton), 18 | /// Proton or Neutron 19 | Baryon(Baryon), 20 | /// AntiBaryons 21 | AntiBaryon(AntiBaryon), 22 | /// Alpha particle 23 | Alpha(f64), 24 | /// Deuterium nucleus 25 | Deuteron(f64), 26 | /// Tritium nucleus 27 | Triton(f64), 28 | /// Elemental nucleus 29 | Element(Nuclide, f64), 30 | // IsoElement(Isomer,f64), 31 | } 32 | 33 | impl Particle { 34 | pub fn identity(&self) -> String { 35 | match self { 36 | Particle::Photon(x) => x.to_string() + " MeV γ", 37 | Particle::Lepton(x) => match x { 38 | Lepton::Electron(y) => y.to_string() + " MeV β-", 39 | Lepton::Muon(y) => y.to_string() + " MeV μ", 40 | Lepton::Tau(y) => y.to_string() + " MeV τ", 41 | Lepton::ElectronNeutrino(y) => y.to_string() + " MeV νe", 42 | Lepton::MuNeutrino(y) => y.to_string() + " MeV νμ", 43 | Lepton::TauNeutrino(y) => y.to_string() + " MeV ντ", 44 | }, 45 | Particle::AntiLepton(x) => match x { 46 | AntiLepton::Electron(y) => y.to_string() + " MeV β+", 47 | AntiLepton::Muon(y) => y.to_string() + " MeV -μ", 48 | AntiLepton::Tau(y) => y.to_string() + " MeV -τ", 49 | AntiLepton::ElectronNeutrino(y) => y.to_string() + " MeV -ve", 50 | AntiLepton::MuNeutrino(y) => y.to_string() + " MeV -νμ", 51 | AntiLepton::TauNeutrino(y) => y.to_string() + " MeV -ντ", 52 | }, 53 | 54 | Particle::Baryon(x) => match x { 55 | Baryon::Proton(y) => y.to_string() + " MeV p", 56 | Baryon::Neutron(y) => y.to_string() + " MeV n", 57 | }, 58 | Particle::AntiBaryon(x) => match x { 59 | AntiBaryon::Proton(y) => y.to_string() + " MeV -p", 60 | AntiBaryon::Neutron(y) => y.to_string() + " MeV -n", 61 | }, 62 | Particle::Alpha(x) => x.to_string() + " MeV α", 63 | Particle::Deuteron(x) => x.to_string() + " MeV d", 64 | Particle::Triton(x) => x.to_string() + " MeV t", 65 | Particle::Element(x, z) => z.to_string() + &x.to_string(), 66 | } 67 | } 68 | 69 | #[allow(dead_code)] 70 | pub fn energy(&self) -> f64 { 71 | match self { 72 | Particle::Photon(x) => *x, 73 | Particle::Lepton(x) => match x { 74 | Lepton::Electron(y) => *y, 75 | Lepton::Muon(y) => *y, 76 | Lepton::Tau(y) => *y, 77 | Lepton::ElectronNeutrino(y) => *y, 78 | Lepton::MuNeutrino(y) => *y, 79 | Lepton::TauNeutrino(y) => *y, 80 | }, 81 | Particle::AntiLepton(x) => match x { 82 | AntiLepton::Electron(y) => *y, 83 | AntiLepton::Muon(y) => *y, 84 | AntiLepton::Tau(y) => *y, 85 | AntiLepton::ElectronNeutrino(y) => *y, 86 | AntiLepton::MuNeutrino(y) => *y, 87 | AntiLepton::TauNeutrino(y) => *y, 88 | }, 89 | 90 | Particle::Baryon(x) => match x { 91 | Baryon::Proton(y) => *y, 92 | Baryon::Neutron(y) => *y, 93 | }, 94 | Particle::AntiBaryon(x) => match x { 95 | AntiBaryon::Proton(y) => *y, 96 | AntiBaryon::Neutron(y) => *y, 97 | }, 98 | Particle::Alpha(x) => *x, 99 | Particle::Deuteron(x) => *x, 100 | Particle::Triton(x) => *x, 101 | Particle::Element(_, z) => *z, 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Iridium 2 | Atomic Physics Library 3 | 4 | ## Nuclide 5 | This is a nuclide database meant to address two issues. The lack of any nuclide databases in Rust, and the generally limited 6 | and outdated information other databases have (frequently copying data from Wikipedia or T. Gray's periodic table, which are outdated in there own right). The current version of this library has data on 3584 nuclides in their ground-base states. The primary sources are NUBASE2020[8], the ENDSF[9], and various Nuclear Data Sheets. 7 | In cases of ambiguity, estimates are made by the author based on trends from the neighboring nuclides. 8 | 9 | -Sory, J.A 10 | #### Available Data 11 | - Electronegative Values predicted for the thermochemical electronega 12 | tivity, currently the best predictor of experimental values[1]. 13 | - Pauling Electronegativity using the Pauling scale, mostly kept for completeness as 14 | the Pauling scale performs poorly in application [3]. 15 | - Allen Electronegativity using the Allen scale[2] 16 | - Electron Affinities of the elements, in kilojoules/mol 17 | - Ionization Energies of all known electron configurations 18 | - Covalent Radii The covalent radii calculated from single[5], double[6] and triple[7] bonds 19 | - Van der Waal radii approximated in crystalline structures and isolated atoms. 20 | Unlike most libraries and databases which use Bondi’s approximations to the van der Waal 21 | radius, Batsanov’s approximations are used here [4]. 22 | - Half-life, mean lifetime and decay constant for each nuclide. 23 | - Atomic mass, binding energy and mass defect for each nuclide. 24 | 25 | #### Features 26 | In addition to providing the previous information this library has some other features. 27 | - Simple modeling of decay including decay particle energies 28 | - Prediction of mass and binding energies of theorectical nuclides using the Duflo-Zuker 10-parameter mass model 29 | - Natural abundance fractions 30 | 31 | #### Usage 32 | ```rust 33 | 34 | use ::Nuclide::{Nuclide,ChemElement,Isotope,::decay::TotalDecay}; 35 | 36 | // Create mutable U-235 nuclide, mutable to permit decay. 37 | 38 | let mut u235 = Nuclide::new("U-235").unwrap(); 39 | 40 | // Or alternately 41 | let mut u235 = "U-235".parse::().unwrap(); 42 | 43 | // Approximation ionization energy of U-235 +52 in kilojoules per mole 44 | 45 | assert_eq!(u235.ionization_energies(52).unwrap(),283474.03313085996); 46 | 47 | // Model the decay over 5x10^20 seconds, total energy and particles are released 48 | 49 | let (decay_energy, decay_particles) = u235.decay::(5E+20); 50 | 51 | assert_eq!(u235.to_string(), "Pb-206") 52 | ``` 53 | 54 | #### References 55 | [1] Tantardini, C., Oganov, A.R. ”Thermochemical Electronegatives of the elements”. Nature Commu- 56 | nications 12, 2087 (2021).doi:10.1038/s41467-021-22429-0 57 | 58 | [2] Allen, L.C. ”Electronegativity is the average one-electron energy of the valence-shell electrons in 59 | ground-state free atoms”. Journal of the American Chemical Society. 111(25),1989. pp. 9003-9014. 60 | doi:10.1021/ja00207a003 61 | 62 | [3] Lynne Reed Murphy, Terry L. Meek, A. Louis Allred, and Leland C. Allen. ”Evaluation and Test 63 | of Pauling’s Electronegativity Scale”. The Journal of Physical Chemistry. vol. 104 (24), 2000. pp. 64 | 5867-5871 doi:10.1021/jp000288e 65 | 66 | [4] Batsanov, S.S. ”Van der Waals Radii of Elements”.Inorganic Materials Vol 37, 2001. pp. 871- 67 | 885.doi:10.1023/A:1011625728803 68 | 69 | [5] Pyykkö, P., Atsumi, M. (2009). Molecular Single-Bond Covalent Radii for Elements 1-118. Chemistry 70 | A European Journal, 15(1), 186–197. doi:10.1002/chem.200800987 71 | 72 | [6] Pyykkö, P., Atsumi, M. (2009). Molecular Double-Bond Covalent Radii for Elements Li–E112. Chem- 73 | istry - A European Journal, 15(46), 12770–12779. doi:10.1002/chem.200901472 74 | 75 | [7] Pyykkö, P., Riedel, S., Patzschke, M. (2005). Triple-Bond Covalent Radii. Chemistry - A European 76 | Journal, 11(12), 3511–3520. doi:10.1002/chem.20040129 77 | 78 | [8] Kondev, F.G et al. ”The NUBASE2020 evaluation of nuclear physics properties”. 2021 Chinese Phys. 79 | C45 030001 80 | 81 | [9] Brookhaven National Laboratory. https://www.nndc.bnl.gov/ensdf/. Accessed Jul 16 2021. 82 | -------------------------------------------------------------------------------- /Nuclide/src/mmodel.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Mass model computing the binding energy 3 | 4 | Translated from Duflo-Zuker's 10-parameter Fortran 90 code 5 | 6 | In: total nucleons, number of protons 7 | Out: Binding energy in MeV 8 | */ 9 | 10 | pub(crate) fn mass_model(a: usize, z: usize) -> f64 { 11 | 12 | const COEF : [f64;10] = [ 13 | 0.7043,17.7418,16.2562,37.5562,53.9017, 14 | 0.4711,2.1307, 0.0210,40.5356,6.0632 15 | ]; 16 | 17 | let mut y : [f64;2] = [0f64,0f64]; 18 | let mut oei : [f64;2] = [0f64,0f64]; 19 | let mut dei : [f64;2] = [0f64,0f64]; 20 | let mut qx : [f64;2] = [0f64,0f64]; 21 | let mut dx : [f64;2] = [0f64,0f64]; 22 | let mut pp : [f64;2] = [0f64,0f64]; 23 | let mut n2 : [f64;2] = [0f64,0f64]; 24 | let mut op : [f64;2] = [0f64,0f64]; 25 | let mut onp : [[[f64;9];2];2] = [[[0f64;9];2];2]; 26 | 27 | let n = a-z; 28 | let nuclei : [usize;2] = [n,z]; 29 | 30 | 31 | let a = n+z; 32 | let t = n.abs_diff(z); 33 | let r = (a as f64).cbrt(); 34 | let rc = r * (1.0-0.25*(t as f64/a as f64).powi(2)); 35 | let ra = rc.powi(2)/r; 36 | let z2 = (z*(z-1)) as f64; 37 | let initial_term = (-z2 + 0.76 * z2.powf(2.0/3.0))/rc; 38 | for deformed in 0..2{ 39 | 40 | let mut ju = 0f64; 41 | 42 | if deformed == 1{ 43 | ju = 4f64 44 | } 45 | 46 | let mut term : [f64;10] = [0f64;10]; 47 | let mut noc : [[f64;18];2] = [[0f64;18];2]; 48 | let mut os : [f64;2] = [0f64,0f64]; 49 | 50 | term[0] = initial_term; 51 | 52 | for idx in 0..2{ 53 | n2[idx] = (2*(nuclei[idx]/2)) as f64; 54 | let mut ncum = 0i64; 55 | let mut i = 0i64; 56 | let mut idd = 0i64; 57 | 58 | loop{ 59 | 60 | i+=1; 61 | if i%2==1{ 62 | idd = i+1; 63 | } 64 | else if i%2==0{ 65 | idd = (i*(i-2))/4; 66 | 67 | } 68 | ncum+=idd; 69 | 70 | if ncum < nuclei[idx] as i64{ 71 | noc[idx][(i-1) as usize] = idd as f64; 72 | } 73 | else if ncum >= nuclei[idx] as i64{ 74 | 75 | break; 76 | } 77 | } 78 | 79 | let imax = i+1; 80 | let mut ip = (i-1)/2; 81 | let ipm = i/2; 82 | pp[idx] = ip as f64; 83 | let moc : f64 = nuclei[idx] as f64 - ncum as f64 + idd as f64; 84 | noc[idx][(i-1) as usize] = moc-ju; 85 | noc[idx][i as usize] = ju; 86 | 87 | if i&1 == 1{ 88 | oei[idx] = (moc as i64+ (ip as i64*(ip as i64-1i64))) as f64; 89 | 90 | dei[idx] = (ip*(ip+1)+2) as f64; 91 | } 92 | else if i&1 == 0{ 93 | oei[idx] = ((moc as i64)-(ju as i64)) as f64; 94 | dei[idx] = ((ip+1)*(ip+2)+2) as f64; 95 | } 96 | 97 | 98 | qx[idx] = oei[idx]*(dei[idx]-oei[idx]-ju)/dei[idx]; 99 | 100 | dx[idx] = qx[idx]*(2.0*oei[idx]-dei[idx]); 101 | if deformed == 1{ 102 | qx[idx] = qx[idx]/dei[idx].sqrt(); 103 | } 104 | 105 | if deformed == 1{ 106 | for i in 1..(imax+1){ 107 | let ip2 = (i-1)/2; 108 | onp[idx][0][ip2 as usize]=0f64; 109 | onp[idx][1][ip2 as usize]=0f64; 110 | } 111 | 112 | } 113 | 114 | 115 | for i in 1..(imax+1){ 116 | ip = (i-1)/2; 117 | let fact = (((ip+1)*(ip+2)) as f64).sqrt(); 118 | 119 | onp[idx][0][ip as usize]+=noc[idx][(i-1) as usize]/fact; 120 | 121 | let mut vm = -1.0; 122 | if i&1==1{ 123 | vm=0.5* ip as f64; 124 | } 125 | onp[idx][1][ip as usize]+=noc[idx][(i-1) as usize]*vm; 126 | } 127 | 128 | op[idx] = 0f64; 129 | os[idx] = 0f64; 130 | for ip in 0..(ipm+1){ 131 | let den = (((ip+1)*(ip+2)) as f64).powf(3.0/2.0); 132 | let one = onp[idx][1][ip as usize]; 133 | let zero = onp[idx][0][ip as usize]; 134 | 135 | op[idx] = op[idx]+zero; 136 | 137 | os[idx] = os[idx] + one* (1.0+zero)* ((ip*ip) as f64/den) 138 | + one*(1.0-zero)*((4i64*ip as i64-5i64) as f64/den); 139 | 140 | } 141 | 142 | op[idx] = op[idx]*op[idx]; 143 | } // end loop 144 | 145 | term[1] = op[0]+op[1]; 146 | 147 | term[2] = -term[1]/ra; 148 | term[1] += os[0]+os[1]; 149 | term[3] = -(t as f64)*(t as f64+2.0)/r.powi(2); 150 | term[4] = -term[3]/ra; 151 | 152 | if deformed == 0{ 153 | term[5] = dx[0]+dx[1]; 154 | term[6] = -term[5]/ra; 155 | let px = pp[0].sqrt()+pp[1].sqrt(); 156 | term[7] = qx[0] * qx[1]*(2f64.powf(px)); 157 | } 158 | else if deformed == 1{ 159 | term[8] = qx[0]*qx[1]; 160 | } 161 | term[4] = t as f64*(1.0-t as f64)/(a as f64*ra.powi(3)) + term[4]; 162 | 163 | 164 | 165 | if(n2[0] != nuclei[0] as f64) && (n2[1] != nuclei[1] as f64){ term[9]= t as f64/a as f64;} 166 | if n > z{ 167 | if(n2[0] == nuclei[0] as f64)&&(n2[1] != nuclei[1] as f64){term[9]= 1.0-t as f64/a as f64;} 168 | if(n2[0] != nuclei[0] as f64)&&(n2[1]==nuclei[1] as f64){term[9]= 1.0;} 169 | } 170 | else { 171 | if(n2[0]==nuclei[0] as f64)&&(n2[1] != nuclei[1] as f64){term[9]= 1.0;} 172 | if(n2[0]!=nuclei[0] as f64)&&(n2[1]==nuclei[1] as f64){term[9]= 1.0-t as f64/a as f64;} 173 | } 174 | if(n2[1]==nuclei[1] as f64) && (n2[0] == nuclei[0] as f64){term[9]= 2.0-t as f64/a as f64;} 175 | 176 | 177 | for i in term[1..].iter_mut(){ 178 | *i=*i/ra; 179 | } 180 | for i in 0..10{ 181 | y[deformed]+=term[i]*COEF[i]; 182 | } 183 | }// end loop 184 | 185 | let de = y[1]-y[0]; 186 | if de > 0f64 || z < 50{ 187 | return y[0] 188 | } 189 | y[1] 190 | } 191 | -------------------------------------------------------------------------------- /Nuclide/src/decay/dhelper.rs: -------------------------------------------------------------------------------- 1 | use crate::decay::*; 2 | use crate::decay::internal::InternalDecay; 3 | use crate::nuclidedata::decay_chain::DECAY_CHAIN; 4 | use crate::rng::rand; 5 | use crate::{Nuclide,Particle}; 6 | use crate::traits::ChemElement; 7 | use crate::constant::*; 8 | use crate::mmodel::mass_model; 9 | 10 | /* 11 | 12 | Given a Nuclide, this function computes a random decay mode and the index point of 13 | the rng bound. 14 | 15 | In: Nuclide 16 | Out: Randomly selected decay, and idx point for the decay coefficient 17 | */ 18 | pub(crate) fn decay_mode_idx(input: &Nuclide) -> (u8,u8){ 19 | let mut x = 255; 20 | let mut ridx = 0u8; 21 | let idx = input.nuclide_index() * 6; 22 | let unu = DECAY_CHAIN[idx]; 23 | let doua = DECAY_CHAIN[idx + 1] + unu; 24 | let trei = DECAY_CHAIN[idx + 2] + doua; 25 | let patru = DECAY_CHAIN[idx + 3] + trei; 26 | let cinci = DECAY_CHAIN[idx + 4] + patru; 27 | let alea = rand(); 28 | let decay_vector = DECAY_CHAIN[input.nuclide_index() * 6 + 5].to_be_bytes(); 29 | 30 | if alea > 0 && alea < unu { 31 | x = decay_vector[0]; 32 | ridx = 0; 33 | } else if alea > unu && alea < doua { 34 | x = decay_vector[1]; 35 | ridx = 1; 36 | } else if alea > doua && alea < trei { 37 | x = decay_vector[2]; 38 | ridx = 2; 39 | } else if alea > trei && alea < patru { 40 | x = decay_vector[3]; 41 | ridx = 3; 42 | } else if alea > patru && alea < cinci { 43 | x = decay_vector[4]; 44 | ridx = 4; 45 | } 46 | (x,ridx) 47 | } 48 | 49 | /* 50 | Takes a mode from the above function and then computes the decay with energy and Vec 51 | */ 52 | pub(crate) fn decay_select(input : &mut Nuclide, mode: u8) -> (f64,Vec){ 53 | match mode { 54 | 1 => AlphaEmission::<1>::decay(input), 55 | 2 => ProtonEmission::<1>::decay(input), 56 | 3 => ProtonEmission::<2>::decay(input), 57 | 4 => NeutronEmission::<1>::decay(input), 58 | 59 | 5 => NeutronEmission::<2>::decay(input), 60 | 6 => ElectronCapture::<1>::decay(input), 61 | 7 => ElectronCapture::<2>::decay(input), 62 | 63 | 8 => ElectronProton::<1>::decay(input), 64 | 9 => PositronEmission::<1>::decay(input), 65 | 10 => PositronEmission::<2>::decay(input), 66 | 11 => ElectronEmission::<1>::decay(input), 67 | 12 => ElectronEmission::<2>::decay(input), 68 | 13 => ElectronNeutron::<1>::decay(input), 69 | 14 => ElectronNeutron::<2>::decay(input), 70 | 15 => ElectronNeutron::<3>::decay(input), 71 | 16 => PositronProton::<1>::decay(input), 72 | 17 => PositronProton::<2>::decay(input), 73 | 18 => PositronProton::<3>::decay(input), 74 | 19 => ElectronAlpha::decay(input), 75 | 20 => PositronAlpha::decay(input), 76 | 21 => ElectronDeuteron::decay(input), 77 | 22 => ElectronTriton::decay(input), 78 | 23 => SpontaneousFission::decay(input), 79 | 24 => ElectronFission::decay(input), 80 | 25 => PositronFission::decay(input), 81 | 26 => ClusterDecay::<61>::decay(input), // C-14 82 | 27 => ClusterDecay::<129>::decay(input), // Ne-20 83 | 28 => ClusterDecay::<133>::decay(input), // Ne-24 84 | 85 | 29 => DoubleCluster::<133,135>::decay(input), // Ne-24 + Ne-26 86 | 87 | 30 => ClusterDecay::<223>::decay(input), //Si-32 emission 88 | 31 => ClusterDecay::<225>::decay(input), //Si-34 emission 89 | 32 => ClusterDecay::<176>::decay(input), // Mg-28 emission 90 | 33 => ClusterDecay::<115>::decay(input), // F-23 emission 91 | 34 => ClusterDecay::<94>::decay(input), // O-18 emission 92 | 35 => ClusterDecay::<96>::decay(input), // O-20 emission 93 | 36 => ClusterDecay::<59>::decay(input), // C-12 emission 94 | 37 => ClusterDecay::<131>::decay(input), // Ne-22 emission 95 | 38 => ClusterDecay::<178>::decay(input), // Mg-30 emission 96 | 97 | 39 => DoubleCluster::<175,178>::decay(input), // Mg-28 + Mg-30 emission 98 | 99 | 40 => NeutronDeuteron::decay(input), 100 | 41 => NeutronTriton::decay(input), 101 | 42 => AlphaNeutron::<1>::decay(input), 102 | 43 => AlphaNeutron::<2>::decay(input), 103 | 44 => AlphaNeutron::<3>::decay(input), 104 | 45 => ElectronProton::<1>::decay(input), 105 | 46 => ElectronProton::<2>::decay(input), 106 | 47 => ElectronProton::<3>::decay(input), 107 | 48 => ProtonEmission::<3>::decay(input), 108 | 49 => ClusterDecay::<135>::decay(input), // Ne-26 109 | 50 => ElectronNeutron::<4>::decay(input), // B- 4n 110 | _ => (0f64, Vec::::new()), // if no decay then return empty data 111 | } // end match 112 | } 113 | 114 | // Determines if the DecayMode is recorded 115 | pub(crate) fn is_mode_recorded(input: &Nuclide) -> bool{ 116 | 117 | let val = DECAY_CHAIN[input.nuclide_index() * 6 + 5].to_be_bytes(); 118 | let search_val = T::decay_index(); 119 | 120 | if val[0] == search_val{ 121 | return true 122 | } 123 | if val[1] == search_val{ 124 | return true 125 | } 126 | if val[2] == search_val{ 127 | return true 128 | } 129 | if val[3] == search_val{ 130 | return true 131 | } 132 | if val[4] == search_val{ 133 | return true 134 | } 135 | false 136 | } 137 | 138 | pub(crate) fn max(x: Nuclide, y: Nuclide) -> (Nuclide, Nuclide) { 139 | if x.am() > y.am() { 140 | return (x, y); 141 | } 142 | (y, x) 143 | } 144 | 145 | pub (crate) fn theoretical_mass(p: usize, n: usize) -> f64{ 146 | ((p as f64)*PROTONMASS + (n as f64)*NEUTRONMASS)-(mass_model(p+n,p)*MeV_DALTON) 147 | } 148 | 149 | pub (crate) fn elementary_mass(p: usize, n: usize, e: usize, neu: usize) -> f64{ 150 | (p as f64)*PROTONMASS + (n as f64)*NEUTRONMASS + (e as f64)*ELECTRONMASS + (neu as f64)*NEUTRINOMASS 151 | } 152 | 153 | -------------------------------------------------------------------------------- /Nuclide/update.rs: -------------------------------------------------------------------------------- 1 | // Used to write and update the data arrays. 2 | use crate::index::SYMBOL; 3 | use crate::index::SYMBOL_INDEX; 4 | 5 | 6 | //Formatting for f64 7 | fn formatstyle_f64(x: f64, index_length: usize)->String{ 8 | 9 | let mut k = String::new(); 10 | if x.is_nan() { 11 | k = "f64::NAN".to_string(); 12 | } 13 | else if !x.is_finite(){ 14 | k= "f64::INFINITY".to_string(); 15 | } 16 | else if x == 0f64{ 17 | k="0f64".to_string(); 18 | } 19 | else{ 20 | k=x.to_string()+"f64"; 21 | } 22 | 23 | (0..(index_length-k.len())).map(|_|" ").collect::() + &k + " , " 24 | 25 | } 26 | 27 | fn formatstyle_u64(x: &u64)-> String{ 28 | let mut k = String::new(); 29 | 30 | k = x.to_string(); 31 | (0..(20-k.len())).map(|_|" ").collect::() + &k + " , " 32 | } 33 | 34 | //Formatting for u8 tuple 35 | fn stringstyle()->String{ 36 | 37 | "(0u8, 99u8, 99u8) , ".to_string() 38 | 39 | } 40 | 41 | // opens and writes file 42 | fn note(x:&str,y:&str){ 43 | use std::fs::File; 44 | use std::io::Write; 45 | 46 | let mut file = File::create(y).expect("Whoopsie, ran out of paper . . ."); 47 | 48 | println!("Wrote to {} in the local directory",y ); 49 | file.write_all(x.as_bytes()).expect("Whoopsie ran out of ink . . ."); 50 | } 51 | /* 52 | Formatting styles tablename: (index length, column count) 53 | HALF_LIFE : (35,4), ATOMIC_MASS : (17,5), (50,3) for Kilogram Elemental arrays of length 118: (7;6) 54 | 55 | 56 | */ 57 | 58 | 59 | pub fn table_string_f64(data: Vec, index_length: usize, col_count: usize, name: &str)-> String{ 60 | let mut t = data.iter().map(|x| formatstyle_f64(*x,index_length)).collect::>(); 61 | 62 | for i in 0..t.len(){ 63 | if i%col_count == col_count-1{ 64 | t[i]+="\n " 65 | } 66 | } 67 | let k = t.iter().map(|x| x.clone()).collect::(); 68 | "pub const ".to_owned() + name +" : [f64;" + &data.len().to_string() + "] = [ \n \n " + &k + "\n \n ];" 69 | } 70 | // 71 | pub fn table_string_u64(data: Vec, col_count: usize, name: &str)->String{ 72 | 73 | let mut t = data.iter().map(|x| formatstyle_u64(x)).collect::>(); 74 | for i in 0..t.len(){ 75 | if i%col_count == col_count-1{ 76 | t[i]+="\n " 77 | } 78 | } 79 | let k = t.iter().map(|x| x.clone()).collect::(); 80 | "pub const ".to_owned() + name +" : [u64;" + &data.len().to_string() + "] = [ \n \n " + &k + "\n \n ];" 81 | 82 | } 83 | 84 | //prints data vector with spaces of index_length and columns = col_count, some useful ones are Half-Life = 35, 4 and atomic mass 15,5 85 | pub fn table_print_f64(data: Vec, index_length: usize,col_count: usize, name: &str, output: &str){ 86 | let table = table_string_f64(data, index_length, col_count, name); 87 | 88 | note(&table,output) 89 | } 90 | 91 | pub fn table_print_u64(data: Vec, col_count: usize, name: &str, output: &str){ 92 | let table = table_string_u64(data, col_count, name); 93 | note(&table, output) 94 | } 95 | 96 | 97 | // changes the value at the index for a vector x 98 | fn update(x: &mut[f64],value:f64 , index: usize){ 99 | 100 | x[index]=value 101 | 102 | } 103 | 104 | //changes the value for all in x between index and the length of the updating vector, a superior version of update 105 | pub fn update_vector(x: &mut[f64], value: &[f64],index:usize){ 106 | let start_slice = &mut x[index..(value.len()+index)]; 107 | 108 | for (i,j) in start_slice.iter_mut().zip(value.iter()){ 109 | 110 | *i=*j; 111 | } 112 | } 113 | 114 | pub fn update_vector_u64(x : &mut[u64], value: &[u64], index: usize){ 115 | let start_slice = &mut x[index..(value.len()+index)]; 116 | 117 | for (i,j) in start_slice.iter_mut().zip(value.iter()){ 118 | 119 | *i=*j; 120 | } 121 | } 122 | 123 | 124 | //Finds the nuclide index to start from, this is identical to Nuclide::new 125 | 126 | fn bounds_check(x: &str, isotope:usize)->Result{ 127 | 128 | match SYMBOL.iter().position(|y| y ==&x){ 129 | Some(x)=> if isotope >= SYMBOL_INDEX[x].1 && isotope <= SYMBOL_INDEX[x].2 { 130 | return Ok(SYMBOL_INDEX[x].0+isotope-SYMBOL_INDEX[x].1) 131 | } 132 | else{ 133 | return Err("Not a known isotope".to_string()) 134 | } 135 | None=> return Err("Not a known element".to_string()) 136 | } 137 | } 138 | 139 | pub const PROTON_DECAY_MASK : u16 = 8192 ; // 1 140 | pub const NEUTRON_DECAY_MASK : u16 = 16384; // 2 141 | pub const BETA_DECAY_MASK : u16 = 24576; // 3 24576 142 | pub const POSITRON_DECAY_MASK : u16 = 32768; // 4 32768 143 | pub const ELECTRON_CAPTURE_MASK : u16 = 40960; // 5 Particle 5 == Electron Neutrino 144 | pub const ALPHA_DECAY_MASK : u16 = 49152; // 6 145 | pub const CLUSTER_DECAY_MASK : u16 = 57344; // 7 (7,14) is a particle of C-14 146 | 147 | 148 | 149 | fn join(typ : &str, number: u16)->u64{ 150 | match typ{ 151 | "P" => return (PROTON_DECAY_MASK + number ) as u64, 152 | "N" => return (NEUTRON_DECAY_MASK + number ) as u64, 153 | "B" => return (BETA_DECAY_MASK + number) as u64, 154 | "Pos"=> return (POSITRON_DECAY_MASK + number) as u64, 155 | "EC"=> return (ELECTRON_CAPTURE_MASK + number) as u64, 156 | "A" => return (ALPHA_DECAY_MASK + number) as u64, 157 | "CD"=> return (CLUSTER_DECAY_MASK + number) as u64, 158 | _=> return 0u64, 159 | } 160 | } 161 | 162 | 163 | pub fn decay_map(prob_1: f64, decay_type_1: &str, number_1: u16, decay_12: &str, numar : u16, 164 | prob_2: f64, decay_type_2: &str, number_2: u16, decay_22: &str, numar_2: u16, 165 | prob_3: f64, decay_type_3: &str, number_3: u16, decay_32: &str, numar_3: u16, 166 | prob_4: f64, decay_type_4: &str, number_4: u16, decay_42: &str, numar_4: u16, 167 | prob_5: f64, decay_type_5: &str, number_5: u16, decay_52: &str, numar_5: u16 168 | )-> Vec{ 169 | 170 | // println!("{}", (join(decay_12,numar)<<16) + join(decay_type_1, number_1)); 171 | let mut array = vec![0u64;10]; 172 | array[0] = (prob_1*18446744073709551615f64).round() as u64; 173 | array[1] = (prob_2*18446744073709551615f64).round() as u64; 174 | array[2] = (prob_3*18446744073709551615f64).round() as u64; 175 | array[3] = (prob_4*18446744073709551615f64).round() as u64; 176 | array[4] = (prob_5*18446744073709551615f64).round() as u64; 177 | array[5] = join(decay_type_1, number_1) + (join(decay_12,numar)<<16) ; 178 | array[6] = join(decay_type_2, number_2) + (join(decay_22,numar_2)<<16) ; 179 | array[7] = join(decay_type_3, number_3) + (join(decay_32,numar_3)<<16) ; 180 | array[8] = join(decay_type_4, number_4) + (join(decay_42,numar_4)<<16) ; 181 | array[9] = join(decay_type_5, number_5) + (join(decay_52,numar_5)<<16) ; 182 | array 183 | } 184 | 185 | -------------------------------------------------------------------------------- /Nuclide/src/nstruct/core.rs: -------------------------------------------------------------------------------- 1 | use crate::nuclidedata::{index::{SYMBOL,SYMBOL_INDEX},lang::*, spinparity::SPIN_PARITY}; 2 | use crate::mmodel::mass_model; 3 | use crate::constant::*; 4 | use crate::traits::{ChemElement}; 5 | 6 | 7 | 8 | 9 | 10 | /// Efficient representation of nuclide 11 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 12 | pub struct Nuclide { 13 | idx: usize, 14 | } 15 | 16 | impl std::fmt::Display for Nuclide { 17 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 18 | let (z, n) = self.isotope(); 19 | write!(f, "{}-{}", SYMBOL[z - 1], n) 20 | } 21 | } 22 | 23 | impl std::str::FromStr for Nuclide{ 24 | 25 | type Err = &'static str; 26 | 27 | fn from_str(input: &str) -> Result{ 28 | match Nuclide::new(input){ 29 | Some(x) => Ok(x), 30 | None => Err("Parsing error"), 31 | } 32 | } 33 | 34 | } 35 | 36 | impl Nuclide { 37 | 38 | /// Initializes a new Nuclide from a string representation. 39 | /// Currently the input must be of the form {Symbol}-{Nucleon count} 40 | /// ``` 41 | /// use ::Nuclide::Nuclide; 42 | /// 43 | /// let u235 = Nuclide::new("U-235").unwrap(); 44 | /// assert_eq!(u235.to_string(),"U-235"); 45 | /// ``` 46 | pub fn new(input: &str) -> Option { 47 | let [symbol, nucleons_str]: [&str; 2] = input.split('-') 48 | .collect::>() 49 | .try_into() 50 | .ok()?; 51 | 52 | let nucleons = nucleons_str.parse::().ok()?; 53 | 54 | let z_offset = SYMBOL.iter().position(|&element_str| element_str == symbol)?; 55 | let (start, a_min, a_max) = SYMBOL_INDEX[z_offset]; 56 | (a_min..=a_max).contains(&nucleons) 57 | .then_some(Nuclide {idx: start + nucleons - a_min}) 58 | } 59 | 60 | /// In : proton, neutron 61 | /// Out: Nuclide 62 | pub fn from_nucleons_unchecked(protons: usize, neutrons: usize) -> Self{ 63 | let (start, a_min, _) = SYMBOL_INDEX[protons - 1]; 64 | let a = protons + neutrons; 65 | Nuclide {idx: start + a - a_min} 66 | } 67 | 68 | /// In: proton, neutron 69 | /// Returns None if the Nuclide doesn't exist 70 | pub fn from_nucleons(protons: usize, neutrons: usize) -> Option { 71 | if protons == 0 { 72 | return None; 73 | } 74 | 75 | let (start, a_min, a_max) = *SYMBOL_INDEX.get(protons - 1)?; 76 | let a = protons + neutrons; 77 | (a_min..=a_max).contains(&a) 78 | .then_some(Nuclide {idx: start + a - a_min}) 79 | } 80 | 81 | /// Construct a nuclide from the unique index. Avoid direct use as no checks are performed to ensure that it is valid 82 | pub fn assign(idx: usize) -> Self { 83 | Self { idx } 84 | } 85 | 86 | /// Transforms a nuclide from the unique index. 87 | pub fn change(&mut self, idx: usize) { 88 | self.idx = idx; 89 | } 90 | 91 | /// Returns the approximate mass and binding energy of a nuclide, theorectical or real, using the DZ-10 mass model. 92 | pub fn create(z: usize, n: usize) -> (f64, f64) { 93 | let b_e = mass_model(z + n, z); 94 | ( 95 | (z as f64 * PROTONMASS + n as f64 * NEUTRONMASS) - (b_e / 931.36808885), 96 | b_e, 97 | ) 98 | } 99 | 100 | /// Returns the underlying unique value. Can be used in conjunction with "assign" and "change" to rapidly create or 101 | /// convert nuclides without decay 102 | pub fn nuclide_index(&self) -> usize { 103 | self.idx 104 | } 105 | 106 | /// Returns the atomic number and the nucleon count 107 | pub fn isotope(&self) -> (usize, usize) { 108 | let z = self.atomic_num() as usize; 109 | let (start, a_min, _) = SYMBOL_INDEX[z - 1]; 110 | let a = self.idx - start + a_min; 111 | (z, a) 112 | } 113 | 114 | ///Returns the element name. 115 | pub fn element_name(&self, lang: Lang) -> String { 116 | let idx = self.atomic_num().wrapping_sub(1) as usize; 117 | match lang{ 118 | Lang::En => NAME_EN[idx].to_string(), 119 | Lang::Es => NAME_ES[idx].to_string(), 120 | Lang::Pl => NAME_PL[idx].to_string(), 121 | Lang::De => NAME_DE[idx].to_string(), 122 | Lang::Fr => NAME_FR[idx].to_string(), 123 | Lang::Ru => NAME_RU[idx].to_string(), 124 | } 125 | } 126 | 127 | ///Returns the proton and neutron count 128 | pub fn proton_neutron(&self) -> (usize, usize) { 129 | let (z, a) = self.isotope(); 130 | (z, a - z) 131 | } 132 | 133 | /// Approximate neutron separation energy 134 | pub fn neutron_separation(&self) -> f64 { 135 | let (z, n) = self.proton_neutron(); 136 | mass_model(z + n, z) - mass_model(z + n - 1, z) 137 | } 138 | 139 | /// Approximate proton separation energy 140 | pub fn proton_separation(&self) -> f64 { 141 | let (z, n) = self.proton_neutron(); 142 | mass_model(z + n, z) - mass_model(z + n - 1, z - 1) 143 | } 144 | 145 | /// returns a vector of all isotopes of the element 146 | pub fn isotope_list(&self) -> Vec { 147 | let proton = self.atomic_num() as usize; 148 | let start = SYMBOL_INDEX[proton - 1].0; 149 | let delta = SYMBOL_INDEX[proton - 1].2 - SYMBOL_INDEX[proton - 1].1; 150 | let mut n_vector = vec![]; 151 | for i in 0..delta + 1 { 152 | n_vector.push(Nuclide::assign(start + i)) 153 | } 154 | n_vector 155 | } 156 | 157 | /// Returns the nuclide (if it exists) that has swapped proton-neutron count 158 | pub fn mirror(&self) -> Option { 159 | let (z, n) = self.proton_neutron(); 160 | Nuclide::from_nucleons(n, z) 161 | } 162 | /* 163 | isobar = permutations of z+1,n-1 and z-1,n+1 164 | 165 | Iterate through the symbollist 166 | 167 | 168 | z-(z-i) n+(z-i) 169 | 170 | check that n+(z-i) is valid for the point z-(z-i) 171 | */ 172 | /// Produces an iterator of all nuclides sorted by atomic number, e.g all hydrogen isotopes, all helium isotopes, ... 173 | pub fn list() -> Vec { 174 | (0..NUCLIDE_COUNT) 175 | .map(Nuclide::assign) 176 | .collect::>() 177 | } 178 | 179 | /// Produces a list of all nuclides that share the same atomic number as the selected nuclide 180 | pub fn isobar_list(&self) -> Vec { 181 | let table = Nuclide::list(); 182 | let mut isobars = vec![]; 183 | let a = self.proton_neutron().0 + self.proton_neutron().1; 184 | for i in table{ 185 | let (z, n) = i.proton_neutron(); 186 | if (z + n) == a { 187 | isobars.push(i) 188 | } 189 | } 190 | isobars 191 | } 192 | 193 | /// Produces a list of nuclides that share the same number of neutrons 194 | pub fn isotone_list(&self) -> Vec { 195 | let n = self.proton_neutron().1; 196 | 197 | let mut n_vector = vec![]; 198 | for (idx, el) in SYMBOL_INDEX.iter().enumerate() { 199 | let n_lo = el.1 - (idx + 1); 200 | let n_hi = el.2 - (idx + 1); 201 | if n >= n_lo && n <= n_hi { 202 | n_vector.push(Nuclide::from_nucleons_unchecked(idx + 1, n)) 203 | } 204 | } 205 | n_vector 206 | } 207 | 208 | ///Returns the isospin and parity in the form of a i8 pair, one of which is negatively signed for - parity 209 | pub fn spin_parity(&self) -> (i8, i8) { 210 | SPIN_PARITY[self.idx] 211 | } 212 | 213 | 214 | 215 | } 216 | 217 | -------------------------------------------------------------------------------- /Nuclide/tests/nuclidetest.rs: -------------------------------------------------------------------------------- 1 | use ::Nuclide::{Isotope, ChemElement, Nuclide}; 2 | use ::Nuclide::decay::{AlphaEmission, TotalDecay, ElectronEmission}; 3 | 4 | /* 5 | Tests to perform 3 6 | Assignment 7 | Identity 8 | Deterministic decay 9 | Forced decay, each type 10 | 11 | 12 | Side tests to perform exhaustively 13 | decaymode 14 | from nucleons Correct 15 | isotope 16 | isobar 17 | isotone 18 | identity 19 | 20 | */ 21 | 22 | const ISOTOPE_COUNT: [u8; 118] = [ 23 | 7, 9, 11, 12, 16, 16, 16, 18, 19, 20, 23, 23, 23, 24, 24, 24, 25, 26, 29, 29, 29, 29, 29, 30, 24 | 31, 32, 32, 35, 33, 33, 33, 33, 33, 33, 37, 36, 36, 36, 37, 38, 39, 39, 40, 41, 41, 42, 42, 42, 25 | 42, 42, 41, 42, 42, 43, 42, 42, 42, 41, 41, 40, 40, 41, 41, 40, 40, 39, 39, 39, 39, 38, 39, 38, 26 | 40, 41, 41, 43, 43, 44, 43, 47, 43, 43, 41, 42, 39, 39, 37, 35, 33, 32, 31, 29, 34, 32, 30, 22, 27 | 22, 20, 20, 20, 19, 17, 16, 16, 16, 16, 19, 18, 18, 18, 15, 13, 13, 8, 6, 6, 4, 3, 28 | ]; 29 | 30 | const _ISOTONE_COUNT: [u8; 179] = [ 31 | 3, 5, 6, 8, 9, 10, 11, 11, 13, 13, 14, 15, 16, 16, 17, 18, 19, 19, 19, 19, 21, 20, 20, 20, 21, 32 | 21, 22, 23, 23, 23, 23, 23, 22, 21, 21, 22, 22, 22, 23, 24, 25, 25, 24, 24, 24, 24, 25, 25, 25, 33 | 25, 25, 25, 25, 26, 27, 26, 26, 26, 25, 25, 24, 25, 25, 25, 26, 27, 28, 27, 27, 28, 28, 27, 28, 34 | 28, 29, 29, 29, 28, 28, 29, 28, 29, 29, 30, 30, 31, 31, 31, 30, 30, 31, 30, 29, 29, 28, 28, 29, 35 | 28, 27, 26, 26, 26, 26, 25, 24, 23, 24, 24, 23, 22, 22, 21, 20, 20, 19, 19, 19, 19, 19, 18, 19, 36 | 19, 18, 19, 18, 17, 18, 19, 19, 18, 18, 17, 16, 16, 16, 17, 18, 17, 16, 16, 17, 18, 17, 18, 17, 37 | 16, 16, 15, 15, 15, 15, 14, 14, 15, 15, 16, 17, 17, 15, 13, 11, 11, 10, 9, 9, 9, 8, 8, 7, 7, 8, 38 | 8, 8, 8, 8, 8, 7, 6, 1, 39 | ]; 40 | 41 | const _ISOBAR_COUNT: [u8; 295] = [ 42 | 1, 2, 3, 3, 4, 5, 5, 5, 5, 6, 6, 6, 7, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 43 | 11, 10, 11, 11, 11, 11, 12, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 13, 13, 12, 12, 13, 12, 13, 44 | 12, 13, 13, 14, 14, 14, 14, 13, 14, 13, 14, 13, 14, 13, 13, 13, 13, 13, 14, 13, 14, 14, 14, 14, 45 | 14, 14, 15, 15, 15, 15, 15, 15, 14, 15, 14, 15, 14, 15, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 46 | 15, 16, 16, 17, 16, 17, 16, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 16, 17, 17, 16, 17, 17, 17, 47 | 17, 18, 17, 18, 18, 17, 18, 17, 18, 17, 17, 17, 17, 18, 17, 18, 17, 18, 18, 17, 17, 17, 17, 18, 48 | 17, 17, 17, 17, 17, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 17, 18, 17, 18, 17, 17, 16, 16, 49 | 15, 16, 15, 16, 15, 15, 14, 14, 13, 14, 14, 14, 14, 14, 13, 13, 13, 13, 14, 14, 13, 13, 14, 13, 50 | 13, 12, 13, 13, 13, 12, 13, 12, 12, 13, 12, 12, 12, 12, 12, 12, 13, 13, 12, 12, 12, 12, 12, 12, 51 | 13, 13, 12, 12, 12, 11, 11, 10, 11, 10, 11, 10, 10, 9, 10, 9, 10, 9, 10, 9, 9, 9, 9, 9, 9, 10, 52 | 10, 10, 11, 11, 8, 8, 8, 8, 7, 8, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 5, 6, 6, 5, 5, 6, 6, 7, 53 | 6, 6, 5, 5, 4, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 1, 54 | ]; 55 | 56 | #[test] 57 | fn decay_test() { 58 | let uranium = Nuclide::new("U-235").unwrap(); 59 | let thorium = uranium.daughter::>().expect("Alpha decay of U-235 valid"); 60 | let expected = Nuclide::new("Th-231").unwrap(); 61 | assert_eq!(thorium, expected, "{thorium} != {expected}"); 62 | 63 | let pa = thorium.daughter::>().expect("Electron emission of Th-231 valid"); 64 | let expected = Nuclide::new("Pa-231").unwrap(); 65 | assert_eq!(pa, expected, "{pa} != {expected}"); 66 | } 67 | 68 | #[test] // Verifies that proton-neutron and from_nucleons are correct inverses 69 | fn initialization_test() { 70 | for i in 0..3585 { 71 | let nuclide = Nuclide::assign(i); 72 | let (z, n) = nuclide.proton_neutron(); 73 | let testnuclide = Nuclide::from_nucleons(z, n).unwrap(); 74 | assert_eq!(nuclide.nuclide_index(), testnuclide.nuclide_index()); 75 | // Verifies that new and identity are inverses 76 | assert_eq!( 77 | nuclide.nuclide_index(), 78 | Nuclide::new(&nuclide.to_string()).unwrap().nuclide_index() 79 | ) 80 | } 81 | 82 | let mut count = 0u32; //Bruteforce check of from_nucleons. Ensures that only unique values are counted 83 | for z in 0..300 { 84 | // generously exceeds the limit of 118 protons 85 | for n in 0..300 { 86 | // Exceeds the limit of 178 neutrons as well 87 | if let Some(nuclide) = Nuclide::from_nucleons(z, n) { 88 | count += 1; 89 | assert_eq!(nuclide.proton_neutron(), (z, n)); 90 | } 91 | } 92 | } 93 | 94 | assert_eq!(3585, count); // Total of 3585 nuclides means that this is correct 95 | } 96 | 97 | #[test] // verifies that isotope lists are accurate 98 | fn isotope_test() { 99 | for i in 1..119 { 100 | let p = Nuclide::from_nucleons(i, (i as f64 * 1.5).round() as usize).unwrap(); 101 | let isocount = p.isotope_list().len(); 102 | assert_eq!(isocount as u8, ISOTOPE_COUNT[i - 1]) 103 | } 104 | /* 105 | for i in 1..178{ 106 | 107 | let p = Nuclide::from_nucleons((i as f64/1.5).ceil() as usize,i).unwrap(); 108 | // println!("{}",p.identity()); 109 | let isocount = p.isotone_list().len(); 110 | assert_eq!(isocount as u8,ISOTONE_COUNT[i-1]) 111 | } 112 | */ 113 | } 114 | 115 | //#[ignore] 116 | #[test] 117 | fn mirror() { 118 | // Construct all mirrorable nuclides and show that the function correctly swaps the nucleons. 119 | // This does not test that the mirror function selects all mirrorable nuclides however 120 | 121 | let nuclides = Nuclide::list(); 122 | let mut mirrors = vec![]; 123 | let mut mirrorable = vec![]; 124 | for atom in nuclides { 125 | if let Some(mir) = atom.mirror() { 126 | mirrors.push(mir); 127 | mirrorable.push(atom); 128 | } 129 | } 130 | 131 | for (og, mir) in mirrorable.iter().zip(mirrors.iter()) { 132 | assert_eq!(*og, mir.mirror().unwrap()); 133 | assert_eq!( 134 | og.proton_neutron(), 135 | (mir.proton_neutron().1, mir.proton_neutron().0) 136 | ) 137 | } 138 | 139 | assert_eq!(mirrors.len(), 514); // Currently believed by the author to be 514 mirror nuclide pairs 140 | } 141 | 142 | /* Checks that the mass model meets the error bounds, binding energy is currently computed using the mass model 143 | so mass_defect as EV is used instead. Note that this means that the binding energy error is not a comparison against 144 | empirical values. The mass error however is, and one can see that the current model has -+ 0.2968 amu maximum error. 145 | 146 | */ 147 | // #[test] 148 | // fn mass_model() { 149 | // const MASS_ERROR: f64 = 0.29677; 150 | // const BE_ERROR: f64 = 235.01; 151 | // 152 | // let nuclides = Nuclide::list(); // List of all nuclides 153 | // 154 | // for atom in nuclides.iter() { 155 | // let (proton, neutron) = atom.proton_neutron(); 156 | // let (mass, be) = Nuclide::create(proton, neutron); 157 | // 158 | // let masshi = atom.am() + MASS_ERROR; 159 | // let masslo = atom.am() - MASS_ERROR; 160 | // 161 | // let be_hi = atom.mass_deficit_ev() + BE_ERROR; 162 | // let be_lo = atom.mass_deficit_ev() - BE_ERROR; 163 | // 164 | // assert!(mass < masshi && mass > masslo); 165 | // assert!(be < be_hi && be > be_lo); 166 | // } 167 | // } 168 | 169 | /* 170 | Checks that all potential daughter nuclides are documented and exist within the nuclide dataset 171 | 172 | This will panic if there are decays that include undocumented nuclides. This will also detect any decay looping 173 | errors where an isotope has a mode set to decay back into it's mother resulting in an infinite loop 174 | */ 175 | 176 | #[test] 177 | fn no_ghost_nuclide() { 178 | let nuclides = Nuclide::list(); 179 | 180 | for atom in nuclides.iter() { 181 | if atom.half_life::() == f64::INFINITY { 182 | // Skips nuclide if it is stable 183 | continue; 184 | } 185 | 186 | let mut radio_isotope = *atom; 187 | radio_isotope.decay::(f64::MAX); // sufficient to decay any nuclide to a stable state 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Nuclide/src/nuclidedata/index.rs: -------------------------------------------------------------------------------- 1 | /* 2 | #[rustfmt::skip] 3 | pub(crate) const NAME: [&str; 118] = [ 4 | 5 | "Hydrogen" , "Helium" , "Lithium" , "Berylium" , 6 | "Boron" , "Carbon" , "Nitrogen" , "Oxygen" , 7 | "Flourine" , "Neon" , "Sodium" , "Magnesium" , 8 | "Aluminium" , "Silicon" , "Phosphorus" , "Sulfur" , 9 | "Chlorine" , "Argon" , "Potassium" , "Calcium" , 10 | "Scandium" , "Titanium" , "Vanadium" , "Chromium" , 11 | "Manganese" , "Iron" , "Cobalt" , "Nickel" , 12 | "Copper" , "Zinc" , "Gallium" , "Germanium" , 13 | "Arsenic" , "Selenium" , "Bromine" , "Krypton" , 14 | "Rubidium" , "Strontium" , "Yttrium" , "Zirconium" , 15 | "Niobium" , "Molybdenum" , "Technetium" , "Ruthenium" , 16 | "Rhodium" , "Palladium" , "Silver" , "Cadmium" , 17 | "Indium" , "Tin" , "Antimony" , "Tellurium" , 18 | "Iodine" , "Xenon" , "Caesium" , "Barium" , 19 | "Lanthanum" , "Cerium" , "Praseodymium" , "Neodymium" , 20 | "Promethium" , "Samarium" , "Europium" , "Gadolinium" , 21 | "Terbium" , "Dysprosium" , "Holmium" , "Erbium" , 22 | "Thulium" , "Ytterbium" , "Lutetium" , "Hafnium" , 23 | "Tantalum" , "Tungsten" , "Rhenium" , "Osmium" , 24 | "Iridium" , "Platinum" , "Gold" , "Mercury" , 25 | "Thallium" , "Lead" , "Bismuth" , "Polonium" , 26 | "Astatine" , "Radon" , "Francium" , "Radium" , 27 | "Actinium" , "Thorium" , "Protactinium" , "Uranium" , 28 | "Neptunium" , "Plutonium" , "Americium" , "Curium" , 29 | "Berkelium" , "Californium" , "Einsteinium" , "Fermium" , 30 | "Mendelevium" , "Nobelium" , "Lawrencium" , "Rutherfordium" , 31 | "Dubnium" , "Seaborgium" , "Bohrium" , "Hassium" , 32 | "Meitnerium" , "Darmstadtium" , "Roentgenium" , "Copernicium" , 33 | "Nihonium" , "Flerovium" , "Moscovium" , "Livermorium" , 34 | "Tennesine" , "Oganesson" , 35 | ]; 36 | */ 37 | #[rustfmt::skip] 38 | pub const SYMBOL: [&str; 118] = [ 39 | 40 | "H" , "He" , "Li" , "Be" , "B" , "C" , "N" , "O" , "F" , 41 | "Ne" , "Na" , "Mg" , "Al" , "Si" , "P" , "S" , "Cl" , "Ar", 42 | "K" , "Ca" , "Sc" , "Ti" , "V" , "Cr" , "Mn" , "Fe" , "Co", 43 | "Ni" , "Cu" , "Zn" , "Ga" , "Ge" , "As" , "Se" , "Br" , "Kr", 44 | "Rb" , "Sr" , "Y" , "Zr" , "Nb" , "Mo" , "Tc" , "Ru" , "Rh", 45 | "Pd" , "Ag" , "Cd" , "In" , "Sn" , "Sb" , "Te" , "I" , "Xe", 46 | "Cs" , "Ba" , "La" , "Ce" , "Pr" , "Nd" , "Pm" , "Sm" , "Eu", 47 | "Gd" , "Tb" , "Dy" , "Ho" , "Er" , "Tm" , "Yb" , "Lu" , "Hf", 48 | "Ta" , "W" , "Re" , "Os" , "Ir" , "Pt" , "Au" , "Hg" , "Tl", 49 | "Pb" , "Bi" , "Po" , "At" , "Rn" , "Fr" , "Ra" , "Ac" , "Th", 50 | "Pa" , "U" , "Np" , "Pu" , "Am" , "Cm" , "Bk" , "Cf" , "Es", 51 | "Fm" , "Md" , "No" , "Lr" , "Rf" , "Db" , "Sg" , "Bh" , "Hs", 52 | "Mt" , "Ds" , "Rg" , "Cn" , "Nh" , "Fl" , "Mc" , "Lv" , "Ts", 53 | "Og" , 54 | 55 | ]; 56 | #[rustfmt::skip] // (Start,A-min,A-max) 57 | pub const SYMBOL_INDEX: [(usize, usize, usize); 118] = [ 58 | 59 | // H He Li Be 60 | ( 0, 1, 7) , ( 7, 2, 10) , ( 16, 3, 13) , ( 27, 5, 16) , 61 | // B C N O 62 | ( 39, 6, 21) , ( 55, 8, 23) , ( 71, 10, 25) , ( 87, 11, 28) , 63 | // F Ne Na Mg 64 | ( 105, 13, 31) , ( 124, 15, 34) , ( 144, 17, 39) , ( 167, 19, 41) , 65 | // Al Si P S 66 | ( 190, 21, 43) , ( 213, 22, 45) , ( 237, 24, 47) , ( 261, 26, 49) , 67 | // Cl Ar K Ca 68 | ( 285, 28, 52) , ( 310, 29, 54) , ( 336, 31, 59) , ( 365, 33, 61) , 69 | // Sc Ti V Cr 70 | ( 394, 35, 63) , ( 423, 37, 65) , ( 452, 39, 67) , ( 481, 41, 70) , 71 | // Mn Fe Co Ni 72 | ( 511, 43, 73) , ( 542, 45, 76) , ( 574, 47, 78) , ( 606, 48, 82) , 73 | // Cu Zn Ga Ge 74 | ( 641, 52, 84) , ( 674, 54, 86) , ( 707, 56, 88) , ( 740, 58, 90) , 75 | // As Se Br Kr 76 | ( 773, 60, 92) , ( 806, 63, 95) , ( 839, 65,101) , ( 876, 67,102) , 77 | // Rb Sr Y Zr 78 | ( 912, 71,106) , ( 948, 73,108) , ( 984, 75,111) , (1021, 77,114) , 79 | // Nb Mo Tc Ru 80 | (1059, 79,117) , (1098, 81,119) , (1137, 83,122) , (1177, 85,125) , 81 | // Rh Pd Ag Cd 82 | (1218, 88,128) , (1259, 90,131) , (1301, 92,133) , (1343, 94,135) , 83 | // In Sn Sb Te 84 | (1385, 96,137) , (1427, 99,140) , (1469,102,142) , (1510,104,145) , 85 | // I Xe Cs Ba 86 | (1552,106,147) , (1594,108,150) , (1637,111,152) , (1679,113,154) , 87 | // La Ce Pr Nd 88 | (1721,116,157) , (1763,119,159) , (1804,121,161) , (1845,124,163) , 89 | // Pm Sm Eu Gd 90 | (1885,126,165) , (1925,128,168) , (1966,130,170) , (2007,133,172) , 91 | // Tb Dy Ho Er 92 | (2047,135,174) , (2087,138,176) , (2126,140,178) , (2165,142,180) , 93 | // Tm Yb Lu Hf 94 | (2204,144,182) , (2243,148,185) , (2281,150,188) , (2320,153,190) , 95 | // Ta W Re Os 96 | (2358,155,194) , (2398,157,197) , (2439,159,199) , (2480,161,203) , 97 | // Ir Pt Au Hg 98 | (2523,163,205) , (2566,165,208) , (2610,168,210) , (2653,170,216) , 99 | // Tl Pb Bi Po 100 | (2700,176,218) , (2743,178,220) , (2786,184,224) , (2827,186,227) , 101 | // At Rn Fr Ra 102 | (2869,191,229) , (2908,193,231) , (2947,197,233) , (2984,201,235) , 103 | // Ac Th Pa U 104 | (3019,205,237) , (3052,208,239) , (3084,211,241) , (3115,215,243) , 105 | // Np Pu Am Cm 106 | (3144,219,252) , (3178,221,252) , (3210,223,252) , (3240,231,252) , 107 | // Bk Cf Es Fm 108 | (3262,233,254) , (3284,237,256) , (3304,239,258) , (3324,241,260) , 109 | // Md No Lr Rf 110 | (3344,244,262) , (3363,248,264) , (3380,251,266) , (3396,253,268) , 111 | // Db Sg Bh Hs 112 | (3412,255,270) , (3428,258,273) , (3444,260,278) , (3463,263,280) , 113 | // Mt Ds Rg Cn 114 | (3481,265,282) , (3499,267,284) , (3517,272,286) , (3532,276,288) , 115 | // Nh Fl Mc Lv 116 | (3545,278,290) , (3558,284,291) , (3566,287,292) , (3572,289,294) , 117 | // Ts Og 118 | (3578,291,294) , (3582,293,295) , 119 | ]; 120 | -------------------------------------------------------------------------------- /Nuclide/src/nstruct/isotope.rs: -------------------------------------------------------------------------------- 1 | use crate::traits::{ChemElement,Isotope}; 2 | use crate::nstruct::core::Nuclide; 3 | 4 | use crate::constant::*; 5 | use crate::mmodel::mass_model; 6 | use crate::decay::DecayMode; 7 | use crate::nuclidedata::half_life::HALF_LIFE; 8 | use crate::nuclidedata::decay_chain::DECAY_CHAIN; 9 | use crate::Particle; 10 | use crate::rng::rand; 11 | 12 | use crate::decay::is_mode_recorded; 13 | use crate::decay::dhelper::decay_mode_idx; 14 | use crate::decay::dhelper::decay_select; 15 | use crate::decay::decayimpl::decayindex; 16 | 17 | 18 | 19 | 20 | 21 | impl Isotope for Nuclide { 22 | 23 | 24 | fn mass_deficit(&self) -> f64 { 25 | let (p,n) = self.proton_neutron(); 26 | ( PROTONMASS* p as f64 27 | + ELECTRONMASS * p as f64 28 | + NEUTRONMASS * n as f64) 29 | - self.am() 30 | } 31 | 32 | fn binding_energy(&self) -> f64 { 33 | let (z, a) = ( 34 | self.proton_neutron().0, 35 | self.proton_neutron().0 + self.proton_neutron().1, 36 | ); 37 | mass_model(a, z) 38 | } 39 | 40 | 41 | fn branching_ratio(&self) -> f64{ 42 | let idx = self.nuclide_index() * 6 + 5; 43 | match decayindex::(idx){ 44 | Some(x) => x as f64/FLOAT_64, 45 | None => f64::NAN, 46 | } 47 | } 48 | 49 | fn half_life(&self) -> f64 { 50 | let branch = self.branching_ratio::(); 51 | HALF_LIFE[self.nuclide_index()]/branch 52 | 53 | } 54 | 55 | fn mean_lifetime(&self) -> f64 { 56 | //reciprocal of ln(2) average lifespan of a particle 57 | self.half_life::() * std::f64::consts::LOG2_E 58 | } 59 | 60 | /// Approximation of decay constant 61 | fn decay_constant(&self) -> f64 { 62 | self.mean_lifetime::().recip() 63 | } 64 | 65 | 66 | /// Returns probability of decay 67 | fn decay_probability(&self, time: f64) -> f64{ 68 | 1.0 - (-self.decay_constant::() * time).exp() 69 | } 70 | 71 | fn daughter_theoretical(&self) -> Option{ 72 | T::decay_result(self) 73 | } 74 | 75 | //lowest probability is 1/u64::MAX 76 | ///Returns true if the nuclide would have decayed in the time given. The nuclide remains unchanged 77 | fn decay_time(&self, time: f64) -> bool { 78 | let prob = 79 | ((1.0 - (-self.decay_constant::() * time).exp()) * FLOAT_64) as u64; 80 | 81 | prob > rand() 82 | } 83 | 84 | /// Returns the probable decay modes as a string 85 | fn decay_string(&self) -> String { 86 | let mut unu_alea = 87 | ((DECAY_CHAIN[self.nuclide_index() * 6] as f64 / FLOAT_64) * 100.0) 88 | .to_string(); 89 | unu_alea.truncate(4); 90 | unu_alea.push_str("% "); 91 | let mut doua_alea = ((DECAY_CHAIN[self.nuclide_index() * 6 + 1] as f64 92 | / FLOAT_64) 93 | * 100.0) 94 | .to_string(); 95 | doua_alea.truncate(4); 96 | doua_alea.push_str("% "); 97 | let mut trei_alea = ((DECAY_CHAIN[self.nuclide_index() * 6 + 2] as f64 98 | / FLOAT_64) 99 | * 100.0) 100 | .to_string(); 101 | trei_alea.truncate(4); 102 | trei_alea.push_str("% "); 103 | let mut patru_alea = ((DECAY_CHAIN[self.nuclide_index() * 6 + 3] as f64 104 | / FLOAT_64) 105 | * 100.0) 106 | .to_string(); 107 | patru_alea.truncate(4); 108 | patru_alea.push_str("% "); 109 | let mut cinci_alea = ((DECAY_CHAIN[self.nuclide_index() * 6 + 4] as f64 110 | / FLOAT_64) 111 | * 100.0) 112 | .to_string(); 113 | cinci_alea.truncate(4); 114 | cinci_alea.push_str("% "); 115 | 116 | let mut decay_str = vec![]; 117 | 118 | let decay_vector = DECAY_CHAIN[self.nuclide_index() * 6 + 5].to_be_bytes(); 119 | 120 | for i in decay_vector[..5].iter() { 121 | match i { 122 | 1 => decay_str.push("α; "), 123 | 2 => decay_str.push("p; "), 124 | 3 => decay_str.push("2p; "), 125 | 4 => decay_str.push("n; "), 126 | 5 => decay_str.push("2n; "), 127 | 6 => decay_str.push("EC; "), 128 | 7 => decay_str.push("2EC; "), 129 | 8 => decay_str.push("β− + p; "), 130 | 9 => decay_str.push("β+; "), 131 | 10 => decay_str.push("2β+; "), 132 | 11 => decay_str.push("β−; "), 133 | 12 => decay_str.push("2β−; "), 134 | 13 => decay_str.push("β− + n; "), 135 | 14 => decay_str.push("β− + 2n; "), 136 | 15 => decay_str.push("β− + 3n; "), 137 | 16 => decay_str.push("β+ + p; "), 138 | 17 => decay_str.push("β+ + 2p; "), 139 | 18 => decay_str.push("β+ + 3p; "), 140 | 19 => decay_str.push("β− + α; "), 141 | 20 => decay_str.push("β+ + α; "), 142 | 21 => decay_str.push("β− + d; "), 143 | 22 => decay_str.push("β− + t; "), 144 | 23 => decay_str.push("SF; "), 145 | 24 => decay_str.push("β− + SF; "), 146 | 25 => decay_str.push("β+ + SF; "), 147 | 26 => decay_str.push("C-14; "), 148 | 27 => decay_str.push("Ne-20; "), 149 | 28 => decay_str.push("Ne-24; "), 150 | 29 => decay_str.push("Ne-20 + Ne-24; "), 151 | 30 => decay_str.push("Si-32; "), 152 | 31 => decay_str.push("Si-34; "), 153 | 32 => decay_str.push("Mg-28; "), 154 | 33 => decay_str.push("F-23; "), 155 | 34 => decay_str.push("O-18; "), 156 | 35 => decay_str.push("O-20; "), 157 | 36 => decay_str.push("C-12; "), 158 | 37 => decay_str.push("Ne-22; "), 159 | 38 => decay_str.push("Mg-30; "), 160 | 39 => decay_str.push("Mg-28 + Mg-30; "), 161 | 40 => decay_str.push("d + n; "), 162 | 41 => decay_str.push("t + n; "), 163 | 42 => decay_str.push("α + n; "), 164 | 43 => decay_str.push("α + 2n; "), 165 | 44 => decay_str.push("α + 3n; "), 166 | 45 => decay_str.push("β− + p; "), 167 | 46 => decay_str.push("β− + 2p; "), 168 | 47 => decay_str.push("β− + 3p; "), 169 | 48 => decay_str.push("3p; "), 170 | 49 => decay_str.push("Ne-26; "), 171 | 50 => decay_str.push("β− + 4n; "), 172 | _ => decay_str.push("Null"), 173 | } 174 | } 175 | let mut decayvec = vec![]; 176 | decayvec.push(unu_alea); 177 | decayvec.push(decay_str[0].to_string()); 178 | decayvec.push(doua_alea); 179 | decayvec.push(decay_str[1].to_string()); 180 | decayvec.push(trei_alea); 181 | decayvec.push(decay_str[2].to_string()); 182 | decayvec.push(patru_alea); 183 | decayvec.push(decay_str[3].to_string()); 184 | decayvec.push(cinci_alea); 185 | decayvec.push(decay_str[4].to_string()); 186 | 187 | if decayvec[0] == "0% " { 188 | "Stable".to_string() 189 | } else { 190 | match decayvec.iter().position(|r| r == "Null") { 191 | Some(x) => decayvec.truncate(x - 1), 192 | None => decayvec.truncate(10), 193 | } 194 | decayvec.join("") 195 | } 196 | } 197 | 198 | fn daughter_energetic(&mut self) -> (f64,Vec){ 199 | T::decay(self) 200 | } 201 | 202 | 203 | fn daughter(&self) -> Option{ 204 | if is_mode_recorded::(self){ 205 | return T::decay_result(self) 206 | } 207 | None 208 | } 209 | 210 | /// Q-value (total energy) of a nuclear decay, regardless of whether it is observed 211 | /// # NAN 212 | /// Returns NAN if this decay mode results in a nonexistent nuclide 213 | fn decay_q(&self) -> f64{ 214 | T::q_value(self) 215 | } 216 | 217 | /** 218 | Returns the name and isotope number of the nuclide 219 | 220 | ``` 221 | use ::Nuclide::{Nuclide, Isotope,::decay::TotalDecay}; 222 | let mut uranium = "U-238".parse::().unwrap(); 223 | 224 | // total particle energy of the nuclide and vector of decay particles 225 | let (particle_energy,particle_vector) = uranium.decay::(5E+20); 226 | 227 | // final nuclide in the U-238 decay series 228 | assert_eq!(uranium.to_string(), "Pb-206"); 229 | ``` 230 | */ 231 | fn decay(&mut self, mut time: f64) -> (f64, Vec) { 232 | let mut total_energy = 0f64; 233 | let mut particlevec = vec![]; 234 | 235 | if T::decay_index() == 254u8{ 236 | 237 | loop { 238 | 239 | // Obtain index of decay branch 240 | let (b,b_idx) = decay_mode_idx(self); 241 | 242 | let idx = self.nuclide_index() * 6usize + b_idx as usize; 243 | 244 | let ratio = (DECAY_CHAIN[idx] as f64)/FLOAT_64; 245 | 246 | let mean_time = (HALF_LIFE[self.nuclide_index()]/ratio) * std::f64::consts::LOG2_E; 247 | 248 | if mean_time >= time || mean_time == f64::INFINITY{ 249 | return (total_energy,particlevec) 250 | } 251 | 252 | let (energy, p_vector) = decay_select(self,b); 253 | particlevec.extend_from_slice(&p_vector[..]); 254 | total_energy+=energy; 255 | time-=mean_time; 256 | 257 | } 258 | 259 | } // end if 260 | 261 | loop{ 262 | 263 | if !is_mode_recorded::(self){ 264 | return (total_energy,particlevec) 265 | } 266 | 267 | let mean_time = self.mean_lifetime::(); 268 | 269 | if mean_time >= time || mean_time.is_infinite() || mean_time.is_nan(){ 270 | return (total_energy,particlevec) 271 | } 272 | 273 | let (energy, p_vector) = T::decay(self); 274 | 275 | particlevec.extend_from_slice(&p_vector[..]); 276 | total_energy+=energy; 277 | 278 | time-=mean_time; 279 | } 280 | } 281 | 282 | } 283 | -------------------------------------------------------------------------------- /Nuclide/src/decay/type.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Decay modes, For efficiency purposes absolutely zero checks are performance 4 | ZZZAAAM syntax acceptance 5 | Isomers 6 | 7 | 8 | check if electron neutrino and electron have same energy output/mass 9 | 10 | /* 11 | Correct formula for particle energies 12 | 13 | total_energy = daughterenergy + n*particle + n*particle2 14 | particle_energy = n*particle + n*particle2; 15 | 16 | To solve for particle mass 17 | 18 | particlemass* 19 | */ 20 | 21 | DecayStruct should have an All variant 22 | 23 | 24 | Functions to implement 25 | // return NAN if DecayMode not possible 26 | fn branching_ratio -> f64 27 | 28 | fn parent_list -> Vec 29 | // Vector of potential daughters no energies 30 | fn daughter_list -> Vec 31 | 32 | fn daughter_list_energetic -> Vec> 33 | // returns 34 | fn unchecked_decay -> 35 | 36 | // Attempts to decay nuclide returns None if it is not a supported branch 37 | fn single_decay -> Option 38 | 39 | // Continously applies the same decay mode 40 | fn continous_decay -> 41 | 42 | // Returns Nan for All or if separation is not possible 43 | fn separation_energy -> f64 44 | 45 | // Halflife for the decay branch, inf if stable and NaN if decaymode is not supported 46 | fn half_life -> f64 47 | 48 | // Decay constant for the decay branch, inf if stable and NaN if decaymode is not supported 49 | fn decay_constant -> f64 50 | // Replaces decay_time, returns the probability of decay for given branch. Zero if stable or branch not supported 51 | fn decay_probability(T) 52 | 53 | 54 | Constructing a parent list 55 | 56 | Enumerate all decay possibilities 57 | Change the nuclide to find the parent, then check possible decay modes that lead to the daughter 58 | Fission is not counted 59 | 60 | */ 61 | use crate::nuclidedata::nuclidestruct::Nuclide; 62 | use crate::nuclidedata::decaymodes::*; 63 | //use crate::atom::Atom; 64 | use crate::nuclidedata::decay_chain::DECAY_CHAIN; 65 | use crate::Particle; 66 | 67 | // Given an u64 representation of the decay modes it returns the corresponding u64 for 68 | pub(crate) fn decayindex(idx: usize, decay_rep: u64) -> Option{ 69 | let modes = DECAY_CHAIN[idx].to_be_bytes(); 70 | let d_mode = T::decay_index() as u8; 71 | 72 | if modes[0] == d_mode{ 73 | return Some(DECAY_CHAIN[idx-5]) 74 | } 75 | if modes[1] == d_mode{ 76 | return Some(DECAY_CHAIN[idx-4]) 77 | } 78 | if modes[2] == d_mode{ 79 | return Some(DECAY_CHAIN[idx-3]) 80 | } 81 | if modes[3] == d_mode{ 82 | return Some(DECAY_CHAIN[idx-2]) 83 | } 84 | if modes[4] == d_mode{ 85 | return Some(DECAY_CHAIN[idx-1]) 86 | } 87 | return None 88 | } 89 | 90 | 91 | pub(crate) mod private{ 92 | //use crate::Nuclide; 93 | //use crate::Particle; 94 | use crate::*; 95 | use crate::nuclidedata::decaymodes::*; 96 | pub trait InternalDecay { 97 | fn decay(x: &mut Nuclide) -> (f64,Vec); 98 | // Decay_index return u32::MAX if not supported by system 254 for all decay modes 99 | fn decay_index() -> u32; 100 | // fn unchecked_decay_result(x: &Nuclide) -> Nuclide; 101 | // fn decay_result(x: &Nuclide) -> Option; 102 | } 103 | 104 | impl InternalDecay for ProtonEmission{ 105 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 106 | proton_emission(x,K) 107 | } 108 | 109 | fn decay_index() -> u32{ 110 | if K == 1{ 111 | return 2 112 | } 113 | 114 | if K == 2{ 115 | return 3 116 | } 117 | 118 | return u32::MAX 119 | } 120 | 121 | 122 | 123 | } 124 | 125 | impl InternalDecay for NeutronEmission{ 126 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 127 | neutron_emission(x,K) 128 | } 129 | 130 | fn decay_index() -> u32{ 131 | if K == 1{ 132 | return 4 133 | } 134 | if K == 2{ 135 | return 5 136 | } 137 | return u32::MAX 138 | } 139 | } 140 | 141 | impl InternalDecay for NeutronDeuteron{ 142 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 143 | neutron_deuteron(x) 144 | } 145 | // FIXME 146 | fn decay_index() -> u32{ 147 | return u32::MAX 148 | } 149 | } 150 | 151 | impl InternalDecay for NeutronTriton{ 152 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 153 | neutron_triton(x) 154 | } 155 | // FIXME 156 | fn decay_index() -> u32{ 157 | return u32::MAX 158 | } 159 | } 160 | 161 | impl InternalDecay for AlphaEmission{ 162 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 163 | alpha_emission(x,K) 164 | } 165 | 166 | fn decay_index() -> u32{ 167 | if K == 1{ 168 | return 1 169 | } 170 | return u32::MAX 171 | } 172 | } 173 | 174 | impl InternalDecay for AlphaNeutron{ 175 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 176 | alpha_neutron(x,K) 177 | } 178 | // FIXME 179 | fn decay_index() -> u32{ 180 | return u32::MAX 181 | } 182 | } 183 | 184 | impl InternalDecay for ElectronCapture{ 185 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 186 | electron_capture(x,K) 187 | } 188 | 189 | fn decay_index() -> u32{ 190 | if K == 1{ 191 | return 6 192 | } 193 | if K == 2{ 194 | return 7 195 | } 196 | return u32::MAX 197 | } 198 | } 199 | 200 | impl InternalDecay for ElectronEmission{ 201 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 202 | electron_emission(x,K) 203 | } 204 | 205 | fn decay_index() -> u32{ 206 | if K == 1{ 207 | return 11 208 | } 209 | if K == 2{ 210 | return 12 211 | } 212 | return u32::MAX 213 | } 214 | 215 | } 216 | 217 | impl InternalDecay for ElectronNeutron{ 218 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 219 | electron_neutron(x,K) 220 | } 221 | 222 | fn decay_index() -> u32{ 223 | if K == 1{ 224 | return 13 225 | } 226 | if K == 2{ 227 | return 14 228 | } 229 | if K == 3{ 230 | return 15 231 | } 232 | return u32::MAX 233 | } 234 | 235 | } 236 | 237 | impl InternalDecay for ElectronProton{ 238 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 239 | electron_proton(x,K) 240 | } 241 | // FIXME 242 | fn decay_index() -> u32{ 243 | return u32::MAX 244 | } 245 | 246 | } 247 | 248 | impl InternalDecay for ElectronAlpha{ 249 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 250 | electron_alpha(x) 251 | } 252 | 253 | fn decay_index() -> u32{ 254 | return 19 255 | } 256 | } 257 | 258 | impl InternalDecay for ElectronDeuteron{ 259 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 260 | electron_deuteron(x) 261 | } 262 | 263 | fn decay_index() -> u32{ 264 | return 21 265 | } 266 | } 267 | 268 | impl InternalDecay for ElectronTriton{ 269 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 270 | electron_triton(x) 271 | } 272 | 273 | fn decay_index() -> u32{ 274 | return 22 275 | } 276 | } 277 | 278 | impl InternalDecay for ElectronFission{ 279 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 280 | electron_fission(x) 281 | } 282 | 283 | fn decay_index() -> u32{ 284 | return 24 285 | } 286 | } 287 | 288 | impl InternalDecay for PositronEmission{ 289 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 290 | positron_emission(x,K) 291 | } 292 | 293 | fn decay_index() -> u32{ 294 | if K == 1{ 295 | return 9 296 | } 297 | if K == 2{ 298 | return 10 299 | } 300 | return u32::MAX 301 | } 302 | } 303 | 304 | impl InternalDecay for PositronProton{ 305 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 306 | positron_proton(x,K) 307 | } 308 | 309 | fn decay_index() -> u32{ 310 | if K == 1{ 311 | return 16 312 | } 313 | if K == 1{ 314 | return 17 315 | } 316 | if K == 1{ 317 | return 18 318 | } 319 | return u32::MAX 320 | } 321 | } 322 | 323 | impl InternalDecay for PositronAlpha{ 324 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 325 | positron_alpha(x) 326 | } 327 | 328 | fn decay_index() -> u32{ 329 | return 20 330 | } 331 | } 332 | 333 | impl InternalDecay for PositronFission{ 334 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 335 | positron_fission(x) 336 | } 337 | 338 | fn decay_index() -> u32{ 339 | return 25 340 | } 341 | } 342 | 343 | impl InternalDecay for SpontaneousFission{ 344 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 345 | spontaneous_fission(x) 346 | } 347 | 348 | fn decay_index() -> u32{ 349 | return 23 350 | } 351 | } 352 | 353 | impl InternalDecay for ClusterDecay{ 354 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 355 | cluster_decay(x,&Nuclide::assign(K)) 356 | } 357 | 358 | fn decay_index() -> u32{ 359 | if K == 60{ 360 | return 26 361 | } 362 | if K == 128{ 363 | return 27 364 | } 365 | if K == 132{ 366 | return 28 367 | } 368 | if K == 224{ 369 | return 30 370 | } 371 | if K == 226{ 372 | return 31 373 | } 374 | return u32::MAX 375 | } 376 | } 377 | 378 | impl InternalDecay for DoubleCluster{ 379 | fn decay(x : &mut Nuclide) -> (f64,Vec){ 380 | double_cluster(x,&Nuclide::assign(D), &Nuclide::assign(S)) 381 | } 382 | 383 | fn decay_index() -> u32{ 384 | if D == 132 && S == 134{ 385 | return 29 386 | } 387 | if D == 134 && S == 132{ 388 | return 29 389 | } 390 | return u32::MAX 391 | } 392 | } // end trait 393 | 394 | } // end pub mod 395 | 396 | pub trait DecayMode : private::InternalDecay{} 397 | 398 | impl DecayMode for ProtonEmission{} 399 | impl DecayMode for NeutronEmission{} 400 | impl DecayMode for AlphaEmission{} 401 | impl DecayMode for AlphaNeutron{} 402 | impl DecayMode for NeutronDeuteron{} 403 | impl DecayMode for NeutronTriton{} 404 | impl DecayMode for ElectronCapture{} 405 | impl DecayMode for ElectronEmission{} 406 | impl DecayMode for ElectronNeutron{} 407 | impl DecayMode for ElectronProton{} 408 | impl DecayMode for ElectronAlpha{} 409 | impl DecayMode for ElectronDeuteron{} 410 | impl DecayMode for ElectronTriton{} 411 | impl DecayMode for ElectronFission{} 412 | impl DecayMode for PositronEmission{} 413 | impl DecayMode for PositronProton{} 414 | impl DecayMode for PositronAlpha{} 415 | impl DecayMode for PositronFission{} 416 | impl DecayMode for SpontaneousFission{} 417 | impl DecayMode for ClusterDecay{} 418 | impl DecayMode for DoubleCluster{} 419 | -------------------------------------------------------------------------------- /Nuclide/src/nuclidedata/lang.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 4 | */ 5 | 6 | 7 | /// Languages 8 | /// 9 | /// English Spanish,French, Russian, German, and Polish are supported 10 | #[repr(u8)] 11 | #[derive(Clone,Copy,Debug,PartialEq)] 12 | pub enum Lang{ 13 | En, 14 | Es, 15 | Fr, 16 | Ru, 17 | Pl, 18 | De 19 | } 20 | 21 | 22 | impl std::str::FromStr for Lang{ 23 | 24 | type Err = &'static str; 25 | 26 | fn from_str(input: &str) -> Result{ 27 | let x = input.to_lowercase(); 28 | 29 | match x.as_str(){ 30 | "en" => Ok(Self::En), 31 | "english" => Ok(Self::En), 32 | "es" => Ok(Self::Es), 33 | "español" => Ok(Self::Es), // Native language 34 | "espanol" => Ok(Self::Es), // Native language using English keyboard 35 | "fr" => Ok(Self::Fr), 36 | "français" => Ok(Self::Fr), 37 | "francais" => Ok(Self::Fr), // With English keyboard 38 | "pl" => Ok(Self::Pl), 39 | "polski" => Ok(Self::Pl), 40 | "de" => Ok(Self::De), 41 | "deutsch" => Ok(Self::De), 42 | "ru" => Ok(Self::Ru), 43 | "русский" => Ok(Self::Ru), 44 | _=> Err("Not a supported language; Try using ISO-639-1:2002 notation") 45 | } 46 | } 47 | } 48 | 49 | 50 | #[rustfmt::skip] 51 | pub(crate) const NAME_EN: [&str; 118] = [ 52 | 53 | "Hydrogen" , "Helium" , "Lithium" , "Berylium" , 54 | "Boron" , "Carbon" , "Nitrogen" , "Oxygen" , 55 | "Flourine" , "Neon" , "Sodium" , "Magnesium" , 56 | "Aluminium" , "Silicon" , "Phosphorus" , "Sulfur" , 57 | "Chlorine" , "Argon" , "Potassium" , "Calcium" , 58 | "Scandium" , "Titanium" , "Vanadium" , "Chromium" , 59 | "Manganese" , "Iron" , "Cobalt" , "Nickel" , 60 | "Copper" , "Zinc" , "Gallium" , "Germanium" , 61 | "Arsenic" , "Selenium" , "Bromine" , "Krypton" , 62 | "Rubidium" , "Strontium" , "Yttrium" , "Zirconium" , 63 | "Niobium" , "Molybdenum" , "Technetium" , "Ruthenium" , 64 | "Rhodium" , "Palladium" , "Silver" , "Cadmium" , 65 | "Indium" , "Tin" , "Antimony" , "Tellurium" , 66 | "Iodine" , "Xenon" , "Caesium" , "Barium" , 67 | "Lanthanum" , "Cerium" , "Praseodymium" , "Neodymium" , 68 | "Promethium" , "Samarium" , "Europium" , "Gadolinium" , 69 | "Terbium" , "Dysprosium" , "Holmium" , "Erbium" , 70 | "Thulium" , "Ytterbium" , "Lutetium" , "Hafnium" , 71 | "Tantalum" , "Tungsten" , "Rhenium" , "Osmium" , 72 | "Iridium" , "Platinum" , "Gold" , "Mercury" , 73 | "Thallium" , "Lead" , "Bismuth" , "Polonium" , 74 | "Astatine" , "Radon" , "Francium" , "Radium" , 75 | "Actinium" , "Thorium" , "Protactinium" , "Uranium" , 76 | "Neptunium" , "Plutonium" , "Americium" , "Curium" , 77 | "Berkelium" , "Californium" , "Einsteinium" , "Fermium" , 78 | "Mendelevium" , "Nobelium" , "Lawrencium" , "Rutherfordium" , 79 | "Dubnium" , "Seaborgium" , "Bohrium" , "Hassium" , 80 | "Meitnerium" , "Darmstadtium" , "Roentgenium" , "Copernicium" , 81 | "Nihonium" , "Flerovium" , "Moscovium" , "Livermorium" , 82 | "Tennesine" , "Oganesson" , 83 | ]; 84 | 85 | 86 | #[rustfmt::skip] 87 | pub(crate) const NAME_ES : [&str; 118] = [ 88 | 89 | "Hidrógeno" , "Helio" , "Litio" , "Berilio" , 90 | "Boro" , "Carbono" , "Nitrógeno" , "Oxigeno" , 91 | "Flúor" , "Neón" , "Sodio" , "Magnesio" , 92 | "Aluminio" , "Silicio" , "Fósforo" , "Azufre" , 93 | "Cloro" , "Argón" , "Potasio" , "Calcio" , 94 | "Escandio" , "Titanio" , "Vanadio" , "Cromo" , 95 | "Manganeso" , "Hierro" , "Cobalto" , "Niquel" , 96 | "Cobre" , "Zinc" , "Gallio" , "Germanio" , 97 | "Arsénico" , "Selenio" , "Bromo" , "Kriptón" , 98 | "Rubidio" , "Estroncio" , "Ytrio" , "Zirconio" , 99 | "Niobio" , "Molibdeno" , "Tecnecio" , "Rutenio" , 100 | "Rodio" , "Palladio" , "Palta" , "Cadmio" , 101 | "Indio" , "Estaño" , "Antimonio" , "Telurio" , 102 | "Yodo" , "Xenón" , "Cesio" , "Bario" , 103 | "Lantano" , "Cerio" , "Praseodimio" , "Neodimio" , 104 | "Prometio" , "Samario" , "Europio" , "Gadolinio" , 105 | "Terbio" , "Disprosio" , "Holmio" , "Erbio" , 106 | "Tulio" , "Yterbio" , "Lutecio" , "Hafnio" , 107 | "Tantalio" , "Tungsteno" , "Renio" , "Osmio" , 108 | "Iridio" , "Platino" , "Oro" , "Mercurio" , 109 | "Talio" , "Plomo" , "Bismuto" , "Polonio" , 110 | "Astato" , "Radón" , "Francio" , "Radio" , 111 | "Actinio" , "Torio" , "Protactinio" , "Uranio" , 112 | "Neptunio" , "Plutonio" , "Americio" , "Curio" , 113 | "Berkelio" , "Californio" , "Einsteinio" , "Fermio" , 114 | "Mendelevio" , "Nobelio" , "Laurencio" , "Rutherfordio" , 115 | "Dubnium" , "Seaborgio" , "Bohrio" , "Hassio" , 116 | "Meitnerio" , "Darmstadtio" , "Roentgenio" , "Copernicio" , 117 | "Nihonio" , "Flerovio" , "Moscovio" , "Livermorio" , 118 | "Teneso" , "Oganesón" , 119 | ]; 120 | 121 | #[rustfmt::skip] 122 | pub(crate) const NAME_FR: [&str; 118] = [ 123 | 124 | "Hydrogène" , "Hélium" , "Lithium" , "Béryllium" , 125 | "Bore" , "Carbone" , "Azote" , "Oxygène" , 126 | "Fluor" , "Néon" , "Sodium" , "Magnésium" , 127 | "Aluminium" , "Silicium" , "Phosphore" , "Soufre" , 128 | "Chlore" , "Argon" , "Potassium" , "Calcium" , 129 | "Scandium" , "Titane" , "Vanadium" , "Chrome" , 130 | "Manganèse" , "Fer" , "Cobalt" , "Nickel" , 131 | "Cuivre" , "Zinc" , "Gallium" , "Germanium" , 132 | "Arsenic" , "Sélénium" , "Brome" , "Krypton" , 133 | "Rubidium" , "Strontium" , "Yttrium" , "Zirconium" , 134 | "Niobium" , "Molybdène" , "Technétium" , "Ruthénium" , 135 | "Rhodium" , "Palladium" , "Argent" , "Cadmium" , 136 | "Indium" , "Étain" , "Antimoine" , "Tellure" , 137 | "Iode" , "Xénon" , "Césium" , "Baryum" , 138 | "Lanthane" , "Cérium" , "Praséodyme" , "Néodyme" , 139 | "Prométhium" , "Samarium" , "Europium" , "Gadolinium" , 140 | "Terbium" , "Dysprosium" , "Holmium" , "Erbium" , 141 | "Thulium" , "Ytterbium" , "Lutécium" , "Hafnium" , 142 | "Tantale" , "Tungsténe" , "Rhenium" , "Osmium" , 143 | "Iridium" , "Platine" , "Or" , "Mercure" , 144 | "Thallium" , "Plomb" , "Bismuth" , "Polonium" , 145 | "Astate" , "Radon" , "Francium" , "Radium" , 146 | "Actinium" , "Thorium" , "Protactinium" , "Uranium" , 147 | "Neptunium" , "Plutonium" , "Américium" , "Curium" , 148 | "Berkélium" , "Californium" , "Einsteinium" , "Fermium" , 149 | "Mendélévium" , "Nobélium" , "Lawrencium" , "Rutherfordium" , 150 | "Dubnium" , "Seaborgium" , "Bohrium" , "Hassium" , 151 | "Meitnérium" , "Darmstadtium" , "Roentgenium" , "Copernicium" , 152 | "Nihonium" , "Flérovium" , "Moscovium" , "Livermorium" , 153 | "Tennesse" , "Oganesson" , 154 | ]; 155 | 156 | 157 | 158 | #[rustfmt::skip] 159 | pub(crate) const NAME_PL: [&str; 118] = [ 160 | 161 | "Wodór" , "Hel" , "Lit" , "Beryl" , 162 | "Bor" , "Węgiel" , "Azot" , "Tlen" , 163 | "Fluor" , "Neon" , "Sód" , "Magnez" , 164 | "Glin" , "Krzem" , "Fosfor" , "Siarka" , 165 | "Chlor" , "Argon" , "Potas" , "Wapń" , 166 | "Skand" , "Tytan" , "Wanad" , "Chrom" , 167 | "Mangan" , "Żelazo" , "Kobalt" , "Nikiel" , 168 | "Miedź" , "Cynk" , "Gal" , "German" , 169 | "Arsen" , "Selenium" , "Bromine" , "Krypton" , 170 | "Rubid" , "Stront" , "Itr" , "Cyrkon" , 171 | "Niob" , "Molibden" , "Technet " , "Ruten" , 172 | "Rod" , "Pallad" , "Srebro" , "Kadm" , 173 | "Ind" , "Cyna" , "Antymon" , "Tellur" , 174 | "Jod" , "Ksenon" , "Cez" , "Bar" , 175 | "Lantan" , "Cer" , "Prazeodym" , "Neodym" , 176 | "Promet" , "Samar" , "Europ" , "Gadolin" , 177 | "Terb" , "Dysproz" , "Holm" , "Erb" , 178 | "Thul" , "Iterb" , "Lutet" , "Hafn" , 179 | "Tantal" , "Wolfram" , "Ren" , "Osm " , 180 | "Iryd" , "Platyna" , "Złoto" , "Rtęć" , 181 | "Tal" , "Ołów" , "Bizmut" , "Polon" , 182 | "Astat" , "Radon" , "Frans" , "Rad" , 183 | "Aktyn" , "Tor" , "Protaktyn" , "Uran" , 184 | "Neptun" , "Pluton" , "Ameryk" , "Kiur" , 185 | "Berkel" , "Kaliforn" , "Einstein" , "Ferm" , 186 | "Mendelew" , "Nobel" , "Lorens" , "Rutherford" , 187 | "Dubn" , "Seaborg" , "Bohr" , "Has" , 188 | "Meitner" , "Darmsztadt" , "Roentgen" , "Kopernik" , 189 | "Nihon" , "Flerow" , "Moscow" , "Liwermor" , 190 | "Tenes" , "Oganeson" , 191 | ]; 192 | 193 | #[rustfmt::skip] 194 | pub(crate) const NAME_DE: [&str; 118] = [ 195 | 196 | "Hydrogen" , "Helium" , "Lithium" , "Beryllium" , 197 | "Bor" , "Kohlenstoff" , "Stickstoff" , "Sauerstoff" , 198 | "Fluor" , "Neon" , "Natrium" , "Magnesium" , 199 | "Aluminium" , "Silicium" , "Phosphor" , "Schwefel" , 200 | "Chlor" , "Argon" , "Kalium" , "Calcium" , 201 | "Scandium" , "Titan" , "Vanadium" , "Chrom" , 202 | "Mangan" , "Eisen" , "Cobalt" , "Nickel" , 203 | "Kupfer" , "Zink" , "Gallium" , "Germanium" , 204 | "Arsen" , "Selen" , "Brom" , "Krypton" , 205 | "Rubidium" , "Strontium" , "Yttrium" , "Zirkonium" , 206 | "Niob" , "Molybdän" , "Technetium" , "Ruthenium" , 207 | "Rhodium" , "Palladium" , "Silber" , "Cadmium" , 208 | "Indium" , "Zinn" , "Antimon" , "Tellur" , 209 | "Iod" , "Xenon" , "Cesium" , "Barium" , 210 | "Lanthan" , "Cer" , "Praseodym" , "Neodym" , 211 | "Promethium" , "Samarium" , "Europium" , "Gadolinium" , 212 | "Terbium" , "Dysprosium" , "Holmium" , "Erbium" , 213 | "Thulium" , "Ytterbium" , "Lutetium" , "Hafnium" , 214 | "Tantal" , "Wolfram" , "Rhenium" , "Osmium" , 215 | "Iridium" , "Platin" , "Gold" , "Quecksilber" , 216 | "Thallium" , "Blei" , "Bismut" , "Polonium" , 217 | "Astat" , "Radon" , "Francium" , "Radium" , 218 | "Actinium" , "Thorium" , "Protactinium" , "Uran" , 219 | "Neptunium" , "Plutonium" , "Americium" , "Curium" , 220 | "Berkelium" , "Californium" , "Einsteinium" , "Fermium" , 221 | "Mendelevium" , "Nobelium" , "Lawrencium" , "Rutherfordium" , 222 | "Dubnium" , "Seaborgium" , "Bohrium" , "Hassium" , 223 | "Meitnerium" , "Darmstadtium" , "Roentgenium" , "Copernicium" , 224 | "Nihonium" , "Flerovium" , "Moscovium" , "Livermorium" , 225 | "Tennessine" , "Oganesson" 226 | ]; 227 | 228 | #[rustfmt::skip] 229 | pub(crate) const NAME_RU: [&str; 118] = [ 230 | 231 | "Водород" , "Гелий" , "Литий" , "Бериллий" , 232 | "Бор" , "Углерод" , "Азот" , "Кислород" , 233 | "Фтор" , "Неон" , "Натрий" , "Магний" , 234 | "Алюминий" , "Кремний" , "Фосфор" , "Сера" , 235 | "Хлор" , "Аргон" , "Калий" , "Кальций" , 236 | "Скандий" , "Титан" , "Ванадий" , "Хром" , 237 | "Марганец" , "Железо" , "Кобальт" , "Никель" , 238 | "Медь" , "Цинк" , "Галлий" , "Германий" , 239 | "Мышьяк" , "Селен" , "Бром" , "Криптон" , 240 | "Рубидий" , "Стронций" , "Иттрий" , "Цирконий" , 241 | "Ниобий" , "Молибден" , "Технеций" , "Рутений" , 242 | "Родий" , "Палладий" , "Серебро" , "Кадмий" , 243 | "Индий" , "Олово" , "Сурьма" , "Теллур" , 244 | "Иод" , "Ксенон" , "Цезий" , "Барий" , 245 | "Лантан" , "Церий" , "Празеодим" , "Неодим" , 246 | "Прометий" , "Самарий" , "Европий" , "Гадолиний" , 247 | "Тербий" , "Диспрозий" , "Гольмий" , "Эрбий" , 248 | "Тулий" , "Иттербий" , "Лютеций" , "Гафний" , 249 | "Тантал" , "Вольфрам" , "Рений" , "Осмий" , 250 | "Иридий" , "Платина" , "Золото" , "Ртуть" , 251 | "Таллий" , "Свинец" , "Висмут" , "Полоний" , 252 | "Астат" , "Радон" , "Франций" , "Радий" , 253 | "Актиний" , "Торий" , "Протактиний" , "Уран" , 254 | "Нептуний" , "Плутоний" , "Америций" , "Кюрий" , 255 | "Берклий" , "Калифорний" , "Эйнштейний" , "Фермий" , 256 | "Менделевий" , "Нобелий" , "Лоуренсий" , "Резерфордий" , 257 | "Дубний" , "Сиборгий" , "Борий" , "Хассий" , 258 | "Мейтнерий" , "Дармштадтий" , "Рентгений" , "Коперниций" , 259 | "Нихоний" , "Флеровий" , "Московий" , "Ливерморий" , 260 | "Теннессин" , "Оганесон" , 261 | ]; 262 | -------------------------------------------------------------------------------- /Nuclide/src/nuclidedata/elemental.rs: -------------------------------------------------------------------------------- 1 | //kj mol 2 | 3 | #[rustfmt::skip] 4 | pub(crate) const ELECTRON_AFFINITY : [f64;118] = [ 5 | 6 | 72.769 , -48.0 , 59.6326 , -48.0 , 26.989 , 7 | 121.7763 , -6.8 , 140.9760 , 328.1649 , -116.0 , 8 | 52.867 , -40.0 , 41.762 , 134.068 , 72.037 , 9 | 200.410 , 348.575 , -96.0 , 48.383 , 2.37 , 10 | 18.0 , 7.289 , 50.911 , 65.21 , -50.0 , 11 | 14.785 , 63.898 , 111.65 , 119.235 , -58.0 , 12 | 29.061 , 118.935 , 77.65 , 194.958 , 324.536 , 13 | -96.0 , 46.884 , 5.023 , 29.60 , 41.806 , 14 | 88.516 , 72.10 , 53.0 , 100.96 , 110.27 , 15 | 54.24 , 125.862 , -68.0 , 37.043 , 107.2984 , 16 | 101.059 , 190.161 , 295.1531 , -77.0 , 45.505 , 17 | 13.954 , 53.795 , 55.0 , 10.539 , 9.406 , 18 | 12.45 , 15.63 , 11.20 , 13.22 , 12.670 , 19 | 33.96 , 32.61 , 30.10 , 99.0 , -1.93 , 20 | 23.04 , 17.18 , 31.0 , 78.76 , 5.8273 , 21 | 103.99 , 150.94 , 205.041 , 222.747 , -48.0 , 22 | 30.8804 , 34.4183 , 90.294 , 136.0 , 233.087 , 23 | -68.0 , 46.89 , 9.6485 , 33.77 , 112.72 , 24 | 53.03 , 50.94 , 45.85 , -48.33 , 9.93 , 25 | 27.17 , -165.24 , -97.31 , -28.60 , 33.96 , 26 | 93.91 , -223.22 , -30.04 , f64::NAN , f64::NAN , 27 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 28 | 151.0 , f64::NAN , 66.6 , f64::NAN , 35.3 , 29 | 74.9 , 165.9 , 5.40318 , 30 | 31 | ]; 32 | 33 | // (Single, double, triple) 34 | 35 | #[rustfmt::skip] 36 | pub(crate) const COVALENT_RADII : [f64;354] = [ 37 | 38 | 3.20E-11 , 0.0 , 0.0 , 4.60E-11 , 0.0 , 0.0 , 39 | 1.33E-10 , 1.24E-10 , 0.0 , 1.02E-10 , 9.00E-11 , 8.50E-11 , 40 | 8.50E-11 , 7.80E-11 , 7.30E-11 , 7.50E-11 , 6.70E-11 , 6.00E-11 , 41 | 7.10E-11 , 6.0E-11 , 5.4E-11 , 6.30E-11 , 5.70E-11 , 5.30E-11 , 42 | 6.40E-11 , 5.90E-11 , 5.30E-11 , 6.70E-11 , 9.60E-11 , 0.0 , 43 | 1.55E-10 , 1.60E-10 , 0.0 , 1.39E-10 , 1.32E-10 , 1.27E-10 , 44 | 1.26E-10 , 1.13E-10 , 1.11E-10 , 1.16E-10 , 1.07E-10 , 1.02E-10 , 45 | 1.11E-10 , 1.02E-10 , 9.40E-11 , 1.03E-10 , 9.40E-11 , 9.50E-11 , 46 | 9.90E-11 , 9.50E-11 , 9.30E-11 , 9.60E-11 , 1.07E-10 , 9.60E-11 , 47 | 1.96E-10 , 1.93E-10 , 0.0 , 1.71E-10 , 1.47E-10 , 1.33E-10 , 48 | 1.48E-10 , 1.16E-10 , 1.14E-10 , 1.36E-10 , 1.17E-10 , 1.08E-10 , 49 | 1.34E-10 , 1.12E-10 , 1.06E-10 , 1.22E-10 , 1.11E-10 , 1.03E-10 , 50 | 1.19E-10 , 1.05E-10 , 1.03E-10 , 1.16E-10 , 1.09E-10 , 1.02E-10 , 51 | 1.11E-10 , 1.03E-10 , 9.60E-11 , 1.10E-10 , 1.01E-10 , 1.01E-10 , 52 | 1.12E-10 , 1.15E-10 , 1.20E-11 , 1.18E-10 , 1.20E-10 , 0.0 , 53 | 1.24E-10 , 1.17E-10 , 1.21E-11 , 1.21E-10 , 1.11E-10 , 1.14E-10 , 54 | 1.21E-10 , 1.14E-10 , 1.06E-10 , 1.16E-10 , 1.07E-10 , 1.07E-10 , 55 | 1.14E-10 , 1.09E-10 , 1.10E-10 , 1.17E-10 , 1.21E-10 , 1.08E-10 , 56 | 2.10E-10 , 2.02E-10 , 0.0 , 1.85E-10 , 1.57E-10 , 1.39E-10 , 57 | 1.63E-10 , 1.30E-10 , 1.24E-10 , 1.54E-10 , 1.27E-10 , 1.21E-10 , 58 | 1.47E-10 , 1.25E-10 , 1.16E-10 , 1.38E-10 , 1.21E-10 , 1.13E-10 , 59 | 1.28E-10 , 1.20E-10 , 1.10E-10 , 1.25E-10 , 1.14E-10 , 1.03E-10 , 60 | 1.25E-10 , 1.10E-10 , 1.06E-10 , 1.20E-10 , 1.17E-10 , 1.12E-10 , 61 | 1.28E-10 , 1.39E-10 , 1.37E-10 , 1.36E-10 , 1.44E-10 , 0.0 , 62 | 1.42E-10 , 1.36E-10 , 1.46E-10 , 1.40E-10 , 1.30E-10 , 1.32E-10 , 63 | 1.40E-10 , 1.33E-10 , 1.27E-10 , 1.36E-10 , 1.28E-10 , 1.21E-10 , 64 | 1.33E-10 , 1.29E-10 , 1.25E-10 , 1.31E-10 , 1.35E-10 , 1.22E-10 , 65 | 2.32E-10 , 2.09E-10 , 0.0 , 1.96E-10 , 1.61E-10 , 1.49E-10 , 66 | 67 | 1.80E-10 , 1.39E-10 , 1.39E-10 , 1.63E-10 , 1.37E-10 , 1.31E-10 , 68 | 1.76E-10 , 1.38E-10 , 1.28E-10 , 1.74E-10 , 1.37E-10 , 0.0 , 69 | 1.73E-10 , 1.35E-10 , 0.0 , 1.72E-10 , 1.34E-10 , 0.0 , 70 | 1.68E-10 , 1.34E-10 , 0.0 , 1.69E-10 , 1.35E-10 , 1.32E-10 , 71 | 1.68E-10 , 1.35E-10 , 0.0 , 1.67E-10 , 1.33E-10 , 0.0 , 72 | 1.66E-10 , 1.33E-10 , 0.0 , 1.65E-10 , 1.33E-10 , 0.0 , 73 | 1.64E-10 , 1.31E-10 , 0.0 , 1.70E-10 , 1.29E-10 , 0.0 , 74 | 75 | 1.62E-10 , 1.31E-10 , 1.31E-10 , 1.52E-10 , 1.28E-10 , 1.22E-10 , 76 | 1.46E-10 , 1.26E-10 , 1.19E-10 , 1.37E-10 , 1.20E-10 , 1.15E-10 , 77 | 1.31E-10 , 1.19E-10 , 1.10E-10 , 1.29E-10 , 1.16E-10 , 1.09E-10 , 78 | 1.22E-10 , 1.15E-10 , 1.07E-10 , 1.23E-10 , 1.12E-10 , 1.10E-10 , 79 | 1.24E-10 , 1.21E-10 , 1.23E-10 , 1.33E-10 , 1.42E-10 , 0.0 , 80 | 1.44E-10 , 1.42E-10 , 1.50E-10 , 1.44E-10 , 1.35E-10 , 1.37E-10 , 81 | 1.51E-10 , 1.41E-10 , 1.35E-10 , 1.45E-10 , 1.35E-10 , 1.29E-10 , 82 | 1.47E-10 , 1.38E-10 , 1.38E-10 , 1.42E-10 , 1.45E-10 , 1.33E-10 , 83 | 2.23E-10 , 2.18E-10 , 0.0 , 2.01E-10 , 1.73E-10 , 1.59E-10 , 84 | 85 | 1.86E-10 , 1.53E-10 , 1.40E-10 , 1.75E-10 , 1.43E-10 , 1.36E-10 , 86 | 1.69E-10 , 1.38E-10 , 1.29E-10 , 1.70E-10 , 1.34E-10 , 1.18E-10 , 87 | 1.71E-10 , 1.36E-10 , 1.16E-10 , 1.72E-10 , 1.35E-10 , 0.0 , 88 | 1.66E-10 , 1.35E-10 , 0.0 , 1.66E-10 , 1.36E-10 , 0.0 , 89 | 1.68E-10 , 1.39E-10 , 0.0 , 1.68E-10 , 1.40E-10 , 0.0 , 90 | 1.65E-10 , 1.40E-10 , 0.0 , 1.67E-10 , 0.0 , 0.0 , 91 | 1.73E-10 , 1.39E-10 , 0.0 , 1.76E-10 , 0.0 , 0.0 , 92 | 1.61E-10 , 1.41E-10 , 0.0 , 1.57E-10 , 1.40E-10 , 1.31E-10 , 93 | 1.49E-10 , 1.36E-10 , 1.26E-10 , 1.43E-10 , 1.28E-10 , 1.21E-10 , 94 | 1.41E-10 , 1.28E-10 , 1.19E-10 , 1.34E-10 , 1.25E-10 , 1.18E-10 , 95 | 1.29E-10 , 1.25E-10 , 1.13E-10 , 1.28E-10 , 1.16E-10 , 1.12E-10 , 96 | 1.21E-10 , 1.16E-10 , 1.18E-10 , 1.22E-10 , 1.37E-10 , 1.30E-10 , 97 | 1.36E-10 , 0.0 , 0.0 , 1.43E-10 , 0.0 , 0.0 , 98 | 1.62E-10 , 0.0 , 0.0 , 1.75E-10 , 0.0 , 0.0 , 99 | 1.65E-10 , 0.0 , 0.0 , 1.57E-10 , 0.0 , 0.0 , 100 | 101 | 102 | ]; 103 | /* 104 | Rahm, M., Hoffmann, R., & Ashcroft, N. W. (2016). Atomic and Ionic Radii of Elements 1-96. Chemistry - A European Journal, 22(41), 14625–14632. doi:10.1002/chem.201602949 105 | */ 106 | #[rustfmt::skip] 107 | pub(crate) const IONIC_RADII : [f64;118] = [ 108 | 109 | 1.54E-10 , 1.34E-10 , 2.20E-10 , 2.19E-10 , 2.05E-10 , 1.90E-10 , 110 | 1.79E-10 , 1.71E-10 , 1.63E-10 , 1.56E-10 , 2.25E-10 , 2.40E-10 ,//Mg 111 | 2.39E-10 , 2.32E-10 , 2.23E-10 , 2.14E-10 , 2.06E-10 , 1.97E-10 ,//Ar 112 | 2.34E-10 , 2.70E-10 , 2.63E-10 , 2.57E-10 , 2.52E-10 , 2.33E-10 ,//Cr 113 | 2.42E-10 , 2.26E-10 , 2.22E-10 , 2.19E-10 , 2.17E-10 , 2.22E-10 ,//Zn 114 | 2.33E-10 , 2.34E-10 , 2.31E-10 , 2.24E-10 , 2.19E-10 , 2.12E-10 ,//Kr 115 | 2.40E-10 , 2.79E-10 , 2.74E-10 , 2.68E-10 , 2.51E-10 , 2.44E-10 ,//Mo 116 | 2.41E-10 , 2.37E-10 , 2.33E-10 , 2.15E-10 , 2.25E-10 , 2.38E-10 ,//Cd 117 | 2.46E-10 , 2.48E-10 , 2.46E-10 , 2.42E-10 , 2.38E-10 , 2.32E-10 ,//Xe 118 | 2.49E-10 , 2.93E-10 , 2.84E-10 , 2.82E-10 , 2.86E-10 , 2.84E-10 ,//Nd 119 | 2.83E-10 , 2.80E-10 , 2.80E-10 , 2.77E-10 , 2.76E-10 , 2.75E-10 ,//Dy 120 | 2.73E-10 , 2.72E-10 , 2.71E-10 , 2.77E-10 , 2.70E-10 , 2.64E-10 ,//Hf 121 | 2.58E-10 , 2.53E-10 , 2.49E-10 , 2.44E-10 , 2.33E-10 , 2.30E-10 ,//Pt 122 | 2.26E-10 , 2.29E-10 , 2.42E-10 , 2.49E-10 , 2.50E-10 , 2.50E-10 ,//Po 123 | 2.47E-10 , 2.43E-10 , 2.58E-10 , 2.92E-10 , 2.93E-10 , 2.89E-10 ,//Th 124 | 2.85E-10 , 2.83E-10 , 2.80E-10 , 2.78E-10 , 2.76E-10 , 2.64E-10 ,//Cm 125 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 126 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 127 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 128 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , 129 | 130 | ]; 131 | 132 | // Batsanov's approximations 133 | #[rustfmt::skip] 134 | pub(crate) const VAN_DER_WAAL_ISOLATED : [f64;118] =[ 135 | 136 | 1.96E-10 , f64::NAN , 2.72E-10 , 2.32E-10 , 2.05E-10 , 1.85E-10 , 137 | 1.70E-10 , 1.64E-10 , f64::NAN , f64::NAN , 2.82E-10 , 2.45E-10 , 138 | 2.47E-10 , 2.25E-10 , 2.09E-10 , 2.00E-10 , f64::NAN , 3.08E-10 , 139 | 2.77E-10 , 2.64E-10 , 2.52E-10 , f64::NAN , f64::NAN , 2.23E-10 , 140 | 2.29E-10 , 2.34E-10 , 2.30E-10 , 2.26E-10 , 2.30E-10 , 2.25E-10 , 141 | 2.38E-10 , 2.23E-10 , f64::NAN , 2.10E-10 , 2.00E-10 , f64::NAN , 142 | 3.22E-10 , 2.90E-10 , 2.73E-10 , 2.63E-10 , 2.50E-10 , 2.40E-10 , 143 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , 2.34E-10 , 2.32E-10 , 144 | 2.44E-10 , 2.34E-10 , 2.33E-10 , 2.30E-10 , 2.15E-10 , f64::NAN , 145 | 3.38E-10 , 3.05E-10 , 2.86E-10 , f64::NAN , f64::NAN , f64::NAN , 146 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 147 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 2.54E-10 , 148 | 2.44E-10 , 2.35E-10 , 2.38E-10 , f64::NAN , f64::NAN , f64::NAN , 149 | f64::NAN , 2.25E-10 , 2.46E-10 , 2.34E-10 , 2.40E-10 , f64::NAN , 150 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 2.78E-10 , 151 | f64::NAN , 2.80E-10 , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 152 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 153 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 154 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 155 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , 156 | 157 | ]; 158 | 159 | #[rustfmt::skip] 160 | pub(crate) const VAN_DER_WAAL_CRYSTAL : [f64;118] = [ 161 | 162 | 1.10E-10 , f64::NAN , 2.24E-10 , 1.86E-10 , 1.74E-10 , 1.77E-10 , 163 | 1.64E-10 , 1.58E-10 , 1.46E-10 , f64::NAN , 2.57E-10 , 2.27E-10 ,//Mg 164 | 2.11E-10 , 2.06E-10 , f64::NAN , 1.81E-10 , 1.76E-10 , f64::NAN ,//Ar 165 | 3.00E-10 , 2.61E-10 , 2.28E-10 , 2.14E-10 , 2.03E-10 , 1.97E-10 ,//Cr 166 | 1.96E-10 , 1.96E-10 , 1.95E-10 , 1.94E-10 , 2.00E-10 , 2.02E-10 , 167 | 2.08E-10, 2.13E-10 , 2.16E-10 , f64::NAN , 1.87E-10 , f64::NAN , 168 | 3.12E-10 , 2.78E-10 , 2.45E-10 , 2.25E-10 , 2.13E-10 , 2.06E-10 , 169 | 2.04E-10 , 2.02E-10 , 2.02E-10 , 2.05E-10 , 2.13E-10 , 2.17E-10 , 170 | 2.24E-10 , 2.29E-10 , 2.33E-10 , f64::NAN , 2.03E-10 , f64::NAN , 171 | 3.31E-10 , 2.85E-10 , 2.51E-10 , f64::NAN , f64::NAN , f64::NAN , 172 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 173 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 2.24E-10 , 174 | 2.13E-10 , 2.07E-10 , 2.05E-10 , 2.03E-10 , 2.03E-10 , 2.06E-10 , 175 | 2.13E-10 , 2.17E-10 , 2.25E-10 , 2.36E-10 , 2.42E-10 , f64::NAN , 176 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 2.43E-10 , 177 | f64::NAN , 2.17E-10 , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 178 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 179 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 180 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 181 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , 182 | ]; 183 | //https://physlab.lums.edu.pk/images/f/f6/Franck_ref2.pdf van der waals source 184 | // Unlike Most Libraries this does not use Bondi's approximations 185 | #[allow(clippy::approx_constant)] // Forces clippy to ignore the 3.14 186 | #[rustfmt::skip] 187 | pub(crate) const THERMOCHEMICAL_ELECTRO_NEGATIVE : [f64;118] = [ 188 | 189 | 3.04f64 , 4.42f64 , 2.17f64 , 2.42f64 , 3.04f64 , 3.15f64 , 190 | 3.56f64 , 3.78f64 , 4.00f64 , 4.44f64 , 2.15f64 , 2.39f64 , 191 | 2.52f64 , 2.82f64 , 3.16f64 , 3.44f64 , 3.50f64 , 3.57f64 , 192 | 2.07f64 , 2.20f64 , 2.35f64 , 2.23f64 , 2.08f64 , 2.12f64 , 193 | 2.20f64 , 2.32f64 , 2.86f64 , 2.26f64 , 2.43f64 , 2.79f64 , 194 | 3.15f64 , 3.37f64 , 3.45f64 , 3.37f64 , 2.07f64 , 2.13f64 , 195 | 2.52f64 , 2.05f64 , 2.59f64 , 2.47f64 , 2.82f64 , 2.68f64 , 196 | 2.65f64 , 2.70f64 , 2.88f64 , 2.36f64 , 2.29f64 , 2.68f64 , 197 | 3.05f64 , 3.14f64 , 3.20f64 , 3.12f64 , 1.97f64 , 2.02f64 , 198 | 2.49f64 , 2.61f64 , 2.24f64 , 2.11f64 , 2.24f64 , 1.90f64 , 199 | 1.81f64 , 2.40f64 , 2.29f64 , 2.07f64 , 2.12f64 , 2.02f64 , 200 | 2.03f64 , 1.78f64 , 2.68f64 , 2.01f64 , 2.32f64 , 2.42f64 , 201 | 2.59f64 , 2.59f64 , 2.72f64 , 2.79f64 , 2.98f64 , 2.81f64 , 202 | 2.92f64 , 2.26f64 , 2.62f64 , 2.69f64 , 2.85f64 , 3.04f64 , 203 | 3.04f64 , 2.01f64 , 2.15f64 , 2.22f64 , 2.62f64 , 2.62f64 , 204 | 2.33f64 , 2.45f64 , 2.35f64 , 2.22f64 , 2.28f64 , 2.31f64 , 205 | 2.08f64 , 2.18f64 , 2.29f64 , 2.38f64 , 2.47f64 , 2.06f64 , 206 | 2.10f64 , 2.27f64 , 2.38f64 , 2.51f64 , 2.48f64 , 2.52f64 , 207 | 2.66f64 , 2.73f64 , 2.83f64 , 3.03f64 , 2.49f64 , 2.57f64 , 208 | 2.21f64 , 2.42f64 , 2.61f64 , 2.59f64 , 209 | ]; 210 | 211 | #[rustfmt::skip] 212 | pub(crate) const ALLEN_ELECTRO : [f64;118] = [ 213 | 214 | 2.30f64 , 4.16f64 , 0.912f64 , 1.576f64 , 2.051f64 , 2.544f64 , 215 | 3.066f64 , 3.610f64 , 4.193f64 , 4.787f64 , 0.869f64 , 1.293f64 , 216 | 1.613f64 , 1.916f64 , 2.253f64 , 2.589f64 , 2.869f64 , 3.24264 ,//Argon 217 | 0.734f64 , 1.034f64 , 1.190f64 , 1.380f64 , 1.530f64 , 1.650f64 , 218 | 1.750f64 , 1.800f64 , 1.840f64 , 1.880f64 , 1.850f64 , 1.588f64 , 219 | 1.756f64 , 1.994f64 , 2.211f64 , 2.424f64 , 2.685f64 , 2.966f64 ,//Krypton 220 | 0.706f64 , 0.963f64 , 1.120f64 , 1.320f64 , 1.410f64 , 1.470f64 , 221 | 1.510f64 , 1.540f64 , 1.560f64 , 1.580f64 , 1.870f64 , 1.521f64 , 222 | 1.656f64 , 1.824f64 , 1.984f64 , 2.158f64 , 2.359f64 , 2.582f64 ,//Xe 223 | 0.659f64 , 0.881f64 , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 224 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 225 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , 1.090f64 , 1.160f64 ,//hf 226 | 1.340f64 , 1.470f64 , 1.600f64 , 1.650f64 , 1.680f64 , 1.720f64 , 227 | 1.920f64 , 1.765f64 , 1.789f64 , 1.854f64 , 2.010f64 , 2.190f64 , 228 | 2.390f64 , 2.600f64 , 0.670f64 , 0.890f64 , f64::NAN , 2.62f64 , 229 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 230 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 231 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 232 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , f64::NAN , 233 | f64::NAN , f64::NAN , f64::NAN , f64::NAN , 234 | ]; 235 | 236 | #[rustfmt::skip] 237 | pub(crate) const PAULING_ELECTRO : [f64;118] = [ 238 | 239 | 2.20f64 , f64::NAN, 0.98f64 , 1.57f64 , 2.04f64 , 2.55f64 , 240 | 3.04f64 , 3.44f64 , 3.98f64 , f64::NAN, 0.93f64 , 1.31f64 , 241 | 1.61f64 , 1.90f64 , 2.19f64 , 2.58f64 , 3.16f64 , f64::NAN, 242 | 0.82f64 , 1.00f64 , 1.36f64 , 1.54f64 , 1.63f64 , 1.66f64 , 243 | 1.55f64 , 1.83f64 , 1.88f64 , 1.91f64 , 1.90f64 , 1.65f64 , 244 | 1.81f64 , 2.01f64 , 2.18f64 , 2.55f64 , 2.96f64 , 3.00f64 ,//kr 245 | 0.82f64 , 0.95f64 , 1.22f64 , 1.33f64 , 1.60f64 , 2.16f64 , 246 | 1.90f64 , 2.20f64 , 2.28f64 , 2.20f64 , 1.93f64 , 1.69f64 , 247 | 1.78f64 , 1.96f64 , 2.05f64 , 2.10f64 , 2.66f64 , 2.60f64 ,//Xe 248 | 0.79f64 , 0.89f64 , 1.10f64 , 1.12f64 , 1.13f64 , 1.14f64 , 249 | 1.13f64 , 1.17f64 , 1.20f64 , 1.20f64 , 1.10f64 , 1.22f64 , 250 | 1.23f64 , 1.24f64 , 1.25f64 , 1.10f64 , 1.27f64 , 1.30f64 , 251 | 1.50f64 , 2.36f64 , 1.90f64 , 2.20f64 , 2.28f64 , 2.54f64 , 252 | 2.00f64 , 1.62f64 , 2.33f64 , 2.02f64 , 2.00f64 , 2.20f64 , 253 | 2.20f64 , 0.79f64 , 0.90f64 , 1.10f64 , 1.30f64 , 1.50f64 , 254 | 1.38f64 , 1.36f64 , 1.28f64 , 1.13f64 , 1.28f64 , 1.30f64 , 255 | 1.30f64 , 1.30f64 , 1.30f64 , 1.30f64 , 1.30f64 , 1.30f64 , 256 | f64::NAN, f64::NAN, f64::NAN, f64::NAN, f64::NAN, f64::NAN, 257 | f64::NAN, f64::NAN, f64::NAN, f64::NAN, f64::NAN, f64::NAN, 258 | f64::NAN, f64::NAN, f64::NAN, f64::NAN, 259 | ]; 260 | -------------------------------------------------------------------------------- /Nuclide/src/element.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::Nuclide; 3 | use crate::traits::ChemElement; 4 | use crate::nuclidedata::{ 5 | elemental::*, 6 | lang::*, 7 | index::SYMBOL, 8 | ionization::IONIZATION_ENERGIES 9 | }; 10 | 11 | 12 | // TODO 13 | // - calculate element mass const for each element 14 | // - #![warn(missing_docs)] 15 | 16 | #[derive(Clone)] 17 | /// Struct representing a fractional composition of multiple isotopes 18 | pub struct NuclideFraction { 19 | pub fractions: Vec<(Nuclide, f64)> 20 | } 21 | 22 | impl std::fmt::Display for NuclideFraction { 23 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 24 | let symbol = self.first().element_name(Lang::En); 25 | write!(f, "{}-[{:?}]", symbol, self.fractions) 26 | } 27 | } 28 | 29 | impl NuclideFraction { 30 | 31 | pub fn from_nucleon_fractions(protons: u8, nucleon_fractions: &[(u16, f64)]) -> Option { 32 | 33 | // At least one nuclide 34 | if nucleon_fractions.is_empty() { 35 | return None; 36 | } 37 | 38 | // Ensure sum of fractions is very close to 1.0 39 | //let fraction_sum: f64 = nucleon_fractions.iter().map(|tup| tup.1).sum(); 40 | //if dbg!((fraction_sum - 1.0).abs()) > 1e-2 { 41 | // return None; 42 | //} 43 | 44 | nucleon_fractions.iter() 45 | .map(|&(nucleons, abundance)| { 46 | let p = protons.into(); 47 | let n = Into::::into(nucleons) - p; 48 | Nuclide::from_nucleons(p, n).map(|nucl| (nucl, abundance)) 49 | }) 50 | .collect::>>() 51 | .map(|fractions| NuclideFraction {fractions}) 52 | } 53 | 54 | pub fn from_nucleon_fractions_unchecked(protons: u8, neutron_fractions: &[(u16, f64)]) -> NuclideFraction { 55 | let fractions: Vec<_> = neutron_fractions.iter() 56 | .map(|&(nucleons, abundance)| { 57 | let p = protons.into(); 58 | let n = Into::::into(nucleons) - p; 59 | let nuclide = Nuclide::from_nucleons_unchecked(p, n); 60 | (nuclide, abundance) 61 | }) 62 | .collect(); 63 | NuclideFraction {fractions} 64 | } 65 | 66 | pub fn from_natural_abundancies(protons: u8) -> NuclideFraction { 67 | Element::from_protons(protons).abundant_nuclides() 68 | } 69 | 70 | fn first(&self) -> &Nuclide { 71 | &self.fractions.first().unwrap().0 72 | } 73 | 74 | fn weighted_property f64>(&self, map: F) -> f64 { 75 | if self.fractions.len() == 1 { 76 | map(self.first()) 77 | } else { 78 | self.fractions.iter() 79 | .map(|(nuclide, abundancy)| abundancy * map(nuclide)) 80 | .sum() 81 | } 82 | } 83 | } 84 | 85 | // This is a bit silly, only the mass is a weighted property 86 | impl ChemElement for NuclideFraction { 87 | fn atomic_num(&self) -> u64 { 88 | self.first().atomic_num() 89 | } 90 | 91 | fn am(&self) -> f64 { 92 | self.weighted_property(Nuclide::am) 93 | } 94 | 95 | fn electron_affinity(&self) -> f64 { 96 | self.first().electron_affinity() 97 | } 98 | 99 | fn ionization_energies(&self, level: usize) -> Option { 100 | self.first().ionization_energies(level) 101 | } 102 | 103 | fn electronegativity(&self) -> f64 { 104 | self.first().electronegativity() 105 | } 106 | 107 | fn mullikan_en(&self) -> f64 { 108 | self.first().mullikan_en() 109 | } 110 | 111 | fn allen_en(&self) -> f64 { 112 | self.first().allen_en() 113 | } 114 | 115 | fn pauling_en(&self) -> f64 { 116 | self.first().pauling_en() 117 | } 118 | 119 | fn covalent_radii(&self, bond: usize) -> Option { 120 | self.first().covalent_radii(bond) 121 | } 122 | 123 | fn ionic_radii(&self) -> f64 { 124 | self.first().ionic_radii() 125 | } 126 | 127 | fn vdr_crystal(&self) -> f64 { 128 | self.first().vdr_crystal() 129 | } 130 | 131 | fn vdr_isolated(&self) -> f64 { 132 | self.first().vdr_isolated() 133 | } 134 | } 135 | 136 | /// Enum for general chemical element properties 137 | /// 138 | /// ``` 139 | /// use ::Nuclide::{Element,ChemElement}; 140 | /// let lithium = Element::Li; 141 | /// let am = lithium.am(); 142 | /// let ionization_lvl2 = lithium.ionization_energies(2).unwrap(); 143 | /// ``` 144 | /// 145 | #[rustfmt::skip] 146 | #[repr(u8)] 147 | #[derive(Copy, Clone, Debug,PartialEq)] 148 | pub enum Element { 149 | H=1, He , Li , Be , B , C , N , O , F , 150 | Ne , Na , Mg , Al , Si , P , S , Cl , Ar, 151 | K , Ca , Sc , Ti , V , Cr , Mn , Fe , Co, 152 | Ni , Cu , Zn , Ga , Ge , As , Se , Br , Kr, 153 | Rb , Sr , Y , Zr , Nb , Mo , Tc , Ru , Rh, 154 | Pd , Ag , Cd , In , Sn , Sb , Te , I , Xe, 155 | Cs , Ba , La , Ce , Pr , Nd , Pm , Sm , Eu, 156 | Gd , Tb , Dy , Ho , Er , Tm , Yb , Lu , Hf, 157 | Ta , W , Re , Os , Ir , Pt , Au , Hg , Tl, 158 | Pb , Bi , Po , At , Rn , Fr , Ra , Ac , Th, 159 | Pa , U , Np , Pu , Am , Cm , Bk , Cf , Es, 160 | Fm , Md , No , Lr , Rf , Db , Sg , Bh , Hs, 161 | Mt , Ds , Rg , Cn , Nh , Fl , Mc , Lv , Ts, 162 | Og , 163 | } 164 | 165 | impl std::fmt::Display for Element { 166 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 167 | write!(f, "{}", self.symbol()) 168 | } 169 | } 170 | 171 | // At which index does this element's list of isotope abundancies end? 172 | // NOTE: Starts at index of previous element, if equal this element does not 173 | // exist naturally on Earth 174 | #[rustfmt::skip] 175 | const ABUNDANCE_LOOKUP_TABLE: [u16; 119] = [ 176 | 0, 2, /* H */ 4, /* He */ 6, /* Li */ 7, /* Be */ 9, /* B */ 12, /* C */ 177 | 14, /* N */ 17, /* O */ 18, /* F */ 21, /* Ne */ 22, /* Na */ 25, /* Mg */ 178 | 26, /* Al */ 29, /* Si */ 30, /* P */ 34, /* S */ 36, /* Cl */ 39, /* Ar */ 179 | 42, /* K */ 48, /* Ca */ 49, /* Sc */ 54, /* Ti */ 56, /* V */ 60, /* Cr */ 180 | 61, /* Mn */ 65, /* Fe */ 66, /* Co */ 71, /* Ni */ 73, /* Cu */ 78, /* Zn */ 181 | 80, /* Ga */ 85, /* Ge */ 86, /* As */ 92, /* Se */ 94, /* Br */ 100, /* Kr */ 182 | 102, /* Rb */ 106, /* Sr */ 107, /* Y */ 112, /* Zr */ 113, /* Nb */ 183 | 120, /* Mo */ 121, /* Tc */ 128, /* Ru */ 129, /* Rh */ 135, /* Pd */ 184 | 137, /* Ag */ 145, /* Cd */ 147, /* In */ 157, /* Sn */ 159, /* Sb */ 185 | 167, /* Te */ 168, /* I */ 177, /* Xe */ 178, /* Cs */ 185, /* Ba */ 186 | 187, /* La */ 191, /* Ce */ 192, /* Pr */ 199, /* Nd */ 200, /* Pm */ 187 | 207, /* Sm */ 209, /* Eu */ 216, /* Gd */ 217, /* Tb */ 224, /* Dy */ 188 | 225, /* Ho */ 231, /* Er */ 232, /* Tm */ 239, /* Yb */ 241, /* Lu */ 189 | 247, /* Hf */ 249, /* Ta */ 254, /* W */ 256, /* Re */ 263, /* Os */ 190 | 265, /* Ir */ 271, /* Pt */ 272, /* Au */ 279, /* Hg */ 281, /* Tl */ 191 | 285, /* Pb */ 286, /* Bi */ 287, /* Po */ 288, /* At */ 289, /* Rn */ 192 | 290, /* Fr */ 291, /* Ra */ 292, /* Ac */ 293, /* Th */ 294, /* Pa */ 193 | 297, /* U */ 298, /* Np */ 299, /* Pu */ 300, /* Am */ 301, /* Cm */ 194 | 302, /* Bk */ 303, /* Cf */ 304, /* Es */ 305, /* Fm */ 306, /* Md */ 195 | 307, /* No */ 308, /* Lr */ 309, /* Rf */ 310, /* Db */ 311, /* Sg */ 196 | 312, /* Bh */ 313, /* Hs */ 314, /* Mt */ 315, /* Ds */ 316, /* Rg */ 197 | 317, /* Cn */ 318, /* Nh */ 319, /* Fl */ 320, /* Mc */ 321, /* Lv */ 198 | 322, /* Ts */ 323, /* Og */ 199 | ]; 200 | 201 | const ABUNDANCE_TABLE: [(u16, f64); 323] = [ 202 | (1, 0.999885), // 1H 203 | (2, 0.000115), // 2H 204 | (3, 0.00000134), // 3He 205 | (4, 0.99999866), // 4He 206 | (6, 0.0759), // 6Li 207 | (7, 0.9241), // 7Li 208 | (9, 1.0), // 9Be 209 | (10, 0.199), // 10B 210 | (11, 0.801), // 11B 211 | (12, 0.9893), // 12C 212 | (13, 0.0107), // 13C 213 | (14, 0.0), // 14C 214 | (14, 0.99636), // 14N 215 | (15, 0.00364), // 15N 216 | (16, 0.99757), // 16O 217 | (17, 0.00038), // 17O 218 | (18, 0.00205), // 18O 219 | (19, 1.0), // 19F 220 | (20, 0.9048), // 20Ne 221 | (21, 0.0027), // 21Ne 222 | (22, 0.0925), // 22Ne 223 | (23, 1.0), // 23Na 224 | (24, 0.7899), // 24Mg 225 | (25, 0.1000), // 25Mg 226 | (26, 0.1101), // 26Mg 227 | (27, 1.0), // 27Al 228 | (28, 0.92223), // 28Si 229 | (29, 0.04685), // 29Si 230 | (30, 0.03092), // 30Si 231 | (31, 1.0), // 31P 232 | (32, 0.9499), // 32S 233 | (33, 0.0075), // 33S 234 | (34, 0.0425), // 34S 235 | (36, 0.0001), // 36S 236 | (35, 0.7576), // 35Cl 237 | (37, 0.2424), // 37Cl 238 | (36, 0.003336), // 36Ar 239 | (38, 0.000629), // 38Ar 240 | (40, 0.996035), // 40Ar 241 | (39, 0.932581), // 39K 242 | (40, 0.000117), // 40K 243 | (41, 0.067302), // 41K 244 | (40, 0.96941), // 40Ca 245 | (42, 0.00647), // 42Ca 246 | (43, 0.00135), // 43Ca 247 | (44, 0.02086), // 44Ca 248 | (46, 0.00004), // 46Ca 249 | (48, 0.00187), // 48Ca 250 | (45, 1.0), // 45Sc 251 | (46, 0.0825), // 46Ti 252 | (47, 0.0744), // 47Ti 253 | (48, 0.7372), // 48Ti 254 | (49, 0.0541), // 49Ti 255 | (50, 0.0518), // 50Ti 256 | (50, 0.00250), // 50V 257 | (51, 0.99750), // 51V 258 | (50, 0.04345), // 50Cr 259 | (52, 0.83789), // 52Cr 260 | (53, 0.09501), // 53Cr 261 | (54, 0.02365), // 54Cr 262 | (55, 1.0), // 55Mn 263 | (54, 0.05845), // 54Fe 264 | (56, 0.91754), // 56Fe 265 | (57, 0.02119), // 57Fe 266 | (58, 0.00282), // 58Fe 267 | (59, 1.0), // 59Co 268 | (58, 0.68077), // 58Ni 269 | (60, 0.26223), // 60Ni 270 | (61, 0.011399), // 61Ni 271 | (62, 0.036346), // 62Ni 272 | (64, 0.009255), // 64Ni 273 | (63, 0.6915), // 63Cu 274 | (65, 0.3085), // 65Cu 275 | (64, 0.4917), // 64Zn 276 | (66, 0.2773), // 66Zn 277 | (67, 0.0404), // 67Zn 278 | (68, 0.1845), // 68Zn 279 | (70, 0.0061), // 70Zn 280 | (69, 0.60108), // 69Ga 281 | (71, 0.39892), // 71Ga 282 | (70, 0.2057), // 70Ge 283 | (72, 0.2745), // 72Ge 284 | (73, 0.0775), // 73Ge 285 | (74, 0.3650), // 74Ge 286 | (76, 0.0773), // 76Ge 287 | (75, 1.0), // 75As 288 | (74, 0.0089), // 74Se 289 | (76, 0.0937), // 76Se 290 | (77, 0.0763), // 77Se 291 | (78, 0.2377), // 78Se 292 | (80, 0.4961), // 80Se 293 | (82, 0.0873), // 82Se 294 | (79, 0.5069), // 79Br 295 | (81, 0.4931), // 81Br 296 | (78, 0.00355), // 78Kr 297 | (80, 0.02286), // 80Kr 298 | (82, 0.11593), // 82Kr 299 | (83, 0.11500), // 83Kr 300 | (84, 0.56987), // 84Kr 301 | (86, 0.17279), // 86Kr 302 | (85, 0.7217), // 85Rb 303 | (87, 0.2783), // 87Rb 304 | (84, 0.0056), // 84Sr 305 | (86, 0.0986), // 86Sr 306 | (87, 0.0700), // 87Sr 307 | (88, 0.8258), // 88Sr 308 | (89, 1.0), // 89Y 309 | (90, 0.5145), // 90Zr 310 | (91, 0.1122), // 91Zr 311 | (92, 0.1715), // 92Zr 312 | (94, 0.1738), // 94Zr 313 | (96, 0.0280), // 96Zr 314 | (93, 1.0), // 93Nb 315 | (92, 0.1453), // 92Mo 316 | (94, 0.0915), // 94Mo 317 | (95, 0.1584), // 95Mo 318 | (96, 0.1667), // 96Mo 319 | (97, 0.0960), // 97Mo 320 | (98, 0.2439), // 98Mo 321 | (100, 0.0982), // 100Mo 322 | (98, 1.0), // 98Tc 323 | (96, 0.0554), // 96Ru 324 | (98, 0.0187), // 98Ru 325 | (99, 0.1276), // 99Ru 326 | (100, 0.1260), // 100Ru 327 | (101, 0.1706), // 101Ru 328 | (102, 0.3155), // 102Ru 329 | (104, 0.1862), // 104Ru 330 | (103, 1.0), // 103Rh 331 | (102, 0.0102), // 102Pd 332 | (104, 0.1114), // 104Pd 333 | (105, 0.2233), // 105Pd 334 | (106, 0.2733), // 106Pd 335 | (108, 0.2646), // 108Pd 336 | (110, 0.1172), // 110Pd 337 | (107, 0.51839), // 107Ag 338 | (109, 0.48161), // 109Ag 339 | (106, 0.0125), // 106Cd 340 | (108, 0.0089), // 108Cd 341 | (110, 0.1249), // 110Cd 342 | (111, 0.1280), // 111Cd 343 | (112, 0.2413), // 112Cd 344 | (113, 0.1222), // 113Cd 345 | (114, 0.2873), // 114Cd 346 | (116, 0.0749), // 116Cd 347 | (113, 0.0429), // 113In 348 | (115, 0.9571), // 115In 349 | (112, 0.0097), // 112Sn 350 | (114, 0.0066), // 114Sn 351 | (115, 0.0034), // 115Sn 352 | (116, 0.1454), // 116Sn 353 | (117, 0.0768), // 117Sn 354 | (118, 0.2422), // 118Sn 355 | (119, 0.0859), // 119Sn 356 | (120, 0.3258), // 120Sn 357 | (122, 0.0463), // 122Sn 358 | (124, 0.0579), // 124Sn 359 | (121, 0.5721), // 121Sb 360 | (123, 0.4279), // 123Sb 361 | (120, 0.0009), // 120Te 362 | (122, 0.0255), // 122Te 363 | (123, 0.0089), // 123Te 364 | (124, 0.0474), // 124Te 365 | (125, 0.0707), // 125Te 366 | (126, 0.1884), // 126Te 367 | (128, 0.3174), // 128Te 368 | (130, 0.3408), // 130Te 369 | (127, 1.0), // 127I 370 | (124, 0.000952), // 124Xe 371 | (126, 0.000890), // 126Xe 372 | (128, 0.019102), // 128Xe 373 | (129, 0.264006), // 129Xe 374 | (130, 0.040710), // 130Xe 375 | (131, 0.212324), // 131Xe 376 | (132, 0.269086), // 132Xe 377 | (134, 0.104357), // 134Xe 378 | (136, 0.088573), // 136Xe 379 | (133, 1.0), // 133Cs 380 | (130, 0.00106), // 130Ba 381 | (132, 0.00101), // 132Ba 382 | (134, 0.02417), // 134Ba 383 | (135, 0.06592), // 135Ba 384 | (136, 0.07854), // 136Ba 385 | (137, 0.11232), // 137Ba 386 | (138, 0.71698), // 138Ba 387 | (138, 0.0008881), // 138La 388 | (139, 0.9991119), // 139La 389 | (136, 0.00185), // 136Ce 390 | (138, 0.00251), // 138Ce 391 | (140, 0.88450), // 140Ce 392 | (142, 0.11114), // 142Ce 393 | (141, 1.0), // 141Pr 394 | (142, 0.27152), // 142Nd 395 | (143, 0.12174), // 143Nd 396 | (144, 0.23798), // 144Nd 397 | (145, 0.08293), // 145Nd 398 | (146, 0.17189), // 146Nd 399 | (148, 0.05756), // 148Nd 400 | (150, 0.05638), // 150Nd 401 | (145, 1.0), // 145Pm 402 | (144, 0.0307), // 144Sm 403 | (147, 0.1499), // 147Sm 404 | (148, 0.1124), // 148Sm 405 | (149, 0.1382), // 149Sm 406 | (150, 0.0738), // 150Sm 407 | (152, 0.2675), // 152Sm 408 | (154, 0.2275), // 154Sm 409 | (151, 0.4781), // 151Eu 410 | (153, 0.5219), // 153Eu 411 | (152, 0.0020), // 152Gd 412 | (154, 0.0218), // 154Gd 413 | (155, 0.1480), // 155Gd 414 | (156, 0.2047), // 156Gd 415 | (157, 0.1565), // 157Gd 416 | (158, 0.2484), // 158Gd 417 | (160, 0.2186), // 160Gd 418 | (159, 1.0), // 159Tb 419 | (156, 0.00056), // 156Dy 420 | (158, 0.00095), // 158Dy 421 | (160, 0.02329), // 160Dy 422 | (161, 0.18889), // 161Dy 423 | (162, 0.25475), // 162Dy 424 | (163, 0.24896), // 163Dy 425 | (164, 0.28260), // 164Dy 426 | (165, 1.0), // 165Ho 427 | (162, 0.00139), // 162Er 428 | (164, 0.01601), // 164Er 429 | (166, 0.33503), // 166Er 430 | (167, 0.22869), // 167Er 431 | (168, 0.26978), // 168Er 432 | (170, 0.14910), // 170Er 433 | (169, 1.0), // 169Tm 434 | (168, 0.00123), // 168Yb 435 | (170, 0.02982), // 170Yb 436 | (171, 0.1409), // 171Yb 437 | (172, 0.2168), // 172Yb 438 | (173, 0.16103), // 173Yb 439 | (174, 0.32026), // 174Yb 440 | (176, 0.12996), // 176Yb 441 | (175, 0.97401), // 175Lu 442 | (176, 0.02599), // 176Lu 443 | (174, 0.0016), // 174Hf 444 | (176, 0.0526), // 176Hf 445 | (177, 0.1860), // 177Hf 446 | (178, 0.2728), // 178Hf 447 | (179, 0.1362), // 179Hf 448 | (180, 0.3508), // 180Hf 449 | (180, 0.0001201), // 180Ta 450 | (181, 0.9998799), // 181Ta 451 | (180, 0.0012), // 180W 452 | (182, 0.2650), // 182W 453 | (183, 0.1431), // 183W 454 | (184, 0.3064), // 184W 455 | (186, 0.2843), // 186W 456 | (185, 0.3740), // 185Re 457 | (187, 0.6260), // 187Re 458 | (184, 0.0002), // 184Os 459 | (186, 0.0159), // 186Os 460 | (187, 0.0196), // 187Os 461 | (188, 0.1324), // 188Os 462 | (189, 0.1615), // 189Os 463 | (190, 0.2626), // 190Os 464 | (192, 0.4078), // 192Os 465 | (191, 0.373), // 191Ir 466 | (193, 0.627), // 193Ir 467 | (190, 0.00012), // 190Pt 468 | (192, 0.00782), // 192Pt 469 | (194, 0.3286), // 194Pt 470 | (195, 0.3378), // 195Pt 471 | (196, 0.2521), // 196Pt 472 | (198, 0.07356), // 198Pt 473 | (197, 1.0), // 197Au 474 | (196, 0.0015), // 196Hg 475 | (198, 0.0997), // 198Hg 476 | (199, 0.1687), // 199Hg 477 | (200, 0.2310), // 200Hg 478 | (201, 0.1318), // 201Hg 479 | (202, 0.2986), // 202Hg 480 | (204, 0.0687), // 204Hg 481 | (203, 0.2952), // 203Tl 482 | (205, 0.7048), // 205Tl 483 | (204, 0.014), // 204Pb 484 | (206, 0.241), // 206Pb 485 | (207, 0.221), // 207Pb 486 | (208, 0.524), // 208Pb 487 | (209, 1.0), // 209Bi 488 | (209, 1.0), // 209Po 489 | (210, 1.0), // 210At 490 | (222, 1.0), // 222Rn 491 | (223, 1.0), // 223Fr 492 | (226, 1.0), // 226Ra 493 | (227, 1.0), // 227Ac 494 | (232, 1.0), // 232Th 495 | (231, 1.0), // 231Pa 496 | (234, 0.000054), // 234U 497 | (235, 0.007204), // 235U 498 | (238, 0.992742), // 238U 499 | (237, 1.0), // 237Np 500 | (244, 1.0), // 244Pu 501 | (243, 1.0), // 243Am 502 | (247, 1.0), // 247Cm 503 | (247, 1.0), // 247Bk 504 | (251, 1.0), // 251Cf 505 | (252, 1.0), // 252Es 506 | (257, 1.0), // 257Fm 507 | (258, 1.0), // 258Md 508 | (259, 1.0), // 259No 509 | (262, 1.0), // 262Lr 510 | (267, 1.0), // 267Rf 511 | (268, 1.0), // 268Db 512 | (271, 1.0), // 271Sg 513 | (272, 1.0), // 272Bh 514 | (270, 1.0), // 270Hs 515 | (276, 1.0), // 276Mt 516 | (281, 1.0), // 281Ds 517 | (280, 1.0), // 280Rg 518 | (285, 1.0), // 285Cn 519 | (286, 1.0), // 286Nh 520 | (290, 1.0), // 290Fl 521 | (290, 1.0), // 290Mc 522 | (293, 1.0), // 293Lv 523 | (294, 1.0), // 294Ts 524 | (294, 1.0), // 294Og 525 | ]; 526 | 527 | impl Element { 528 | pub const fn protons(&self) -> u8 { 529 | *self as u8 530 | } 531 | 532 | const fn from_protons_unchecked(protons: u8) -> Element{ 533 | unsafe {std::mem::transmute(protons)} 534 | } 535 | 536 | pub const fn from_protons(protons: u8) -> Element { 537 | if protons == 0 || protons > Element::Og.protons() { 538 | panic!("Invalid number of protons for an element"); 539 | } 540 | 541 | unsafe { std::mem::transmute(protons) } 542 | } 543 | 544 | pub const fn symbol(&self) -> &'static str { 545 | SYMBOL[*self as usize - 1] 546 | } 547 | 548 | /// Fraction as measured from samples on Earth. For non-abundant elements, 549 | /// returns only the most stable isotope. 550 | pub fn abundant_nuclides(&self) -> NuclideFraction { 551 | NuclideFraction::from(*self) 552 | } 553 | 554 | /// Iterate through all elements 555 | pub fn iter() -> impl Iterator { 556 | (1..=Element::Og.protons()).map(Element::from_protons) 557 | } 558 | 559 | pub fn name(&self, lang: Lang) -> String{ 560 | let idx = self.protons().wrapping_sub(1) as usize; 561 | match lang{ 562 | Lang::En => NAME_EN[idx].to_string(), 563 | Lang::Es => NAME_ES[idx].to_string(), 564 | Lang::Pl => NAME_PL[idx].to_string(), 565 | Lang::De => NAME_DE[idx].to_string(), 566 | Lang::Fr => NAME_FR[idx].to_string(), 567 | Lang::Ru => NAME_RU[idx].to_string(), 568 | } 569 | } 570 | 571 | pub fn from_str_lang(x: &str, lang: Lang) -> Result{ 572 | let input = x.to_lowercase(); 573 | 574 | // FIXME Translate the error message 575 | match lang{ 576 | Lang::En => NAME_EN.iter().position(|el| el.to_lowercase()==input).map(|p| Self::from_protons_unchecked(p as u8+1)).ok_or("Check spelling"), 577 | Lang::Es => NAME_ES.iter().position(|el| el.to_lowercase()==input).map(|p| Self::from_protons_unchecked(p as u8+1)).ok_or("Check spelling"), 578 | Lang::Pl => NAME_PL.iter().position(|el| el.to_lowercase()==input).map(|p| Self::from_protons_unchecked(p as u8+1)).ok_or("Check spelling"), 579 | Lang::De => NAME_DE.iter().position(|el| el.to_lowercase()==input).map(|p| Self::from_protons_unchecked(p as u8+1)).ok_or("Check spelling"), 580 | Lang::Fr => NAME_FR.iter().position(|el| el.to_lowercase()==input).map(|p| Self::from_protons_unchecked(p as u8+1)).ok_or("Check spelling"), 581 | Lang::Ru => NAME_RU.iter().position(|el| el.to_lowercase()==input).map(|p| Self::from_protons_unchecked(p as u8+1)).ok_or("Check spelling"), 582 | } 583 | } 584 | } 585 | 586 | impl ChemElement for Element { 587 | 588 | fn atomic_num(&self) -> u64 { 589 | *self as u64 590 | } 591 | 592 | fn am(&self) -> f64 { 593 | NuclideFraction::from(*self).am() 594 | } 595 | 596 | fn electron_affinity(&self) -> f64 { 597 | ELECTRON_AFFINITY[*self as usize - 1] 598 | } 599 | 600 | fn ionization_energies(&self, level: usize) -> Option { 601 | let z = self.atomic_num(); 602 | 603 | if z > 110 || level == 0 || level > z as usize { 604 | return None 605 | } 606 | 607 | Some( 608 | IONIZATION_ENERGIES[((((z * (z + 1)) >> 1) 609 | - z) 610 | + level as u64 611 | - 1) as usize], 612 | ) 613 | } 614 | 615 | fn electronegativity(&self) -> f64 { 616 | THERMOCHEMICAL_ELECTRO_NEGATIVE[*self as usize - 1] 617 | } 618 | 619 | fn mullikan_en(&self) -> f64 { 620 | (self.ionization_energies(1).unwrap() + ELECTRON_AFFINITY[*self as usize - 1]) 621 | * 1.97E-3 622 | + 0.19 623 | } 624 | 625 | fn allen_en(&self) -> f64 { 626 | ALLEN_ELECTRO[*self as usize - 1] 627 | } 628 | 629 | fn pauling_en(&self) -> f64 { 630 | PAULING_ELECTRO[*self as usize - 1] 631 | } 632 | 633 | fn covalent_radii(&self, bond: usize) -> Option { 634 | if bond > 0 && bond < 4 { 635 | Some(COVALENT_RADII[(*self as usize - 1) * 3 + bond - 1]) 636 | } else { 637 | None 638 | } 639 | } 640 | 641 | fn ionic_radii(&self) -> f64 { 642 | IONIC_RADII[*self as usize - 1] 643 | } 644 | 645 | fn vdr_crystal(&self) -> f64 { 646 | VAN_DER_WAAL_CRYSTAL[*self as usize - 1] 647 | } 648 | 649 | fn vdr_isolated(&self) -> f64 { 650 | VAN_DER_WAAL_ISOLATED[*self as usize - 1] 651 | } 652 | } 653 | 654 | /// Fraction as measured from samples on Earth. For non-abundant elements, 655 | /// returns only the most stable isotope. 656 | impl From for NuclideFraction { 657 | fn from(element: Element) -> Self { 658 | let z = element as usize; 659 | let start = ABUNDANCE_LOOKUP_TABLE[z - 1] as usize; 660 | let end = ABUNDANCE_LOOKUP_TABLE[z] as usize; 661 | NuclideFraction::from_nucleon_fractions( 662 | element as u8, 663 | &ABUNDANCE_TABLE[start..end] 664 | ).expect("Valid abundance table for these elements") 665 | } 666 | } 667 | 668 | 669 | 670 | 671 | impl std::str::FromStr for Element{ 672 | 673 | type Err = &'static str; 674 | 675 | fn from_str(input: &str) -> Result{ 676 | 677 | for i in [Lang::En,Lang::Es,Lang::Pl,Lang::De,Lang::Fr,Lang::Ru]{ 678 | match Self::from_str_lang(input,i){ 679 | Ok(x) => return Ok(x), 680 | Err(_) => (), 681 | } 682 | } 683 | return Err("Check spelling or language support") 684 | } 685 | } 686 | 687 | -------------------------------------------------------------------------------- /Nuclide/src/nuclidedata/spinparity.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | pub(crate) const SPIN_PARITY : [(i8,i8);3585] = [ 3 | 4 | (1, 2),(1, 0),(1, 2),(-2, 0),(1, 2),(-2, 0),(1, 2),(0,0),(1, 2),(0, 0),(3, -2), 5 | 6 | (0, 0),(3, -2),(0, 0),(1, 2),(0, 0), /*