use petir::mathf::RealMath;
use super::traits::EquationOfState;
use crate::thermophysics::constants::{P_REF, R_UNIVERSAL};
use crate::thermophysics::imports::*;
#[derive(Debug, Clone, Copy)]
pub struct PerfectGas {
mol_weight: MolarMass,
}
impl PerfectGas {
pub fn new(mol_weight: MolarMass) -> Self {
Self { mol_weight }
}
}
impl EquationOfState for PerfectGas {
fn mol_weight(&self) -> MolarMass {
self.mol_weight
}
fn r(&self) -> SpecificHeatCapacity {
let w = self.mol_weight.get::<kilogram_per_mole>();
SpecificHeatCapacity::new::<joule_per_kilogram_kelvin>(R_UNIVERSAL / w)
}
fn rho(&self, p: Pressure, t: ThermodynamicTemperature) -> MassDensity {
p / (self.r() * t)
}
fn psi(&self, p: Pressure, t: ThermodynamicTemperature) -> Compressibility {
self.rho(p, t) / p
}
fn z(&self, _p: Pressure, _t: ThermodynamicTemperature) -> Ratio {
Ratio::new::<ratio>(1.0)
}
fn cp_m_cv(&self, _p: Pressure, _t: ThermodynamicTemperature) -> SpecificHeatCapacity {
self.r()
}
fn cp_eos(&self, _p: Pressure, _t: ThermodynamicTemperature) -> SpecificHeatCapacity {
SpecificHeatCapacity::new::<joule_per_kilogram_kelvin>(0.0)
}
fn h_eos(&self, _p: Pressure, _t: ThermodynamicTemperature) -> AvailableEnergy {
AvailableEnergy::new::<joule_per_kilogram>(0.0)
}
fn e_eos(&self, _p: Pressure, _t: ThermodynamicTemperature) -> AvailableEnergy {
AvailableEnergy::new::<joule_per_kilogram>(0.0)
}
fn s_eos(&self, p: Pressure, _t: ThermodynamicTemperature) -> SpecificHeatCapacity {
let p_val = p.get::<pascal>();
let r_val = self.r().get::<joule_per_kilogram_kelvin>();
SpecificHeatCapacity::new::<joule_per_kilogram_kelvin>(-r_val * (p_val / P_REF).r_ln())
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use uom::si::mass_density::kilogram_per_cubic_meter;
use uom::si::molar_mass::gram_per_mole;
use uom::si::pressure::pascal;
use uom::si::thermodynamic_temperature::kelvin;
fn air() -> PerfectGas {
PerfectGas::new(MolarMass::new::<gram_per_mole>(28.97))
}
#[test]
fn air_r() {
let r = air().r().get::<joule_per_kilogram_kelvin>();
assert_relative_eq!(r, 287.05, epsilon = 0.2);
}
#[test]
fn air_density_at_stp() {
let a = air();
let p = Pressure::new::<pascal>(101_325.0);
let t = ThermodynamicTemperature::new::<kelvin>(293.15);
let rho = a.rho(p, t).get::<kilogram_per_cubic_meter>();
assert_relative_eq!(rho, 1.205, epsilon = 0.005);
}
#[test]
fn air_z_is_one() {
let a = air();
let p = Pressure::new::<pascal>(101_325.0);
let t = ThermodynamicTemperature::new::<kelvin>(300.0);
assert_relative_eq!(a.z(p, t).get::<ratio>(), 1.0, epsilon = 1e-10);
}
#[test]
fn psi_times_p_equals_rho() {
let a = air();
let p = Pressure::new::<pascal>(101_325.0);
let t = ThermodynamicTemperature::new::<kelvin>(300.0);
let rho_direct = a.rho(p, t).get::<kilogram_per_cubic_meter>();
let rho_via_psi = (a.psi(p, t) * p).get::<kilogram_per_cubic_meter>();
assert_relative_eq!(rho_direct, rho_via_psi, epsilon = 1e-10);
}
}