pub mod backend;
mod common;
mod defined;
mod invariant;
mod request;
mod undefined;
use std::{collections::HashMap, fmt::Debug, marker::PhantomData};
use backend::Backend;
use request::FluidUpdateRequest;
use crate::{
io::{FluidParam, FluidTrivialParam},
native::{AbstractState, CoolPropError},
state_variant::{Defined, StateVariant, Undefined},
substance::{BinaryMix, CustomMix, IncompPure, PredefinedMix, Pure, Substance},
};
pub type StateResult<T> = Result<T, FluidStateError>;
pub type OutputResult<T> = Result<T, FluidOutputError>;
#[derive(Debug)]
pub struct Fluid<S: StateVariant = Defined> {
backend: AbstractState,
backend_variant: Backend,
substance: Substance,
update_request: Option<FluidUpdateRequest>,
outputs: HashMap<FluidParam, OutputResult<f64>>,
trivial_outputs: HashMap<FluidTrivialParam, OutputResult<f64>>,
state: PhantomData<S>,
}
impl TryFrom<Substance> for Fluid<Undefined> {
type Error = FluidBuildError;
fn try_from(value: Substance) -> Result<Self, Self::Error> {
Self::builder().substance(value).build()
}
}
impl From<Pure> for Fluid<Undefined> {
fn from(value: Pure) -> Self {
Substance::from(value).try_into().unwrap()
}
}
impl From<IncompPure> for Fluid<Undefined> {
fn from(value: IncompPure) -> Self {
Substance::from(value).try_into().unwrap()
}
}
impl From<PredefinedMix> for Fluid<Undefined> {
fn from(value: PredefinedMix) -> Self {
Substance::from(value).try_into().unwrap()
}
}
impl From<BinaryMix> for Fluid<Undefined> {
fn from(value: BinaryMix) -> Self {
Substance::from(value).try_into().unwrap()
}
}
impl TryFrom<CustomMix> for Fluid<Undefined> {
type Error = FluidBuildError;
fn try_from(value: CustomMix) -> Result<Self, Self::Error> {
Substance::from(value).try_into()
}
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[error("Unable to build fluid! {0}")]
pub struct FluidBuildError(#[from] CoolPropError);
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum FluidStateError {
#[error("Specified inputs (`{0:?}`, `{1:?}`) are invalid!")]
InvalidInputPair(FluidParam, FluidParam),
#[error("Input values must be finite!")]
InvalidInputValue,
#[error("Failed to update the fluid state! {0}")]
UpdateFailed(#[from] CoolPropError),
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum FluidOutputError {
#[error("Specified trivial output parameter `{0:?}` is not available!")]
UnavailableTrivialOutput(FluidTrivialParam),
#[error("Specified output parameter `{0:?}` is not available!")]
UnavailableOutput(FluidParam),
#[error("Failed to calculate the output value of `{0:?}`! {1}")]
CalculationFailed(FluidParam, CoolPropError),
}
#[cfg(test)]
mod tests {
use strum::IntoEnumIterator;
use super::*;
use crate::substance::*;
#[test]
fn from_each_pure() {
for substance in Pure::iter() {
let sut = Fluid::from(substance);
let res = size_of_val(&sut);
assert!(res > 0);
}
}
#[test]
fn from_each_incomp_pure() {
for substance in IncompPure::iter() {
let sut = Fluid::from(substance);
let res = size_of_val(&sut);
assert!(res > 0);
}
}
#[test]
fn from_each_predefined_mix() {
for substance in PredefinedMix::iter() {
let sut = Fluid::from(substance);
let res = size_of_val(&sut);
assert!(res > 0);
}
}
#[test]
fn from_each_binary_mix() {
for kind in BinaryMixKind::iter() {
let sut = Fluid::from(
kind.with_fraction(0.5 * (kind.min_fraction() + kind.max_fraction())).unwrap(),
);
let res = size_of_val(&sut);
assert!(res > 0);
}
}
#[test]
fn try_from_supported_custom_mix() {
let supported_mix = CustomMix::mass_based([(Pure::R32, 0.7), (Pure::R134a, 0.3)]).unwrap();
let res = Fluid::try_from(supported_mix);
assert!(res.is_ok());
}
#[test]
fn try_from_unsupported_custom_mix() {
let unsupported_mix =
CustomMix::mole_based([(Pure::Xenon, 0.1), (Pure::R22, 0.9)]).unwrap();
let res = Fluid::try_from(unsupported_mix);
assert!(res.is_err());
}
}