#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
#[cfg_attr(feature = "python", pyo3::pyclass(eq, eq_int))]
pub enum MixingRule {
WongSandler = 26,
HuronVidalOriginal = 27,
HuronVidalSimplified = 28,
MHV1 = 29,
MHV2 = 30,
IVDW = 31,
IIVDW = 32,
Classical = 33,
PatelTejaC = 34,
PatelTejaUSBC = 35,
SchmidtWenzelC = 36,
}
pub fn c_mix(rule: MixingRule, x: &[f64], c: &[f64], weight: &[f64]) -> Option<f64> {
match rule {
MixingRule::PatelTejaC => Some(x.iter().zip(c).map(|(xi, ci)| xi * ci).sum()),
MixingRule::PatelTejaUSBC | MixingRule::SchmidtWenzelC => {
let mut num = 0.0;
let mut den = 0.0;
for ((xi, ci), wi) in x.iter().zip(c).zip(weight) {
num += xi * wi * ci;
den += xi * wi;
}
if den.abs() < f64::EPSILON {
None
} else {
Some(num / den)
}
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vb6_mixing_rules_start_at_26() {
assert_eq!(MixingRule::WongSandler as i32, 26);
assert_eq!(MixingRule::Classical as i32, 33);
}
#[test]
fn c_mix_patel_teja_is_mole_fraction_average() {
let x = [0.4, 0.6];
let c = [0.1, 0.2];
let cm = c_mix(MixingRule::PatelTejaC, &x, &c, &[]).unwrap();
assert!((cm - (0.4 * 0.1 + 0.6 * 0.2)).abs() < 1e-12);
}
#[test]
fn c_mix_usb_is_weighted_average() {
let x = [0.5, 0.5];
let c = [0.1, 0.3];
let w = [1.0, 3.0]; let cm = c_mix(MixingRule::PatelTejaUSBC, &x, &c, &w).unwrap();
let expected = (0.5 * 1.0 * 0.1 + 0.5 * 3.0 * 0.3) / (0.5 * 1.0 + 0.5 * 3.0);
assert!((cm - expected).abs() < 1e-12);
}
#[test]
fn c_mix_returns_none_for_ab_rules() {
assert!(c_mix(MixingRule::Classical, &[1.0], &[0.1], &[]).is_none());
}
}