use std::ops::Deref;
use crate::support::constraint::{Constrained, ConstraintResult, NonNegative, UnitInterval};
use uom::si::{
f64::{Ratio, ThermalConductance},
ratio::ratio,
};
use super::{CapacitanceRate, CapacityRatio};
pub trait EffectivenessRelation {
fn effectiveness(&self, ntu: Ntu, capacitance_rates: [CapacitanceRate; 2]) -> Effectiveness;
}
pub trait NtuRelation {
fn ntu(&self, effectiveness: Effectiveness, capacitance_rates: [CapacitanceRate; 2]) -> Ntu;
}
#[derive(Debug, Clone, Copy)]
pub struct Effectiveness(Constrained<Ratio, UnitInterval>);
impl Effectiveness {
pub fn new(value: f64) -> ConstraintResult<Self> {
let quantity = Ratio::new::<ratio>(value);
Self::from_quantity(quantity)
}
pub fn from_quantity(quantity: Ratio) -> ConstraintResult<Self> {
Ok(Self(UnitInterval::new(quantity)?))
}
}
impl Deref for Effectiveness {
type Target = Ratio;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Ntu(Constrained<Ratio, NonNegative>);
impl Ntu {
pub fn new(value: f64) -> ConstraintResult<Self> {
let quantity = Ratio::new::<ratio>(value);
Self::from_quantity(quantity)
}
pub fn from_quantity(quantity: Ratio) -> ConstraintResult<Self> {
Ok(Self(NonNegative::new(quantity)?))
}
pub fn from_conductance_and_capacitance_rates(
ua: ThermalConductance,
capacitance_rates: [CapacitanceRate; 2],
) -> ConstraintResult<Self> {
Self::from_quantity(ua / capacitance_rates[0].min(*capacitance_rates[1]))
}
}
impl Deref for Ntu {
type Target = Ratio;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
#[inline]
pub(crate) fn effectiveness_via(
ntu: Ntu,
capacitance_rates: [CapacitanceRate; 2],
fn_raw: impl Fn(f64, f64) -> f64,
) -> Effectiveness {
let cr = CapacityRatio::from_capacitance_rates(capacitance_rates).get::<ratio>();
let ntu = ntu.get::<ratio>();
if cr == 0.0 {
return {
Effectiveness::new(1. - (-ntu).exp())
.expect("ntu should always yield valid effectiveness")
};
}
Effectiveness::new(fn_raw(ntu, cr)).expect("ntu should always yield valid effectiveness")
}
#[inline]
pub(crate) fn ntu_via(
effectiveness: Effectiveness,
capacitance_rates: [CapacitanceRate; 2],
fn_raw: impl Fn(f64, f64) -> f64,
) -> Ntu {
let cr = CapacityRatio::from_capacitance_rates(capacitance_rates).get::<ratio>();
let eff = effectiveness.get::<ratio>();
if cr == 0.0 {
return {
Ntu::new(-(1. - eff).ln()).expect("effectiveness should always yield valid ntu")
};
}
Ntu::new(fn_raw(eff, cr)).expect("effectiveness should always yield valid ntu")
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use uom::si::thermal_conductance::watt_per_kelvin;
use super::*;
#[test]
fn ntu_from_conductance_and_capacitance_rates() -> ConstraintResult<()> {
let ua = ThermalConductance::new::<watt_per_kelvin>(10.);
let capacitance_rates = [
CapacitanceRate::new::<watt_per_kelvin>(10.)?,
CapacitanceRate::new::<watt_per_kelvin>(20.)?,
];
let ntu = Ntu::from_conductance_and_capacitance_rates(ua, capacitance_rates)?;
assert_relative_eq!(ntu.get::<ratio>(), 1.);
Ok(())
}
}