#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
use std::f64::consts::PI;
const R_GAS: f64 = 8.314;
const M_H2: f64 = 2.016e-3;
const LHV_H2_MJ_KG: f64 = 119.96;
const VDW_A_H2: f64 = 0.02476e0; const VDW_B_H2: f64 = 26.61e-6;
#[derive(Clone, Debug, PartialEq)]
pub enum HydrogenStorageType {
CompressedGas,
LiquidH2,
MetalHydride,
ChemicalHydride,
MOF,
}
#[derive(Clone, Debug)]
pub struct MetalHydride {
pub metal_name: String,
pub max_capacity_wt_percent: f64,
pub desorption_temperature: f64,
pub van_hoff_enthalpy: f64,
pub van_hoff_entropy: f64,
}
impl MetalHydride {
pub fn new(
metal_name: impl Into<String>,
max_capacity_wt_percent: f64,
desorption_temperature: f64,
van_hoff_enthalpy: f64,
van_hoff_entropy: f64,
) -> Self {
Self {
metal_name: metal_name.into(),
max_capacity_wt_percent,
desorption_temperature,
van_hoff_enthalpy,
van_hoff_entropy,
}
}
pub fn lani5() -> Self {
Self::new("LaNi5", 1.4, 310.0, -30_500.0, -108.0)
}
pub fn mgh2() -> Self {
Self::new("MgH2", 7.6, 573.0, -74_500.0, -135.0)
}
pub fn equilibrium_pressure(&self, t_k: f64) -> f64 {
let ln_p = self.van_hoff_enthalpy / (R_GAS * t_k) - self.van_hoff_entropy / R_GAS;
ln_p.exp()
}
}
#[derive(Clone, Debug)]
pub struct PressureVesselH2 {
pub volume_l: f64,
pub max_pressure_bar: f64,
pub temperature: f64,
}
impl PressureVesselH2 {
pub fn new(volume_l: f64, max_pressure_bar: f64, temperature: f64) -> Self {
Self {
volume_l,
max_pressure_bar,
temperature,
}
}
pub fn type_iv_700bar() -> Self {
Self::new(120.0, 700.0, 298.15)
}
pub fn mass_h2(&self) -> f64 {
let p_pa = self.max_pressure_bar * 1e5;
let v_m3 = self.volume_l * 1e-3;
p_pa * v_m3 * M_H2 / (R_GAS * self.temperature)
}
pub fn energy_density(&self) -> f64 {
LHV_H2_MJ_KG
}
pub fn stored_energy_mj(&self) -> f64 {
self.mass_h2() * LHV_H2_MJ_KG
}
}
#[derive(Clone, Debug)]
pub struct MofAdsorption {
pub surface_area_m2g: f64,
pub pore_volume: f64,
pub isosteric_heat: f64,
pub saturation_capacity_wt: f64,
pub t_ref: f64,
pub k_lang_ref: f64,
}
impl MofAdsorption {
#[allow(clippy::too_many_arguments)]
pub fn new(
surface_area_m2g: f64,
pore_volume: f64,
isosteric_heat: f64,
saturation_capacity_wt: f64,
t_ref: f64,
k_lang_ref: f64,
) -> Self {
Self {
surface_area_m2g,
pore_volume,
isosteric_heat,
saturation_capacity_wt,
t_ref,
k_lang_ref,
}
}
pub fn mof5() -> Self {
Self::new(3800.0, 1.55, 4500.0, 7.0, 77.0, 0.05)
}
fn k_lang(&self, t_k: f64) -> f64 {
self.k_lang_ref * (self.isosteric_heat / R_GAS * (1.0 / t_k - 1.0 / self.t_ref)).exp()
}
pub fn capacity_at_condition(&self, t_k: f64, p_bar: f64) -> f64 {
let k = self.k_lang(t_k);
self.saturation_capacity_wt * k * p_bar / (1.0 + k * p_bar)
}
}
#[derive(Clone, Debug)]
pub struct HydrogenSafety {
pub lower_flammability_limit: f64,
pub upper_flammability_limit: f64,
pub autoignition_temp: f64,
}
impl HydrogenSafety {
pub fn standard() -> Self {
Self {
lower_flammability_limit: 0.04,
upper_flammability_limit: 0.75,
autoignition_temp: 773.0,
}
}
pub fn is_flammable_mixture(&self, concentration: f64) -> bool {
concentration >= self.lower_flammability_limit
&& concentration <= self.upper_flammability_limit
}
}
pub fn h2_density_kg_m3(p_bar: f64, t_k: f64) -> f64 {
let p_pa = p_bar * 1e5;
let a = 0.02476e-3 * 101_325.0; let b = VDW_B_H2;
let f = |v: f64| -> f64 { (p_pa + a / (v * v)) * (v - b) - R_GAS * t_k };
let v_lo = b * 1.001_f64;
let v_hi = 10.0 * R_GAS * t_k / p_pa.max(1.0);
if f(v_lo) * f(v_hi) > 0.0 {
return M_H2 * p_pa / (R_GAS * t_k);
}
let mut lo = v_lo;
let mut hi = v_hi;
for _ in 0..80 {
let mid = 0.5 * (lo + hi);
if f(lo) * f(mid) <= 0.0 {
hi = mid;
} else {
lo = mid;
}
}
M_H2 / (0.5 * (lo + hi))
}
pub fn gravimetric_energy_density_mj_kg(wt_percent: f64) -> f64 {
(wt_percent / 100.0) * LHV_H2_MJ_KG
}
pub fn volumetric_capacity(density: f64, wt_percent: f64) -> f64 {
density * wt_percent / 100.0
}
pub fn vessel_wrinkling_wavelength(bending_stiffness: f64, tension: f64) -> f64 {
2.0 * PI * (bending_stiffness / tension).sqrt()
}
#[cfg(test)]
mod tests {
use super::*;
const EPS: f64 = 1e-9;
#[test]
fn test_h2_density_ambient() {
let rho = h2_density_kg_m3(1.0, 298.15);
assert!(rho > 0.07 && rho < 0.10, "ρ(1 bar,298K)={rho}");
}
#[test]
fn test_h2_density_700bar() {
let rho = h2_density_kg_m3(700.0, 298.15);
assert!(rho > 30.0 && rho < 100.0, "ρ(700 bar,298K)={rho}");
}
#[test]
fn test_h2_density_increases_with_pressure() {
let rho_low = h2_density_kg_m3(50.0, 298.15);
let rho_high = h2_density_kg_m3(200.0, 298.15);
assert!(rho_high > rho_low, "Density must increase with pressure");
}
#[test]
fn test_h2_density_decreases_with_temperature() {
let rho_cold = h2_density_kg_m3(100.0, 200.0);
let rho_hot = h2_density_kg_m3(100.0, 400.0);
assert!(rho_cold > rho_hot, "Density must decrease with temperature");
}
#[test]
fn test_h2_density_positive() {
let rho = h2_density_kg_m3(350.0, 333.0);
assert!(rho > 0.0, "Density must be positive");
}
#[test]
fn test_lani5_equilibrium_pressure_room_temp() {
let mh = MetalHydride::lani5();
let p = mh.equilibrium_pressure(313.0);
assert!(p > 0.5 && p < 20.0, "LaNi5 P_eq at 313 K = {p} bar");
}
#[test]
fn test_mgh2_equilibrium_pressure_high_temp() {
let mh = MetalHydride::mgh2();
let p = mh.equilibrium_pressure(573.0);
assert!(p > 0.1 && p < 50.0, "MgH2 P_eq at 573 K = {p} bar");
}
#[test]
fn test_equilibrium_pressure_increases_with_temperature() {
let mh = MetalHydride::lani5();
let p_low = mh.equilibrium_pressure(300.0);
let p_high = mh.equilibrium_pressure(350.0);
assert!(
p_high > p_low,
"Higher T must give higher P_eq: {p_low} vs {p_high}"
);
}
#[test]
fn test_equilibrium_pressure_positive() {
let mh = MetalHydride::lani5();
assert!(mh.equilibrium_pressure(400.0) > 0.0);
}
#[test]
fn test_metal_hydride_capacity_positive() {
let mh = MetalHydride::mgh2();
assert!(mh.max_capacity_wt_percent > 0.0);
}
#[test]
fn test_metal_hydride_new_roundtrip() {
let mh = MetalHydride::new("TestMH", 3.5, 400.0, -50_000.0, -120.0);
assert_eq!(mh.metal_name, "TestMH");
assert!((mh.max_capacity_wt_percent - 3.5).abs() < EPS);
}
#[test]
fn test_pressure_vessel_mass_positive() {
let pv = PressureVesselH2::type_iv_700bar();
let m = pv.mass_h2();
assert!(m > 0.0, "Mass must be positive, got {m}");
}
#[test]
fn test_pressure_vessel_mass_scales_with_volume() {
let pv1 = PressureVesselH2::new(100.0, 700.0, 298.15);
let pv2 = PressureVesselH2::new(200.0, 700.0, 298.15);
let ratio = pv2.mass_h2() / pv1.mass_h2();
assert!(
(ratio - 2.0).abs() < 1e-9,
"Mass must scale linearly with volume, ratio={ratio}"
);
}
#[test]
fn test_pressure_vessel_mass_scales_with_pressure() {
let pv1 = PressureVesselH2::new(100.0, 350.0, 298.15);
let pv2 = PressureVesselH2::new(100.0, 700.0, 298.15);
let ratio = pv2.mass_h2() / pv1.mass_h2();
assert!(
(ratio - 2.0).abs() < 1e-9,
"Mass must scale linearly with pressure, ratio={ratio}"
);
}
#[test]
fn test_pressure_vessel_energy_density_lhv() {
let pv = PressureVesselH2::new(50.0, 350.0, 298.15);
assert!((pv.energy_density() - LHV_H2_MJ_KG).abs() < EPS);
}
#[test]
fn test_pressure_vessel_stored_energy() {
let pv = PressureVesselH2::new(100.0, 350.0, 298.15);
let expected = pv.mass_h2() * LHV_H2_MJ_KG;
assert!((pv.stored_energy_mj() - expected).abs() < EPS);
}
#[test]
fn test_pressure_vessel_type_iv_reasonable_mass() {
let pv = PressureVesselH2::type_iv_700bar();
let m = pv.mass_h2();
assert!(
m > 3.0 && m < 10.0,
"Type-IV 700-bar vessel should store ~5 kg H₂, got {m}"
);
}
#[test]
fn test_mof_capacity_positive() {
let mof = MofAdsorption::mof5();
let c = mof.capacity_at_condition(77.0, 40.0);
assert!(c > 0.0, "MOF capacity must be positive, got {c}");
}
#[test]
fn test_mof_capacity_below_saturation() {
let mof = MofAdsorption::mof5();
let c = mof.capacity_at_condition(77.0, 40.0);
assert!(
c <= mof.saturation_capacity_wt,
"Capacity {c} must not exceed saturation {}",
mof.saturation_capacity_wt
);
}
#[test]
fn test_mof_capacity_increases_with_pressure() {
let mof = MofAdsorption::mof5();
let c_low = mof.capacity_at_condition(77.0, 1.0);
let c_high = mof.capacity_at_condition(77.0, 40.0);
assert!(c_high > c_low, "Higher pressure must give higher capacity");
}
#[test]
fn test_mof_capacity_decreases_with_temperature() {
let mof = MofAdsorption::mof5();
let c_cold = mof.capacity_at_condition(77.0, 10.0);
let c_warm = mof.capacity_at_condition(298.0, 10.0);
assert!(c_cold > c_warm, "Lower T must give higher physisorption");
}
#[test]
fn test_mof_capacity_zero_pressure() {
let mof = MofAdsorption::mof5();
let c = mof.capacity_at_condition(77.0, 0.0);
assert!(c.abs() < EPS, "Zero pressure must give zero capacity");
}
#[test]
fn test_safety_lfl_not_flammable_below() {
let safety = HydrogenSafety::standard();
assert!(!safety.is_flammable_mixture(0.03));
}
#[test]
fn test_safety_at_lfl_flammable() {
let safety = HydrogenSafety::standard();
assert!(safety.is_flammable_mixture(0.04));
}
#[test]
fn test_safety_midrange_flammable() {
let safety = HydrogenSafety::standard();
assert!(safety.is_flammable_mixture(0.20));
}
#[test]
fn test_safety_at_ufl_flammable() {
let safety = HydrogenSafety::standard();
assert!(safety.is_flammable_mixture(0.75));
}
#[test]
fn test_safety_above_ufl_not_flammable() {
let safety = HydrogenSafety::standard();
assert!(!safety.is_flammable_mixture(0.80));
}
#[test]
fn test_safety_zero_concentration_not_flammable() {
let safety = HydrogenSafety::standard();
assert!(!safety.is_flammable_mixture(0.0));
}
#[test]
fn test_safety_autoignition_temp() {
let safety = HydrogenSafety::standard();
assert!((safety.autoignition_temp - 773.0).abs() < EPS);
}
#[test]
fn test_safety_lfl_4_percent() {
let safety = HydrogenSafety::standard();
assert!((safety.lower_flammability_limit - 0.04).abs() < EPS);
}
#[test]
fn test_safety_ufl_75_percent() {
let safety = HydrogenSafety::standard();
assert!((safety.upper_flammability_limit - 0.75).abs() < EPS);
}
#[test]
fn test_gravimetric_energy_density_7wt() {
let e = gravimetric_energy_density_mj_kg(7.0);
assert!((e - 0.07 * LHV_H2_MJ_KG).abs() < 1e-9, "Got {e}");
}
#[test]
fn test_gravimetric_energy_density_scales_linearly() {
let e1 = gravimetric_energy_density_mj_kg(2.0);
let e2 = gravimetric_energy_density_mj_kg(4.0);
assert!((e2 - 2.0 * e1).abs() < EPS, "Must scale linearly");
}
#[test]
fn test_volumetric_capacity() {
let vc = volumetric_capacity(1000.0, 5.0);
assert!((vc - 50.0).abs() < EPS, "Got {vc}");
}
#[test]
fn test_volumetric_capacity_zero_wt() {
let vc = volumetric_capacity(800.0, 0.0);
assert!(vc.abs() < EPS);
}
#[test]
fn test_vessel_wrinkling_wavelength() {
let lam = vessel_wrinkling_wavelength(1.0, 1.0);
let expected = 2.0 * PI;
assert!((lam - expected).abs() < 1e-10, "Got {lam}");
}
#[test]
fn test_storage_type_eq() {
assert_eq!(
HydrogenStorageType::CompressedGas,
HydrogenStorageType::CompressedGas
);
assert_ne!(HydrogenStorageType::LiquidH2, HydrogenStorageType::MOF);
}
#[test]
fn test_storage_type_clone() {
let t = HydrogenStorageType::MetalHydride;
let t2 = t.clone();
assert_eq!(t, t2);
}
#[test]
fn test_mof_new_fields_accessible() {
let mof = MofAdsorption::new(2000.0, 1.0, 5000.0, 5.0, 77.0, 0.03);
assert!((mof.surface_area_m2g - 2000.0).abs() < EPS);
assert!((mof.pore_volume - 1.0).abs() < EPS);
}
#[test]
fn test_h2_density_linearity_low_pressure() {
let rho_1 = h2_density_kg_m3(1.0, 300.0);
let rho_2 = h2_density_kg_m3(2.0, 300.0);
let ratio = rho_2 / rho_1;
assert!(
(ratio - 2.0).abs() < 0.15,
"Low-P density should be ~linear in P: ratio={ratio}"
);
}
}
pub fn van_der_waals_h2(p: f64, t: f64, a: f64, b: f64) -> f64 {
let f = |v: f64| -> f64 { (p + a / (v * v)) * (v - b) - R_GAS * t };
let v_lo = b * 1.001_f64;
let v_hi = 10.0 * R_GAS * t / p.max(1.0);
if f(v_lo) * f(v_hi) > 0.0 {
return R_GAS * t / p;
}
let mut lo = v_lo;
let mut hi = v_hi;
for _ in 0..80 {
let mid = 0.5 * (lo + hi);
if f(lo) * f(mid) <= 0.0 {
hi = mid;
} else {
lo = mid;
}
}
0.5 * (lo + hi)
}
pub fn sievert_law(k_s: f64, p_h2: f64) -> f64 {
k_s * p_h2.sqrt()
}
pub fn clausius_clapeyron_h2(delta_h: f64, t1: f64, t2: f64) -> f64 {
(-delta_h / R_GAS * (1.0 / t2 - 1.0 / t1)).exp()
}
pub fn langmuir_hydrogen(k: f64, pressure: f64) -> f64 {
k * pressure / (1.0 + k * pressure)
}
pub fn bep_relationship(barrier: f64, delta_e: f64, alpha: f64) -> f64 {
barrier + alpha * delta_e
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HydrideStorage {
pub capacity_wt_pct: f64,
pub temperature: f64,
pub pressure: f64,
pub activation_energy: f64,
}
impl HydrideStorage {
pub fn new(cap: f64, t: f64, p: f64) -> Self {
Self {
capacity_wt_pct: cap,
temperature: t,
pressure: p,
activation_energy: 50_000.0,
}
}
pub fn with_activation_energy(cap: f64, t: f64, p: f64, ea: f64) -> Self {
Self {
capacity_wt_pct: cap,
temperature: t,
pressure: p,
activation_energy: ea,
}
}
pub fn desorption_rate(&self) -> f64 {
let a_pre = 1.0e6_f64;
self.capacity_wt_pct * a_pre * (-self.activation_energy / (R_GAS * self.temperature)).exp()
}
pub fn gravimetric_density(&self) -> f64 {
(self.capacity_wt_pct / 100.0) * LHV_H2_MJ_KG / 3.6 }
pub fn charging_time(&self, target_wt_pct: f64) -> f64 {
let rate = self.desorption_rate();
if rate <= 0.0 {
return f64::INFINITY;
}
target_wt_pct / rate
}
}
pub fn hydrogen_energy_density_volumetric(pressure_mpa: f64, temperature_k: f64) -> f64 {
let p_pa = pressure_mpa * 1.0e6;
let rho = p_pa * M_H2 / (R_GAS * temperature_k);
rho * LHV_H2_MJ_KG / 3.6 / 1000.0
}
pub fn storage_efficiency(useful_energy: f64, total_system_mass: f64) -> f64 {
useful_energy / total_system_mass
}
pub fn safe_pressure_limit(vessel_diameter: f64, wall_thickness: f64, uts: f64) -> f64 {
2.0 * wall_thickness * uts / vessel_diameter
}
#[cfg(test)]
mod extended_tests {
use crate::additive_manufacturing_materials::R_GAS;
use crate::hydrogen_storage::HydrideStorage;
use crate::hydrogen_storage::bep_relationship;
use crate::hydrogen_storage::clausius_clapeyron_h2;
use crate::hydrogen_storage::hydrogen_energy_density_volumetric;
use crate::hydrogen_storage::langmuir_hydrogen;
use crate::hydrogen_storage::safe_pressure_limit;
use crate::hydrogen_storage::sievert_law;
use crate::hydrogen_storage::storage_efficiency;
use crate::hydrogen_storage::van_der_waals_h2;
const EPS: f64 = 1e-10;
const A_VDW: f64 = 0.02476e-3 * 101_325.0;
const B_VDW: f64 = 26.61e-6;
#[test]
fn test_vdw_positive_volume() {
let v = van_der_waals_h2(1e5, 300.0, A_VDW, B_VDW);
assert!(v > 0.0, "Molar volume must be positive, got {v}");
}
#[test]
fn test_vdw_volume_decreases_with_pressure() {
let v_low = van_der_waals_h2(1e5, 300.0, A_VDW, B_VDW);
let v_high = van_der_waals_h2(1e6, 300.0, A_VDW, B_VDW);
assert!(v_low > v_high, "Higher pressure → smaller molar volume");
}
#[test]
fn test_vdw_approaches_ideal_at_low_p() {
let v = van_der_waals_h2(1e5, 300.0, A_VDW, B_VDW);
let v_ideal = R_GAS * 300.0 / 1e5;
assert!(
(v - v_ideal).abs() / v_ideal < 0.05,
"vdW should ≈ ideal at low P: vdW={v}, ideal={v_ideal}"
);
}
#[test]
fn test_vdw_finite() {
let v = van_der_waals_h2(7e7, 300.0, A_VDW, B_VDW);
assert!(v.is_finite() && v > 0.0);
}
#[test]
fn test_sievert_positive() {
let c = sievert_law(1e-3, 1e5);
assert!(c > 0.0, "Sievert concentration must be positive");
}
#[test]
fn test_sievert_sqrt_dependence() {
let c1 = sievert_law(1e-3, 1e5);
let c2 = sievert_law(1e-3, 4e5);
let ratio = c2 / c1;
assert!(
(ratio - 2.0).abs() < 1e-9,
"Sievert: c should scale as √P, ratio={ratio}"
);
}
#[test]
fn test_sievert_zero_pressure() {
let c = sievert_law(1e-3, 0.0);
assert!(c.abs() < EPS, "Zero pressure → zero concentration");
}
#[test]
fn test_sievert_increases_with_pressure() {
let c1 = sievert_law(1e-3, 1e5);
let c2 = sievert_law(1e-3, 9e5);
assert!(c2 > c1);
}
#[test]
fn test_clausius_clapeyron_positive_ratio() {
let ratio = clausius_clapeyron_h2(30_000.0, 300.0, 350.0);
assert!(
ratio > 1.0,
"Higher T → higher equilibrium P for endothermic: ratio={ratio}"
);
}
#[test]
fn test_clausius_clapeyron_t1_eq_t2() {
let ratio = clausius_clapeyron_h2(30_000.0, 300.0, 300.0);
assert!((ratio - 1.0).abs() < 1e-9, "Equal temps → ratio=1");
}
#[test]
fn test_clausius_clapeyron_finite() {
let ratio = clausius_clapeyron_h2(74_500.0, 573.0, 700.0);
assert!(ratio.is_finite() && ratio > 0.0);
}
#[test]
fn test_langmuir_in_zero_one() {
let theta = langmuir_hydrogen(1e-5, 1e5);
assert!(
(0.0..1.0).contains(&theta),
"Langmuir theta must be in [0,1): {theta}"
);
}
#[test]
fn test_langmuir_increases_with_pressure() {
let t1 = langmuir_hydrogen(1e-5, 1e5);
let t2 = langmuir_hydrogen(1e-5, 1e6);
assert!(t2 > t1, "Higher pressure → higher Langmuir coverage");
}
#[test]
fn test_langmuir_zero_pressure() {
let theta = langmuir_hydrogen(1e-5, 0.0);
assert!(theta.abs() < EPS, "Zero pressure → zero Langmuir coverage");
}
#[test]
fn test_langmuir_approaches_one_high_pressure() {
let theta = langmuir_hydrogen(1.0, 1e9);
assert!(theta > 0.999, "Very high P → θ → 1, got {theta}");
}
#[test]
fn test_bep_positive_barrier() {
let ea = bep_relationship(50_000.0, 10_000.0, 0.5);
assert!(ea > 0.0);
}
#[test]
fn test_bep_formula() {
let ea = bep_relationship(50_000.0, 20_000.0, 0.3);
let expected = 50_000.0 + 0.3 * 20_000.0;
assert!((ea - expected).abs() < EPS);
}
#[test]
fn test_hydride_storage_desorption_rate_positive() {
let hs = HydrideStorage::new(6.0, 573.0, 1e5);
assert!(
hs.desorption_rate() > 0.0,
"Desorption rate must be positive"
);
}
#[test]
fn test_hydride_storage_desorption_rate_increases_with_temperature() {
let hs_cold = HydrideStorage::new(6.0, 400.0, 1e5);
let hs_hot = HydrideStorage::new(6.0, 600.0, 1e5);
assert!(
hs_hot.desorption_rate() > hs_cold.desorption_rate(),
"Higher T → faster desorption"
);
}
#[test]
fn test_hydride_storage_gravimetric_density_positive() {
let hs = HydrideStorage::new(6.0, 573.0, 1e5);
assert!(hs.gravimetric_density() > 0.0);
}
#[test]
fn test_hydride_storage_charging_time_positive() {
let hs = HydrideStorage::new(6.0, 573.0, 1e5);
let t = hs.charging_time(5.0);
assert!(
t > 0.0 && t.is_finite(),
"Charging time must be positive and finite"
);
}
#[test]
fn test_hydride_storage_new_fields() {
let hs = HydrideStorage::new(7.6, 573.0, 1e5);
assert!((hs.capacity_wt_pct - 7.6).abs() < EPS);
assert!((hs.temperature - 573.0).abs() < EPS);
}
#[test]
fn test_energy_density_positive() {
let e = hydrogen_energy_density_volumetric(70.0, 298.15);
assert!(e > 0.0, "Energy density must be positive");
}
#[test]
fn test_energy_density_increases_with_pressure() {
let e1 = hydrogen_energy_density_volumetric(35.0, 298.15);
let e2 = hydrogen_energy_density_volumetric(70.0, 298.15);
assert!(
e2 > e1,
"Higher pressure → higher volumetric energy density"
);
}
#[test]
fn test_storage_efficiency_positive() {
let eff = storage_efficiency(10.0, 100.0);
assert!(eff > 0.0);
}
#[test]
fn test_storage_efficiency_formula() {
let eff = storage_efficiency(15.0, 75.0);
assert!((eff - 0.2).abs() < 1e-12);
}
#[test]
fn test_safe_pressure_positive() {
let p = safe_pressure_limit(0.3, 0.01, 500e6);
assert!(p > 0.0, "Safe pressure must be positive");
}
#[test]
fn test_safe_pressure_increases_with_wall_thickness() {
let p_thin = safe_pressure_limit(0.3, 0.005, 500e6);
let p_thick = safe_pressure_limit(0.3, 0.015, 500e6);
assert!(p_thick > p_thin, "Thicker wall → higher safe pressure");
}
#[test]
fn test_safe_pressure_formula() {
let d = 0.4_f64;
let t = 0.01_f64;
let uts = 800e6_f64;
let expected = 2.0 * t * uts / d;
let got = safe_pressure_limit(d, t, uts);
assert!((got - expected).abs() < 1e-6);
}
#[test]
fn test_safe_pressure_increases_with_uts() {
let p1 = safe_pressure_limit(0.3, 0.01, 400e6);
let p2 = safe_pressure_limit(0.3, 0.01, 800e6);
assert!(p2 > p1, "Higher UTS → higher safe pressure");
}
}