diff --git a/feos-core/src/cubic.rs b/feos-core/src/cubic.rs index ccd6fff7d..9ad01b4fb 100644 --- a/feos-core/src/cubic.rs +++ b/feos-core/src/cubic.rs @@ -26,15 +26,18 @@ pub struct PengRobinsonRecord { pc: f64, /// acentric factor acentric_factor: f64, + /// molar weight + molarweight: f64, } impl PengRobinsonRecord { /// Create a new pure substance record for the Peng-Robinson equation of state. - pub fn new(tc: f64, pc: f64, acentric_factor: f64) -> Self { + pub fn new(tc: f64, pc: f64, acentric_factor: f64, molarweight: f64) -> Self { Self { tc, pc, acentric_factor, + molarweight, } } } @@ -43,7 +46,8 @@ impl std::fmt::Display for PengRobinsonRecord { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "PengRobinsonRecord(tc={} K", self.tc)?; write!(f, ", pc={} Pa", self.pc)?; - write!(f, ", acentric factor={}", self.acentric_factor) + write!(f, ", acentric factor={}", self.acentric_factor)?; + write!(f, ", molar weight={})", self.molarweight) } } @@ -93,9 +97,10 @@ impl PengRobinsonParameters { tc: tc[i], pc: pc[i], acentric_factor: acentric_factor[i], + molarweight: molarweight[i], }; let id = Identifier::default(); - PureRecord::new(id, molarweight[i], record) + PureRecord::new(id, record) }) .collect(); PengRobinsonParameters::from_records(records, None) @@ -120,8 +125,8 @@ impl Parameter for PengRobinsonParameters { let mut kappa = Array1::zeros(n); for (i, record) in pure_records.iter().enumerate() { - molarweight[i] = record.molarweight; let r = &record.model_record; + molarweight[i] = r.molarweight; tc[i] = r.tc; a[i] = 0.45724 * r.tc.powi(2) * KB_A3 / r.pc; b[i] = 0.07780 * r.tc * KB_A3 / r.pc; diff --git a/feos-core/src/equation_of_state/mod.rs b/feos-core/src/equation_of_state/mod.rs index a1ac42a64..a19331b7d 100644 --- a/feos-core/src/equation_of_state/mod.rs +++ b/feos-core/src/equation_of_state/mod.rs @@ -27,14 +27,14 @@ pub trait Components { /// and a residual Helmholtz energy model. #[derive(Clone)] pub struct EquationOfState { - pub ideal_gas: Arc, + pub ideal_gas: I, pub residual: Arc, } impl EquationOfState { /// Return a new [EquationOfState] with the given ideal gas /// and residual models. - pub fn new(ideal_gas: Arc, residual: Arc) -> Self { + pub fn new(ideal_gas: I, residual: Arc) -> Self { Self { ideal_gas, residual, @@ -45,7 +45,7 @@ impl EquationOfState { impl EquationOfState { /// Return a new [EquationOfState] that only consists of /// an ideal gas models. - pub fn ideal_gas(ideal_gas: Arc) -> Self { + pub fn ideal_gas(ideal_gas: I) -> Self { let residual = Arc::new(NoResidual(ideal_gas.components())); Self { ideal_gas, @@ -66,7 +66,7 @@ impl Components for EquationOfState { fn subset(&self, component_list: &[usize]) -> Self { Self::new( - Arc::new(self.ideal_gas.subset(component_list)), + self.ideal_gas.subset(component_list), Arc::new(self.residual.subset(component_list)), ) } diff --git a/feos-core/src/joback.rs b/feos-core/src/joback.rs index e7d734a9d..ddb91de5e 100644 --- a/feos-core/src/joback.rs +++ b/feos-core/src/joback.rs @@ -333,8 +333,8 @@ mod tests { ); assert_relative_eq!(jr.e, 0.0); - let pr = PureRecord::new(Identifier::default(), 1.0, jr); - let joback = Arc::new(Joback::new(Arc::new(JobackParameters::new_pure(pr)?))); + let pr = PureRecord::new(Identifier::default(), jr); + let joback = Joback::new(Arc::new(JobackParameters::new_pure(pr)?)); let eos = Arc::new(EquationOfState::ideal_gas(joback)); let state = State::new_nvt( &eos, @@ -357,17 +357,16 @@ mod tests { fn c_p_comparison() -> EosResult<()> { let record1 = PureRecord::new( Identifier::default(), - 1.0, JobackRecord::new(1.0, 0.2, 0.03, 0.004, 0.005), ); let record2 = PureRecord::new( Identifier::default(), - 1.0, JobackRecord::new(-5.0, 0.4, 0.03, 0.002, 0.001), ); let parameters = Arc::new(JobackParameters::new_binary(vec![record1, record2], None)?); - let joback = Arc::new(Joback::new(parameters)); - let eos = Arc::new(EquationOfState::ideal_gas(joback.clone())); + let joback = Joback::new(parameters.clone()); + let ideal_gas = Joback::new(parameters); + let eos = Arc::new(EquationOfState::ideal_gas(ideal_gas)); let temperature = 300.0 * KELVIN; let volume = METER.powi::(); let moles = &arr1(&[1.0, 3.0]) * MOL; diff --git a/feos-core/src/lib.rs b/feos-core/src/lib.rs index 2a8158e19..c2745bd05 100644 --- a/feos-core/src/lib.rs +++ b/feos-core/src/lib.rs @@ -173,14 +173,13 @@ mod tests { fn validate_residual_properties() -> EosResult<()> { let mixture = pure_record_vec(); let propane = mixture[0].clone(); - let parameters = PengRobinsonParameters::new_pure(propane)?; - let residual = Arc::new(PengRobinson::new(Arc::new(parameters))); + let parameters = Arc::new(PengRobinsonParameters::new_pure(propane)?); + let residual = Arc::new(PengRobinson::new(parameters)); let joback_parameters = Arc::new(JobackParameters::new_pure(PureRecord::new( Identifier::default(), - 1.0, JobackRecord::new(0.0, 0.0, 0.0, 0.0, 0.0), ))?); - let ideal_gas = Arc::new(Joback::new(joback_parameters)); + let ideal_gas = Joback::new(joback_parameters); let eos = Arc::new(EquationOfState::new(ideal_gas, residual.clone())); let sr = StateBuilder::new(&residual) diff --git a/feos-core/src/parameter/mod.rs b/feos-core/src/parameter/mod.rs index 457ef1fb6..8f4fdae9a 100644 --- a/feos-core/src/parameter/mod.rs +++ b/feos-core/src/parameter/mod.rs @@ -66,7 +66,7 @@ where fn from_model_records(model_records: Vec) -> Result { let pure_records = model_records .into_iter() - .map(|r| PureRecord::new(Default::default(), Default::default(), r)) + .map(|r| PureRecord::new(Default::default(), r)) .collect(); Self::from_records(pure_records, None) } diff --git a/feos-core/src/parameter/model_record.rs b/feos-core/src/parameter/model_record.rs index fd4669d6a..aa3c1e2a3 100644 --- a/feos-core/src/parameter/model_record.rs +++ b/feos-core/src/parameter/model_record.rs @@ -1,7 +1,6 @@ use super::identifier::Identifier; use super::segment::SegmentRecord; use super::{IdentifierOption, ParameterError}; -use conv::ValueInto; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -13,16 +12,14 @@ use std::path::Path; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PureRecord { pub identifier: Identifier, - pub molarweight: f64, pub model_record: M, } impl PureRecord { /// Create a new `PureRecord`. - pub fn new(identifier: Identifier, molarweight: f64, model_record: M) -> Self { + pub fn new(identifier: Identifier, model_record: M) -> Self { Self { identifier, - molarweight, model_record, } } @@ -33,19 +30,16 @@ impl PureRecord { /// and the ideal gas record. pub fn from_segments(identifier: Identifier, segments: S) -> Result where - T: Copy + ValueInto, M: FromSegments, S: IntoIterator, T)>, { - let mut molarweight = 0.0; - let mut model_segments = Vec::new(); - for (s, n) in segments { - molarweight += s.molarweight * n.value_into().unwrap(); - model_segments.push((s.model_record, n)); - } + let model_segments: Vec<_> = segments + .into_iter() + .map(|(s, n)| (s.model_record, n)) + .collect(); let model_record = M::from_segments(&model_segments)?; - Ok(Self::new(identifier, molarweight, model_record)) + Ok(Self::new(identifier, model_record)) } /// Create pure substance parameters from a json file. @@ -104,7 +98,6 @@ where fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "PureRecord(")?; write!(f, "\n\tidentifier={},", self.identifier)?; - write!(f, "\n\tmolarweight={},", self.molarweight)?; write!(f, "\n\tmodel_record={},", self.model_record)?; write!(f, "\n)") } diff --git a/feos-core/src/python/cubic.rs b/feos-core/src/python/cubic.rs index 132d9d630..4bb285c92 100644 --- a/feos-core/src/python/cubic.rs +++ b/feos-core/src/python/cubic.rs @@ -18,8 +18,13 @@ pub struct PyPengRobinsonRecord(PengRobinsonRecord); #[pymethods] impl PyPengRobinsonRecord { #[new] - fn new(tc: f64, pc: f64, acentric_factor: f64) -> Self { - Self(PengRobinsonRecord::new(tc, pc, acentric_factor)) + fn new(tc: f64, pc: f64, acentric_factor: f64, molarweight: f64) -> Self { + Self(PengRobinsonRecord::new( + tc, + pc, + acentric_factor, + molarweight, + )) } fn __repr__(&self) -> PyResult { diff --git a/feos-core/src/python/parameter/mod.rs b/feos-core/src/python/parameter/mod.rs index c6f04b9ec..aa1f1d51c 100644 --- a/feos-core/src/python/parameter/mod.rs +++ b/feos-core/src/python/parameter/mod.rs @@ -399,16 +399,8 @@ macro_rules! impl_pure_record { impl PyPureRecord { #[new] #[pyo3(text_signature = "(identifier, molarweight, model_record)")] - fn new( - identifier: PyIdentifier, - molarweight: f64, - model_record: $py_model_record, - ) -> PyResult { - Ok(Self(PureRecord::new( - identifier.0, - molarweight, - model_record.0, - ))) + fn new(identifier: PyIdentifier, model_record: $py_model_record) -> PyResult { + Ok(Self(PureRecord::new(identifier.0, model_record.0))) } #[getter] @@ -421,16 +413,6 @@ macro_rules! impl_pure_record { self.0.identifier = identifier.0; } - #[getter] - fn get_molarweight(&self) -> f64 { - self.0.molarweight - } - - #[setter] - fn set_molarweight(&mut self, molarweight: f64) { - self.0.molarweight = molarweight; - } - #[getter] fn get_model_record(&self) -> $py_model_record { $py_model_record(self.0.model_record.clone()) diff --git a/feos-dft/src/functional.rs b/feos-dft/src/functional.rs index 913406f10..6d02b5608 100644 --- a/feos-dft/src/functional.rs +++ b/feos-dft/src/functional.rs @@ -80,7 +80,7 @@ impl Deref for DFT { impl DFT { pub fn ideal_gas(self, ideal_gas: I) -> DFT> { - DFT(EquationOfState::new(Arc::new(ideal_gas), Arc::new(self.0))) + DFT(EquationOfState::new(ideal_gas, Arc::new(self.0))) } } diff --git a/src/pcsaft/parameters.rs b/src/pcsaft/parameters.rs index da72435b8..8dd4173ea 100644 --- a/src/pcsaft/parameters.rs +++ b/src/pcsaft/parameters.rs @@ -15,6 +15,8 @@ use std::fmt::Write; /// PC-SAFT pure-component parameters. #[derive(Serialize, Deserialize, Clone, Default)] pub struct PcSaftRecord { + /// molar weight + pub molarweight: f64, /// Segment number pub m: f64, /// Segment diameter in units of Angstrom @@ -44,11 +46,13 @@ pub struct PcSaftRecord { impl FromSegments for PcSaftRecord { fn from_segments(segments: &[(Self, f64)]) -> Result { + let mut molarweight = 0.0; let mut m = 0.0; let mut sigma3 = 0.0; let mut epsilon_k = 0.0; segments.iter().for_each(|(s, n)| { + molarweight += s.molarweight * n; m += s.m * n; sigma3 += s.m * s.sigma.powi(3) * n; epsilon_k += s.m * s.epsilon_k * n; @@ -143,6 +147,7 @@ impl FromSegments for PcSaftRecord { viscosity = viscosity.map(|v| [v[0] - 0.5 * m.ln(), v[1], v[2], v[3]]); Ok(Self { + molarweight, m, sigma: (sigma3 / m).cbrt(), epsilon_k: epsilon_k / m, @@ -225,6 +230,7 @@ impl std::fmt::Display for PcSaftRecord { impl PcSaftRecord { pub fn new( + molarweight: f64, m: f64, sigma: f64, epsilon_k: f64, @@ -256,6 +262,7 @@ impl PcSaftRecord { )) }; PcSaftRecord { + molarweight, m, sigma, epsilon_k, @@ -399,7 +406,7 @@ impl Parameter for PcSaftParameters { viscosity.push(r.viscosity); diffusion.push(r.diffusion); thermal_conductivity.push(r.thermal_conductivity); - molarweight[i] = record.molarweight; + molarweight[i] = r.molarweight; } let mu2 = &mu * &mu / (&m * &sigma * &sigma * &sigma * &epsilon_k) @@ -544,7 +551,7 @@ impl PcSaftParameters { o, "\n|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|", component, - record.molarweight, + record.model_record.molarweight, record.model_record.m, record.model_record.sigma, record.model_record.epsilon_k, diff --git a/src/pcsaft/python.rs b/src/pcsaft/python.rs index a65efaa71..3ba259dff 100644 --- a/src/pcsaft/python.rs +++ b/src/pcsaft/python.rs @@ -53,6 +53,7 @@ impl PyPcSaftRecord { text_signature = "(m, sigma, epsilon_k, mu=None, q=None, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, viscosity=None, diffusion=None, thermal_conductivity=None)" )] fn new( + molarweight: f64, m: f64, sigma: f64, epsilon_k: f64, @@ -68,6 +69,7 @@ impl PyPcSaftRecord { thermal_conductivity: Option<[f64; 4]>, ) -> Self { Self(PcSaftRecord::new( + molarweight, m, sigma, epsilon_k, @@ -84,6 +86,11 @@ impl PyPcSaftRecord { )) } + #[getter] + fn get_molarweight(&self) -> f64 { + self.0.molarweight + } + #[getter] fn get_m(&self) -> f64 { self.0.m diff --git a/src/python/eos.rs b/src/python/eos.rs index d4bae905f..d4d2924f4 100644 --- a/src/python/eos.rs +++ b/src/python/eos.rs @@ -95,7 +95,7 @@ impl PyEquationOfState { parameters.0, options, ))); - let ideal_gas = Arc::new(IdealGasModel::NoModel(residual.components())); + let ideal_gas = IdealGasModel::NoModel(residual.components()); Self(Arc::new(EquationOfState::new(ideal_gas, residual))) } @@ -157,7 +157,7 @@ impl PyEquationOfState { #[staticmethod] pub fn peng_robinson(parameters: PyPengRobinsonParameters) -> Self { let residual = Arc::new(ResidualModel::PengRobinson(PengRobinson::new(parameters.0))); - let ideal_gas = Arc::new(IdealGasModel::NoModel(residual.components())); + let ideal_gas = IdealGasModel::NoModel(residual.components()); Self(Arc::new(EquationOfState::new(ideal_gas, residual))) } @@ -175,7 +175,7 @@ impl PyEquationOfState { #[staticmethod] fn python_residual(residual: Py) -> PyResult { let residual = Arc::new(ResidualModel::Python(PyResidual::new(residual)?)); - let ideal_gas = Arc::new(IdealGasModel::NoModel(residual.components())); + let ideal_gas = IdealGasModel::NoModel(residual.components()); Ok(Self(Arc::new(EquationOfState::new(ideal_gas, residual)))) } @@ -293,7 +293,7 @@ impl PyEquationOfState { #[staticmethod] fn ideal_gas() -> Self { let residual = Arc::new(ResidualModel::NoResidual(NoResidual(0))); - let ideal_gas = Arc::new(IdealGasModel::NoModel(0)); + let ideal_gas = IdealGasModel::NoModel(0); Self(Arc::new(EquationOfState::new(ideal_gas, residual))) } @@ -335,10 +335,7 @@ impl PyEquationOfState { ))), _ => self.0.residual.clone(), }; - Self(Arc::new(EquationOfState::new( - Arc::new(ideal_gas), - residual, - ))) + Self(Arc::new(EquationOfState::new(ideal_gas, residual))) } } diff --git a/tests/pcsaft/state_creation_mixture.rs b/tests/pcsaft/state_creation_mixture.rs index 04e1c6dcd..b2368e895 100644 --- a/tests/pcsaft/state_creation_mixture.rs +++ b/tests/pcsaft/state_creation_mixture.rs @@ -32,7 +32,7 @@ fn pressure_entropy_molefracs() -> Result<(), Box> { let (saft_params, joback_params) = propane_butane_parameters()?; let saft = Arc::new(PcSaft::new(saft_params)); let joback = Joback::new(joback_params); - let eos = Arc::new(EquationOfState::new(Arc::new(joback), saft)); + let eos = Arc::new(EquationOfState::new(joback, saft)); let pressure = BAR; let temperature = 300.0 * KELVIN; let x = arr1(&[0.3, 0.7]); diff --git a/tests/pcsaft/state_creation_pure.rs b/tests/pcsaft/state_creation_pure.rs index 976cc22c1..fba38c48c 100644 --- a/tests/pcsaft/state_creation_pure.rs +++ b/tests/pcsaft/state_creation_pure.rs @@ -148,7 +148,7 @@ fn pressure_enthalpy_vapor() -> Result<(), Box> { let (saft_params, joback_params) = propane_parameters()?; let saft = Arc::new(PcSaft::new(saft_params)); let joback = Joback::new(joback_params); - let eos = Arc::new(EquationOfState::new(Arc::new(joback), saft)); + let eos = Arc::new(EquationOfState::new(joback, saft)); let pressure = 0.3 * BAR; let molar_enthalpy = 2000.0 * JOULE / MOL; let state = StateBuilder::new(&eos) @@ -190,7 +190,7 @@ fn density_internal_energy() -> Result<(), Box> { let (saft_params, joback_params) = propane_parameters()?; let saft = Arc::new(PcSaft::new(saft_params)); let joback = Joback::new(joback_params); - let eos = Arc::new(EquationOfState::new(Arc::new(joback), saft)); + let eos = Arc::new(EquationOfState::new(joback, saft)); let pressure = 5.0 * BAR; let temperature = 315.0 * KELVIN; let total_moles = 2.5 * MOL; @@ -220,7 +220,7 @@ fn pressure_enthalpy_total_moles_vapor() -> Result<(), Box> { let (saft_params, joback_params) = propane_parameters()?; let saft = Arc::new(PcSaft::new(saft_params)); let joback = Joback::new(joback_params); - let eos = Arc::new(EquationOfState::new(Arc::new(joback), saft)); + let eos = Arc::new(EquationOfState::new(joback, saft)); let pressure = 0.3 * BAR; let molar_enthalpy = 2000.0 * JOULE / MOL; let total_moles = 2.5 * MOL; @@ -264,7 +264,7 @@ fn pressure_entropy_vapor() -> Result<(), Box> { let (saft_params, joback_params) = propane_parameters()?; let saft = Arc::new(PcSaft::new(saft_params)); let joback = Joback::new(joback_params); - let eos = Arc::new(EquationOfState::new(Arc::new(joback), saft)); + let eos = Arc::new(EquationOfState::new(joback, saft)); let pressure = 0.3 * BAR; let molar_entropy = -2.0 * JOULE / MOL / KELVIN; let state = StateBuilder::new(&eos) @@ -306,7 +306,7 @@ fn temperature_entropy_vapor() -> Result<(), Box> { let (saft_params, joback_params) = propane_parameters()?; let saft = Arc::new(PcSaft::new(saft_params)); let joback = Joback::new(joback_params); - let eos = Arc::new(EquationOfState::new(Arc::new(joback), saft)); + let eos = Arc::new(EquationOfState::new(joback, saft)); let pressure = 3.0 * BAR; let temperature = 315.15 * KELVIN; let total_moles = 3.0 * MOL; @@ -366,7 +366,7 @@ fn test_consistency() -> Result<(), Box> { let (saft_params, joback_params) = propane_parameters()?; let saft = Arc::new(PcSaft::new(saft_params)); let joback = Joback::new(joback_params); - let eos = Arc::new(EquationOfState::new(Arc::new(joback), saft)); + let eos = Arc::new(EquationOfState::new(joback, saft)); let temperatures = [350.0 * KELVIN, 400.0 * KELVIN, 450.0 * KELVIN]; let pressures = [1.0 * BAR, 2.0 * BAR, 3.0 * BAR];