1use serde::{Deserialize, Serialize};
27
28use crate::error::{RefpropError, Result};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36pub enum TempUnit {
37 Kelvin,
39 Celsius,
41 Fahrenheit,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
47pub enum PressUnit {
48 KPa,
50 Bar,
52 MPa,
54 Pa,
56 Atm,
58 Psi,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64pub enum DensityUnit {
65 MolPerL,
67 KgPerM3,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73pub enum EnergyUnit {
74 JPerMol,
76 KJPerKg,
78 JPerKg,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
84pub enum EntropyUnit {
85 JPerMolK,
87 KJPerKgK,
89 JPerKgK,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
95pub enum ViscosityUnit {
96 MicroPaS,
98 MilliPaS,
100 PaS,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
106pub enum ConductivityUnit {
107 WPerMK,
109 MilliWPerMK,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct UnitSystem {
123 pub temperature: TempUnit,
124 pub pressure: PressUnit,
125 pub density: DensityUnit,
126 pub energy: EnergyUnit,
127 pub entropy: EntropyUnit,
128 pub viscosity: ViscosityUnit,
129 pub conductivity: ConductivityUnit,
130}
131
132impl UnitSystem {
133 pub fn new() -> Self {
136 Self::refprop()
137 }
138
139 pub fn refprop() -> Self {
143 Self {
144 temperature: TempUnit::Kelvin,
145 pressure: PressUnit::KPa,
146 density: DensityUnit::MolPerL,
147 energy: EnergyUnit::JPerMol,
148 entropy: EntropyUnit::JPerMolK,
149 viscosity: ViscosityUnit::MicroPaS,
150 conductivity: ConductivityUnit::WPerMK,
151 }
152 }
153
154 pub fn engineering() -> Self {
156 Self {
157 temperature: TempUnit::Celsius,
158 pressure: PressUnit::Bar,
159 density: DensityUnit::KgPerM3,
160 energy: EnergyUnit::KJPerKg,
161 entropy: EntropyUnit::KJPerKgK,
162 viscosity: ViscosityUnit::MicroPaS,
163 conductivity: ConductivityUnit::WPerMK,
164 }
165 }
166
167 pub fn si() -> Self {
169 Self {
170 temperature: TempUnit::Kelvin,
171 pressure: PressUnit::Pa,
172 density: DensityUnit::KgPerM3,
173 energy: EnergyUnit::JPerKg,
174 entropy: EntropyUnit::JPerKgK,
175 viscosity: ViscosityUnit::PaS,
176 conductivity: ConductivityUnit::WPerMK,
177 }
178 }
179
180 pub fn temperature(mut self, u: TempUnit) -> Self {
183 self.temperature = u;
184 self
185 }
186 pub fn pressure(mut self, u: PressUnit) -> Self {
187 self.pressure = u;
188 self
189 }
190 pub fn density(mut self, u: DensityUnit) -> Self {
191 self.density = u;
192 self
193 }
194 pub fn energy(mut self, u: EnergyUnit) -> Self {
195 self.energy = u;
196 self
197 }
198 pub fn entropy(mut self, u: EntropyUnit) -> Self {
199 self.entropy = u;
200 self
201 }
202 pub fn viscosity(mut self, u: ViscosityUnit) -> Self {
203 self.viscosity = u;
204 self
205 }
206 pub fn conductivity(mut self, u: ConductivityUnit) -> Self {
207 self.conductivity = u;
208 self
209 }
210}
211
212impl Default for UnitSystem {
213 fn default() -> Self {
214 Self::refprop()
215 }
216}
217
218#[derive(Debug, Clone)]
227pub struct Converter {
228 pub units: UnitSystem,
229 pub molar_mass: f64,
231}
232
233impl Converter {
234 pub fn new(units: UnitSystem, molar_mass: f64) -> Self {
235 Self { units, molar_mass }
236 }
237
238 pub fn identity() -> Self {
241 Self {
242 units: UnitSystem::refprop(),
243 molar_mass: 1.0,
244 }
245 }
246
247 pub fn t_to_rp(&self, t: f64) -> f64 {
251 match self.units.temperature {
252 TempUnit::Kelvin => t,
253 TempUnit::Celsius => t + 273.15,
254 TempUnit::Fahrenheit => (t - 32.0) * 5.0 / 9.0 + 273.15,
255 }
256 }
257
258 pub fn t_from_rp(&self, t: f64) -> f64 {
260 match self.units.temperature {
261 TempUnit::Kelvin => t,
262 TempUnit::Celsius => t - 273.15,
263 TempUnit::Fahrenheit => (t - 273.15) * 9.0 / 5.0 + 32.0,
264 }
265 }
266
267 pub fn p_to_rp(&self, p: f64) -> f64 {
271 match self.units.pressure {
272 PressUnit::KPa => p,
273 PressUnit::Bar => p * 100.0,
274 PressUnit::MPa => p * 1000.0,
275 PressUnit::Pa => p / 1000.0,
276 PressUnit::Atm => p * 101.325,
277 PressUnit::Psi => p * 6.894_757,
278 }
279 }
280
281 pub fn p_from_rp(&self, p: f64) -> f64 {
283 match self.units.pressure {
284 PressUnit::KPa => p,
285 PressUnit::Bar => p / 100.0,
286 PressUnit::MPa => p / 1000.0,
287 PressUnit::Pa => p * 1000.0,
288 PressUnit::Atm => p / 101.325,
289 PressUnit::Psi => p / 6.894_757,
290 }
291 }
292
293 pub fn d_to_rp(&self, d: f64) -> f64 {
297 match self.units.density {
298 DensityUnit::MolPerL => d,
299 DensityUnit::KgPerM3 => d / self.molar_mass,
300 }
301 }
302
303 pub fn d_from_rp(&self, d: f64) -> f64 {
305 match self.units.density {
306 DensityUnit::MolPerL => d,
307 DensityUnit::KgPerM3 => d * self.molar_mass,
308 }
309 }
310
311 pub fn h_to_rp(&self, h: f64) -> f64 {
315 match self.units.energy {
316 EnergyUnit::JPerMol => h,
317 EnergyUnit::KJPerKg => h * self.molar_mass,
318 EnergyUnit::JPerKg => h * self.molar_mass / 1000.0,
319 }
320 }
321
322 pub fn h_from_rp(&self, h: f64) -> f64 {
324 match self.units.energy {
325 EnergyUnit::JPerMol => h,
326 EnergyUnit::KJPerKg => h / self.molar_mass,
327 EnergyUnit::JPerKg => h * 1000.0 / self.molar_mass,
328 }
329 }
330
331 pub fn s_to_rp(&self, s: f64) -> f64 {
335 match self.units.entropy {
336 EntropyUnit::JPerMolK => s,
337 EntropyUnit::KJPerKgK => s * self.molar_mass,
338 EntropyUnit::JPerKgK => s * self.molar_mass / 1000.0,
339 }
340 }
341
342 pub fn s_from_rp(&self, s: f64) -> f64 {
344 match self.units.entropy {
345 EntropyUnit::JPerMolK => s,
346 EntropyUnit::KJPerKgK => s / self.molar_mass,
347 EntropyUnit::JPerKgK => s * 1000.0 / self.molar_mass,
348 }
349 }
350
351 pub fn eta_from_rp(&self, eta: f64) -> f64 {
355 match self.units.viscosity {
356 ViscosityUnit::MicroPaS => eta,
357 ViscosityUnit::MilliPaS => eta / 1000.0,
358 ViscosityUnit::PaS => eta / 1_000_000.0,
359 }
360 }
361
362 pub fn eta_to_rp(&self, eta: f64) -> f64 {
364 match self.units.viscosity {
365 ViscosityUnit::MicroPaS => eta,
366 ViscosityUnit::MilliPaS => eta * 1000.0,
367 ViscosityUnit::PaS => eta * 1_000_000.0,
368 }
369 }
370
371 pub fn tcx_from_rp(&self, tcx: f64) -> f64 {
375 match self.units.conductivity {
376 ConductivityUnit::WPerMK => tcx,
377 ConductivityUnit::MilliWPerMK => tcx * 1000.0,
378 }
379 }
380
381 pub fn tcx_to_rp(&self, tcx: f64) -> f64 {
383 match self.units.conductivity {
384 ConductivityUnit::WPerMK => tcx,
385 ConductivityUnit::MilliWPerMK => tcx / 1000.0,
386 }
387 }
388
389 pub fn q_to_rp(&self, q: f64) -> Result<f64> {
396 if q < 0.0 || q > 100.0 {
397 return Err(RefpropError::InvalidInput(format!(
398 "Quality Q must be between 0 and 100 (got {q})"
399 )));
400 }
401 Ok(q / 100.0)
402 }
403
404 pub fn q_from_rp(&self, q: f64) -> f64 {
406 q * 100.0
407 }
408
409 pub fn input_to_rp(&self, key: &str, val: f64) -> Result<f64> {
419 match key.to_uppercase().as_str() {
420 "T" => Ok(self.t_to_rp(val)),
421 "P" => Ok(self.p_to_rp(val)),
422 "D" | "RHO" => Ok(self.d_to_rp(val)),
423 "H" => Ok(self.h_to_rp(val)),
424 "S" => Ok(self.s_to_rp(val)),
425 "E" | "U" => Ok(self.h_to_rp(val)),
426 "CV" | "CP" => Ok(self.s_to_rp(val)),
427 "ETA" | "V" | "VIS" => Ok(self.eta_to_rp(val)),
428 "TCX" | "L" | "LAMBDA" => Ok(self.tcx_to_rp(val)),
429 "Q" => self.q_to_rp(val),
430 _ => Ok(val), }
432 }
433
434 pub fn output_from_rp(&self, key: &str, val: f64) -> f64 {
439 match key.to_uppercase().as_str() {
440 "T" => self.t_from_rp(val),
441 "P" => self.p_from_rp(val),
442 "D" | "RHO" => self.d_from_rp(val),
443 "H" => self.h_from_rp(val),
444 "S" => self.s_from_rp(val),
445 "E" | "U" => self.h_from_rp(val),
446 "CV" | "CP" => self.s_from_rp(val),
447 "ETA" | "V" | "VIS" => self.eta_from_rp(val),
448 "TCX" | "L" | "LAMBDA" => self.tcx_from_rp(val),
449 "Q" => self.q_from_rp(val),
450 _ => val, }
452 }
453}