twine_models/support/hx/
effectiveness_ntu.rs1use std::ops::Deref;
2
3use crate::support::constraint::{Constrained, ConstraintResult, NonNegative, UnitInterval};
4use uom::si::{
5 f64::{Ratio, ThermalConductance},
6 ratio::ratio,
7};
8
9use super::{CapacitanceRate, CapacityRatio};
10
11pub trait EffectivenessRelation {
13 fn effectiveness(&self, ntu: Ntu, capacitance_rates: [CapacitanceRate; 2]) -> Effectiveness;
16}
17
18pub trait NtuRelation {
20 fn ntu(&self, effectiveness: Effectiveness, capacitance_rates: [CapacitanceRate; 2]) -> Ntu;
23}
24
25#[derive(Debug, Clone, Copy)]
32pub struct Effectiveness(Constrained<Ratio, UnitInterval>);
33
34impl Effectiveness {
35 pub fn new(value: f64) -> ConstraintResult<Self> {
41 let quantity = Ratio::new::<ratio>(value);
42 Self::from_quantity(quantity)
43 }
44
45 pub fn from_quantity(quantity: Ratio) -> ConstraintResult<Self> {
51 Ok(Self(UnitInterval::new(quantity)?))
52 }
53}
54
55impl Deref for Effectiveness {
56 type Target = Ratio;
57
58 fn deref(&self) -> &Self::Target {
59 self.0.as_ref()
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
70pub struct Ntu(Constrained<Ratio, NonNegative>);
71
72impl Ntu {
73 pub fn new(value: f64) -> ConstraintResult<Self> {
79 let quantity = Ratio::new::<ratio>(value);
80 Self::from_quantity(quantity)
81 }
82
83 pub fn from_quantity(quantity: Ratio) -> ConstraintResult<Self> {
89 Ok(Self(NonNegative::new(quantity)?))
90 }
91
92 pub fn from_conductance_and_capacitance_rates(
103 ua: ThermalConductance,
104 capacitance_rates: [CapacitanceRate; 2],
105 ) -> ConstraintResult<Self> {
106 Self::from_quantity(ua / capacitance_rates[0].min(*capacitance_rates[1]))
107 }
108}
109
110impl Deref for Ntu {
111 type Target = Ratio;
112
113 fn deref(&self) -> &Self::Target {
114 self.0.as_ref()
115 }
116}
117
118#[inline]
119pub(crate) fn effectiveness_via(
120 ntu: Ntu,
121 capacitance_rates: [CapacitanceRate; 2],
122 fn_raw: impl Fn(f64, f64) -> f64,
123) -> Effectiveness {
124 let cr = CapacityRatio::from_capacitance_rates(capacitance_rates).get::<ratio>();
125 let ntu = ntu.get::<ratio>();
126 if cr == 0.0 {
127 return {
128 Effectiveness::new(1. - (-ntu).exp())
129 .expect("ntu should always yield valid effectiveness")
130 };
131 }
132 Effectiveness::new(fn_raw(ntu, cr)).expect("ntu should always yield valid effectiveness")
133}
134
135#[inline]
136pub(crate) fn ntu_via(
137 effectiveness: Effectiveness,
138 capacitance_rates: [CapacitanceRate; 2],
139 fn_raw: impl Fn(f64, f64) -> f64,
140) -> Ntu {
141 let cr = CapacityRatio::from_capacitance_rates(capacitance_rates).get::<ratio>();
142 let eff = effectiveness.get::<ratio>();
143 if cr == 0.0 {
144 return {
145 Ntu::new(-(1. - eff).ln()).expect("effectiveness should always yield valid ntu")
146 };
147 }
148 Ntu::new(fn_raw(eff, cr)).expect("effectiveness should always yield valid ntu")
149}
150
151#[cfg(test)]
152mod tests {
153 use approx::assert_relative_eq;
154 use uom::si::thermal_conductance::watt_per_kelvin;
155
156 use super::*;
157
158 #[test]
159 fn ntu_from_conductance_and_capacitance_rates() -> ConstraintResult<()> {
160 let ua = ThermalConductance::new::<watt_per_kelvin>(10.);
161 let capacitance_rates = [
162 CapacitanceRate::new::<watt_per_kelvin>(10.)?,
163 CapacitanceRate::new::<watt_per_kelvin>(20.)?,
164 ];
165
166 let ntu = Ntu::from_conductance_and_capacitance_rates(ua, capacitance_rates)?;
167
168 assert_relative_eq!(ntu.get::<ratio>(), 1.);
169 Ok(())
170 }
171}