use serde::{Deserialize, Serialize};
use crate::error::{RefpropError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TempUnit {
Kelvin,
Celsius,
Fahrenheit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PressUnit {
KPa,
Bar,
MPa,
Pa,
Atm,
Psi,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DensityUnit {
MolPerL,
KgPerM3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EnergyUnit {
JPerMol,
KJPerKg,
JPerKg,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EntropyUnit {
JPerMolK,
KJPerKgK,
JPerKgK,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ViscosityUnit {
MicroPaS,
MilliPaS,
PaS,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConductivityUnit {
WPerMK,
MilliWPerMK,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnitSystem {
pub temperature: TempUnit,
pub pressure: PressUnit,
pub density: DensityUnit,
pub energy: EnergyUnit,
pub entropy: EntropyUnit,
pub viscosity: ViscosityUnit,
pub conductivity: ConductivityUnit,
}
impl UnitSystem {
pub fn new() -> Self {
Self::refprop()
}
pub fn refprop() -> Self {
Self {
temperature: TempUnit::Kelvin,
pressure: PressUnit::KPa,
density: DensityUnit::MolPerL,
energy: EnergyUnit::JPerMol,
entropy: EntropyUnit::JPerMolK,
viscosity: ViscosityUnit::MicroPaS,
conductivity: ConductivityUnit::WPerMK,
}
}
pub fn engineering() -> Self {
Self {
temperature: TempUnit::Celsius,
pressure: PressUnit::Bar,
density: DensityUnit::KgPerM3,
energy: EnergyUnit::KJPerKg,
entropy: EntropyUnit::KJPerKgK,
viscosity: ViscosityUnit::MicroPaS,
conductivity: ConductivityUnit::WPerMK,
}
}
pub fn si() -> Self {
Self {
temperature: TempUnit::Kelvin,
pressure: PressUnit::Pa,
density: DensityUnit::KgPerM3,
energy: EnergyUnit::JPerKg,
entropy: EntropyUnit::JPerKgK,
viscosity: ViscosityUnit::PaS,
conductivity: ConductivityUnit::WPerMK,
}
}
pub fn temperature(mut self, u: TempUnit) -> Self {
self.temperature = u;
self
}
pub fn pressure(mut self, u: PressUnit) -> Self {
self.pressure = u;
self
}
pub fn density(mut self, u: DensityUnit) -> Self {
self.density = u;
self
}
pub fn energy(mut self, u: EnergyUnit) -> Self {
self.energy = u;
self
}
pub fn entropy(mut self, u: EntropyUnit) -> Self {
self.entropy = u;
self
}
pub fn viscosity(mut self, u: ViscosityUnit) -> Self {
self.viscosity = u;
self
}
pub fn conductivity(mut self, u: ConductivityUnit) -> Self {
self.conductivity = u;
self
}
}
impl Default for UnitSystem {
fn default() -> Self {
Self::refprop()
}
}
#[derive(Debug, Clone)]
pub struct Converter {
pub units: UnitSystem,
pub molar_mass: f64,
}
impl Converter {
pub fn new(units: UnitSystem, molar_mass: f64) -> Self {
Self { units, molar_mass }
}
pub fn identity() -> Self {
Self {
units: UnitSystem::refprop(),
molar_mass: 1.0,
}
}
pub fn t_to_rp(&self, t: f64) -> f64 {
match self.units.temperature {
TempUnit::Kelvin => t,
TempUnit::Celsius => t + 273.15,
TempUnit::Fahrenheit => (t - 32.0) * 5.0 / 9.0 + 273.15,
}
}
pub fn t_from_rp(&self, t: f64) -> f64 {
match self.units.temperature {
TempUnit::Kelvin => t,
TempUnit::Celsius => t - 273.15,
TempUnit::Fahrenheit => (t - 273.15) * 9.0 / 5.0 + 32.0,
}
}
pub fn p_to_rp(&self, p: f64) -> f64 {
match self.units.pressure {
PressUnit::KPa => p,
PressUnit::Bar => p * 100.0,
PressUnit::MPa => p * 1000.0,
PressUnit::Pa => p / 1000.0,
PressUnit::Atm => p * 101.325,
PressUnit::Psi => p * 6.894_757,
}
}
pub fn p_from_rp(&self, p: f64) -> f64 {
match self.units.pressure {
PressUnit::KPa => p,
PressUnit::Bar => p / 100.0,
PressUnit::MPa => p / 1000.0,
PressUnit::Pa => p * 1000.0,
PressUnit::Atm => p / 101.325,
PressUnit::Psi => p / 6.894_757,
}
}
pub fn d_to_rp(&self, d: f64) -> f64 {
match self.units.density {
DensityUnit::MolPerL => d,
DensityUnit::KgPerM3 => d / self.molar_mass,
}
}
pub fn d_from_rp(&self, d: f64) -> f64 {
match self.units.density {
DensityUnit::MolPerL => d,
DensityUnit::KgPerM3 => d * self.molar_mass,
}
}
pub fn h_to_rp(&self, h: f64) -> f64 {
match self.units.energy {
EnergyUnit::JPerMol => h,
EnergyUnit::KJPerKg => h * self.molar_mass,
EnergyUnit::JPerKg => h * self.molar_mass / 1000.0,
}
}
pub fn h_from_rp(&self, h: f64) -> f64 {
match self.units.energy {
EnergyUnit::JPerMol => h,
EnergyUnit::KJPerKg => h / self.molar_mass,
EnergyUnit::JPerKg => h * 1000.0 / self.molar_mass,
}
}
pub fn s_to_rp(&self, s: f64) -> f64 {
match self.units.entropy {
EntropyUnit::JPerMolK => s,
EntropyUnit::KJPerKgK => s * self.molar_mass,
EntropyUnit::JPerKgK => s * self.molar_mass / 1000.0,
}
}
pub fn s_from_rp(&self, s: f64) -> f64 {
match self.units.entropy {
EntropyUnit::JPerMolK => s,
EntropyUnit::KJPerKgK => s / self.molar_mass,
EntropyUnit::JPerKgK => s * 1000.0 / self.molar_mass,
}
}
pub fn eta_from_rp(&self, eta: f64) -> f64 {
match self.units.viscosity {
ViscosityUnit::MicroPaS => eta,
ViscosityUnit::MilliPaS => eta / 1000.0,
ViscosityUnit::PaS => eta / 1_000_000.0,
}
}
pub fn eta_to_rp(&self, eta: f64) -> f64 {
match self.units.viscosity {
ViscosityUnit::MicroPaS => eta,
ViscosityUnit::MilliPaS => eta * 1000.0,
ViscosityUnit::PaS => eta * 1_000_000.0,
}
}
pub fn tcx_from_rp(&self, tcx: f64) -> f64 {
match self.units.conductivity {
ConductivityUnit::WPerMK => tcx,
ConductivityUnit::MilliWPerMK => tcx * 1000.0,
}
}
pub fn tcx_to_rp(&self, tcx: f64) -> f64 {
match self.units.conductivity {
ConductivityUnit::WPerMK => tcx,
ConductivityUnit::MilliWPerMK => tcx / 1000.0,
}
}
pub fn q_to_rp(&self, q: f64) -> Result<f64> {
if q < 0.0 || q > 100.0 {
return Err(RefpropError::InvalidInput(format!(
"Quality Q must be between 0 and 100 (got {q})"
)));
}
Ok(q / 100.0)
}
pub fn q_from_rp(&self, q: f64) -> f64 {
q * 100.0
}
pub fn input_to_rp(&self, key: &str, val: f64) -> Result<f64> {
match key.to_uppercase().as_str() {
"T" => Ok(self.t_to_rp(val)),
"P" => Ok(self.p_to_rp(val)),
"D" | "RHO" => Ok(self.d_to_rp(val)),
"H" => Ok(self.h_to_rp(val)),
"S" => Ok(self.s_to_rp(val)),
"E" | "U" => Ok(self.h_to_rp(val)),
"CV" | "CP" => Ok(self.s_to_rp(val)),
"ETA" | "V" | "VIS" => Ok(self.eta_to_rp(val)),
"TCX" | "L" | "LAMBDA" => Ok(self.tcx_to_rp(val)),
"Q" => self.q_to_rp(val),
_ => Ok(val), }
}
pub fn output_from_rp(&self, key: &str, val: f64) -> f64 {
match key.to_uppercase().as_str() {
"T" => self.t_from_rp(val),
"P" => self.p_from_rp(val),
"D" | "RHO" => self.d_from_rp(val),
"H" => self.h_from_rp(val),
"S" => self.s_from_rp(val),
"E" | "U" => self.h_from_rp(val),
"CV" | "CP" => self.s_from_rp(val),
"ETA" | "V" | "VIS" => self.eta_from_rp(val),
"TCX" | "L" | "LAMBDA" => self.tcx_from_rp(val),
"Q" => self.q_from_rp(val),
_ => val, }
}
}