mendeleev/properties/
atomic_weight.rs

1use core::{
2    fmt::{Display, Formatter},
3    ops::RangeInclusive,
4};
5
6use super::Element;
7
8#[cfg(feature = "ranges")]
9/// Range from the minimum to the maximum atomic weight across all elements
10///
11/// Convenience constant to avoid writing the code below when this range is needed:
12///
13/// ```
14/// use mendeleev::{Element, ATOMIC_WEIGHT_RANGE};
15/// let all_values = Element::iter().map(|e| f64::from(e.atomic_weight()));
16/// let min = all_values.clone().min_by(|a, b| a.total_cmp(&b)).unwrap();
17/// let max = all_values.max_by(|a, b| a.total_cmp(&b)).unwrap();
18/// assert_eq!(min..=max, ATOMIC_WEIGHT_RANGE);
19/// ```
20pub const ATOMIC_WEIGHT_RANGE: RangeInclusive<f64> = 1.008..=294.0;
21
22#[derive(Clone, Debug, PartialEq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24/// The Standard Atomic Weight as defined by the CIAAW
25pub enum AtomicWeight {
26    /// Value defined as an interval
27    Interval {
28        /// Interval of atomic weights
29        range: RangeInclusive<f64>,
30        /// The mean value conventionally used
31        conventional: f64,
32    },
33    /// Value defined with uncertainty
34    Uncertainty {
35        /// The mean weight value
36        weight: f64,
37        /// The uncertainty in the weight
38        uncertainty: f64,
39    },
40    /// Atomic weight not known, default to the mass number of the most stable isotope
41    MassNumber {
42        /// Mass number (number of protons + neutrons)
43        number: u64,
44    },
45}
46
47impl PartialOrd for AtomicWeight {
48    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
49        f64::from(self).partial_cmp(&f64::from(other))
50    }
51}
52
53impl From<AtomicWeight> for f64 {
54    fn from(weight: AtomicWeight) -> Self {
55        weight.get_value()
56    }
57}
58
59impl From<&AtomicWeight> for f64 {
60    fn from(weight: &AtomicWeight) -> Self {
61        weight.get_value()
62    }
63}
64
65impl AtomicWeight {
66    const fn get_value(&self) -> f64 {
67        match self {
68            AtomicWeight::Interval {
69                range: _,
70                conventional,
71            } => *conventional,
72            AtomicWeight::Uncertainty {
73                weight,
74                uncertainty: _,
75            } => *weight,
76            AtomicWeight::MassNumber { number } => *number as f64,
77        }
78    }
79}
80
81impl Display for AtomicWeight {
82    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
83        fn get_precision(uncertainty: f64) -> (usize, u8) {
84            let mut digit = uncertainty;
85            let mut precision = 0u8;
86            while digit < 1.0 && precision < 15 {
87                digit *= 10.0;
88                precision = precision.saturating_add(1);
89            }
90            (precision as usize, digit as u8)
91        }
92        match self {
93            AtomicWeight::Interval {
94                range: _,
95                conventional,
96            } => f.write_fmt(format_args!("{}", conventional)),
97            AtomicWeight::Uncertainty {
98                weight,
99                uncertainty,
100            } => {
101                let (precision, digit) = get_precision(*uncertainty);
102                f.write_fmt(format_args!(
103                    "{w:.prec$}({u})",
104                    w = weight,
105                    prec = precision,
106                    u = digit
107                ))
108            }
109            AtomicWeight::MassNumber { number } => f.write_fmt(format_args!("[{}]", number)),
110        }
111    }
112}
113
114const fn bounds(range: RangeInclusive<f64>, conventional: f64) -> AtomicWeight {
115    AtomicWeight::Interval {
116        range,
117        conventional,
118    }
119}
120
121const fn unc(weight: f64, uncertainty: f64) -> AtomicWeight {
122    AtomicWeight::Uncertainty {
123        weight,
124        uncertainty,
125    }
126}
127const fn mn(number: u64) -> AtomicWeight {
128    AtomicWeight::MassNumber { number }
129}
130
131impl Element {
132    /// Returns the element's Standard Atomic Weight, if applicable,
133    /// or its mass number otherwise.
134    ///
135    /// ```
136    /// use mendeleev::{Element, AtomicWeight};
137    ///
138    /// assert_eq!(Element::H.atomic_weight(),
139    /// AtomicWeight::Interval{range: 1.0078..=1.0082, conventional: 1.008});
140    /// assert_eq!(Element::Og.atomic_weight(), AtomicWeight::MassNumber{number: 294});
141    /// ```
142    pub const fn atomic_weight(&self) -> AtomicWeight {
143        match self {
144            Element::H => bounds(1.0078..=1.0082, 1.008),
145            Element::He => unc(4.002602, 0.000002),
146            Element::Li => bounds(6.938..=6.997, 6.94),
147            Element::Be => unc(9.0121831, 0.0000005),
148            Element::B => bounds(10.806..=10.821, 10.81),
149            Element::C => bounds(12.009..=12.012, 12.011),
150            Element::N => bounds(14.006..=14.008, 14.007),
151            Element::O => bounds(15.999..=16.000, 15.999),
152            Element::F => unc(18.998403163, 0.000000006),
153            Element::Ne => unc(20.1797, 0.0006),
154            Element::Na => unc(22.98976928, 0.00000002),
155            Element::Mg => bounds(24.304..=24.307, 24.305),
156            Element::Al => unc(26.9815385, 0.0000007),
157            Element::Si => bounds(28.084..=28.086, 28.085),
158            Element::P => unc(30.973761998, 0.000000005),
159            Element::S => bounds(32.059..=32.076, 32.06),
160            Element::Cl => bounds(35.446..=35.457, 35.45),
161            Element::Ar => unc(39.948, 0.001),
162            Element::K => unc(39.0983, 0.0001),
163            Element::Ca => unc(40.078, 0.004),
164            Element::Sc => unc(44.955908, 0.000005),
165            Element::Ti => unc(47.867, 0.001),
166            Element::V => unc(50.9415, 0.0001),
167            Element::Cr => unc(51.9961, 0.0006),
168            Element::Mn => unc(54.938044, 0.000003),
169            Element::Fe => unc(55.845, 0.002),
170            Element::Co => unc(58.933194, 0.000004),
171            Element::Ni => unc(58.6934, 0.0004),
172            Element::Cu => unc(63.546, 0.003),
173            Element::Zn => unc(65.38, 0.02),
174            Element::Ga => unc(69.723, 0.001),
175            Element::Ge => unc(72.63, 0.008),
176            Element::As => unc(74.921595, 0.000006),
177            Element::Se => unc(78.971, 0.008),
178            Element::Br => bounds(79.901..=79.907, 79.904),
179            Element::Kr => unc(83.798, 0.002),
180            Element::Rb => unc(85.4678, 0.0003),
181            Element::Sr => unc(87.62, 0.01),
182            Element::Y => unc(88.90584, 0.00002),
183            Element::Zr => unc(91.224, 0.002),
184            Element::Nb => unc(92.90637, 0.00002),
185            Element::Mo => unc(95.95, 0.01),
186            Element::Tc => unc(97.90721, 0.00003),
187            Element::Ru => unc(101.07, 0.02),
188            Element::Rh => unc(102.9055, 0.00002),
189            Element::Pd => unc(106.42, 0.01),
190            Element::Ag => unc(107.8682, 0.0002),
191            Element::Cd => unc(112.414, 0.004),
192            Element::In => unc(114.818, 0.001),
193            Element::Sn => unc(118.71, 0.007),
194            Element::Sb => unc(121.76, 0.001),
195            Element::Te => unc(127.6, 0.03),
196            Element::I => unc(126.90447, 0.00003),
197            Element::Xe => unc(131.293, 0.006),
198            Element::Cs => unc(132.90545196, 0.00000006),
199            Element::Ba => unc(137.327, 0.007),
200            Element::La => unc(138.90547, 0.00007),
201            Element::Ce => unc(140.116, 0.001),
202            Element::Pr => unc(140.90766, 0.00002),
203            Element::Nd => unc(144.242, 0.003),
204            Element::Pm => unc(144.91276, 0.00002),
205            Element::Sm => unc(150.36, 0.02),
206            Element::Eu => unc(151.964, 0.001),
207            Element::Gd => unc(157.25, 0.03),
208            Element::Tb => unc(158.92535, 0.00002),
209            Element::Dy => unc(162.5, 0.001),
210            Element::Ho => unc(164.93033, 0.00002),
211            Element::Er => unc(167.259, 0.003),
212            Element::Tm => unc(168.93422, 0.00002),
213            Element::Yb => unc(173.045, 0.01),
214            Element::Lu => unc(174.9668, 0.0001),
215            Element::Hf => unc(178.49, 0.02),
216            Element::Ta => unc(180.94788, 0.00002),
217            Element::W => unc(183.84, 0.01),
218            Element::Re => unc(186.207, 0.001),
219            Element::Os => unc(190.23, 0.03),
220            Element::Ir => unc(192.217, 0.003),
221            Element::Pt => unc(195.084, 0.009),
222            Element::Au => unc(196.966569, 0.000005),
223            Element::Hg => unc(200.592, 0.003),
224            Element::Tl => bounds(204.38..=204.39, 204.38),
225            Element::Pb => unc(207.2, 0.1),
226            Element::Bi => unc(208.9804, 0.00001),
227            Element::Po => mn(209),
228            Element::At => mn(210),
229            Element::Rn => mn(222),
230            Element::Fr => mn(223),
231            Element::Ra => mn(226),
232            Element::Ac => mn(227),
233            Element::Th => unc(232.0377, 0.0004),
234            Element::Pa => unc(231.03588, 0.00002),
235            Element::U => unc(238.02891, 0.00003),
236            Element::Np => mn(237),
237            Element::Pu => mn(244),
238            Element::Am => mn(243),
239            Element::Cm => mn(247),
240            Element::Bk => mn(247),
241            Element::Cf => mn(251),
242            Element::Es => mn(252),
243            Element::Fm => mn(257),
244            Element::Md => mn(258),
245            Element::No => mn(259),
246            Element::Lr => mn(262),
247            Element::Rf => mn(267),
248            Element::Db => mn(268),
249            Element::Sg => mn(271),
250            Element::Bh => mn(274),
251            Element::Hs => mn(269),
252            Element::Mt => mn(276),
253            Element::Ds => mn(281),
254            Element::Rg => mn(281),
255            Element::Cn => mn(285),
256            Element::Nh => mn(286),
257            Element::Fl => mn(289),
258            Element::Mc => mn(288),
259            Element::Lv => mn(293),
260            Element::Ts => mn(294),
261            Element::Og => mn(294),
262        }
263    }
264}