feos_core/equation_of_state/
residual.rs1use super::Components;
2use crate::{EosError, EosResult, ReferenceSystem, StateHD};
3use ndarray::prelude::*;
4use ndarray::ScalarOperand;
5use num_dual::*;
6use num_traits::{One, Zero};
7use quantity::*;
8use std::ops::Div;
9use typenum::Quot;
10
11pub trait Molarweight {
15 fn molar_weight(&self) -> MolarWeight<Array1<f64>>;
16}
17
18pub trait Residual: Components + Send + Sync {
20 fn compute_max_density(&self, moles: &Array1<f64>) -> f64;
27
28 fn residual_helmholtz_energy_contributions<D: DualNum<f64> + Copy + ScalarOperand>(
31 &self,
32 state: &StateHD<D>,
33 ) -> Vec<(String, D)>;
34
35 fn residual_helmholtz_energy<D: DualNum<f64> + Copy + ScalarOperand>(
37 &self,
38 state: &StateHD<D>,
39 ) -> D {
40 self.residual_helmholtz_energy_contributions(state)
41 .iter()
42 .fold(D::zero(), |acc, (_, a)| acc + a)
43 }
44
45 fn validate_moles(&self, moles: Option<&Moles<Array1<f64>>>) -> EosResult<Moles<Array1<f64>>> {
53 let l = moles.map_or(1, |m| m.len());
54 if self.components() == l {
55 match moles {
56 Some(m) => Ok(m.to_owned()),
57 None => Ok(Moles::from_reduced(Array::ones(1))),
58 }
59 } else {
60 Err(EosError::IncompatibleComponents(self.components(), l))
61 }
62 }
63
64 fn max_density(&self, moles: Option<&Moles<Array1<f64>>>) -> EosResult<Density> {
71 let mr = self.validate_moles(moles)?.to_reduced();
72 Ok(Density::from_reduced(self.compute_max_density(&mr)))
73 }
74
75 fn second_virial_coefficient(
77 &self,
78 temperature: Temperature,
79 moles: Option<&Moles<Array1<f64>>>,
80 ) -> EosResult<Quot<f64, Density>> {
81 let mr = self.validate_moles(moles)?;
82 let x = (&mr / mr.sum()).into_value();
83 let mut rho = HyperDual64::zero();
84 rho.eps1 = 1.0;
85 rho.eps2 = 1.0;
86 let t = HyperDual64::from(temperature.to_reduced());
87 let s = StateHD::new_virial(t, rho, x);
88 Ok(Quantity::from_reduced(
89 self.residual_helmholtz_energy(&s).eps1eps2 * 0.5,
90 ))
91 }
92
93 #[allow(clippy::type_complexity)]
95 fn third_virial_coefficient(
96 &self,
97 temperature: Temperature,
98 moles: Option<&Moles<Array1<f64>>>,
99 ) -> EosResult<<<f64 as Div<Density>>::Output as Div<Density>>::Output> {
100 let mr = self.validate_moles(moles)?;
101 let x = (&mr / mr.sum()).into_value();
102 let rho = Dual3_64::zero().derivative();
103 let t = Dual3_64::from(temperature.to_reduced());
104 let s = StateHD::new_virial(t, rho, x);
105 Ok(Quantity::from_reduced(
106 self.residual_helmholtz_energy(&s).v3 / 3.0,
107 ))
108 }
109
110 #[allow(clippy::type_complexity)]
112 fn second_virial_coefficient_temperature_derivative(
113 &self,
114 temperature: Temperature,
115 moles: Option<&Moles<Array1<f64>>>,
116 ) -> EosResult<<<f64 as Div<Density>>::Output as Div<Temperature>>::Output> {
117 let mr = self.validate_moles(moles)?;
118 let x = (&mr / mr.sum()).into_value();
119 let mut rho = HyperDual::zero();
120 rho.eps1 = Dual64::one();
121 rho.eps2 = Dual64::one();
122 let t = HyperDual::from_re(Dual64::from(temperature.to_reduced()).derivative());
123 let s = StateHD::new_virial(t, rho, x);
124 Ok(Quantity::from_reduced(
125 self.residual_helmholtz_energy(&s).eps1eps2.eps * 0.5,
126 ))
127 }
128
129 #[allow(clippy::type_complexity)]
131 fn third_virial_coefficient_temperature_derivative(
132 &self,
133 temperature: Temperature,
134 moles: Option<&Moles<Array1<f64>>>,
135 ) -> EosResult<
136 <<<f64 as Div<Density>>::Output as Div<Density>>::Output as Div<Temperature>>::Output,
137 > {
138 let mr = self.validate_moles(moles)?;
139 let x = (&mr / mr.sum()).into_value();
140 let rho = Dual3::zero().derivative();
141 let t = Dual3::from_re(Dual64::from(temperature.to_reduced()).derivative());
142 let s = StateHD::new_virial(t, rho, x);
143 Ok(Quantity::from_reduced(
144 self.residual_helmholtz_energy(&s).v3.eps / 3.0,
145 ))
146 }
147}
148
149pub trait EntropyScaling {
151 fn viscosity_reference(
152 &self,
153 temperature: Temperature,
154 volume: Volume,
155 moles: &Moles<Array1<f64>>,
156 ) -> EosResult<Viscosity>;
157 fn viscosity_correlation(&self, s_res: f64, x: &Array1<f64>) -> EosResult<f64>;
158 fn diffusion_reference(
159 &self,
160 temperature: Temperature,
161 volume: Volume,
162 moles: &Moles<Array1<f64>>,
163 ) -> EosResult<Diffusivity>;
164 fn diffusion_correlation(&self, s_res: f64, x: &Array1<f64>) -> EosResult<f64>;
165 fn thermal_conductivity_reference(
166 &self,
167 temperature: Temperature,
168 volume: Volume,
169 moles: &Moles<Array1<f64>>,
170 ) -> EosResult<ThermalConductivity>;
171 fn thermal_conductivity_correlation(&self, s_res: f64, x: &Array1<f64>) -> EosResult<f64>;
172}
173
174pub struct NoResidual(pub usize);
176
177impl Components for NoResidual {
178 fn components(&self) -> usize {
179 self.0
180 }
181
182 fn subset(&self, component_list: &[usize]) -> Self {
183 Self(component_list.len())
184 }
185}
186
187impl Residual for NoResidual {
188 fn compute_max_density(&self, _: &Array1<f64>) -> f64 {
189 1.0
190 }
191
192 fn residual_helmholtz_energy_contributions<D: DualNum<f64> + Copy>(
193 &self,
194 _: &StateHD<D>,
195 ) -> Vec<(String, D)> {
196 vec![]
197 }
198}