feos_core/phase_equilibria/
phase_diagram_pure.rs1use super::PhaseEquilibrium;
2#[cfg(feature = "rayon")]
3use crate::ReferenceSystem;
4use crate::SolverOptions;
5use crate::equation_of_state::Residual;
6use crate::errors::FeosResult;
7use crate::state::{State, StateVec};
8#[cfg(feature = "rayon")]
9use ndarray::{Array1, ArrayView1, Axis};
10use quantity::Temperature;
11#[cfg(feature = "rayon")]
12use rayon::{ThreadPool, prelude::*};
13
14#[derive(Clone)]
16pub struct PhaseDiagram<E, const N: usize> {
17 pub states: Vec<PhaseEquilibrium<E, N>>,
18}
19
20impl<E, const N: usize> PhaseDiagram<E, N> {
21 pub fn new(states: Vec<PhaseEquilibrium<E, N>>) -> Self {
23 Self { states }
24 }
25}
26
27impl<E: Residual> PhaseDiagram<E, 2> {
28 pub fn pure(
30 eos: &E,
31 min_temperature: Temperature,
32 npoints: usize,
33 critical_temperature: Option<Temperature>,
34 options: SolverOptions,
35 ) -> FeosResult<Self> {
36 let mut states = Vec::with_capacity(npoints);
37
38 let sc = State::critical_point(
39 eos,
40 None,
41 critical_temperature,
42 None,
43 SolverOptions::default(),
44 )?;
45
46 let max_temperature = min_temperature
47 + (sc.temperature - min_temperature) * ((npoints - 2) as f64 / (npoints - 1) as f64);
48 let temperatures = Temperature::linspace(min_temperature, max_temperature, npoints - 1);
49
50 let mut vle = None;
51 for ti in &temperatures {
52 vle = PhaseEquilibrium::pure(eos, ti, vle.as_ref(), options).ok();
53 if let Some(vle) = vle.as_ref() {
54 states.push(vle.clone());
55 }
56 }
57 states.push(PhaseEquilibrium::from_states(sc.clone(), sc));
58
59 Ok(PhaseDiagram::new(states))
60 }
61
62 pub fn vapor(&self) -> StateVec<'_, E> {
64 self.states.iter().map(|s| s.vapor()).collect()
65 }
66
67 pub fn liquid(&self) -> StateVec<'_, E> {
69 self.states.iter().map(|s| s.liquid()).collect()
70 }
71}
72
73#[cfg(feature = "rayon")]
74impl<E: Residual> PhaseDiagram<E, 2> {
75 fn solve_temperatures(
76 eos: &E,
77 temperatures: ArrayView1<f64>,
78 options: SolverOptions,
79 ) -> FeosResult<Vec<PhaseEquilibrium<E, 2>>> {
80 let mut states = Vec::with_capacity(temperatures.len());
81 let mut vle = None;
82 for ti in temperatures {
83 vle =
84 PhaseEquilibrium::pure(eos, Temperature::from_reduced(*ti), vle.as_ref(), options)
85 .ok();
86 if let Some(vle) = vle.as_ref() {
87 states.push(vle.clone());
88 }
89 }
90 Ok(states)
91 }
92
93 pub fn par_pure(
94 eos: &E,
95 min_temperature: Temperature,
96 npoints: usize,
97 chunksize: usize,
98 thread_pool: ThreadPool,
99 critical_temperature: Option<Temperature>,
100 options: SolverOptions,
101 ) -> FeosResult<Self>
102 where
103 E: Send + Sync,
104 {
105 let sc = State::critical_point(
106 eos,
107 None,
108 critical_temperature,
109 None,
110 SolverOptions::default(),
111 )?;
112
113 let max_temperature = min_temperature
114 + (sc.temperature - min_temperature) * ((npoints - 2) as f64 / (npoints - 1) as f64);
115 let temperatures = Array1::linspace(
116 min_temperature.to_reduced(),
117 max_temperature.to_reduced(),
118 npoints - 1,
119 );
120
121 let mut states: Vec<PhaseEquilibrium<E, 2>> = thread_pool.install(|| {
122 temperatures
123 .axis_chunks_iter(Axis(0), chunksize)
124 .into_par_iter()
125 .filter_map(|t| Self::solve_temperatures(eos, t, options).ok())
126 .flatten()
127 .collect()
128 });
129
130 states.push(PhaseEquilibrium::from_states(sc.clone(), sc));
131 Ok(PhaseDiagram::new(states))
132 }
133}