use super::{Error, Site, Structure, element_by_symbol};
impl Structure {
fn cell_atoms(&self) -> Vec<(String, f64, f64)> {
if !self.is_p1()
&& let Ok(p1) = self.p1()
{
return p1
.atoms
.iter()
.map(|atom| {
let charge = self
.sites
.get(atom.site_idx)
.and_then(Site::charge)
.unwrap_or(0.0);
(atom.element.clone(), atom.occupancy, charge)
})
.collect();
}
self.sites
.iter()
.map(|site| {
(
site.element().to_owned(),
site.occupancy(),
site.charge().unwrap_or(0.0),
)
})
.collect()
}
#[must_use]
pub fn cell_contents(&self) -> Vec<(String, f64)> {
let mut contents: Vec<(String, f64)> = Vec::new();
for (element, occupancy, _) in self.cell_atoms() {
if let Some(entry) = contents.iter_mut().find(|(name, _)| *name == element) {
entry.1 += occupancy;
} else {
contents.push((element, occupancy));
}
}
contents
}
#[must_use]
pub fn formula(&self) -> String {
self.cell_contents()
.iter()
.map(|(element, count)| {
if (*count - count.round()).abs() < 1e-9 {
let count = count.round() as u64;
if count == 1 {
element.clone()
} else {
format!("{element}{count}")
}
} else {
format!("{element}{count:.3}")
}
})
.collect::<Vec<_>>()
.join(" ")
}
pub fn cell_mass(&self) -> Result<f64, Error> {
let mut mass = 0.0;
for (element, occupancy, _) in self.cell_atoms() {
mass += occupancy * element_by_symbol(&element)?.mass;
}
Ok(mass)
}
pub fn molar_mass(&self) -> Result<f64, Error> {
self.cell_mass()
}
pub fn density(&self) -> Result<f64, Error> {
Ok(self.cell_mass()? * 1.660_539_066_60 / self.lattice.volume())
}
#[must_use]
pub fn z(&self) -> u32 {
let mut divisor: u64 = 0;
for (_, count) in self.cell_contents() {
if count < 0.5 || (count - count.round()).abs() > 1e-6 {
return 1;
}
let mut a = divisor;
let mut b = count.round() as u64;
while b != 0 {
(a, b) = (b, a % b);
}
divisor = a;
}
u32::try_from(divisor.max(1)).unwrap_or(1)
}
#[must_use]
pub fn total_charge(&self) -> f64 {
self.cell_atoms()
.iter()
.map(|(_, occupancy, charge)| occupancy * charge)
.sum()
}
#[must_use]
pub fn charge_balance(&self) -> f64 {
self.total_charge()
}
}