twine_thermo/stream.rs
1use twine_core::constraint::{Constrained, ConstraintError, StrictlyPositive};
2use uom::si::f64::{MassRate, Power};
3
4use crate::{
5 PropertyError, State,
6 capability::{HasEnthalpy, ThermoModel},
7};
8
9/// A stream of fluid at a thermodynamic state.
10///
11/// A `Stream` represents steady-state transport of mass and energy without
12/// storing either. For transient systems with mass or energy storage, use a
13/// [`ControlVolume`](crate::ControlVolume).
14///
15/// Zero-flow streams are not physically meaningful.
16/// Use `Option<Stream<Fluid>>` to represent an optional or inactive stream.
17#[derive(Debug, Clone, Copy, PartialEq)]
18pub struct Stream<Fluid> {
19 pub rate: Constrained<MassRate, StrictlyPositive>,
20 pub state: State<Fluid>,
21}
22
23impl<Fluid> Stream<Fluid> {
24 /// Creates a new [`Stream`] from a mass rate and thermodynamic state.
25 ///
26 /// # Errors
27 ///
28 /// Returns a [`ConstraintError`] if `rate` is not strictly positive.
29 pub fn new(rate: MassRate, state: State<Fluid>) -> Result<Self, ConstraintError> {
30 let rate = Constrained::new(rate)?;
31 Ok(Self::from_constrained(rate, state))
32 }
33
34 /// Creates a new [`Stream`] from a pre-validated positive mass rate and state.
35 pub fn from_constrained(
36 rate: Constrained<MassRate, StrictlyPositive>,
37 state: State<Fluid>,
38 ) -> Self {
39 Self { rate, state }
40 }
41
42 /// Returns the enthalpy flow rate of this stream.
43 ///
44 /// This method computes the energy flow carried by the stream as `ṁ · h`,
45 /// where `ṁ` is the mass flow rate and `h` is the enthalpy of its state.
46 ///
47 /// # Errors
48 ///
49 /// Returns a [`PropertyError`] if enthalpy cannot be computed.
50 pub fn enthalpy_flow<Model>(&self, model: &Model) -> Result<Power, PropertyError>
51 where
52 Model: ThermoModel<Fluid = Fluid> + HasEnthalpy,
53 {
54 let m_dot = self.rate.into_inner();
55 let h = model.enthalpy(&self.state)?;
56 Ok(m_dot * h)
57 }
58}