use crate::composition::Composition;
use crate::error::{GugenError, Result, require_finite};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ReactionSpecies {
pub composition: Composition,
pub coefficient: u64,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BalancedReaction {
pub reactants: Vec<ReactionSpecies>,
pub products: Vec<ReactionSpecies>,
}
impl BalancedReaction {
pub fn new(reactants: Vec<ReactionSpecies>, products: Vec<ReactionSpecies>) -> Result<Self> {
if reactants.is_empty() || products.is_empty() {
return Err(GugenError::EmptyReaction);
}
if reactants
.iter()
.chain(products.iter())
.any(|s| s.coefficient == 0)
{
return Err(GugenError::ZeroCoefficient);
}
Ok(Self {
reactants,
products,
})
}
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ThermodynamicConditions {
pub temperature_celsius: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ReactionEnergy {
value_ev_per_atom: f64,
}
impl ReactionEnergy {
pub fn new(value_ev_per_atom: f64) -> Result<Self> {
require_finite("value_ev_per_atom", value_ev_per_atom)?;
Ok(Self { value_ev_per_atom })
}
pub fn value_ev_per_atom(&self) -> f64 {
self.value_ev_per_atom
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for ReactionEnergy {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct Raw {
value_ev_per_atom: f64,
}
let raw = Raw::deserialize(deserializer)?;
ReactionEnergy::new(raw.value_ev_per_atom).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CompetingPhase {
pub composition: Composition,
formation_energy_ev_per_atom: f64,
}
impl CompetingPhase {
pub fn new(composition: Composition, formation_energy_ev_per_atom: f64) -> Result<Self> {
require_finite("formation_energy_ev_per_atom", formation_energy_ev_per_atom)?;
Ok(Self {
composition,
formation_energy_ev_per_atom,
})
}
pub fn formation_energy_ev_per_atom(&self) -> f64 {
self.formation_energy_ev_per_atom
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for CompetingPhase {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct Raw {
composition: Composition,
formation_energy_ev_per_atom: f64,
}
let raw = Raw::deserialize(deserializer)?;
CompetingPhase::new(raw.composition, raw.formation_energy_ev_per_atom)
.map_err(serde::de::Error::custom)
}
}