feos_core/state/
builder.rs

1use super::{DensityInitialization, State};
2use crate::equation_of_state::{IdealGas, Residual};
3use crate::errors::EosResult;
4use ndarray::Array1;
5use quantity::*;
6use std::sync::Arc;
7
8/// A simple tool to construct [State]s with arbitrary input parameters.
9///
10/// # Examples
11/// ```
12/// # use feos_core::{EosResult, StateBuilder};
13/// # use feos_core::cubic::{PengRobinson, PengRobinsonParameters};
14/// # use quantity::*;
15/// # use std::sync::Arc;
16/// # use ndarray::arr1;
17/// # use approx::assert_relative_eq;
18/// # use typenum::P3;
19/// # fn main() -> EosResult<()> {
20/// // Create a state for given T,V,N
21/// let eos = Arc::new(PengRobinson::new(Arc::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?)));
22/// let state = StateBuilder::new(&eos)
23///                 .temperature(300.0 * KELVIN)
24///                 .volume(12.5 * METER.powi::<P3>())
25///                 .moles(&(arr1(&[2.5]) * MOL))
26///                 .build()?;
27/// assert_eq!(state.density, 0.2 * MOL / METER.powi::<P3>());
28///
29/// // For a pure component, the composition does not need to be specified.
30/// let eos = Arc::new(PengRobinson::new(Arc::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?)));
31/// let state = StateBuilder::new(&eos)
32///                 .temperature(300.0 * KELVIN)
33///                 .volume(12.5 * METER.powi::<P3>())
34///                 .total_moles(2.5 * MOL)
35///                 .build()?;
36/// assert_eq!(state.density, 0.2 * MOL / METER.powi::<P3>());
37///
38/// // The state can be constructed without providing any extensive property.
39/// let eos = Arc::new(PengRobinson::new(
40///     Arc::new(PengRobinsonParameters::new_simple(
41///         &[369.8, 305.4],
42///         &[41.9 * 1e5, 48.2 * 1e5],
43///         &[0.15, 0.10],
44///         &[15.0, 30.0]
45///     )?)
46/// ));
47/// let state = StateBuilder::new(&eos)
48///                 .temperature(300.0 * KELVIN)
49///                 .partial_density(&(arr1(&[0.2, 0.6]) * MOL / METER.powi::<P3>()))
50///                 .build()?;
51/// assert_relative_eq!(state.molefracs, arr1(&[0.25, 0.75]));
52/// assert_relative_eq!(state.density, 0.8 * MOL / METER.powi::<P3>());
53/// # Ok(())
54/// # }
55/// ```
56pub struct StateBuilder<'a, E, const IG: bool> {
57    eos: Arc<E>,
58    temperature: Option<Temperature>,
59    volume: Option<Volume>,
60    density: Option<Density>,
61    partial_density: Option<&'a Density<Array1<f64>>>,
62    total_moles: Option<Moles>,
63    moles: Option<&'a Moles<Array1<f64>>>,
64    molefracs: Option<&'a Array1<f64>>,
65    pressure: Option<Pressure>,
66    molar_enthalpy: Option<MolarEnergy>,
67    molar_entropy: Option<MolarEntropy>,
68    molar_internal_energy: Option<MolarEnergy>,
69    density_initialization: DensityInitialization,
70    initial_temperature: Option<Temperature>,
71}
72
73impl<E: Residual> StateBuilder<'_, E, false> {
74    /// Create a new `StateBuilder` for the given equation of state.
75    pub fn new(eos: &Arc<E>) -> Self {
76        StateBuilder {
77            eos: eos.clone(),
78            temperature: None,
79            volume: None,
80            density: None,
81            partial_density: None,
82            total_moles: None,
83            moles: None,
84            molefracs: None,
85            pressure: None,
86            molar_enthalpy: None,
87            molar_entropy: None,
88            molar_internal_energy: None,
89            density_initialization: DensityInitialization::None,
90            initial_temperature: None,
91        }
92    }
93}
94
95impl<'a, E: Residual, const IG: bool> StateBuilder<'a, E, IG> {
96    /// Provide the temperature for the new state.
97    pub fn temperature(mut self, temperature: Temperature) -> Self {
98        self.temperature = Some(temperature);
99        self
100    }
101
102    /// Provide the volume for the new state.
103    pub fn volume(mut self, volume: Volume) -> Self {
104        self.volume = Some(volume);
105        self
106    }
107
108    /// Provide the density for the new state.
109    pub fn density(mut self, density: Density) -> Self {
110        self.density = Some(density);
111        self
112    }
113
114    /// Provide partial densities for the new state.
115    pub fn partial_density(mut self, partial_density: &'a Density<Array1<f64>>) -> Self {
116        self.partial_density = Some(partial_density);
117        self
118    }
119
120    /// Provide the total moles for the new state.
121    pub fn total_moles(mut self, total_moles: Moles) -> Self {
122        self.total_moles = Some(total_moles);
123        self
124    }
125
126    /// Provide the moles for the new state.
127    pub fn moles(mut self, moles: &'a Moles<Array1<f64>>) -> Self {
128        self.moles = Some(moles);
129        self
130    }
131
132    /// Provide the molefracs for the new state.
133    pub fn molefracs(mut self, molefracs: &'a Array1<f64>) -> Self {
134        self.molefracs = Some(molefracs);
135        self
136    }
137
138    /// Provide the pressure for the new state.
139    pub fn pressure(mut self, pressure: Pressure) -> Self {
140        self.pressure = Some(pressure);
141        self
142    }
143
144    /// Specify a vapor state.
145    pub fn vapor(mut self) -> Self {
146        self.density_initialization = DensityInitialization::Vapor;
147        self
148    }
149
150    /// Specify a liquid state.
151    pub fn liquid(mut self) -> Self {
152        self.density_initialization = DensityInitialization::Liquid;
153        self
154    }
155
156    /// Provide an initial density used in density iterations.
157    pub fn initial_density(mut self, initial_density: Density) -> Self {
158        self.density_initialization = DensityInitialization::InitialDensity(initial_density);
159        self
160    }
161}
162
163impl<'a, E: Residual + IdealGas, const IG: bool> StateBuilder<'a, E, IG> {
164    /// Provide the molar enthalpy for the new state.
165    pub fn molar_enthalpy(mut self, molar_enthalpy: MolarEnergy) -> StateBuilder<'a, E, true> {
166        self.molar_enthalpy = Some(molar_enthalpy);
167        self.convert()
168    }
169
170    /// Provide the molar entropy for the new state.
171    pub fn molar_entropy(mut self, molar_entropy: MolarEntropy) -> StateBuilder<'a, E, true> {
172        self.molar_entropy = Some(molar_entropy);
173        self.convert()
174    }
175
176    /// Provide the molar internal energy for the new state.
177    pub fn molar_internal_energy(
178        mut self,
179        molar_internal_energy: MolarEnergy,
180    ) -> StateBuilder<'a, E, true> {
181        self.molar_internal_energy = Some(molar_internal_energy);
182        self.convert()
183    }
184
185    /// Provide an initial temperature used in the Newton solver.
186    pub fn initial_temperature(
187        mut self,
188        initial_temperature: Temperature,
189    ) -> StateBuilder<'a, E, true> {
190        self.initial_temperature = Some(initial_temperature);
191        self.convert()
192    }
193
194    fn convert(self) -> StateBuilder<'a, E, true> {
195        StateBuilder {
196            eos: self.eos,
197            temperature: self.temperature,
198            volume: self.volume,
199            density: self.density,
200            partial_density: self.partial_density,
201            total_moles: self.total_moles,
202            moles: self.moles,
203            molefracs: self.molefracs,
204            pressure: self.pressure,
205            molar_enthalpy: self.molar_enthalpy,
206            molar_entropy: self.molar_entropy,
207            molar_internal_energy: self.molar_internal_energy,
208            density_initialization: self.density_initialization,
209            initial_temperature: self.initial_temperature,
210        }
211    }
212}
213
214impl<E: Residual> StateBuilder<'_, E, false> {
215    /// Try to build the state with the given inputs.
216    pub fn build(self) -> EosResult<State<E>> {
217        State::new(
218            &self.eos,
219            self.temperature,
220            self.volume,
221            self.density,
222            self.partial_density,
223            self.total_moles,
224            self.moles,
225            self.molefracs,
226            self.pressure,
227            self.density_initialization,
228        )
229    }
230}
231
232impl<E: Residual + IdealGas> StateBuilder<'_, E, true> {
233    /// Try to build the state with the given inputs.
234    pub fn build(self) -> EosResult<State<E>> {
235        State::new_full(
236            &self.eos,
237            self.temperature,
238            self.volume,
239            self.density,
240            self.partial_density,
241            self.total_moles,
242            self.moles,
243            self.molefracs,
244            self.pressure,
245            self.molar_enthalpy,
246            self.molar_entropy,
247            self.molar_internal_energy,
248            self.density_initialization,
249            self.initial_temperature,
250        )
251    }
252}
253
254impl<E, const IG: bool> Clone for StateBuilder<'_, E, IG> {
255    fn clone(&self) -> Self {
256        Self {
257            eos: self.eos.clone(),
258            temperature: self.temperature,
259            volume: self.volume,
260            density: self.density,
261            partial_density: self.partial_density,
262            total_moles: self.total_moles,
263            moles: self.moles,
264            molefracs: self.molefracs,
265            pressure: self.pressure,
266            molar_enthalpy: self.molar_enthalpy,
267            molar_entropy: self.molar_entropy,
268            molar_internal_energy: self.molar_internal_energy,
269            density_initialization: self.density_initialization,
270            initial_temperature: self.initial_temperature,
271        }
272    }
273}