From 7f298b5c8fa04fe41df094f0aea603adf4164ea0 Mon Sep 17 00:00:00 2001 From: Philipp Rehner <69816385+prehner@users.noreply.github.com> Date: Wed, 7 Jan 2026 09:00:47 +0100 Subject: [PATCH 01/12] Update quantity to 0.13 and remove typenum dependency (#328) --- CHANGELOG.md | 4 ++ Cargo.toml | 3 +- crates/feos-core/Cargo.toml | 1 - .../src/equation_of_state/residual.rs | 5 +- crates/feos-core/src/lib.rs | 55 +++++++++++-------- .../src/phase_equilibria/bubble_dew.rs | 3 +- crates/feos-core/src/state/builder.rs | 13 ++--- crates/feos-core/src/state/cache.rs | 4 +- crates/feos-core/src/state/mod.rs | 13 ++--- crates/feos-dft/Cargo.toml | 1 - crates/feos-dft/src/adsorption/pore.rs | 4 +- crates/feos-dft/src/pdgt.rs | 6 +- crates/feos-dft/src/profile/mod.rs | 7 +-- crates/feos/Cargo.toml | 1 - crates/feos/benches/dft_pore.rs | 3 +- crates/feos/benches/dual_numbers.rs | 3 +- crates/feos/benches/state_properties.rs | 5 +- crates/feos/src/association/mod.rs | 9 ++- crates/feos/src/epcsaft/eos/mod.rs | 7 +-- crates/feos/src/gc_pcsaft/eos/dispersion.rs | 5 +- crates/feos/src/gc_pcsaft/eos/hard_chain.rs | 5 +- crates/feos/src/gc_pcsaft/eos/mod.rs | 5 +- crates/feos/src/ideal_gas/dippr.rs | 7 +-- crates/feos/src/ideal_gas/joback.rs | 5 +- crates/feos/src/multiparameter/mod.rs | 5 +- crates/feos/src/pcsaft/eos/mod.rs | 14 ++--- crates/feos/src/pets/eos/mod.rs | 5 +- crates/feos/src/uvtheory/eos/mod.rs | 13 ++--- crates/feos/tests/gc_pcsaft/binary.rs | 5 +- crates/feos/tests/gc_pcsaft/dft.rs | 9 ++- crates/feos/tests/pcsaft/critical_point.rs | 5 +- crates/feos/tests/pcsaft/dft.rs | 11 ++-- .../tests/pcsaft/state_creation_mixture.rs | 7 +-- .../feos/tests/pcsaft/state_creation_pure.rs | 11 ++-- .../tests/saftvrmie/critical_properties.rs | 3 +- py-feos/Cargo.toml | 1 - py-feos/src/dft/profile.rs | 16 +++--- py-feos/src/eos/mod.rs | 4 +- py-feos/src/phase_equilibria.rs | 13 ++--- py-feos/src/state.rs | 13 +++-- 40 files changed, 149 insertions(+), 160 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f63fbd358..6364ace25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Breaking] +### Packaging +- Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#323](https://github.com/feos-org/feos/pull/323) + ## [Unreleased] ## [0.9.6] - 2026-07-03 diff --git a/Cargo.toml b/Cargo.toml index 8ff4c5c41..b805b446a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ keywords = [ categories = ["science"] [workspace.dependencies] -quantity = "0.12" +quantity = "0.13" num-dual = "0.13" ndarray = "0.17" nalgebra = "0.34" @@ -33,7 +33,6 @@ serde = "1.0" serde_json = "1.0" indexmap = "2.0" itertools = "0.14" -typenum = "1.16" rayon = "1.11" petgraph = "0.8" rustdct = "0.7" diff --git a/crates/feos-core/Cargo.toml b/crates/feos-core/Cargo.toml index 15cfd525f..810dc2996 100644 --- a/crates/feos-core/Cargo.toml +++ b/crates/feos-core/Cargo.toml @@ -25,7 +25,6 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["preserve_order"] } indexmap = { workspace = true, features = ["serde"] } rayon = { workspace = true, optional = true } -typenum = { workspace = true } itertools = { workspace = true } rusqlite = { workspace = true, features = ["bundled"], optional = true } diff --git a/crates/feos-core/src/equation_of_state/residual.rs b/crates/feos-core/src/equation_of_state/residual.rs index 1fe7bb56c..326aff883 100644 --- a/crates/feos-core/src/equation_of_state/residual.rs +++ b/crates/feos-core/src/equation_of_state/residual.rs @@ -3,9 +3,10 @@ use nalgebra::{DVector, DefaultAllocator, Dim, Dyn, OMatrix, OVector, U1, alloca use num_dual::{DualNum, Gradients, partial, partial2, second_derivative, third_derivative}; use quantity::ad::first_derivative; use quantity::*; -use std::ops::Deref; +use std::ops::{Deref, Div}; use std::sync::Arc; -use typenum::Quot; + +type Quot = >::Output; /// Molar weight of all components. /// diff --git a/crates/feos-core/src/lib.rs b/crates/feos-core/src/lib.rs index b883193ca..b51ae0653 100644 --- a/crates/feos-core/src/lib.rs +++ b/crates/feos-core/src/lib.rs @@ -3,7 +3,6 @@ #![warn(clippy::allow_attributes)] use quantity::{Quantity, SIUnit}; use std::ops::{Div, Mul}; -use typenum::Integer; /// Print messages with level `Verbosity::Iter` or higher. #[macro_export] @@ -132,20 +131,20 @@ const fn powi(x: f64, n: i32) -> f64 { /// Conversion between reduced units and SI units. pub trait ReferenceSystem { type Inner; - type T: Integer; - type L: Integer; - type M: Integer; - type I: Integer; - type THETA: Integer; - type N: Integer; - type J: Integer; - const FACTOR: f64 = powi(REFERENCE_VALUES[0], Self::T::I32) - * powi(REFERENCE_VALUES[1], Self::L::I32) - * powi(REFERENCE_VALUES[2], Self::M::I32) - * powi(REFERENCE_VALUES[3], Self::I::I32) - * powi(REFERENCE_VALUES[4], Self::THETA::I32) - * powi(REFERENCE_VALUES[5], Self::N::I32) - * powi(REFERENCE_VALUES[6], Self::J::I32); + const T: i8; + const L: i8; + const M: i8; + const I: i8; + const THETA: i8; + const N: i8; + const J: i8; + const FACTOR: f64 = powi(REFERENCE_VALUES[0], Self::T as i32) + * powi(REFERENCE_VALUES[1], Self::L as i32) + * powi(REFERENCE_VALUES[2], Self::M as i32) + * powi(REFERENCE_VALUES[3], Self::I as i32) + * powi(REFERENCE_VALUES[4], Self::THETA as i32) + * powi(REFERENCE_VALUES[5], Self::N as i32) + * powi(REFERENCE_VALUES[6], Self::J as i32); fn from_reduced(value: Self::Inner) -> Self where @@ -161,17 +160,25 @@ pub trait ReferenceSystem { } /// Conversion to and from reduced units -impl - ReferenceSystem for Quantity> +impl< + Inner, + const T: i8, + const L: i8, + const M: i8, + const I: i8, + const THETA: i8, + const N: i8, + const J: i8, +> ReferenceSystem for Quantity> { type Inner = Inner; - type T = T; - type L = L; - type M = M; - type I = I; - type THETA = THETA; - type N = N; - type J = J; + const T: i8 = T; + const L: i8 = L; + const M: i8 = M; + const I: i8 = I; + const THETA: i8 = THETA; + const N: i8 = N; + const J: i8 = J; fn from_reduced(value: Inner) -> Self where Inner: Mul, diff --git a/crates/feos-core/src/phase_equilibria/bubble_dew.rs b/crates/feos-core/src/phase_equilibria/bubble_dew.rs index 9433720cf..a50cc46a1 100644 --- a/crates/feos-core/src/phase_equilibria/bubble_dew.rs +++ b/crates/feos-core/src/phase_equilibria/bubble_dew.rs @@ -12,7 +12,6 @@ use ndarray::Array1; use num_dual::linalg::LU; use num_dual::{DualNum, DualStruct, Gradients}; use quantity::{Density, Dimensionless, Moles, Pressure, Quantity, RGAS, SIUnit, Temperature}; -use typenum::{N1, N2, P1, Z0}; const MAX_ITER_INNER: usize = 5; const TOL_INNER: f64 = 1e-9; @@ -94,7 +93,7 @@ impl + Copy> TemperatureOrPressure for Temperature { // used instead of the explicit unit. Maybe the type is too complicated for the // compiler? impl + Copy> TemperatureOrPressure - for Quantity> + for Quantity> { type Other = Temperature; const IDENTIFIER: &'static str = "pressure"; diff --git a/crates/feos-core/src/state/builder.rs b/crates/feos-core/src/state/builder.rs index 02c61c93d..c418a7c1f 100644 --- a/crates/feos-core/src/state/builder.rs +++ b/crates/feos-core/src/state/builder.rs @@ -14,25 +14,24 @@ use quantity::*; /// # use quantity::*; /// # use nalgebra::dvector; /// # use approx::assert_relative_eq; -/// # use typenum::P3; /// # fn main() -> FeosResult<()> { /// // Create a state for given T,V,N /// let eos = &PengRobinson::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?); /// let state = StateBuilder::new(&eos) /// .temperature(300.0 * KELVIN) -/// .volume(12.5 * METER.powi::()) +/// .volume(12.5 * METER.powi::<3>()) /// .moles(&(dvector![2.5] * MOL)) /// .build()?; -/// assert_eq!(state.density, 0.2 * MOL / METER.powi::()); +/// assert_eq!(state.density, 0.2 * MOL / METER.powi::<3>()); /// /// // For a pure component, the composition does not need to be specified. /// let eos = &PengRobinson::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?); /// let state = StateBuilder::new(&eos) /// .temperature(300.0 * KELVIN) -/// .volume(12.5 * METER.powi::()) +/// .volume(12.5 * METER.powi::<3>()) /// .total_moles(2.5 * MOL) /// .build()?; -/// assert_eq!(state.density, 0.2 * MOL / METER.powi::()); +/// assert_eq!(state.density, 0.2 * MOL / METER.powi::<3>()); /// /// // The state can be constructed without providing any extensive property. /// let eos = &PengRobinson::new( @@ -45,10 +44,10 @@ use quantity::*; /// ); /// let state = StateBuilder::new(&eos) /// .temperature(300.0 * KELVIN) -/// .partial_density(&(dvector![0.2, 0.6] * MOL / METER.powi::())) +/// .partial_density(&(dvector![0.2, 0.6] * MOL / METER.powi::<3>())) /// .build()?; /// assert_relative_eq!(state.molefracs, dvector![0.25, 0.75]); -/// assert_relative_eq!(state.density, 0.8 * MOL / METER.powi::()); +/// assert_relative_eq!(state.density, 0.8 * MOL / METER.powi::<3>()); /// # Ok(()) /// # } /// ``` diff --git a/crates/feos-core/src/state/cache.rs b/crates/feos-core/src/state/cache.rs index 2ee54a737..b9a1326f7 100644 --- a/crates/feos-core/src/state/cache.rs +++ b/crates/feos-core/src/state/cache.rs @@ -1,8 +1,10 @@ use nalgebra::allocator::Allocator; use nalgebra::{DefaultAllocator, Dim, OVector, Scalar}; use quantity::*; +use std::ops::Sub; use std::sync::OnceLock; -use typenum::Diff; + +type Diff = >::Output; #[derive(Clone, Debug)] #[expect(clippy::type_complexity)] diff --git a/crates/feos-core/src/state/mod.rs b/crates/feos-core/src/state/mod.rs index 4f896d72e..5da7a43c5 100644 --- a/crates/feos-core/src/state/mod.rs +++ b/crates/feos-core/src/state/mod.rs @@ -798,12 +798,11 @@ mod critical_point; mod tests { use super::*; use nalgebra::dvector; - use typenum::P3; #[test] fn test_validate() { let temperature = 298.15 * KELVIN; - let density = 3000.0 * MOL / METER.powi::(); + let density = 3000.0 * MOL / METER.powi::<3>(); let molefracs = dvector![0.03, 0.02, 0.05]; assert!(validate(temperature, density, &molefracs).is_ok()); } @@ -811,7 +810,7 @@ mod tests { #[test] fn test_negative_temperature() { let temperature = -298.15 * KELVIN; - let density = 3000.0 * MOL / METER.powi::(); + let density = 3000.0 * MOL / METER.powi::<3>(); let molefracs = dvector![0.03, 0.02, 0.05]; assert!(validate(temperature, density, &molefracs).is_err()); } @@ -819,7 +818,7 @@ mod tests { #[test] fn test_nan_temperature() { let temperature = f64::NAN * KELVIN; - let density = 3000.0 * MOL / METER.powi::(); + let density = 3000.0 * MOL / METER.powi::<3>(); let molefracs = dvector![0.03, 0.02, 0.05]; assert!(validate(temperature, density, &molefracs).is_err()); } @@ -827,7 +826,7 @@ mod tests { #[test] fn test_negative_mole_number() { let temperature = 298.15 * KELVIN; - let density = 3000.0 * MOL / METER.powi::(); + let density = 3000.0 * MOL / METER.powi::<3>(); let molefracs = dvector![-0.03, 0.02, 0.05]; assert!(validate(temperature, density, &molefracs).is_err()); } @@ -835,7 +834,7 @@ mod tests { #[test] fn test_nan_mole_number() { let temperature = 298.15 * KELVIN; - let density = 3000.0 * MOL / METER.powi::(); + let density = 3000.0 * MOL / METER.powi::<3>(); let molefracs = dvector![f64::NAN, 0.02, 0.05]; assert!(validate(temperature, density, &molefracs).is_err()); } @@ -843,7 +842,7 @@ mod tests { #[test] fn test_negative_density() { let temperature = 298.15 * KELVIN; - let density = -3000.0 * MOL / METER.powi::(); + let density = -3000.0 * MOL / METER.powi::<3>(); let molefracs = dvector![0.01, 0.02, 0.05]; assert!(validate(temperature, density, &molefracs).is_err()); } diff --git a/crates/feos-dft/Cargo.toml b/crates/feos-dft/Cargo.toml index 9a010add0..2e9e52a7f 100644 --- a/crates/feos-dft/Cargo.toml +++ b/crates/feos-dft/Cargo.toml @@ -25,7 +25,6 @@ num-traits = { workspace = true } libm = { workspace = true } gauss-quad = { workspace = true, optional = true } petgraph = { workspace = true } -typenum = { workspace = true } feos-core = { workspace = true } diff --git a/crates/feos-dft/src/adsorption/pore.rs b/crates/feos-dft/src/adsorption/pore.rs index c7602b7d5..faf19e62d 100644 --- a/crates/feos-dft/src/adsorption/pore.rs +++ b/crates/feos-dft/src/adsorption/pore.rs @@ -19,12 +19,12 @@ use quantity::{ Temperature, Volume, }; use rustdct::DctNum; -use typenum::Diff; +use std::ops::Sub; const POTENTIAL_OFFSET: f64 = 2.0; const DEFAULT_GRID_POINTS: usize = 2048; -pub type _HenryCoefficient = Diff<_Moles, _Pressure>; +pub type _HenryCoefficient = <_Moles as Sub<_Pressure>>::Output; pub type HenryCoefficient = Quantity; /// Parameters required to specify a 1D pore. diff --git a/crates/feos-dft/src/pdgt.rs b/crates/feos-dft/src/pdgt.rs index d3a9fa30a..47ad359eb 100644 --- a/crates/feos-dft/src/pdgt.rs +++ b/crates/feos-dft/src/pdgt.rs @@ -10,7 +10,9 @@ use quantity::{ Temperature, }; use std::ops::{Add, AddAssign, Sub}; -use typenum::{Diff, P2, Sum}; + +type Sum = >::Output; +type Diff = >::Output; type _InfluenceParameter = Diff, _Density>; type InfluenceParameter = Quantity; @@ -218,7 +220,7 @@ pub trait PdgtFunctionalProperties: HelmholtzEnergyFunctional { // calculate interfacial width let w_temp = integrate_trapezoidal(&rho_r * &*z * z_int, dx); - *w = (24.0 * (w_temp - 0.5 * ze.powi::())).sqrt(); + *w = (24.0 * (w_temp - 0.5 * ze.powi::<2>())).sqrt(); // shift density profile *z -= ze; diff --git a/crates/feos-dft/src/profile/mod.rs b/crates/feos-dft/src/profile/mod.rs index 7bc9b7ac9..edac0e7b2 100644 --- a/crates/feos-dft/src/profile/mod.rs +++ b/crates/feos-dft/src/profile/mod.rs @@ -12,7 +12,6 @@ use num_dual::DualNum; use quantity::{_Volume, DEGREES, Density, Length, Moles, Quantity, Temperature, Volume}; use std::ops::{Add, MulAssign}; use std::sync::Arc; -use typenum::Sum; mod properties; @@ -304,7 +303,7 @@ where pub fn integrate, U>( &self, profile: &Quantity, U>, - ) -> Quantity> + ) -> Quantity>::Output> where _Volume: Add, { @@ -322,7 +321,7 @@ where pub fn integrate_comp, U>( &self, profile: &Quantity, U>, - ) -> Quantity, Sum<_Volume, U>> + ) -> Quantity, <_Volume as Add>::Output> where _Volume: Add, { @@ -335,7 +334,7 @@ where pub fn integrate_segments, U>( &self, profile: &Quantity, U>, - ) -> Quantity, Sum<_Volume, U>> + ) -> Quantity, <_Volume as Add>::Output> where _Volume: Add, { diff --git a/crates/feos/Cargo.toml b/crates/feos/Cargo.toml index 5965e4354..28e6e6109 100644 --- a/crates/feos/Cargo.toml +++ b/crates/feos/Cargo.toml @@ -26,7 +26,6 @@ serde_json = { workspace = true } indexmap = { workspace = true } rayon = { workspace = true, optional = true } itertools = { workspace = true } -typenum = { workspace = true } feos-core = { workspace = true } feos-derive = { workspace = true, optional = true } diff --git a/crates/feos/benches/dft_pore.rs b/crates/feos/benches/dft_pore.rs index 440c3648e..667c5ff12 100644 --- a/crates/feos/benches/dft_pore.rs +++ b/crates/feos/benches/dft_pore.rs @@ -10,7 +10,6 @@ use feos::hard_sphere::{FMTFunctional, FMTVersion}; use feos::pcsaft::{PcSaftFunctional, PcSaftParameters}; use nalgebra::dvector; use quantity::{ANGSTROM, KELVIN, NAV}; -use typenum::P3; fn fmt(c: &mut Criterion) { let mut group = c.benchmark_group("DFT_pore_fmt"); @@ -23,7 +22,7 @@ fn fmt(c: &mut Criterion) { None, None, ); - let bulk = State::new_pure(&func, KELVIN, 0.75 / NAV / ANGSTROM.powi::()).unwrap(); + let bulk = State::new_pure(&func, KELVIN, 0.75 / NAV / ANGSTROM.powi::<3>()).unwrap(); group.bench_function("liquid", |b| { b.iter(|| pore.initialize(&bulk, None, None).unwrap().solve(None)) }); diff --git a/crates/feos/benches/dual_numbers.rs b/crates/feos/benches/dual_numbers.rs index 6ec66a63c..454c6828f 100644 --- a/crates/feos/benches/dual_numbers.rs +++ b/crates/feos/benches/dual_numbers.rs @@ -13,7 +13,6 @@ use feos_core::parameter::PureRecord; use nalgebra::{DVector, Dyn, dvector}; use num_dual::{Dual2_64, Dual3_64, Dual64, DualNum, HyperDual64}; use quantity::*; -use typenum::P3; /// Helper function to create a state for given parameters. /// - temperature is 80% of critical temperature, @@ -129,7 +128,7 @@ fn methane_co2_pcsaft(c: &mut Criterion) { // 230 K, 50 bar, x0 = 0.15 let temperature = 230.0 * KELVIN; - let density = 24.16896 * KILO * MOL / METER.powi::(); + let density = 24.16896 * KILO * MOL / METER.powi::<3>(); let volume = 10.0 * MOL / density; let x = dvector![0.15, 0.85]; let moles = &x * 10.0 * MOL; diff --git a/crates/feos/benches/state_properties.rs b/crates/feos/benches/state_properties.rs index 401addf03..916f094ed 100644 --- a/crates/feos/benches/state_properties.rs +++ b/crates/feos/benches/state_properties.rs @@ -5,7 +5,6 @@ use feos::core::{Contributions, Residual, State}; use feos::pcsaft::{PcSaft, PcSaftParameters}; use nalgebra::{DVector, dvector}; use quantity::*; -use typenum::P3; /// Evaluate a property of a state given the EoS, the property to compute, /// temperature, volume, moles, and the contributions to consider. @@ -42,7 +41,7 @@ fn properties_pcsaft(c: &mut Criterion) { .unwrap(); let eos = PcSaft::new(parameters); let t = 300.0 * KELVIN; - let density = 71.18 * KILO * MOL / METER.powi::(); + let density = 71.18 * KILO * MOL / METER.powi::<3>(); let v = 100.0 * MOL / density; let x = dvector![1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0]; let m = &x * 100.0 * MOL; @@ -92,7 +91,7 @@ fn properties_pcsaft_polar(c: &mut Criterion) { .unwrap(); let eos = PcSaft::new(parameters); let t = 300.0 * KELVIN; - let density = 71.18 * KILO * MOL / METER.powi::(); + let density = 71.18 * KILO * MOL / METER.powi::<3>(); let v = 100.0 * MOL / density; let x = dvector![1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0]; let m = &x * 100.0 * MOL; diff --git a/crates/feos/src/association/mod.rs b/crates/feos/src/association/mod.rs index a12e12e14..72bd97a38 100644 --- a/crates/feos/src/association/mod.rs +++ b/crates/feos/src/association/mod.rs @@ -18,7 +18,7 @@ pub use dft::YuWuAssociationFunctional; /// [AssociationStrength::association_strength] multiplies the model-specific /// site-site association strength [AssociationStrength::association_strength_ij] /// with the contact value of the hard-sphere pair correlation function. -/// +/// /// For implementations that require a different form, /// [AssociationStrength::association_strength] can be overwritten. pub trait AssociationStrength: HardSphereProperties { @@ -463,7 +463,6 @@ mod tests_gc_pcsaft { use nalgebra::dvector; use num_dual::Dual64; use quantity::{METER, MOL, PASCAL, Pressure}; - use typenum::P3; #[test] fn test_assoc_propanol() { @@ -471,7 +470,7 @@ mod tests_gc_pcsaft { let params = GcPcSaftEosParameters::new(¶meters); let contrib = Association::new(50, 1e-10); let temperature = 300.0; - let volume = Dual64::from_re(METER.powi::().to_reduced()).derivative(); + let volume = Dual64::from_re(METER.powi::<3>().to_reduced()).derivative(); let moles = Dual64::from_re((1.5 * MOL).to_reduced()); let molar_volume = volume / moles; let state = StateHD::new( @@ -499,7 +498,7 @@ mod tests_gc_pcsaft { let params = GcPcSaftEosParameters::new(¶meters); let contrib = Association::new_cross_association(50, 1e-10); let temperature = 300.0; - let volume = Dual64::from_re(METER.powi::().to_reduced()).derivative(); + let volume = Dual64::from_re(METER.powi::<3>().to_reduced()).derivative(); let moles = Dual64::from_re((1.5 * MOL).to_reduced()); let molar_volume = volume / moles; let state = StateHD::new( @@ -527,7 +526,7 @@ mod tests_gc_pcsaft { let params = GcPcSaftEosParameters::new(¶meters); let contrib = Association::new(50, 1e-10); let temperature = 300.0; - let volume = Dual64::from_re(METER.powi::().to_reduced()).derivative(); + let volume = Dual64::from_re(METER.powi::<3>().to_reduced()).derivative(); let moles = (dvector![1.5, 2.5] * MOL).to_reduced().map(Dual64::from_re); let total_moles = moles.sum(); let molar_volume = volume / total_moles; diff --git a/crates/feos/src/epcsaft/eos/mod.rs b/crates/feos/src/epcsaft/eos/mod.rs index 06aa87b63..03a2713d0 100644 --- a/crates/feos/src/epcsaft/eos/mod.rs +++ b/crates/feos/src/epcsaft/eos/mod.rs @@ -173,13 +173,12 @@ mod tests { use approx::assert_relative_eq; use feos_core::*; use nalgebra::dvector; - use typenum::P3; #[test] fn ideal_gas_pressure() { let e = ElectrolytePcSaft::new(propane_parameters()).unwrap(); let t = 200.0 * KELVIN; - let v = 1e-3 * METER.powi::(); + let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&&e, t, v, &n).unwrap(); let p_ig = s.total_moles * RGAS * t / v; @@ -195,7 +194,7 @@ mod tests { fn ideal_gas_heat_capacity_joback() { let e = ElectrolytePcSaft::new(propane_parameters()).unwrap(); let t = 200.0 * KELVIN; - let v = 1e-3 * METER.powi::(); + let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&&e, t, v, &n).unwrap(); let p_ig = s.total_moles * RGAS * t / v; @@ -282,7 +281,7 @@ mod tests { let e2 = ElectrolytePcSaft::new(butane_parameters()).unwrap(); let e12 = ElectrolytePcSaft::new(propane_butane_parameters()).unwrap(); let t = 300.0 * KELVIN; - let v = 0.02456883872966545 * METER.powi::(); + let v = 0.02456883872966545 * METER.powi::<3>(); let m1 = dvector![2.0] * MOL; let m1m = dvector![2.0, 0.0] * MOL; let m2m = dvector![0.0, 2.0] * MOL; diff --git a/crates/feos/src/gc_pcsaft/eos/dispersion.rs b/crates/feos/src/gc_pcsaft/eos/dispersion.rs index a1a8169bf..3f464507d 100644 --- a/crates/feos/src/gc_pcsaft/eos/dispersion.rs +++ b/crates/feos/src/gc_pcsaft/eos/dispersion.rs @@ -129,13 +129,12 @@ mod test { use nalgebra::dvector; use num_dual::Dual64; use quantity::{METER, MOL, PASCAL, Pressure}; - use typenum::P3; #[test] fn test_dispersion_propane() { let parameters = propane(); let temperature = 300.0; - let volume = Dual64::from_re(METER.powi::().to_reduced()).derivative(); + let volume = Dual64::from_re(METER.powi::<3>().to_reduced()).derivative(); let moles = Dual64::from_re((1.5 * MOL).to_reduced()); let molar_volume = volume / moles; let state = StateHD::new( @@ -153,7 +152,7 @@ mod test { fn test_dispersion_propanol() { let parameters = GcPcSaftEosParameters::new(&propanol()); let temperature = 300.0; - let volume = Dual64::from_re(METER.powi::().to_reduced()).derivative(); + let volume = Dual64::from_re(METER.powi::<3>().to_reduced()).derivative(); let moles = Dual64::from_re((1.5 * MOL).to_reduced()); let molar_volume = volume / moles; let state = StateHD::new( diff --git a/crates/feos/src/gc_pcsaft/eos/hard_chain.rs b/crates/feos/src/gc_pcsaft/eos/hard_chain.rs index 50b3c4afa..f67dffda4 100644 --- a/crates/feos/src/gc_pcsaft/eos/hard_chain.rs +++ b/crates/feos/src/gc_pcsaft/eos/hard_chain.rs @@ -42,13 +42,12 @@ mod test { use nalgebra::dvector; use num_dual::Dual64; use quantity::{METER, MOL, PASCAL, Pressure}; - use typenum::P3; #[test] fn test_hc_propane() { let parameters = propane(); let temperature = 300.0; - let volume = METER.powi::().to_reduced(); + let volume = METER.powi::<3>().to_reduced(); let volume = Dual64::from_re(volume).derivative(); let moles = (1.5 * MOL).to_reduced(); let state = StateHD::new( @@ -70,7 +69,7 @@ mod test { fn test_hc_propanol() { let parameters = GcPcSaftEosParameters::new(&propanol()); let temperature = 300.0; - let volume = METER.powi::().to_reduced(); + let volume = METER.powi::<3>().to_reduced(); let volume = Dual64::from_re(volume).derivative(); let moles = (1.5 * MOL).to_reduced(); let state = StateHD::new( diff --git a/crates/feos/src/gc_pcsaft/eos/mod.rs b/crates/feos/src/gc_pcsaft/eos/mod.rs index 4198da197..ae6acf960 100644 --- a/crates/feos/src/gc_pcsaft/eos/mod.rs +++ b/crates/feos/src/gc_pcsaft/eos/mod.rs @@ -150,13 +150,12 @@ mod test { use nalgebra::dvector; use num_dual::Dual64; use quantity::{METER, MOL, PASCAL, Pressure}; - use typenum::P3; #[test] fn hs_propane() { let parameters = propane(); let temperature = 300.0; - let volume = Dual64::from_re(METER.powi::().to_reduced()).derivative(); + let volume = Dual64::from_re(METER.powi::<3>().to_reduced()).derivative(); let moles = Dual64::from_re((1.5 * MOL).to_reduced()); let molar_volume = volume / moles; let state = StateHD::new( @@ -174,7 +173,7 @@ mod test { fn hs_propanol() { let parameters = GcPcSaftEosParameters::new(&propanol()); let temperature = 300.0; - let volume = Dual64::from_re(METER.powi::().to_reduced()).derivative(); + let volume = Dual64::from_re(METER.powi::<3>().to_reduced()).derivative(); let moles = Dual64::from_re((1.5 * MOL).to_reduced()); let molar_volume = volume / moles; let state = StateHD::new( diff --git a/crates/feos/src/ideal_gas/dippr.rs b/crates/feos/src/ideal_gas/dippr.rs index 927dd2e07..7553e376c 100644 --- a/crates/feos/src/ideal_gas/dippr.rs +++ b/crates/feos/src/ideal_gas/dippr.rs @@ -159,7 +159,6 @@ mod tests { use feos_core::{Contributions, EquationOfState, StateBuilder}; use num_dual::first_derivative; use quantity::*; - use typenum::P3; use super::*; @@ -173,7 +172,7 @@ mod tests { let dippr = Dippr::new(DipprParameters::new_pure(record.clone())?); let eos = EquationOfState::ideal_gas(dippr.clone()); let temperature = 300.0 * KELVIN; - let volume = METER.powi::(); + let volume = METER.powi::<3>(); let state = StateBuilder::new(&&eos) .temperature(temperature) .volume(volume) @@ -217,7 +216,7 @@ mod tests { let dippr = Dippr::new(DipprParameters::new_pure(record.clone())?); let eos = EquationOfState::ideal_gas(dippr.clone()); let temperature = 300.0 * KELVIN; - let volume = METER.powi::(); + let volume = METER.powi::<3>(); let state = StateBuilder::new(&&eos) .temperature(temperature) .volume(volume) @@ -263,7 +262,7 @@ mod tests { let dippr = Dippr::new(DipprParameters::new_pure(record.clone())?); let eos = EquationOfState::ideal_gas(dippr.clone()); let temperature = 20.0 * KELVIN; - let volume = METER.powi::(); + let volume = METER.powi::<3>(); let state = StateBuilder::new(&&eos) .temperature(temperature) .volume(volume) diff --git a/crates/feos/src/ideal_gas/joback.rs b/crates/feos/src/ideal_gas/joback.rs index bef674722..742c6ef2e 100644 --- a/crates/feos/src/ideal_gas/joback.rs +++ b/crates/feos/src/ideal_gas/joback.rs @@ -179,7 +179,6 @@ mod tests { use nalgebra::dvector; use quantity::*; use std::collections::HashMap; - use typenum::P3; use super::*; @@ -263,7 +262,7 @@ mod tests { let pr = PureRecord::new(Identifier::default(), 1.0, jr); let joback = Joback::new(JobackParameters::new_pure(pr)?); let eos = EquationOfState::ideal_gas(joback); - let state = State::new_pure(&&eos, 1000.0 * KELVIN, 1.0 * MOL / METER.powi::())?; + let state = State::new_pure(&&eos, 1000.0 * KELVIN, 1.0 * MOL / METER.powi::<3>())?; assert!( ((state.molar_isobaric_heat_capacity(Contributions::IdealGas) / (JOULE / MOL / KELVIN)) @@ -294,7 +293,7 @@ mod tests { )?); let eos = EquationOfState::ideal_gas(joback.clone()); let temperature = 300.0 * KELVIN; - let volume = METER.powi::(); + let volume = METER.powi::<3>(); let moles = &dvector![1.0, 3.0] * MOL; let state = StateBuilder::new(&&eos) .temperature(temperature) diff --git a/crates/feos/src/multiparameter/mod.rs b/crates/feos/src/multiparameter/mod.rs index f8ab47634..68bbd98b8 100644 --- a/crates/feos/src/multiparameter/mod.rs +++ b/crates/feos/src/multiparameter/mod.rs @@ -138,7 +138,6 @@ mod test { use nalgebra::{Dyn, SVector, U2, dvector}; use num_dual::{Dual2Vec, hessian}; use quantity::{GRAM, KELVIN, KILO, KILOGRAM, METER, MOL, RGAS}; - use typenum::P3; use super::*; @@ -262,7 +261,7 @@ mod test { #[test] fn test_ideal_gas_hack() { let t = 647. * KELVIN; - let rho = 358. * KILOGRAM / METER.powi::(); + let rho = 358. * KILOGRAM / METER.powi::<3>(); let eos = &water(); let mw = eos.molar_weight.get(0); let moles = dvector![1.8] * MOL; @@ -270,7 +269,7 @@ mod test { let phi_feos = (a_feos / RGAS / moles.sum() / t).into_value(); println!("A: {a_feos}"); println!("phi(feos): {phi_feos}"); - let delta = (rho / (eos.rhoc * MOL / METER.powi::() * mw)).into_value(); + let delta = (rho / (eos.rhoc * MOL / METER.powi::<3>() * mw)).into_value(); let tau = (eos.tc * KELVIN / t).into_value(); let phi = eos.ideal_gas[0] .terms diff --git a/crates/feos/src/pcsaft/eos/mod.rs b/crates/feos/src/pcsaft/eos/mod.rs index aea6b6890..6b0aa8782 100644 --- a/crates/feos/src/pcsaft/eos/mod.rs +++ b/crates/feos/src/pcsaft/eos/mod.rs @@ -9,7 +9,6 @@ use num_dual::{DualNum, partial2}; use quantity::ad::first_derivative; use quantity::*; use std::f64::consts::{FRAC_PI_6, PI}; -use typenum::P2; pub(crate) mod dispersion; pub(crate) mod hard_chain; @@ -241,7 +240,7 @@ impl EntropyScaling for PcSaft { let tr = (temperature / p.epsilon_k[i] / KELVIN).into_value(); 5.0 / 16.0 * (mw.get(i) * KB / NAV * temperature / PI).sqrt() / omega22(tr) - / (p.sigma[i] * ANGSTROM).powi::() + / (p.sigma[i] * ANGSTROM).powi::<2>() }) .collect(); let mut ce_mix = 0.0 * MILLI * PASCAL * SECOND; @@ -291,7 +290,7 @@ impl EntropyScaling for PcSaft { let res: Vec<_> = (0..self.components()) .map(|i| { let tr = (temperature / p.epsilon_k[i] / KELVIN).into_value(); - 3.0 / 8.0 / (p.sigma[i] * ANGSTROM).powi::() / omega11(tr) / (density * NAV) + 3.0 / 8.0 / (p.sigma[i] * ANGSTROM).powi::<2>() / omega11(tr) / (density * NAV) * (temperature * RGAS / PI / mw.get(i) / p.m[i]).sqrt() }) .collect(); @@ -393,13 +392,12 @@ mod tests { use feos_core::*; use nalgebra::dvector; use quantity::{BAR, KELVIN, METER, PASCAL, RGAS}; - use typenum::{P2, P3}; #[test] fn ideal_gas_pressure() { let e = &propane_parameters(); let t = 200.0 * KELVIN; - let v = 1e-3 * METER.powi::(); + let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&e, t, v, &n).unwrap(); let p_ig = s.total_moles * RGAS * t / v; @@ -415,7 +413,7 @@ mod tests { fn ideal_gas_heat_capacity_joback() { let e = &propane_parameters(); let t = 200.0 * KELVIN; - let v = 1e-3 * METER.powi::(); + let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&e, t, v, &n).unwrap(); let p_ig = s.total_moles * RGAS * t / v; @@ -504,7 +502,7 @@ mod tests { let e2 = &butane_parameters(); let e12 = &propane_butane_parameters(); let t = 300.0 * KELVIN; - let v = 0.02456883872966545 * METER.powi::(); + let v = 0.02456883872966545 * METER.powi::<3>(); let m1 = dvector![2.0] * MOL; let m1m = dvector![2.0, 0.0] * MOL; let m2m = dvector![0.0, 2.0] * MOL; @@ -577,7 +575,7 @@ mod tests { let s = State::new_npt(&e, t, p, &n, None)?; assert_relative_eq!( s.diffusion(), - 0.01505 * (CENTI * METER).powi::() / SECOND, + 0.01505 * (CENTI * METER).powi::<2>() / SECOND, epsilon = 1e-5 ); assert_relative_eq!( diff --git a/crates/feos/src/pets/eos/mod.rs b/crates/feos/src/pets/eos/mod.rs index 0ad191726..f1d1d76cb 100644 --- a/crates/feos/src/pets/eos/mod.rs +++ b/crates/feos/src/pets/eos/mod.rs @@ -144,13 +144,12 @@ mod tests { use feos_core::{Contributions, PhaseEquilibrium, State, StateHD}; use nalgebra::dvector; use quantity::{BAR, KELVIN, METER, MOL, RGAS}; - use typenum::P3; #[test] fn ideal_gas_pressure() { let e = &Pets::new(argon_parameters()); let t = 200.0 * KELVIN; - let v = 1e-3 * METER.powi::(); + let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&e, t, v, &n).unwrap(); let p_ig = s.total_moles * RGAS * t / v; @@ -212,7 +211,7 @@ mod tests { let e2 = &Pets::new(krypton_parameters()); let e12 = &Pets::new(argon_krypton_parameters()); let t = 300.0 * KELVIN; - let v = 0.02456883872966545 * METER.powi::(); + let v = 0.02456883872966545 * METER.powi::<3>(); let m1 = dvector![2.0] * MOL; let m1m = dvector![2.0, 0.0] * MOL; let m2m = dvector![0.0, 2.0] * MOL; diff --git a/crates/feos/src/uvtheory/eos/mod.rs b/crates/feos/src/uvtheory/eos/mod.rs index bc38dcd33..9fa963ae2 100644 --- a/crates/feos/src/uvtheory/eos/mod.rs +++ b/crates/feos/src/uvtheory/eos/mod.rs @@ -110,7 +110,6 @@ mod test { use feos_core::{FeosResult, State}; use nalgebra::dvector; use quantity::{ANGSTROM, KELVIN, MOL, NAV, RGAS}; - use typenum::P3; #[test] fn helmholtz_energy_pure_wca() -> FeosResult<()> { @@ -123,7 +122,7 @@ mod test { let reduced_density = 1.0; let temperature = reduced_temperature * eps_k * KELVIN; let moles = dvector![2.0] * MOL; - let volume = (sig * ANGSTROM).powi::() / reduced_density * NAV * 2.0 * MOL; + let volume = (sig * ANGSTROM).powi::<3>() / reduced_density * NAV * 2.0 * MOL; let s = State::new_nvt(&eos, temperature, volume, &moles).unwrap(); let a = (s.residual_molar_helmholtz_energy() / (RGAS * temperature)).into_value(); assert_relative_eq!(a, 2.972986567516, max_relative = 1e-12); //wca @@ -147,7 +146,7 @@ mod test { let reduced_density = 1.0; let temperature = reduced_temperature * eps_k * KELVIN; let moles = dvector![2.0] * MOL; - let volume = (sig * ANGSTROM).powi::() / reduced_density * NAV * 2.0 * MOL; + let volume = (sig * ANGSTROM).powi::<3>() / reduced_density * NAV * 2.0 * MOL; let s = State::new_nvt(&eos, temperature, volume, &moles).unwrap(); let a = (s.residual_molar_helmholtz_energy() / (RGAS * temperature)).into_value(); @@ -173,7 +172,7 @@ mod test { let reduced_density = 0.5; let temperature = reduced_temperature * eps_k * KELVIN; let moles = dvector![2.0] * MOL; - let volume = (sig * ANGSTROM).powi::() / reduced_density * NAV * 2.0 * MOL; + let volume = (sig * ANGSTROM).powi::<3>() / reduced_density * NAV * 2.0 * MOL; let s = State::new_nvt(&eos, temperature, volume, &moles).unwrap(); let a = (s.residual_molar_helmholtz_energy() / (RGAS * temperature)).into_value(); dbg!(a); @@ -209,7 +208,7 @@ mod test { let reduced_density = 1.0; let moles = dvector![1.7, 0.3] * MOL; let total_moles = moles.sum(); - let volume = (sig_x * ANGSTROM).powi::() / reduced_density * NAV * total_moles; + let volume = (sig_x * ANGSTROM).powi::<3>() / reduced_density * NAV * total_moles; // EoS let options = UVTheoryOptions { @@ -242,7 +241,7 @@ mod test { let reduced_density = 0.9; let moles = dvector![0.4, 0.6] * MOL; let total_moles = moles.sum(); - let volume = (p.sigma[0] * ANGSTROM).powi::() / reduced_density * NAV * total_moles; + let volume = (p.sigma[0] * ANGSTROM).powi::<3>() / reduced_density * NAV * total_moles; // EoS let eos_wca = &UVTheory::new(parameters); @@ -267,7 +266,7 @@ mod test { // state let reduced_temperature = 1.5; let t_x = reduced_temperature * p.epsilon_k[0] * KELVIN; - let sigma_x_3 = (0.4 + 0.6 * 8.0) * ANGSTROM.powi::(); + let sigma_x_3 = (0.4 + 0.6 * 8.0) * ANGSTROM.powi::<3>(); let density = 0.52000000000000002 / sigma_x_3; let moles = dvector![0.4, 0.6] * MOL; let total_moles = moles.sum(); diff --git a/crates/feos/tests/gc_pcsaft/binary.rs b/crates/feos/tests/gc_pcsaft/binary.rs index 0419a8987..e9b9ed434 100644 --- a/crates/feos/tests/gc_pcsaft/binary.rs +++ b/crates/feos/tests/gc_pcsaft/binary.rs @@ -6,7 +6,6 @@ use feos_core::parameter::IdentifierOption; use feos_core::{Contributions, FeosResult, State}; use nalgebra::dvector; use quantity::{KELVIN, METER, MOL}; -use typenum::P3; #[test] fn test_binary() -> FeosResult<()> { @@ -70,9 +69,9 @@ fn test_polar_term() -> FeosResult<()> { let eos1 = &GcPcSaft::new(parameters1); let eos2 = &GcPcSaft::new(parameters2); let moles = dvector![0.5, 0.5] * MOL; - let p1 = State::new_nvt(&eos1, 300.0 * KELVIN, METER.powi::(), &moles)? + let p1 = State::new_nvt(&eos1, 300.0 * KELVIN, METER.powi::<3>(), &moles)? .pressure(Contributions::Total); - let p2 = State::new_nvt(&eos2, 300.0 * KELVIN, METER.powi::(), &moles)? + let p2 = State::new_nvt(&eos2, 300.0 * KELVIN, METER.powi::<3>(), &moles)? .pressure(Contributions::Total); println!("{p1} {p2}"); assert_eq!(p1, p2); diff --git a/crates/feos/tests/gc_pcsaft/dft.rs b/crates/feos/tests/gc_pcsaft/dft.rs index 5d245bfb1..a9813c56e 100644 --- a/crates/feos/tests/gc_pcsaft/dft.rs +++ b/crates/feos/tests/gc_pcsaft/dft.rs @@ -10,7 +10,6 @@ use feos_dft::{DFTSolver, Geometry}; use nalgebra::dvector; use quantity::*; use std::error::Error; -use typenum::P3; #[test] #[allow(non_snake_case)] @@ -40,7 +39,7 @@ fn test_bulk_implementation() -> Result<(), Box> { let eos = GcPcSaft::new(parameters); let func = GcPcSaftFunctional::new(parameters_func); let t = 200.0 * KELVIN; - let v = 0.002 * METER.powi::() * NAV / NAV_old; + let v = 0.002 * METER.powi::<3>() * NAV / NAV_old; let n = dvector![1.5] * MOL; let state_eos = State::new_nvt(&&eos, t, v, &n)?; let state_func = State::new_nvt(&&func, t, v, &n)?; @@ -123,7 +122,7 @@ fn test_bulk_association() -> Result<(), Box> { let func = GcPcSaftFunctional::new(func_parameters); let t = 200.0 * KELVIN; - let v = 0.002 * METER.powi::(); + let v = 0.002 * METER.powi::<3>(); let n = dvector![1.5] * MOL; let state_eos = State::new_nvt(&&eos, t, v, &n)?; let state_func = State::new_nvt(&&func, t, v, &n)?; @@ -171,13 +170,13 @@ fn test_dft() -> Result<(), Box> { assert_relative_eq!( vle.vapor().density, - 12.8820179191167643 * MOL / METER.powi::() * NAV_old / NAV, + 12.8820179191167643 * MOL / METER.powi::<3>() * NAV_old / NAV, max_relative = 1e-13, ); assert_relative_eq!( vle.liquid().density, - 13.2705903446123212 * KILO * MOL / METER.powi::() * NAV_old / NAV, + 13.2705903446123212 * KILO * MOL / METER.powi::<3>() * NAV_old / NAV, max_relative = 1e-13, ); diff --git a/crates/feos/tests/pcsaft/critical_point.rs b/crates/feos/tests/pcsaft/critical_point.rs index 48d36045c..9183e5e5d 100644 --- a/crates/feos/tests/pcsaft/critical_point.rs +++ b/crates/feos/tests/pcsaft/critical_point.rs @@ -6,7 +6,6 @@ use nalgebra::dvector; use quantity::*; use std::error::Error; use std::sync::Arc; -use typenum::P3; #[test] fn test_critical_point_pure() -> Result<(), Box> { @@ -22,7 +21,7 @@ fn test_critical_point_pure() -> Result<(), Box> { assert_relative_eq!(cp.temperature, 375.12441 * KELVIN, max_relative = 1e-8); assert_relative_eq!( cp.density, - 4733.00377 * MOL / METER.powi::(), + 4733.00377 * MOL / METER.powi::<3>(), max_relative = 1e-6 ); Ok(()) @@ -43,7 +42,7 @@ fn test_critical_point_mix() -> Result<(), Box> { assert_relative_eq!(cp.temperature, 407.93481 * KELVIN, max_relative = 1e-8); assert_relative_eq!( cp.density, - 4265.50745 * MOL / METER.powi::(), + 4265.50745 * MOL / METER.powi::<3>(), max_relative = 1e-6 ); Ok(()) diff --git a/crates/feos/tests/pcsaft/dft.rs b/crates/feos/tests/pcsaft/dft.rs index b6c6f3880..e8d057e47 100644 --- a/crates/feos/tests/pcsaft/dft.rs +++ b/crates/feos/tests/pcsaft/dft.rs @@ -12,7 +12,6 @@ use nalgebra::dvector; use ndarray::Axis; use quantity::*; use std::error::Error; -use typenum::P3; fn parameters(comp: &str) -> FeosResult { PcSaftParameters::from_json( @@ -35,7 +34,7 @@ fn test_bulk_implementations() -> Result<(), Box> { let func_full = PcSaftFunctional::new_full(parameters("water_np")?, FMTVersion::KierlikRosinberg); let t = 300.0 * KELVIN; - let v = 0.002 * METER.powi::() * NAV / NAV_old; + let v = 0.002 * METER.powi::<3>() * NAV / NAV_old; let n = dvector![1.5] * MOL; let state = State::new_nvt(&&eos, t, v, &n)?; let state_pure = State::new_nvt(&&func_pure, t, v, &n)?; @@ -135,7 +134,7 @@ fn test_dft_propane() -> Result<(), Box> { (&func_full_vec).solve_pdgt(&vle_full_vec, 198, 0, None)?.1 ); - let vapor_density = 12.2557486248527745 * MOL / METER.powi::() * NAV_old / NAV; + let vapor_density = 12.2557486248527745 * MOL / METER.powi::<3>() * NAV_old / NAV; assert_relative_eq!( vle_pure.vapor().density, vapor_density, @@ -152,7 +151,7 @@ fn test_dft_propane() -> Result<(), Box> { max_relative = 1e-13, ); - let liquid_density = 13.8941749145544549 * KILO * MOL / METER.powi::() * NAV_old / NAV; + let liquid_density = 13.8941749145544549 * KILO * MOL / METER.powi::<3>() * NAV_old / NAV; assert_relative_eq!( vle_pure.liquid().density, liquid_density, @@ -254,7 +253,7 @@ fn test_dft_water() -> Result<(), Box> { vle_full_vec.liquid().density ); - let vapor_density = 75.8045715345905222 * MOL / METER.powi::() * NAV_old / NAV; + let vapor_density = 75.8045715345905222 * MOL / METER.powi::<3>() * NAV_old / NAV; assert_relative_eq!( vle_pure.vapor().density, vapor_density, @@ -266,7 +265,7 @@ fn test_dft_water() -> Result<(), Box> { max_relative = 1e-13, ); - let liquid_density = 47.8480850281608454 * KILO * MOL / METER.powi::() * NAV_old / NAV; + let liquid_density = 47.8480850281608454 * KILO * MOL / METER.powi::<3>() * NAV_old / NAV; assert_relative_eq!( vle_pure.liquid().density, liquid_density, diff --git a/crates/feos/tests/pcsaft/state_creation_mixture.rs b/crates/feos/tests/pcsaft/state_creation_mixture.rs index e30d4058c..b7b93c73a 100644 --- a/crates/feos/tests/pcsaft/state_creation_mixture.rs +++ b/crates/feos/tests/pcsaft/state_creation_mixture.rs @@ -6,7 +6,6 @@ use feos_core::{Contributions, EquationOfState, FeosResult, StateBuilder}; use nalgebra::dvector; use quantity::*; use std::error::Error; -use typenum::P3; fn propane_butane_parameters() -> FeosResult<(PcSaftParameters, Vec)> { let saft = PcSaftParameters::from_json( @@ -61,7 +60,7 @@ fn pressure_entropy_molefracs() -> Result<(), Box> { fn volume_temperature_molefracs() -> Result<(), Box> { let saft = PcSaft::new(propane_butane_parameters()?.0); let temperature = 300.0 * KELVIN; - let volume = 1.5e-3 * METER.powi::(); + let volume = 1.5e-3 * METER.powi::<3>(); let moles = MOL; let x = dvector![0.3, 0.7]; let state = StateBuilder::new(&&saft) @@ -79,7 +78,7 @@ fn temperature_partial_density() -> Result<(), Box> { let saft = PcSaft::new(propane_butane_parameters()?.0); let temperature = 300.0 * KELVIN; let x = dvector![0.3, 0.7]; - let partial_density = x.clone() * MOL / METER.powi::(); + let partial_density = x.clone() * MOL / METER.powi::<3>(); let density = partial_density.sum(); let state = StateBuilder::new(&&saft) .temperature(temperature) @@ -98,7 +97,7 @@ fn temperature_density_molefracs() -> Result<(), Box> { let saft = PcSaft::new(propane_butane_parameters()?.0); let temperature = 300.0 * KELVIN; let x = dvector![0.3, 0.7]; - let density = MOL / METER.powi::(); + let density = MOL / METER.powi::<3>(); let state = StateBuilder::new(&&saft) .temperature(temperature) .density(density) diff --git a/crates/feos/tests/pcsaft/state_creation_pure.rs b/crates/feos/tests/pcsaft/state_creation_pure.rs index c8fea1780..d5bc9769e 100644 --- a/crates/feos/tests/pcsaft/state_creation_pure.rs +++ b/crates/feos/tests/pcsaft/state_creation_pure.rs @@ -7,7 +7,6 @@ use feos_core::{ }; use quantity::*; use std::error::Error; -use typenum::P3; fn propane_parameters() -> FeosResult<(PcSaftParameters, Vec)> { let saft = PcSaftParameters::from_json( @@ -29,7 +28,7 @@ fn propane_parameters() -> FeosResult<(PcSaftParameters, Vec)> { fn temperature_volume() -> Result<(), Box> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; - let volume = 1.5e-3 * METER.powi::(); + let volume = 1.5e-3 * METER.powi::<3>(); let moles = MOL; let state = StateBuilder::new(&&saft) .temperature(temperature) @@ -44,7 +43,7 @@ fn temperature_volume() -> Result<(), Box> { fn temperature_density() -> Result<(), Box> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; - let density = MOL / METER.powi::(); + let density = MOL / METER.powi::<3>(); let state = StateBuilder::new(&&saft) .temperature(temperature) .density(density) @@ -58,7 +57,7 @@ fn temperature_total_moles_volume() -> Result<(), Box> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; let total_moles = MOL; - let volume = METER.powi::(); + let volume = METER.powi::<3>(); let state = StateBuilder::new(&&saft) .temperature(temperature) .volume(volume) @@ -74,7 +73,7 @@ fn temperature_total_moles_density() -> Result<(), Box> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; let total_moles = MOL; - let density = MOL / METER.powi::(); + let density = MOL / METER.powi::<3>(); let state = StateBuilder::new(&&saft) .temperature(temperature) .density(density) @@ -131,7 +130,7 @@ fn pressure_temperature_initial_density() -> Result<(), Box> { let state = StateBuilder::new(&&saft) .temperature(temperature) .pressure(pressure) - .initial_density(MOL / METER.powi::()) + .initial_density(MOL / METER.powi::<3>()) .build()?; assert_relative_eq!( state.pressure(Contributions::Total), diff --git a/crates/feos/tests/saftvrmie/critical_properties.rs b/crates/feos/tests/saftvrmie/critical_properties.rs index 3a4a585ed..c78eb110a 100644 --- a/crates/feos/tests/saftvrmie/critical_properties.rs +++ b/crates/feos/tests/saftvrmie/critical_properties.rs @@ -3,12 +3,11 @@ use feos::saftvrmie::{SaftVRMie, test_utils}; use feos_core::{SolverOptions, State}; use quantity::*; use std::collections::HashMap; -use typenum::P3; /// Critical data reported in Lafitte et al. pub fn critical_data() -> HashMap<&'static str, (Temperature, Pressure, MassDensity)> { let mut data = HashMap::new(); - let kg_m3 = KILOGRAM / METER.powi::(); + let kg_m3 = KILOGRAM / METER.powi::<3>(); let mpa = MEGA * PASCAL; let k = KELVIN; diff --git a/py-feos/Cargo.toml b/py-feos/Cargo.toml index c68f5dd04..aa21f4171 100644 --- a/py-feos/Cargo.toml +++ b/py-feos/Cargo.toml @@ -35,7 +35,6 @@ serde_json = { workspace = true } indexmap = { workspace = true } rayon = { workspace = true, optional = true } itertools = { workspace = true } -typenum = { workspace = true } paste = { workspace = true } rusqlite = { workspace = true, features = ["bundled"]} diff --git a/py-feos/src/dft/profile.rs b/py-feos/src/dft/profile.rs index 20fd4ed19..426d5b099 100644 --- a/py-feos/src/dft/profile.rs +++ b/py-feos/src/dft/profile.rs @@ -1,5 +1,7 @@ macro_rules! impl_profile { ($struct:ident, $arr:ident, $arr2:ident, $si_arr:ident, $si_arr2:ident, $py_arr2:ident, [$([$ind:expr, $ax:ident]),+]$(, $si_arr3:ident)?) => { + + #[pymethods] impl $struct { /// Calculate the residual for the given profile. @@ -118,7 +120,7 @@ macro_rules! impl_profile { fn entropy_density( &mut self, contributions: PyContributions, - ) -> PyResult>, Volume>> { + ) -> PyResult<> as std::ops::Div>::Output> { Ok(self.0.profile.entropy_density(contributions.into()).map_err(PyFeosError::from)?) } @@ -166,33 +168,33 @@ macro_rules! impl_profile { } $( #[getter] - fn get_drho_dmu(&self) -> PyResult>, MolarEnergy>> { + fn get_drho_dmu(&self) -> PyResult<> as std::ops::Div>::Output> { Ok(self.0.profile.drho_dmu().map_err(PyFeosError::from)?) } )? #[getter] - fn get_dn_dmu(&self) -> PyResult>, MolarEnergy>> { + fn get_dn_dmu(&self) -> PyResult<> as std::ops::Div>::Output> { Ok(self.0.profile.dn_dmu().map_err(PyFeosError::from)?) } #[getter] - fn get_drho_dp(&self) -> PyResult>, Pressure>> { + fn get_drho_dp(&self) -> PyResult<> as std::ops::Div>::Output> { Ok(self.0.profile.drho_dp().map_err(PyFeosError::from)?) } #[getter] - fn get_dn_dp(&self) -> PyResult>, Pressure>> { + fn get_dn_dp(&self) -> PyResult<> as std::ops::Div>::Output> { Ok(self.0.profile.dn_dp().map_err(PyFeosError::from)?) } #[getter] - fn get_drho_dt(&self) -> PyResult>, Temperature>> { + fn get_drho_dt(&self) -> PyResult<> as std::ops::Div>::Output> { Ok(self.0.profile.drho_dt().map_err(PyFeosError::from)?) } #[getter] - fn get_dn_dt(&self) -> PyResult>, Temperature>> { + fn get_dn_dt(&self) -> PyResult<> as std::ops::Div>::Output> { Ok(self.0.profile.dn_dt().map_err(PyFeosError::from)?) } } diff --git a/py-feos/src/eos/mod.rs b/py-feos/src/eos/mod.rs index 86b6a1447..1147ac020 100644 --- a/py-feos/src/eos/mod.rs +++ b/py-feos/src/eos/mod.rs @@ -7,8 +7,10 @@ use nalgebra::{DVector, DVectorView, Dyn}; use numpy::{PyArray1, PyReadonlyArray1, ToPyArray}; use pyo3::prelude::*; use quantity::*; +use std::ops::Div; use std::sync::Arc; -use typenum::Quot; + +type Quot = >::Output; mod constructors; #[cfg(feature = "epcsaft")] diff --git a/py-feos/src/phase_equilibria.rs b/py-feos/src/phase_equilibria.rs index 77f95df3d..c3786e300 100644 --- a/py-feos/src/phase_equilibria.rs +++ b/py-feos/src/phase_equilibria.rs @@ -1,10 +1,10 @@ use crate::{ - eos::{parse_molefracs, PyEquationOfState}, + PyVerbosity, + eos::{PyEquationOfState, parse_molefracs}, error::PyFeosError, ideal_gas::IdealGasModel, residual::ResidualModel, state::{PyContributions, PyState, PyStateVec}, - PyVerbosity, }; use feos_core::{ Contributions, EquationOfState, PhaseDiagram, PhaseDiagramHetero, PhaseEquilibrium, ResidualDyn, @@ -17,7 +17,6 @@ use pyo3::prelude::*; use quantity::*; use std::ops::Deref; use std::sync::Arc; -use typenum::P3; /// A thermodynamic two phase equilibrium state. #[pyclass(name = "PhaseEquilibrium")] @@ -1082,7 +1081,7 @@ impl PyPhaseDiagram { self.0 .liquid() .density() - .convert_to(MOL / METER.powi::()) + .convert_to(MOL / METER.powi::<3>()) .into_raw_vec_and_offset() .0, ); @@ -1091,7 +1090,7 @@ impl PyPhaseDiagram { self.0 .vapor() .density() - .convert_to(MOL / METER.powi::()) + .convert_to(MOL / METER.powi::<3>()) .into_raw_vec_and_offset() .0, ); @@ -1137,7 +1136,7 @@ impl PyPhaseDiagram { self.0 .liquid() .mass_density() - .convert_to(KILOGRAM / METER.powi::()) + .convert_to(KILOGRAM / METER.powi::<3>()) .into_raw_vec_and_offset() .0, ); @@ -1146,7 +1145,7 @@ impl PyPhaseDiagram { self.0 .vapor() .mass_density() - .convert_to(KILOGRAM / METER.powi::()) + .convert_to(KILOGRAM / METER.powi::<3>()) .into_raw_vec_and_offset() .0, ); diff --git a/py-feos/src/state.rs b/py-feos/src/state.rs index 2b5492d85..e7b54211c 100644 --- a/py-feos/src/state.rs +++ b/py-feos/src/state.rs @@ -1,7 +1,7 @@ use crate::eos::parse_molefracs; use crate::{ - eos::PyEquationOfState, error::PyFeosError, ideal_gas::IdealGasModel, residual::ResidualModel, - PyVerbosity, + PyVerbosity, eos::PyEquationOfState, error::PyFeosError, ideal_gas::IdealGasModel, + residual::ResidualModel, }; use feos_core::{ Contributions, DensityInitialization, EquationOfState, FeosError, ResidualDyn, State, StateVec, @@ -13,9 +13,10 @@ use pyo3::exceptions::{PyIndexError, PyValueError}; use pyo3::prelude::*; use quantity::*; use std::collections::HashMap; -use std::ops::{Deref, Neg, Sub}; +use std::ops::{Deref, Div, Neg, Sub}; use std::sync::Arc; -use typenum::{Quot, P3}; + +type Quot = >::Output; type DpDn = Quantity>::Output>; type InvT = Quantity::Output>; @@ -1633,7 +1634,7 @@ impl PyStateVec { String::from("density"), states .density() - .convert_to(MOL / METER.powi::()) + .convert_to(MOL / METER.powi::<3>()) .into_raw_vec_and_offset() .0, ); @@ -1658,7 +1659,7 @@ impl PyStateVec { String::from("mass density"), states .mass_density() - .convert_to(KILOGRAM / METER.powi::()) + .convert_to(KILOGRAM / METER.powi::<3>()) .into_raw_vec_and_offset() .0, ); From 2f0a5cb019add48f47d7cdf06dae18c977c35028 Mon Sep 17 00:00:00 2001 From: Philipp Rehner Date: Tue, 27 Jan 2026 09:50:15 +0100 Subject: [PATCH 02/12] Extend tp-flash to static arrays and AD --- .github/workflows/test.yml | 4 +- .github/workflows/wheels.yml | 4 +- CHANGELOG.md | 3 + crates/feos-core/src/phase_equilibria/mod.rs | 11 +- .../phase_equilibria/stability_analysis.rs | 25 ++- .../src/phase_equilibria/tp_flash.rs | 156 ++++++++++++++---- crates/feos/src/pcsaft/eos/mod.rs | 58 ++++++- 7 files changed, 207 insertions(+), 54 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7c7b1ad4a..6ea6192e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,9 @@ name: Test on: push: - branches: [main] + branches: [main, development] pull_request: - branches: [main] + branches: [main, development] env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index c2fab4c05..41f501d43 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -1,9 +1,9 @@ name: Build Wheels on: push: - branches: [main] + branches: [main, development] pull_request: - branches: [main] + branches: [main, development] jobs: linux: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 6364ace25..21054c147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Breaking] +### Added +- Extended tp-flash algorithm to static numbers of components and enabled automatic differentiation for binary systems. [#336](https://github.com/feos-org/feos/pull/336) + ### Packaging - Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#323](https://github.com/feos-org/feos/pull/323) diff --git a/crates/feos-core/src/phase_equilibria/mod.rs b/crates/feos-core/src/phase_equilibria/mod.rs index b27d97fbb..9683e49a1 100644 --- a/crates/feos-core/src/phase_equilibria/mod.rs +++ b/crates/feos-core/src/phase_equilibria/mod.rs @@ -3,8 +3,8 @@ use crate::errors::{FeosError, FeosResult}; use crate::state::{DensityInitialization, State}; use crate::{Contributions, ReferenceSystem}; use nalgebra::allocator::Allocator; -use nalgebra::{DVector, DefaultAllocator, Dim, Dyn, OVector}; -use num_dual::{DualNum, DualStruct}; +use nalgebra::{DefaultAllocator, Dim, Dyn, OVector}; +use num_dual::{DualNum, DualStruct, Gradients}; use quantity::{Energy, Moles, Pressure, RGAS, Temperature}; use std::fmt; use std::fmt::Write; @@ -168,7 +168,10 @@ where } } -impl PhaseEquilibrium { +impl, N: Gradients, const P: usize> PhaseEquilibrium +where + DefaultAllocator: Allocator, +{ pub(super) fn update_pressure( mut self, temperature: Temperature, @@ -189,7 +192,7 @@ impl PhaseEquilibrium { pub(super) fn update_moles( &mut self, pressure: Pressure, - moles: [&Moles>; P], + moles: [&Moles>; P], ) -> FeosResult<()> { for (i, s) in self.0.iter_mut().enumerate() { *s = State::new_npt( diff --git a/crates/feos-core/src/phase_equilibria/stability_analysis.rs b/crates/feos-core/src/phase_equilibria/stability_analysis.rs index e9af96e8e..69988f8c2 100644 --- a/crates/feos-core/src/phase_equilibria/stability_analysis.rs +++ b/crates/feos-core/src/phase_equilibria/stability_analysis.rs @@ -3,7 +3,9 @@ use crate::equation_of_state::Residual; use crate::errors::{FeosError, FeosResult}; use crate::state::{Contributions, DensityInitialization, State}; use crate::{ReferenceSystem, SolverOptions, Verbosity}; -use nalgebra::{DMatrix, DVector}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, OMatrix, OVector, U1}; +use num_dual::Gradients; use num_dual::linalg::LU; use num_dual::linalg::smallest_ev; use quantity::Moles; @@ -16,7 +18,10 @@ const MINIMIZE_KMAX: usize = 100; const ZERO_TPD: f64 = -1E-08; /// # Stability analysis -impl State { +impl, N: Gradients> State +where + DefaultAllocator: Allocator + Allocator, +{ /// Determine if the state is stable, i.e. if a phase split should /// occur or not. pub fn is_stable(&self, options: SolverOptions) -> FeosResult { @@ -26,7 +31,7 @@ impl State { /// Perform a stability analysis. The result is a list of [State]s with /// negative tangent plane distance (i.e. lower Gibbs energy) that can be /// used as initial estimates for a phase equilibrium calculation. - pub fn stability_analysis(&self, options: SolverOptions) -> FeosResult>> { + pub fn stability_analysis(&self, options: SolverOptions) -> FeosResult>> { let mut result = Vec::new(); for i_trial in 0..self.eos.components() + 1 { let phase = if i_trial == self.eos.components() { @@ -59,8 +64,9 @@ impl State { Ok(result) } - fn define_trial_state(&self, dominant_component: usize) -> FeosResult> { + fn define_trial_state(&self, dominant_component: usize) -> FeosResult> { let x_feed = &self.molefracs; + let (n, _) = x_feed.shape_generic(); let (x_trial, phase) = if dominant_component == self.eos.components() { // try an ideal vapor phase @@ -70,7 +76,7 @@ impl State { // try each component as nearly pure phase let factor = (1.0 - X_DOMINANT) / (x_feed.sum() - x_feed[dominant_component]); ( - DVector::from_fn(self.eos.components(), |i, _| { + OVector::from_fn_generic(n, U1, |i, _| { if i == dominant_component { X_DOMINANT } else { @@ -92,7 +98,7 @@ impl State { fn minimize_tpd( &self, - trial: &mut State, + trial: &mut State, options: SolverOptions, ) -> FeosResult<(Option, usize)> { let (max_iter, tol, verbosity) = options.unwrap_or(MINIMIZE_KMAX, MINIMIZE_TOL); @@ -154,9 +160,10 @@ impl State { Err(FeosError::NotConverged(String::from("stability analysis"))) } - fn stability_newton_step(&mut self, di: &DVector, tpd: &mut f64) -> FeosResult { + fn stability_newton_step(&mut self, di: &OVector, tpd: &mut f64) -> FeosResult { // save old values let tpd_old = *tpd; + let (n, _) = di.shape_generic(); // calculate residual and ideal hesse matrix let mut hesse = (self.dln_phi_dnj() * Moles::from_reduced(1.0)).into_value(); @@ -166,7 +173,7 @@ impl State { let sq_y = y.map(f64::sqrt); let gradient = (&ln_y + &lnphi - di).component_mul(&sq_y); - let hesse_ig = DMatrix::identity(self.eos.components(), self.eos.components()); + let hesse_ig = OMatrix::identity_generic(n, n); for i in 0..self.eos.components() { hesse.column_mut(i).component_mul_assign(&(sq_y[i] * &sq_y)); if y[i] > f64::EPSILON { @@ -181,7 +188,7 @@ impl State { // ! (3) objective function (tpd) does not descent // !----------------------------------------------------------------------------- let mut adjust_hessian = true; - let mut hessian: DMatrix; + let mut hessian: OMatrix; let mut eta_h = 1.0; while adjust_hessian { diff --git a/crates/feos-core/src/phase_equilibria/tp_flash.rs b/crates/feos-core/src/phase_equilibria/tp_flash.rs index baefea80b..cd9d9bea3 100644 --- a/crates/feos-core/src/phase_equilibria/tp_flash.rs +++ b/crates/feos-core/src/phase_equilibria/tp_flash.rs @@ -2,16 +2,22 @@ use super::PhaseEquilibrium; use crate::equation_of_state::Residual; use crate::errors::{FeosError, FeosResult}; use crate::state::{Contributions, State}; -use crate::{SolverOptions, Verbosity}; -use nalgebra::{DVector, Matrix3, Matrix4xX}; -use num_dual::{Dual, DualNum, first_derivative}; -use quantity::{Dimensionless, Moles, Pressure, Temperature}; +use crate::{ReferenceSystem, SolverOptions, Verbosity}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, Dim, Matrix3, OVector, SVector, U1, U2, vector}; +use num_dual::{ + Dual, Dual2Vec, DualNum, DualStruct, Gradients, first_derivative, implicit_derivative_sp, +}; +use quantity::{Dimensionless, MOL, MolarVolume, Moles, Pressure, Quantity, Temperature}; const MAX_ITER_TP: usize = 400; const TOL_TP: f64 = 1e-8; /// # Flash calculations -impl PhaseEquilibrium { +impl, N: Gradients> PhaseEquilibrium +where + DefaultAllocator: Allocator + Allocator, +{ /// Perform a Tp-flash calculation. If no initial values are /// given, the solution is initialized using a stability analysis. /// @@ -21,8 +27,8 @@ impl PhaseEquilibrium { eos: &E, temperature: Temperature, pressure: Pressure, - feed: &Moles>, - initial_state: Option<&PhaseEquilibrium>, + feed: &Moles>, + initial_state: Option<&PhaseEquilibrium>, options: SolverOptions, non_volatile_components: Option>, ) -> FeosResult { @@ -34,8 +40,76 @@ impl PhaseEquilibrium { } } +impl, D: DualNum + Copy> PhaseEquilibrium { + /// Perform a Tp-flash calculation for a binary mixture. + /// Compared to the version of the algorithm for a generic + /// number of components ([tp_flash](PhaseEquilibrium::tp_flash)), + /// this can be used in combination with automatic differentiation. + pub fn tp_flash_binary( + eos: &E, + temperature: Temperature, + pressure: Pressure, + feed: &Moles>, + options: SolverOptions, + ) -> FeosResult { + let z = feed.get(0).convert_into(feed.get(0) + feed.get(1)); + let total_moles = feed.sum(); + let moles = vector![z.re(), 1.0 - z.re()] * MOL; + let vle_re = State::new_npt(&eos.re(), temperature.re(), pressure.re(), &moles, None)? + .tp_flash(None, options, None)?; + + // implicit differentiation + + // specifications + let t = temperature.into_reduced(); + let p = pressure.into_reduced(); + + // molar volume and composition of the two phases + let variables = SVector::from([ + vle_re.liquid().density.into_reduced().recip(), + vle_re.vapor().density.into_reduced().recip(), + vle_re.liquid().molefracs[0], + vle_re.vapor().molefracs[0], + ]); + + // calculate derivatives for molar volumes and compositions (first component) + // with respect to t, p, or z or equation of state parameters + // using implicit differentiation of the minimum in the Gibbs energy + let [[v_l, v_v, x, y]] = implicit_derivative_sp( + |variables, &[t, p, z]: &[_; 3]| { + let [[v_l, v_v, x, y]] = variables.data.0; + let beta = (z - x) / (y - x); + let eos = eos.lift(); + let molar_gibbs_energy = |x: Dual2Vec<_, _, _>, v| { + let molefracs = vector![x, -x + 1.0]; + let a_res = eos.residual_molar_helmholtz_energy(t, v, &molefracs); + let a_ig = (x * (x / v).ln() - (x - 1.0) * ((-x + 1.0) / v).ln() - 1.0) * t; + a_res + a_ig + v * p + }; + // g = a + pv is the potential function for a tp flash using a Helmholtz energy model + // see https://www.sciencedirect.com/science/article/pii/S0378381299000928 + molar_gibbs_energy(y, v_v) * beta - molar_gibbs_energy(x, v_l) * (beta - 1.0) + }, + variables, + &[t, p, z], + ) + .data + .0; + let beta = (z - x) / (y - x); + let state = |x: D, v, phi| { + let volume = MolarVolume::from_reduced(v * phi) * total_moles; + let moles = Quantity::new(vector![x, -x + 1.0] * phi * total_moles.convert_into(MOL)); + State::new_nvt(eos, temperature, volume, &moles) + }; + Ok(Self([state(y, v_v, beta)?, state(x, v_l, -beta + 1.0)?])) + } +} + /// # Flash calculations -impl State { +impl, N: Gradients> State +where + DefaultAllocator: Allocator + Allocator, +{ /// Perform a Tp-flash calculation using the [State] as feed. /// If no initial values are given, the solution is initialized /// using a stability analysis. @@ -44,10 +118,10 @@ impl State { /// containing non-volatile components (e.g. ions). pub fn tp_flash( &self, - initial_state: Option<&PhaseEquilibrium>, + initial_state: Option<&PhaseEquilibrium>, options: SolverOptions, non_volatile_components: Option>, - ) -> FeosResult> { + ) -> FeosResult> { // initialization if let Some(init) = initial_state { let vle = self.tp_flash_( @@ -76,10 +150,10 @@ impl State { pub fn tp_flash_( &self, - mut new_vle_state: PhaseEquilibrium, + mut new_vle_state: PhaseEquilibrium, options: SolverOptions, non_volatile_components: Option>, - ) -> FeosResult> { + ) -> FeosResult> { // set options let (max_iter, tol, verbosity) = options.unwrap_or(MAX_ITER_TP, TOL_TP); @@ -92,8 +166,8 @@ impl State { verbosity, " {:4} | | {:10.8?} | {:10.8?}", 0, - new_vle_state.vapor().molefracs.data.as_vec(), - new_vle_state.liquid().molefracs.data.as_vec(), + new_vle_state.vapor().molefracs.as_slice(), + new_vle_state.liquid().molefracs.as_slice(), ); let mut iter = 0; @@ -169,7 +243,7 @@ impl State { Ok(new_vle_state) } - fn tangent_plane_distance(&self, trial_state: &State) -> f64 { + fn tangent_plane_distance(&self, trial_state: &State) -> f64 { let ln_phi_z = self.ln_phi(); let ln_phi_w = trial_state.ln_phi(); let z = &self.molefracs; @@ -178,20 +252,23 @@ impl State { } } -impl PhaseEquilibrium { +impl, N: Gradients> PhaseEquilibrium +where + DefaultAllocator: Allocator + Allocator, +{ fn accelerated_successive_substitution( &mut self, - feed_state: &State, + feed_state: &State, iter: &mut usize, max_iter: usize, tol: f64, verbosity: Verbosity, non_volatile_components: &Option>, ) -> FeosResult<()> { + let (n, _) = feed_state.molefracs.shape_generic(); for _ in 0..max_iter { // do 5 successive substitution steps and check for convergence - let mut k_vec = Matrix4xX::zeros(self.vapor().eos.components()); - // let mut k_vec = Array::zeros((4, self.vapor().eos.components())); + let mut k_vec = std::array::repeat(OVector::zeros_generic(n, U1)); if self.successive_substitution( feed_state, 5, @@ -213,16 +290,19 @@ impl PhaseEquilibrium { let gibbs = self.total_gibbs_energy(); // extrapolate K values - let delta_vec = k_vec.rows_range(1..) - k_vec.rows_range(..3); - let delta = Matrix3::from_fn(|i, j| delta_vec.row(i).dot(&delta_vec.row(j))); + let delta_vec = [ + &k_vec[1] - &k_vec[0], + &k_vec[2] - &k_vec[1], + &k_vec[3] - &k_vec[2], + ]; + let delta = Matrix3::from_fn(|i, j| delta_vec[i].dot(&delta_vec[j])); let d = delta[(0, 1)] * delta[(0, 1)] - delta[(0, 0)] * delta[(1, 1)]; let a = (delta[(0, 2)] * delta[(0, 1)] - delta[(1, 2)] * delta[(0, 0)]) / d; let b = (delta[(1, 2)] * delta[(0, 1)] - delta[(0, 2)] * delta[(1, 1)]) / d; - let mut k = (k_vec.row(3) - + ((b * delta_vec.row(1) + (a + b) * delta_vec.row(2)) / (1.0 - a - b))) - .map(f64::exp) - .transpose(); + let mut k = (&k_vec[3] + + ((b * &delta_vec[1] + (a + b) * &delta_vec[2]) / (1.0 - a - b))) + .map(f64::exp); // Set k = 0 for non-volatile components if let Some(nvc) = non_volatile_components.as_ref() { @@ -245,10 +325,10 @@ impl PhaseEquilibrium { #[expect(clippy::too_many_arguments)] fn successive_substitution( &mut self, - feed_state: &State, + feed_state: &State, iterations: usize, iter: &mut usize, - k_vec: &mut Option<&mut Matrix4xX>, + k_vec: &mut Option<&mut [OVector; 4]>, abs_tol: f64, verbosity: Verbosity, non_volatile_components: &Option>, @@ -278,8 +358,8 @@ impl PhaseEquilibrium { " {:4} | {:14.8e} | {:.8?} | {:.8?}", iter, res, - self.vapor().molefracs.data.as_vec(), - self.liquid().molefracs.data.as_vec(), + self.vapor().molefracs.as_slice(), + self.liquid().molefracs.as_slice(), ); if res < abs_tol { return Ok(true); @@ -289,16 +369,13 @@ impl PhaseEquilibrium { if let Some(k_vec) = k_vec && i >= iterations - 3 { - k_vec.set_row( - i + 3 - iterations, - &k.map(|ki| if ki > 0.0 { ki.ln() } else { 0.0 }).transpose(), - ); + k_vec[i + 3 - iterations] = k.map(|ki| if ki > 0.0 { ki.ln() } else { 0.0 }); } } Ok(false) } - fn update_states(&mut self, feed_state: &State, k: &DVector) -> FeosResult<()> { + fn update_states(&mut self, feed_state: &State, k: &OVector) -> FeosResult<()> { // calculate vapor phase fraction using Rachford-Rice algorithm let mut beta = self.vapor_phase_fraction(); beta = rachford_rice(&feed_state.molefracs, k, Some(beta))?; @@ -314,7 +391,7 @@ impl PhaseEquilibrium { Ok(()) } - fn vle_init_stability(feed_state: &State) -> FeosResult<(Self, Option)> { + fn vle_init_stability(feed_state: &State) -> FeosResult<(Self, Option)> { let mut stable_states = feed_state.stability_analysis(SolverOptions::default())?; let state1 = stable_states.pop(); let state2 = stable_states.pop(); @@ -331,7 +408,14 @@ impl PhaseEquilibrium { } } -fn rachford_rice(feed: &DVector, k: &DVector, beta_in: Option) -> FeosResult { +fn rachford_rice( + feed: &OVector, + k: &OVector, + beta_in: Option, +) -> FeosResult +where + DefaultAllocator: Allocator, +{ const MAX_ITER: usize = 10; const ABS_TOL: f64 = 1e-6; diff --git a/crates/feos/src/pcsaft/eos/mod.rs b/crates/feos/src/pcsaft/eos/mod.rs index 6b0aa8782..6514be72d 100644 --- a/crates/feos/src/pcsaft/eos/mod.rs +++ b/crates/feos/src/pcsaft/eos/mod.rs @@ -596,7 +596,7 @@ mod tests_parameter_fit { use super::*; use approx::assert_relative_eq; use feos_core::DensityInitialization::Liquid; - use feos_core::{Contributions, PropertiesAD, ReferenceSystem}; + use feos_core::{Contributions, PropertiesAD, ReferenceSystem, SolverOptions}; use feos_core::{FeosResult, ParametersAD, PhaseEquilibrium, State}; use nalgebra::{U1, U3, U8, vector}; use num_dual::{DualStruct, DualVec, partial}; @@ -1023,4 +1023,60 @@ mod tests_parameter_fit { assert_relative_eq!(grad, dt_h, max_relative = 1e-7); Ok(()) } + + #[test] + fn test_tp_flash() -> FeosResult<()> { + let (pcsaft, _) = pcsaft_binary()?; + let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let temperature = 500.0 * KELVIN; + let pressure = 44.6 * BAR; + let x = vector![0.5, 0.5]; + let vle = PhaseEquilibrium::tp_flash_binary( + &pcsaft_ad, + Temperature::from_inner(&temperature), + Pressure::from_inner(&pressure), + &Moles::from_inner(&(x * MOL)), + SolverOptions { + verbosity: feos_core::Verbosity::Iter, + tol: Some(1e-10), + ..Default::default() + }, + )?; + let beta = vle + .vapor() + .total_moles + .convert_into(vle.vapor().total_moles + vle.liquid().total_moles); + let (beta, [[grad]]) = (beta.re, beta.eps.unwrap_generic(U1, U1).data.0); + + println!("{beta:.5}"); + println!("{grad:.5?}"); + + let (params, mut kij) = pcsaft.0; + let h = 1e-7; + kij += h; + let pcsaft_h = PcSaftBinary::new(params, kij); + let vle = PhaseEquilibrium::tp_flash_binary( + &pcsaft_h, + temperature, + pressure, + &(x * MOL), + SolverOptions { + tol: Some(1e-10), + ..Default::default() + }, + )?; + let beta_h = vle + .vapor() + .total_moles + .convert_into(vle.vapor().total_moles + vle.liquid().total_moles); + let dbeta_h = (beta_h - beta) / h; + println!( + "k_ij: {:11.5} {:11.5} {:.3e}", + dbeta_h, + grad, + ((dbeta_h - grad) / grad).abs() + ); + assert_relative_eq!(grad, dbeta_h, max_relative = 1e-4); + Ok(()) + } } From c1178cc5a1f00906f199b4ec574651b9e4e4eebe Mon Sep 17 00:00:00 2001 From: Philipp Rehner Date: Tue, 27 Jan 2026 15:57:59 +0100 Subject: [PATCH 03/12] Enable AD for pure-component VLEs with given pressure --- CHANGELOG.md | 2 + crates/feos-core/src/ad/mod.rs | 66 ++- .../src/equation_of_state/residual.rs | 36 +- crates/feos-core/src/phase_equilibria/mod.rs | 16 +- .../src/phase_equilibria/vle_pure.rs | 432 ++++++++++-------- .../src/state/residual_properties.rs | 9 - crates/feos/src/pcsaft/eos/mod.rs | 44 ++ py-feos/src/ad/mod.rs | 28 +- py-feos/src/lib.rs | 1 + 9 files changed, 413 insertions(+), 221 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21054c147..8b6ba4c41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Breaking] ### Added - Extended tp-flash algorithm to static numbers of components and enabled automatic differentiation for binary systems. [#336](https://github.com/feos-org/feos/pull/336) +- Rewrote `PhaseEquilibrium::pure_p` to mirror `pure_t` and enable automatic differentiation. [#337](https://github.com/feos-org/feos/pull/337) +- Added `boiling_temperature` to the list of properties for parallel evaluations of gradients. [#337](https://github.com/feos-org/feos/pull/337) ### Packaging - Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#323](https://github.com/feos-org/feos/pull/323) diff --git a/crates/feos-core/src/ad/mod.rs b/crates/feos-core/src/ad/mod.rs index ce92d05d3..308e722d1 100644 --- a/crates/feos-core/src/ad/mod.rs +++ b/crates/feos-core/src/ad/mod.rs @@ -4,7 +4,7 @@ use crate::{FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; use nalgebra::{Const, SVector, U1, U2}; #[cfg(feature = "rayon")] use ndarray::{Array1, Array2, ArrayView2, Zip}; -use num_dual::{Derivative, DualSVec, DualStruct}; +use num_dual::{Derivative, DualNum, DualSVec, DualStruct, first_derivative, partial2}; use quantity::{Density, Pressure, Temperature}; #[cfg(feature = "rayon")] use quantity::{KELVIN, KILO, METER, MOL, PASCAL}; @@ -66,6 +66,50 @@ pub trait PropertiesAD { Ok(Pressure::from_reduced(p)) } + fn boiling_temperature( + &self, + pressure: Pressure, + ) -> FeosResult>> + where + Self: Residual>, + { + let eos_f64 = self.re(); + let (temperature, [vapor_density, liquid_density]) = + PhaseEquilibrium::pure_p(&eos_f64, pressure, None, Default::default())?; + + // implicit differentiation is implemented here instead of just calling pure_t with dual + // numbers, because for the first derivative, we can avoid calculating density derivatives. + let t = temperature.into_reduced(); + let v1 = 1.0 / liquid_density.to_reduced(); + let v2 = 1.0 / vapor_density.to_reduced(); + let p = pressure.into_reduced(); + let t = Gradient::from(t); + let t = t + { + let v1 = Gradient::from(v1); + let v2 = Gradient::from(v2); + let p = Gradient::from(p); + let x = Self::pure_molefracs(); + + let residual_entropy = |v| { + let (a, s) = first_derivative( + partial2( + |t, &v, x| self.lift().residual_molar_helmholtz_energy(t, v, x), + &v, + &x, + ), + t, + ); + (a, -s) + }; + let (a1, s1) = residual_entropy(v1); + let (a2, s2) = residual_entropy(v2); + + let ln_rho = (v1 / v2).ln(); + (p * (v2 - v1) + (a2 - a1 + t * ln_rho)) / (s2 - s1 - ln_rho) + }; + Ok(Temperature::from_reduced(t)) + } + fn equilibrium_liquid_density( &self, temperature: Temperature, @@ -111,6 +155,26 @@ pub trait PropertiesAD { ) } + #[cfg(feature = "rayon")] + fn boiling_temperature_parallel( + parameter_names: [String; P], + parameters: ArrayView2, + input: ArrayView2, + ) -> (Array1, Array2, Array1) + where + Self: ParametersAD<1>, + { + parallelize::<_, Self, _, _>( + parameter_names, + parameters, + input, + |eos: &Self::Lifted>, inp| { + eos.boiling_temperature(inp[0] * PASCAL) + .map(|p| p.convert_into(KELVIN)) + }, + ) + } + #[cfg(feature = "rayon")] fn liquid_density_parallel( parameter_names: [String; P], diff --git a/crates/feos-core/src/equation_of_state/residual.rs b/crates/feos-core/src/equation_of_state/residual.rs index 326aff883..9de69d3db 100644 --- a/crates/feos-core/src/equation_of_state/residual.rs +++ b/crates/feos-core/src/equation_of_state/residual.rs @@ -1,6 +1,9 @@ use crate::{FeosError, FeosResult, ReferenceSystem, state::StateHD}; +use nalgebra::SVector; use nalgebra::{DVector, DefaultAllocator, Dim, Dyn, OMatrix, OVector, U1, allocator::Allocator}; -use num_dual::{DualNum, Gradients, partial, partial2, second_derivative, third_derivative}; +use num_dual::{ + DualNum, Gradients, hessian, partial, partial2, second_derivative, third_derivative, +}; use quantity::ad::first_derivative; use quantity::*; use std::ops::{Deref, Div}; @@ -313,12 +316,41 @@ where molar_volume, ); ( - a * density, + a, -da + temperature * density, molar_volume * molar_volume * d2a + temperature, ) } + /// calculates a_res, p, s_res, dp_drho, dp_dt + fn p_dpdrho_dpdt( + &self, + temperature: D, + density: D, + molefracs: &OVector, + ) -> (D, D, D, D, D) { + let molar_volume = density.recip(); + let (a, da, d2a) = hessian::<_, _, _, nalgebra::U2, _>( + partial( + |vt: SVector<_, 2>, x: &OVector<_, N>| { + let [[v, t]] = vt.data.0; + self.lift().residual_molar_helmholtz_energy(t, v, x) + }, + molefracs, + ), + &SVector::from([molar_volume, temperature]), + ); + let [[da_dv, da_dt]] = da.data.0; + let [[d2a_dv2, d2a_dvdt], _] = d2a.data.0; + ( + a, + -da_dv + temperature * density, + -da_dt, + molar_volume * molar_volume * d2a_dv2 + temperature, + -d2a_dvdt + density, + ) + } + /// calculates p, dp_drho, d2p_drho2 fn p_dpdrho_d2pdrho2( &self, diff --git a/crates/feos-core/src/phase_equilibria/mod.rs b/crates/feos-core/src/phase_equilibria/mod.rs index 9683e49a1..4a10aa0fe 100644 --- a/crates/feos-core/src/phase_equilibria/mod.rs +++ b/crates/feos-core/src/phase_equilibria/mod.rs @@ -1,5 +1,5 @@ use crate::equation_of_state::Residual; -use crate::errors::{FeosError, FeosResult}; +use crate::errors::FeosResult; use crate::state::{DensityInitialization, State}; use crate::{Contributions, ReferenceSystem}; use nalgebra::allocator::Allocator; @@ -44,12 +44,6 @@ pub struct PhaseEquilibrium + C where DefaultAllocator: Allocator; -// impl Clone for PhaseEquilibrium { -// fn clone(&self) -> Self { -// Self(self.0.clone()) -// } -// } - impl fmt::Display for PhaseEquilibrium { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (i, s) in self.0.iter().enumerate() { @@ -224,14 +218,6 @@ impl, N: Dim> PhaseEquilibrium where DefaultAllocator: Allocator, { - pub(super) fn check_trivial_solution(self) -> FeosResult { - if Self::is_trivial_solution(self.vapor(), self.liquid()) { - Err(FeosError::TrivialSolution) - } else { - Ok(self) - } - } - /// Check if the two states form a trivial solution pub fn is_trivial_solution(state1: &State, state2: &State) -> bool { let rho1 = state1.molefracs.clone() * state1.density.into_reduced(); diff --git a/crates/feos-core/src/phase_equilibria/vle_pure.rs b/crates/feos-core/src/phase_equilibria/vle_pure.rs index 5e58601af..e3575c094 100644 --- a/crates/feos-core/src/phase_equilibria/vle_pure.rs +++ b/crates/feos-core/src/phase_equilibria/vle_pure.rs @@ -5,40 +5,37 @@ use crate::errors::{FeosError, FeosResult}; use crate::state::{Contributions, DensityInitialization, State}; use crate::{ReferenceSystem, SolverOptions, TemperatureOrPressure, Verbosity}; use nalgebra::allocator::Allocator; -use nalgebra::{DVector, DefaultAllocator, Dim, dvector}; -use num_dual::{DualNum, DualStruct, Gradients}; -use quantity::{Density, Pressure, RGAS, Temperature}; +use nalgebra::{DVector, DefaultAllocator, Dim, SVector, U1, U2}; +use num_dual::{DualNum, DualStruct, Gradients, gradient, partial}; +use quantity::{Density, Pressure, Temperature}; const SCALE_T_NEW: f64 = 0.7; const MAX_ITER_PURE: usize = 50; const TOL_PURE: f64 = 1e-12; /// # Pure component phase equilibria -impl PhaseEquilibrium { +impl, N: Gradients, D: DualNum + Copy> PhaseEquilibrium +where + DefaultAllocator: Allocator + Allocator + Allocator, +{ /// Calculate a phase equilibrium for a pure component. - pub fn pure( + pub fn pure>( eos: &E, temperature_or_pressure: TP, initial_state: Option<&Self>, options: SolverOptions, ) -> FeosResult { - if let Some(t) = temperature_or_pressure.temperature() { + let (t, rho) = if let Some(t) = temperature_or_pressure.temperature() { let (_, rho) = Self::pure_t(eos, t, initial_state, options)?; - Ok(Self(rho.map(|r| { - State::new_intensive(eos, t, r, &dvector![1.0]).unwrap() - }))) + (t, rho) } else if let Some(p) = temperature_or_pressure.pressure() { - Self::pure_p(eos, p, initial_state, options) + Self::pure_p(eos, p, initial_state, options)? } else { unreachable!() - } + }; + Ok(Self(rho.map(|r| State::new_pure(eos, t, r).unwrap()))) } -} -impl, N: Gradients, D: DualNum + Copy> PhaseEquilibrium -where - DefaultAllocator: Allocator, -{ /// Calculate a phase equilibrium for a pure component /// and given temperature. pub fn pure_t( @@ -89,9 +86,9 @@ where for _ in 0..D::NDERIV { let v_l = liquid_density.recip(); let v_v = vapor_density.recip(); - let (f_l, p_l, dp_l) = eos.p_dpdrho(t, liquid_density, &x); - let (f_v, p_v, dp_v) = eos.p_dpdrho(t, vapor_density, &x); - pressure = -(f_l * v_l - f_v * v_v + t * (v_v / v_l).ln()) / (v_l - v_v); + let (a_l, p_l, dp_l) = eos.p_dpdrho(t, liquid_density, &x); + let (a_v, p_v, dp_v) = eos.p_dpdrho(t, vapor_density, &x); + pressure = -(a_l - a_v + t * (v_v / v_l).ln()) / (v_l - v_v); liquid_density += (pressure - p_l) / dp_l; vapor_density += (pressure - p_v) / dp_v; } @@ -133,15 +130,14 @@ where for i in 1..=max_iter { // calculate properties - let (f_l_res, p_l, p_rho_l) = eos.p_dpdrho(temperature, liquid_density, &x); - let (f_v_res, p_v, p_rho_v) = eos.p_dpdrho(temperature, vapor_density, &x); + let (a_l_res, p_l, p_rho_l) = eos.p_dpdrho(temperature, liquid_density, &x); + let (a_v_res, p_v, p_rho_v) = eos.p_dpdrho(temperature, vapor_density, &x); // Estimate the new pressure let v_v = vapor_density.recip(); let v_l = liquid_density.recip(); let delta_v = v_v - v_l; - let delta_a = - f_v_res * v_v - f_l_res * v_l + temperature * (vapor_density / liquid_density).ln(); + let delta_a = a_v_res - a_l_res + temperature * (vapor_density / liquid_density).ln(); let mut p_new = -delta_a / delta_v; // If the pressure becomes negative, assume the gas phase is ideal. The @@ -238,198 +234,248 @@ where Ok((p, [rho_v, rho_l])) } -impl PhaseEquilibrium { - fn new_pt(eos: &E, temperature: Temperature, pressure: Pressure) -> FeosResult { - let liquid = State::new_xpt( - eos, - temperature, - pressure, - &dvector![1.0], - Some(DensityInitialization::Liquid), - )?; - let vapor = State::new_xpt( - eos, - temperature, - pressure, - &dvector![1.0], - Some(DensityInitialization::Vapor), - )?; - Ok(Self([vapor, liquid])) - } - +impl, N: Gradients, D: DualNum + Copy> PhaseEquilibrium +where + DefaultAllocator: Allocator + Allocator + Allocator, +{ /// Calculate a phase equilibrium for a pure component /// and given pressure. - fn pure_p( + pub fn pure_p( eos: &E, - pressure: Pressure, + pressure: Pressure, initial_state: Option<&Self>, options: SolverOptions, - ) -> FeosResult { - let (max_iter, tol, verbosity) = options.unwrap_or(MAX_ITER_PURE, TOL_PURE); + ) -> FeosResult<(Temperature, [Density; 2])> { + let eos_f64 = eos.re(); + let p = pressure.into_reduced(); // Initialize the phase equilibrium - let mut vle = match initial_state { - Some(init) => init - .clone() - .update_pressure(init.vapor().temperature, pressure)?, - None => PhaseEquilibrium::init_pure_p(eos, pressure)?, + let vle = match initial_state { + Some(init) => ( + init.vapor().temperature.into_reduced().re(), + [ + init.vapor().density.into_reduced().re(), + init.liquid().density.into_reduced().re(), + ], + ), + None => init_pure_p(&eos_f64, pressure.re())?, }; + let (t, [rho_v, rho_l]) = iterate_pure_p(&eos_f64, p.re(), vle, options)?; + // Implicit differentiation + let mut temperature = D::from(t); + let mut vapor_density = D::from(rho_v); + let mut liquid_density = D::from(rho_l); + let x = E::pure_molefracs(); + for _ in 0..D::NDERIV { + let v_l = liquid_density.recip(); + let v_v = vapor_density.recip(); + let (a_l, p_l, s_l, p_rho_l, p_t_l) = + eos.p_dpdrho_dpdt(temperature, liquid_density, &x); + let (a_v, p_v, s_v, p_rho_v, p_t_v) = eos.p_dpdrho_dpdt(temperature, vapor_density, &x); + let ln_rho = (v_l / v_v).ln(); + let delta_t = + (p * (v_v - v_l) + (a_v - a_l + temperature * ln_rho)) / (s_v - s_l - ln_rho); + temperature += delta_t; + liquid_density += (p - p_l - p_t_l * delta_t) / p_rho_l; + vapor_density += (p - p_v - p_t_v * delta_t) / p_rho_v; + } + Ok(( + Temperature::from_reduced(temperature), + [ + Density::from_reduced(vapor_density), + Density::from_reduced(liquid_density), + ], + )) + } +} + +/// Calculate a phase equilibrium for a pure component +/// and given pressure. +fn iterate_pure_p, N: Dim>( + eos: &E, + pressure: f64, + (mut temperature, [mut vapor_density, mut liquid_density]): (f64, [f64; 2]), + options: SolverOptions, +) -> FeosResult<(f64, [f64; 2])> +where + DefaultAllocator: Allocator, +{ + let (max_iter, tol, verbosity) = options.unwrap_or(MAX_ITER_PURE, TOL_PURE); + let x = E::pure_molefracs(); + + log_iter!( + verbosity, + " iter | residual | temperature | liquid density | vapor density " + ); + log_iter!(verbosity, "{:-<89}", ""); + log_iter!( + verbosity, + " {:4} | | {:13.8} | {:12.8} | {:12.8}", + 0, + Temperature::from_reduced(temperature), + Density::from_reduced(liquid_density), + Density::from_reduced(vapor_density) + ); + for i in 1..=max_iter { + // calculate properties + let (a_l_res, p_l, s_l_res, p_rho_l, p_t_l) = + eos.p_dpdrho_dpdt(temperature, liquid_density, &x); + let (a_v_res, p_v, s_v_res, p_rho_v, p_t_v) = + eos.p_dpdrho_dpdt(temperature, vapor_density, &x); + + // calculate the molar volumes + let v_l = liquid_density.recip(); + let v_v = vapor_density.recip(); + + // estimate the temperature steps + let ln_rho = (v_l / v_v).ln(); + let delta_t = (pressure * (v_v - v_l) + (a_v_res - a_l_res + temperature * ln_rho)) + / (s_v_res - s_l_res - ln_rho); + temperature += delta_t; + + // calculate Newton steps for the densities and update state. + let rho_l = liquid_density + (pressure - p_l - p_t_l * delta_t) / p_rho_l; + let rho_v = vapor_density + (pressure - p_v - p_t_v * delta_t) / p_rho_v; + + if rho_l.is_sign_negative() || rho_v.is_sign_negative() || delta_t.abs() > 1.0 { + // if densities are negative or the temperature step is large use density iteration instead + liquid_density = _density_iteration( + eos, + temperature, + pressure, + &x, + DensityInitialization::InitialDensity(liquid_density), + )?; + vapor_density = _density_iteration( + eos, + temperature, + pressure, + &x, + DensityInitialization::InitialDensity(vapor_density), + )?; + } else { + liquid_density = rho_l; + vapor_density = rho_v; + } + + // check for trivial solution + if (vapor_density / liquid_density - 1.0).abs() < TRIVIAL_REL_DEVIATION { + return Err(FeosError::TrivialSolution); + } + + // check for convergence + let res = delta_t.abs(); log_iter!( verbosity, - " iter | residual | temperature | liquid density | vapor density " - ); - log_iter!(verbosity, "{:-<89}", ""); - log_iter!( - verbosity, - " {:4} | | {:13.8} | {:12.8} | {:12.8}", - 0, - vle.vapor().temperature, - vle.liquid().density, - vle.vapor().density + " {:4} | {:14.8e} | {:13.8} | {:12.8} | {:12.8}", + i, + res, + Temperature::from_reduced(temperature), + Density::from_reduced(liquid_density), + Density::from_reduced(vapor_density) ); - for i in 1..=max_iter { - // calculate the pressures and derivatives - let (p_l, p_rho_l) = vle.liquid().p_dpdrho(); - let (p_v, p_rho_v) = vle.vapor().p_dpdrho(); - let p_t_l = vle.liquid().dp_dt(Contributions::Total); - let p_t_v = vle.vapor().dp_dt(Contributions::Total); - - // calculate the residual molar entropies (already cached) - let s_l_res = vle.liquid().residual_molar_entropy(); - let s_v_res = vle.vapor().residual_molar_entropy(); - - // calculate the residual molar Helmholtz energies (already cached) - let a_l_res = vle.liquid().residual_molar_helmholtz_energy(); - let a_v_res = vle.vapor().residual_molar_helmholtz_energy(); - - // calculate the molar volumes - let v_l = 1.0 / vle.liquid().density; - let v_v = 1.0 / vle.vapor().density; - - // estimate the temperature steps - let kt = RGAS * vle.vapor().temperature; - let ln_rho = (v_l / v_v).into_value().ln(); - let delta_t = (pressure * (v_v - v_l) + (a_v_res - a_l_res + kt * ln_rho)) - / (s_v_res - s_l_res - RGAS * ln_rho); - let t_new = vle.vapor().temperature + delta_t; - - // calculate Newton steps for the densities and update state. - let rho_l = vle.liquid().density + (pressure - p_l - p_t_l * delta_t) / p_rho_l; - let rho_v = vle.vapor().density + (pressure - p_v - p_t_v * delta_t) / p_rho_v; - - if rho_l.is_sign_negative() - || rho_v.is_sign_negative() - || delta_t.abs() > Temperature::from_reduced(1.0) - { - // if densities are negative or the temperature step is large use density iteration instead - vle = vle - .update_pressure(t_new, pressure)? - .check_trivial_solution()?; - } else { - // update state - vle = Self([ - State::new_pure(eos, t_new, rho_v)?, - State::new_pure(eos, t_new, rho_l)?, - ]); - } - - // check for convergence - let res = delta_t.abs(); - log_iter!( + if res < temperature * tol { + log_result!( verbosity, - " {:4} | {:14.8e} | {:13.8} | {:12.8} | {:12.8}", - i, - res, - vle.vapor().temperature, - vle.liquid().density, - vle.vapor().density + "PhaseEquilibrium::pure_p: calculation converged in {} step(s)\n", + i ); - if res < vle.vapor().temperature * tol { - log_result!( - verbosity, - "PhaseEquilibrium::pure_p: calculation converged in {} step(s)\n", - i - ); - return Ok(vle); - } + return Ok((temperature, [vapor_density, liquid_density])); } - Err(FeosError::NotConverged("pure_p".to_owned())) } + Err(FeosError::NotConverged("pure_p".to_owned())) +} - /// Initialize a new VLE for a pure substance for a given pressure. - fn init_pure_p(eos: &E, pressure: Pressure) -> FeosResult { - let trial_temperatures = [ - Temperature::from_reduced(300.0), - Temperature::from_reduced(500.0), - Temperature::from_reduced(200.0), - ]; - let x = dvector![1.0]; - let mut vle = None; - let mut t0 = Temperature::from_reduced(1.0); - for t in trial_temperatures.iter() { - t0 = *t; - let _vle = PhaseEquilibrium::new_pt(eos, *t, pressure)?; - if !Self::is_trivial_solution(_vle.vapor(), _vle.liquid()) { - return Ok(_vle); +/// Initialize a new VLE for a pure substance for a given pressure. +fn init_pure_p, N: Gradients>( + eos: &E, + pressure: Pressure, +) -> FeosResult<(f64, [f64; 2])> +where + DefaultAllocator: Allocator + Allocator + Allocator, +{ + let trial_temperatures = [300.0, 500.0, 200.0]; + let p = pressure.into_reduced(); + let x = E::pure_molefracs(); + let mut vle = None; + for t in trial_temperatures { + let liquid_density = _density_iteration(eos, t, p, &x, DensityInitialization::Liquid)?; + let vapor_density = _density_iteration(eos, t, p, &x, DensityInitialization::Vapor)?; + let _vle = (t, [vapor_density, liquid_density]); + if (vapor_density / liquid_density - 1.0).abs() >= TRIVIAL_REL_DEVIATION { + return Ok(_vle); + } + vle = Some(_vle); + } + let Some((t0, [mut rho_v, mut rho_l])) = vle else { + unreachable!() + }; + let [mut t_v, mut t_l] = [t0, t0]; + + let cp = State::critical_point(eos, None, None, None, SolverOptions::default())?; + let cp_density = cp.density.into_reduced(); + if pressure > cp.pressure(Contributions::Total) { + return Err(FeosError::SuperCritical); + }; + + if rho_v < cp_density { + // reduce temperature of liquid phase... + for _ in 0..8 { + t_l *= SCALE_T_NEW; + rho_l = _density_iteration(eos, t_l, p, &x, DensityInitialization::Liquid)?; + if rho_l > cp_density { + break; } - vle = Some(_vle); } - - let cp = State::critical_point(eos, None, None, None, SolverOptions::default())?; - if pressure > cp.pressure(Contributions::Total) { - return Err(FeosError::SuperCritical); - }; - if let Some(mut e) = vle { - if e.vapor().density < cp.density { - for _ in 0..8 { - t0 *= SCALE_T_NEW; - e.0[1] = - State::new_xpt(eos, t0, pressure, &x, Some(DensityInitialization::Liquid))?; - if e.liquid().density > cp.density { - break; - } - } - } else { - for _ in 0..8 { - t0 /= SCALE_T_NEW; - e.0[0] = - State::new_xpt(eos, t0, pressure, &x, Some(DensityInitialization::Vapor))?; - if e.vapor().density < cp.density { - break; - } - } + } else { + // ...or increase temperature of vapor phase + for _ in 0..8 { + t_v /= SCALE_T_NEW; + rho_v = _density_iteration(eos, t_v, p, &x, DensityInitialization::Vapor)?; + if rho_v < cp_density { + break; } + } + } - for _ in 0..20 { - let h = |s: &State<_>| s.residual_enthalpy() + s.total_moles * RGAS * s.temperature; - t0 = (h(e.vapor()) - h(e.liquid())) - / (e.vapor().residual_entropy() - - e.liquid().residual_entropy() - - RGAS - * e.vapor().total_moles - * ((e.vapor().density / e.liquid().density).into_value().ln())); - let trial_state = - State::new_xpt(eos, t0, pressure, &x, Some(DensityInitialization::Vapor))?; - if trial_state.density < cp.density { - e.0[0] = trial_state; - } - let trial_state = - State::new_xpt(eos, t0, pressure, &x, Some(DensityInitialization::Liquid))?; - if trial_state.density > cp.density { - e.0[1] = trial_state; - } - if e.liquid().temperature == e.vapor().temperature { - return Ok(e); - } - } - Err(FeosError::IterationFailed( - "new_init_p: could not find proper initial state".to_owned(), - )) - } else { - unreachable!() + // determine new temperatures and assign them to either the liquid or the vapor phase until + // both phases have the same temperature + for _ in 0..20 { + let h_s = |t, v| { + let (a_res, da_res) = gradient::<_, _, _, U2, _>( + partial( + |t_v: SVector<_, _>, x| { + let [[t, v]] = t_v.data.0; + eos.lift().residual_molar_helmholtz_energy(t, v, x) + }, + &x, + ), + &SVector::from([t, v]), + ); + let [[da_res_dt, da_res_dv]] = da_res.data.0; + (a_res - t * da_res_dt - v * da_res_dv + t, -da_res_dt) + }; + let (h_l, s_l_res) = h_s(t_l, rho_l.recip()); + let (h_v, s_v_res) = h_s(t_v, rho_v.recip()); + let t = (h_v - h_l) / (s_v_res - s_l_res - (rho_v / rho_l).ln()); + let trial_density = _density_iteration(eos, t, p, &x, DensityInitialization::Vapor)?; + if trial_density < cp_density { + rho_v = trial_density; + t_v = t; + } + let trial_density = _density_iteration(eos, t, p, &x, DensityInitialization::Liquid)?; + if trial_density > cp_density { + rho_l = trial_density; + t_l = t; + } + if t_l == t_v { + return Ok((t_l, [rho_v, rho_l])); } } + Err(FeosError::IterationFailed( + "new_init_p: could not find proper initial state".to_owned(), + )) } impl PhaseEquilibrium { @@ -453,7 +499,7 @@ impl PhaseEquilibrium { .map(|i| { let pure_eos = eos.subset(&[i]); PhaseEquilibrium::pure_p(&pure_eos, pressure, None, SolverOptions::default()) - .map(|vle| vle.vapor().temperature) + .map(|(t, _)| t) .ok() }) .collect() diff --git a/crates/feos-core/src/state/residual_properties.rs b/crates/feos-core/src/state/residual_properties.rs index 554facf08..53695fdbe 100644 --- a/crates/feos-core/src/state/residual_properties.rs +++ b/crates/feos-core/src/state/residual_properties.rs @@ -233,15 +233,6 @@ where .into_value() } - // This function is designed specifically for use in density iterations - pub(crate) fn p_dpdrho(&self) -> (Pressure, as Div>>::Output) { - let dp_dv = self.dp_dv(Contributions::Total); - ( - self.pressure(Contributions::Total), - (-self.volume * dp_dv / self.density), - ) - } - /// Partial molar volume: $v_i=\left(\frac{\partial V}{\partial N_i}\right)_{T,p,N_j}$ pub fn partial_molar_volume(&self) -> MolarVolume> { -self.dp_dni(Contributions::Total) / self.dp_dv(Contributions::Total) diff --git a/crates/feos/src/pcsaft/eos/mod.rs b/crates/feos/src/pcsaft/eos/mod.rs index 6514be72d..45d3635da 100644 --- a/crates/feos/src/pcsaft/eos/mod.rs +++ b/crates/feos/src/pcsaft/eos/mod.rs @@ -685,6 +685,50 @@ mod tests_parameter_fit { Ok(()) } + #[test] + fn test_boiling_temperature_derivatives_fit() -> FeosResult<()> { + let pcsaft = pcsaft_non_assoc(); + let pcsaft_ad = pcsaft.named_derivatives(["m", "sigma", "epsilon_k"]); + let pressure = BAR; + let t = pcsaft_ad.boiling_temperature(pressure)?; + let t = t.convert_into(KELVIN); + let (t, grad) = (t.re, t.eps.unwrap_generic(U3, U1)); + + println!("{t:.5}"); + println!("{grad:.5?}"); + + let (t_check, _) = PhaseEquilibrium::pure_p( + &pcsaft_ad, + Pressure::from_inner(&pressure), + None, + Default::default(), + )?; + let t_check = t_check.convert_into(KELVIN); + let (t_check, grad_check) = (t_check.re, t_check.eps.unwrap_generic(U3, U1)); + println!("{t_check:.5}"); + println!("{grad_check:.5?}"); + assert_relative_eq!(t, t_check, max_relative = 1e-15); + assert_relative_eq!(grad, grad_check, max_relative = 1e-15); + + for (i, par) in ["m", "sigma", "epsilon_k"].into_iter().enumerate() { + let mut params = pcsaft.0; + let h = params[i] * 1e-8; + params[i] += h; + let pcsaft_h = PcSaftPure(params); + let (t_h, _) = PhaseEquilibrium::pure_p(&pcsaft_h, pressure, None, Default::default())?; + let dt_h = (t_h.convert_into(KELVIN) - t) / h; + let dt = grad[i]; + println!( + "{par:12}: {:11.5} {:11.5} {:.3e}", + dt_h, + dt, + ((dt_h - dt) / dt).abs() + ); + assert_relative_eq!(dt, dt_h, max_relative = 1e-6); + } + Ok(()) + } + #[test] fn test_equilibrium_liquid_density_derivatives_fit() -> FeosResult<()> { let pcsaft = pcsaft_non_assoc(); diff --git a/py-feos/src/ad/mod.rs b/py-feos/src/ad/mod.rs index 1286f6c14..c18d1eacb 100644 --- a/py-feos/src/ad/mod.rs +++ b/py-feos/src/ad/mod.rs @@ -57,6 +57,32 @@ pub fn vapor_pressure_derivatives<'py>( _vapor_pressure_derivatives(model, parameter_names, parameters, input) } +/// Calculate boiling temperatures and derivatives w.r.t. model parameters. +/// +/// Parameters +/// ---------- +/// model: EquationOfStateAD +/// The equation of state to use. +/// parameter_names: List[string] +/// The name of the parameters for which derivatives are calculated. +/// parameters: np.ndarray[float] +/// The parameters for every data point. +/// input: np.ndarray[float] +/// The pressure (in Pa) for every data point. +/// +/// Returns +/// ------- +/// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The boiling temperature (in K), gradients, and convergence status. +#[pyfunction] +pub fn boiling_temperature_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: Bound<'py, PyAny>, + parameters: PyReadonlyArray2, + input: PyReadonlyArray2, +) -> GradResult<'py> { + _boiling_temperature_derivatives(model, parameter_names, parameters, input) +} + /// Calculate liquid densities and derivatives w.r.t. model parameters. /// /// Parameters @@ -222,7 +248,7 @@ macro_rules! impl_evaluate_gradients { impl_evaluate_gradients!( pure, - [vapor_pressure, liquid_density, equilibrium_liquid_density], + [vapor_pressure, boiling_temperature, liquid_density, equilibrium_liquid_density], {PcSaftNonAssoc: PcSaftPure, PcSaftFull: PcSaftPure} ); diff --git a/py-feos/src/lib.rs b/py-feos/src/lib.rs index 0886c3eb0..948a24d6b 100644 --- a/py-feos/src/lib.rs +++ b/py-feos/src/lib.rs @@ -189,6 +189,7 @@ fn feos(m: &Bound<'_, PyModule>) -> PyResult<()> { #[cfg(feature = "ad")] { m.add_function(wrap_pyfunction!(ad::vapor_pressure_derivatives, m)?)?; + m.add_function(wrap_pyfunction!(ad::boiling_temperature_derivatives, m)?)?; m.add_function(wrap_pyfunction!(ad::liquid_density_derivatives, m)?)?; m.add_function(wrap_pyfunction!( ad::equilibrium_liquid_density_derivatives, From 001c89d9f6b9f24d1bef87974d527c04e8e2653b Mon Sep 17 00:00:00 2001 From: Philipp Rehner Date: Tue, 27 Jan 2026 09:50:59 +0100 Subject: [PATCH 04/12] More rigorous treatment of extensive states --- crates/feos-core/src/ad/mod.rs | 18 +- crates/feos-core/src/cubic.rs | 2 +- crates/feos-core/src/density_iteration.rs | 2 +- crates/feos-core/src/equation_of_state/mod.rs | 17 +- .../src/equation_of_state/residual.rs | 180 +++---- crates/feos-core/src/errors.rs | 2 +- crates/feos-core/src/lib.rs | 32 +- .../src/phase_equilibria/bubble_dew.rs | 61 +-- crates/feos-core/src/phase_equilibria/mod.rs | 76 +-- .../phase_equilibria/phase_diagram_binary.rs | 93 ++-- .../phase_equilibria/phase_diagram_pure.rs | 4 +- .../src/phase_equilibria/phase_envelope.rs | 8 +- .../phase_equilibria/stability_analysis.rs | 10 +- .../src/phase_equilibria/tp_flash.rs | 32 +- .../src/phase_equilibria/vle_pure.rs | 19 +- crates/feos-core/src/state/builder.rs | 251 ---------- crates/feos-core/src/state/cache.rs | 14 +- crates/feos-core/src/state/composition.rs | 304 +++++++++++ crates/feos-core/src/state/critical_point.rs | 41 +- crates/feos-core/src/state/mod.rs | 473 +++++++----------- crates/feos-core/src/state/properties.rs | 106 ++-- .../src/state/residual_properties.rs | 276 +++++----- crates/feos-core/src/state/statevec.rs | 2 +- crates/feos-derive/src/residual.rs | 20 +- crates/feos-dft/src/adsorption/mod.rs | 93 ++-- crates/feos-dft/src/adsorption/pore.rs | 9 +- crates/feos-dft/src/interface/mod.rs | 13 +- crates/feos-dft/src/pdgt.rs | 6 +- crates/feos-dft/src/profile/mod.rs | 13 +- crates/feos-dft/src/profile/properties.rs | 4 +- .../src/solvation/pair_correlation.rs | 6 +- crates/feos/benches/contributions.rs | 2 +- crates/feos/benches/dft_pore.rs | 9 +- crates/feos/benches/dual_numbers.rs | 10 +- crates/feos/benches/dual_numbers_saftvrmie.rs | 10 +- crates/feos/benches/state_creation.rs | 10 +- crates/feos/src/epcsaft/eos/mod.rs | 6 +- crates/feos/src/gc_pcsaft/eos/ad.rs | 2 +- crates/feos/src/ideal_gas/dippr.rs | 20 +- crates/feos/src/ideal_gas/joback.rs | 12 +- crates/feos/src/lib.rs | 2 +- crates/feos/src/multiparameter/mod.rs | 15 +- crates/feos/src/pcsaft/eos/mod.rs | 77 ++- crates/feos/src/pcsaft/eos/pcsaft_binary.rs | 2 +- crates/feos/src/pcsaft/eos/pcsaft_pure.rs | 4 +- crates/feos/src/pets/eos/mod.rs | 2 +- crates/feos/src/uvtheory/eos/mod.rs | 5 +- crates/feos/tests/gc_pcsaft/binary.rs | 4 +- crates/feos/tests/gc_pcsaft/dft.rs | 11 +- crates/feos/tests/pcsaft/critical_point.rs | 8 +- crates/feos/tests/pcsaft/dft.rs | 28 +- crates/feos/tests/pcsaft/properties.rs | 18 +- .../tests/pcsaft/state_creation_mixture.rs | 43 +- .../feos/tests/pcsaft/state_creation_pure.rs | 202 +++----- .../tests/saftvrmie/critical_properties.rs | 2 +- py-feos/src/ad/mod.rs | 14 +- py-feos/src/dft/adsorption/mod.rs | 52 +- py-feos/src/eos/mod.rs | 167 ++++--- py-feos/src/phase_equilibria.rs | 26 +- py-feos/src/state.rs | 116 ++--- 60 files changed, 1436 insertions(+), 1630 deletions(-) delete mode 100644 crates/feos-core/src/state/builder.rs create mode 100644 crates/feos-core/src/state/composition.rs diff --git a/crates/feos-core/src/ad/mod.rs b/crates/feos-core/src/ad/mod.rs index 308e722d1..aae9fc198 100644 --- a/crates/feos-core/src/ad/mod.rs +++ b/crates/feos-core/src/ad/mod.rs @@ -57,8 +57,8 @@ pub trait PropertiesAD { let v2 = Gradient::from(v2); let x = Self::pure_molefracs(); - let a1 = self.residual_molar_helmholtz_energy(t, v1, &x); - let a2 = self.residual_molar_helmholtz_energy(t, v2, &x); + let a1 = self.residual_helmholtz_energy(t, v1, &x); + let a2 = self.residual_helmholtz_energy(t, v2, &x); (a1, a2) }; @@ -93,7 +93,7 @@ pub trait PropertiesAD { let residual_entropy = |v| { let (a, s) = first_derivative( partial2( - |t, &v, x| self.lift().residual_molar_helmholtz_energy(t, v, x), + |t, &v, x| self.lift().residual_helmholtz_energy(t, v, x), &v, &x, ), @@ -248,16 +248,16 @@ pub trait PropertiesAD { let y = y.map(Gradient::from); let x = liquid_molefracs.map(Gradient::from); - let a_v = self.residual_molar_helmholtz_energy(t, v_v, &y); + let a_v = self.residual_helmholtz_energy(t, v_v, &y); let (p_l, mu_res_l, dp_l, dmu_l) = self.dmu_dv(t, v_l, &x); let vi_l = dmu_l / dp_l; let v_l = vi_l.dot(&y); let a_l = (mu_res_l - vi_l * p_l).dot(&y); (a_l, a_v, v_l, v_v) }; - let rho_l = vle.liquid().partial_density.to_reduced(); + let rho_l = vle.liquid().partial_density().to_reduced(); let rho_l = [rho_l[0], rho_l[1]]; - let rho_v = vle.vapor().partial_density.to_reduced(); + let rho_v = vle.vapor().partial_density().to_reduced(); let rho_v = [rho_v[0], rho_v[1]]; let p = -(a_v - a_l + t * (y[0] * (rho_v[0] / rho_l[0]).ln() + y[1] * (rho_v[1] / rho_l[1]).ln() - 1.0)) @@ -298,16 +298,16 @@ pub trait PropertiesAD { let x = x.map(Gradient::from); let y = vapor_molefracs.map(Gradient::from); - let a_l = self.residual_molar_helmholtz_energy(t, v_l, &x); + let a_l = self.residual_helmholtz_energy(t, v_l, &x); let (p_v, mu_res_v, dp_v, dmu_v) = self.dmu_dv(t, v_v, &y); let vi_v = dmu_v / dp_v; let v_v = vi_v.dot(&x); let a_v = (mu_res_v - vi_v * p_v).dot(&x); (a_l, a_v, v_l, v_v) }; - let rho_l = vle.liquid().partial_density.to_reduced(); + let rho_l = vle.liquid().partial_density().to_reduced(); let rho_l = [rho_l[0], rho_l[1]]; - let rho_v = vle.vapor().partial_density.to_reduced(); + let rho_v = vle.vapor().partial_density().to_reduced(); let rho_v = [rho_v[0], rho_v[1]]; let p = -(a_l - a_v + t * (x[0] * (rho_l[0] / rho_v[0]).ln() + x[1] * (rho_l[1] / rho_v[1]).ln() - 1.0)) diff --git a/crates/feos-core/src/cubic.rs b/crates/feos-core/src/cubic.rs index 562422b05..a12f615e5 100644 --- a/crates/feos-core/src/cubic.rs +++ b/crates/feos-core/src/cubic.rs @@ -221,7 +221,7 @@ mod tests { let parameters = PengRobinsonParameters::new_pure(propane)?; let pr = PengRobinson::new(parameters); let options = SolverOptions::new().verbosity(Verbosity::Iter); - let cp = State::critical_point(&&pr, None, None, None, options)?; + let cp = State::critical_point(&&pr, (), None, None, options)?; println!("{} {}", cp.temperature, cp.pressure(Contributions::Total)); assert_relative_eq!(cp.temperature, tc * KELVIN, max_relative = 1e-4); assert_relative_eq!( diff --git a/crates/feos-core/src/density_iteration.rs b/crates/feos-core/src/density_iteration.rs index a54cfd8b9..aaee68ec1 100644 --- a/crates/feos-core/src/density_iteration.rs +++ b/crates/feos-core/src/density_iteration.rs @@ -87,7 +87,7 @@ where let (a_res, da_res) = first_derivative( |molar_volume| { eos.lift() - .residual_molar_helmholtz_energy(t, molar_volume, &x) + .residual_helmholtz_energy(t, molar_volume, &x) }, molar_volume, ); diff --git a/crates/feos-core/src/equation_of_state/mod.rs b/crates/feos-core/src/equation_of_state/mod.rs index fa72f6997..dc849068f 100644 --- a/crates/feos-core/src/equation_of_state/mod.rs +++ b/crates/feos-core/src/equation_of_state/mod.rs @@ -1,9 +1,10 @@ -use crate::{ReferenceSystem, StateHD}; +use crate::ReferenceSystem; +use crate::state::StateHD; use nalgebra::{ Const, DVector, DefaultAllocator, Dim, Dyn, OVector, SVector, U1, allocator::Allocator, }; use num_dual::DualNum; -use quantity::{Energy, MolarEnergy, Moles, Temperature, Volume}; +use quantity::{Dimensionless, MolarEnergy, MolarVolume, Temperature}; use std::ops::Deref; mod residual; @@ -164,17 +165,17 @@ where fn ideal_gas_helmholtz_energy + Copy>( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, - ) -> Energy { + volume: MolarVolume, + moles: &OVector, + ) -> MolarEnergy { let total_moles = moles.sum(); let molefracs = moles / total_moles; - let molar_volume = volume / total_moles; + let molar_volume = volume.into_reduced() / total_moles; MolarEnergy::from_reduced(self.ideal_gas_molar_helmholtz_energy( temperature.into_reduced(), - molar_volume.into_reduced(), + molar_volume, &molefracs, - )) * total_moles + )) * Dimensionless::new(total_moles) } } diff --git a/crates/feos-core/src/equation_of_state/residual.rs b/crates/feos-core/src/equation_of_state/residual.rs index 9de69d3db..af5fc88f6 100644 --- a/crates/feos-core/src/equation_of_state/residual.rs +++ b/crates/feos-core/src/equation_of_state/residual.rs @@ -1,6 +1,7 @@ -use crate::{FeosError, FeosResult, ReferenceSystem, state::StateHD}; -use nalgebra::SVector; -use nalgebra::{DVector, DefaultAllocator, Dim, Dyn, OMatrix, OVector, U1, allocator::Allocator}; +use crate::state::StateHD; +use crate::{Composition, FeosResult, ReferenceSystem}; +use nalgebra::allocator::Allocator; +use nalgebra::{DVector, DefaultAllocator, Dim, Dyn, OMatrix, OVector, SVector, U1, U2}; use num_dual::{ DualNum, Gradients, hessian, partial, partial2, second_derivative, third_derivative, }; @@ -143,74 +144,42 @@ where .fold(D::zero(), |acc, (_, a)| acc + a) } - /// Evaluate the molar Helmholtz energy of each individual contribution - /// and return them together with a string representation of the contribution. - fn molar_helmholtz_energy_contributions( + /// Evaluate the Helmholtz energy of each individual contribution and return them + /// together with a string representation of the contribution. + fn helmholtz_energy_contributions( &self, temperature: D, - molar_volume: D, - molefracs: &OVector, + volume: D, + moles: &OVector, ) -> Vec<(&'static str, D)> { - let state = StateHD::new(temperature, molar_volume, molefracs); + let state = StateHD::new(temperature, volume, moles); self.reduced_helmholtz_energy_density_contributions(&state) .into_iter() - .map(|(n, f)| (n, f * temperature * molar_volume)) + .map(|(n, f)| (n, f * temperature * volume)) .collect() } - /// Evaluate the residual molar Helmholtz energy $a^\mathrm{res}$. - fn residual_molar_helmholtz_energy( - &self, - temperature: D, - molar_volume: D, - molefracs: &OVector, - ) -> D { - let state = StateHD::new(temperature, molar_volume, molefracs); - self.reduced_residual_helmholtz_energy_density(&state) * temperature * molar_volume - } - /// Evaluate the residual Helmholtz energy $A^\mathrm{res}$. fn residual_helmholtz_energy(&self, temperature: D, volume: D, moles: &OVector) -> D { - let state = StateHD::new_density(temperature, &(moles / volume)); + let state = StateHD::new(temperature, volume, moles); self.reduced_residual_helmholtz_energy_density(&state) * temperature * volume } - /// Evaluate the residual Helmholtz energy $A^\mathrm{res}$. - fn residual_helmholtz_energy_unit( + /// Evaluate the residual molar Helmholtz energy $a^\mathrm{res}$. + /// + /// The molefracs are treated as independently variable in order to + /// calculate derivatives like the chemical potential. + fn residual_molar_helmholtz_energy( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, - ) -> Energy { - let temperature = temperature.into_reduced(); - let total_moles = moles.sum(); - let molar_volume = (volume / total_moles).into_reduced(); - let molefracs = moles / total_moles; - let state = StateHD::new(temperature, molar_volume, &molefracs); - Pressure::from_reduced(self.reduced_residual_helmholtz_energy_density(&state) * temperature) - * volume - } - - /// Check if the provided optional molar concentration is consistent with the - /// equation of state. - /// - /// In general, the number of elements in `molefracs` needs to match the number - /// of components of the equation of state. For a pure component, however, - /// no molefracs need to be provided. - fn validate_molefracs(&self, molefracs: &Option>) -> FeosResult> { - let l = molefracs.as_ref().map_or(1, |m| m.len()); - if self.components() == l { - match molefracs { - Some(m) => Ok(m.clone()), - None => Ok(OVector::from_element_generic( - N::from_usize(1), - U1, - D::one(), - )), - } - } else { - Err(FeosError::IncompatibleComponents(self.components(), l)) - } + molar_volume: MolarVolume, + molefracs: &OVector, + ) -> MolarEnergy { + MolarEnergy::from_reduced(self.residual_helmholtz_energy( + temperature.into_reduced(), + molar_volume.into_reduced(), + molefracs, + )) } /// Calculate the maximum density. @@ -219,18 +188,18 @@ where /// equilibria and other iterations. It is not explicitly meant to /// be a mathematical limit for the density (if those exist in the /// equation of state anyways). - fn max_density(&self, molefracs: &Option>) -> FeosResult> { - let x = self.validate_molefracs(molefracs)?; + fn max_density>(&self, composition: X) -> FeosResult> { + let (x, _) = composition.into_molefracs(self)?; Ok(Density::from_reduced(self.compute_max_density(&x))) } /// Calculate the second virial coefficient $B(T)$ - fn second_virial_coefficient( + fn second_virial_coefficient>( &self, temperature: Temperature, - molefracs: &Option>, - ) -> MolarVolume { - let x = self.validate_molefracs(molefracs).unwrap(); + composition: X, + ) -> FeosResult> { + let (x, _) = composition.into_molefracs(self)?; let (_, _, d2f) = second_derivative( partial2( |rho, &t, x| { @@ -244,16 +213,16 @@ where D::from(0.0), ); - Quantity::from_reduced(d2f * 0.5) + Ok(Quantity::from_reduced(d2f * 0.5)) } /// Calculate the third virial coefficient $C(T)$ - fn third_virial_coefficient( + fn third_virial_coefficient>( &self, temperature: Temperature, - molefracs: &Option>, - ) -> Quot, Density> { - let x = self.validate_molefracs(molefracs).unwrap(); + composition: X, + ) -> FeosResult, Density>> { + let (x, _) = composition.into_molefracs(self)?; let (_, _, _, d3f) = third_derivative( partial2( |rho, &t, x| { @@ -267,36 +236,43 @@ where D::from(0.0), ); - Quantity::from_reduced(d3f / 3.0) + Ok(Quantity::from_reduced(d3f / 3.0)) } /// Calculate the temperature derivative of the second virial coefficient $B'(T)$ - fn second_virial_coefficient_temperature_derivative( + fn second_virial_coefficient_temperature_derivative>( &self, temperature: Temperature, - molefracs: &Option>, - ) -> Quot, Temperature> { + composition: X, + ) -> FeosResult, Temperature>> { + let (molefracs, _) = composition.into_molefracs(self)?; let (_, db_dt) = first_derivative( partial( - |t, x| self.lift().second_virial_coefficient(t, x), - molefracs, + |t, x: &OVector<_, _>| self.lift().second_virial_coefficient(t, x).unwrap(), + &molefracs, ), temperature, ); - db_dt + Ok(db_dt) } /// Calculate the temperature derivative of the third virial coefficient $C'(T)$ - fn third_virial_coefficient_temperature_derivative( + #[expect(clippy::type_complexity)] + fn third_virial_coefficient_temperature_derivative>( &self, temperature: Temperature, - molefracs: &Option>, - ) -> Quot, Density>, Temperature> { + composition: X, + ) -> FeosResult, Density>, Temperature>> { + let (molefracs, _) = composition.into_molefracs(self)?; let (_, dc_dt) = first_derivative( - partial(|t, x| self.lift().third_virial_coefficient(t, x), molefracs), + partial( + // TODO: Fallible partial would be nice here... + |t, x: &OVector<_, _>| self.lift().third_virial_coefficient(t, x).unwrap(), + &molefracs, + ), temperature, ); - dc_dt + Ok(dc_dt) } // The following methods are used in phase equilibrium algorithms @@ -306,10 +282,7 @@ where let molar_volume = density.recip(); let (a, da, d2a) = second_derivative( partial2( - |molar_volume, &t, x| { - self.lift() - .residual_molar_helmholtz_energy(t, molar_volume, x) - }, + |molar_volume, &t, x| self.lift().residual_helmholtz_energy(t, molar_volume, x), &temperature, molefracs, ), @@ -330,11 +303,11 @@ where molefracs: &OVector, ) -> (D, D, D, D, D) { let molar_volume = density.recip(); - let (a, da, d2a) = hessian::<_, _, _, nalgebra::U2, _>( + let (a, da, d2a) = hessian::<_, _, _, U2, _>( partial( |vt: SVector<_, 2>, x: &OVector<_, N>| { let [[v, t]] = vt.data.0; - self.lift().residual_molar_helmholtz_energy(t, v, x) + self.lift().residual_helmholtz_energy(t, v, x) }, molefracs, ), @@ -361,10 +334,7 @@ where let molar_volume = density.recip(); let (_, da, d2a, d3a) = third_derivative( partial2( - |molar_volume, &t, x| { - self.lift() - .residual_molar_helmholtz_energy(t, molar_volume, x) - }, + |molar_volume, &t, x| self.lift().residual_helmholtz_energy(t, molar_volume, x), &temperature, molefracs, ), @@ -455,22 +425,22 @@ where fn viscosity_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &OVector, ) -> Viscosity; fn viscosity_correlation(&self, s_res: D, x: &OVector) -> D; fn diffusion_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &OVector, ) -> Diffusivity; fn diffusion_correlation(&self, s_res: D, x: &OVector) -> D; fn thermal_conductivity_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &OVector, ) -> ThermalConductivity; fn thermal_conductivity_correlation(&self, s_res: D, x: &OVector) -> D; } @@ -483,10 +453,11 @@ where fn viscosity_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &OVector, ) -> Viscosity { - self.deref().viscosity_reference(temperature, volume, moles) + self.deref() + .viscosity_reference(temperature, molar_volume, molefracs) } fn viscosity_correlation(&self, s_res: D, x: &OVector) -> D { self.deref().viscosity_correlation(s_res, x) @@ -494,10 +465,11 @@ where fn diffusion_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &OVector, ) -> Diffusivity { - self.deref().diffusion_reference(temperature, volume, moles) + self.deref() + .diffusion_reference(temperature, molar_volume, molefracs) } fn diffusion_correlation(&self, s_res: D, x: &OVector) -> D { self.deref().diffusion_correlation(s_res, x) @@ -505,11 +477,11 @@ where fn thermal_conductivity_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &OVector, ) -> ThermalConductivity { self.deref() - .thermal_conductivity_reference(temperature, volume, moles) + .thermal_conductivity_reference(temperature, molar_volume, molefracs) } fn thermal_conductivity_correlation(&self, s_res: D, x: &OVector) -> D { self.deref().thermal_conductivity_correlation(s_res, x) diff --git a/crates/feos-core/src/errors.rs b/crates/feos-core/src/errors.rs index 80c73b309..d2aecffdb 100644 --- a/crates/feos-core/src/errors.rs +++ b/crates/feos-core/src/errors.rs @@ -22,7 +22,7 @@ pub enum FeosError { IncompatibleComponents(usize, usize), #[error("Invalid state in {0}: {1} = {2}.")] InvalidState(String, String, f64), - #[error("Undetermined state: {0}.")] + #[error("Undetermined state: {0}")] UndeterminedState(String), #[error("System is supercritical.")] SuperCritical, diff --git a/crates/feos-core/src/lib.rs b/crates/feos-core/src/lib.rs index b51ae0653..a2552007d 100644 --- a/crates/feos-core/src/lib.rs +++ b/crates/feos-core/src/lib.rs @@ -41,7 +41,7 @@ pub use errors::{FeosError, FeosResult}; #[cfg(feature = "ndarray")] pub use phase_equilibria::{PhaseDiagram, PhaseDiagramHetero}; pub use phase_equilibria::{PhaseEquilibrium, TemperatureOrPressure}; -pub use state::{Contributions, DensityInitialization, State, StateBuilder, StateHD, StateVec}; +pub use state::{Composition, Contributions, DensityInitialization, State, StateHD, StateVec}; /// Level of detail in the iteration output. #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Default)] @@ -205,7 +205,7 @@ impl< mod tests { use crate::Contributions; use crate::FeosResult; - use crate::StateBuilder; + use crate::State; use crate::cubic::*; use crate::equation_of_state::{EquationOfState, IdealGas}; use crate::parameter::*; @@ -268,20 +268,12 @@ mod tests { let parameters = PengRobinsonParameters::new_pure(propane.clone())?; let residual = PengRobinson::new(parameters); - let sr = StateBuilder::new(&&residual) - .temperature(300.0 * KELVIN) - .pressure(1.0 * BAR) - .total_moles(2.0 * MOL) - .build()?; + let sr = State::new_npt(&&residual, 300.0 * KELVIN, 1.0 * BAR, 2.0 * MOL, None)?; let parameters = PengRobinsonParameters::new_pure(propane.clone())?; let residual = PengRobinson::new(parameters); let eos = EquationOfState::new(vec![NoIdealGas], residual); - let s = StateBuilder::new(&&eos) - .temperature(300.0 * KELVIN) - .pressure(1.0 * BAR) - .total_moles(2.0 * MOL) - .build()?; + let s = State::new_npt(&&eos, 300.0 * KELVIN, 1.0 * BAR, 2.0 * MOL, None)?; // pressure assert_relative_eq!( @@ -348,7 +340,7 @@ mod tests { ); assert_relative_eq!( s.gibbs_energy(Contributions::Residual) - - s.total_moles + - s.total_moles() * RGAS * s.temperature * s.compressibility(Contributions::Total).ln(), @@ -424,13 +416,13 @@ mod tests { max_relative = 1e-15 ); assert_relative_eq!( - s.dp_dni(Contributions::Total), - sr.dp_dni(Contributions::Total), + s.n_dp_dni(Contributions::Total), + sr.n_dp_dni(Contributions::Total), max_relative = 1e-15 ); assert_relative_eq!( - s.dp_dni(Contributions::Residual), - sr.dp_dni(Contributions::Residual), + s.n_dp_dni(Contributions::Residual), + sr.n_dp_dni(Contributions::Residual), max_relative = 1e-15 ); @@ -448,8 +440,8 @@ mod tests { max_relative = 1e-15 ); assert_relative_eq!( - s.dmu_dni(Contributions::Residual), - sr.dmu_dni(Contributions::Residual), + s.n_dmu_dni(Contributions::Residual), + sr.n_dmu_dni(Contributions::Residual), max_relative = 1e-15 ); assert_relative_eq!( @@ -462,7 +454,7 @@ mod tests { assert_relative_eq!(s.ln_phi(), sr.ln_phi(), max_relative = 1e-15); assert_relative_eq!(s.dln_phi_dt(), sr.dln_phi_dt(), max_relative = 1e-15); assert_relative_eq!(s.dln_phi_dp(), sr.dln_phi_dp(), max_relative = 1e-15); - assert_relative_eq!(s.dln_phi_dnj(), sr.dln_phi_dnj(), max_relative = 1e-15); + assert_relative_eq!(s.n_dln_phi_dnj(), sr.n_dln_phi_dnj(), max_relative = 1e-15); assert_relative_eq!( s.thermodynamic_factor(), sr.thermodynamic_factor(), diff --git a/crates/feos-core/src/phase_equilibria/bubble_dew.rs b/crates/feos-core/src/phase_equilibria/bubble_dew.rs index a50cc46a1..91ad57d3d 100644 --- a/crates/feos-core/src/phase_equilibria/bubble_dew.rs +++ b/crates/feos-core/src/phase_equilibria/bubble_dew.rs @@ -11,7 +11,7 @@ use nalgebra::{DMatrix, DVector, DefaultAllocator, Dim, Dyn, OVector, U1}; use ndarray::Array1; use num_dual::linalg::LU; use num_dual::{DualNum, DualStruct, Gradients}; -use quantity::{Density, Dimensionless, Moles, Pressure, Quantity, RGAS, SIUnit, Temperature}; +use quantity::{Density, Dimensionless, Pressure, Quantity, RGAS, SIUnit, Temperature}; const MAX_ITER_INNER: usize = 5; const TOL_INNER: f64 = 1e-9; @@ -333,7 +333,7 @@ where ) }; } - let state1 = State::new_intensive( + let state1 = State::new( eos, Temperature::from_reduced(t), Density::from_reduced(molar_volume.recip()), @@ -341,11 +341,11 @@ where )?; let rho2_total = rho2.sum(); let x2 = rho2 / rho2_total; - let state2 = State::new_intensive( + let state2 = State::new( eos, Temperature::from_reduced(t), Density::from_reduced(rho2_total), - &x2, + x2, )?; Ok(PhaseEquilibrium(if bubble { @@ -578,8 +578,8 @@ where } else { let mut t = temperature.into_reduced(); let mut p = pressure.into_reduced(); - let mut molar_volume = state1.density.into_reduced().recip(); - let mut rho2 = state2.partial_density.to_reduced(); + let mut molar_volume = state1.molar_volume.into_reduced(); + let mut rho2 = state2.partial_density().to_reduced(); let err = if iterate_p { Self::newton_step_t( &state1.eos, @@ -603,8 +603,19 @@ where }; *temperature = Temperature::from_reduced(t); *pressure = Pressure::from_reduced(p); - state1.density = Density::from_reduced(molar_volume.recip()); - state2.partial_density = Density::from_reduced(rho2); + state1 = State::new( + &state1.eos, + *temperature, + Density::from_reduced(molar_volume.recip()), + molefracs_spec, + )?; + let density = rho2.sum(); + state2 = State::new( + &state2.eos, + *temperature, + Density::from_reduced(density), + rho2 / density, + )?; Ok(err) }?; @@ -627,7 +638,7 @@ where ); Ok(( state1.density.into_reduced().recip(), - state2.partial_density.to_reduced(), + state2.partial_density().to_reduced(), )) } else { // not converged, return error @@ -752,7 +763,7 @@ where liquid_molefracs: &OVector, ) -> FeosResult<(Pressure, OVector)> { let density = 0.75 * Density::from_reduced(eos.compute_max_density(liquid_molefracs)); - let liquid = State::new_intensive(eos, temperature, density, liquid_molefracs)?; + let liquid = State::new(eos, temperature, density, liquid_molefracs)?; let v_l = liquid.partial_molar_volume(); let p_l = liquid.pressure(Contributions::Total); let mu_l = liquid.residual_chemical_potential(); @@ -776,7 +787,7 @@ where let mut x = vapor_molefracs.clone(); for _ in 0..5 { let density = Density::from_reduced(0.75 * eos.compute_max_density(&x)); - let liquid = State::new_intensive(eos, temperature, density, &x)?; + let liquid = State::new(eos, temperature, density, x)?; let v_l = liquid.partial_molar_volume(); let p_l = liquid.pressure(Contributions::Total); let mu_l = liquid.residual_chemical_potential(); @@ -804,7 +815,7 @@ where temperature: Temperature, molefracs: &OVector, ) -> FeosResult { - let [sp_v, sp_l] = State::spinodal(eos, temperature, Some(molefracs), Default::default())?; + let [sp_v, sp_l] = State::spinodal(eos, temperature, molefracs, Default::default())?; let pv = sp_v.pressure(Contributions::Total); let pl = sp_l.pressure(Contributions::Total); Ok(0.5 * (Pressure::from_reduced(0.0).max(pl) + pv)) @@ -818,7 +829,7 @@ where vapor_molefracs: Option<&OVector>, ) -> FeosResult<[State; 2]> { let liquid_state = - State::new_xpt(eos, temperature, pressure, liquid_molefracs, Some(Liquid))?; + State::new_npt(eos, temperature, pressure, liquid_molefracs, Some(Liquid))?; let xv = match vapor_molefracs { Some(xv) => xv.clone(), None => liquid_state @@ -826,7 +837,7 @@ where .map(f64::exp) .component_mul(liquid_molefracs), }; - let vapor_state = State::new_xpt(eos, temperature, pressure, &xv, Some(Vapor))?; + let vapor_state = State::new_npt(eos, temperature, pressure, xv, Some(Vapor))?; Ok([liquid_state, vapor_state]) } @@ -837,13 +848,7 @@ where vapor_molefracs: &OVector, liquid_molefracs: Option<&OVector>, ) -> FeosResult<[State; 2]> { - let vapor_state = State::new_npt( - eos, - temperature, - pressure, - &Moles::from_reduced(vapor_molefracs.clone()), - Some(Vapor), - )?; + let vapor_state = State::new_npt(eos, temperature, pressure, vapor_molefracs, Some(Vapor))?; let xl = match liquid_molefracs { Some(xl) => xl.clone(), None => { @@ -851,13 +856,13 @@ where .ln_phi() .map(f64::exp) .component_mul(vapor_molefracs); - let liquid_state = State::new_xpt(eos, temperature, pressure, &xl, Some(Liquid))?; + let liquid_state = State::new_npt(eos, temperature, pressure, xl, Some(Liquid))?; (vapor_state.ln_phi() - liquid_state.ln_phi()) .map(f64::exp) .component_mul(vapor_molefracs) } }; - let liquid_state = State::new_xpt(eos, temperature, pressure, &xl, Some(Liquid))?; + let liquid_state = State::new_npt(eos, temperature, pressure, xl, Some(Liquid))?; Ok([vapor_state, liquid_state]) } @@ -866,20 +871,20 @@ where pressure: Pressure, state1: &mut State, state2: &mut State, - moles_state2: Option<&Moles>>, + molefracs_state2: Option<&OVector>, ) -> FeosResult<()> { *state1 = State::new_npt( &state1.eos, temperature, pressure, - &state1.moles, + &state1.molefracs, Some(InitialDensity(state1.density)), )?; *state2 = State::new_npt( &state2.eos, temperature, pressure, - moles_state2.unwrap_or(&state2.moles), + molefracs_state2.unwrap_or(&state2.molefracs), Some(InitialDensity(state2.density)), )?; Ok(()) @@ -908,11 +913,11 @@ where "", "" ); - *state2 = State::new_xpt( + *state2 = State::new_npt( &state2.eos, state2.temperature, state2.pressure(Contributions::Total), - &x2, + x2, Some(InitialDensity(state2.density)), )?; Ok(err_out) diff --git a/crates/feos-core/src/phase_equilibria/mod.rs b/crates/feos-core/src/phase_equilibria/mod.rs index 4a10aa0fe..95671deb2 100644 --- a/crates/feos-core/src/phase_equilibria/mod.rs +++ b/crates/feos-core/src/phase_equilibria/mod.rs @@ -126,39 +126,41 @@ where Self([vapor, liquid]) } - /// Creates a new PhaseEquilibrium that contains two states at the - /// specified temperature, pressure and molefracs. - /// - /// The constructor can be used in custom phase equilibrium solvers or, - /// e.g., to generate initial guesses for an actual VLE solver. - /// In general, the two states generated are NOT in an equilibrium. - pub fn new_xpt( - eos: &E, - temperature: Temperature, - pressure: Pressure, - vapor_molefracs: &OVector, - liquid_molefracs: &OVector, - ) -> FeosResult { - let liquid = State::new_xpt( - eos, - temperature, - pressure, - liquid_molefracs, - Some(DensityInitialization::Liquid), - )?; - let vapor = State::new_xpt( - eos, - temperature, - pressure, - vapor_molefracs, - Some(DensityInitialization::Vapor), - )?; - Ok(Self([vapor, liquid])) - } - - pub(super) fn vapor_phase_fraction(&self) -> f64 { - (self.vapor().total_moles / (self.vapor().total_moles + self.liquid().total_moles)) - .into_value() + // /// Creates a new PhaseEquilibrium that contains two states at the + // /// specified temperature, pressure and molefracs. + // /// + // /// The constructor can be used in custom phase equilibrium solvers or, + // /// e.g., to generate initial guesses for an actual VLE solver. + // /// In general, the two states generated are NOT in an equilibrium. + // pub fn new_xpt( + // eos: &E, + // temperature: Temperature, + // pressure: Pressure, + // vapor_molefracs: &OVector, + // liquid_molefracs: &OVector, + // ) -> FeosResult { + // let liquid = State::new_xpt( + // eos, + // temperature, + // pressure, + // liquid_molefracs, + // Some(DensityInitialization::Liquid), + // )?; + // let vapor = State::new_xpt( + // eos, + // temperature, + // pressure, + // vapor_molefracs, + // Some(DensityInitialization::Vapor), + // )?; + // Ok(Self([vapor, liquid])) + // } + + pub(super) fn vapor_phase_fraction(&self) -> Option { + self.vapor() + .total_moles + .zip(self.liquid().total_moles) + .map(|(v, l)| (v / (l + v)).into_value()) } } @@ -176,7 +178,7 @@ where &s.eos, temperature, pressure, - &s.moles, + &*s, Some(DensityInitialization::InitialDensity(s.density)), )?; } @@ -203,10 +205,10 @@ where // Total Gibbs energy excluding the constant contribution RT sum_i N_i ln(\Lambda_i^3) pub(super) fn total_gibbs_energy(&self) -> Energy { self.0.iter().fold(Energy::from_reduced(0.0), |acc, s| { - let ln_rho_m1 = s.partial_density.to_reduced().map(|r| r.ln() - 1.0); + let ln_rho_m1 = s.partial_density().to_reduced().map(|r| r.ln() - 1.0); acc + s.residual_helmholtz_energy() - + s.pressure(Contributions::Total) * s.volume - + RGAS * s.temperature * s.total_moles * s.molefracs.dot(&ln_rho_m1) + + s.pressure(Contributions::Total) * s.volume() + + RGAS * s.temperature * s.total_moles() * s.molefracs.dot(&ln_rho_m1) }) } } diff --git a/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs b/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs index 0d576a4c1..d519d3255 100644 --- a/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs +++ b/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs @@ -1,7 +1,7 @@ use super::bubble_dew::TemperatureOrPressure; use super::{PhaseDiagram, PhaseEquilibrium}; use crate::errors::{FeosError, FeosResult}; -use crate::state::{Contributions, DensityInitialization::Vapor, State, StateBuilder}; +use crate::state::{Contributions, DensityInitialization::Vapor, State}; use crate::{ReferenceSystem, Residual, SolverOptions, Subset}; use nalgebra::{DVector, dvector, matrix, stack, vector}; use ndarray::{Array1, s}; @@ -558,21 +558,21 @@ impl PhaseEquilibrium { let p0 = (vle1.vapor().pressure(Contributions::Total) + vle2.vapor().pressure(Contributions::Total)) * 0.5; - let nv0 = (&vle1.vapor().moles + &vle2.vapor().moles) * 0.5; - let mut v = State::new_npt(eos, temperature, p0, &nv0, Some(Vapor))?; + let y0 = (&vle1.vapor().molefracs + &vle2.vapor().molefracs) * 0.5; + let mut v = State::new_npt(eos, temperature, p0, y0, Some(Vapor))?; for _ in 0..options.max_iter.unwrap_or(MAX_ITER_HETERO) { // calculate properties - let dmu_drho_l1 = (l1.dmu_dni(Contributions::Total) * l1.volume).to_reduced(); - let dmu_drho_l2 = (l2.dmu_dni(Contributions::Total) * l2.volume).to_reduced(); - let dmu_drho_v = (v.dmu_dni(Contributions::Total) * v.volume).to_reduced(); - let dp_drho_l1 = (l1.dp_dni(Contributions::Total) * l1.volume) + let dmu_drho_l1 = (l1.n_dmu_dni(Contributions::Total) * l1.molar_volume).to_reduced(); + let dmu_drho_l2 = (l2.n_dmu_dni(Contributions::Total) * l2.molar_volume).to_reduced(); + let dmu_drho_v = (v.n_dmu_dni(Contributions::Total) * v.molar_volume).to_reduced(); + let dp_drho_l1 = (l1.n_dp_dni(Contributions::Total) * l1.molar_volume) .to_reduced() .transpose(); - let dp_drho_l2 = (l2.dp_dni(Contributions::Total) * l2.volume) + let dp_drho_l2 = (l2.n_dp_dni(Contributions::Total) * l2.molar_volume) .to_reduced() .transpose(); - let dp_drho_v = (v.dp_dni(Contributions::Total) * v.volume) + let dp_drho_v = (v.n_dp_dni(Contributions::Total) * v.molar_volume) .to_reduced() .transpose(); let mu_l1_res = l1.residual_chemical_potential().to_reduced(); @@ -585,15 +585,15 @@ impl PhaseEquilibrium { // calculate residual let delta_l1v_mu_ig = (RGAS * v.temperature).to_reduced() * (l1 - .partial_density + .partial_density() .to_reduced() - .component_div(&v.partial_density.to_reduced())) + .component_div(&v.partial_density().to_reduced())) .map(f64::ln); let delta_l2v_mu_ig = (RGAS * v.temperature).to_reduced() * (l2 - .partial_density + .partial_density() .to_reduced() - .component_div(&v.partial_density.to_reduced())) + .component_div(&v.partial_density().to_reduced())) .map(f64::ln); let res = stack![ mu_l1_res - &mu_v_res + delta_l1v_mu_ig; @@ -620,11 +620,11 @@ impl PhaseEquilibrium { // apply Newton step let rho_l1 = - &l1.partial_density - &Density::from_reduced(dx.rows_range(0..2).into_owned()); + &l1.partial_density() - &Density::from_reduced(dx.rows_range(0..2).into_owned()); let rho_l2 = - &l2.partial_density - &Density::from_reduced(dx.rows_range(2..4).into_owned()); + &l2.partial_density() - &Density::from_reduced(dx.rows_range(2..4).into_owned()); let rho_v = - &v.partial_density - &Density::from_reduced(dx.rows_range(4..6).into_owned()); + &v.partial_density() - &Density::from_reduced(dx.rows_range(4..6).into_owned()); // check for negative densities for i in 0..2 { @@ -639,18 +639,9 @@ impl PhaseEquilibrium { } // update states - l1 = StateBuilder::new(eos) - .temperature(temperature) - .partial_density(&rho_l1) - .build()?; - l2 = StateBuilder::new(eos) - .temperature(temperature) - .partial_density(&rho_l2) - .build()?; - v = StateBuilder::new(eos) - .temperature(temperature) - .partial_density(&rho_v) - .build()?; + l1 = State::new_density(eos, temperature, rho_l1)?; + l2 = State::new_density(eos, temperature, rho_l2)?; + v = State::new_density(eos, temperature, rho_v)?; } Err(FeosError::NotConverged(String::from( "PhaseEquilibrium::heteroazeotrope_t", @@ -680,24 +671,24 @@ impl PhaseEquilibrium { let mut l1 = vle1.liquid().clone(); let mut l2 = vle2.liquid().clone(); let t0 = (vle1.vapor().temperature + vle2.vapor().temperature) * 0.5; - let nv0 = (&vle1.vapor().moles + &vle2.vapor().moles) * 0.5; - let mut v = State::new_npt(eos, t0, pressure, &nv0, Some(Vapor))?; + let y0 = (&vle1.vapor().molefracs + &vle2.vapor().molefracs) * 0.5; + let mut v = State::new_npt(eos, t0, pressure, y0, Some(Vapor))?; for _ in 0..options.max_iter.unwrap_or(MAX_ITER_HETERO) { // calculate properties - let dmu_drho_l1 = (l1.dmu_dni(Contributions::Total) * l1.volume).to_reduced(); - let dmu_drho_l2 = (l2.dmu_dni(Contributions::Total) * l2.volume).to_reduced(); - let dmu_drho_v = (v.dmu_dni(Contributions::Total) * v.volume).to_reduced(); + let dmu_drho_l1 = (l1.n_dmu_dni(Contributions::Total) * l1.molar_volume).to_reduced(); + let dmu_drho_l2 = (l2.n_dmu_dni(Contributions::Total) * l2.molar_volume).to_reduced(); + let dmu_drho_v = (v.n_dmu_dni(Contributions::Total) * v.molar_volume).to_reduced(); let dmu_res_dt_l1 = (l1.dmu_res_dt()).to_reduced(); let dmu_res_dt_l2 = (l2.dmu_res_dt()).to_reduced(); let dmu_res_dt_v = (v.dmu_res_dt()).to_reduced(); - let dp_drho_l1 = (l1.dp_dni(Contributions::Total) * l1.volume) + let dp_drho_l1 = (l1.n_dp_dni(Contributions::Total) * l1.molar_volume) .to_reduced() .transpose(); - let dp_drho_l2 = (l2.dp_dni(Contributions::Total) * l2.volume) + let dp_drho_l2 = (l2.n_dp_dni(Contributions::Total) * l2.molar_volume) .to_reduced() .transpose(); - let dp_drho_v = (v.dp_dni(Contributions::Total) * v.volume) + let dp_drho_v = (v.n_dp_dni(Contributions::Total) * v.molar_volume) .to_reduced() .transpose(); let dp_dt_l1 = (l1.dp_dt(Contributions::Total)).to_reduced(); @@ -712,14 +703,14 @@ impl PhaseEquilibrium { // calculate residual let delta_l1v_dmu_ig_dt = l1 - .partial_density + .partial_density() .to_reduced() - .component_div(&v.partial_density.to_reduced()) + .component_div(&v.partial_density().to_reduced()) .map(f64::ln); let delta_l2v_dmu_ig_dt = l2 - .partial_density + .partial_density() .to_reduced() - .component_div(&v.partial_density.to_reduced()) + .component_div(&v.partial_density().to_reduced()) .map(f64::ln); let delta_l1v_mu_ig = (RGAS * v.temperature).to_reduced() * &delta_l1v_dmu_ig_dt; let delta_l2v_mu_ig = (RGAS * v.temperature).to_reduced() * &delta_l2v_dmu_ig_dt; @@ -749,10 +740,11 @@ impl PhaseEquilibrium { // apply Newton step let rho_l1 = - l1.partial_density - Density::from_reduced(dx.rows_range(0..2).into_owned()); + l1.partial_density() - Density::from_reduced(dx.rows_range(0..2).into_owned()); let rho_l2 = - l2.partial_density - Density::from_reduced(dx.rows_range(2..4).into_owned()); - let rho_v = v.partial_density - Density::from_reduced(dx.rows_range(4..6).into_owned()); + l2.partial_density() - Density::from_reduced(dx.rows_range(2..4).into_owned()); + let rho_v = + v.partial_density() - Density::from_reduced(dx.rows_range(4..6).into_owned()); let t = v.temperature - Temperature::from_reduced(dx[6]); // check for negative densities and temperatures @@ -769,18 +761,9 @@ impl PhaseEquilibrium { } // update states - l1 = StateBuilder::new(eos) - .temperature(t) - .partial_density(&rho_l1) - .build()?; - l2 = StateBuilder::new(eos) - .temperature(t) - .partial_density(&rho_l2) - .build()?; - v = StateBuilder::new(eos) - .temperature(t) - .partial_density(&rho_v) - .build()?; + l1 = State::new_density(eos, t, rho_l1)?; + l2 = State::new_density(eos, t, rho_l2)?; + v = State::new_density(eos, t, rho_v)?; } Err(FeosError::NotConverged(String::from( "PhaseEquilibrium::heteroazeotrope_p", diff --git a/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs b/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs index daeb12132..2382a974c 100644 --- a/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs +++ b/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs @@ -37,7 +37,7 @@ impl PhaseDiagram { let sc = State::critical_point( eos, - None, + (), critical_temperature, None, SolverOptions::default(), @@ -104,7 +104,7 @@ impl PhaseDiagram { { let sc = State::critical_point( eos, - None, + (), critical_temperature, None, SolverOptions::default(), diff --git a/crates/feos-core/src/phase_equilibria/phase_envelope.rs b/crates/feos-core/src/phase_equilibria/phase_envelope.rs index 89610e538..0f01e7288 100644 --- a/crates/feos-core/src/phase_equilibria/phase_envelope.rs +++ b/crates/feos-core/src/phase_equilibria/phase_envelope.rs @@ -20,7 +20,7 @@ impl PhaseDiagram { let sc = State::critical_point( eos, - Some(molefracs), + molefracs, critical_temperature, None, SolverOptions::default(), @@ -69,7 +69,7 @@ impl PhaseDiagram { let sc = State::critical_point( eos, - Some(molefracs), + molefracs, critical_temperature, None, SolverOptions::default(), @@ -134,7 +134,7 @@ impl PhaseDiagram { let sc = State::critical_point( eos, - Some(molefracs), + molefracs, critical_temperature, None, SolverOptions::default(), @@ -145,7 +145,7 @@ impl PhaseDiagram { let temperatures = Temperature::linspace(min_temperature, max_temperature, npoints - 1); for ti in &temperatures { - let spinodal = State::spinodal(eos, ti, Some(molefracs), options).ok(); + let spinodal = State::spinodal(eos, ti, molefracs, options).ok(); if let Some(spinodal) = spinodal { states.push(PhaseEquilibrium(spinodal)); } diff --git a/crates/feos-core/src/phase_equilibria/stability_analysis.rs b/crates/feos-core/src/phase_equilibria/stability_analysis.rs index 69988f8c2..950f05026 100644 --- a/crates/feos-core/src/phase_equilibria/stability_analysis.rs +++ b/crates/feos-core/src/phase_equilibria/stability_analysis.rs @@ -91,7 +91,7 @@ where &self.eos, self.temperature, self.pressure(Contributions::Total), - &Moles::from_reduced(x_trial), + Moles::from_reduced(x_trial), Some(phase), ) } @@ -122,7 +122,7 @@ where &trial.eos, trial.temperature, trial.pressure(Contributions::Total), - &Moles::from_reduced(y), + Moles::from_reduced(y), Some(DensityInitialization::InitialDensity(trial.density)), )?; if (i > 4 && error > scaled_tol) || (tpd > tpd_old + 1E-05 && i > 2) { @@ -166,9 +166,9 @@ where let (n, _) = di.shape_generic(); // calculate residual and ideal hesse matrix - let mut hesse = (self.dln_phi_dnj() * Moles::from_reduced(1.0)).into_value(); + let mut hesse = self.n_dln_phi_dnj() / self.total_moles().into_reduced(); let lnphi = self.ln_phi(); - let y = self.moles.to_reduced(); + let y = self.moles().into_reduced(); let ln_y = y.map(|y| if y > f64::EPSILON { y.ln() } else { 0.0 }); let sq_y = y.map(f64::sqrt); let gradient = (&ln_y + &lnphi - di).component_mul(&sq_y); @@ -228,7 +228,7 @@ where &self.eos, self.temperature, self.pressure(Contributions::Total), - &Moles::from_reduced(y), + Moles::from_reduced(y), Some(DensityInitialization::InitialDensity(self.density)), )?; } diff --git a/crates/feos-core/src/phase_equilibria/tp_flash.rs b/crates/feos-core/src/phase_equilibria/tp_flash.rs index cd9d9bea3..e955acaf6 100644 --- a/crates/feos-core/src/phase_equilibria/tp_flash.rs +++ b/crates/feos-core/src/phase_equilibria/tp_flash.rs @@ -55,7 +55,7 @@ impl, D: DualNum + Copy> PhaseEquilibrium { let z = feed.get(0).convert_into(feed.get(0) + feed.get(1)); let total_moles = feed.sum(); let moles = vector![z.re(), 1.0 - z.re()] * MOL; - let vle_re = State::new_npt(&eos.re(), temperature.re(), pressure.re(), &moles, None)? + let vle_re = State::new_npt(&eos.re(), temperature.re(), pressure.re(), moles, None)? .tp_flash(None, options, None)?; // implicit differentiation @@ -82,7 +82,7 @@ impl, D: DualNum + Copy> PhaseEquilibrium { let eos = eos.lift(); let molar_gibbs_energy = |x: Dual2Vec<_, _, _>, v| { let molefracs = vector![x, -x + 1.0]; - let a_res = eos.residual_molar_helmholtz_energy(t, v, &molefracs); + let a_res = eos.residual_helmholtz_energy(t, v, &molefracs); let a_ig = (x * (x / v).ln() - (x - 1.0) * ((-x + 1.0) / v).ln() - 1.0) * t; a_res + a_ig + v * p }; @@ -99,7 +99,7 @@ impl, D: DualNum + Copy> PhaseEquilibrium { let state = |x: D, v, phi| { let volume = MolarVolume::from_reduced(v * phi) * total_moles; let moles = Quantity::new(vector![x, -x + 1.0] * phi * total_moles.convert_into(MOL)); - State::new_nvt(eos, temperature, volume, &moles) + State::new_nvt(eos, temperature, volume, moles) }; Ok(Self([state(y, v_v, beta)?, state(x, v_l, -beta + 1.0)?])) } @@ -184,7 +184,9 @@ where )?; // check convergence - let beta = new_vle_state.vapor_phase_fraction(); + // unwrap is safe here, because after the first successive substitution step the + // phase amounts in new_vle_state are known. + let beta = new_vle_state.vapor_phase_fraction().unwrap(); let tpd = [ self.tangent_plane_distance(new_vle_state.vapor()), self.tangent_plane_distance(new_vle_state.liquid()), @@ -377,16 +379,22 @@ where fn update_states(&mut self, feed_state: &State, k: &OVector) -> FeosResult<()> { // calculate vapor phase fraction using Rachford-Rice algorithm - let mut beta = self.vapor_phase_fraction(); - beta = rachford_rice(&feed_state.molefracs, k, Some(beta))?; + let beta = self.vapor_phase_fraction(); + let beta = rachford_rice(&feed_state.molefracs, k, beta)?; // update VLE - let v = feed_state.moles.clone().component_mul(&Dimensionless::new( - k.map(|k| beta * k / (1.0 - beta + beta * k)), - )); - let l = feed_state.moles.clone().component_mul(&Dimensionless::new( - k.map(|k| (1.0 - beta) / (1.0 - beta + beta * k)), - )); + let v = feed_state + .moles() + .clone() + .component_mul(&Dimensionless::new( + k.map(|k| beta * k / (1.0 - beta + beta * k)), + )); + let l = feed_state + .moles() + .clone() + .component_mul(&Dimensionless::new( + k.map(|k| (1.0 - beta) / (1.0 - beta + beta * k)), + )); self.update_moles(feed_state.pressure(Contributions::Total), [&v, &l])?; Ok(()) } diff --git a/crates/feos-core/src/phase_equilibria/vle_pure.rs b/crates/feos-core/src/phase_equilibria/vle_pure.rs index e3575c094..63756fd5b 100644 --- a/crates/feos-core/src/phase_equilibria/vle_pure.rs +++ b/crates/feos-core/src/phase_equilibria/vle_pure.rs @@ -3,7 +3,7 @@ use crate::density_iteration::{_density_iteration, _pressure_spinodal}; use crate::equation_of_state::{Residual, Subset}; use crate::errors::{FeosError, FeosResult}; use crate::state::{Contributions, DensityInitialization, State}; -use crate::{ReferenceSystem, SolverOptions, TemperatureOrPressure, Verbosity}; +use crate::{Composition, ReferenceSystem, SolverOptions, TemperatureOrPressure, Verbosity}; use nalgebra::allocator::Allocator; use nalgebra::{DVector, DefaultAllocator, Dim, SVector, U1, U2}; use num_dual::{DualNum, DualStruct, Gradients, gradient, partial}; @@ -17,6 +17,7 @@ const TOL_PURE: f64 = 1e-12; impl, N: Gradients, D: DualNum + Copy> PhaseEquilibrium where DefaultAllocator: Allocator + Allocator + Allocator, + (): Composition + Composition, { /// Calculate a phase equilibrium for a pure component. pub fn pure>( @@ -207,7 +208,7 @@ where let x = E::pure_molefracs(); let v = (0.75 * eos.compute_max_density(&x)).recip(); let t = temperature.into_reduced(); - let a_res = eos.residual_molar_helmholtz_energy(t, v, &x); + let a_res = eos.residual_helmholtz_energy(t, v, &x); let p = t / v * (a_res / t - 1.0).exp(); let rho_v = p / t; let rho_l = v.recip(); @@ -237,6 +238,7 @@ where impl, N: Gradients, D: DualNum + Copy> PhaseEquilibrium where DefaultAllocator: Allocator + Allocator + Allocator, + (): Composition, { /// Calculate a phase equilibrium for a pure component /// and given pressure. @@ -394,6 +396,7 @@ fn init_pure_p, N: Gradients>( ) -> FeosResult<(f64, [f64; 2])> where DefaultAllocator: Allocator + Allocator + Allocator, + (): Composition, { let trial_temperatures = [300.0, 500.0, 200.0]; let p = pressure.into_reduced(); @@ -413,7 +416,7 @@ where }; let [mut t_v, mut t_l] = [t0, t0]; - let cp = State::critical_point(eos, None, None, None, SolverOptions::default())?; + let cp = State::critical_point(eos, (), None, None, SolverOptions::default())?; let cp_density = cp.density.into_reduced(); if pressure > cp.pressure(Contributions::Total) { return Err(FeosError::SuperCritical); @@ -447,7 +450,7 @@ where partial( |t_v: SVector<_, _>, x| { let [[t, v]] = t_v.data.0; - eos.lift().residual_molar_helmholtz_energy(t, v, x) + eos.lift().residual_helmholtz_energy(t, v, x) }, &x, ), @@ -525,17 +528,17 @@ impl PhaseEquilibrium { let mut molefracs_liquid = molefracs_vapor.clone(); molefracs_vapor[i] = 1.0; molefracs_liquid[i] = 1.0; - let vapor = State::new_intensive( + let vapor = State::new( eos, vle_pure.vapor().temperature, vle_pure.vapor().density, - &molefracs_vapor, + molefracs_vapor, )?; - let liquid = State::new_intensive( + let liquid = State::new( eos, vle_pure.liquid().temperature, vle_pure.liquid().density, - &molefracs_liquid, + molefracs_liquid, )?; Ok(PhaseEquilibrium::from_states(vapor, liquid)) }) diff --git a/crates/feos-core/src/state/builder.rs b/crates/feos-core/src/state/builder.rs deleted file mode 100644 index c418a7c1f..000000000 --- a/crates/feos-core/src/state/builder.rs +++ /dev/null @@ -1,251 +0,0 @@ -use super::{DensityInitialization, State}; -use crate::Total; -use crate::equation_of_state::Residual; -use crate::errors::FeosResult; -use nalgebra::DVector; -use quantity::*; - -/// A simple tool to construct [State]s with arbitrary input parameters. -/// -/// # Examples -/// ``` -/// # use feos_core::{FeosResult, StateBuilder}; -/// # use feos_core::cubic::{PengRobinson, PengRobinsonParameters}; -/// # use quantity::*; -/// # use nalgebra::dvector; -/// # use approx::assert_relative_eq; -/// # fn main() -> FeosResult<()> { -/// // Create a state for given T,V,N -/// let eos = &PengRobinson::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?); -/// let state = StateBuilder::new(&eos) -/// .temperature(300.0 * KELVIN) -/// .volume(12.5 * METER.powi::<3>()) -/// .moles(&(dvector![2.5] * MOL)) -/// .build()?; -/// assert_eq!(state.density, 0.2 * MOL / METER.powi::<3>()); -/// -/// // For a pure component, the composition does not need to be specified. -/// let eos = &PengRobinson::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?); -/// let state = StateBuilder::new(&eos) -/// .temperature(300.0 * KELVIN) -/// .volume(12.5 * METER.powi::<3>()) -/// .total_moles(2.5 * MOL) -/// .build()?; -/// assert_eq!(state.density, 0.2 * MOL / METER.powi::<3>()); -/// -/// // The state can be constructed without providing any extensive property. -/// let eos = &PengRobinson::new( -/// PengRobinsonParameters::new_simple( -/// &[369.8, 305.4], -/// &[41.9 * 1e5, 48.2 * 1e5], -/// &[0.15, 0.10], -/// &[15.0, 30.0] -/// )? -/// ); -/// let state = StateBuilder::new(&eos) -/// .temperature(300.0 * KELVIN) -/// .partial_density(&(dvector![0.2, 0.6] * MOL / METER.powi::<3>())) -/// .build()?; -/// assert_relative_eq!(state.molefracs, dvector![0.25, 0.75]); -/// assert_relative_eq!(state.density, 0.8 * MOL / METER.powi::<3>()); -/// # Ok(()) -/// # } -/// ``` -#[derive(Clone)] -pub struct StateBuilder<'a, E, const IG: bool> { - eos: &'a E, - temperature: Option, - volume: Option, - density: Option, - partial_density: Option<&'a Density>>, - total_moles: Option, - moles: Option<&'a Moles>>, - molefracs: Option<&'a DVector>, - pressure: Option, - molar_enthalpy: Option, - molar_entropy: Option, - molar_internal_energy: Option, - density_initialization: Option, - initial_temperature: Option, -} - -impl<'a, E: Residual> StateBuilder<'a, E, false> { - /// Create a new `StateBuilder` for the given equation of state. - pub fn new(eos: &'a E) -> Self { - StateBuilder { - eos, - temperature: None, - volume: None, - density: None, - partial_density: None, - total_moles: None, - moles: None, - molefracs: None, - pressure: None, - molar_enthalpy: None, - molar_entropy: None, - molar_internal_energy: None, - density_initialization: None, - initial_temperature: None, - } - } -} - -impl<'a, E: Residual, const IG: bool> StateBuilder<'a, E, IG> { - /// Provide the temperature for the new state. - pub fn temperature(mut self, temperature: Temperature) -> Self { - self.temperature = Some(temperature); - self - } - - /// Provide the volume for the new state. - pub fn volume(mut self, volume: Volume) -> Self { - self.volume = Some(volume); - self - } - - /// Provide the density for the new state. - pub fn density(mut self, density: Density) -> Self { - self.density = Some(density); - self - } - - /// Provide partial densities for the new state. - pub fn partial_density(mut self, partial_density: &'a Density>) -> Self { - self.partial_density = Some(partial_density); - self - } - - /// Provide the total moles for the new state. - pub fn total_moles(mut self, total_moles: Moles) -> Self { - self.total_moles = Some(total_moles); - self - } - - /// Provide the moles for the new state. - pub fn moles(mut self, moles: &'a Moles>) -> Self { - self.moles = Some(moles); - self - } - - /// Provide the molefracs for the new state. - pub fn molefracs(mut self, molefracs: &'a DVector) -> Self { - self.molefracs = Some(molefracs); - self - } - - /// Provide the pressure for the new state. - pub fn pressure(mut self, pressure: Pressure) -> Self { - self.pressure = Some(pressure); - self - } - - /// Specify a vapor state. - pub fn vapor(mut self) -> Self { - self.density_initialization = Some(DensityInitialization::Vapor); - self - } - - /// Specify a liquid state. - pub fn liquid(mut self) -> Self { - self.density_initialization = Some(DensityInitialization::Liquid); - self - } - - /// Provide an initial density used in density iterations. - pub fn initial_density(mut self, initial_density: Density) -> Self { - self.density_initialization = Some(DensityInitialization::InitialDensity(initial_density)); - self - } -} - -impl<'a, E: Total, const IG: bool> StateBuilder<'a, E, IG> { - /// Provide the molar enthalpy for the new state. - pub fn molar_enthalpy(mut self, molar_enthalpy: MolarEnergy) -> StateBuilder<'a, E, true> { - self.molar_enthalpy = Some(molar_enthalpy); - self.convert() - } - - /// Provide the molar entropy for the new state. - pub fn molar_entropy(mut self, molar_entropy: MolarEntropy) -> StateBuilder<'a, E, true> { - self.molar_entropy = Some(molar_entropy); - self.convert() - } - - /// Provide the molar internal energy for the new state. - pub fn molar_internal_energy( - mut self, - molar_internal_energy: MolarEnergy, - ) -> StateBuilder<'a, E, true> { - self.molar_internal_energy = Some(molar_internal_energy); - self.convert() - } - - /// Provide an initial temperature used in the Newton solver. - pub fn initial_temperature( - mut self, - initial_temperature: Temperature, - ) -> StateBuilder<'a, E, true> { - self.initial_temperature = Some(initial_temperature); - self.convert() - } - - fn convert(self) -> StateBuilder<'a, E, true> { - StateBuilder { - eos: self.eos, - temperature: self.temperature, - volume: self.volume, - density: self.density, - partial_density: self.partial_density, - total_moles: self.total_moles, - moles: self.moles, - molefracs: self.molefracs, - pressure: self.pressure, - molar_enthalpy: self.molar_enthalpy, - molar_entropy: self.molar_entropy, - molar_internal_energy: self.molar_internal_energy, - density_initialization: self.density_initialization, - initial_temperature: self.initial_temperature, - } - } -} - -impl StateBuilder<'_, E, false> { - /// Try to build the state with the given inputs. - pub fn build(self) -> FeosResult> { - State::new( - self.eos, - self.temperature, - self.volume, - self.density, - self.partial_density, - self.total_moles, - self.moles, - self.molefracs, - self.pressure, - self.density_initialization, - ) - } -} - -impl StateBuilder<'_, E, true> { - /// Try to build the state with the given inputs. - pub fn build(self) -> FeosResult> { - State::new_full( - self.eos, - self.temperature, - self.volume, - self.density, - self.partial_density, - self.total_moles, - self.moles, - self.molefracs, - self.pressure, - self.molar_enthalpy, - self.molar_entropy, - self.molar_internal_energy, - self.density_initialization, - self.initial_temperature, - ) - } -} diff --git a/crates/feos-core/src/state/cache.rs b/crates/feos-core/src/state/cache.rs index b9a1326f7..cefc798d8 100644 --- a/crates/feos-core/src/state/cache.rs +++ b/crates/feos-core/src/state/cache.rs @@ -12,17 +12,17 @@ pub struct Cache where DefaultAllocator: Allocator, { - pub a: OnceLock>, - pub da_dt: OnceLock>, + pub a: OnceLock>, + pub da_dt: OnceLock>, pub da_dv: OnceLock>, pub da_dn: OnceLock>>, - pub d2a_dt2: OnceLock>>, - pub d2a_dv2: OnceLock>>, + pub d2a_dt2: OnceLock>>, + pub d2a_dv2: OnceLock>>, pub d2a_dtdv: OnceLock>>, pub d2a_dndt: OnceLock, Diff<_MolarEnergy, _Temperature>>>, - pub d2a_dndv: OnceLock, Diff<_MolarEnergy, _Volume>>>, - pub d3a_dt3: OnceLock, _Temperature>>>, - pub d3a_dv3: OnceLock, _Volume>>>, + pub d2a_dndv: OnceLock, Diff<_Energy, _Volume>>>, + pub d3a_dt3: OnceLock, _Temperature>>>, + pub d3a_dv3: OnceLock, _MolarVolume>>>, } impl Cache diff --git a/crates/feos-core/src/state/composition.rs b/crates/feos-core/src/state/composition.rs new file mode 100644 index 000000000..0ad47ac9e --- /dev/null +++ b/crates/feos-core/src/state/composition.rs @@ -0,0 +1,304 @@ +use super::State; +use crate::equation_of_state::Residual; +use crate::{FeosError, FeosResult}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, Dim, Dyn, OVector, U1, U2, dvector, vector}; +use num_dual::{DualNum, DualStruct}; +use quantity::{Density, Moles, Quantity, SIUnit}; + +pub trait Composition + Copy, N: Dim> +where + DefaultAllocator: Allocator, +{ + #[expect(clippy::type_complexity)] + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)>; + fn density(&self) -> Option> { + None + } +} + +pub trait FullComposition + Copy, N: Dim>: Composition +where + DefaultAllocator: Allocator, +{ + fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)>; +} + +// trivial implementations +impl + Copy, N: Dim> Composition for (OVector, Moles) +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + _: &E, + ) -> FeosResult<(OVector, Option>)> { + Ok((self.0, Some(self.1))) + } +} + +impl + Copy, N: Dim> FullComposition for (OVector, Moles) +where + DefaultAllocator: Allocator, +{ + fn into_moles>(self, _: &E) -> FeosResult<(OVector, Moles)> { + Ok((self.0, self.1)) + } +} + +impl + Copy, N: Dim> Composition for (OVector, Option>) +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + _: &E, + ) -> FeosResult<(OVector, Option>)> { + Ok((self.0, self.1)) + } +} + +// copy the composition from a given state +impl + Copy, N: Dim> Composition for &State +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + _: &E1, + ) -> FeosResult<(OVector, Option>)> { + Ok(((self.molefracs.clone()), self.total_moles)) + } +} + +// a pure component needs no specification +impl + Copy> Composition for () { + fn into_molefracs>( + self, + _: &E, + ) -> FeosResult<(OVector, Option>)> { + Ok(((vector![D::one()]), None)) + } +} +impl + Copy> Composition for () { + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)> { + if eos.components() == 1 { + Ok(((dvector![D::one()]), None)) + } else { + Err(FeosError::UndeterminedState( + "The composition needs to be specified for a system with more than one component." + .into(), + )) + } + } +} + +// a binary mixture can be specified by a scalar (x1) +impl + Copy> Composition for D { + fn into_molefracs>( + self, + _: &E, + ) -> FeosResult<(OVector, Option>)> { + Ok(((vector![self, -self + 1.0]), None)) + } +} + +// this cannot be implemented generically for D due to mising specialization +impl Composition for f64 { + fn into_molefracs( + self, + eos: &E, + ) -> FeosResult<(OVector, Option)> { + if eos.components() == 2 { + Ok(((dvector![self, 1.0 - self]), None)) + } else { + Err(FeosError::UndeterminedState(format!( + "A scalar ({}) can only be used to specify a binary mixture!", + self + ))) + } + } +} + +// a pure component can be specified by the total mole number +impl + Copy> Composition for Moles { + fn into_molefracs>( + self, + _: &E, + ) -> FeosResult<(OVector, Option>)> { + Ok(((vector![D::one()]), Some(self))) + } +} + +impl + Copy> FullComposition for Moles { + fn into_moles>(self, _: &E) -> FeosResult<(OVector, Moles)> { + Ok(((vector![D::one()]), self)) + } +} + +impl + Copy> Composition for Moles { + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)> { + if eos.components() == 1 { + Ok(((dvector![D::one()]), Some(self))) + } else { + Err(FeosError::UndeterminedState(format!( + "A single mole number ({}) can only be used to specify a pure component!", + self.re() + ))) + } + } +} + +impl + Copy> FullComposition for Moles { + fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)> { + if eos.components() == 1 { + Ok(((dvector![D::one()]), self)) + } else { + Err(FeosError::UndeterminedState(format!( + "A single mole number ({}) can only be used to specify a pure component!", + self.re() + ))) + } + } +} + +// the mixture can be specified by its molefractions +// +// for a dynamic number of components, it is also possible to specify only the +// N-1 first components +impl + Copy, N: Dim> Composition for OVector +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)> { + (&self).into_molefracs(eos) + } +} + +impl + Copy, N: Dim> Composition for &OVector +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)> { + let sum = self.sum(); + if eos.components() == self.len() { + Ok(((self.clone() / sum), None)) + } else if eos.components() == self.len() + 1 { + let mut x = OVector::zeros_generic(N::from_usize(eos.components()), U1); + for i in 0..self.len() { + x[i] = self[i]; + } + x[self.len()] = -sum + 1.0; + Ok(((x), None)) + } else { + Err(FeosError::UndeterminedState(format!( + "The length of the composition vector ({}) does not match the number of components ({})!", + self.len(), + eos.components() + ))) + } + } +} + +// the mixture can be specified by its moles +impl + Copy, N: Dim> Composition for Moles> +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)> { + (&self).into_molefracs(eos) + } +} + +impl + Copy, N: Dim> FullComposition for Moles> +where + DefaultAllocator: Allocator, +{ + fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)> { + (&self).into_moles(eos) + } +} + +impl + Copy, N: Dim> Composition for &Moles> +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)> { + if eos.components() == self.len() { + let total_moles = self.sum(); + Ok(((self.convert_to(total_moles)), Some(total_moles))) + } else { + Err(FeosError::UndeterminedState(format!( + "The length of the composition vector ({}) does not match the number of components ({})!", + self.len(), + eos.components() + ))) + } + } +} + +impl + Copy, N: Dim> FullComposition for &Moles> +where + DefaultAllocator: Allocator, +{ + fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)> { + if eos.components() == self.len() { + let total_moles = self.sum(); + Ok(((self.convert_to(total_moles)), total_moles)) + } else { + Err(FeosError::UndeterminedState(format!( + "The length of the composition vector ({}) does not match the number of components ({})!", + self.len(), + eos.components() + ))) + } + } +} + +// the mixture can be specified with the partial density +impl + Copy, N: Dim> Composition + for Quantity, SIUnit<0, -3, 0, 0, 0, 1, 0>> +where + DefaultAllocator: Allocator, +{ + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(OVector, Option>)> { + if eos.components() == self.len() { + let density = self.sum(); + Ok(((self.convert_to(density)), None)) + } else { + panic!( + "The length of the composition vector ({}) does not match the number of components ({})!", + self.len(), + eos.components() + ) + } + } + + fn density(&self) -> Option> { + Some(self.sum()) + } +} diff --git a/crates/feos-core/src/state/critical_point.rs b/crates/feos-core/src/state/critical_point.rs index e77b95299..4de9f9562 100644 --- a/crates/feos-core/src/state/critical_point.rs +++ b/crates/feos-core/src/state/critical_point.rs @@ -1,4 +1,4 @@ -use super::{DensityInitialization, State}; +use super::{Composition, DensityInitialization, State}; use crate::equation_of_state::Residual; use crate::errors::{FeosError, FeosResult}; use crate::{ReferenceSystem, SolverOptions, Subset, TemperatureOrPressure, Verbosity}; @@ -31,14 +31,14 @@ impl State { let pure_eos = eos.subset(&[i]); let cp = State::critical_point( &pure_eos, - None, + (), initial_temperature, initial_density, options, )?; let mut molefracs = DVector::zeros(eos.components()); molefracs[i] = 1.0; - State::new_intensive(eos, cp.temperature, cp.density, &molefracs) + State::new(eos, cp.temperature, cp.density, molefracs) }) .collect() } @@ -81,7 +81,7 @@ where ); let density = rho[0] + rho[1]; let molefracs = OVector::from_fn_generic(n, U1, |i, _| rho[i] / density); - Self::new_intensive(eos, t, Density::from_reduced(density), &molefracs) + Self::new(eos, t, Density::from_reduced(density), molefracs) } else if let Some(p) = temperature_or_pressure.pressure() { let x = critical_point_binary_p( &eos_re, @@ -103,22 +103,22 @@ where let density = trho[1] + trho[2]; let molefracs = OVector::from_fn_generic(n, U1, |i, _| trho[i + 1] / density); let t = Temperature::from_reduced(trho[0]); - Self::new_intensive(eos, t, Density::from_reduced(density), &molefracs) + Self::new(eos, t, Density::from_reduced(density), molefracs) } else { unreachable!() } } /// Calculate the critical point of a system for given moles. - pub fn critical_point( + pub fn critical_point>( eos: &E, - molefracs: Option<&OVector>, + composition: X, initial_temperature: Option, initial_density: Option, options: SolverOptions, ) -> FeosResult { let eos_re = eos.re(); - let molefracs = molefracs.map_or_else(E::pure_molefracs, |x| x.clone()); + let (molefracs, total_moles) = composition.into_molefracs(eos)?; let x = &molefracs.map(|x| x.re()); let rho_init = initial_density.map(|r| r.into_reduced()); let trial_temperatures = [300.0, 700.0, 500.0]; @@ -144,11 +144,11 @@ where t_rho[1], &molefracs, ); - Self::new_intensive( + Self::new( eos, Temperature::from_reduced(temperature), Density::from_reduced(density), - &molefracs, + (molefracs, total_moles), ) } } @@ -411,18 +411,18 @@ impl, N: Gradients> State where DefaultAllocator: Allocator + Allocator + Allocator, { - pub fn spinodal( + pub fn spinodal + Clone>( eos: &E, temperature: Temperature, - molefracs: Option<&OVector>, + composition: X, options: SolverOptions, ) -> FeosResult<[Self; 2]> { - let critical_point = Self::critical_point(eos, molefracs, None, None, options)?; - let molefracs = molefracs.map_or_else(E::pure_molefracs, |x| x.clone()); + let critical_point = Self::critical_point(eos, composition, None, None, options)?; + let molefracs = &critical_point.molefracs; let spinodal_vapor = Self::calculate_spinodal( eos, temperature, - &molefracs, + molefracs, DensityInitialization::Vapor, options, )?; @@ -430,7 +430,7 @@ where let spinodal_liquid = Self::calculate_spinodal( eos, temperature, - &molefracs, + molefracs, DensityInitialization::InitialDensity(rho), options, )?; @@ -500,12 +500,7 @@ where "Spinodal calculation converged in {} step(s)\n", i ); - return Self::new_intensive( - eos, - temperature, - Density::from_reduced(rho), - molefracs, - ); + return Self::new(eos, temperature, Density::from_reduced(rho), molefracs); } } Err(FeosError::SuperCritical) @@ -571,7 +566,7 @@ where // calculate pressure let a = partial2( - |v, &t, x| eos.lift().residual_molar_helmholtz_energy(t, v, x), + |v, &t, x| eos.lift().residual_helmholtz_energy(t, v, x), &temperature, &molefracs, ); diff --git a/crates/feos-core/src/state/mod.rs b/crates/feos-core/src/state/mod.rs index 5da7a43c5..ec99d4f19 100644 --- a/crates/feos-core/src/state/mod.rs +++ b/crates/feos-core/src/state/mod.rs @@ -11,19 +11,19 @@ use crate::equation_of_state::Residual; use crate::errors::{FeosError, FeosResult}; use crate::{ReferenceSystem, Total}; use nalgebra::allocator::Allocator; -use nalgebra::{DefaultAllocator, Dim, Dyn, OVector, U1}; +use nalgebra::{DefaultAllocator, Dim, Dyn, OVector}; use num_dual::*; use quantity::*; use std::fmt; use std::ops::Sub; -mod builder; mod cache; +mod composition; mod properties; mod residual_properties; mod statevec; -pub use builder::StateBuilder; pub(crate) use cache::Cache; +pub use composition::{Composition, FullComposition}; pub use statevec::StateVec; /// Possible contributions that can be computed. @@ -70,8 +70,6 @@ where { /// temperature in Kelvin pub temperature: D, - // /// volume in Angstrom^3 - // pub molar_volume: D, /// mole fractions pub molefracs: OVector, /// partial number densities in Angstrom^-3 @@ -82,15 +80,9 @@ impl + Copy> StateHD where DefaultAllocator: Allocator, { - /// Create a new `StateHD` for given temperature, molar volume and composition. - pub fn new(temperature: D, molar_volume: D, molefracs: &OVector) -> Self { - let partial_density = molefracs / molar_volume; - - Self { - temperature, - molefracs: molefracs.clone(), - partial_density, - } + /// Create a new `StateHD` for given temperature, volume and composition. + pub fn new(temperature: D, volume: D, moles: &OVector) -> Self { + Self::new_density(temperature, &(moles / volume)) } /// Create a new `StateHD` for given temperature and partial densities @@ -144,7 +136,7 @@ where /// + [State constructors](#state-constructors) /// + [Stability analysis](#stability-analysis) /// + [Flash calculations](#flash-calculations) -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct State + Copy = f64> where DefaultAllocator: Allocator, @@ -153,14 +145,10 @@ where pub eos: E, /// Temperature $T$ pub temperature: Temperature, - /// Volume $V$ - pub volume: Volume, - /// Mole numbers $N_i$ - pub moles: Moles>, + /// Molar volume $v=\frac{V}{N}$ + pub molar_volume: MolarVolume, /// Total number of moles $N=\sum_iN_i$ - pub total_moles: Moles, - /// Partial densities $\rho_i=\frac{N_i}{V}$ - pub partial_density: Density>, + pub total_moles: Option>, /// Total density $\rho=\frac{N}{V}=\sum_i\rho_i$ pub density: Density, /// Mole fractions $x_i=\frac{N_i}{N}=\frac{\rho_i}{\rho}$ @@ -169,22 +157,38 @@ where cache: Cache, } -impl + Copy> Clone for State +impl + Copy> State where DefaultAllocator: Allocator, { - fn clone(&self) -> Self { - Self { - eos: self.eos.clone(), - temperature: self.temperature, - volume: self.volume, - moles: self.moles.clone(), - total_moles: self.total_moles, - partial_density: self.partial_density.clone(), - density: self.density, - molefracs: self.molefracs.clone(), - cache: self.cache.clone(), - } + /// Set the total amount of substance to the given value. + /// + /// This method does not introduce inconsistencies, because the + /// total moles are the only field that stores information about + /// the size of the state. + pub fn set_total_moles(mut self, total_moles: Moles) -> State { + self.total_moles = Some(total_moles); + self + } + + /// Partial densities $\rho_i=\frac{N_i}{V}$ + pub fn partial_density(&self) -> Density> { + Dimensionless::new(&self.molefracs) * self.density + } + + /// Mole numbers $N_i$ + pub fn moles(&self) -> Moles> { + Dimensionless::new(&self.molefracs) * self.total_moles() + } + + /// Total moles $N=\sum_iN_i$ + pub fn total_moles(&self) -> Moles { + self.total_moles.expect("Extensive properties can only be evaluated for states that are initialized with extensive properties!") + } + + /// Volume $V$ + pub fn volume(&self) -> Volume { + self.molar_volume * self.total_moles() } } @@ -221,83 +225,76 @@ where /// This function will perform a validation of the given properties, i.e. test for signs /// and if values are finite. It will **not** validate physics, i.e. if the resulting /// densities are below the maximum packing fraction. - pub fn new_nvt( + pub fn new_nvt>( eos: &E, temperature: Temperature, volume: Volume, - moles: &Moles>, + composition: X, ) -> FeosResult { - let total_moles = moles.sum(); - let molefracs = (moles / total_moles).into_value(); + let (molefracs, total_moles) = composition.into_moles(eos)?; let density = total_moles / volume; - validate(temperature, density, &molefracs)?; + Self::new(eos, temperature, density, (molefracs, total_moles)) + } - Ok(Self::new_unchecked( - eos, - temperature, - density, - total_moles, - &molefracs, - )) + /// Return a new `State` given a temperature and the partial density of all components. + /// + /// This function will perform a validation of the given properties, i.e. test for signs + /// and if values are finite. It will **not** validate physics, i.e. if the resulting + /// densities are below the maximum packing fraction. + pub fn new_density( + eos: &E, + temperature: Temperature, + partial_density: Density>, + ) -> FeosResult { + let density = partial_density.sum(); + Self::new(eos, temperature, density, partial_density) } - /// Return a new `State` for which the total amount of substance is unspecified. + /// Return a new `State` for a pure component given a temperature and a density. /// - /// Internally the total number of moles will be set to 1 mol. + /// This function will perform a validation of the given properties, i.e. test for signs + /// and if values are finite. It will **not** validate physics, i.e. if the resulting + /// densities are below the maximum packing fraction. + pub fn new_pure(eos: &E, temperature: Temperature, density: Density) -> FeosResult + where + (): Composition, + { + Self::new(eos, temperature, density, ()) + } + + /// Return a new `State` given a temperature, a density and the composition. /// /// This function will perform a validation of the given properties, i.e. test for signs /// and if values are finite. It will **not** validate physics, i.e. if the resulting /// densities are below the maximum packing fraction. - pub fn new_intensive( + pub fn new>( eos: &E, temperature: Temperature, density: Density, - molefracs: &OVector, + composition: X, ) -> FeosResult { - validate(temperature, density, molefracs)?; - let total_moles = Moles::new(D::one()); - Ok(Self::new_unchecked( - eos, - temperature, - density, - total_moles, - molefracs, - )) + let (molefracs, total_moles) = composition.into_molefracs(eos)?; + Self::_new(eos, temperature, density, molefracs, total_moles) } - fn new_unchecked( + fn _new( eos: &E, temperature: Temperature, density: Density, - total_moles: Moles, - molefracs: &OVector, - ) -> Self { - let volume = total_moles / density; - let moles = Dimensionless::new(molefracs.clone()) * total_moles; - let partial_density = moles.clone() / volume; - - State { + molefracs: OVector, + total_moles: Option>, + ) -> FeosResult { + let molar_volume = density.inv(); + validate(temperature, density, &molefracs)?; + Ok(State { eos: eos.clone(), temperature, - volume, - moles, - total_moles, - partial_density, + molar_volume, density, - molefracs: molefracs.clone(), + molefracs, + total_moles, cache: Cache::new(), - } - } - - /// Return a new `State` for a pure component given a temperature and a density. The moles - /// are set to the reference value for each component. - /// - /// This function will perform a validation of the given properties, i.e. test for signs - /// and if values are finite. It will **not** validate physics, i.e. if the resulting - /// densities are below the maximum packing fraction. - pub fn new_pure(eos: &E, temperature: Temperature, density: Density) -> FeosResult { - let molefracs = OVector::from_element_generic(N::from_usize(1), U1, D::one()); - Self::new_intensive(eos, temperature, density, &molefracs) + }) } /// Return a new `State` for the combination of inputs. @@ -313,200 +310,105 @@ where /// # Errors /// /// When the state cannot be created using the combination of inputs. - #[expect(clippy::too_many_arguments)] - pub fn new( + pub fn build>( eos: &E, - temperature: Option>, + temperature: Temperature, volume: Option>, density: Option>, - partial_density: Option<&Density>>, - total_moles: Option>, - moles: Option<&Moles>>, - molefracs: Option<&OVector>, + composition: X, pressure: Option>, density_initialization: Option, ) -> FeosResult { - Self::_new( + Self::_build( eos, temperature, volume, density, - partial_density, - total_moles, - moles, - molefracs, + composition, pressure, density_initialization, )? - .map_err(|_| FeosError::UndeterminedState(String::from("Missing input parameters."))) + .ok_or_else(|| FeosError::UndeterminedState(String::from("Missing input parameters."))) } - #[expect(clippy::too_many_arguments)] - #[expect(clippy::type_complexity)] - fn _new( + fn _build>( eos: &E, - temperature: Option>, + temperature: Temperature, volume: Option>, density: Option>, - partial_density: Option<&Density>>, - total_moles: Option>, - moles: Option<&Moles>>, - molefracs: Option<&OVector>, + composition: X, pressure: Option>, density_initialization: Option, - ) -> FeosResult>>>> { - // check for density - if density.and(partial_density).is_some() { + ) -> FeosResult> { + // check if density is given twice + if density.and(composition.density()).is_some() { return Err(FeosError::UndeterminedState(String::from( "Both density and partial density given.", ))); } - let rho = density.or_else(|| partial_density.map(|pd| pd.sum())); - - // check for total moles - if moles.and(total_moles).is_some() { - return Err(FeosError::UndeterminedState(String::from( - "Both moles and total moles given.", - ))); - } - let mut n = total_moles.or_else(|| moles.map(|m| m.sum())); - - // check if total moles can be inferred from volume - if rho.and(n).and(volume).is_some() { - return Err(FeosError::UndeterminedState(String::from( + let density = density.or_else(|| composition.density()); + + // unwrap composition + let (x, n) = composition.into_molefracs(eos)?; + + let t = temperature; + let di = density_initialization; + // find the appropriate state constructor + match (volume, density, n, pressure) { + (None, None, None, None) => Ok(None), + (None, None, Some(_), None) => Ok(None), + (Some(_), None, None, None) => Ok(None), + (None, None, _, Some(p)) => State::new_npt(eos, t, p, (x, n), di).map(Some), + (None, Some(d), _, None) => State::new(eos, t, d, (x, n)).map(Some), + (Some(v), None, None, Some(p)) => State::new_tpvx(eos, t, p, v, x, di).map(Some), + (Some(v), None, Some(n), None) => State::new_nvt(eos, t, v, (x, n)).map(Some), + (Some(v), Some(d), None, None) => State::new_nvt(eos, t, v, (x, d * v)).map(Some), + (Some(_), Some(_), Some(_), _) => Err(FeosError::UndeterminedState(String::from( "Density is overdetermined.", - ))); + ))), + (_, _, _, Some(_)) => Err(FeosError::UndeterminedState(String::from( + "Pressure is overdetermined.", + ))), } - n = n.or_else(|| rho.and_then(|d| volume.map(|v| v * d))); - - // check for composition - if partial_density.and(moles).is_some() { - return Err(FeosError::UndeterminedState(String::from( - "Composition is overdetermined.", - ))); - } - let x = partial_density - .map(|pd| pd / pd.sum()) - .or_else(|| moles.map(|ms| ms / ms.sum())) - .map(Quantity::into_value); - let x_u = match (x, molefracs, eos.components()) { - (Some(_), Some(_), _) => { - return Err(FeosError::UndeterminedState(String::from( - "Composition is overdetermined.", - ))); - } - (Some(x), None, _) => x, - (None, Some(x), _) => x.clone(), - (None, None, 1) => OVector::from_element_generic(N::from_usize(1), U1, D::from(1.0)), - _ => { - return Err(FeosError::UndeterminedState(String::from( - "Missing composition.", - ))); - } - }; - let x_u = &x_u / x_u.sum(); - - // If no extensive property is given, moles is set to the reference value. - if let (None, None) = (volume, n) { - n = Some(Moles::from_reduced(D::from(1.0))) - } - let n_i = n.map(|n| Dimensionless::new(&x_u) * n); - let v = volume.or_else(|| rho.and_then(|d| n.map(|n| n / d))); - - // check if new state can be created using default constructor - if let (Some(v), Some(t), Some(n_i)) = (v, temperature, &n_i) { - return Ok(Ok(State::new_nvt(eos, t, v, n_i)?)); - } - - // Check if new state can be created using density iteration - if let (Some(p), Some(t), Some(n_i)) = (pressure, temperature, &n_i) { - return Ok(Ok(State::new_npt(eos, t, p, n_i, density_initialization)?)); - } - if let (Some(p), Some(t), Some(v)) = (pressure, temperature, v) { - return Ok(Ok(State::new_npvx( - eos, - t, - p, - v, - &x_u, - density_initialization, - )?)); - } - Ok(Err(n_i.to_owned())) } /// Return a new `State` using a density iteration. [DensityInitialization] is used to /// influence the calculation with respect to the possible solutions. - pub fn new_npt( + pub fn new_npt>( eos: &E, temperature: Temperature, pressure: Pressure, - moles: &Moles>, - density_initialization: Option, - ) -> FeosResult { - let total_moles = moles.sum(); - let molefracs = (moles / total_moles).into_value(); - let density = Self::new_xpt( - eos, - temperature, - pressure, - &molefracs, - density_initialization, - )? - .density; - Ok(Self::new_unchecked( - eos, - temperature, - density, - total_moles, - &molefracs, - )) - } - - /// Return a new `State` using a density iteration. [DensityInitialization] is used to - /// influence the calculation with respect to the possible solutions. - pub fn new_xpt( - eos: &E, - temperature: Temperature, - pressure: Pressure, - molefracs: &OVector, + composition: X, density_initialization: Option, ) -> FeosResult { + let (molefracs, total_moles) = composition.into_molefracs(eos)?; density_iteration( eos, temperature, pressure, - molefracs, + &molefracs, density_initialization, ) - .and_then(|density| Self::new_intensive(eos, temperature, density, molefracs)) + .and_then(|density| Self::_new(eos, temperature, density, molefracs, total_moles)) } /// Return a new `State` for given pressure $p$, volume $V$, temperature $T$ and composition $x_i$. - pub fn new_npvx( + pub fn new_tpvx( eos: &E, temperature: Temperature, pressure: Pressure, volume: Volume, - molefracs: &OVector, + molefracs: OVector, density_initialization: Option, ) -> FeosResult { - let density = Self::new_xpt( + let density = density_iteration( eos, temperature, pressure, - molefracs, + &molefracs, density_initialization, - )? - .density; - let total_moles = density * volume; - Ok(Self::new_unchecked( - eos, - temperature, - density, - total_moles, - molefracs, - )) + )?; + Self::new_nvt(eos, temperature, volume, (molefracs, density * volume)) } } @@ -529,15 +431,12 @@ where /// /// When the state cannot be created using the combination of inputs. #[expect(clippy::too_many_arguments)] - pub fn new_full( + pub fn build_full + Clone>( eos: &E, temperature: Option>, volume: Option>, density: Option>, - partial_density: Option<&Density>>, - total_moles: Option>, - moles: Option<&Moles>>, - molefracs: Option<&OVector>, + composition: X, pressure: Option>, molar_enthalpy: Option>, molar_entropy: Option>, @@ -545,38 +444,42 @@ where density_initialization: Option, initial_temperature: Option>, ) -> FeosResult { - let state = Self::_new( - eos, - temperature, - volume, - density, - partial_density, - total_moles, - moles, - molefracs, - pressure, - density_initialization, - )?; + let state = if let Some(temperature) = temperature { + Self::_build( + eos, + temperature, + volume, + density, + composition.clone(), + pressure, + density_initialization, + )? + } else { + None + }; let ti = initial_temperature; match state { - Ok(state) => Ok(state), - Err(n_i) => { + Some(state) => Ok(state), + None => { // Check if new state can be created using molar_enthalpy and temperature - if let (Some(p), Some(h), Some(n_i)) = (pressure, molar_enthalpy, &n_i) { - return State::new_nph(eos, p, h, n_i, density_initialization, ti); + if let (Some(p), Some(h)) = (pressure, molar_enthalpy) { + return State::new_nph(eos, p, h, composition, density_initialization, ti); } - if let (Some(p), Some(s), Some(n_i)) = (pressure, molar_entropy, &n_i) { - return State::new_nps(eos, p, s, n_i, density_initialization, ti); + if let (Some(p), Some(s)) = (pressure, molar_entropy) { + return State::new_nps(eos, p, s, composition, density_initialization, ti); } - if let (Some(t), Some(h), Some(n_i)) = (temperature, molar_enthalpy, &n_i) { - return State::new_nth(eos, t, h, n_i, density_initialization); + if let (Some(t), Some(h)) = (temperature, molar_enthalpy) { + return State::new_nth(eos, t, h, composition, density_initialization); } - if let (Some(t), Some(s), Some(n_i)) = (temperature, molar_entropy, &n_i) { - return State::new_nts(eos, t, s, n_i, density_initialization); + if let (Some(t), Some(s)) = (temperature, molar_entropy) { + return State::new_nts(eos, t, s, composition, density_initialization); } - if let (Some(u), Some(v), Some(n_i)) = (molar_internal_energy, volume, &n_i) { - return State::new_nvu(eos, v, u, n_i, ti); + if let (Some(u), Some(v)) = (molar_internal_energy, volume) { + let (molefracs, total_moles) = composition.into_molefracs(eos)?; + if let Some(n) = total_moles { + return State::new_nvu(eos, v, u, (molefracs, n), ti); + } } Err(FeosError::UndeterminedState(String::from( "Missing input parameters.", @@ -586,18 +489,18 @@ where } /// Return a new `State` for given pressure $p$ and molar enthalpy $h$. - pub fn new_nph( + pub fn new_nph + Clone>( eos: &E, pressure: Pressure, molar_enthalpy: MolarEnergy, - moles: &Moles>, + composition: X, density_initialization: Option, initial_temperature: Option>, ) -> FeosResult { let t0 = initial_temperature.unwrap_or(Temperature::from_reduced(D::from(298.15))); let mut density = density_initialization; let f = |x0| { - let s = State::new_npt(eos, x0, pressure, moles, density)?; + let s = State::new_npt(eos, x0, pressure, composition.clone(), density)?; let dfx = s.molar_isobaric_heat_capacity(Contributions::Total); let fx = s.molar_enthalpy(Contributions::Total) - molar_enthalpy; density = Some(DensityInitialization::InitialDensity(s.density.re())); @@ -607,28 +510,27 @@ where } /// Return a new `State` for given temperature $T$ and molar enthalpy $h$. - pub fn new_nth( + pub fn new_nth + Clone>( eos: &E, temperature: Temperature, molar_enthalpy: MolarEnergy, - moles: &Moles>, + composition: X, density_initialization: Option, ) -> FeosResult { - let x = moles.convert_to(moles.sum()); + let (x, _) = composition.clone().into_molefracs(eos)?; let rho0 = match density_initialization { Some(DensityInitialization::InitialDensity(r)) => { Density::from_reduced(D::from(r.into_reduced())) } - Some(DensityInitialization::Liquid) => eos.max_density(&Some(x))?, - Some(DensityInitialization::Vapor) => eos.max_density(&Some(x))? * 1.0e-5, - None => eos.max_density(&Some(x))? * 0.01, + Some(DensityInitialization::Liquid) => eos.max_density(&x)?, + Some(DensityInitialization::Vapor) => eos.max_density(&x)? * 1.0e-5, + None => eos.max_density(&x)? * 0.01, }; - let n_inv = moles.sum().inv(); - let f = |x0| { - let s = State::new_nvt(eos, temperature, moles.sum() / x0, moles)?; - let dfx = -s.volume / s.density - * n_inv - * (s.volume * s.dp_dv(Contributions::Total) + let f = |rho| { + let s = State::new(eos, temperature, rho, composition.clone())?; + let dfx = -s.molar_volume + * s.molar_volume + * (s.molar_volume * s.dp_dv(Contributions::Total) + temperature * s.dp_dt(Contributions::Total)); let fx = s.molar_enthalpy(Contributions::Total) - molar_enthalpy; Ok((fx, dfx, s)) @@ -637,26 +539,25 @@ where } /// Return a new `State` for given temperature $T$ and molar entropy $s$. - pub fn new_nts( + pub fn new_nts + Clone>( eos: &E, temperature: Temperature, molar_entropy: MolarEntropy, - moles: &Moles>, + composition: X, density_initialization: Option, ) -> FeosResult { - let x = moles.convert_to(moles.sum()); + let (x, _) = composition.clone().into_molefracs(eos)?; let rho0 = match density_initialization { Some(DensityInitialization::InitialDensity(r)) => { Density::from_reduced(D::from(r.into_reduced())) } - Some(DensityInitialization::Liquid) => eos.max_density(&Some(x))?, - Some(DensityInitialization::Vapor) => eos.max_density(&Some(x))? * 1.0e-5, - None => eos.max_density(&Some(x))? * 0.01, + Some(DensityInitialization::Liquid) => eos.max_density(&x)?, + Some(DensityInitialization::Vapor) => eos.max_density(&x)? * 1.0e-5, + None => eos.max_density(&x)? * 0.01, }; - let n_inv = moles.sum().inv(); - let f = |x0| { - let s = State::new_nvt(eos, temperature, moles.sum() / x0, moles)?; - let dfx = -n_inv * s.volume / s.density * s.dp_dt(Contributions::Total); + let f = |rho| { + let s = State::new(eos, temperature, rho, composition.clone())?; + let dfx = -s.molar_volume * s.molar_volume * s.dp_dt(Contributions::Total); let fx = s.molar_entropy(Contributions::Total) - molar_entropy; Ok((fx, dfx, s)) }; @@ -664,18 +565,18 @@ where } /// Return a new `State` for given pressure $p$ and molar entropy $s$. - pub fn new_nps( + pub fn new_nps + Clone>( eos: &E, pressure: Pressure, molar_entropy: MolarEntropy, - moles: &Moles>, + composition: X, density_initialization: Option, initial_temperature: Option>, ) -> FeosResult { let t0 = initial_temperature.unwrap_or(Temperature::from_reduced(D::from(298.15))); let mut density = density_initialization; let f = |x0| { - let s = State::new_npt(eos, x0, pressure, moles, density)?; + let s = State::new_npt(eos, x0, pressure, composition.clone(), density)?; let dfx = s.molar_isobaric_heat_capacity(Contributions::Total) / s.temperature; let fx = s.molar_entropy(Contributions::Total) - molar_entropy; density = Some(DensityInitialization::InitialDensity(s.density.re())); @@ -685,16 +586,16 @@ where } /// Return a new `State` for given volume $V$ and molar internal energy $u$. - pub fn new_nvu( + pub fn new_nvu + Clone>( eos: &E, volume: Volume, molar_internal_energy: MolarEnergy, - moles: &Moles>, + composition: X, initial_temperature: Option>, ) -> FeosResult { let t0 = initial_temperature.unwrap_or(Temperature::from_reduced(D::from(298.15))); let f = |x0| { - let s = State::new_nvt(eos, x0, volume, moles)?; + let s = State::new_nvt(eos, x0, volume, composition.clone())?; let fx = s.molar_internal_energy(Contributions::Total) - molar_internal_energy; let dfx = s.molar_isochoric_heat_capacity(Contributions::Total); Ok((fx, dfx, s)) diff --git a/crates/feos-core/src/state/properties.rs b/crates/feos-core/src/state/properties.rs index f45bfb751..c93c1e77d 100644 --- a/crates/feos-core/src/state/properties.rs +++ b/crates/feos-core/src/state/properties.rs @@ -20,11 +20,11 @@ where let ideal_gas = || { quantity::ad::gradient_copy( partial2( - |n, &t, &v| self.eos.ideal_gas_helmholtz_energy(t, v, &n), + |n: Dimensionless<_>, &t, &v| self.eos.ideal_gas_helmholtz_energy(t, v, &n), &self.temperature, - &self.volume, + &self.molar_volume, ), - &self.moles, + &Dimensionless::new(self.molefracs.clone()), ) .1 }; @@ -37,10 +37,15 @@ where let ideal_gas = || { quantity::ad::partial_hessian_copy( partial( - |(n, t), &v| self.eos.ideal_gas_helmholtz_energy(t, v, &n), - &self.volume, + |(n, t): (Dimensionless<_>, _), &v| { + self.eos.ideal_gas_helmholtz_energy(t, v, &n) + }, + &self.molar_volume, + ), + ( + &Dimensionless::new(self.molefracs.clone()), + self.temperature, ), - (&self.moles, self.temperature), ) .3 }; @@ -49,7 +54,7 @@ where /// Molar isochoric heat capacity: $c_v=\left(\frac{\partial u}{\partial T}\right)_{V,N_i}$ pub fn molar_isochoric_heat_capacity(&self, contributions: Contributions) -> MolarEntropy { - self.temperature * self.ds_dt(contributions) / self.total_moles + self.temperature * self.ds_dt(contributions) } /// Partial derivative of the molar isochoric heat capacity w.r.t. temperature: $\left(\frac{\partial c_V}{\partial T}\right)_{V,N_i}$ @@ -57,8 +62,7 @@ where &self, contributions: Contributions, ) -> as Div>>::Output { - (self.temperature * self.d2s_dt2(contributions) + self.ds_dt(contributions)) - / self.total_moles + self.temperature * self.d2s_dt2(contributions) + self.ds_dt(contributions) } /// Molar isobaric heat capacity: $c_p=\left(\frac{\partial h}{\partial T}\right)_{p,N_i}$ @@ -66,7 +70,7 @@ where match contributions { Contributions::Residual => self.residual_molar_isobaric_heat_capacity(), _ => { - self.temperature / self.total_moles + self.temperature * (self.ds_dt(contributions) - (self.dp_dt(contributions) * self.dp_dt(contributions)) / self.dp_dv(contributions)) @@ -76,13 +80,18 @@ where /// Entropy: $S=-\left(\frac{\partial A}{\partial T}\right)_{V,N_i}$ pub fn entropy(&self, contributions: Contributions) -> Entropy { - let residual = || self.residual_entropy(); + self.molar_entropy(contributions) * self.total_moles() + } + + /// Molar entropy: $s=\frac{S}{N}$ + pub fn molar_entropy(&self, contributions: Contributions) -> MolarEntropy { + let residual = || self.residual_molar_entropy(); let ideal_gas = || { -quantity::ad::first_derivative( partial2( |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), - &self.volume, - &self.moles, + &self.molar_volume, + &self.molefracs, ), self.temperature, ) @@ -91,29 +100,24 @@ where Self::contributions(ideal_gas, residual, contributions) } - /// Molar entropy: $s=\frac{S}{N}$ - pub fn molar_entropy(&self, contributions: Contributions) -> MolarEntropy { - self.entropy(contributions) / self.total_moles - } - /// Partial molar entropy: $s_i=\left(\frac{\partial S}{\partial N_i}\right)_{T,p,N_j}$ pub fn partial_molar_entropy(&self) -> MolarEntropy> { let c = Contributions::Total; - -(self.dmu_dt(c) + self.dp_dni(c) * (self.dp_dt(c) / self.dp_dv(c))) + -(self.dmu_dt(c) + self.n_dp_dni(c) * (self.dp_dt(c) / self.dp_dv(c))) } - /// Partial derivative of the entropy w.r.t. temperature: $\left(\frac{\partial S}{\partial T}\right)_{V,N_i}$ + /// Partial derivative of the molar entropy w.r.t. temperature: $\left(\frac{\partial s}{\partial T}\right)_{V,N_i}$ pub fn ds_dt( &self, contributions: Contributions, - ) -> as Div>>::Output { + ) -> as Div>>::Output { let residual = || self.ds_res_dt(); let ideal_gas = || { -quantity::ad::second_derivative( partial2( |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), - &self.volume, - &self.moles, + &self.molar_volume, + &self.molefracs, ), self.temperature, ) @@ -122,18 +126,18 @@ where Self::contributions(ideal_gas, residual, contributions) } - /// Second partial derivative of the entropy w.r.t. temperature: $\left(\frac{\partial^2 S}{\partial T^2}\right)_{V,N_i}$ + /// Second partial derivative of the molar entropy w.r.t. temperature: $\left(\frac{\partial^2 s}{\partial T^2}\right)_{V,N_i}$ pub fn d2s_dt2( &self, contributions: Contributions, - ) -> < as Div>>::Output as Div>>::Output { + ) -> < as Div>>::Output as Div>>::Output { let residual = || self.d2s_res_dt2(); let ideal_gas = || { -quantity::ad::third_derivative( partial2( |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), - &self.volume, - &self.moles, + &self.molar_volume, + &self.molefracs, ), self.temperature, ) @@ -144,14 +148,14 @@ where /// Enthalpy: $H=A+TS+pV$ pub fn enthalpy(&self, contributions: Contributions) -> Energy { - self.temperature * self.entropy(contributions) - + self.helmholtz_energy(contributions) - + self.pressure(contributions) * self.volume + self.molar_enthalpy(contributions) * self.total_moles() } /// Molar enthalpy: $h=\frac{H}{N}$ pub fn molar_enthalpy(&self, contributions: Contributions) -> MolarEnergy { - self.enthalpy(contributions) / self.total_moles + self.temperature * self.molar_entropy(contributions) + + self.molar_helmholtz_energy(contributions) + + self.pressure(contributions) * self.molar_volume } /// Partial molar enthalpy: $h_i=\left(\frac{\partial H}{\partial N_i}\right)_{T,p,N_j}$ @@ -163,13 +167,18 @@ where /// Helmholtz energy: $A$ pub fn helmholtz_energy(&self, contributions: Contributions) -> Energy { - let residual = || self.residual_helmholtz_energy(); + self.molar_helmholtz_energy(contributions) * self.total_moles() + } + + /// Molar Helmholtz energy: $a=\frac{A}{N}$ + pub fn molar_helmholtz_energy(&self, contributions: Contributions) -> MolarEnergy { + let residual = || self.residual_molar_helmholtz_energy(); let ideal_gas = || { quantity::ad::zeroth_derivative( partial2( |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), - &self.volume, - &self.moles, + &self.molar_volume, + &self.molefracs, ), self.temperature, ) @@ -177,43 +186,40 @@ where Self::contributions(ideal_gas, residual, contributions) } - /// Molar Helmholtz energy: $a=\frac{A}{N}$ - pub fn molar_helmholtz_energy(&self, contributions: Contributions) -> MolarEnergy { - self.helmholtz_energy(contributions) / self.total_moles - } - /// Internal energy: $U=A+TS$ pub fn internal_energy(&self, contributions: Contributions) -> Energy { - self.temperature * self.entropy(contributions) + self.helmholtz_energy(contributions) + self.molar_internal_energy(contributions) * self.total_moles() } /// Molar internal energy: $u=\frac{U}{N}$ pub fn molar_internal_energy(&self, contributions: Contributions) -> MolarEnergy { - self.internal_energy(contributions) / self.total_moles + self.temperature * self.molar_entropy(contributions) + + self.molar_helmholtz_energy(contributions) } /// Gibbs energy: $G=A+pV$ pub fn gibbs_energy(&self, contributions: Contributions) -> Energy { - self.pressure(contributions) * self.volume + self.helmholtz_energy(contributions) + self.molar_gibbs_energy(contributions) * self.total_moles() } /// Molar Gibbs energy: $g=\frac{G}{N}$ pub fn molar_gibbs_energy(&self, contributions: Contributions) -> MolarEnergy { - self.gibbs_energy(contributions) / self.total_moles + self.pressure(contributions) * self.molar_volume + + self.molar_helmholtz_energy(contributions) } /// Joule Thomson coefficient: $\mu_{JT}=\left(\frac{\partial T}{\partial p}\right)_{H,N_i}$ pub fn joule_thomson(&self) -> as Div>>::Output { let c = Contributions::Total; - -(self.volume + self.temperature * self.dp_dt(c) / self.dp_dv(c)) - / (self.total_moles * self.molar_isobaric_heat_capacity(c)) + -(self.molar_volume + self.temperature * self.dp_dt(c) / self.dp_dv(c)) + / self.molar_isobaric_heat_capacity(c) } /// Isentropic compressibility: $\kappa_s=-\frac{1}{V}\left(\frac{\partial V}{\partial p}\right)_{S,N_i}$ pub fn isentropic_compressibility(&self) -> InvP { let c = Contributions::Total; -self.molar_isochoric_heat_capacity(c) - / (self.molar_isobaric_heat_capacity(c) * self.dp_dv(c) * self.volume) + / (self.molar_isobaric_heat_capacity(c) * self.dp_dv(c) * self.molar_volume) } /// Isenthalpic compressibility: $\kappa_H=-\frac{1}{V}\left(\frac{\partial V}{\partial p}\right)_{H,N_i}$ @@ -224,7 +230,7 @@ where /// Thermal expansivity: $\alpha_p=-\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{p,N_i}$ pub fn thermal_expansivity(&self) -> InvT { let c = Contributions::Total; - -self.dp_dt(c) / self.dp_dv(c) / self.volume + -self.dp_dt(c) / (self.dp_dv(c) * self.molar_volume) } /// Grueneisen parameter: $\phi=V\left(\frac{\partial p}{\partial U}\right)_{V,n_i}=\frac{v}{c_v}\left(\frac{\partial p}{\partial T}\right)_{v,n_i}=\frac{\rho}{T}\left(\frac{\partial T}{\partial \rho}\right)_{s, n_i}$ @@ -251,11 +257,7 @@ where )); } if let Contributions::Residual | Contributions::Total = contributions { - res.extend( - self.eos - .lift() - .molar_helmholtz_energy_contributions(t, v, &x), - ); + res.extend(self.eos.lift().helmholtz_energy_contributions(t, v, &x)); } res.into_iter() .map(|(s, v)| (s, MolarEnergy::from_reduced(v.eps))) diff --git a/crates/feos-core/src/state/residual_properties.rs b/crates/feos-core/src/state/residual_properties.rs index 53695fdbe..4a6b8f015 100644 --- a/crates/feos-core/src/state/residual_properties.rs +++ b/crates/feos-core/src/state/residual_properties.rs @@ -7,11 +7,8 @@ use num_dual::{Dual, DualNum, Gradients, partial, partial2}; use quantity::*; use std::ops::{Add, Div, Neg, Sub}; -type DpDn = Quantity>::Output>; -type DeDn = Quantity>::Output>; type InvT = Quantity::Output>; type InvP = Quantity::Output>; -type InvM = Quantity::Output>; type POverT = Quantity>::Output>; /// # State properties @@ -38,25 +35,33 @@ where /// Residual Helmholtz energy $A^\text{res}$ pub fn residual_helmholtz_energy(&self) -> Energy { - *self.cache.a.get_or_init(|| { - self.eos - .residual_helmholtz_energy_unit(self.temperature, self.volume, &self.moles) - }) + self.residual_molar_helmholtz_energy() * self.total_moles() } /// Residual molar Helmholtz energy $a^\text{res}$ pub fn residual_molar_helmholtz_energy(&self) -> MolarEnergy { - self.residual_helmholtz_energy() / self.total_moles + *self.cache.a.get_or_init(|| { + self.eos.residual_molar_helmholtz_energy( + self.temperature, + self.molar_volume, + &self.molefracs, + ) + }) } /// Residual entropy $S^\text{res}=\left(\frac{\partial A^\text{res}}{\partial T}\right)_{V,N_i}$ pub fn residual_entropy(&self) -> Entropy { + self.residual_molar_entropy() * self.total_moles() + } + + /// Residual molar entropy $s^\text{res}=\left(\frac{\partial a^\text{res}}{\partial T}\right)_{V,N_i}$ + pub fn residual_molar_entropy(&self) -> MolarEntropy { -*self.cache.da_dt.get_or_init(|| { let (a, da_dt) = quantity::ad::first_derivative( partial2( - |t, &v, n| self.eos.lift().residual_helmholtz_energy_unit(t, v, n), - &self.volume, - &self.moles, + |t, &v, n| self.eos.lift().residual_molar_helmholtz_energy(t, v, n), + &self.molar_volume, + &self.molefracs, ), self.temperature, ); @@ -65,11 +70,6 @@ where }) } - /// Residual entropy $s^\text{res}=\left(\frac{\partial a^\text{res}}{\partial T}\right)_{V,N_i}$ - pub fn residual_molar_entropy(&self) -> MolarEntropy { - self.residual_entropy() / self.total_moles - } - /// Pressure: $p=-\left(\frac{\partial A}{\partial V}\right)_{T,N_i}$ pub fn pressure(&self, contributions: Contributions) -> Pressure { let ideal_gas = || self.density * RGAS * self.temperature; @@ -77,11 +77,11 @@ where -*self.cache.da_dv.get_or_init(|| { let (a, da_dv) = quantity::ad::first_derivative( partial2( - |v, &t, n| self.eos.lift().residual_helmholtz_energy_unit(t, v, n), + |v, &t, n| self.eos.lift().residual_molar_helmholtz_energy(t, v, n), &self.temperature, - &self.moles, + &self.molefracs, ), - self.volume, + self.molar_volume, ); let _ = self.cache.a.set(a); da_dv @@ -97,11 +97,13 @@ where .get_or_init(|| { let (a, mu) = quantity::ad::gradient_copy( partial2( - |n, &t, &v| self.eos.lift().residual_helmholtz_energy_unit(t, v, &n), + |n: Dimensionless<_>, &t, &v| { + self.eos.lift().residual_molar_helmholtz_energy(t, v, &n) + }, &self.temperature, - &self.volume, + &self.molar_volume, ), - &self.moles, + &Dimensionless::new(self.molefracs.clone()), ); let _ = self.cache.a.set(a); mu @@ -116,18 +118,21 @@ where // pressure derivatives - /// Partial derivative of pressure w.r.t. volume: $\left(\frac{\partial p}{\partial V}\right)_{T,N_i}$ - pub fn dp_dv(&self, contributions: Contributions) -> as Div>>::Output { - let ideal_gas = || -self.density * RGAS * self.temperature / self.volume; + /// Partial derivative of pressure w.r.t. molar volume: $\left(\frac{\partial p}{\partial v}\right)_{T,N_i}$ + pub fn dp_dv( + &self, + contributions: Contributions, + ) -> as Div>>::Output { + let ideal_gas = || -self.density * RGAS * self.temperature / self.molar_volume; let residual = || { -*self.cache.d2a_dv2.get_or_init(|| { let (a, da_dv, d2a_dv2) = quantity::ad::second_derivative( partial2( - |v, &t, n| self.eos.lift().residual_helmholtz_energy_unit(t, v, n), + |v, &t, n| self.eos.lift().residual_molar_helmholtz_energy(t, v, n), &self.temperature, - &self.moles, + &self.molefracs, ), - self.volume, + self.molar_volume, ); let _ = self.cache.a.set(a); let _ = self.cache.da_dv.set(da_dv); @@ -142,7 +147,7 @@ where &self, contributions: Contributions, ) -> as Div>>::Output { - -self.volume / self.density * self.dp_dv(contributions) + -self.molar_volume / self.density * self.dp_dv(contributions) } /// Partial derivative of pressure w.r.t. temperature: $\left(\frac{\partial p}{\partial T}\right)_{V,N_i}$ @@ -152,10 +157,10 @@ where -*self.cache.d2a_dtdv.get_or_init(|| { let (a, da_dt, da_dv, d2a_dtdv) = quantity::ad::second_partial_derivative( partial( - |(t, v), n| self.eos.lift().residual_helmholtz_energy_unit(t, v, n), - &self.moles, + |(t, v), n| self.eos.lift().residual_molar_helmholtz_energy(t, v, n), + &self.molefracs, ), - (self.temperature, self.volume), + (self.temperature, self.molar_volume), ); let _ = self.cache.a.set(a); let _ = self.cache.da_dt.set(da_dt); @@ -166,18 +171,23 @@ where Self::contributions(ideal_gas, residual, contributions) } - /// Partial derivative of pressure w.r.t. moles: $\left(\frac{\partial p}{\partial N_i}\right)_{T,V,N_j}$ - pub fn dp_dni(&self, contributions: Contributions) -> DpDn> { + /// Partial derivative of pressure w.r.t. moles: $N\left(\frac{\partial p}{\partial N_i}\right)_{T,V,N_j}$ + pub fn n_dp_dni(&self, contributions: Contributions) -> Pressure> { let residual = -self .cache .d2a_dndv .get_or_init(|| { let (a, da_dn, da_dv, dmu_dv) = quantity::ad::partial_hessian_copy( partial( - |(n, v), &t| self.eos.lift().residual_helmholtz_energy_unit(t, v, &n), + |(n, v): (Dimensionless<_>, _), &t| { + self.eos.lift().residual_molar_helmholtz_energy(t, v, &n) + }, &self.temperature, ), - (&self.moles, self.volume), + ( + &Dimensionless::new(self.molefracs.clone()), + self.molar_volume, + ), ); let _ = self.cache.a.set(a); let _ = self.cache.da_dn.set(da_dn); @@ -186,7 +196,7 @@ where }) .clone(); let (r, c) = residual.shape_generic(); - let ideal_gas = || self.temperature / self.volume * RGAS; + let ideal_gas = || self.temperature * self.density * RGAS; Quantity::from_fn_generic(r, c, |i, _| { Self::contributions(ideal_gas, || residual.get(i), contributions) }) @@ -196,18 +206,19 @@ where pub fn d2p_dv2( &self, contributions: Contributions, - ) -> < as Div>>::Output as Div>>::Output { - let ideal_gas = - || self.density * RGAS * self.temperature / (self.volume * self.volume) * 2.0; + ) -> < as Div>>::Output as Div>>::Output { + let ideal_gas = || { + self.density * RGAS * self.temperature / (self.molar_volume * self.molar_volume) * 2.0 + }; let residual = || { -*self.cache.d3a_dv3.get_or_init(|| { let (a, da_dv, d2a_dv2, d3a_dv3) = quantity::ad::third_derivative( partial2( - |v, &t, n| self.eos.lift().residual_helmholtz_energy_unit(t, v, n), + |v, &t, n| self.eos.lift().residual_molar_helmholtz_energy(t, v, n), &self.temperature, - &self.moles, + &self.molefracs, ), - self.volume, + self.molar_volume, ); let _ = self.cache.a.set(a); let _ = self.cache.da_dv.set(da_dv); @@ -223,59 +234,62 @@ where &self, contributions: Contributions, ) -> < as Div>>::Output as Div>>::Output { - self.volume / (self.density * self.density) - * (self.volume * self.d2p_dv2(contributions) + self.dp_dv(contributions) * 2.0) + self.molar_volume.powi::<3>() + * (self.molar_volume * self.d2p_dv2(contributions) + self.dp_dv(contributions) * 2.0) } /// Structure factor: $S(0)=k_BT\left(\frac{\partial\rho}{\partial p}\right)_{T,N_i}$ pub fn structure_factor(&self) -> D { - -(self.temperature * self.density * RGAS / (self.volume * self.dp_dv(Contributions::Total))) - .into_value() + -(self.temperature * self.density * RGAS + / (self.molar_volume * self.dp_dv(Contributions::Total))) + .into_value() } /// Partial molar volume: $v_i=\left(\frac{\partial V}{\partial N_i}\right)_{T,p,N_j}$ pub fn partial_molar_volume(&self) -> MolarVolume> { - -self.dp_dni(Contributions::Total) / self.dp_dv(Contributions::Total) + -self.n_dp_dni(Contributions::Total) / self.dp_dv(Contributions::Total) } - /// Partial derivative of chemical potential w.r.t. moles: $\left(\frac{\partial\mu_i}{\partial N_j}\right)_{T,V,N_k}$ - pub fn dmu_dni(&self, contributions: Contributions) -> DeDn> + /// Partial derivative of chemical potential w.r.t. moles: $N\left(\frac{\partial\mu_i}{\partial N_j}\right)_{T,V,N_k}$ + pub fn n_dmu_dni(&self, contributions: Contributions) -> MolarEnergy> where DefaultAllocator: Allocator, { let (a, da_dn, d2a_dn2) = quantity::ad::hessian_copy( partial2( - |n, &t, &v| self.eos.lift().residual_helmholtz_energy_unit(t, v, &n), + |n: Dimensionless<_>, &t, &v| { + self.eos.lift().residual_molar_helmholtz_energy(t, v, &n) + }, &self.temperature, - &self.volume, + &self.molar_volume, ), - &self.moles, + &Dimensionless::new(self.molefracs.clone()), ); let _ = self.cache.a.set(a); let _ = self.cache.da_dn.set(da_dn); let residual = || d2a_dn2; let ideal_gas = || { Dimensionless::new(OMatrix::from_diagonal(&self.molefracs.map(|x| x.recip()))) - * (self.temperature * RGAS / self.total_moles) + * (self.temperature * RGAS) }; Self::contributions(ideal_gas, residual, contributions) } /// Isothermal compressibility: $\kappa_T=-\frac{1}{V}\left(\frac{\partial V}{\partial p}\right)_{T,N_i}$ pub fn isothermal_compressibility(&self) -> InvP { - -(self.dp_dv(Contributions::Total) * self.volume).inv() + -(self.dp_dv(Contributions::Total) * self.molar_volume).inv() } // entropy derivatives - /// Partial derivative of the residual entropy w.r.t. temperature: $\left(\frac{\partial S^\text{res}}{\partial T}\right)_{V,N_i}$ - pub fn ds_res_dt(&self) -> as Div>>::Output { + /// Partial derivative of the residual molar entropy w.r.t. temperature: $\left(\frac{\partial s^\text{res}}{\partial T}\right)_{V,N_i}$ + pub fn ds_res_dt(&self) -> as Div>>::Output { -*self.cache.d2a_dt2.get_or_init(|| { let (a, da_dt, d2a_dt2) = quantity::ad::second_derivative( partial2( - |t, &v, n| self.eos.lift().residual_helmholtz_energy_unit(t, v, n), - &self.volume, - &self.moles, + |t, &v, n| self.eos.lift().residual_molar_helmholtz_energy(t, v, n), + &self.molar_volume, + &self.molefracs, ), self.temperature, ); @@ -285,16 +299,16 @@ where }) } - /// Second partial derivative of the residual entropy w.r.t. temperature: $\left(\frac{\partial^2S^\text{res}}{\partial T^2}\right)_{V,N_i}$ + /// Second partial derivative of the residual molar entropy w.r.t. temperature: $\left(\frac{\partial^2s^\text{res}}{\partial T^2}\right)_{V,N_i}$ pub fn d2s_res_dt2( &self, - ) -> < as Div>>::Output as Div>>::Output { + ) -> < as Div>>::Output as Div>>::Output { -*self.cache.d3a_dt3.get_or_init(|| { let (a, da_dt, d2a_dt2, d3a_dt3) = quantity::ad::third_derivative( partial2( - |t, &v, n| self.eos.lift().residual_helmholtz_energy_unit(t, v, n), - &self.volume, - &self.moles, + |t, &v, n| self.eos.lift().residual_molar_helmholtz_energy(t, v, n), + &self.molar_volume, + &self.molefracs, ), self.temperature, ); @@ -312,10 +326,15 @@ where .get_or_init(|| { let (a, da_dn, da_dt, d2a_dndt) = quantity::ad::partial_hessian_copy( partial( - |(n, t), &v| self.eos.lift().residual_helmholtz_energy_unit(t, v, &n), - &self.volume, + |(n, t): (Dimensionless<_>, _), &v| { + self.eos.lift().residual_molar_helmholtz_energy(t, v, &n) + }, + &self.molar_volume, + ), + ( + &Dimensionless::new(self.molefracs.clone()), + self.temperature, ), - (&self.moles, self.temperature), ); let _ = self.cache.a.set(a); let _ = self.cache.da_dn.set(da_dn); @@ -350,17 +369,19 @@ where .add_scalar(-self.pressure(Contributions::Total).inv()) } - /// Partial derivative of the logarithm of the fugacity coefficient w.r.t. moles: $\left(\frac{\partial\ln\varphi_i}{\partial N_j}\right)_{T,p,N_k}$ - pub fn dln_phi_dnj(&self) -> InvM> + /// Partial derivative of the logarithm of the fugacity coefficient w.r.t. moles: $N\left(\frac{\partial\ln\varphi_i}{\partial N_j}\right)_{T,p,N_k}$ + pub fn n_dln_phi_dnj(&self) -> OMatrix where DefaultAllocator: Allocator, { - let dmu_dni = self.dmu_dni(Contributions::Residual); - let dp_dni = self.dp_dni(Contributions::Total); + let dmu_dni = self.n_dmu_dni(Contributions::Residual); + let dp_dni = self.n_dp_dni(Contributions::Total); let dp_dv = self.dp_dv(Contributions::Total); let (r, c) = dmu_dni.shape_generic(); let dp_dn_2 = Quantity::from_fn_generic(r, c, |i, j| dp_dni.get(i) * dp_dni.get(j)); - ((dmu_dni + dp_dn_2 / dp_dv) / (self.temperature * RGAS)).add_scalar(self.total_moles.inv()) + ((dmu_dni + dp_dn_2 / dp_dv) / (self.temperature * RGAS)) + .into_value() + .add_scalar(D::from(1.0)) } } @@ -371,11 +392,11 @@ impl State { (0..self.eos.components()) .map(|i| { let eos = self.eos.subset(&[i]); - let state = State::new_xpt( + let state = State::new_npt( &eos, self.temperature, pressure, - &dvector![1.0], + dvector![1.0], Some(crate::DensityInitialization::Liquid), )?; Ok(state.ln_phi()[0]) @@ -424,12 +445,7 @@ impl State { }?; // Calculate the liquid state including the Henry components - let liquid = State::new_nvt( - eos, - temperature, - vle.liquid().volume, - &(molefracs * vle.liquid().total_moles), - )?; + let liquid = State::new(eos, temperature, vle.liquid().density, molefracs.clone())?; // Calculate the vapor state including the Henry components let mut molefracs_vapor = molefracs.clone(); @@ -437,12 +453,7 @@ impl State { .into_iter() .zip(&vle.vapor().molefracs) .for_each(|(i, &y)| molefracs_vapor[i] = y); - let vapor = State::new_nvt( - eos, - temperature, - vle.vapor().volume, - &(molefracs_vapor * vle.vapor().total_moles), - )?; + let vapor = State::new(eos, temperature, vle.vapor().density, molefracs.clone())?; // Determine the Henry's law coefficients and return only those of the Henry components let p = vle.vapor().pressure(Contributions::Total).into_reduced(); @@ -465,11 +476,10 @@ impl State { impl State { /// Thermodynamic factor: $\Gamma_{ij}=\delta_{ij}+x_i\left(\frac{\partial\ln\varphi_i}{\partial x_j}\right)_{T,p,\Sigma}$ pub fn thermodynamic_factor(&self) -> DMatrix { - let dln_phi_dnj = (self.dln_phi_dnj() * Moles::from_reduced(1.0)).into_value(); - let moles = &self.molefracs * self.total_moles.into_reduced(); + let dln_phi_dnj = self.n_dln_phi_dnj(); let n = self.eos.components() - 1; DMatrix::from_fn(n, n, |i, j| { - moles[i] * (dln_phi_dnj[(i, j)] - dln_phi_dnj[(i, n)]) + if i == j { 1.0 } else { 0.0 } + dln_phi_dnj[(i, j)] - dln_phi_dnj[(i, n)] + if i == j { 1.0 } else { 0.0 } }) } } @@ -480,63 +490,62 @@ where { /// Residual molar isochoric heat capacity: $c_v^\text{res}=\left(\frac{\partial u^\text{res}}{\partial T}\right)_{V,N_i}$ pub fn residual_molar_isochoric_heat_capacity(&self) -> MolarEntropy { - self.ds_res_dt() * self.temperature / self.total_moles + self.ds_res_dt() * self.temperature } /// Partial derivative of the residual molar isochoric heat capacity w.r.t. temperature: $\left(\frac{\partial c_V^\text{res}}{\partial T}\right)_{V,N_i}$ pub fn dc_v_res_dt(&self) -> as Div>>::Output { - (self.temperature * self.d2s_res_dt2() + self.ds_res_dt()) / self.total_moles + self.temperature * self.d2s_res_dt2() + self.ds_res_dt() } /// Residual molar isobaric heat capacity: $c_p^\text{res}=\left(\frac{\partial h^\text{res}}{\partial T}\right)_{p,N_i}$ pub fn residual_molar_isobaric_heat_capacity(&self) -> MolarEntropy { let dp_dt = self.dp_dt(Contributions::Total); - self.temperature / self.total_moles - * (self.ds_res_dt() - dp_dt * dp_dt / self.dp_dv(Contributions::Total)) + self.temperature * (self.ds_res_dt() - dp_dt * dp_dt / self.dp_dv(Contributions::Total)) - RGAS } /// Residual enthalpy: $H^\text{res}(T,p,\mathbf{n})=A^\text{res}+TS^\text{res}+p^\text{res}V$ pub fn residual_enthalpy(&self) -> Energy { - self.temperature * self.residual_entropy() - + self.residual_helmholtz_energy() - + self.pressure(Contributions::Residual) * self.volume + self.residual_molar_enthalpy() * self.total_moles() } /// Residual molar enthalpy: $h^\text{res}(T,p,\mathbf{n})=a^\text{res}+Ts^\text{res}+p^\text{res}v$ pub fn residual_molar_enthalpy(&self) -> MolarEnergy { - self.residual_enthalpy() / self.total_moles + self.temperature * self.residual_molar_entropy() + + self.residual_molar_helmholtz_energy() + + self.pressure(Contributions::Residual) * self.molar_volume } /// Residual internal energy: $U^\text{res}(T,V,\mathbf{n})=A^\text{res}+TS^\text{res}$ pub fn residual_internal_energy(&self) -> Energy { - self.temperature * self.residual_entropy() + self.residual_helmholtz_energy() + self.residual_molar_internal_energy() * self.total_moles() } /// Residual molar internal energy: $u^\text{res}(T,V,\mathbf{n})=a^\text{res}+Ts^\text{res}$ pub fn residual_molar_internal_energy(&self) -> MolarEnergy { - self.residual_internal_energy() / self.total_moles + self.temperature * self.residual_molar_entropy() + self.residual_molar_helmholtz_energy() } /// Residual Gibbs energy: $G^\text{res}(T,p,\mathbf{n})=A^\text{res}+p^\text{res}V-NRT \ln Z$ pub fn residual_gibbs_energy(&self) -> Energy { - self.pressure(Contributions::Residual) * self.volume + self.residual_helmholtz_energy() - - self.total_moles - * RGAS - * self.temperature - * Dimensionless::new(self.compressibility(Contributions::Total).ln()) + self.residual_molar_gibbs_energy() * self.total_moles() } /// Residual Gibbs energy: $g^\text{res}(T,p,\mathbf{n})=a^\text{res}+p^\text{res}v-RT \ln Z$ pub fn residual_molar_gibbs_energy(&self) -> MolarEnergy { - self.residual_gibbs_energy() / self.total_moles + self.pressure(Contributions::Residual) * self.molar_volume + + self.residual_molar_helmholtz_energy() + - self.temperature + * RGAS + * Dimensionless::new(self.compressibility(Contributions::Total).ln()) } /// Molar Helmholtz energy $a^\text{res}$ evaluated for each residual contribution of the equation of state. pub fn residual_molar_helmholtz_energy_contributions( &self, ) -> Vec<(&'static str, MolarEnergy)> { - let residual_contributions = self.eos.molar_helmholtz_energy_contributions( + let residual_contributions = self.eos.helmholtz_energy_contributions( self.temperature.into_reduced(), self.density.into_reduced().recip(), &self.molefracs, @@ -557,10 +566,7 @@ where let v = Dual::from_re(self.temperature.into_reduced()); let mut x = self.molefracs.map(Dual::from_re); x[component].eps = D::one(); - let contributions = self - .eos - .lift() - .molar_helmholtz_energy_contributions(t, v, &x); + let contributions = self.eos.lift().helmholtz_energy_contributions(t, v, &x); let mut res = Vec::with_capacity(contributions.len()); for (s, v) in contributions { res.push((s, MolarEnergy::from_reduced(v.eps))); @@ -573,10 +579,7 @@ where let t = Dual::from_re(self.temperature.into_reduced()); let v = Dual::from_re(self.density.into_reduced().recip()).derivative(); let x = self.molefracs.map(Dual::from_re); - let contributions = self - .eos - .lift() - .molar_helmholtz_energy_contributions(t, v, &x); + let contributions = self.eos.lift().helmholtz_energy_contributions(t, v, &x); let mut res = Vec::with_capacity(contributions.len() + 1); res.push(("Ideal gas", self.density * RGAS * self.temperature)); for (s, v) in contributions { @@ -599,12 +602,15 @@ where /// Mass of each component: $m_i=n_iMW_i$ pub fn mass(&self) -> Mass> { - self.eos.molar_weight().component_mul(&self.moles) + self.eos + .molar_weight() + .component_mul(&Dimensionless::new(self.molefracs.clone())) + * self.total_moles() } /// Total mass: $m=\sum_im_i=nMW$ pub fn total_mass(&self) -> Mass { - self.total_moles * self.total_molar_weight() + self.total_molar_weight() * self.total_moles() } /// Mass density: $\rho^{(m)}=\frac{m}{V}$ @@ -614,7 +620,10 @@ where /// Mass fractions: $w_i=\frac{m_i}{m}$ pub fn massfracs(&self) -> OVector { - (self.mass() / self.total_mass()).into_value() + self.eos + .molar_weight() + .convert_into(self.total_molar_weight()) + .component_mul(&self.molefracs) } } @@ -630,7 +639,7 @@ where pub fn viscosity(&self) -> Viscosity { let s = self.residual_molar_entropy().into_reduced(); self.eos - .viscosity_reference(self.temperature, self.volume, &self.moles) + .viscosity_reference(self.temperature, self.molar_volume, &self.molefracs) * Dimensionless::new(self.eos.viscosity_correlation(s, &self.molefracs).exp()) } @@ -646,14 +655,14 @@ where /// Return the viscosity reference as used in entropy scaling. pub fn viscosity_reference(&self) -> Viscosity { self.eos - .viscosity_reference(self.temperature, self.volume, &self.moles) + .viscosity_reference(self.temperature, self.molar_volume, &self.molefracs) } /// Return the diffusion via entropy scaling. pub fn diffusion(&self) -> Diffusivity { let s = self.residual_molar_entropy().into_reduced(); self.eos - .diffusion_reference(self.temperature, self.volume, &self.moles) + .diffusion_reference(self.temperature, self.molar_volume, &self.molefracs) * Dimensionless::new(self.eos.diffusion_correlation(s, &self.molefracs).exp()) } @@ -669,19 +678,21 @@ where /// Return the diffusion reference as used in entropy scaling. pub fn diffusion_reference(&self) -> Diffusivity { self.eos - .diffusion_reference(self.temperature, self.volume, &self.moles) + .diffusion_reference(self.temperature, self.molar_volume, &self.molefracs) } /// Return the thermal conductivity via entropy scaling. pub fn thermal_conductivity(&self) -> ThermalConductivity { let s = self.residual_molar_entropy().into_reduced(); - self.eos - .thermal_conductivity_reference(self.temperature, self.volume, &self.moles) - * Dimensionless::new( - self.eos - .thermal_conductivity_correlation(s, &self.molefracs) - .exp(), - ) + self.eos.thermal_conductivity_reference( + self.temperature, + self.molar_volume, + &self.molefracs, + ) * Dimensionless::new( + self.eos + .thermal_conductivity_correlation(s, &self.molefracs) + .exp(), + ) } /// Return the logarithm of the reduced thermal conductivity. @@ -696,7 +707,10 @@ where /// Return the thermal conductivity reference as used in entropy scaling. pub fn thermal_conductivity_reference(&self) -> ThermalConductivity { - self.eos - .thermal_conductivity_reference(self.temperature, self.volume, &self.moles) + self.eos.thermal_conductivity_reference( + self.temperature, + self.molar_volume, + &self.molefracs, + ) } } diff --git a/crates/feos-core/src/state/statevec.rs b/crates/feos-core/src/state/statevec.rs index a62fe9413..63d51b3fd 100644 --- a/crates/feos-core/src/state/statevec.rs +++ b/crates/feos-core/src/state/statevec.rs @@ -63,7 +63,7 @@ impl StateVec<'_, E> { pub fn moles(&self) -> Moles> { Moles::from_shape_fn((self.0.len(), self.0[0].eos.components()), |(i, j)| { - self.0[i].moles.get(j) + self.0[i].moles().get(j) }) } diff --git a/crates/feos-derive/src/residual.rs b/crates/feos-derive/src/residual.rs index 12e4fe862..df3f5367f 100644 --- a/crates/feos-derive/src/residual.rs +++ b/crates/feos-derive/src/residual.rs @@ -1,4 +1,4 @@ -use super::{implement, OPT_IMPLS}; +use super::{OPT_IMPLS, implement}; use quote::quote; use syn::DeriveInput; @@ -201,19 +201,19 @@ fn impl_entropy_scaling( let name = &v.ident; if implement("entropy_scaling", v, &OPT_IMPLS)? { etar.push(quote! { - Self::#name(eos) => eos.viscosity_reference(temperature, volume, moles) + Self::#name(eos) => eos.viscosity_reference(temperature, molar_volume, molefracs) }); etac.push(quote! { Self::#name(eos) => eos.viscosity_correlation(s_res, x) }); dr.push(quote! { - Self::#name(eos) => eos.diffusion_reference(temperature, volume, moles) + Self::#name(eos) => eos.diffusion_reference(temperature, molar_volume, molefracs) }); dc.push(quote! { Self::#name(eos) => eos.diffusion_correlation(s_res, x) }); thcr.push(quote! { - Self::#name(eos) => eos.thermal_conductivity_reference(temperature, volume, moles) + Self::#name(eos) => eos.thermal_conductivity_reference(temperature, molar_volume, molefracs) }); thcc.push(quote! { Self::#name(eos) => eos.thermal_conductivity_correlation(s_res, x) @@ -245,8 +245,8 @@ fn impl_entropy_scaling( fn viscosity_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &DVector, ) -> Viscosity { match self { #(#etar,)* @@ -262,8 +262,8 @@ fn impl_entropy_scaling( fn diffusion_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &DVector, ) -> Diffusivity { match self { #(#dr,)* @@ -279,8 +279,8 @@ fn impl_entropy_scaling( fn thermal_conductivity_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &DVector, ) -> ThermalConductivity { match self { #(#thcr,)* diff --git a/crates/feos-dft/src/adsorption/mod.rs b/crates/feos-dft/src/adsorption/mod.rs index 9962f27a5..bf9ddabe0 100644 --- a/crates/feos-dft/src/adsorption/mod.rs +++ b/crates/feos-dft/src/adsorption/mod.rs @@ -1,11 +1,12 @@ //! Adsorption profiles and isotherms. use super::functional::HelmholtzEnergyFunctional; use super::solver::DFTSolver; +use feos_core::DensityInitialization::{Liquid, Vapor}; use feos_core::{ - Contributions, DensityInitialization, FeosError, FeosResult, ReferenceSystem, SolverOptions, - State, StateBuilder, + Composition, Contributions, DensityInitialization, FeosError, FeosResult, ReferenceSystem, + SolverOptions, State, }; -use nalgebra::{DMatrix, DVector}; +use nalgebra::{DMatrix, DVector, Dyn}; use ndarray::{Array1, Array2, Dimension, Ix1, Ix3, RemoveAxis}; use quantity::{Energy, MolarEnergy, Moles, Pressure, Temperature}; use std::iter; @@ -53,12 +54,12 @@ where } /// Calculate an adsorption isotherm (starting at low pressure) - pub fn adsorption_isotherm>( + pub fn adsorption_isotherm, X: Composition + Clone>( functional: &F, temperature: Temperature, pressure: &Pressure>, pore: &S, - molefracs: &Option>, + composition: X, solver: Option<&DFTSolver>, ) -> FeosResult> { Self::isotherm( @@ -66,19 +67,19 @@ where temperature, pressure, pore, - molefracs, + composition, DensityInitialization::Vapor, solver, ) } /// Calculate an desorption isotherm (starting at high pressure) - pub fn desorption_isotherm>( + pub fn desorption_isotherm, X: Composition + Clone>( functional: &F, temperature: Temperature, pressure: &Pressure>, pore: &S, - molefracs: &Option>, + composition: X, solver: Option<&DFTSolver>, ) -> FeosResult> { let pressure = pressure.into_iter().rev().collect(); @@ -87,7 +88,7 @@ where temperature, &pressure, pore, - molefracs, + composition, DensityInitialization::Liquid, solver, )?; @@ -98,12 +99,12 @@ where } /// Calculate an equilibrium isotherm - pub fn equilibrium_isotherm>( + pub fn equilibrium_isotherm, X: Composition + Clone>( functional: &F, temperature: Temperature, pressure: &Pressure>, pore: &S, - molefracs: &Option>, + composition: X, solver: Option<&DFTSolver>, ) -> FeosResult> { let (p_min, p_max) = (pressure.get(0), pressure.get(pressure.len() - 1)); @@ -113,7 +114,7 @@ where p_min, p_max, pore, - molefracs, + composition.clone(), solver, SolverOptions::default(), ); @@ -132,7 +133,7 @@ where temperature, &p_ads, pore, - molefracs, + composition.clone(), solver, )? .profiles; @@ -141,7 +142,7 @@ where temperature, &p_des, pore, - molefracs, + composition, solver, )? .profiles; @@ -155,7 +156,7 @@ where temperature, pressure, pore, - molefracs, + composition.clone(), solver, )?; let desorption = Self::desorption_isotherm( @@ -163,7 +164,7 @@ where temperature, pressure, pore, - molefracs, + composition, solver, )?; let omega_a = adsorption.grand_potential(); @@ -181,25 +182,24 @@ where } } - fn isotherm>( + fn isotherm, X: Composition + Clone>( functional: &F, temperature: Temperature, pressure: &Pressure>, pore: &S, - molefracs: &Option>, + composition: X, density_initialization: DensityInitialization, solver: Option<&DFTSolver>, ) -> FeosResult> { - let x = functional.validate_molefracs(molefracs)?; let mut profiles: Vec>> = Vec::with_capacity(pressure.len()); // On the first iteration, initialize the density profile according to the direction // and calculate the external potential once. - let mut bulk = State::new_xpt( + let mut bulk = State::new_npt( functional, temperature, pressure.get(0), - &x, + composition.clone(), Some(density_initialization), )?; if functional.components() > 1 && !bulk.is_stable(SolverOptions::default())? { @@ -215,11 +215,13 @@ where let mut old_density = Some(&profile.density); for i in 0..pressure.len() { - let mut bulk = StateBuilder::new(functional) - .temperature(temperature) - .pressure(pressure.get(i)) - .molefracs(&x) - .build()?; + let mut bulk = State::new_npt( + functional, + temperature, + pressure.get(i), + composition.clone(), + None, + )?; if functional.components() > 1 && !bulk.is_stable(SolverOptions::default())? { bulk = bulk .tp_flash(None, SolverOptions::default(), None)? @@ -243,37 +245,21 @@ where /// Calculate the phase transition from an empty to a filled pore. #[expect(clippy::too_many_arguments)] - pub fn phase_equilibrium>( + pub fn phase_equilibrium, X: Composition + Clone>( functional: &F, temperature: Temperature, p_min: Pressure, p_max: Pressure, pore: &S, - molefracs: &Option>, + composition: X, solver: Option<&DFTSolver>, options: SolverOptions, ) -> FeosResult> { - let x = functional.validate_molefracs(molefracs)?; - + let x = composition; // calculate density profiles for the minimum and maximum pressure - let vapor_bulk = StateBuilder::new(functional) - .temperature(temperature) - .pressure(p_min) - .molefracs(&x) - .vapor() - .build()?; - let bulk_init = StateBuilder::new(functional) - .temperature(temperature) - .pressure(p_max) - .molefracs(&x) - .liquid() - .build()?; - let liquid_bulk = StateBuilder::new(functional) - .temperature(temperature) - .pressure(p_max) - .molefracs(&x) - .vapor() - .build()?; + let vapor_bulk = State::new_npt(functional, temperature, p_min, x.clone(), Some(Vapor))?; + let bulk_init = State::new_npt(functional, temperature, p_max, x.clone(), Some(Liquid))?; + let liquid_bulk = State::new_npt(functional, temperature, p_max, x.clone(), Some(Vapor))?; let mut vapor = pore.initialize(&vapor_bulk, None, None)?.solve(solver)?; let mut liquid = pore.initialize(&bulk_init, None, None)?.solve(solver)?; @@ -287,18 +273,13 @@ where / (n_dp_drho_v / vapor_bulk.density - n_dp_drho_l / liquid_bulk.density); // update filled pore with limited step size - let mut bulk = StateBuilder::new(functional) - .temperature(temperature) - .pressure(p_max) - .molefracs(&x) - .vapor() - .build()?; + let mut bulk = State::new_npt(functional, temperature, p_max, x.clone(), Some(Vapor))?; let rho0 = liquid_bulk.density; let steps = (10.0 * (rho - rho0) / rho0).into_value().abs().ceil() as usize; let delta_rho = (rho - rho0) / steps as f64; for i in 1..=steps { let rho_i = rho0 + i as f64 * delta_rho; - bulk = State::new_intensive(functional, temperature, rho_i, &x)?; + bulk = State::new(functional, temperature, rho_i, x.clone())?; liquid = liquid.update_bulk(&bulk).solve(solver)?; } @@ -324,7 +305,7 @@ where rho += delta_rho; // update bulk phase - bulk = State::new_intensive(functional, temperature, rho, &x)?; + bulk = State::new(functional, temperature, rho, x.clone())?; } Err(FeosError::NotConverged( "Adsorption::phase_equilibrium".into(), diff --git a/crates/feos-dft/src/adsorption/pore.rs b/crates/feos-dft/src/adsorption/pore.rs index faf19e62d..ddf2f7479 100644 --- a/crates/feos-dft/src/adsorption/pore.rs +++ b/crates/feos-dft/src/adsorption/pore.rs @@ -6,9 +6,7 @@ use crate::functional_contribution::FunctionalContribution; use crate::geometry::{Axis, Geometry, Grid}; use crate::profile::{DFTProfile, MAX_POTENTIAL}; use crate::solver::DFTSolver; -use feos_core::{ - Contributions, FeosResult, ReferenceSystem, ResidualDyn, State, StateBuilder, StateHD, -}; +use feos_core::{Contributions, FeosResult, ReferenceSystem, ResidualDyn, State, StateHD}; use nalgebra::{DVector, dvector}; use ndarray::prelude::*; use ndarray::{Axis as Axis_nd, RemoveAxis}; @@ -69,10 +67,7 @@ pub trait PoreSpecification { where D::Larger: Dimension, { - let bulk = StateBuilder::new(&&Helium) - .temperature(298.0 * KELVIN) - .density(Density::from_reduced(1.0)) - .build()?; + let bulk = State::new_pure(&&Helium, 298.0 * KELVIN, Density::from_reduced(1.0))?; let pore = self.initialize(&bulk, None, None)?; let pot = Dimensionless::from_reduced( pore.profile diff --git a/crates/feos-dft/src/interface/mod.rs b/crates/feos-dft/src/interface/mod.rs index 6697535d0..1e9f02b99 100644 --- a/crates/feos-dft/src/interface/mod.rs +++ b/crates/feos-dft/src/interface/mod.rs @@ -84,8 +84,8 @@ impl PlanarInterface { let reduced_temperature = (vle.vapor().temperature / critical_temperature).into_value(); profile.profile.density = Density::from_shape_fn(profile.profile.density.raw_dim(), |(i, z)| { - let rho_v = profile.vle.vapor().partial_density.get(indices[i]); - let rho_l = profile.vle.liquid().partial_density.get(indices[i]); + let rho_v = profile.vle.vapor().partial_density().get(indices[i]); + let rho_l = profile.vle.liquid().partial_density().get(indices[i]); 0.5 * (rho_l - rho_v) * (sign * (profile.profile.grid.grids()[0][z] - z0) / 3.0 * (2.4728 - 2.3625 * reduced_temperature)) @@ -343,8 +343,9 @@ fn interp_symmetric( radius: Length, ) -> FeosResult>> { let reduced_density = Array2::from_shape_fn(rho_pdgt.raw_dim(), |(i, j)| { - ((rho_pdgt.get((i, j)) - vle_pdgt.vapor().partial_density.get(i)) - / (vle_pdgt.liquid().partial_density.get(i) - vle_pdgt.vapor().partial_density.get(i))) + ((rho_pdgt.get((i, j)) - vle_pdgt.vapor().partial_density().get(i)) + / (vle_pdgt.liquid().partial_density().get(i) + - vle_pdgt.vapor().partial_density().get(i))) .into_value() - 0.5 }); @@ -371,8 +372,8 @@ fn interp_symmetric( reduced_density.raw_dim(), |(i, j)| { reduced_density[(i, j)] - * (vle.liquid().partial_density.get(i) - vle.vapor().partial_density.get(i)) - + vle.vapor().partial_density.get(i) + * (vle.liquid().partial_density().get(i) - vle.vapor().partial_density().get(i)) + + vle.vapor().partial_density().get(i) }, )) } diff --git a/crates/feos-dft/src/pdgt.rs b/crates/feos-dft/src/pdgt.rs index 47ad359eb..eb7cfe0f2 100644 --- a/crates/feos-dft/src/pdgt.rs +++ b/crates/feos-dft/src/pdgt.rs @@ -185,7 +185,7 @@ pub trait PdgtFunctionalProperties: HelmholtzEnergyFunctional { let mu_res = vle.vapor().residual_chemical_potential(); for i in 0..self.components() { let rhoi = density.index_axis(Axis(0), i).to_owned(); - let rhoi_b = vle.vapor().partial_density.get(i); + let rhoi_b = vle.vapor().partial_density().get(i); let mui_res = mu_res.get(i); let kt = RGAS * vle.vapor().temperature; delta_omega += @@ -198,8 +198,8 @@ pub trait PdgtFunctionalProperties: HelmholtzEnergyFunctional { let drho = gradient( &density, -dx, - &vle.liquid().partial_density, - &vle.vapor().partial_density, + &vle.liquid().partial_density(), + &vle.vapor().partial_density(), ); // calculate integrand diff --git a/crates/feos-dft/src/profile/mod.rs b/crates/feos-dft/src/profile/mod.rs index edac0e7b2..941a3818b 100644 --- a/crates/feos-dft/src/profile/mod.rs +++ b/crates/feos-dft/src/profile/mod.rs @@ -232,7 +232,7 @@ where let mut bonds = bulk.eos.bond_integrals(t, &exp_dfdrho, convolver.as_ref()); bonds *= &exp_dfdrho; let mut density = Array::zeros(external_potential.raw_dim()); - let bulk_density = bulk.partial_density.to_reduced(); + let bulk_density = bulk.partial_density().into_reduced(); for (s, &c) in bulk.eos.component_index().iter().enumerate() { density.index_axis_mut(Axis_nd(0), s).assign( &(bonds.index_axis(Axis_nd(0), s).map(|is| is.min(1.0)) * bulk_density[c]), @@ -373,7 +373,7 @@ where pub fn residual(&self, log: bool) -> FeosResult<(Array, Array1, f64)> { // Read from profile let density = self.density.to_reduced(); - let partial_density = self.bulk.partial_density.to_reduced(); + let partial_density = self.bulk.partial_density().into_reduced(); let bulk_density = self .bulk .eos @@ -484,7 +484,7 @@ where // Read from profile let component_index = self.bulk.eos.component_index().into_owned(); let mut density = self.density.to_reduced(); - let partial_density = self.bulk.partial_density.to_reduced(); + let partial_density = self.bulk.partial_density().into_reduced(); let mut bulk_density = component_index .iter() .map(|&i| partial_density[i]) @@ -495,13 +495,12 @@ where // Update profile self.density = Density::from_reduced(density); - let volume = Volume::from_reduced(1.0); - let mut moles = self.bulk.moles.clone(); + let mut partial_density = self.bulk.partial_density(); bulk_density .into_iter() .enumerate() - .for_each(|(i, r)| moles.set(component_index[i], Density::from_reduced(r) * volume)); - self.bulk = State::new_nvt(&self.bulk.eos, self.bulk.temperature, volume, &moles)?; + .for_each(|(i, r)| partial_density.set(component_index[i], Density::from_reduced(r))); + self.bulk = State::new_density(&self.bulk.eos, self.bulk.temperature, partial_density)?; Ok(()) } diff --git a/crates/feos-dft/src/profile/properties.rs b/crates/feos-dft/src/profile/properties.rs index 225967409..6095fb172 100644 --- a/crates/feos-dft/src/profile/properties.rs +++ b/crates/feos-dft/src/profile/properties.rs @@ -298,7 +298,7 @@ where { fn density_derivative(&self, lhs: &Array) -> FeosResult> { let rho = self.density.to_reduced(); - let partial_density = self.bulk.partial_density.to_reduced(); + let partial_density = self.bulk.partial_density().into_reduced(); let rho_bulk = self .bulk .eos @@ -402,7 +402,7 @@ where dfdrho += &(&self.external_potential * t).mapv(|v| Dual64::from(v) / t_dual); // calculate bulk functional derivative - let partial_density = self.bulk.partial_density.to_reduced(); + let partial_density = self.bulk.partial_density().into_reduced(); let rho_bulk: Array1<_> = self .bulk .eos diff --git a/crates/feos-dft/src/solvation/pair_correlation.rs b/crates/feos-dft/src/solvation/pair_correlation.rs index d901887cb..25849af4f 100644 --- a/crates/feos-dft/src/solvation/pair_correlation.rs +++ b/crates/feos-dft/src/solvation/pair_correlation.rs @@ -59,12 +59,10 @@ impl PairCorrelation { self.profile.solve(solver, debug)?; // calculate pair correlation function + let partial_density = self.profile.bulk.partial_density(); self.pair_correlation_function = Some(Array::from_shape_fn( self.profile.density.raw_dim(), - |(i, j)| { - (self.profile.density.get((i, j)) / self.profile.bulk.partial_density.get(i)) - .into_value() - }, + |(i, j)| (self.profile.density.get((i, j)) / partial_density.get(i)).into_value(), )); // calculate self solvation free energy diff --git a/crates/feos/benches/contributions.rs b/crates/feos/benches/contributions.rs index 3b2e8b367..3e74bf68c 100644 --- a/crates/feos/benches/contributions.rs +++ b/crates/feos/benches/contributions.rs @@ -74,7 +74,7 @@ fn pcsaft(c: &mut Criterion) { State::new_npt(&&eos, t, p, &moles, Some(DensityInitialization::Liquid)).unwrap(); let temperature = Dual64::from(state.temperature.into_reduced()).derivative(); let molar_volume = Dual::from(1.0 / state.density.into_reduced()); - let moles = state.moles.to_reduced().map(Dual::from); + let moles = state.moles().to_reduced().map(Dual::from); // let state_hd = state.derive1(Derivative::DT); let name1 = comp1.identifier.name.as_deref().unwrap(); let name2 = comp2.identifier.name.as_deref().unwrap(); diff --git a/crates/feos/benches/dft_pore.rs b/crates/feos/benches/dft_pore.rs index 667c5ff12..98dcba4e2 100644 --- a/crates/feos/benches/dft_pore.rs +++ b/crates/feos/benches/dft_pore.rs @@ -2,7 +2,7 @@ //! in pores at different conditions. use criterion::{Criterion, criterion_group, criterion_main}; use feos::core::parameter::IdentifierOption; -use feos::core::{PhaseEquilibrium, State, StateBuilder}; +use feos::core::{PhaseEquilibrium, State}; use feos::dft::adsorption::{ExternalPotential, Pore1D, PoreSpecification}; use feos::dft::{DFTSolver, Geometry}; use feos::gc_pcsaft::{GcPcSaftFunctional, GcPcSaftParameters}; @@ -80,11 +80,8 @@ fn pcsaft(c: &mut Criterion) { group.bench_function("butane_pentane_liquid", |b| { b.iter(|| pore.initialize(bulk, None, None).unwrap().solve(None)) }); - let bulk = StateBuilder::new(&func) - .temperature(300.0 * KELVIN) - .partial_density(&(&vle.vapor().partial_density * 0.2)) - .build() - .unwrap(); + let bulk = + State::new_density(&func, 300.0 * KELVIN, vle.vapor().partial_density() * 0.2).unwrap(); group.bench_function("butane_pentane_vapor", |b| { b.iter(|| pore.initialize(&bulk, None, None).unwrap().solve(None)) }); diff --git a/crates/feos/benches/dual_numbers.rs b/crates/feos/benches/dual_numbers.rs index 454c6828f..c3c1bd07f 100644 --- a/crates/feos/benches/dual_numbers.rs +++ b/crates/feos/benches/dual_numbers.rs @@ -21,9 +21,9 @@ use quantity::*; fn state_pcsaft(n: usize, eos: &PcSaft) -> State<&PcSaft> { let moles = DVector::from_element(n, 1.0 / n as f64) * 10.0 * MOL; let molefracs = (&moles / moles.sum()).into_value(); - let cp = State::critical_point(&eos, Some(&molefracs), None, None, Default::default()).unwrap(); + let cp = State::critical_point(&eos, molefracs, None, None, Default::default()).unwrap(); let temperature = 0.8 * cp.temperature; - State::new_nvt(&eos, temperature, cp.volume, &moles).unwrap() + State::new_nvt(&eos, temperature, cp.volume(), moles).unwrap() } /// Residual Helmholtz energy given an equation of state and a StateHD. @@ -152,11 +152,11 @@ enum Derivative { /// Creates a [StateHD] cloning temperature, volume and moles. fn derive0(state: &State) -> StateHD { - let total_moles = state.total_moles.into_reduced(); + let total_moles = state.total_moles().into_reduced(); StateHD::new( state.temperature.into_reduced(), - state.volume.into_reduced() / total_moles, - &(state.moles.to_reduced() / total_moles), + state.volume().into_reduced() / total_moles, + &(state.moles().to_reduced() / total_moles), ) } diff --git a/crates/feos/benches/dual_numbers_saftvrmie.rs b/crates/feos/benches/dual_numbers_saftvrmie.rs index 0d6329974..e7409e307 100644 --- a/crates/feos/benches/dual_numbers_saftvrmie.rs +++ b/crates/feos/benches/dual_numbers_saftvrmie.rs @@ -18,9 +18,9 @@ use quantity::*; /// - molefracs (or moles) for equimolar mixture. fn state_saftvrmie(n: usize, eos: &SaftVRMie) -> State<&SaftVRMie> { let molefracs = DVector::from_element(n, 1.0 / n as f64); - let cp = State::critical_point(&eos, Some(&molefracs), None, None, Default::default()).unwrap(); + let cp = State::critical_point(&eos, &molefracs, None, None, Default::default()).unwrap(); let temperature = 0.8 * cp.temperature; - State::new_nvt(&eos, temperature, cp.volume, &(molefracs * 10. * MOL)).unwrap() + State::new_nvt(&eos, temperature, cp.volume(), &(molefracs * 10. * MOL)).unwrap() } /// Residual Helmholtz energy given an equation of state and a StateHD. @@ -100,11 +100,11 @@ enum Derivative { /// Creates a [StateHD] cloning temperature, volume and moles. fn derive0(state: &State) -> StateHD { - let total_moles = state.total_moles.into_reduced(); + let total_moles = state.total_moles().into_reduced(); StateHD::new( state.temperature.into_reduced(), - state.volume.into_reduced() / total_moles, - &(state.moles.to_reduced() / total_moles), + state.volume().into_reduced() / total_moles, + &(state.moles().to_reduced() / total_moles), ) } diff --git a/crates/feos/benches/state_creation.rs b/crates/feos/benches/state_creation.rs index 6110abd68..3aa08778e 100644 --- a/crates/feos/benches/state_creation.rs +++ b/crates/feos/benches/state_creation.rs @@ -22,7 +22,7 @@ fn npt( } /// Evaluate critical point constructor -fn critical_point((eos, n): (&E, Option<&DVector>)) { +fn critical_point((eos, n): (&E, &DVector)) { State::critical_point(eos, n, None, None, Default::default()).unwrap(); } @@ -69,7 +69,7 @@ fn bench_states(c: &mut Criterion, group_name: &str, eos: &E) { let ncomponents = eos.components(); let x = DVector::from_element(ncomponents, 1.0 / ncomponents as f64); let n = &x * 100.0 * MOL; - let crit = State::critical_point(eos, Some(&x), None, None, Default::default()).unwrap(); + let crit = State::critical_point(eos, &x, None, None, Default::default()).unwrap(); let vle = if ncomponents == 1 { PhaseEquilibrium::pure(eos, crit.temperature * 0.95, None, Default::default()).unwrap() } else { @@ -77,7 +77,7 @@ fn bench_states(c: &mut Criterion, group_name: &str, eos: &E) { eos, crit.temperature, crit.pressure(Contributions::Total) * 0.95, - &crit.moles, + &crit.moles(), None, Default::default(), None, @@ -108,9 +108,7 @@ fn bench_states(c: &mut Criterion, group_name: &str, eos: &E) { )) }) }); - group.bench_function("critical_point", |b| { - b.iter(|| critical_point((eos, Some(&x)))) - }); + group.bench_function("critical_point", |b| b.iter(|| critical_point((eos, &x)))); if ncomponents == 2 { group.bench_function("critical_point_binary_t", |b| { b.iter(|| critical_point_binary((eos, crit.temperature))) diff --git a/crates/feos/src/epcsaft/eos/mod.rs b/crates/feos/src/epcsaft/eos/mod.rs index 03a2713d0..2edba6f69 100644 --- a/crates/feos/src/epcsaft/eos/mod.rs +++ b/crates/feos/src/epcsaft/eos/mod.rs @@ -181,7 +181,7 @@ mod tests { let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&&e, t, v, &n).unwrap(); - let p_ig = s.total_moles * RGAS * t / v; + let p_ig = s.total_moles() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), @@ -197,7 +197,7 @@ mod tests { let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&&e, t, v, &n).unwrap(); - let p_ig = s.total_moles * RGAS * t / v; + let p_ig = s.total_moles() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), @@ -269,7 +269,7 @@ mod tests { fn critical_point() { let e = ElectrolytePcSaft::new(propane_parameters()).unwrap(); let t = 300.0 * KELVIN; - let cp = State::critical_point(&&e, None, Some(t), None, Default::default()); + let cp = State::critical_point(&&e, (), Some(t), None, Default::default()); if let Ok(v) = cp { assert_relative_eq!(v.temperature, 375.1244078318015 * KELVIN, epsilon = 1e-8) } diff --git a/crates/feos/src/gc_pcsaft/eos/ad.rs b/crates/feos/src/gc_pcsaft/eos/ad.rs index 252f9a061..72b251eaa 100644 --- a/crates/feos/src/gc_pcsaft/eos/ad.rs +++ b/crates/feos/src/gc_pcsaft/eos/ad.rs @@ -510,7 +510,7 @@ pub mod test { let eos_ad = GcPcSaftAD(params); let moles = vector![1.3] * KILO * MOL; - let state = State::new_nvt(&eos_ad, temperature, volume, &moles)?; + let state = State::new_nvt(&eos_ad, temperature, volume, moles)?; let a_ad = state.residual_molar_helmholtz_energy(); let mu_ad = state.residual_chemical_potential(); let p_ad = state.pressure(Total); diff --git a/crates/feos/src/ideal_gas/dippr.rs b/crates/feos/src/ideal_gas/dippr.rs index 7553e376c..89824b07a 100644 --- a/crates/feos/src/ideal_gas/dippr.rs +++ b/crates/feos/src/ideal_gas/dippr.rs @@ -156,7 +156,7 @@ impl IdealGas for Dippr { mod tests { use approx::assert_relative_eq; use feos_core::parameter::{Identifier, PureRecord}; - use feos_core::{Contributions, EquationOfState, StateBuilder}; + use feos_core::{Contributions, EquationOfState, State}; use num_dual::first_derivative; use quantity::*; @@ -173,11 +173,7 @@ mod tests { let eos = EquationOfState::ideal_gas(dippr.clone()); let temperature = 300.0 * KELVIN; let volume = METER.powi::<3>(); - let state = StateBuilder::new(&&eos) - .temperature(temperature) - .volume(volume) - .total_moles(MOL) - .build()?; + let state = State::new_nvt(&&eos, temperature, volume, MOL)?; let t = temperature.convert_to(KELVIN); let c_p_direct = record.model_record.c_p(t); @@ -217,11 +213,7 @@ mod tests { let eos = EquationOfState::ideal_gas(dippr.clone()); let temperature = 300.0 * KELVIN; let volume = METER.powi::<3>(); - let state = StateBuilder::new(&&eos) - .temperature(temperature) - .volume(volume) - .total_moles(MOL) - .build()?; + let state = State::new_nvt(&&eos, temperature, volume, MOL)?; let t = temperature.convert_to(KELVIN); let c_p_direct = record.model_record.c_p(t); @@ -263,11 +255,7 @@ mod tests { let eos = EquationOfState::ideal_gas(dippr.clone()); let temperature = 20.0 * KELVIN; let volume = METER.powi::<3>(); - let state = StateBuilder::new(&&eos) - .temperature(temperature) - .volume(volume) - .total_moles(MOL) - .build()?; + let state = State::new_nvt(&&eos, temperature, volume, MOL)?; let t = temperature.convert_to(KELVIN); let c_p_direct = record.model_record.c_p(t); diff --git a/crates/feos/src/ideal_gas/joback.rs b/crates/feos/src/ideal_gas/joback.rs index 742c6ef2e..8f2279d5b 100644 --- a/crates/feos/src/ideal_gas/joback.rs +++ b/crates/feos/src/ideal_gas/joback.rs @@ -172,10 +172,8 @@ const KB: f64 = 1.38064852e-23; #[cfg(test)] mod tests { use approx::assert_relative_eq; - use feos_core::{ - Contributions, EquationOfState, State, StateBuilder, - parameter::{ChemicalRecord, GroupCount, Identifier, PureRecord, SegmentRecord}, - }; + use feos_core::parameter::{ChemicalRecord, GroupCount, Identifier, PureRecord, SegmentRecord}; + use feos_core::{Contributions, EquationOfState, State}; use nalgebra::dvector; use quantity::*; use std::collections::HashMap; @@ -295,11 +293,7 @@ mod tests { let temperature = 300.0 * KELVIN; let volume = METER.powi::<3>(); let moles = &dvector![1.0, 3.0] * MOL; - let state = StateBuilder::new(&&eos) - .temperature(temperature) - .volume(volume) - .moles(&moles) - .build()?; + let state = State::new_nvt(&&eos, temperature, volume, moles)?; println!( "{} {}", Joback::molar_isobaric_heat_capacity(&joback, temperature, &state.molefracs)?, diff --git a/crates/feos/src/lib.rs b/crates/feos/src/lib.rs index eab5ae128..0fbdffa71 100644 --- a/crates/feos/src/lib.rs +++ b/crates/feos/src/lib.rs @@ -23,7 +23,7 @@ //! let saft = PcSaft::new(parameters); //! //! // Define thermodynamic conditions. -//! let critical_point = State::critical_point(&&saft, Some(&dvector![1.0]), None, None, Default::default())?; +//! let critical_point = State::critical_point(&&saft, dvector![1.0], None, None, Default::default())?; //! //! // Compute properties. //! let p = critical_point.pressure(Contributions::Total); diff --git a/crates/feos/src/multiparameter/mod.rs b/crates/feos/src/multiparameter/mod.rs index 68bbd98b8..b4a6c4f9f 100644 --- a/crates/feos/src/multiparameter/mod.rs +++ b/crates/feos/src/multiparameter/mod.rs @@ -265,8 +265,13 @@ mod test { let eos = &water(); let mw = eos.molar_weight.get(0); let moles = dvector![1.8] * MOL; - let a_feos = eos.ideal_gas_helmholtz_energy(t, moles.sum() * mw / rho, &moles); - let phi_feos = (a_feos / RGAS / moles.sum() / t).into_value(); + let total_moles = moles.sum(); + let a_feos = eos.ideal_gas_helmholtz_energy( + t, + moles.sum() * mw / rho / total_moles, + &moles.convert_into(total_moles), + ); + let phi_feos = (a_feos / RGAS / t).into_value(); println!("A: {a_feos}"); println!("phi(feos): {phi_feos}"); let delta = (rho / (eos.rhoc * MOL / METER.powi::<3>() * mw)).into_value(); @@ -288,15 +293,15 @@ mod test { ..Default::default() }; let cp: State<_, Dyn, f64> = - State::critical_point(&eos, None, Some(647. * KELVIN), None, options).unwrap(); + State::critical_point(&eos, (), Some(647. * KELVIN), None, options).unwrap(); println!("{cp}"); assert_relative_eq!(cp.temperature, eos.tc * KELVIN, max_relative = 1e-13); let cp: State<_, Dyn, f64> = - State::critical_point(&eos, None, None, None, Default::default()).unwrap(); + State::critical_point(&eos, (), None, None, Default::default()).unwrap(); println!("{cp}"); assert_relative_ne!(cp.temperature, eos.tc * KELVIN, max_relative = 1e-13); let cp: State<_, Dyn, f64> = - State::critical_point(&eos, None, Some(700.0 * KELVIN), None, Default::default()) + State::critical_point(&eos, (), Some(700.0 * KELVIN), None, Default::default()) .unwrap(); println!("{cp}"); assert_relative_eq!(cp.temperature, eos.tc * KELVIN, max_relative = 1e-13) diff --git a/crates/feos/src/pcsaft/eos/mod.rs b/crates/feos/src/pcsaft/eos/mod.rs index 45d3635da..752e7c6f6 100644 --- a/crates/feos/src/pcsaft/eos/mod.rs +++ b/crates/feos/src/pcsaft/eos/mod.rs @@ -229,12 +229,11 @@ impl EntropyScaling for PcSaft { fn viscosity_reference( &self, temperature: Temperature, - _: Volume, - moles: &Moles>, + _: MolarVolume, + molefracs: &DVector, ) -> Viscosity { let p = &self.params; let mw = &self.parameters.molar_weight; - let x = (moles / moles.sum()).into_value(); let ce: Vec<_> = (0..self.components()) .map(|i| { let tr = (temperature / p.epsilon_k[i] / KELVIN).into_value(); @@ -247,14 +246,15 @@ impl EntropyScaling for PcSaft { for i in 0..self.components() { let denom: f64 = (0..self.components()) .map(|j| { - x[j] * (1.0 - + (ce[i] / ce[j]).into_value().sqrt() - * (mw.get(j) / mw.get(i)).powf(1.0 / 4.0)) - .powi(2) + molefracs[j] + * (1.0 + + (ce[i] / ce[j]).into_value().sqrt() + * (mw.get(j) / mw.get(i)).powf(1.0 / 4.0)) + .powi(2) / (8.0 * (1.0 + (mw.get(i) / mw.get(j)).into_value())).sqrt() }) .sum(); - ce_mix += ce[i] * x[i] / denom + ce_mix += ce[i] * molefracs[i] / denom } ce_mix } @@ -278,19 +278,19 @@ impl EntropyScaling for PcSaft { fn diffusion_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + _: &DVector, ) -> Diffusivity { if self.components() != 1 { panic!("Diffusion coefficients in PC-SAFT are only implemented for pure components!"); } let p = &self.params; let mw = &self.parameters.molar_weight; - let density = moles.sum() / volume; let res: Vec<_> = (0..self.components()) .map(|i| { let tr = (temperature / p.epsilon_k[i] / KELVIN).into_value(); - 3.0 / 8.0 / (p.sigma[i] * ANGSTROM).powi::<2>() / omega11(tr) / (density * NAV) + 3.0 / 8.0 / (p.sigma[i] * ANGSTROM).powi::<2>() / omega11(tr) + * (molar_volume / NAV) * (temperature * RGAS / PI / mw.get(i) / p.m[i]).sqrt() }) .collect(); @@ -321,8 +321,8 @@ impl EntropyScaling for PcSaft { fn thermal_conductivity_reference( &self, temperature: Temperature, - volume: Volume, - moles: &Moles>, + molar_volume: MolarVolume, + molefracs: &DVector, ) -> ThermalConductivity { if self.components() != 1 { panic!("Thermal conductivity in PC-SAFT is only implemented for pure components!"); @@ -331,9 +331,9 @@ impl EntropyScaling for PcSaft { let mws = self.molar_weight(); let (_, s_res) = first_derivative( partial2( - |t, &v, n| -self.residual_helmholtz_energy_unit(t, v, n) / n.sum(), - &volume, - moles, + |t, &v, n| -self.residual_molar_helmholtz_energy(t, v, n), + &molar_volume, + molefracs, ), temperature, ); @@ -399,8 +399,8 @@ mod tests { let t = 200.0 * KELVIN; let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; - let s = State::new_nvt(&e, t, v, &n).unwrap(); - let p_ig = s.total_moles * RGAS * t / v; + let s = State::new_nvt(&e, t, v, n).unwrap(); + let p_ig = s.total_moles() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), @@ -415,8 +415,8 @@ mod tests { let t = 200.0 * KELVIN; let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; - let s = State::new_nvt(&e, t, v, &n).unwrap(); - let p_ig = s.total_moles * RGAS * t / v; + let s = State::new_nvt(&e, t, v, n).unwrap(); + let p_ig = s.total_moles() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), @@ -463,7 +463,7 @@ mod tests { let t = 300.0 * KELVIN; let p = BAR; let m = dvector![1.5] * MOL; - let s = State::new_npt(&e, t, p, &m, None); + let s = State::new_npt(&e, t, p, m, None); let p_calc = if let Ok(state) = s { state.pressure(Contributions::Total) } else { @@ -490,7 +490,7 @@ mod tests { fn critical_point() { let e = &propane_parameters(); let t = 300.0 * KELVIN; - let cp = State::critical_point(&e, None, Some(t), None, Default::default()); + let cp = State::critical_point(&e, (), Some(t), None, Default::default()); if let Ok(v) = cp { assert_relative_eq!(v.temperature, 375.1244078318015 * KELVIN, epsilon = 1e-8) } @@ -507,9 +507,9 @@ mod tests { let m1m = dvector![2.0, 0.0] * MOL; let m2m = dvector![0.0, 2.0] * MOL; let s1 = State::new_nvt(&e1, t, v, &m1).unwrap(); - let s2 = State::new_nvt(&e2, t, v, &m1).unwrap(); - let s1m = State::new_nvt(&e12, t, v, &m1m).unwrap(); - let s2m = State::new_nvt(&e12, t, v, &m2m).unwrap(); + let s2 = State::new_nvt(&e2, t, v, m1).unwrap(); + let s1m = State::new_nvt(&e12, t, v, m1m).unwrap(); + let s2m = State::new_nvt(&e12, t, v, m2m).unwrap(); assert_relative_eq!( s1.pressure(Contributions::Total), s1m.pressure(Contributions::Total), @@ -528,7 +528,7 @@ mod tests { let t = 300.0 * KELVIN; let p = BAR; let n = dvector![1.0] * MOL; - let s = State::new_npt(&e, t, p, &n, None)?; + let s = State::new_npt(&e, t, p, n, None)?; assert_relative_eq!( s.viscosity(), 0.00797 * MILLI * PASCAL * SECOND, @@ -536,7 +536,7 @@ mod tests { ); assert_relative_eq!( s.ln_viscosity_reduced(), - (s.viscosity() / e.viscosity_reference(s.temperature, s.volume, &s.moles)) + (s.viscosity() / e.viscosity_reference(s.temperature, s.molar_volume, &s.molefracs)) .into_value() .ln(), epsilon = 1e-15 @@ -553,15 +553,15 @@ mod tests { let t = 303.15 * KELVIN; let p = 500.0 * BAR; let n = dvector![0.25, 0.75] * MOL; - let viscosity_mix = State::new_npt(&e, t, p, &n, None)?.viscosity(); + let viscosity_mix = State::new_npt(&e, t, p, n, None)?.viscosity(); let viscosity_paper = 0.68298 * MILLI * PASCAL * SECOND; assert_relative_eq!(viscosity_paper, viscosity_mix, epsilon = 1e-8); // Make sure pure substance case is recovered let n_pseudo_mix = dvector![1.0, 0.0] * MOL; - let viscosity_pseudo_mix = State::new_npt(&e, t, p, &n_pseudo_mix, None)?.viscosity(); + let viscosity_pseudo_mix = State::new_npt(&e, t, p, n_pseudo_mix, None)?.viscosity(); let n_nonane = dvector![1.0] * MOL; - let viscosity_nonane = State::new_npt(&nonane, t, p, &n_nonane, None)?.viscosity(); + let viscosity_nonane = State::new_npt(&nonane, t, p, n_nonane, None)?.viscosity(); assert_relative_eq!(viscosity_pseudo_mix, viscosity_nonane, epsilon = 1e-15); Ok(()) } @@ -572,7 +572,7 @@ mod tests { let t = 300.0 * KELVIN; let p = BAR; let n = dvector![1.0] * MOL; - let s = State::new_npt(&e, t, p, &n, None)?; + let s = State::new_npt(&e, t, p, n, None)?; assert_relative_eq!( s.diffusion(), 0.01505 * (CENTI * METER).powi::<2>() / SECOND, @@ -580,7 +580,7 @@ mod tests { ); assert_relative_eq!( s.ln_diffusion_reduced(), - (s.diffusion() / e.diffusion_reference(s.temperature, s.volume, &s.moles)) + (s.diffusion() / e.diffusion_reference(s.temperature, s.molar_volume, &s.molefracs)) .into_value() .ln(), epsilon = 1e-15 @@ -787,14 +787,9 @@ mod tests_parameter_fit { let h = params[i] * 1e-7; params[i] += h; let pcsaft_h = PcSaftPure(params); - let rho_h = State::new_xpt( - &pcsaft_h, - temperature, - pressure, - &vector![1.0], - Some(Liquid), - )? - .density; + let rho_h = + State::new_npt(&pcsaft_h, temperature, pressure, vector![1.0], Some(Liquid))? + .density; let drho_h = (rho_h.convert_into(MOL / LITER) - rho) / h; let drho = grad[i]; println!( diff --git a/crates/feos/src/pcsaft/eos/pcsaft_binary.rs b/crates/feos/src/pcsaft/eos/pcsaft_binary.rs index 3eecc9db4..71b15ae88 100644 --- a/crates/feos/src/pcsaft/eos/pcsaft_binary.rs +++ b/crates/feos/src/pcsaft/eos/pcsaft_binary.rs @@ -557,7 +557,7 @@ pub mod test { let h_feos = state.residual_molar_enthalpy(); let moles = vector![1.3, 2.5] * KILO * MOL; - let state = State::new_nvt(&pcsaft, temperature, volume, &moles)?; + let state = State::new_nvt(&pcsaft, temperature, volume, moles)?; let a_ad = state.residual_molar_helmholtz_energy(); let mu_ad = state.residual_chemical_potential(); let p_ad = state.pressure(Total); diff --git a/crates/feos/src/pcsaft/eos/pcsaft_pure.rs b/crates/feos/src/pcsaft/eos/pcsaft_pure.rs index baa5bc10f..f9ea2983f 100644 --- a/crates/feos/src/pcsaft/eos/pcsaft_pure.rs +++ b/crates/feos/src/pcsaft/eos/pcsaft_pure.rs @@ -228,9 +228,9 @@ impl ParametersAD<1> for PcSaftPure { #[cfg(test)] pub mod test { - use crate::pcsaft::PcSaftRecord; use super::super::{PcSaft, PcSaftAssociationRecord, PcSaftParameters}; use super::*; + use crate::pcsaft::PcSaftRecord; use approx::assert_relative_eq; use feos_core::parameter::{AssociationRecord, PureRecord}; use feos_core::{Contributions::Total, FeosResult, State}; @@ -278,7 +278,7 @@ pub mod test { let h_feos = state.residual_molar_enthalpy(); let moles = vector![1.3] * KILO * MOL; - let state = State::new_nvt(&pcsaft, temperature, volume, &moles)?; + let state = State::new_nvt(&pcsaft, temperature, volume, moles)?; let a_ad = state.residual_molar_helmholtz_energy(); let mu_ad = state.residual_chemical_potential(); let p_ad = state.pressure(Total); diff --git a/crates/feos/src/pets/eos/mod.rs b/crates/feos/src/pets/eos/mod.rs index f1d1d76cb..e26b94960 100644 --- a/crates/feos/src/pets/eos/mod.rs +++ b/crates/feos/src/pets/eos/mod.rs @@ -152,7 +152,7 @@ mod tests { let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&e, t, v, &n).unwrap(); - let p_ig = s.total_moles * RGAS * t / v; + let p_ig = s.total_moles() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), diff --git a/crates/feos/src/uvtheory/eos/mod.rs b/crates/feos/src/uvtheory/eos/mod.rs index 9fa963ae2..8a660db81 100644 --- a/crates/feos/src/uvtheory/eos/mod.rs +++ b/crates/feos/src/uvtheory/eos/mod.rs @@ -246,8 +246,9 @@ mod test { // EoS let eos_wca = &UVTheory::new(parameters); let state_wca = State::new_nvt(&eos_wca, t_x, volume, &moles).unwrap(); - let a_wca = (state_wca.residual_helmholtz_energy() / (RGAS * t_x * state_wca.total_moles)) - .into_value(); + let a_wca = (state_wca.residual_helmholtz_energy() + / (RGAS * t_x * state_wca.total_moles())) + .into_value(); assert_relative_eq!(a_wca, -0.597791038364405, max_relative = 1e-5); Ok(()) diff --git a/crates/feos/tests/gc_pcsaft/binary.rs b/crates/feos/tests/gc_pcsaft/binary.rs index e9b9ed434..ce627ec8c 100644 --- a/crates/feos/tests/gc_pcsaft/binary.rs +++ b/crates/feos/tests/gc_pcsaft/binary.rs @@ -30,9 +30,9 @@ fn test_binary() -> FeosResult<()> { #[cfg(feature = "dft")] let func = &GcPcSaftFunctional::new(parameters_func); let molefracs = dvector![0.5, 0.5]; - let cp = State::critical_point(&eos, Some(&molefracs), None, None, Default::default())?; + let cp = State::critical_point(&eos, &molefracs, None, None, Default::default())?; #[cfg(feature = "dft")] - let cp_func = State::critical_point(&func, Some(&molefracs), None, None, Default::default())?; + let cp_func = State::critical_point(&func, molefracs, None, None, Default::default())?; println!("{}", cp.temperature); #[cfg(feature = "dft")] println!("{}", cp_func.temperature); diff --git a/crates/feos/tests/gc_pcsaft/dft.rs b/crates/feos/tests/gc_pcsaft/dft.rs index a9813c56e..ce5d103cb 100644 --- a/crates/feos/tests/gc_pcsaft/dft.rs +++ b/crates/feos/tests/gc_pcsaft/dft.rs @@ -3,7 +3,7 @@ use approx::assert_relative_eq; use feos::gc_pcsaft::{GcPcSaft, GcPcSaftFunctional, GcPcSaftParameters}; use feos_core::parameter::{ChemicalRecord, Identifier, IdentifierOption, SegmentRecord}; -use feos_core::{PhaseEquilibrium, State, StateBuilder, Verbosity}; +use feos_core::{PhaseEquilibrium, State, Verbosity}; use feos_dft::adsorption::{ExternalPotential, Pore1D, PoreSpecification}; use feos_dft::interface::PlanarInterface; use feos_dft::{DFTSolver, Geometry}; @@ -158,7 +158,7 @@ fn test_dft() -> Result<(), Box> { let t = 200.0 * KELVIN; let w = 150.0 * ANGSTROM; let points = 2048; - let tc = State::critical_point(&&func, None, None, None, Default::default())?.temperature; + let tc = State::critical_point(&&func, (), None, None, Default::default())?.temperature; let vle = PhaseEquilibrium::pure(&&func, t, None, Default::default())?; let profile = PlanarInterface::from_tanh(&vle, points, w, tc, false).solve(None)?; println!( @@ -216,10 +216,7 @@ fn test_dft_assoc() -> Result<(), Box> { let solver = DFTSolver::new(Some(Verbosity::Iter)) .picard_iteration(None, None, Some(1e-5), Some(0.05)) .anderson_mixing(None, None, None, None, None); - let bulk = StateBuilder::new(&func) - .temperature(t) - .pressure(5.0 * BAR) - .build()?; + let bulk = State::new_npt(&func, t, 5.0 * BAR, (), None)?; Pore1D::new( Geometry::Cartesian, 20.0 * ANGSTROM, @@ -252,7 +249,7 @@ fn test_dft_newton() -> Result<(), Box> { let t = 200.0 * KELVIN; let w = 150.0 * ANGSTROM; let points = 512; - let tc = State::critical_point(&&func, None, None, None, Default::default())?.temperature; + let tc = State::critical_point(&&func, (), None, None, Default::default())?.temperature; let vle = PhaseEquilibrium::pure(&&func, t, None, Default::default())?; let solver = DFTSolver::new(Some(Verbosity::Iter)) .picard_iteration(None, Some(10), None, None) diff --git a/crates/feos/tests/pcsaft/critical_point.rs b/crates/feos/tests/pcsaft/critical_point.rs index 9183e5e5d..f0f435b13 100644 --- a/crates/feos/tests/pcsaft/critical_point.rs +++ b/crates/feos/tests/pcsaft/critical_point.rs @@ -17,7 +17,7 @@ fn test_critical_point_pure() -> Result<(), Box> { )?; let saft = PcSaft::new(params); let t = 300.0 * KELVIN; - let cp = State::critical_point(&&saft, None, Some(t), None, Default::default())?; + let cp = State::critical_point(&&saft, (), Some(t), None, Default::default())?; assert_relative_eq!(cp.temperature, 375.12441 * KELVIN, max_relative = 1e-8); assert_relative_eq!( cp.density, @@ -38,7 +38,7 @@ fn test_critical_point_mix() -> Result<(), Box> { let saft = PcSaft::new(params); let t = 300.0 * KELVIN; let molefracs = dvector![0.5, 0.5]; - let cp = State::critical_point(&&saft, Some(&molefracs), Some(t), None, Default::default())?; + let cp = State::critical_point(&&saft, molefracs, Some(t), None, Default::default())?; assert_relative_eq!(cp.temperature, 407.93481 * KELVIN, max_relative = 1e-8); assert_relative_eq!( cp.density, @@ -64,10 +64,10 @@ fn test_critical_point_limits() -> Result<(), Box> { let cp_pure = State::critical_point_pure(&saft, None, None, options)?; println!("{} {}", cp_pure[0], cp_pure[1]); let molefracs = dvector![0.0, 1.0]; - let cp_2 = State::critical_point(&saft, Some(&molefracs), None, None, options)?; + let cp_2 = State::critical_point(&saft, &molefracs, None, None, options)?; println!("{}", cp_2); let molefracs = dvector![1.0, 0.0]; - let cp_1 = State::critical_point(&saft, Some(&molefracs), None, None, options)?; + let cp_1 = State::critical_point(&saft, &molefracs, None, None, options)?; println!("{}", cp_1); assert_eq!(cp_pure[0].temperature, cp_1.temperature); assert_eq!(cp_pure[0].density, cp_1.density); diff --git a/crates/feos/tests/pcsaft/dft.rs b/crates/feos/tests/pcsaft/dft.rs index e8d057e47..b04a40aa1 100644 --- a/crates/feos/tests/pcsaft/dft.rs +++ b/crates/feos/tests/pcsaft/dft.rs @@ -103,7 +103,7 @@ fn test_dft_propane() -> Result<(), Box> { let t = 200.0 * KELVIN; let w = 150.0 * ANGSTROM; let points = 2048; - let tc = State::critical_point(&&func_pure, None, None, None, Default::default())?.temperature; + let tc = State::critical_point(&&func_pure, (), None, None, Default::default())?.temperature; let vle_pure = PhaseEquilibrium::pure(&&func_pure, t, None, Default::default())?; let vle_full = PhaseEquilibrium::pure(&&func_full, t, None, Default::default())?; let vle_full_vec = PhaseEquilibrium::pure(&&func_full_vec, t, None, Default::default())?; @@ -213,7 +213,7 @@ fn test_dft_propane_newton() -> Result<(), Box> { let t = 200.0 * KELVIN; let w = 150.0 * ANGSTROM; let points = 512; - let tc = State::critical_point(&&func, None, None, None, Default::default())?.temperature; + let tc = State::critical_point(&&func, (), None, None, Default::default())?.temperature; let vle = PhaseEquilibrium::pure(&&func, t, None, Default::default())?; let solver = DFTSolver::new(Some(Verbosity::Iter)).newton(None, None, None, None); PlanarInterface::from_tanh(&vle, points, w, tc, false).solve(Some(&solver))?; @@ -234,7 +234,7 @@ fn test_dft_water() -> Result<(), Box> { let t = 400.0 * KELVIN; let w = 120.0 * ANGSTROM; let points = 2048; - let tc = State::critical_point(&&func_pure, None, None, None, Default::default())?.temperature; + let tc = State::critical_point(&&func_pure, (), None, None, Default::default())?.temperature; let vle_pure = PhaseEquilibrium::pure(&&func_pure, t, None, Default::default())?; let vle_full_vec = PhaseEquilibrium::pure(&&func_full_vec, t, None, Default::default())?; let profile_pure = PlanarInterface::from_tanh(&vle_pure, points, w, tc, false).solve(None)?; @@ -334,33 +334,39 @@ fn test_entropy_bulk_values() -> Result<(), Box> { println!("\nResidual:\n{s_res:?}"); println!( "liquid: {:?}, vapor: {:?}", - profile.vle.liquid().entropy(Contributions::Residual) / profile.vle.liquid().volume, - profile.vle.vapor().entropy(Contributions::Residual) / profile.vle.vapor().volume + profile.vle.liquid().molar_entropy(Contributions::Residual) + / profile.vle.liquid().molar_volume, + profile.vle.vapor().molar_entropy(Contributions::Residual) + / profile.vle.vapor().molar_volume ); println!("\nTotal:\n{s_tot:?}"); println!( "liquid: {:?}, vapor: {:?}", - profile.vle.liquid().entropy(Contributions::Total) / profile.vle.liquid().volume, - profile.vle.vapor().entropy(Contributions::Total) / profile.vle.vapor().volume + profile.vle.liquid().molar_entropy(Contributions::Total) + / profile.vle.liquid().molar_volume, + profile.vle.vapor().molar_entropy(Contributions::Total) / profile.vle.vapor().molar_volume ); assert_relative_eq!( s_res.get(0), - profile.vle.liquid().entropy(Contributions::Residual) / profile.vle.liquid().volume, + profile.vle.liquid().molar_entropy(Contributions::Residual) + / profile.vle.liquid().molar_volume, max_relative = 1e-8, ); assert_relative_eq!( s_res.get(2047), - profile.vle.vapor().entropy(Contributions::Residual) / profile.vle.vapor().volume, + profile.vle.vapor().molar_entropy(Contributions::Residual) + / profile.vle.vapor().molar_volume, max_relative = 1e-8, ); assert_relative_eq!( s_tot.get(0), - profile.vle.liquid().entropy(Contributions::Total) / profile.vle.liquid().volume, + profile.vle.liquid().molar_entropy(Contributions::Total) + / profile.vle.liquid().molar_volume, max_relative = 1e-8, ); assert_relative_eq!( s_tot.get(2047), - profile.vle.vapor().entropy(Contributions::Total) / profile.vle.vapor().volume, + profile.vle.vapor().molar_entropy(Contributions::Total) / profile.vle.vapor().molar_volume, max_relative = 1e-8, ); Ok(()) diff --git a/crates/feos/tests/pcsaft/properties.rs b/crates/feos/tests/pcsaft/properties.rs index 3b05c86e1..489760e5e 100644 --- a/crates/feos/tests/pcsaft/properties.rs +++ b/crates/feos/tests/pcsaft/properties.rs @@ -1,7 +1,7 @@ use approx::assert_relative_eq; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::IdentifierOption; -use feos_core::{Residual, StateBuilder}; +use feos_core::{DensityInitialization::Vapor, Residual, State}; use nalgebra::dvector; use quantity::*; use std::error::Error; @@ -18,18 +18,8 @@ fn test_dln_phi_dp() -> Result<(), Box> { let t = 300.0 * KELVIN; let p = BAR; let h = 1e-1 * PASCAL; - let s = StateBuilder::new(&&saft) - .temperature(t) - .pressure(p) - .molefracs(&dvector![0.5, 0.5]) - .vapor() - .build()?; - let sh = StateBuilder::new(&&saft) - .temperature(t) - .pressure(p + h) - .molefracs(&dvector![0.5, 0.5]) - .vapor() - .build()?; + let s = State::new_npt(&&saft, t, p, dvector![0.5, 0.5], Some(Vapor))?; + let sh = State::new_npt(&&saft, t, p + h, dvector![0.5, 0.5], Some(Vapor))?; let ln_phi = s.ln_phi()[0]; let ln_phi_h = sh.ln_phi()[0]; @@ -48,7 +38,7 @@ fn test_virial_is_not_nan() -> Result<(), Box> { IdentifierOption::Name, )?; let saft = &PcSaft::new(params); - let virial_b = saft.second_virial_coefficient(300.0 * KELVIN, &None); + let virial_b = saft.second_virial_coefficient(300.0 * KELVIN, ())?; assert!(!virial_b.is_nan()); Ok(()) } diff --git a/crates/feos/tests/pcsaft/state_creation_mixture.rs b/crates/feos/tests/pcsaft/state_creation_mixture.rs index b7b93c73a..46e0442b8 100644 --- a/crates/feos/tests/pcsaft/state_creation_mixture.rs +++ b/crates/feos/tests/pcsaft/state_creation_mixture.rs @@ -2,7 +2,7 @@ use approx::assert_relative_eq; use feos::ideal_gas::{Joback, JobackParameters}; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::IdentifierOption; -use feos_core::{Contributions, EquationOfState, FeosResult, StateBuilder}; +use feos_core::{Contributions, EquationOfState, FeosResult, State}; use nalgebra::dvector; use quantity::*; use std::error::Error; @@ -31,17 +31,9 @@ fn pressure_entropy_molefracs() -> Result<(), Box> { let pressure = BAR; let temperature = 300.0 * KELVIN; let x = dvector![0.3, 0.7]; - let state = StateBuilder::new(&&eos) - .temperature(temperature) - .pressure(pressure) - .molefracs(&x) - .build()?; + let state = State::new_npt(&&eos, temperature, pressure, &x, None)?; let molar_entropy = state.molar_entropy(Contributions::Total); - let state = StateBuilder::new(&&eos) - .pressure(pressure) - .molar_entropy(molar_entropy) - .molefracs(&x) - .build()?; + let state = State::new_nps(&&eos, pressure, molar_entropy, x, None, None)?; assert_relative_eq!( state.molar_entropy(Contributions::Total), molar_entropy, @@ -63,13 +55,8 @@ fn volume_temperature_molefracs() -> Result<(), Box> { let volume = 1.5e-3 * METER.powi::<3>(); let moles = MOL; let x = dvector![0.3, 0.7]; - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .volume(volume) - .total_moles(moles) - .molefracs(&x) - .build()?; - assert_relative_eq!(state.volume, volume, max_relative = 1e-10); + let state = State::new_nvt(&&saft, temperature, volume, (x, moles))?; + assert_relative_eq!(state.volume(), volume, max_relative = 1e-10); Ok(()) } @@ -80,15 +67,9 @@ fn temperature_partial_density() -> Result<(), Box> { let x = dvector![0.3, 0.7]; let partial_density = x.clone() * MOL / METER.powi::<3>(); let density = partial_density.sum(); - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .partial_density(&partial_density) - .build()?; + let state = State::new_density(&&saft, temperature, partial_density)?; assert_relative_eq!(x, state.molefracs, max_relative = 1e-10); assert_relative_eq!(density, state.density, max_relative = 1e-10); - // Zip::from(&state.partial_density.to_reduced(reference)) - // .and(&partial_density.into_value()?) - // .for_each(|&r1, &r2| assert_relative_eq!(r1, r2, max_relative = 1e-10)); Ok(()) } @@ -98,11 +79,7 @@ fn temperature_density_molefracs() -> Result<(), Box> { let temperature = 300.0 * KELVIN; let x = dvector![0.3, 0.7]; let density = MOL / METER.powi::<3>(); - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .density(density) - .molefracs(&x) - .build()?; + let state = State::new(&&saft, temperature, density, &x)?; state .molefracs .iter() @@ -118,11 +95,7 @@ fn temperature_pressure_molefracs() -> Result<(), Box> { let temperature = 300.0 * KELVIN; let pressure = BAR; let x = dvector![0.3, 0.7]; - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .pressure(pressure) - .molefracs(&x) - .build()?; + let state = State::new_npt(&&saft, temperature, pressure, &x, None)?; state .molefracs .iter() diff --git a/crates/feos/tests/pcsaft/state_creation_pure.rs b/crates/feos/tests/pcsaft/state_creation_pure.rs index d5bc9769e..cda35b75f 100644 --- a/crates/feos/tests/pcsaft/state_creation_pure.rs +++ b/crates/feos/tests/pcsaft/state_creation_pure.rs @@ -1,12 +1,10 @@ use approx::assert_relative_eq; use feos::ideal_gas::{Joback, JobackParameters}; use feos::pcsaft::{PcSaft, PcSaftParameters}; +use feos_core::DensityInitialization::{InitialDensity, Liquid, Vapor}; use feos_core::parameter::IdentifierOption; -use feos_core::{ - Contributions, EquationOfState, FeosResult, PhaseEquilibrium, State, StateBuilder, Total, -}; +use feos_core::{Contributions, EquationOfState, FeosResult, PhaseEquilibrium, State, Total}; use quantity::*; -use std::error::Error; fn propane_parameters() -> FeosResult<(PcSaftParameters, Vec)> { let saft = PcSaftParameters::from_json( @@ -25,77 +23,59 @@ fn propane_parameters() -> FeosResult<(PcSaftParameters, Vec)> { } #[test] -fn temperature_volume() -> Result<(), Box> { +fn temperature_volume() -> FeosResult<()> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; let volume = 1.5e-3 * METER.powi::<3>(); let moles = MOL; - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .volume(volume) - .total_moles(moles) - .build()?; - assert_relative_eq!(state.volume, volume, max_relative = 1e-10); + let state = State::new_nvt(&&saft, temperature, volume, moles)?; + assert_relative_eq!(state.volume(), volume, max_relative = 1e-10); Ok(()) } #[test] -fn temperature_density() -> Result<(), Box> { +fn temperature_density() -> FeosResult<()> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; let density = MOL / METER.powi::<3>(); - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .density(density) - .build()?; + let state = State::new_pure(&&saft, temperature, density)?; assert_relative_eq!(state.density, density, max_relative = 1e-10); Ok(()) } #[test] -fn temperature_total_moles_volume() -> Result<(), Box> { +fn temperature_total_moles_volume() -> FeosResult<()> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; let total_moles = MOL; let volume = METER.powi::<3>(); - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .volume(volume) - .total_moles(total_moles) - .build()?; - assert_relative_eq!(state.volume, volume, max_relative = 1e-10); - assert_relative_eq!(state.total_moles, total_moles, max_relative = 1e-10); + let state = State::new_nvt(&&saft, temperature, volume, total_moles)?; + assert_relative_eq!(state.volume(), volume, max_relative = 1e-10); + assert_relative_eq!(state.total_moles(), total_moles, max_relative = 1e-10); Ok(()) } #[test] -fn temperature_total_moles_density() -> Result<(), Box> { +fn temperature_total_moles_density() -> FeosResult<()> { let saft = PcSaft::new(propane_parameters()?.0); let temperature = 300.0 * KELVIN; let total_moles = MOL; let density = MOL / METER.powi::<3>(); - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .density(density) - .total_moles(total_moles) - .build()?; + let state = State::new_pure(&&saft, temperature, density)?.set_total_moles(total_moles); assert_relative_eq!(state.density, density, max_relative = 1e-10); - assert_relative_eq!(state.total_moles, total_moles, max_relative = 1e-10); - assert_relative_eq!(state.volume, total_moles / density, max_relative = 1e-10); + assert_relative_eq!(state.total_moles(), total_moles, max_relative = 1e-10); + assert_relative_eq!(state.volume(), total_moles / density, max_relative = 1e-10); Ok(()) } // Pressure constructors #[test] -fn pressure_temperature() -> Result<(), Box> { +fn pressure_temperature() -> FeosResult<()> { let saft = PcSaft::new(propane_parameters()?.0); let pressure = BAR; let temperature = 300.0 * KELVIN; - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .pressure(pressure) - .build()?; + let state = State::new_npt(&&saft, temperature, pressure, (), None)?; assert_relative_eq!( state.pressure(Contributions::Total), pressure, @@ -105,15 +85,11 @@ fn pressure_temperature() -> Result<(), Box> { } #[test] -fn pressure_temperature_phase() -> Result<(), Box> { +fn pressure_temperature_phase() -> FeosResult<()> { let saft = PcSaft::new(propane_parameters()?.0); let pressure = BAR; let temperature = 300.0 * KELVIN; - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .pressure(pressure) - .liquid() - .build()?; + let state = State::new_npt(&&saft, temperature, pressure, (), Some(Liquid))?; assert_relative_eq!( state.pressure(Contributions::Total), pressure, @@ -123,15 +99,12 @@ fn pressure_temperature_phase() -> Result<(), Box> { } #[test] -fn pressure_temperature_initial_density() -> Result<(), Box> { +fn pressure_temperature_initial_density() -> FeosResult<()> { let saft = PcSaft::new(propane_parameters()?.0); let pressure = BAR; let temperature = 300.0 * KELVIN; - let state = StateBuilder::new(&&saft) - .temperature(temperature) - .pressure(pressure) - .initial_density(MOL / METER.powi::<3>()) - .build()?; + let init = Some(InitialDensity(MOL / METER.powi::<3>())); + let state = State::new_npt(&&saft, temperature, pressure, (), init)?; assert_relative_eq!( state.pressure(Contributions::Total), pressure, @@ -141,17 +114,13 @@ fn pressure_temperature_initial_density() -> Result<(), Box> { } #[test] -fn pressure_enthalpy_vapor() -> Result<(), Box> { +fn pressure_enthalpy_vapor() -> FeosResult<()> { let (saft_params, joback) = propane_parameters()?; let saft = PcSaft::new(saft_params); let eos = EquationOfState::new(joback, saft); let pressure = 0.3 * BAR; let molar_enthalpy = 2000.0 * JOULE / MOL; - let state = StateBuilder::new(&&eos) - .pressure(pressure) - .molar_enthalpy(molar_enthalpy) - .vapor() - .build()?; + let state = State::new_nph(&&eos, pressure, molar_enthalpy, (), Some(Vapor), None)?; assert_relative_eq!( state.molar_enthalpy(Contributions::Total), molar_enthalpy, @@ -163,11 +132,7 @@ fn pressure_enthalpy_vapor() -> Result<(), Box> { max_relative = 1e-10 ); - let state = StateBuilder::new(&&eos) - .volume(state.volume) - .temperature(state.temperature) - .moles(&state.moles) - .build()?; + let state = State::new(&&eos, state.temperature, state.density, state.molefracs)?; assert_relative_eq!( state.molar_enthalpy(Contributions::Total), molar_enthalpy, @@ -182,24 +147,22 @@ fn pressure_enthalpy_vapor() -> Result<(), Box> { } #[test] -fn density_internal_energy() -> Result<(), Box> { +fn density_internal_energy() -> FeosResult<()> { let (saft_params, joback) = propane_parameters()?; let saft = PcSaft::new(saft_params); let eos = EquationOfState::new(joback, saft); let pressure = 5.0 * BAR; let temperature = 315.0 * KELVIN; let total_moles = 2.5 * MOL; - let state = StateBuilder::new(&&eos) - .pressure(pressure) - .temperature(temperature) - .total_moles(total_moles) - .build()?; + let state = State::new_npt(&&eos, temperature, pressure, total_moles, None)?; let molar_internal_energy = state.molar_internal_energy(Contributions::Total); - let state_nvu = StateBuilder::new(&&eos) - .volume(state.volume) - .molar_internal_energy(molar_internal_energy) - .total_moles(total_moles) - .build()?; + let state_nvu = State::new_nvu( + &&eos, + state.volume(), + molar_internal_energy, + total_moles, + None, + )?; assert_relative_eq!( molar_internal_energy, state_nvu.molar_internal_energy(Contributions::Total), @@ -211,19 +174,21 @@ fn density_internal_energy() -> Result<(), Box> { } #[test] -fn pressure_enthalpy_total_moles_vapor() -> Result<(), Box> { +fn pressure_enthalpy_total_moles_vapor() -> FeosResult<()> { let (saft_params, joback) = propane_parameters()?; let saft = PcSaft::new(saft_params); let eos = EquationOfState::new(joback, saft); let pressure = 0.3 * BAR; let molar_enthalpy = 2000.0 * JOULE / MOL; let total_moles = 2.5 * MOL; - let state = StateBuilder::new(&&eos) - .pressure(pressure) - .molar_enthalpy(molar_enthalpy) - .total_moles(total_moles) - .vapor() - .build()?; + let state = State::new_nph( + &&eos, + pressure, + molar_enthalpy, + total_moles, + Some(Vapor), + None, + )?; assert_relative_eq!( state.molar_enthalpy(Contributions::Total), molar_enthalpy, @@ -235,11 +200,12 @@ fn pressure_enthalpy_total_moles_vapor() -> Result<(), Box> { max_relative = 1e-10 ); - let state = StateBuilder::new(&&eos) - .volume(state.volume) - .temperature(state.temperature) - .total_moles(state.total_moles) - .build()?; + let state = State::new_nvt( + &&eos, + state.temperature, + state.volume(), + state.total_moles(), + )?; assert_relative_eq!( state.molar_enthalpy(Contributions::Total), molar_enthalpy, @@ -254,17 +220,13 @@ fn pressure_enthalpy_total_moles_vapor() -> Result<(), Box> { } #[test] -fn pressure_entropy_vapor() -> Result<(), Box> { +fn pressure_entropy_vapor() -> FeosResult<()> { let (saft_params, joback) = propane_parameters()?; let saft = PcSaft::new(saft_params); let eos = EquationOfState::new(joback, saft); let pressure = 0.3 * BAR; let molar_entropy = -2.0 * JOULE / MOL / KELVIN; - let state = StateBuilder::new(&&eos) - .pressure(pressure) - .molar_entropy(molar_entropy) - .vapor() - .build()?; + let state = State::new_nps(&&eos, pressure, molar_entropy, (), Some(Vapor), None)?; assert_relative_eq!( state.molar_entropy(Contributions::Total), molar_entropy, @@ -276,11 +238,7 @@ fn pressure_entropy_vapor() -> Result<(), Box> { max_relative = 1e-10 ); - let state = StateBuilder::new(&&eos) - .volume(state.volume) - .temperature(state.temperature) - .moles(&state.moles) - .build()?; + let state = State::new(&&eos, state.temperature, state.density, state.molefracs)?; assert_relative_eq!( state.molar_entropy(Contributions::Total), molar_entropy, @@ -295,24 +253,20 @@ fn pressure_entropy_vapor() -> Result<(), Box> { } #[test] -fn temperature_entropy_vapor() -> Result<(), Box> { +fn temperature_entropy_vapor() -> FeosResult<()> { let (saft_params, joback) = propane_parameters()?; let saft = PcSaft::new(saft_params); let eos = EquationOfState::new(joback, saft); let pressure = 3.0 * BAR; let temperature = 315.15 * KELVIN; let total_moles = 3.0 * MOL; - let state = StateBuilder::new(&&eos) - .temperature(temperature) - .pressure(pressure) - .total_moles(total_moles) - .build()?; + let state = State::new_npt(&&eos, temperature, pressure, total_moles, None)?; let s = State::new_nts( &&eos, temperature, state.molar_entropy(Contributions::Total), - &state.moles, + state.moles(), None, )?; assert_relative_eq!( @@ -354,7 +308,7 @@ fn assert_multiple_states( } #[test] -fn test_consistency() -> Result<(), Box> { +fn test_consistency() -> FeosResult<()> { let (saft_params, joback) = propane_parameters()?; let saft = PcSaft::new(saft_params); let eos = EquationOfState::new(joback, saft); @@ -362,10 +316,7 @@ fn test_consistency() -> Result<(), Box> { let pressures = [1.0 * BAR, 2.0 * BAR, 3.0 * BAR]; for (&temperature, &pressure) in temperatures.iter().zip(pressures.iter()) { - let state = StateBuilder::new(&&eos) - .pressure(pressure) - .temperature(temperature) - .build()?; + let state = State::new_npt(&&eos, temperature, pressure, (), None)?; assert_relative_eq!( state.pressure(Contributions::Total), pressure, @@ -379,49 +330,26 @@ fn test_consistency() -> Result<(), Box> { let molar_entropy = state.molar_entropy(Contributions::Total); let density = state.density; - let state_tv = StateBuilder::new(&&eos) - .temperature(temperature) - .density(density) - .build()?; + let state_tv = State::new_pure(&&eos, temperature, density)?; let vle = PhaseEquilibrium::pure(&&eos, temperature, None, Default::default()); let eos = &eos; - let builder = if let Ok(ps) = vle { + let phase = if let Ok(ps) = vle { let p_sat = ps.liquid().pressure(Contributions::Total); - if pressure > p_sat { - StateBuilder::new(&eos).liquid() - } else { - StateBuilder::new(&eos).vapor() - } + if pressure > p_sat { Liquid } else { Vapor } } else { - StateBuilder::new(&eos).vapor() + Vapor }; - let state_ts = builder - .clone() - .temperature(temperature) - .molar_entropy(molar_entropy) - .build()?; + let state_ts = State::new_nts(&eos, temperature, molar_entropy, (), Some(phase))?; - let state_ps = builder - .clone() - .pressure(pressure) - .molar_entropy(molar_entropy) - .build()?; + let state_ps = State::new_nps(&eos, pressure, molar_entropy, (), Some(phase), None)?; dbg!("ph"); - let state_ph = builder - .clone() - .pressure(pressure) - .molar_enthalpy(molar_enthalpy) - .build()?; + let state_ph = State::new_nph(&eos, pressure, molar_enthalpy, (), Some(phase), None)?; dbg!("th"); - let state_th = builder - .clone() - .temperature(temperature) - .molar_enthalpy(molar_enthalpy) - .build()?; + let state_th = State::new_nth(&eos, temperature, molar_enthalpy, (), Some(phase))?; dbg!("assertions"); assert_multiple_states( diff --git a/crates/feos/tests/saftvrmie/critical_properties.rs b/crates/feos/tests/saftvrmie/critical_properties.rs index c78eb110a..1f3334ea3 100644 --- a/crates/feos/tests/saftvrmie/critical_properties.rs +++ b/crates/feos/tests/saftvrmie/critical_properties.rs @@ -52,7 +52,7 @@ fn critical_properties_pure() { let option = SolverOptions::default(); let p = parameters.remove(name).unwrap(); let eos = SaftVRMie::new(p); - let cp = State::critical_point(&&eos, None, t0, None, option).unwrap(); + let cp = State::critical_point(&&eos, (), t0, None, option).unwrap(); assert_relative_eq!(cp.temperature, data.0, max_relative = 2e-3); assert_relative_eq!( cp.pressure(feos_core::Contributions::Total), diff --git a/py-feos/src/ad/mod.rs b/py-feos/src/ad/mod.rs index c18d1eacb..75958d7ca 100644 --- a/py-feos/src/ad/mod.rs +++ b/py-feos/src/ad/mod.rs @@ -50,7 +50,7 @@ type GradResult<'py> = ( #[pyfunction] pub fn vapor_pressure_derivatives<'py>( model: PyEquationOfStateAD, - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> GradResult<'py> { @@ -102,7 +102,7 @@ pub fn boiling_temperature_derivatives<'py>( #[pyfunction] pub fn liquid_density_derivatives<'py>( model: PyEquationOfStateAD, - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> GradResult<'py> { @@ -128,7 +128,7 @@ pub fn liquid_density_derivatives<'py>( #[pyfunction] pub fn equilibrium_liquid_density_derivatives<'py>( model: PyEquationOfStateAD, - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> GradResult<'py> { @@ -155,7 +155,7 @@ pub fn equilibrium_liquid_density_derivatives<'py>( #[pyfunction] pub fn bubble_point_pressure_derivatives<'py>( model: PyEquationOfStateAD, - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> GradResult<'py> { @@ -182,7 +182,7 @@ pub fn bubble_point_pressure_derivatives<'py>( #[pyfunction] pub fn dew_point_pressure_derivatives<'py>( model: PyEquationOfStateAD, - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> GradResult<'py> { @@ -195,7 +195,7 @@ macro_rules! expand_models { #[pyfunction] fn [<_ $prop _derivatives>]<'py>( model: PyEquationOfStateAD, - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> GradResult<'py> { @@ -220,7 +220,7 @@ macro_rules! impl_evaluate_gradients { expand_models!($enum, $prop, $($model: $type),*); paste!( fn $prop<'py, R: ParametersAD<$n>>( - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> ( diff --git a/py-feos/src/dft/adsorption/mod.rs b/py-feos/src/dft/adsorption/mod.rs index 1f006be0a..1c5040003 100644 --- a/py-feos/src/dft/adsorption/mod.rs +++ b/py-feos/src/dft/adsorption/mod.rs @@ -1,9 +1,9 @@ use super::PyDFTSolver; -use crate::eos::{parse_molefracs, PyEquationOfState}; +use crate::PyVerbosity; +use crate::eos::{Compositions, PyEquationOfState}; use crate::error::PyFeosError; use crate::ideal_gas::IdealGasModel; use crate::residual::ResidualModel; -use crate::PyVerbosity; use feos_core::EquationOfState; use feos_dft::adsorption::{Adsorption, Adsorption1D, Adsorption3D}; use nalgebra::DMatrix; @@ -45,8 +45,8 @@ macro_rules! impl_adsorption_isotherm { /// The pressures for which the profiles are calculated. /// pore : Pore /// The pore parameters. - /// molefracs: numpy.ndarray[float], optional - /// For a mixture, the molefracs of the bulk system. + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional + /// The composition of the mixture. /// solver: DFTSolver, optional /// Custom solver options. /// @@ -55,14 +55,14 @@ macro_rules! impl_adsorption_isotherm { /// Adsorption /// #[staticmethod] - #[pyo3(text_signature = "(functional, temperature, pressure, pore, molefracs=None, solver=None)")] - #[pyo3(signature = (functional, temperature, pressure, pore, molefracs=None, solver=None))] + #[pyo3(text_signature = "(functional, temperature, pressure, pore, composition=None, solver=None)")] + #[pyo3(signature = (functional, temperature, pressure, pore, composition=None, solver=None))] fn adsorption_isotherm( functional: &PyEquationOfState, temperature: Temperature, pressure: Pressure>, pore: &$py_pore, - molefracs: Option>, + composition: Option<&Bound<'_, PyAny>>, solver: Option, ) -> PyResult { Ok(Self(Adsorption::adsorption_isotherm( @@ -70,7 +70,7 @@ macro_rules! impl_adsorption_isotherm { temperature, &pressure, &pore.0, - &parse_molefracs(molefracs), + Compositions::try_from(composition)?, solver.map(|s| s.0).as_ref(), ).map_err(PyFeosError::from)?)) } @@ -89,8 +89,8 @@ macro_rules! impl_adsorption_isotherm { /// The pressures for which the profiles are calculated. /// pore : Pore /// The pore parameters. - /// molefracs: numpy.ndarray[float], optional - /// For a mixture, the molefracs of the bulk system. + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional + /// The composition of the mixture. /// solver: DFTSolver, optional /// Custom solver options. /// @@ -99,14 +99,14 @@ macro_rules! impl_adsorption_isotherm { /// Adsorption /// #[staticmethod] - #[pyo3(text_signature = "(functional, temperature, pressure, pore, molefracs=None, solver=None)")] - #[pyo3(signature = (functional, temperature, pressure, pore, molefracs=None, solver=None))] + #[pyo3(text_signature = "(functional, temperature, pressure, pore, composition=None, solver=None)")] + #[pyo3(signature = (functional, temperature, pressure, pore, composition=None, solver=None))] fn desorption_isotherm( functional: &PyEquationOfState, temperature: Temperature, pressure: Pressure>, pore: &$py_pore, - molefracs: Option>, + composition: Option<&Bound<'_, PyAny>>, solver: Option, ) -> PyResult { Ok(Self(Adsorption::desorption_isotherm( @@ -114,7 +114,7 @@ macro_rules! impl_adsorption_isotherm { temperature, &pressure, &pore.0, - &parse_molefracs(molefracs), + Compositions::try_from(composition)?, solver.map(|s| s.0).as_ref(), ).map_err(PyFeosError::from)?)) } @@ -136,8 +136,8 @@ macro_rules! impl_adsorption_isotherm { /// The pressures for which the profiles are calculated. /// pore : Pore /// The pore parameters. - /// molefracs: numpy.ndarray[float], optional - /// For a mixture, the molefracs of the bulk system. + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional + /// The composition of the mixture. /// solver: DFTSolver, optional /// Custom solver options. /// @@ -146,14 +146,14 @@ macro_rules! impl_adsorption_isotherm { /// Adsorption /// #[staticmethod] - #[pyo3(text_signature = "(functional, temperature, pressure, pore, molefracs=None, solver=None)")] - #[pyo3(signature = (functional, temperature, pressure, pore, molefracs=None, solver=None))] + #[pyo3(text_signature = "(functional, temperature, pressure, pore, composition=None, solver=None)")] + #[pyo3(signature = (functional, temperature, pressure, pore, composition=None, solver=None))] fn equilibrium_isotherm( functional: &PyEquationOfState, temperature: Temperature, pressure: Pressure>, pore: &$py_pore, - molefracs: Option>, + composition: Option<&Bound<'_, PyAny>>, solver: Option, ) -> PyResult { Ok(Self(Adsorption::equilibrium_isotherm( @@ -161,7 +161,7 @@ macro_rules! impl_adsorption_isotherm { temperature, &pressure, &pore.0, - &parse_molefracs(molefracs), + Compositions::try_from(composition)?, solver.map(|s| s.0).as_ref(), ).map_err(PyFeosError::from)?)) } @@ -180,8 +180,8 @@ macro_rules! impl_adsorption_isotherm { /// A suitable upper limit for the pressure. /// pore : Pore /// The pore parameters. - /// molefracs: numpy.ndarray[float], optional - /// For a mixture, the molefracs of the bulk system. + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional + /// The composition of the mixture. /// solver: DFTSolver, optional /// Custom solver options. /// max_iter : int, optional @@ -196,8 +196,8 @@ macro_rules! impl_adsorption_isotherm { /// Adsorption /// #[staticmethod] - #[pyo3(text_signature = "(functional, temperature, p_min, p_max, pore, molefracs=None, solver=None, max_iter=None, tol=None, verbosity=None)")] - #[pyo3(signature = (functional, temperature, p_min, p_max, pore, molefracs=None, solver=None, max_iter=None, tol=None, verbosity=None))] + #[pyo3(text_signature = "(functional, temperature, p_min, p_max, pore, composition=None, solver=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (functional, temperature, p_min, p_max, pore, composition=None, solver=None, max_iter=None, tol=None, verbosity=None))] #[expect(clippy::too_many_arguments)] fn phase_equilibrium( functional: &PyEquationOfState, @@ -205,7 +205,7 @@ macro_rules! impl_adsorption_isotherm { p_min: Pressure, p_max: Pressure, pore: &$py_pore, - molefracs: Option>, + composition: Option<&Bound<'_, PyAny>>, solver: Option, max_iter: Option, tol: Option, @@ -217,7 +217,7 @@ macro_rules! impl_adsorption_isotherm { p_min, p_max, &pore.0, - &parse_molefracs(molefracs), + Compositions::try_from(composition)?, solver.map(|s| s.0).as_ref(), (max_iter, tol, verbosity.map(|v| v.into())).into(), ).map_err(PyFeosError::from)?)) diff --git a/py-feos/src/eos/mod.rs b/py-feos/src/eos/mod.rs index 1147ac020..cd50de42f 100644 --- a/py-feos/src/eos/mod.rs +++ b/py-feos/src/eos/mod.rs @@ -3,9 +3,9 @@ use crate::ideal_gas::IdealGasModel; use crate::residual::ResidualModel; use feos_core::*; use indexmap::IndexMap; -use nalgebra::{DVector, DVectorView, Dyn}; +use nalgebra::{DVector, DVectorView, Dyn, U1}; use numpy::{PyArray1, PyReadonlyArray1, ToPyArray}; -use pyo3::prelude::*; +use pyo3::{exceptions::PyValueError, prelude::*}; use quantity::*; use std::ops::Div; use std::sync::Arc; @@ -58,17 +58,17 @@ impl PyEquationOfState { /// /// Parameters /// ---------- - /// molefracs : np.ndarray[float], optional + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional /// The composition of the mixture. /// /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(molefracs=None)", signature = (molefracs=None))] - fn max_density(&self, molefracs: Option>) -> PyResult { + #[pyo3(text_signature = "(composition=None)", signature = (composition=None))] + fn max_density(&self, composition: Option<&Bound<'_, PyAny>>) -> PyResult { Ok(self .0 - .max_density(&parse_molefracs(molefracs)) + .max_density(Compositions::try_from(composition)?) .map_err(PyFeosError::from)?) } @@ -78,20 +78,22 @@ impl PyEquationOfState { /// ---------- /// temperature : SINumber /// The temperature for which B should be computed. - /// molefracs : np.ndarray[float], optional + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional /// The composition of the mixture. /// /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, molefracs=None)", signature = (temperature, molefracs=None))] + #[pyo3(text_signature = "(temperature, composition=None)", signature = (temperature, composition=None))] fn second_virial_coefficient( &self, temperature: Temperature, - molefracs: Option>, - ) -> Quot { - self.0 - .second_virial_coefficient(temperature, &parse_molefracs(molefracs)) + composition: Option<&Bound<'_, PyAny>>, + ) -> PyResult> { + Ok(self + .0 + .second_virial_coefficient(temperature, Compositions::try_from(composition)?) + .map_err(PyFeosError::from)?) } /// Calculate the third Virial coefficient C(T,x). @@ -100,20 +102,22 @@ impl PyEquationOfState { /// ---------- /// temperature : SINumber /// The temperature for which C should be computed. - /// molefracs : np.ndarray[float], optional + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional /// The composition of the mixture. /// /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, molefracs=None)", signature = (temperature, molefracs=None))] + #[pyo3(text_signature = "(temperature, composition=None)", signature = (temperature, composition=None))] fn third_virial_coefficient( &self, temperature: Temperature, - molefracs: Option>, - ) -> Quot, Density> { - self.0 - .third_virial_coefficient(temperature, &parse_molefracs(molefracs)) + composition: Option<&Bound<'_, PyAny>>, + ) -> PyResult, Density>> { + Ok(self + .0 + .third_virial_coefficient(temperature, Compositions::try_from(composition)?) + .map_err(PyFeosError::from)?) } /// Calculate the derivative of the second Virial coefficient B(T,x) @@ -123,22 +127,25 @@ impl PyEquationOfState { /// ---------- /// temperature : SINumber /// The temperature for which B' should be computed. - /// molefracs : np.ndarray[float], optional + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional /// The composition of the mixture. /// /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, molefracs=None)", signature = (temperature, molefracs=None))] + #[pyo3(text_signature = "(temperature, composition=None)", signature = (temperature, composition=None))] fn second_virial_coefficient_temperature_derivative( &self, temperature: Temperature, - molefracs: Option>, - ) -> Quot, Temperature> { - self.0.second_virial_coefficient_temperature_derivative( - temperature, - &parse_molefracs(molefracs), - ) + composition: Option<&Bound<'_, PyAny>>, + ) -> PyResult, Temperature>> { + Ok(self + .0 + .second_virial_coefficient_temperature_derivative( + temperature, + Compositions::try_from(composition)?, + ) + .map_err(PyFeosError::from)?) } /// Calculate the derivative of the third Virial coefficient C(T,x) @@ -148,22 +155,26 @@ impl PyEquationOfState { /// ---------- /// temperature : SINumber /// The temperature for which C' should be computed. - /// molefracs : np.ndarray[float], optional + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional /// The composition of the mixture. /// /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, molefracs=None)", signature = (temperature, molefracs=None))] + #[expect(clippy::type_complexity)] + #[pyo3(text_signature = "(temperature, composition=None)", signature = (temperature, composition=None))] fn third_virial_coefficient_temperature_derivative( &self, temperature: Temperature, - molefracs: Option>, - ) -> Quot, Density>, Temperature> { - self.0.third_virial_coefficient_temperature_derivative( - temperature, - &parse_molefracs(molefracs), - ) + composition: Option<&Bound<'_, PyAny>>, + ) -> PyResult, Density>, Temperature>> { + Ok(self + .0 + .third_virial_coefficient_temperature_derivative( + temperature, + Compositions::try_from(composition)?, + ) + .map_err(PyFeosError::from)?) } } @@ -192,36 +203,64 @@ pub(crate) fn parse_molefracs(molefracs: Option>) -> Optio }) } -// impl_state_entropy_scaling!(EquationOfState, ResidualModel>, PyEquationOfState); -// impl_phase_equilibrium!(EquationOfState, ResidualModel>, PyEquationOfState); - -// #[cfg(feature = "estimator")] -// impl_estimator!(EquationOfState, ResidualModel>, PyEquationOfState); -// #[cfg(all(feature = "estimator", feature = "pcsaft"))] -// impl_estimator_entropy_scaling!(EquationOfState, ResidualModel>, PyEquationOfState); - -// #[pymodule] -// pub fn eos(m: &Bound<'_, PyModule>) -> PyResult<()> { -// m.add_class::()?; -// m.add_class::()?; - -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; +#[derive(Clone)] +pub enum Compositions { + None, + Scalar(f64), + TotalMoles(Moles), + Molefracs(DVector), + Moles(Moles>), + PartialDensity(Density>), +} -// #[cfg(feature = "estimator")] -// m.add_wrapped(wrap_pymodule!(estimator_eos))?; +impl Composition for Compositions { + fn into_molefracs>( + self, + eos: &E, + ) -> FeosResult<(DVector, Option>)> { + match self { + Self::None => ().into_molefracs(eos), + Self::Scalar(x) => x.into_molefracs(eos), + Self::TotalMoles(total_moles) => total_moles.into_molefracs(eos), + Self::Molefracs(molefracs) => molefracs.into_molefracs(eos), + Self::Moles(moles) => moles.into_molefracs(eos), + Self::PartialDensity(partial_density) => partial_density.into_molefracs(eos), + } + } -// Ok(()) -// } + fn density(&self) -> Option> { + if let Self::PartialDensity(partial_density) = self { + partial_density.density() + } else { + None + } + } +} -// #[cfg(feature = "estimator")] -// #[pymodule] -// pub fn estimator_eos(m: &Bound<'_, PyModule>) -> PyResult<()> { -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::()?; -// m.add_class::() -// } +impl TryFrom>> for Compositions { + type Error = PyErr; + fn try_from(composition: Option<&Bound<'_, PyAny>>) -> PyResult { + let Some(composition) = composition else { + return Ok(Compositions::None); + }; + if let Ok(x) = composition.extract::>() + && let Some(x) = x.try_as_matrix::() + { + Ok(Compositions::Molefracs(x.clone_owned())) + } else if let Ok(x) = composition.extract::>() { + Ok(Compositions::Molefracs(DVector::from_vec(x))) + } else if let Ok(x) = composition.extract::() { + Ok(Compositions::Scalar(x)) + } else if let Ok(n) = composition.extract::>>() { + Ok(Compositions::Moles(n)) + } else if let Ok(n) = composition.extract::() { + Ok(Compositions::TotalMoles(n)) + } else if let Ok(rho) = composition.extract::>>() { + Ok(Compositions::PartialDensity(rho)) + } else { + Err(PyErr::new::(format!( + "failed to parse value '{composition}' as composition." + ))) + } + } +} diff --git a/py-feos/src/phase_equilibria.rs b/py-feos/src/phase_equilibria.rs index c3786e300..38d0d2d12 100644 --- a/py-feos/src/phase_equilibria.rs +++ b/py-feos/src/phase_equilibria.rs @@ -61,7 +61,7 @@ impl PyPhaseEquilibrium { #[pyo3(signature = (eos, temperature_or_pressure, initial_state=None, max_iter=None, tol=None, verbosity=None))] pub(crate) fn pure( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, initial_state: Option<&PyPhaseEquilibrium>, max_iter: Option, tol: Option, @@ -198,9 +198,9 @@ impl PyPhaseEquilibrium { #[expect(clippy::too_many_arguments)] pub(crate) fn bubble_point<'py>( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, liquid_molefracs: PyReadonlyArray1<'py, f64>, - tp_init: Option>, + tp_init: Option<&Bound<'_, PyAny>>, vapor_molefracs: Option>, max_iter_inner: Option, max_iter_outer: Option, @@ -286,9 +286,9 @@ impl PyPhaseEquilibrium { #[expect(clippy::too_many_arguments)] pub(crate) fn dew_point<'py>( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, vapor_molefracs: PyReadonlyArray1<'py, f64>, - tp_init: Option>, + tp_init: Option<&Bound<'_, PyAny>>, liquid_molefracs: Option>, max_iter_inner: Option, max_iter_outer: Option, @@ -398,7 +398,7 @@ impl PyPhaseEquilibrium { #[staticmethod] fn vle_pure_comps( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, ) -> PyResult>> { if let Ok(t) = temperature_or_pressure.extract::() { Ok(PhaseEquilibrium::vle_pure_comps(&eos.0, t) @@ -514,9 +514,9 @@ impl PyPhaseEquilibrium { #[expect(clippy::too_many_arguments)] fn heteroazeotrope( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, x_init: (f64, f64), - tp_init: Option>, + tp_init: Option<&Bound<'_, PyAny>>, max_iter: Option, tol: Option, verbosity: Option, @@ -1224,7 +1224,7 @@ impl PyPhaseDiagram { #[expect(clippy::too_many_arguments)] pub(crate) fn binary_vle( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, npoints: Option, x_lle: Option<(f64, f64)>, max_iter_inner: Option, @@ -1299,7 +1299,7 @@ impl PyPhaseDiagram { #[pyo3(signature = (eos, temperature_or_pressure, feed, min_tp, max_tp, npoints=None))] pub(crate) fn lle( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, feed: Moles>, min_tp: Bound<'_, PyAny>, max_tp: Bound<'_, PyAny>, @@ -1388,10 +1388,10 @@ impl PyPhaseDiagram { #[expect(clippy::too_many_arguments)] pub(crate) fn binary_vlle( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, x_lle: (f64, f64), - tp_lim_lle: Option>, - tp_init_vlle: Option>, + tp_lim_lle: Option<&Bound<'_, PyAny>>, + tp_init_vlle: Option<&Bound<'_, PyAny>>, npoints_vle: Option, npoints_lle: Option, max_iter_inner: Option, diff --git a/py-feos/src/state.rs b/py-feos/src/state.rs index e7b54211c..6d5ff09f8 100644 --- a/py-feos/src/state.rs +++ b/py-feos/src/state.rs @@ -1,4 +1,4 @@ -use crate::eos::parse_molefracs; +use crate::eos::{Compositions, parse_molefracs}; use crate::{ PyVerbosity, eos::PyEquationOfState, error::PyFeosError, ideal_gas::IdealGasModel, residual::ResidualModel, @@ -13,15 +13,13 @@ use pyo3::exceptions::{PyIndexError, PyValueError}; use pyo3::prelude::*; use quantity::*; use std::collections::HashMap; -use std::ops::{Deref, Div, Neg, Sub}; +use std::ops::{Deref, Div, Neg}; use std::sync::Arc; type Quot = >::Output; -type DpDn = Quantity>::Output>; type InvT = Quantity::Output>; type InvP = Quantity::Output>; -type InvM = Quantity::Output>; /// Possible contributions that can be computed. #[derive(Clone, Copy, PartialEq)] @@ -69,14 +67,8 @@ impl From for Contributions { /// Volume. /// density : SINumber, optional /// Molar density. -/// partial_density : SIArray1, optional -/// Partial molar densities. -/// total_moles : SINumber, optional -/// Total amount of substance (of a mixture). -/// moles : SIArray1, optional -/// Amount of substance for each component. -/// molefracs : numpy.ndarray[float] -/// Molar fraction of each component. +/// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional +/// Composition of the mixture. /// pressure : SINumber, optional /// Pressure. /// molar_enthalpy : SINumber, optional @@ -110,19 +102,16 @@ pub struct PyState(pub State, ResidualMod impl PyState { #[new] #[pyo3( - text_signature = "(eos, temperature=None, volume=None, density=None, partial_density=None, total_moles=None, moles=None, molefracs=None, pressure=None, molar_enthalpy=None, molar_entropy=None, molar_internal_energy=None, density_initialization=None, initial_temperature=None)" + text_signature = "(eos, temperature=None, volume=None, density=None, composition=None, pressure=None, molar_enthalpy=None, molar_entropy=None, molar_internal_energy=None, density_initialization=None, initial_temperature=None)" )] - #[pyo3(signature = (eos, temperature=None, volume=None, density=None, partial_density=None, total_moles=None, moles=None, molefracs=None, pressure=None, molar_enthalpy=None, molar_entropy=None, molar_internal_energy=None, density_initialization=None, initial_temperature=None))] + #[pyo3(signature = (eos, temperature=None, volume=None, density=None, composition=None, pressure=None, molar_enthalpy=None, molar_entropy=None, molar_internal_energy=None, density_initialization=None, initial_temperature=None))] #[expect(clippy::too_many_arguments)] pub fn new<'py>( eos: &PyEquationOfState, temperature: Option, volume: Option, density: Option, - partial_density: Option>>, - total_moles: Option, - moles: Option>>, - molefracs: Option>, + composition: Option<&Bound<'py, PyAny>>, pressure: Option, molar_enthalpy: Option, molar_entropy: Option, @@ -130,7 +119,7 @@ impl PyState { density_initialization: Option<&Bound<'py, PyAny>>, initial_temperature: Option, ) -> PyResult { - let x = parse_molefracs(molefracs); + let composition = Compositions::try_from(composition)?; let density_init = if let Some(di) = density_initialization { if let Ok(d) = di.extract::().as_deref() { match d { @@ -147,25 +136,23 @@ impl PyState { } } else { Ok(None) - }; - let s = State::new_full( - &eos.0, - temperature, - volume, - density, - partial_density.as_ref(), - total_moles, - moles.as_ref(), - x.as_ref(), - pressure, - molar_enthalpy, - molar_entropy, - molar_internal_energy, - density_init?, - initial_temperature, - ) - .map_err(PyFeosError::from)?; - Ok(Self(s)) + }?; + Ok(Self( + State::build_full( + &eos.0, + temperature, + volume, + density, + composition, + pressure, + molar_enthalpy, + molar_entropy, + molar_internal_energy, + density_init, + initial_temperature, + ) + .map_err(PyFeosError::from)?, + )) } /// Return a list of thermodynamic state at critical conditions @@ -233,12 +220,12 @@ impl PyState { /// State : State at critical conditions. #[staticmethod] #[pyo3( - text_signature = "(eos, molefracs=None, initial_temperature=None, initial_density=None, max_iter=None, tol=None, verbosity=None)" + text_signature = "(eos, composition=None, initial_temperature=None, initial_density=None, max_iter=None, tol=None, verbosity=None)" )] - #[pyo3(signature = (eos, molefracs=None, initial_temperature=None, initial_density=None, max_iter=None, tol=None, verbosity=None))] + #[pyo3(signature = (eos, composition=None, initial_temperature=None, initial_density=None, max_iter=None, tol=None, verbosity=None))] fn critical_point<'py>( eos: &PyEquationOfState, - molefracs: Option>, + composition: Option<&Bound<'py, PyAny>>, initial_temperature: Option, initial_density: Option, max_iter: Option, @@ -248,7 +235,7 @@ impl PyState { Ok(PyState( State::critical_point( &eos.0, - parse_molefracs(molefracs).as_ref(), + Compositions::try_from(composition)?, initial_temperature, initial_density, (max_iter, tol, verbosity.map(|v| v.into())).into(), @@ -287,7 +274,7 @@ impl PyState { #[expect(clippy::too_many_arguments)] fn critical_point_binary( eos: &PyEquationOfState, - temperature_or_pressure: Bound<'_, PyAny>, + temperature_or_pressure: &Bound<'_, PyAny>, initial_temperature: Option, initial_molefracs: Option<[f64; 2]>, initial_density: Option, @@ -353,13 +340,13 @@ impl PyState { /// (State, State): Spinodal states. #[staticmethod] #[pyo3( - text_signature = "(eos, temperature, molefracs=None, max_iter=None, tol=None, verbosity=None)" + text_signature = "(eos, temperature, composition=None, max_iter=None, tol=None, verbosity=None)" )] - #[pyo3(signature = (eos, temperature, molefracs=None, max_iter=None, tol=None, verbosity=None))] + #[pyo3(signature = (eos, temperature, composition=None, max_iter=None, tol=None, verbosity=None))] fn spinodal<'py>( eos: &PyEquationOfState, temperature: Temperature, - molefracs: Option>, + composition: Option<&Bound<'py, PyAny>>, max_iter: Option, tol: Option, verbosity: Option, @@ -367,7 +354,7 @@ impl PyState { let [state1, state2] = State::spinodal( &eos.0, temperature, - parse_molefracs(molefracs).as_ref(), + Compositions::try_from(composition)?, (max_iter, tol, verbosity.map(|v| v.into())).into(), ) .map_err(PyFeosError::from)?; @@ -476,7 +463,7 @@ impl PyState { self.0.compressibility(contributions.into()) } - /// Return partial derivative of pressure w.r.t. volume. + /// Return partial derivative of pressure w.r.t. molar volume. /// /// Parameters /// ---------- @@ -488,7 +475,7 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn dp_dv(&self, contributions: PyContributions) -> Quot { + fn dp_dv(&self, contributions: PyContributions) -> Quot { self.0.dp_dv(contributions.into()) } @@ -536,11 +523,11 @@ impl PyState { /// ------- /// SIArray1 #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn dp_dni(&self, contributions: PyContributions) -> DpDn> { - self.0.dp_dni(contributions.into()) + fn dp_dni(&self, contributions: PyContributions) -> Pressure> { + self.0.n_dp_dni(contributions.into()) } - /// Return second partial derivative of pressure w.r.t. volume. + /// Return second partial derivative of pressure w.r.t. molar volume. /// /// Parameters /// ---------- @@ -552,7 +539,10 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn d2p_dv2(&self, contributions: PyContributions) -> Quot, Volume> { + fn d2p_dv2( + &self, + contributions: PyContributions, + ) -> Quot, MolarVolume> { self.0.d2p_dv2(contributions.into()) } @@ -652,8 +642,8 @@ impl PyState { /// ------- /// SIArray2 #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn dmu_dni(&self, contributions: PyContributions) -> Quot>, Moles> { - self.0.dmu_dni(contributions.into()) + fn n_dmu_dni(&self, contributions: PyContributions) -> MolarEnergy> { + self.0.n_dmu_dni(contributions.into()) } /// Return logarithmic fugacity coefficient. @@ -771,8 +761,8 @@ impl PyState { /// Returns /// ------- /// SIArray2 - fn dln_phi_dnj(&self) -> InvM> { - self.0.dln_phi_dnj() + fn n_dln_phi_dnj<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray2> { + self.0.n_dln_phi_dnj().to_pyarray(py) } /// Return thermodynamic factor. @@ -848,7 +838,7 @@ impl PyState { self.0.entropy(contributions.into()) } - /// Return derivative of entropy with respect to temperature. + /// Return derivative of molar entropy with respect to temperature. /// /// Parameters /// ---------- @@ -860,7 +850,7 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn ds_dt(&self, contributions: PyContributions) -> Quot { + fn ds_dt(&self, contributions: PyContributions) -> Quot { self.0.ds_dt(contributions.into()) } @@ -1357,7 +1347,7 @@ impl PyState { #[getter] fn get_total_moles(&self) -> Moles { - self.0.total_moles + self.0.total_moles() } #[getter] @@ -1367,7 +1357,7 @@ impl PyState { #[getter] fn get_volume(&self) -> Volume { - self.0.volume + self.0.volume() } #[getter] @@ -1377,12 +1367,12 @@ impl PyState { #[getter] fn get_moles(&self) -> Moles> { - self.0.moles.clone() + self.0.moles() } #[getter] fn get_partial_density(&self) -> Density> { - self.0.partial_density.clone() + self.0.partial_density() } #[getter] From 636dc2dc0d41a1699712858398a2365a933d8acd Mon Sep 17 00:00:00 2001 From: Philipp Rehner Date: Tue, 27 Jan 2026 12:36:56 +0100 Subject: [PATCH 05/12] include phase fraction in PhaseEquilibrium --- .github/workflows/test.yml | 2 +- .github/workflows/wheels.yml | 2 +- CHANGELOG.md | 9 +- crates/feos-core/src/ad/mod.rs | 32 ++- crates/feos-core/src/errors.rs | 4 + crates/feos-core/src/lib.rs | 22 +- .../src/phase_equilibria/bubble_dew.rs | 63 +++--- crates/feos-core/src/phase_equilibria/mod.rs | 200 +++++++++--------- .../phase_equilibria/phase_diagram_binary.rs | 16 +- .../phase_equilibria/phase_diagram_pure.rs | 4 +- .../src/phase_equilibria/phase_envelope.rs | 60 +++--- .../phase_equilibria/stability_analysis.rs | 6 +- .../src/phase_equilibria/tp_flash.rs | 161 +++++++++----- .../src/phase_equilibria/vle_pure.rs | 17 +- crates/feos-core/src/state/composition.rs | 94 +------- crates/feos-core/src/state/mod.rs | 37 ++-- crates/feos-core/src/state/properties.rs | 22 +- .../src/state/residual_properties.rs | 51 +++-- crates/feos-core/src/state/statevec.rs | 15 +- crates/feos/benches/contributions.rs | 2 +- crates/feos/benches/dft_pore.rs | 12 +- crates/feos/benches/dual_numbers.rs | 10 +- crates/feos/benches/dual_numbers_saftvrmie.rs | 13 +- crates/feos/benches/state_creation.rs | 2 +- crates/feos/src/epcsaft/eos/mod.rs | 4 +- crates/feos/src/pcsaft/eos/mod.rs | 82 +++---- crates/feos/src/pets/eos/mod.rs | 2 +- crates/feos/src/uvtheory/eos/mod.rs | 4 +- .../feos/tests/pcsaft/stability_analysis.rs | 5 +- .../tests/pcsaft/state_creation_mixture.rs | 2 +- .../feos/tests/pcsaft/state_creation_pure.rs | 18 +- py-feos/src/ad/mod.rs | 2 +- py-feos/src/eos/mod.rs | 12 -- py-feos/src/phase_equilibria.rs | 107 +++------- py-feos/src/state.rs | 99 ++++++--- 35 files changed, 573 insertions(+), 620 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ea6192e7..6c35188d0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: Test on: push: - branches: [main, development] + branches: [main] pull_request: branches: [main, development] diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 41f501d43..197da9586 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -1,7 +1,7 @@ name: Build Wheels on: push: - branches: [main, development] + branches: [main] pull_request: branches: [main, development] jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b6ba4c41..b82f858f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Breaking] ### Added - Extended tp-flash algorithm to static numbers of components and enabled automatic differentiation for binary systems. [#336](https://github.com/feos-org/feos/pull/336) -- Rewrote `PhaseEquilibrium::pure_p` to mirror `pure_t` and enable automatic differentiation. [#337](https://github.com/feos-org/feos/pull/337) +- Rewrote `PhaseEquilibrium::pure_p` to mirror `pure_t` and enabled automatic differentiation. [#337](https://github.com/feos-org/feos/pull/337) - Added `boiling_temperature` to the list of properties for parallel evaluations of gradients. [#337](https://github.com/feos-org/feos/pull/337) +- Added the `Composition` trait to allow more flexibility in the creation of states and phase equilibria. [#330](https://github.com/feos-org/feos/pull/330) + +### Changed +- Removed any assumptions about the total number of moles in a `State` or `PhaseEquilibrium`. Evaluating extensive properties now returns a `Result`. [#330](https://github.com/feos-org/feos/pull/330) + +### Removed +- Removed the `StateBuilder` struct, because it is mostly obsolete with the addition of the `Composition` trait. [#330](https://github.com/feos-org/feos/pull/330) ### Packaging - Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#323](https://github.com/feos-org/feos/pull/323) diff --git a/crates/feos-core/src/ad/mod.rs b/crates/feos-core/src/ad/mod.rs index aae9fc198..f48fb153c 100644 --- a/crates/feos-core/src/ad/mod.rs +++ b/crates/feos-core/src/ad/mod.rs @@ -1,6 +1,6 @@ use crate::DensityInitialization::Liquid; use crate::density_iteration::density_iteration; -use crate::{FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; +use crate::{Composition, FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; use nalgebra::{Const, SVector, U1, U2}; #[cfg(feature = "rayon")] use ndarray::{Array1, Array2, ArrayView2, Zip}; @@ -215,20 +215,21 @@ pub trait PropertiesAD { ) } - fn bubble_point_pressure( + fn bubble_point_pressure>( &self, temperature: Temperature, pressure: Option, - liquid_molefracs: SVector, + liquid_molefracs: X, ) -> FeosResult>> where Self: Residual>, { let eos_f64 = self.re(); + let (liquid_molefracs, _) = liquid_molefracs.into_molefracs(&eos_f64)?; let vle = PhaseEquilibrium::bubble_point( &eos_f64, temperature, - &liquid_molefracs, + liquid_molefracs, pressure, None, Default::default(), @@ -265,20 +266,21 @@ pub trait PropertiesAD { Ok(Pressure::from_reduced(p)) } - fn dew_point_pressure( + fn dew_point_pressure>( &self, temperature: Temperature, pressure: Option, - vapor_molefracs: SVector, + vapor_molefracs: X, ) -> FeosResult>> where Self: Residual>, { let eos_f64 = self.re(); + let (vapor_molefracs, _) = vapor_molefracs.into_molefracs(&eos_f64)?; let vle = PhaseEquilibrium::dew_point( &eos_f64, temperature, - &vapor_molefracs, + vapor_molefracs, pressure, None, Default::default(), @@ -329,12 +331,8 @@ pub trait PropertiesAD { parameters, input, |eos: &Self::Lifted>, inp| { - eos.bubble_point_pressure( - inp[0] * KELVIN, - Some(inp[2] * PASCAL), - SVector::from([inp[1], 1.0 - inp[1]]), - ) - .map(|p| p.convert_into(PASCAL)) + eos.bubble_point_pressure(inp[0] * KELVIN, Some(inp[2] * PASCAL), inp[1]) + .map(|p| p.convert_into(PASCAL)) }, ) } @@ -353,12 +351,8 @@ pub trait PropertiesAD { parameters, input, |eos: &Self::Lifted>, inp| { - eos.dew_point_pressure( - inp[0] * KELVIN, - Some(inp[2] * PASCAL), - SVector::from([inp[1], 1.0 - inp[1]]), - ) - .map(|p| p.convert_into(PASCAL)) + eos.dew_point_pressure(inp[0] * KELVIN, Some(inp[2] * PASCAL), inp[1]) + .map(|p| p.convert_into(PASCAL)) }, ) } diff --git a/crates/feos-core/src/errors.rs b/crates/feos-core/src/errors.rs index d2aecffdb..6f32dcaec 100644 --- a/crates/feos-core/src/errors.rs +++ b/crates/feos-core/src/errors.rs @@ -24,6 +24,10 @@ pub enum FeosError { InvalidState(String, String, f64), #[error("Undetermined state: {0}")] UndeterminedState(String), + #[error( + "Extensive properties can only be evaluated for states that are initialized with extensive properties." + )] + IntensiveState, #[error("System is supercritical.")] SuperCritical, #[error("No phase split according to stability analysis.")] diff --git a/crates/feos-core/src/lib.rs b/crates/feos-core/src/lib.rs index a2552007d..48b8306f3 100644 --- a/crates/feos-core/src/lib.rs +++ b/crates/feos-core/src/lib.rs @@ -299,8 +299,8 @@ mod tests { // residual properties assert_relative_eq!( - s.helmholtz_energy(Contributions::Residual), - sr.residual_helmholtz_energy(), + s.helmholtz_energy(Contributions::Residual)?, + sr.residual_helmholtz_energy()?, max_relative = 1e-15 ); assert_relative_eq!( @@ -309,8 +309,8 @@ mod tests { max_relative = 1e-15 ); assert_relative_eq!( - s.entropy(Contributions::Residual), - sr.residual_entropy(), + s.entropy(Contributions::Residual)?, + sr.residual_entropy()?, max_relative = 1e-15 ); assert_relative_eq!( @@ -319,8 +319,8 @@ mod tests { max_relative = 1e-15 ); assert_relative_eq!( - s.enthalpy(Contributions::Residual), - sr.residual_enthalpy(), + s.enthalpy(Contributions::Residual)?, + sr.residual_enthalpy()?, max_relative = 1e-15 ); assert_relative_eq!( @@ -329,8 +329,8 @@ mod tests { max_relative = 1e-15 ); assert_relative_eq!( - s.internal_energy(Contributions::Residual), - sr.residual_internal_energy(), + s.internal_energy(Contributions::Residual)?, + sr.residual_internal_energy()?, max_relative = 1e-15 ); assert_relative_eq!( @@ -339,12 +339,12 @@ mod tests { max_relative = 1e-15 ); assert_relative_eq!( - s.gibbs_energy(Contributions::Residual) - - s.total_moles() + s.gibbs_energy(Contributions::Residual)? + - s.total_moles()? * RGAS * s.temperature * s.compressibility(Contributions::Total).ln(), - sr.residual_gibbs_energy(), + sr.residual_gibbs_energy()?, max_relative = 1e-15 ); assert_relative_eq!( diff --git a/crates/feos-core/src/phase_equilibria/bubble_dew.rs b/crates/feos-core/src/phase_equilibria/bubble_dew.rs index 91ad57d3d..05dd42849 100644 --- a/crates/feos-core/src/phase_equilibria/bubble_dew.rs +++ b/crates/feos-core/src/phase_equilibria/bubble_dew.rs @@ -4,7 +4,7 @@ use crate::state::{ Contributions, DensityInitialization::{InitialDensity, Liquid, Vapor}, }; -use crate::{ReferenceSystem, Residual, SolverOptions, State, Verbosity}; +use crate::{Composition, ReferenceSystem, Residual, SolverOptions, State, Verbosity}; use nalgebra::allocator::Allocator; use nalgebra::{DMatrix, DVector, DefaultAllocator, Dim, Dyn, OVector, U1}; #[cfg(feature = "ndarray")] @@ -141,10 +141,10 @@ where { /// Calculate a phase equilibrium for a given temperature /// or pressure and composition of the liquid phase. - pub fn bubble_point>( + pub fn bubble_point, X: Composition>( eos: &E, temperature_or_pressure: TP, - liquid_molefracs: &OVector, + liquid_molefracs: X, tp_init: Option, vapor_molefracs: Option<&OVector>, options: (SolverOptions, SolverOptions), @@ -162,10 +162,10 @@ where /// Calculate a phase equilibrium for a given temperature /// or pressure and composition of the vapor phase. - pub fn dew_point>( + pub fn dew_point, X: Composition>( eos: &E, temperature_or_pressure: TP, - vapor_molefracs: &OVector, + vapor_molefracs: X, tp_init: Option, liquid_molefracs: Option<&OVector>, options: (SolverOptions, SolverOptions), @@ -181,35 +181,43 @@ where ) } - pub(super) fn bubble_dew_point>( + pub(super) fn bubble_dew_point, X: Composition>( eos: &E, temperature_or_pressure: TP, - vapor_molefracs: &OVector, + vapor_molefracs: X, tp_init: Option, liquid_molefracs: Option<&OVector>, bubble: bool, options: (SolverOptions, SolverOptions), ) -> FeosResult { - let (temperature, pressure, iterate_p) = - temperature_or_pressure.temperature_pressure(tp_init); - Self::bubble_dew_point_tp( - eos, - temperature, - pressure, - vapor_molefracs, - liquid_molefracs, - bubble, - iterate_p, - options, - ) + if eos.components() == 1 { + let mut vle = Self::pure(eos, temperature_or_pressure, None, options.1)?; + if bubble { + vle.phase_fractions = [D::from(0.0), D::from(1.0)]; + } + Ok(vle) + } else { + let (temperature, pressure, iterate_p) = + temperature_or_pressure.temperature_pressure(tp_init); + Self::bubble_dew_point_tp( + eos, + temperature, + pressure, + vapor_molefracs, + liquid_molefracs, + bubble, + iterate_p, + options, + ) + } } #[expect(clippy::too_many_arguments)] - fn bubble_dew_point_tp( + fn bubble_dew_point_tp>( eos: &E, temperature: Option>, pressure: Option>, - molefracs_spec: &OVector, + composition: X, molefracs_init: Option<&OVector>, bubble: bool, iterate_p: bool, @@ -218,6 +226,7 @@ where let eos_re = eos.re(); let mut temperature_re = temperature.map(|t| t.re()); let mut pressure_re = pressure.map(|p| p.re()); + let (molefracs_spec, total_moles) = composition.into_molefracs(eos)?; let molefracs_spec_re = molefracs_spec.map(|x| x.re()); let (v1, rho2) = if iterate_p { // temperature is specified @@ -315,7 +324,7 @@ where Self::newton_step_t( eos, t, - molefracs_spec, + &molefracs_spec, &mut p, &mut molar_volume, &mut rho2, @@ -325,7 +334,7 @@ where Self::newton_step_p( eos, &mut t, - molefracs_spec, + &molefracs_spec, p, &mut molar_volume, &mut rho2, @@ -348,11 +357,11 @@ where x2, )?; - Ok(PhaseEquilibrium(if bubble { - [state2, state1] + Ok(if bubble { + PhaseEquilibrium::with_vapor_phase_fraction(state2, state1, D::from(0.0), total_moles) } else { - [state1, state2] - })) + PhaseEquilibrium::with_vapor_phase_fraction(state1, state2, D::from(1.0), total_moles) + }) } fn newton_step_t( diff --git a/crates/feos-core/src/phase_equilibria/mod.rs b/crates/feos-core/src/phase_equilibria/mod.rs index 95671deb2..461a1a901 100644 --- a/crates/feos-core/src/phase_equilibria/mod.rs +++ b/crates/feos-core/src/phase_equilibria/mod.rs @@ -1,11 +1,12 @@ +use crate::FeosError; use crate::equation_of_state::Residual; use crate::errors::FeosResult; -use crate::state::{DensityInitialization, State}; -use crate::{Contributions, ReferenceSystem}; +use crate::state::State; +use crate::{Contributions::Total as Tot, ReferenceSystem, Total}; use nalgebra::allocator::Allocator; -use nalgebra::{DefaultAllocator, Dim, Dyn, OVector}; -use num_dual::{DualNum, DualStruct, Gradients}; -use quantity::{Energy, Moles, Pressure, RGAS, Temperature}; +use nalgebra::{DefaultAllocator, Dim, Dyn}; +use num_dual::{DualNum, Gradients}; +use quantity::{Dimensionless, Energy, Entropy, MolarEnergy, MolarEntropy, Moles}; use std::fmt; use std::fmt::Write; @@ -38,15 +39,18 @@ pub use phase_diagram_pure::PhaseDiagram; /// + [Pure component phase equilibria](#pure-component-phase-equilibria) /// + [Utility functions](#utility-functions) #[derive(Debug, Clone)] -pub struct PhaseEquilibrium + Copy = f64>( - pub [State; P], -) +pub struct PhaseEquilibrium + Copy = f64> where - DefaultAllocator: Allocator; + DefaultAllocator: Allocator, +{ + states: [State; P], + pub phase_fractions: [D; P], + total_moles: Option>, +} impl fmt::Display for PhaseEquilibrium { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for (i, s) in self.0.iter().enumerate() { + for (i, s) in self.states.iter().enumerate() { writeln!(f, "phase {i}: {s}")?; } Ok(()) @@ -55,9 +59,9 @@ impl fmt::Display for PhaseEquilibrium { impl PhaseEquilibrium { pub fn _repr_markdown_(&self) -> String { - if self.0[0].eos.components() == 1 { + if self.states[0].eos.components() == 1 { let mut res = "||temperature|density|\n|-|-|-|\n".to_string(); - for (i, s) in self.0.iter().enumerate() { + for (i, s) in self.states.iter().enumerate() { writeln!( res, "|phase {}|{:.5}|{:.5}|", @@ -70,7 +74,7 @@ impl PhaseEquilibrium { res } else { let mut res = "||temperature|density|molefracs|\n|-|-|-|-|\n".to_string(); - for (i, s) in self.0.iter().enumerate() { + for (i, s) in self.states.iter().enumerate() { writeln!( res, "|phase {}|{:.5}|{:.5}|{:.5?}|", @@ -91,125 +95,115 @@ where DefaultAllocator: Allocator, { pub fn vapor(&self) -> &State { - &self.0[0] + &self.states[0] } pub fn liquid(&self) -> &State { - &self.0[1] + &self.states[1] + } + + pub fn vapor_phase_fraction(&self) -> D { + self.phase_fractions[0] } } impl PhaseEquilibrium { pub fn vapor(&self) -> &State { - &self.0[0] + &self.states[0] } pub fn liquid1(&self) -> &State { - &self.0[1] + &self.states[1] } pub fn liquid2(&self) -> &State { - &self.0[2] + &self.states[2] } } -impl, N: Dim> PhaseEquilibrium +impl, N: Dim, D: DualNum + Copy> PhaseEquilibrium where DefaultAllocator: Allocator, { - pub(super) fn from_states(state1: State, state2: State) -> Self { - let (vapor, liquid) = if state1.density.re() < state2.density.re() { - (state1, state2) - } else { - (state2, state1) - }; - Self([vapor, liquid]) - } - - // /// Creates a new PhaseEquilibrium that contains two states at the - // /// specified temperature, pressure and molefracs. - // /// - // /// The constructor can be used in custom phase equilibrium solvers or, - // /// e.g., to generate initial guesses for an actual VLE solver. - // /// In general, the two states generated are NOT in an equilibrium. - // pub fn new_xpt( - // eos: &E, - // temperature: Temperature, - // pressure: Pressure, - // vapor_molefracs: &OVector, - // liquid_molefracs: &OVector, - // ) -> FeosResult { - // let liquid = State::new_xpt( - // eos, - // temperature, - // pressure, - // liquid_molefracs, - // Some(DensityInitialization::Liquid), - // )?; - // let vapor = State::new_xpt( - // eos, - // temperature, - // pressure, - // vapor_molefracs, - // Some(DensityInitialization::Vapor), - // )?; - // Ok(Self([vapor, liquid])) - // } - - pub(super) fn vapor_phase_fraction(&self) -> Option { - self.vapor() - .total_moles - .zip(self.liquid().total_moles) - .map(|(v, l)| (v / (l + v)).into_value()) + pub(super) fn single_phase(state: State) -> Self { + let total_moles = state.total_moles; + Self::with_vapor_phase_fraction(state.clone(), state, D::from(1.0), total_moles) + } + + pub(super) fn two_phase(vapor: State, liquid: State) -> Self { + let (beta, total_moles) = + if let (Some(nv), Some(nl)) = (vapor.total_moles, liquid.total_moles) { + (nv.convert_into(nl + nv), Some(nl + nv)) + } else { + (D::from(1.0), None) + }; + Self::with_vapor_phase_fraction(vapor, liquid, beta, total_moles) + } + + pub(super) fn with_vapor_phase_fraction( + vapor: State, + liquid: State, + vapor_phase_fraction: D, + total_moles: Option>, + ) -> Self { + Self { + states: [vapor, liquid], + phase_fractions: [vapor_phase_fraction, -vapor_phase_fraction + 1.0], + total_moles, + } } } -impl, N: Gradients, const P: usize> PhaseEquilibrium +impl, N: Dim, D: DualNum + Copy> PhaseEquilibrium where DefaultAllocator: Allocator, { - pub(super) fn update_pressure( - mut self, - temperature: Temperature, - pressure: Pressure, - ) -> FeosResult { - for s in self.0.iter_mut() { - *s = State::new_npt( - &s.eos, - temperature, - pressure, - &*s, - Some(DensityInitialization::InitialDensity(s.density)), - )?; - } - Ok(self) - } - - pub(super) fn update_moles( - &mut self, - pressure: Pressure, - moles: [&Moles>; P], - ) -> FeosResult<()> { - for (i, s) in self.0.iter_mut().enumerate() { - *s = State::new_npt( - &s.eos, - s.temperature, - pressure, - moles[i], - Some(DensityInitialization::InitialDensity(s.density)), - )?; + pub(super) fn new( + vapor: State, + liquid1: State, + liquid2: State, + ) -> Self { + Self { + states: [vapor, liquid1, liquid2], + phase_fractions: [D::from(1.0), D::from(0.0), D::from(0.0)], + total_moles: None, } - Ok(()) + } +} + +impl, N: Gradients, const P: usize, D: DualNum + Copy> + PhaseEquilibrium +where + DefaultAllocator: Allocator, +{ + pub fn total_moles(&self) -> FeosResult> { + self.total_moles.ok_or(FeosError::IntensiveState) + } + + pub fn molar_enthalpy(&self) -> MolarEnergy { + self.states + .iter() + .zip(&self.phase_fractions) + .map(|(s, x)| s.molar_enthalpy(Tot) * Dimensionless::new(x)) + .reduce(|a, b| a + b) + .unwrap() + } + + pub fn enthalpy(&self) -> FeosResult> { + Ok(self.total_moles()? * self.molar_enthalpy()) + } + + pub fn molar_entropy(&self) -> MolarEntropy { + self.states + .iter() + .zip(&self.phase_fractions) + .map(|(s, x)| s.molar_entropy(Tot) * Dimensionless::new(x)) + .reduce(|a, b| a + b) + .unwrap() } - // Total Gibbs energy excluding the constant contribution RT sum_i N_i ln(\Lambda_i^3) - pub(super) fn total_gibbs_energy(&self) -> Energy { - self.0.iter().fold(Energy::from_reduced(0.0), |acc, s| { - let ln_rho_m1 = s.partial_density().to_reduced().map(|r| r.ln() - 1.0); - acc + s.residual_helmholtz_energy() - + s.pressure(Contributions::Total) * s.volume() - + RGAS * s.temperature * s.total_moles() * s.molefracs.dot(&ln_rho_m1) - }) + pub fn entropy(&self) -> FeosResult> { + Ok(self.total_moles()? * self.molar_entropy()) } } diff --git a/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs b/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs index d519d3255..48a7028d4 100644 --- a/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs +++ b/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs @@ -63,8 +63,9 @@ impl PhaseDiagram { None, SolverOptions::default(), )?; - let cp_vle = PhaseEquilibrium::from_states(cp.clone(), cp.clone()); - ([0.0, cp.molefracs[0]], (vle2, cp_vle), bubble) + let x_max = cp.molefracs[0]; + let cp_vle = PhaseEquilibrium::single_phase(cp); + ([0.0, x_max], (vle2, cp_vle), bubble) } [None, Some(vle1)] => { let cp = State::critical_point_binary( @@ -75,8 +76,9 @@ impl PhaseDiagram { None, SolverOptions::default(), )?; - let cp_vle = PhaseEquilibrium::from_states(cp.clone(), cp.clone()); - ([1.0, cp.molefracs[0]], (vle1, cp_vle), bubble) + let x_min = cp.molefracs[0]; + let cp_vle = PhaseEquilibrium::single_phase(cp); + ([1.0, x_min], (vle1, cp_vle), bubble) } [Some(vle2), Some(vle1)] => ([0.0, 1.0], (vle2, vle1), true), }; @@ -201,7 +203,7 @@ fn iterate_vle( let vle = PhaseEquilibrium::bubble_dew_point( eos, tp, - &dvector![*xi, 1.0 - xi], + dvector![*xi, 1.0 - xi], tp_old, y_old.as_ref(), bubble, @@ -604,7 +606,7 @@ impl PhaseEquilibrium { // check for convergence if res.norm() < options.tol.unwrap_or(TOL_HETERO) { - return Ok(Self([v, l1, l2])); + return Ok(Self::new(v, l1, l2)); } // calculate Jacobian @@ -724,7 +726,7 @@ impl PhaseEquilibrium { // check for convergence if res.norm() < options.tol.unwrap_or(TOL_HETERO) { - return Ok(Self([v, l1, l2])); + return Ok(Self::new(v, l1, l2)); } let jacobian = stack![ diff --git a/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs b/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs index 2382a974c..ab2c37cd0 100644 --- a/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs +++ b/crates/feos-core/src/phase_equilibria/phase_diagram_pure.rs @@ -54,7 +54,7 @@ impl PhaseDiagram { states.push(vle.clone()); } } - states.push(PhaseEquilibrium::from_states(sc.clone(), sc)); + states.push(PhaseEquilibrium::single_phase(sc)); Ok(PhaseDiagram::new(states)) } @@ -127,7 +127,7 @@ impl PhaseDiagram { .collect() }); - states.push(PhaseEquilibrium::from_states(sc.clone(), sc)); + states.push(PhaseEquilibrium::single_phase(sc)); Ok(PhaseDiagram::new(states)) } } diff --git a/crates/feos-core/src/phase_equilibria/phase_envelope.rs b/crates/feos-core/src/phase_equilibria/phase_envelope.rs index 0f01e7288..7f118a3cd 100644 --- a/crates/feos-core/src/phase_equilibria/phase_envelope.rs +++ b/crates/feos-core/src/phase_equilibria/phase_envelope.rs @@ -1,16 +1,16 @@ use super::{PhaseDiagram, PhaseEquilibrium}; -use crate::SolverOptions; use crate::equation_of_state::Residual; use crate::errors::FeosResult; use crate::state::{Contributions, State}; -use nalgebra::DVector; +use crate::{Composition, SolverOptions}; +use nalgebra::Dyn; use quantity::{Pressure, Temperature}; impl PhaseDiagram { /// Calculate the bubble point line of a mixture with given composition. - pub fn bubble_point_line( + pub fn bubble_point_line + Clone>( eos: &E, - molefracs: &DVector, + composition: X, min_temperature: Temperature, npoints: usize, critical_temperature: Option, @@ -20,7 +20,7 @@ impl PhaseDiagram { let sc = State::critical_point( eos, - molefracs, + composition.clone(), critical_temperature, None, SolverOptions::default(), @@ -40,7 +40,7 @@ impl PhaseDiagram { vle = PhaseEquilibrium::bubble_point( eos, ti, - molefracs, + composition.clone(), p_init, vapor_molefracs, options, @@ -51,15 +51,15 @@ impl PhaseDiagram { states.push(vle.clone()); } } - states.push(PhaseEquilibrium::from_states(sc.clone(), sc)); + states.push(PhaseEquilibrium::single_phase(sc)); Ok(PhaseDiagram::new(states)) } /// Calculate the dew point line of a mixture with given composition. - pub fn dew_point_line( + pub fn dew_point_line + Clone>( eos: &E, - molefracs: &DVector, + composition: X, min_temperature: Temperature, npoints: usize, critical_temperature: Option, @@ -69,7 +69,7 @@ impl PhaseDiagram { let sc = State::critical_point( eos, - molefracs, + composition.clone(), critical_temperature, None, SolverOptions::default(), @@ -86,9 +86,15 @@ impl PhaseDiagram { .as_ref() .map(|vle| vle.vapor().pressure(Contributions::Total)); let liquid_molefracs = vle.as_ref().map(|vle| &vle.liquid().molefracs); - vle = - PhaseEquilibrium::dew_point(eos, ti, molefracs, p_init, liquid_molefracs, options) - .ok(); + vle = PhaseEquilibrium::dew_point( + eos, + ti, + composition.clone(), + p_init, + liquid_molefracs, + options, + ) + .ok(); if let Some(vle) = vle.as_ref() { states.push(vle.clone()); } @@ -108,23 +114,29 @@ impl PhaseDiagram { for pi in &pressures { let t_init = vle.as_ref().map(|vle| vle.vapor().temperature); let liquid_molefracs = vle.as_ref().map(|vle| &vle.liquid().molefracs); - vle = - PhaseEquilibrium::dew_point(eos, pi, molefracs, t_init, liquid_molefracs, options) - .ok(); + vle = PhaseEquilibrium::dew_point( + eos, + pi, + composition.clone(), + t_init, + liquid_molefracs, + options, + ) + .ok(); if let Some(vle) = vle.as_ref() { states.push(vle.clone()); } } - states.push(PhaseEquilibrium::from_states(sc.clone(), sc)); + states.push(PhaseEquilibrium::single_phase(sc)); Ok(PhaseDiagram::new(states)) } /// Calculate the spinodal lines for a mixture with fixed composition. - pub fn spinodal( + pub fn spinodal + Clone>( eos: &E, - molefracs: &DVector, + composition: X, min_temperature: Temperature, npoints: usize, critical_temperature: Option, @@ -134,7 +146,7 @@ impl PhaseDiagram { let sc = State::critical_point( eos, - molefracs, + composition.clone(), critical_temperature, None, SolverOptions::default(), @@ -145,12 +157,12 @@ impl PhaseDiagram { let temperatures = Temperature::linspace(min_temperature, max_temperature, npoints - 1); for ti in &temperatures { - let spinodal = State::spinodal(eos, ti, molefracs, options).ok(); - if let Some(spinodal) = spinodal { - states.push(PhaseEquilibrium(spinodal)); + let spinodal = State::spinodal(eos, ti, composition.clone(), options).ok(); + if let Some([sp_v, sp_l]) = spinodal { + states.push(PhaseEquilibrium::two_phase(sp_v, sp_l)); } } - states.push(PhaseEquilibrium::from_states(sc.clone(), sc)); + states.push(PhaseEquilibrium::single_phase(sc)); Ok(PhaseDiagram::new(states)) } diff --git a/crates/feos-core/src/phase_equilibria/stability_analysis.rs b/crates/feos-core/src/phase_equilibria/stability_analysis.rs index 950f05026..ec2329da6 100644 --- a/crates/feos-core/src/phase_equilibria/stability_analysis.rs +++ b/crates/feos-core/src/phase_equilibria/stability_analysis.rs @@ -166,9 +166,11 @@ where let (n, _) = di.shape_generic(); // calculate residual and ideal hesse matrix - let mut hesse = self.n_dln_phi_dnj() / self.total_moles().into_reduced(); + // TODO: this should not require extensive properties, but I couldn't rewrite it + // quickly without breaking it. + let mut hesse = self.n_dln_phi_dnj() / self.total_moles().unwrap().into_reduced(); let lnphi = self.ln_phi(); - let y = self.moles().into_reduced(); + let y = self.moles().unwrap().into_reduced(); let ln_y = y.map(|y| if y > f64::EPSILON { y.ln() } else { 0.0 }); let sq_y = y.map(f64::sqrt); let gradient = (&ln_y + &lnphi - di).component_mul(&sq_y); diff --git a/crates/feos-core/src/phase_equilibria/tp_flash.rs b/crates/feos-core/src/phase_equilibria/tp_flash.rs index e955acaf6..88b30679e 100644 --- a/crates/feos-core/src/phase_equilibria/tp_flash.rs +++ b/crates/feos-core/src/phase_equilibria/tp_flash.rs @@ -2,13 +2,13 @@ use super::PhaseEquilibrium; use crate::equation_of_state::Residual; use crate::errors::{FeosError, FeosResult}; use crate::state::{Contributions, State}; -use crate::{ReferenceSystem, SolverOptions, Verbosity}; +use crate::{Composition, DensityInitialization, ReferenceSystem, SolverOptions, Verbosity}; use nalgebra::allocator::Allocator; use nalgebra::{DefaultAllocator, Dim, Matrix3, OVector, SVector, U1, U2, vector}; use num_dual::{ Dual, Dual2Vec, DualNum, DualStruct, Gradients, first_derivative, implicit_derivative_sp, }; -use quantity::{Dimensionless, MOL, MolarVolume, Moles, Pressure, Quantity, Temperature}; +use quantity::{MolarEnergy, MolarVolume, Pressure, RGAS, Temperature}; const MAX_ITER_TP: usize = 400; const TOL_TP: f64 = 1e-8; @@ -23,11 +23,11 @@ where /// /// The algorithm can be use to calculate phase equilibria of systems /// containing non-volatile components (e.g. ions). - pub fn tp_flash( + pub fn tp_flash>( eos: &E, temperature: Temperature, pressure: Pressure, - feed: &Moles>, + feed: X, initial_state: Option<&PhaseEquilibrium>, options: SolverOptions, non_volatile_components: Option>, @@ -45,17 +45,16 @@ impl, D: DualNum + Copy> PhaseEquilibrium { /// Compared to the version of the algorithm for a generic /// number of components ([tp_flash](PhaseEquilibrium::tp_flash)), /// this can be used in combination with automatic differentiation. - pub fn tp_flash_binary( + pub fn tp_flash_binary>( eos: &E, temperature: Temperature, pressure: Pressure, - feed: &Moles>, + feed: X, options: SolverOptions, ) -> FeosResult { - let z = feed.get(0).convert_into(feed.get(0) + feed.get(1)); - let total_moles = feed.sum(); - let moles = vector![z.re(), 1.0 - z.re()] * MOL; - let vle_re = State::new_npt(&eos.re(), temperature.re(), pressure.re(), moles, None)? + let (feed, total_moles) = feed.into_molefracs(eos)?; + let z = feed[0]; + let vle_re = State::new_npt(&eos.re(), temperature.re(), pressure.re(), z.re(), None)? .tp_flash(None, options, None)?; // implicit differentiation @@ -96,12 +95,16 @@ impl, D: DualNum + Copy> PhaseEquilibrium { .data .0; let beta = (z - x) / (y - x); - let state = |x: D, v, phi| { - let volume = MolarVolume::from_reduced(v * phi) * total_moles; - let moles = Quantity::new(vector![x, -x + 1.0] * phi * total_moles.convert_into(MOL)); - State::new_nvt(eos, temperature, volume, moles) + let state = |x: D, v| { + let density = MolarVolume::from_reduced(v).inv(); + State::new(eos, temperature, density, x) }; - Ok(Self([state(y, v_v, beta)?, state(x, v_l, -beta + 1.0)?])) + Ok(Self::with_vapor_phase_fraction( + state(y, v_v)?, + state(x, v_l)?, + beta, + total_moles, + )) } } @@ -123,13 +126,15 @@ where non_volatile_components: Option>, ) -> FeosResult> { // initialization - if let Some(init) = initial_state { - let vle = self.tp_flash_( - init.clone() - .update_pressure(self.temperature, self.pressure(Contributions::Total))?, - options, - non_volatile_components.clone(), - ); + if let Some(initial_state) = initial_state { + let mut init = initial_state.clone(); + init.update_states( + self, + initial_state.vapor().molefracs.clone(), + initial_state.liquid().molefracs.clone(), + initial_state.vapor_phase_fraction(), + )?; + let vle = self.tp_flash_(init, options, non_volatile_components.clone()); if vle.is_ok() { return vle; } @@ -184,14 +189,12 @@ where )?; // check convergence - // unwrap is safe here, because after the first successive substitution step the - // phase amounts in new_vle_state are known. - let beta = new_vle_state.vapor_phase_fraction().unwrap(); let tpd = [ self.tangent_plane_distance(new_vle_state.vapor()), self.tangent_plane_distance(new_vle_state.liquid()), ]; - let dg = (1.0 - beta) * tpd[1] + beta * tpd[0]; + let b = new_vle_state.phase_fractions; + let dg = b[0] * tpd[0] + b[1] * tpd[1]; // fix if only tpd[1] is positive if tpd[0] < 0.0 && dg >= 0.0 { @@ -200,7 +203,7 @@ where if let Some(nvc) = non_volatile_components.as_ref() { nvc.iter().for_each(|&c| k[c] = 0.0); } - new_vle_state.update_states(self, &k)?; + new_vle_state.rachford_rice_inplace(self, &k)?; new_vle_state.successive_substitution( self, 1, @@ -219,7 +222,7 @@ where if let Some(nvc) = non_volatile_components.as_ref() { nvc.iter().for_each(|&c| k[c] = 0.0); } - new_vle_state.update_states(self, &k)?; + new_vle_state.rachford_rice_inplace(self, &k)?; new_vle_state.successive_substitution( self, 1, @@ -289,7 +292,7 @@ where } // calculate total Gibbs energy before the extrapolation - let gibbs = self.total_gibbs_energy(); + let gibbs = self.molar_gibbs_energy(); // extrapolate K values let delta_vec = [ @@ -316,8 +319,8 @@ where // calculate new states let mut trial_vle_state = self.clone(); - trial_vle_state.update_states(feed_state, &k)?; - if trial_vle_state.total_gibbs_energy() < gibbs { + trial_vle_state.rachford_rice_inplace(feed_state, &k)?; + if trial_vle_state.molar_gibbs_energy() < gibbs { *self = trial_vle_state; } } @@ -367,7 +370,7 @@ where return Ok(true); } - self.update_states(feed_state, &k)?; + self.rachford_rice_inplace(feed_state, &k)?; if let Some(k_vec) = k_vec && i >= iterations - 3 { @@ -377,25 +380,41 @@ where Ok(false) } - fn update_states(&mut self, feed_state: &State, k: &OVector) -> FeosResult<()> { + fn rachford_rice_inplace( + &mut self, + feed_state: &State, + k: &OVector, + ) -> FeosResult<()> { // calculate vapor phase fraction using Rachford-Rice algorithm - let beta = self.vapor_phase_fraction(); - let beta = rachford_rice(&feed_state.molefracs, k, beta)?; - - // update VLE - let v = feed_state - .moles() - .clone() - .component_mul(&Dimensionless::new( - k.map(|k| beta * k / (1.0 - beta + beta * k)), - )); - let l = feed_state - .moles() - .clone() - .component_mul(&Dimensionless::new( - k.map(|k| (1.0 - beta) / (1.0 - beta + beta * k)), - )); - self.update_moles(feed_state.pressure(Contributions::Total), [&v, &l])?; + let (b, [v, l]) = + rachford_rice(&feed_state.molefracs, k, Some(self.vapor_phase_fraction()))?; + self.update_states(feed_state, v, l, b) + } + + fn update_states( + &mut self, + feed_state: &State, + vapor_molefracs: OVector, + liquid_molefracs: OVector, + beta: f64, + ) -> FeosResult<()> { + let vapor = State::new_npt( + &feed_state.eos, + feed_state.temperature, + feed_state.pressure(Contributions::Total), + vapor_molefracs, + Some(DensityInitialization::InitialDensity(self.vapor().density)), + )?; + let liquid = State::new_npt( + &feed_state.eos, + feed_state.temperature, + feed_state.pressure(Contributions::Total), + liquid_molefracs, + Some(DensityInitialization::InitialDensity(self.liquid().density)), + )?; + + *self = Self::with_vapor_phase_fraction(vapor, liquid, beta, feed_state.total_moles); + Ok(()) } @@ -404,23 +423,42 @@ where let state1 = stable_states.pop(); let state2 = stable_states.pop(); if let Some(s1) = state1 { - let init1 = Self::from_states(s1.clone(), feed_state.clone()); - if let Some(s2) = state2 { - Ok((Self::from_states(s1, s2), Some(init1))) + let init1 = if s1.density < feed_state.density { + Self::two_phase(s1.clone(), feed_state.clone()) } else { - Ok((init1, None)) - } + Self::two_phase(feed_state.clone(), s1.clone()) + }; + let init2 = state2.map(|s2| { + if s1.density < s2.density { + Self::two_phase(s1.clone(), s2.clone()) + } else { + Self::two_phase(s2.clone(), s1.clone()) + } + }); + Ok((init1, init2)) } else { Err(FeosError::NoPhaseSplit) } } + + // Total molar Gibbs energy excluding the constant contribution RT sum_i x_i ln(\Lambda_i^3) + fn molar_gibbs_energy(&self) -> MolarEnergy { + self.states + .iter() + .fold(MolarEnergy::from_reduced(0.0), |acc, s| { + let ln_rho_m1 = s.partial_density().to_reduced().map(|r| r.ln() - 1.0); + acc + s.residual_molar_helmholtz_energy() + + s.pressure(Contributions::Total) * s.molar_volume + + RGAS * s.temperature * s.molefracs.dot(&ln_rho_m1) + }) + } } fn rachford_rice( feed: &OVector, k: &OVector, beta_in: Option, -) -> FeosResult +) -> FeosResult<(f64, [OVector; 2])> where DefaultAllocator: Allocator, { @@ -494,9 +532,16 @@ where beta = 0.5 * (beta_min + beta_max); } if dbeta.abs() < ABS_TOL { - return Ok(beta); + // update VLE + let v = feed.component_mul(&k.map(|k| beta * k / (1.0 - beta + beta * k))); + let l = feed.component_mul(&k.map(|k| (1.0 - beta) / (1.0 - beta + beta * k))); + return Ok((beta, [v, l])); } } - Ok(beta) + // update VLE + let v = feed.component_mul(&k.map(|k| beta * k / (1.0 - beta + beta * k))); + let l = feed.component_mul(&k.map(|k| (1.0 - beta) / (1.0 - beta + beta * k))); + + Ok((beta, [v, l])) } diff --git a/crates/feos-core/src/phase_equilibria/vle_pure.rs b/crates/feos-core/src/phase_equilibria/vle_pure.rs index 63756fd5b..f58fc6e21 100644 --- a/crates/feos-core/src/phase_equilibria/vle_pure.rs +++ b/crates/feos-core/src/phase_equilibria/vle_pure.rs @@ -3,7 +3,7 @@ use crate::density_iteration::{_density_iteration, _pressure_spinodal}; use crate::equation_of_state::{Residual, Subset}; use crate::errors::{FeosError, FeosResult}; use crate::state::{Contributions, DensityInitialization, State}; -use crate::{Composition, ReferenceSystem, SolverOptions, TemperatureOrPressure, Verbosity}; +use crate::{ReferenceSystem, SolverOptions, TemperatureOrPressure, Verbosity}; use nalgebra::allocator::Allocator; use nalgebra::{DVector, DefaultAllocator, Dim, SVector, U1, U2}; use num_dual::{DualNum, DualStruct, Gradients, gradient, partial}; @@ -17,7 +17,6 @@ const TOL_PURE: f64 = 1e-12; impl, N: Gradients, D: DualNum + Copy> PhaseEquilibrium where DefaultAllocator: Allocator + Allocator + Allocator, - (): Composition + Composition, { /// Calculate a phase equilibrium for a pure component. pub fn pure>( @@ -26,7 +25,7 @@ where initial_state: Option<&Self>, options: SolverOptions, ) -> FeosResult { - let (t, rho) = if let Some(t) = temperature_or_pressure.temperature() { + let (t, [rho_v, rho_l]) = if let Some(t) = temperature_or_pressure.temperature() { let (_, rho) = Self::pure_t(eos, t, initial_state, options)?; (t, rho) } else if let Some(p) = temperature_or_pressure.pressure() { @@ -34,7 +33,11 @@ where } else { unreachable!() }; - Ok(Self(rho.map(|r| State::new_pure(eos, t, r).unwrap()))) + let x = E::pure_molefracs(); + Ok(Self::two_phase( + State::new(eos, t, rho_v, &x)?, + State::new(eos, t, rho_l, x)?, + )) } /// Calculate a phase equilibrium for a pure component @@ -238,7 +241,6 @@ where impl, N: Gradients, D: DualNum + Copy> PhaseEquilibrium where DefaultAllocator: Allocator + Allocator + Allocator, - (): Composition, { /// Calculate a phase equilibrium for a pure component /// and given pressure. @@ -396,7 +398,6 @@ fn init_pure_p, N: Gradients>( ) -> FeosResult<(f64, [f64; 2])> where DefaultAllocator: Allocator + Allocator + Allocator, - (): Composition, { let trial_temperatures = [300.0, 500.0, 200.0]; let p = pressure.into_reduced(); @@ -416,7 +417,7 @@ where }; let [mut t_v, mut t_l] = [t0, t0]; - let cp = State::critical_point(eos, (), None, None, SolverOptions::default())?; + let cp = State::critical_point(eos, &x, None, None, SolverOptions::default())?; let cp_density = cp.density.into_reduced(); if pressure > cp.pressure(Contributions::Total) { return Err(FeosError::SuperCritical); @@ -540,7 +541,7 @@ impl PhaseEquilibrium { vle_pure.liquid().density, molefracs_liquid, )?; - Ok(PhaseEquilibrium::from_states(vapor, liquid)) + Ok(PhaseEquilibrium::two_phase(vapor, liquid)) }) .ok() }) diff --git a/crates/feos-core/src/state/composition.rs b/crates/feos-core/src/state/composition.rs index 0ad47ac9e..f6a190e33 100644 --- a/crates/feos-core/src/state/composition.rs +++ b/crates/feos-core/src/state/composition.rs @@ -4,7 +4,7 @@ use crate::{FeosError, FeosResult}; use nalgebra::allocator::Allocator; use nalgebra::{DefaultAllocator, Dim, Dyn, OVector, U1, U2, dvector, vector}; use num_dual::{DualNum, DualStruct}; -use quantity::{Density, Moles, Quantity, SIUnit}; +use quantity::Moles; pub trait Composition + Copy, N: Dim> where @@ -15,16 +15,6 @@ where self, eos: &E, ) -> FeosResult<(OVector, Option>)>; - fn density(&self) -> Option> { - None - } -} - -pub trait FullComposition + Copy, N: Dim>: Composition -where - DefaultAllocator: Allocator, -{ - fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)>; } // trivial implementations @@ -40,15 +30,6 @@ where } } -impl + Copy, N: Dim> FullComposition for (OVector, Moles) -where - DefaultAllocator: Allocator, -{ - fn into_moles>(self, _: &E) -> FeosResult<(OVector, Moles)> { - Ok((self.0, self.1)) - } -} - impl + Copy, N: Dim> Composition for (OVector, Option>) where DefaultAllocator: Allocator, @@ -136,12 +117,6 @@ impl + Copy> Composition for Moles { } } -impl + Copy> FullComposition for Moles { - fn into_moles>(self, _: &E) -> FeosResult<(OVector, Moles)> { - Ok(((vector![D::one()]), self)) - } -} - impl + Copy> Composition for Moles { fn into_molefracs>( self, @@ -158,19 +133,6 @@ impl + Copy> Composition for Moles { } } -impl + Copy> FullComposition for Moles { - fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)> { - if eos.components() == 1 { - Ok(((dvector![D::one()]), self)) - } else { - Err(FeosError::UndeterminedState(format!( - "A single mole number ({}) can only be used to specify a pure component!", - self.re() - ))) - } - } -} - // the mixture can be specified by its molefractions // // for a dynamic number of components, it is also possible to specify only the @@ -228,15 +190,6 @@ where } } -impl + Copy, N: Dim> FullComposition for Moles> -where - DefaultAllocator: Allocator, -{ - fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)> { - (&self).into_moles(eos) - } -} - impl + Copy, N: Dim> Composition for &Moles> where DefaultAllocator: Allocator, @@ -257,48 +210,3 @@ where } } } - -impl + Copy, N: Dim> FullComposition for &Moles> -where - DefaultAllocator: Allocator, -{ - fn into_moles>(self, eos: &E) -> FeosResult<(OVector, Moles)> { - if eos.components() == self.len() { - let total_moles = self.sum(); - Ok(((self.convert_to(total_moles)), total_moles)) - } else { - Err(FeosError::UndeterminedState(format!( - "The length of the composition vector ({}) does not match the number of components ({})!", - self.len(), - eos.components() - ))) - } - } -} - -// the mixture can be specified with the partial density -impl + Copy, N: Dim> Composition - for Quantity, SIUnit<0, -3, 0, 0, 0, 1, 0>> -where - DefaultAllocator: Allocator, -{ - fn into_molefracs>( - self, - eos: &E, - ) -> FeosResult<(OVector, Option>)> { - if eos.components() == self.len() { - let density = self.sum(); - Ok(((self.convert_to(density)), None)) - } else { - panic!( - "The length of the composition vector ({}) does not match the number of components ({})!", - self.len(), - eos.components() - ) - } - } - - fn density(&self) -> Option> { - Some(self.sum()) - } -} diff --git a/crates/feos-core/src/state/mod.rs b/crates/feos-core/src/state/mod.rs index ec99d4f19..716468aa8 100644 --- a/crates/feos-core/src/state/mod.rs +++ b/crates/feos-core/src/state/mod.rs @@ -23,7 +23,7 @@ mod properties; mod residual_properties; mod statevec; pub(crate) use cache::Cache; -pub use composition::{Composition, FullComposition}; +pub use composition::Composition; pub use statevec::StateVec; /// Possible contributions that can be computed. @@ -177,18 +177,18 @@ where } /// Mole numbers $N_i$ - pub fn moles(&self) -> Moles> { - Dimensionless::new(&self.molefracs) * self.total_moles() + pub fn moles(&self) -> FeosResult>> { + Ok(Dimensionless::new(&self.molefracs) * self.total_moles()?) } /// Total moles $N=\sum_iN_i$ - pub fn total_moles(&self) -> Moles { - self.total_moles.expect("Extensive properties can only be evaluated for states that are initialized with extensive properties!") + pub fn total_moles(&self) -> FeosResult> { + self.total_moles.ok_or(FeosError::IntensiveState) } /// Volume $V$ - pub fn volume(&self) -> Volume { - self.molar_volume * self.total_moles() + pub fn volume(&self) -> FeosResult> { + Ok(self.molar_volume * self.total_moles()?) } } @@ -225,13 +225,19 @@ where /// This function will perform a validation of the given properties, i.e. test for signs /// and if values are finite. It will **not** validate physics, i.e. if the resulting /// densities are below the maximum packing fraction. - pub fn new_nvt>( + pub fn new_nvt>( eos: &E, temperature: Temperature, volume: Volume, composition: X, ) -> FeosResult { - let (molefracs, total_moles) = composition.into_moles(eos)?; + let (molefracs, total_moles) = composition.into_molefracs(eos)?; + let Some(total_moles) = total_moles else { + return Err(FeosError::UndeterminedState( + "Missing total mole number in the specification!".into(), + )); + }; + let density = total_moles / volume; Self::new(eos, temperature, density, (molefracs, total_moles)) } @@ -247,7 +253,8 @@ where partial_density: Density>, ) -> FeosResult { let density = partial_density.sum(); - Self::new(eos, temperature, density, partial_density) + let molefracs = partial_density.convert_into(density); + Self::new(eos, temperature, density, molefracs) } /// Return a new `State` for a pure component given a temperature and a density. @@ -340,14 +347,6 @@ where pressure: Option>, density_initialization: Option, ) -> FeosResult> { - // check if density is given twice - if density.and(composition.density()).is_some() { - return Err(FeosError::UndeterminedState(String::from( - "Both density and partial density given.", - ))); - } - let density = density.or_else(|| composition.density()); - // unwrap composition let (x, n) = composition.into_molefracs(eos)?; @@ -586,7 +585,7 @@ where } /// Return a new `State` for given volume $V$ and molar internal energy $u$. - pub fn new_nvu + Clone>( + pub fn new_nvu + Clone>( eos: &E, volume: Volume, molar_internal_energy: MolarEnergy, diff --git a/crates/feos-core/src/state/properties.rs b/crates/feos-core/src/state/properties.rs index c93c1e77d..fa0f53b4a 100644 --- a/crates/feos-core/src/state/properties.rs +++ b/crates/feos-core/src/state/properties.rs @@ -1,6 +1,6 @@ use super::{Contributions, State}; use crate::equation_of_state::{Molarweight, Total}; -use crate::{ReferenceSystem, Residual}; +use crate::{FeosResult, ReferenceSystem, Residual}; use nalgebra::allocator::Allocator; use nalgebra::{DefaultAllocator, OVector}; use num_dual::{Dual, DualNum, Gradients, partial, partial2}; @@ -79,8 +79,8 @@ where } /// Entropy: $S=-\left(\frac{\partial A}{\partial T}\right)_{V,N_i}$ - pub fn entropy(&self, contributions: Contributions) -> Entropy { - self.molar_entropy(contributions) * self.total_moles() + pub fn entropy(&self, contributions: Contributions) -> FeosResult> { + Ok(self.molar_entropy(contributions) * self.total_moles()?) } /// Molar entropy: $s=\frac{S}{N}$ @@ -147,8 +147,8 @@ where } /// Enthalpy: $H=A+TS+pV$ - pub fn enthalpy(&self, contributions: Contributions) -> Energy { - self.molar_enthalpy(contributions) * self.total_moles() + pub fn enthalpy(&self, contributions: Contributions) -> FeosResult> { + Ok(self.molar_enthalpy(contributions) * self.total_moles()?) } /// Molar enthalpy: $h=\frac{H}{N}$ @@ -166,8 +166,8 @@ where } /// Helmholtz energy: $A$ - pub fn helmholtz_energy(&self, contributions: Contributions) -> Energy { - self.molar_helmholtz_energy(contributions) * self.total_moles() + pub fn helmholtz_energy(&self, contributions: Contributions) -> FeosResult> { + Ok(self.molar_helmholtz_energy(contributions) * self.total_moles()?) } /// Molar Helmholtz energy: $a=\frac{A}{N}$ @@ -187,8 +187,8 @@ where } /// Internal energy: $U=A+TS$ - pub fn internal_energy(&self, contributions: Contributions) -> Energy { - self.molar_internal_energy(contributions) * self.total_moles() + pub fn internal_energy(&self, contributions: Contributions) -> FeosResult> { + Ok(self.molar_internal_energy(contributions) * self.total_moles()?) } /// Molar internal energy: $u=\frac{U}{N}$ @@ -198,8 +198,8 @@ where } /// Gibbs energy: $G=A+pV$ - pub fn gibbs_energy(&self, contributions: Contributions) -> Energy { - self.molar_gibbs_energy(contributions) * self.total_moles() + pub fn gibbs_energy(&self, contributions: Contributions) -> FeosResult> { + Ok(self.molar_gibbs_energy(contributions) * self.total_moles()?) } /// Molar Gibbs energy: $g=\frac{G}{N}$ diff --git a/crates/feos-core/src/state/residual_properties.rs b/crates/feos-core/src/state/residual_properties.rs index 4a6b8f015..184f1aa53 100644 --- a/crates/feos-core/src/state/residual_properties.rs +++ b/crates/feos-core/src/state/residual_properties.rs @@ -34,8 +34,8 @@ where } /// Residual Helmholtz energy $A^\text{res}$ - pub fn residual_helmholtz_energy(&self) -> Energy { - self.residual_molar_helmholtz_energy() * self.total_moles() + pub fn residual_helmholtz_energy(&self) -> FeosResult> { + Ok(self.residual_molar_helmholtz_energy() * self.total_moles()?) } /// Residual molar Helmholtz energy $a^\text{res}$ @@ -50,8 +50,8 @@ where } /// Residual entropy $S^\text{res}=\left(\frac{\partial A^\text{res}}{\partial T}\right)_{V,N_i}$ - pub fn residual_entropy(&self) -> Entropy { - self.residual_molar_entropy() * self.total_moles() + pub fn residual_entropy(&self) -> FeosResult> { + Ok(self.residual_molar_entropy() * self.total_moles()?) } /// Residual molar entropy $s^\text{res}=\left(\frac{\partial a^\text{res}}{\partial T}\right)_{V,N_i}$ @@ -431,18 +431,14 @@ impl State { .unzip(); let solvent_molefracs = DVector::from_vec(solvent_molefracs); let solvent = eos.subset(&solvent_comps); - let vle = if solvent_comps.len() == 1 { - PhaseEquilibrium::pure(&solvent, temperature, None, Default::default()) - } else { - PhaseEquilibrium::bubble_point( - &solvent, - temperature, - &solvent_molefracs, - None, - None, - Default::default(), - ) - }?; + let vle = PhaseEquilibrium::bubble_point( + &solvent, + temperature, + &solvent_molefracs, + None, + None, + Default::default(), + )?; // Calculate the liquid state including the Henry components let liquid = State::new(eos, temperature, vle.liquid().density, molefracs.clone())?; @@ -506,8 +502,8 @@ where } /// Residual enthalpy: $H^\text{res}(T,p,\mathbf{n})=A^\text{res}+TS^\text{res}+p^\text{res}V$ - pub fn residual_enthalpy(&self) -> Energy { - self.residual_molar_enthalpy() * self.total_moles() + pub fn residual_enthalpy(&self) -> FeosResult> { + Ok(self.residual_molar_enthalpy() * self.total_moles()?) } /// Residual molar enthalpy: $h^\text{res}(T,p,\mathbf{n})=a^\text{res}+Ts^\text{res}+p^\text{res}v$ @@ -518,8 +514,8 @@ where } /// Residual internal energy: $U^\text{res}(T,V,\mathbf{n})=A^\text{res}+TS^\text{res}$ - pub fn residual_internal_energy(&self) -> Energy { - self.residual_molar_internal_energy() * self.total_moles() + pub fn residual_internal_energy(&self) -> FeosResult> { + Ok(self.residual_molar_internal_energy() * self.total_moles()?) } /// Residual molar internal energy: $u^\text{res}(T,V,\mathbf{n})=a^\text{res}+Ts^\text{res}$ @@ -528,8 +524,8 @@ where } /// Residual Gibbs energy: $G^\text{res}(T,p,\mathbf{n})=A^\text{res}+p^\text{res}V-NRT \ln Z$ - pub fn residual_gibbs_energy(&self) -> Energy { - self.residual_molar_gibbs_energy() * self.total_moles() + pub fn residual_gibbs_energy(&self) -> FeosResult> { + Ok(self.residual_molar_gibbs_energy() * self.total_moles()?) } /// Residual Gibbs energy: $g^\text{res}(T,p,\mathbf{n})=a^\text{res}+p^\text{res}v-RT \ln Z$ @@ -601,16 +597,17 @@ where } /// Mass of each component: $m_i=n_iMW_i$ - pub fn mass(&self) -> Mass> { - self.eos + pub fn mass(&self) -> FeosResult>> { + Ok(self + .eos .molar_weight() .component_mul(&Dimensionless::new(self.molefracs.clone())) - * self.total_moles() + * self.total_moles()?) } /// Total mass: $m=\sum_im_i=nMW$ - pub fn total_mass(&self) -> Mass { - self.total_molar_weight() * self.total_moles() + pub fn total_mass(&self) -> FeosResult> { + Ok(self.total_molar_weight() * self.total_moles()?) } /// Mass density: $\rho^{(m)}=\frac{m}{V}$ diff --git a/crates/feos-core/src/state/statevec.rs b/crates/feos-core/src/state/statevec.rs index 63d51b3fd..8a498e2db 100644 --- a/crates/feos-core/src/state/statevec.rs +++ b/crates/feos-core/src/state/statevec.rs @@ -2,6 +2,8 @@ use super::Contributions; use super::State; #[cfg(feature = "ndarray")] +use crate::FeosResult; +#[cfg(feature = "ndarray")] use crate::equation_of_state::{Molarweight, Residual, Total}; #[cfg(feature = "ndarray")] use ndarray::{Array1, Array2}; @@ -61,10 +63,15 @@ impl StateVec<'_, E> { Density::from_shape_fn(self.0.len(), |i| self.0[i].density) } - pub fn moles(&self) -> Moles> { - Moles::from_shape_fn((self.0.len(), self.0[0].eos.components()), |(i, j)| { - self.0[i].moles().get(j) - }) + pub fn moles(&self) -> FeosResult>> { + if let Err(e) = self.0[0].moles() { + Err(e) + } else { + Ok(Moles::from_shape_fn( + (self.0.len(), self.0[0].eos.components()), + |(i, j)| self.0[i].moles().unwrap().get(j), + )) + } } pub fn molefracs(&self) -> Array2 { diff --git a/crates/feos/benches/contributions.rs b/crates/feos/benches/contributions.rs index 3e74bf68c..7dc8332c2 100644 --- a/crates/feos/benches/contributions.rs +++ b/crates/feos/benches/contributions.rs @@ -74,7 +74,7 @@ fn pcsaft(c: &mut Criterion) { State::new_npt(&&eos, t, p, &moles, Some(DensityInitialization::Liquid)).unwrap(); let temperature = Dual64::from(state.temperature.into_reduced()).derivative(); let molar_volume = Dual::from(1.0 / state.density.into_reduced()); - let moles = state.moles().to_reduced().map(Dual::from); + let moles = state.moles().unwrap().to_reduced().map(Dual::from); // let state_hd = state.derive1(Derivative::DT); let name1 = comp1.identifier.name.as_deref().unwrap(); let name2 = comp2.identifier.name.as_deref().unwrap(); diff --git a/crates/feos/benches/dft_pore.rs b/crates/feos/benches/dft_pore.rs index 98dcba4e2..4c5b3d7ba 100644 --- a/crates/feos/benches/dft_pore.rs +++ b/crates/feos/benches/dft_pore.rs @@ -67,15 +67,9 @@ fn pcsaft(c: &mut Criterion) { ) .unwrap(); let func = &PcSaftFunctional::new(parameters); - let vle = PhaseEquilibrium::bubble_point( - &func, - 300.0 * KELVIN, - &dvector![0.5, 0.5], - None, - None, - Default::default(), - ) - .unwrap(); + let vle = + PhaseEquilibrium::bubble_point(&func, 300.0 * KELVIN, 0.5, None, None, Default::default()) + .unwrap(); let bulk = vle.liquid(); group.bench_function("butane_pentane_liquid", |b| { b.iter(|| pore.initialize(bulk, None, None).unwrap().solve(None)) diff --git a/crates/feos/benches/dual_numbers.rs b/crates/feos/benches/dual_numbers.rs index c3c1bd07f..eb2eff2e4 100644 --- a/crates/feos/benches/dual_numbers.rs +++ b/crates/feos/benches/dual_numbers.rs @@ -20,10 +20,9 @@ use quantity::*; /// - molefracs (or moles) for equimolar mixture. fn state_pcsaft(n: usize, eos: &PcSaft) -> State<&PcSaft> { let moles = DVector::from_element(n, 1.0 / n as f64) * 10.0 * MOL; - let molefracs = (&moles / moles.sum()).into_value(); - let cp = State::critical_point(&eos, molefracs, None, None, Default::default()).unwrap(); + let cp = State::critical_point(&eos, &moles, None, None, Default::default()).unwrap(); let temperature = 0.8 * cp.temperature; - State::new_nvt(&eos, temperature, cp.volume(), moles).unwrap() + State::new_nvt(&eos, temperature, cp.volume().unwrap(), moles).unwrap() } /// Residual Helmholtz energy given an equation of state and a StateHD. @@ -152,11 +151,10 @@ enum Derivative { /// Creates a [StateHD] cloning temperature, volume and moles. fn derive0(state: &State) -> StateHD { - let total_moles = state.total_moles().into_reduced(); StateHD::new( state.temperature.into_reduced(), - state.volume().into_reduced() / total_moles, - &(state.moles().to_reduced() / total_moles), + state.molar_volume.into_reduced(), + &state.molefracs, ) } diff --git a/crates/feos/benches/dual_numbers_saftvrmie.rs b/crates/feos/benches/dual_numbers_saftvrmie.rs index e7409e307..80f4da09d 100644 --- a/crates/feos/benches/dual_numbers_saftvrmie.rs +++ b/crates/feos/benches/dual_numbers_saftvrmie.rs @@ -20,7 +20,13 @@ fn state_saftvrmie(n: usize, eos: &SaftVRMie) -> State<&SaftVRMie> { let molefracs = DVector::from_element(n, 1.0 / n as f64); let cp = State::critical_point(&eos, &molefracs, None, None, Default::default()).unwrap(); let temperature = 0.8 * cp.temperature; - State::new_nvt(&eos, temperature, cp.volume(), &(molefracs * 10. * MOL)).unwrap() + State::new_nvt( + &eos, + temperature, + cp.volume().unwrap(), + &(molefracs * 10. * MOL), + ) + .unwrap() } /// Residual Helmholtz energy given an equation of state and a StateHD. @@ -100,11 +106,10 @@ enum Derivative { /// Creates a [StateHD] cloning temperature, volume and moles. fn derive0(state: &State) -> StateHD { - let total_moles = state.total_moles().into_reduced(); StateHD::new( state.temperature.into_reduced(), - state.volume().into_reduced() / total_moles, - &(state.moles().to_reduced() / total_moles), + state.molar_volume.into_reduced(), + &state.molefracs, ) } diff --git a/crates/feos/benches/state_creation.rs b/crates/feos/benches/state_creation.rs index 3aa08778e..eb0c07bcd 100644 --- a/crates/feos/benches/state_creation.rs +++ b/crates/feos/benches/state_creation.rs @@ -77,7 +77,7 @@ fn bench_states(c: &mut Criterion, group_name: &str, eos: &E) { eos, crit.temperature, crit.pressure(Contributions::Total) * 0.95, - &crit.moles(), + &crit.molefracs, None, Default::default(), None, diff --git a/crates/feos/src/epcsaft/eos/mod.rs b/crates/feos/src/epcsaft/eos/mod.rs index 2edba6f69..5a0a8c81f 100644 --- a/crates/feos/src/epcsaft/eos/mod.rs +++ b/crates/feos/src/epcsaft/eos/mod.rs @@ -181,7 +181,7 @@ mod tests { let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&&e, t, v, &n).unwrap(); - let p_ig = s.total_moles() * RGAS * t / v; + let p_ig = s.total_moles().unwrap() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), @@ -197,7 +197,7 @@ mod tests { let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&&e, t, v, &n).unwrap(); - let p_ig = s.total_moles() * RGAS * t / v; + let p_ig = s.total_moles().unwrap() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), diff --git a/crates/feos/src/pcsaft/eos/mod.rs b/crates/feos/src/pcsaft/eos/mod.rs index 752e7c6f6..37a885248 100644 --- a/crates/feos/src/pcsaft/eos/mod.rs +++ b/crates/feos/src/pcsaft/eos/mod.rs @@ -394,35 +394,37 @@ mod tests { use quantity::{BAR, KELVIN, METER, PASCAL, RGAS}; #[test] - fn ideal_gas_pressure() { + fn ideal_gas_pressure() -> FeosResult<()> { let e = &propane_parameters(); let t = 200.0 * KELVIN; let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&e, t, v, n).unwrap(); - let p_ig = s.total_moles() * RGAS * t / v; + let p_ig = s.total_moles()? * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), s.pressure(Contributions::Total), epsilon = 1e-10 ); + Ok(()) } #[test] - fn ideal_gas_heat_capacity_joback() { + fn ideal_gas_heat_capacity_joback() -> FeosResult<()> { let e = &propane_parameters(); let t = 200.0 * KELVIN; let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&e, t, v, n).unwrap(); - let p_ig = s.total_moles() * RGAS * t / v; + let p_ig = s.total_moles()? * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), s.pressure(Contributions::Total), epsilon = 1e-10 ); + Ok(()) } #[test] @@ -599,7 +601,7 @@ mod tests_parameter_fit { use feos_core::{Contributions, PropertiesAD, ReferenceSystem, SolverOptions}; use feos_core::{FeosResult, ParametersAD, PhaseEquilibrium, State}; use nalgebra::{U1, U3, U8, vector}; - use num_dual::{DualStruct, DualVec, partial}; + use num_dual::{Dual64, DualStruct, DualVec, partial}; use quantity::{BAR, KELVIN, LITER, MOL, PASCAL}; fn pcsaft_non_assoc() -> PcSaftPure { @@ -787,9 +789,7 @@ mod tests_parameter_fit { let h = params[i] * 1e-7; params[i] += h; let pcsaft_h = PcSaftPure(params); - let rho_h = - State::new_npt(&pcsaft_h, temperature, pressure, vector![1.0], Some(Liquid))? - .density; + let rho_h = State::new_npt(&pcsaft_h, temperature, pressure, (), Some(Liquid))?.density; let drho_h = (rho_h.convert_into(MOL / LITER) - rho) / h; let drho = grad[i]; println!( @@ -823,7 +823,7 @@ mod tests_parameter_fit { let p_h = PhaseEquilibrium::bubble_point( &pcsaft_h, temperature, - &x, + x, None, None, Default::default(), @@ -846,7 +846,7 @@ mod tests_parameter_fit { let (pcsaft, _) = pcsaft_binary()?; let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); let temperature = 500.0 * KELVIN; - let y = vector![0.5, 0.5]; + let y = 0.5; let p = pcsaft_ad.dew_point_pressure(temperature, None, y)?; let p = p.convert_into(BAR); let (p, [[grad]]) = (p.re, p.eps.unwrap_generic(U1, U1).data.0); @@ -858,16 +858,10 @@ mod tests_parameter_fit { let h = 1e-7; kij += h; let pcsaft_h = PcSaftBinary::new(params, kij); - let p_h = PhaseEquilibrium::dew_point( - &pcsaft_h, - temperature, - &y, - None, - None, - Default::default(), - )? - .vapor() - .pressure(Contributions::Total); + let p_h = + PhaseEquilibrium::dew_point(&pcsaft_h, temperature, y, None, None, Default::default())? + .vapor() + .pressure(Contributions::Total); let dp_h = (p_h.convert_into(BAR) - p) / h; println!( "k_ij: {:11.5} {:11.5} {:.3e}", @@ -885,11 +879,11 @@ mod tests_parameter_fit { let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); let pressure = Pressure::from_reduced(DualVec::from(45. * BAR.into_reduced())); let t_init = Temperature::from_reduced(DualVec::from(500.0)); - let x = vector![0.5, 0.5].map(DualVec::from); + let x = DualVec::from(0.5); let t = PhaseEquilibrium::bubble_point( &pcsaft_ad, pressure, - &x, + x, Some(t_init), None, Default::default(), @@ -909,7 +903,7 @@ mod tests_parameter_fit { let t_h = PhaseEquilibrium::bubble_point( &pcsaft_h, pressure.re(), - &x.map(|x| x.re()), + x.re(), Some(t_init.re()), None, Default::default(), @@ -933,11 +927,11 @@ mod tests_parameter_fit { let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); let pressure = Pressure::from_reduced(DualVec::from(45. * BAR.into_reduced())); let t_init = Temperature::from_reduced(DualVec::from(500.0)); - let x = vector![0.5, 0.5].map(DualVec::from); + let x = DualVec::from(0.5); let t = PhaseEquilibrium::dew_point( &pcsaft_ad, pressure, - &x, + x, Some(t_init), None, Default::default(), @@ -957,7 +951,7 @@ mod tests_parameter_fit { let t_h = PhaseEquilibrium::dew_point( &pcsaft_h, pressure.re(), - &x.map(|x| x.re()), + x.re(), Some(t_init.re()), None, Default::default(), @@ -980,10 +974,10 @@ mod tests_parameter_fit { let (pcsaft, _) = pcsaft_binary()?; let pcsaft_ad = pcsaft; let mut temperature = 500.0 * KELVIN; - let x = vector![0.5, 0.5]; + let x = 0.5; let (p, grad) = first_derivative( partial( - |t, x| { + |t, &x: &Dual64| { let eos = pcsaft_ad.lift(); PhaseEquilibrium::bubble_point(&eos, t, x, None, None, Default::default()) .unwrap() @@ -1003,7 +997,7 @@ mod tests_parameter_fit { let p_h = PhaseEquilibrium::bubble_point( &pcsaft_ad, temperature, - &x, + x, None, None, Default::default(), @@ -1017,7 +1011,7 @@ mod tests_parameter_fit { grad, ((dp_h - grad).convert_into(grad)).abs() ); - assert_relative_eq!(grad, dp_h, max_relative = 1e-7); + assert_relative_eq!(grad, dp_h, max_relative = 2e-7); Ok(()) } @@ -1027,10 +1021,10 @@ mod tests_parameter_fit { let pcsaft_ad = pcsaft; let mut pressure = 45. * BAR; let t0 = Some(500. * KELVIN); - let x = vector![0.5, 0.5]; + let x = 0.5; let (t, grad) = first_derivative( partial2( - |p, x, &t0| { + |p, &x: &Dual64, &t0| { let eos = pcsaft_ad.lift(); PhaseEquilibrium::bubble_point(&eos, p, x, t0, None, Default::default()) .unwrap() @@ -1049,7 +1043,7 @@ mod tests_parameter_fit { let h = 1e-5 * BAR; pressure += h; let t_h = - PhaseEquilibrium::bubble_point(&pcsaft_ad, pressure, &x, t0, None, Default::default())? + PhaseEquilibrium::bubble_point(&pcsaft_ad, pressure, x, t0, None, Default::default())? .vapor() .temperature; let dt_h = (t_h - t) / h; @@ -1059,7 +1053,7 @@ mod tests_parameter_fit { grad, ((dt_h - grad).convert_into(grad)).abs() ); - assert_relative_eq!(grad, dt_h, max_relative = 1e-7); + assert_relative_eq!(grad, dt_h, max_relative = 2e-7); Ok(()) } @@ -1069,22 +1063,19 @@ mod tests_parameter_fit { let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); let temperature = 500.0 * KELVIN; let pressure = 44.6 * BAR; - let x = vector![0.5, 0.5]; + let x = 0.5; let vle = PhaseEquilibrium::tp_flash_binary( &pcsaft_ad, Temperature::from_inner(&temperature), Pressure::from_inner(&pressure), - &Moles::from_inner(&(x * MOL)), + DualVec::from(x), SolverOptions { verbosity: feos_core::Verbosity::Iter, - tol: Some(1e-10), + tol: Some(1e-12), ..Default::default() }, )?; - let beta = vle - .vapor() - .total_moles - .convert_into(vle.vapor().total_moles + vle.liquid().total_moles); + let beta = vle.vapor_phase_fraction(); let (beta, [[grad]]) = (beta.re, beta.eps.unwrap_generic(U1, U1).data.0); println!("{beta:.5}"); @@ -1098,16 +1089,13 @@ mod tests_parameter_fit { &pcsaft_h, temperature, pressure, - &(x * MOL), + x, SolverOptions { - tol: Some(1e-10), + tol: Some(1e-12), ..Default::default() }, )?; - let beta_h = vle - .vapor() - .total_moles - .convert_into(vle.vapor().total_moles + vle.liquid().total_moles); + let beta_h = vle.vapor_phase_fraction(); let dbeta_h = (beta_h - beta) / h; println!( "k_ij: {:11.5} {:11.5} {:.3e}", diff --git a/crates/feos/src/pets/eos/mod.rs b/crates/feos/src/pets/eos/mod.rs index e26b94960..5d9b9a666 100644 --- a/crates/feos/src/pets/eos/mod.rs +++ b/crates/feos/src/pets/eos/mod.rs @@ -152,7 +152,7 @@ mod tests { let v = 1e-3 * METER.powi::<3>(); let n = dvector![1.0] * MOL; let s = State::new_nvt(&e, t, v, &n).unwrap(); - let p_ig = s.total_moles() * RGAS * t / v; + let p_ig = s.total_moles().unwrap() * RGAS * t / v; assert_relative_eq!(s.pressure(Contributions::IdealGas), p_ig, epsilon = 1e-10); assert_relative_eq!( s.pressure(Contributions::IdealGas) + s.pressure(Contributions::Residual), diff --git a/crates/feos/src/uvtheory/eos/mod.rs b/crates/feos/src/uvtheory/eos/mod.rs index 8a660db81..1957af6e8 100644 --- a/crates/feos/src/uvtheory/eos/mod.rs +++ b/crates/feos/src/uvtheory/eos/mod.rs @@ -246,9 +246,7 @@ mod test { // EoS let eos_wca = &UVTheory::new(parameters); let state_wca = State::new_nvt(&eos_wca, t_x, volume, &moles).unwrap(); - let a_wca = (state_wca.residual_helmholtz_energy() - / (RGAS * t_x * state_wca.total_moles())) - .into_value(); + let a_wca = (state_wca.residual_molar_helmholtz_energy() / (RGAS * t_x)).into_value(); assert_relative_eq!(a_wca, -0.597791038364405, max_relative = 1e-5); Ok(()) diff --git a/crates/feos/tests/pcsaft/stability_analysis.rs b/crates/feos/tests/pcsaft/stability_analysis.rs index bf31ee24d..dc1cf71d0 100644 --- a/crates/feos/tests/pcsaft/stability_analysis.rs +++ b/crates/feos/tests/pcsaft/stability_analysis.rs @@ -1,7 +1,6 @@ use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::IdentifierOption; use feos_core::{DensityInitialization, PhaseEquilibrium, SolverOptions, State}; -use nalgebra::dvector; use quantity::*; use std::error::Error; @@ -18,7 +17,7 @@ fn test_stability_analysis() -> Result<(), Box> { &&mix, 300.0 * KELVIN, 1.0 * BAR, - &(dvector![0.5, 0.5] * MOL), + 0.5, Some(DensityInitialization::Liquid), )?; let options = SolverOptions { @@ -38,7 +37,7 @@ fn test_stability_analysis() -> Result<(), Box> { let vle = PhaseEquilibrium::bubble_point( &&mix, 300.0 * KELVIN, - &dvector![0.5, 0.5], + 0.5, Some(6.0 * BAR), None, (options, options), diff --git a/crates/feos/tests/pcsaft/state_creation_mixture.rs b/crates/feos/tests/pcsaft/state_creation_mixture.rs index 46e0442b8..f74ed559b 100644 --- a/crates/feos/tests/pcsaft/state_creation_mixture.rs +++ b/crates/feos/tests/pcsaft/state_creation_mixture.rs @@ -56,7 +56,7 @@ fn volume_temperature_molefracs() -> Result<(), Box> { let moles = MOL; let x = dvector![0.3, 0.7]; let state = State::new_nvt(&&saft, temperature, volume, (x, moles))?; - assert_relative_eq!(state.volume(), volume, max_relative = 1e-10); + assert_relative_eq!(state.volume()?, volume, max_relative = 1e-10); Ok(()) } diff --git a/crates/feos/tests/pcsaft/state_creation_pure.rs b/crates/feos/tests/pcsaft/state_creation_pure.rs index cda35b75f..81ef57168 100644 --- a/crates/feos/tests/pcsaft/state_creation_pure.rs +++ b/crates/feos/tests/pcsaft/state_creation_pure.rs @@ -29,7 +29,7 @@ fn temperature_volume() -> FeosResult<()> { let volume = 1.5e-3 * METER.powi::<3>(); let moles = MOL; let state = State::new_nvt(&&saft, temperature, volume, moles)?; - assert_relative_eq!(state.volume(), volume, max_relative = 1e-10); + assert_relative_eq!(state.volume()?, volume, max_relative = 1e-10); Ok(()) } @@ -50,8 +50,8 @@ fn temperature_total_moles_volume() -> FeosResult<()> { let total_moles = MOL; let volume = METER.powi::<3>(); let state = State::new_nvt(&&saft, temperature, volume, total_moles)?; - assert_relative_eq!(state.volume(), volume, max_relative = 1e-10); - assert_relative_eq!(state.total_moles(), total_moles, max_relative = 1e-10); + assert_relative_eq!(state.volume()?, volume, max_relative = 1e-10); + assert_relative_eq!(state.total_moles()?, total_moles, max_relative = 1e-10); Ok(()) } @@ -63,8 +63,8 @@ fn temperature_total_moles_density() -> FeosResult<()> { let density = MOL / METER.powi::<3>(); let state = State::new_pure(&&saft, temperature, density)?.set_total_moles(total_moles); assert_relative_eq!(state.density, density, max_relative = 1e-10); - assert_relative_eq!(state.total_moles(), total_moles, max_relative = 1e-10); - assert_relative_eq!(state.volume(), total_moles / density, max_relative = 1e-10); + assert_relative_eq!(state.total_moles()?, total_moles, max_relative = 1e-10); + assert_relative_eq!(state.volume()?, total_moles / density, max_relative = 1e-10); Ok(()) } @@ -158,7 +158,7 @@ fn density_internal_energy() -> FeosResult<()> { let molar_internal_energy = state.molar_internal_energy(Contributions::Total); let state_nvu = State::new_nvu( &&eos, - state.volume(), + state.volume()?, molar_internal_energy, total_moles, None, @@ -203,8 +203,8 @@ fn pressure_enthalpy_total_moles_vapor() -> FeosResult<()> { let state = State::new_nvt( &&eos, state.temperature, - state.volume(), - state.total_moles(), + state.volume()?, + state.total_moles()?, )?; assert_relative_eq!( state.molar_enthalpy(Contributions::Total), @@ -266,7 +266,7 @@ fn temperature_entropy_vapor() -> FeosResult<()> { &&eos, temperature, state.molar_entropy(Contributions::Total), - state.moles(), + state.moles()?, None, )?; assert_relative_eq!( diff --git a/py-feos/src/ad/mod.rs b/py-feos/src/ad/mod.rs index 75958d7ca..3f775a2da 100644 --- a/py-feos/src/ad/mod.rs +++ b/py-feos/src/ad/mod.rs @@ -76,7 +76,7 @@ pub fn vapor_pressure_derivatives<'py>( #[pyfunction] pub fn boiling_temperature_derivatives<'py>( model: PyEquationOfStateAD, - parameter_names: Bound<'py, PyAny>, + parameter_names: &Bound<'py, PyAny>, parameters: PyReadonlyArray2, input: PyReadonlyArray2, ) -> GradResult<'py> { diff --git a/py-feos/src/eos/mod.rs b/py-feos/src/eos/mod.rs index cd50de42f..0bc7f1a00 100644 --- a/py-feos/src/eos/mod.rs +++ b/py-feos/src/eos/mod.rs @@ -210,7 +210,6 @@ pub enum Compositions { TotalMoles(Moles), Molefracs(DVector), Moles(Moles>), - PartialDensity(Density>), } impl Composition for Compositions { @@ -224,15 +223,6 @@ impl Composition for Compositions { Self::TotalMoles(total_moles) => total_moles.into_molefracs(eos), Self::Molefracs(molefracs) => molefracs.into_molefracs(eos), Self::Moles(moles) => moles.into_molefracs(eos), - Self::PartialDensity(partial_density) => partial_density.into_molefracs(eos), - } - } - - fn density(&self) -> Option> { - if let Self::PartialDensity(partial_density) = self { - partial_density.density() - } else { - None } } } @@ -255,8 +245,6 @@ impl TryFrom>> for Compositions { Ok(Compositions::Moles(n)) } else if let Ok(n) = composition.extract::() { Ok(Compositions::TotalMoles(n)) - } else if let Ok(rho) = composition.extract::>>() { - Ok(Compositions::PartialDensity(rho)) } else { Err(PyErr::new::(format!( "failed to parse value '{composition}' as composition." diff --git a/py-feos/src/phase_equilibria.rs b/py-feos/src/phase_equilibria.rs index 38d0d2d12..c6c857506 100644 --- a/py-feos/src/phase_equilibria.rs +++ b/py-feos/src/phase_equilibria.rs @@ -1,6 +1,6 @@ use crate::{ PyVerbosity, - eos::{PyEquationOfState, parse_molefracs}, + eos::{Compositions, PyEquationOfState, parse_molefracs}, error::PyFeosError, ideal_gas::IdealGasModel, residual::ResidualModel, @@ -108,8 +108,8 @@ impl PyPhaseEquilibrium { /// The system temperature. /// pressure : SINumber /// The system pressure. - /// feed : SIArray1 - /// Feed composition (units of amount of substance). + /// feed : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float] + /// Feed composition. /// initial_state : PhaseEquilibrium, optional /// A phase equilibrium used as initial guess. /// Can speed up convergence. @@ -138,7 +138,7 @@ impl PyPhaseEquilibrium { eos: &PyEquationOfState, temperature: Temperature, pressure: Pressure, - feed: Moles>, + feed: &Bound<'_, PyAny>, initial_state: Option<&PyPhaseEquilibrium>, max_iter: Option, tol: Option, @@ -150,7 +150,7 @@ impl PyPhaseEquilibrium { &eos.0, temperature, pressure, - &feed, + Compositions::try_from(Some(feed))?, initial_state.map(|s| &s.0), (max_iter, tol, verbosity.map(|v| v.into())).into(), non_volatile_components, @@ -168,7 +168,7 @@ impl PyPhaseEquilibrium { /// The equation of state. /// temperature_or_pressure : SINumber /// The system temperature_or_pressure. - /// liquid_molefracs : numpy.ndarray + /// liquid_molefracs : float | numpy.ndarray[float] | SIArray1 | list[float] /// The mole fraction of the liquid phase. /// tp_init : SINumber, optional /// The system pressure/temperature used as starting @@ -196,12 +196,12 @@ impl PyPhaseEquilibrium { )] #[pyo3(signature = (eos, temperature_or_pressure, liquid_molefracs, tp_init=None, vapor_molefracs=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] #[expect(clippy::too_many_arguments)] - pub(crate) fn bubble_point<'py>( + pub(crate) fn bubble_point( eos: &PyEquationOfState, temperature_or_pressure: &Bound<'_, PyAny>, - liquid_molefracs: PyReadonlyArray1<'py, f64>, + liquid_molefracs: &Bound<'_, PyAny>, tp_init: Option<&Bound<'_, PyAny>>, - vapor_molefracs: Option>, + vapor_molefracs: Option>, max_iter_inner: Option, max_iter_outer: Option, tol_inner: Option, @@ -214,7 +214,7 @@ impl PyPhaseEquilibrium { PhaseEquilibrium::bubble_point( &eos.0, t, - &parse_molefracs(Some(liquid_molefracs)).unwrap(), + Compositions::try_from(Some(liquid_molefracs))?, tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( @@ -229,7 +229,7 @@ impl PyPhaseEquilibrium { PhaseEquilibrium::bubble_point( &eos.0, p, - &parse_molefracs(Some(liquid_molefracs)).unwrap(), + Compositions::try_from(Some(liquid_molefracs))?, tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( @@ -256,7 +256,7 @@ impl PyPhaseEquilibrium { /// The equation of state. /// temperature_or_pressure : SINumber /// The system temperature or pressure. - /// vapor_molefracs : numpy.ndarray + /// vapor_molefracs : float | numpy.ndarray[float] | SIArray1 | list[float] /// The mole fraction of the vapor phase. /// tp_init : SINumber, optional /// The system pressure/temperature used as starting @@ -284,12 +284,12 @@ impl PyPhaseEquilibrium { )] #[pyo3(signature = (eos, temperature_or_pressure, vapor_molefracs, tp_init=None, liquid_molefracs=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] #[expect(clippy::too_many_arguments)] - pub(crate) fn dew_point<'py>( + pub(crate) fn dew_point( eos: &PyEquationOfState, temperature_or_pressure: &Bound<'_, PyAny>, - vapor_molefracs: PyReadonlyArray1<'py, f64>, + vapor_molefracs: &Bound<'_, PyAny>, tp_init: Option<&Bound<'_, PyAny>>, - liquid_molefracs: Option>, + liquid_molefracs: Option>, max_iter_inner: Option, max_iter_outer: Option, tol_inner: Option, @@ -302,7 +302,7 @@ impl PyPhaseEquilibrium { PhaseEquilibrium::dew_point( &eos.0, t, - &parse_molefracs(Some(vapor_molefracs)).unwrap(), + Compositions::try_from(Some(vapor_molefracs))?, tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( @@ -317,7 +317,7 @@ impl PyPhaseEquilibrium { PhaseEquilibrium::dew_point( &eos.0, p, - &parse_molefracs(Some(vapor_molefracs)).unwrap(), + Compositions::try_from(Some(vapor_molefracs))?, tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( @@ -335,43 +335,6 @@ impl PyPhaseEquilibrium { } } - // /// Creates a new PhaseEquilibrium that contains two states at the - // /// specified temperature, pressure and moles. - // /// - // /// The constructor can be used in custom phase equilibrium solvers or, - // /// e.g., to generate initial guesses for an actual VLE solver. - // /// In general, the two states generated are NOT in an equilibrium. - // /// - // /// Parameters - // /// ---------- - // /// eos : EquationOfState - // /// The equation of state. - // /// temperature : SINumber - // /// The system temperature. - // /// pressure : SINumber - // /// The system pressure. - // /// vapor_moles : SIArray1 - // /// Amount of substance of the vapor phase. - // /// liquid_moles : SIArray1 - // /// Amount of substance of the liquid phase. - // /// - // /// Returns - // /// ------- - // /// PhaseEquilibrium - // #[staticmethod] - // pub(crate) fn new_npt( - // eos: &PyEquationOfState, - // temperature: Temperature, - // pressure: Pressure, - // vapor_moles: Moles>, - // liquid_moles: Moles>, - // ) -> PyResult { - // Ok(Self( - // PhaseEquilibrium::new_xpt(&eos.0, temperature, pressure, &vapor_moles, &liquid_moles) - // .map_err(PyFeosError::from)?, - // )) - // } - #[getter] fn get_vapor(&self) -> PyState { PyState(self.0.vapor().clone()) @@ -804,8 +767,8 @@ impl PyPhaseDiagram { /// ---------- /// eos: Eos /// The equation of state. - /// molefracs: np.ndarray[float] - /// The composition of the liquid phase. + /// composition : float | numpy.ndarray[float] | SIArray1 | list[float] + /// Composition of the mixture. /// min_temperature: SINumber /// The lower limit for the temperature. /// npoints: int @@ -830,13 +793,13 @@ impl PyPhaseDiagram { /// PhaseDiagram #[staticmethod] #[pyo3( - text_signature = "(eos, molefracs, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None)" + text_signature = "(eos, composition, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None)" )] - #[pyo3(signature = (eos, molefracs, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] + #[pyo3(signature = (eos, composition, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] #[expect(clippy::too_many_arguments)] pub(crate) fn bubble_point_line<'py>( eos: &PyEquationOfState, - molefracs: PyReadonlyArray1<'py, f64>, + composition: &Bound<'py, PyAny>, min_temperature: Temperature, npoints: usize, critical_temperature: Option, @@ -848,7 +811,7 @@ impl PyPhaseDiagram { ) -> PyResult { let dia = PhaseDiagram::bubble_point_line( &eos.0, - &parse_molefracs(Some(molefracs)).unwrap(), + Compositions::try_from(Some(composition))?, min_temperature, npoints, critical_temperature, @@ -871,8 +834,8 @@ impl PyPhaseDiagram { /// ---------- /// eos: Eos /// The equation of state. - /// molefracs: np.ndarray[float] - /// The composition of the vapor phase. + /// composition : float | numpy.ndarray[float] | SIArray1 | list[float] + /// Composition of the mixture. /// min_temperature: SINumber /// The lower limit for the temperature. /// npoints: int @@ -897,13 +860,13 @@ impl PyPhaseDiagram { /// PhaseDiagram #[staticmethod] #[pyo3( - text_signature = "(eos, molefracs, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None)" + text_signature = "(eos, composition, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None)" )] - #[pyo3(signature = (eos, molefracs, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] + #[pyo3(signature = (eos, composition, min_temperature, npoints, critical_temperature=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] #[expect(clippy::too_many_arguments)] pub(crate) fn dew_point_line<'py>( eos: &PyEquationOfState, - molefracs: PyReadonlyArray1<'py, f64>, + composition: &Bound<'py, PyAny>, min_temperature: Temperature, npoints: usize, critical_temperature: Option, @@ -915,7 +878,7 @@ impl PyPhaseDiagram { ) -> PyResult { let dia = PhaseDiagram::dew_point_line( &eos.0, - &parse_molefracs(Some(molefracs)).unwrap(), + Compositions::try_from(Some(composition))?, min_temperature, npoints, critical_temperature, @@ -934,8 +897,8 @@ impl PyPhaseDiagram { /// ---------- /// eos: Eos /// The equation of state. - /// molefracs: np.ndarray[float] - /// The composition of the mixture. + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float] + /// Composition of the mixture. /// min_temperature: SINumber /// The lower limit for the temperature. /// npoints: int @@ -956,13 +919,13 @@ impl PyPhaseDiagram { /// PhaseDiagram #[staticmethod] #[pyo3( - text_signature = "(eos, molefracs, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None)" + text_signature = "(eos, composition, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None)" )] - #[pyo3(signature = (eos, molefracs, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None))] + #[pyo3(signature = (eos, composition, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None))] #[expect(clippy::too_many_arguments)] pub(crate) fn spinodal<'py>( eos: &PyEquationOfState, - molefracs: PyReadonlyArray1<'py, f64>, + composition: &Bound<'py, PyAny>, min_temperature: Temperature, npoints: usize, critical_temperature: Option, @@ -972,7 +935,7 @@ impl PyPhaseDiagram { ) -> PyResult { let dia = PhaseDiagram::spinodal( &eos.0, - &parse_molefracs(Some(molefracs)).unwrap(), + Compositions::try_from(Some(composition))?, min_temperature, npoints, critical_temperature, diff --git a/py-feos/src/state.rs b/py-feos/src/state.rs index 6d5ff09f8..761055a3b 100644 --- a/py-feos/src/state.rs +++ b/py-feos/src/state.rs @@ -110,7 +110,7 @@ impl PyState { eos: &PyEquationOfState, temperature: Option, volume: Option, - density: Option, + mut density: Option, composition: Option<&Bound<'py, PyAny>>, pressure: Option, molar_enthalpy: Option, @@ -119,7 +119,15 @@ impl PyState { density_initialization: Option<&Bound<'py, PyAny>>, initial_temperature: Option, ) -> PyResult { - let composition = Compositions::try_from(composition)?; + // partial density is supported here as a special case + let composition = if let Some(composition) = composition + && let Ok(rho) = composition.extract::>>() + { + density = Some(rho.sum()); + Compositions::Molefracs(rho.convert_into(density.unwrap())) + } else { + Compositions::try_from(composition)? + }; let density_init = if let Some(di) = density_initialization { if let Ok(d) = di.extract::().as_deref() { match d { @@ -203,9 +211,8 @@ impl PyState { /// ---------- /// eos: EquationOfState /// The equation of state to use. - /// molefracs: np.ndarray[float], optional - /// Molar composition. - /// Only optional for a pure component. + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional + /// Composition of the mixture. /// initial_temperature: SINumber, optional /// The initial temperature. /// max_iter : int, optional @@ -325,9 +332,8 @@ impl PyState { /// The equation of state to use. /// temperature: SINumber /// The temperature. - /// molefracs: np.ndarray[float], optional - /// Molar composition. - /// Only optional for a pure component. + /// composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional + /// Composition of the mixture. /// max_iter : int, optional /// The maximum number of iterations. /// tol: float, optional @@ -834,8 +840,11 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn entropy(&self, contributions: PyContributions) -> Entropy { - self.0.entropy(contributions.into()) + fn entropy(&self, contributions: PyContributions) -> PyResult { + self.0 + .entropy(contributions.into()) + .map_err(PyFeosError::from) + .map_err(PyErr::from) } /// Return derivative of molar entropy with respect to temperature. @@ -891,8 +900,11 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn enthalpy(&self, contributions: PyContributions) -> Energy { - self.0.enthalpy(contributions.into()) + fn enthalpy(&self, contributions: PyContributions) -> PyResult { + self.0 + .enthalpy(contributions.into()) + .map_err(PyFeosError::from) + .map_err(PyErr::from) } /// Return molar enthalpy. @@ -932,8 +944,11 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn helmholtz_energy(&self, contributions: PyContributions) -> Energy { - self.0.helmholtz_energy(contributions.into()) + fn helmholtz_energy(&self, contributions: PyContributions) -> PyResult { + self.0 + .helmholtz_energy(contributions.into()) + .map_err(PyFeosError::from) + .map_err(PyErr::from) } /// Return molar Helmholtz energy. @@ -973,8 +988,11 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn gibbs_energy(&self, contributions: PyContributions) -> Energy { - self.0.gibbs_energy(contributions.into()) + fn gibbs_energy(&self, contributions: PyContributions) -> PyResult { + self.0 + .gibbs_energy(contributions.into()) + .map_err(PyFeosError::from) + .map_err(PyErr::from) } /// Return molar Gibbs energy. @@ -1005,8 +1023,11 @@ impl PyState { /// ------- /// SINumber #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn internal_energy(&self, contributions: PyContributions) -> Energy { - self.0.internal_energy(contributions.into()) + fn internal_energy(&self, contributions: PyContributions) -> PyResult { + self.0 + .internal_energy(contributions.into()) + .map_err(PyFeosError::from) + .map_err(PyErr::from) } /// Return molar internal energy. @@ -1111,8 +1132,11 @@ impl PyState { /// Returns /// ------- /// SIArray1 - fn mass(&self) -> Mass> { - self.0.mass() + fn mass(&self) -> PyResult>> { + self.0 + .mass() + .map_err(PyFeosError::from) + .map_err(PyErr::from) } /// Returns system's total mass. @@ -1120,8 +1144,11 @@ impl PyState { /// Returns /// ------- /// SINumber - fn total_mass(&self) -> Mass { - self.0.total_mass() + fn total_mass(&self) -> PyResult { + self.0 + .total_mass() + .map_err(PyFeosError::from) + .map_err(PyErr::from) } /// Returns system's mass density. @@ -1346,8 +1373,11 @@ impl PyState { } #[getter] - fn get_total_moles(&self) -> Moles { - self.0.total_moles() + fn get_total_moles(&self) -> PyResult { + self.0 + .total_moles() + .map_err(PyFeosError::from) + .map_err(PyErr::from) } #[getter] @@ -1356,8 +1386,11 @@ impl PyState { } #[getter] - fn get_volume(&self) -> Volume { - self.0.volume() + fn get_volume(&self) -> PyResult { + self.0 + .volume() + .map_err(PyFeosError::from) + .map_err(PyErr::from) } #[getter] @@ -1366,8 +1399,11 @@ impl PyState { } #[getter] - fn get_moles(&self) -> Moles> { - self.0.moles() + fn get_moles(&self) -> PyResult>> { + self.0 + .moles() + .map_err(PyFeosError::from) + .map_err(PyErr::from) } #[getter] @@ -1541,8 +1577,11 @@ impl PyStateVec { } #[getter] - fn get_moles(&self) -> Moles> { - StateVec::from(self).moles() + fn get_moles(&self) -> PyResult>> { + StateVec::from(self) + .moles() + .map_err(PyFeosError::from) + .map_err(PyErr::from) } #[getter] From 9c78cf271ad260925b41e4d42c1b9873ee3a4d0c Mon Sep 17 00:00:00 2001 From: Philipp Rehner <69816385+prehner@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:28:56 +0200 Subject: [PATCH 06/12] Implement ph and ps flashes for binary mixtures (#338) --- CHANGELOG.md | 5 +- crates/feos-core/src/equation_of_state/mod.rs | 115 +++-- crates/feos-core/src/lib.rs | 4 +- crates/feos-core/src/phase_equilibria/mod.rs | 42 +- .../src/phase_equilibria/px_flashes.rs | 465 ++++++++++++++++++ crates/feos-core/src/state/composition.rs | 31 +- crates/feos-core/src/state/mod.rs | 66 +-- crates/feos-core/src/state/properties.rs | 18 +- crates/feos-derive/src/ideal_gas.rs | 2 +- crates/feos-dft/src/profile/properties.rs | 2 +- crates/feos/src/ideal_gas/joback.rs | 34 +- crates/feos/src/multiparameter/mod.rs | 4 +- crates/feos/tests/pcsaft/mod.rs | 1 + crates/feos/tests/pcsaft/px_flashes.rs | 138 ++++++ crates/feos/tests/pcsaft/tp_flash.rs | 14 +- docs/recipes/index.md | 1 + .../recipes_phase_equilibrium_flash.ipynb | 149 ++++++ py-feos/src/phase_equilibria.rs | 152 ++++++ 18 files changed, 1110 insertions(+), 133 deletions(-) create mode 100644 crates/feos-core/src/phase_equilibria/px_flashes.rs create mode 100644 crates/feos/tests/pcsaft/px_flashes.rs create mode 100644 docs/recipes/recipes_phase_equilibrium_flash.ipynb diff --git a/CHANGELOG.md b/CHANGELOG.md index b82f858f3..442f80bd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,15 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rewrote `PhaseEquilibrium::pure_p` to mirror `pure_t` and enabled automatic differentiation. [#337](https://github.com/feos-org/feos/pull/337) - Added `boiling_temperature` to the list of properties for parallel evaluations of gradients. [#337](https://github.com/feos-org/feos/pull/337) - Added the `Composition` trait to allow more flexibility in the creation of states and phase equilibria. [#330](https://github.com/feos-org/feos/pull/330) +- Added `PhaseEquilibrium::ph_flash` and `PhaseEquilibrium::ps_flash`. [#338](https://github.com/feos-org/feos/pull/338) +- Added getters for `vapor_phase_fraction`, `molar_enthalpy`, `molar_entropy`, `total_moles`, `enthalpy`, and `entropy` to `PhaseEquilibrium`. [#338](https://github.com/feos-org/feos/pull/338) ### Changed - Removed any assumptions about the total number of moles in a `State` or `PhaseEquilibrium`. Evaluating extensive properties now returns a `Result`. [#330](https://github.com/feos-org/feos/pull/330) +- Redesigned the `IdealGas` trait and added `IdealGasAD` in analogy to `ResidualDyn` and `Residual`. [#330](https://github.com/feos-org/feos/pull/330) ### Removed - Removed the `StateBuilder` struct, because it is mostly obsolete with the addition of the `Composition` trait. [#330](https://github.com/feos-org/feos/pull/330) ### Packaging -- Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#323](https://github.com/feos-org/feos/pull/323) +- Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#328](https://github.com/feos-org/feos/pull/328) ## [Unreleased] diff --git a/crates/feos-core/src/equation_of_state/mod.rs b/crates/feos-core/src/equation_of_state/mod.rs index dc849068f..14e45c07a 100644 --- a/crates/feos-core/src/equation_of_state/mod.rs +++ b/crates/feos-core/src/equation_of_state/mod.rs @@ -1,7 +1,7 @@ use crate::ReferenceSystem; use crate::state::StateHD; use nalgebra::{ - Const, DVector, DefaultAllocator, Dim, Dyn, OVector, SVector, U1, allocator::Allocator, + Const, DVector, DefaultAllocator, Dim, Dyn, OVector, SVector, allocator::Allocator, }; use num_dual::DualNum; use quantity::{Dimensionless, MolarEnergy, MolarVolume, Temperature}; @@ -114,11 +114,28 @@ impl, D>, D: DualNum + Copy, const N: usize> } /// Ideal gas Helmholtz energy contribution. -pub trait IdealGas { +pub trait IdealGas { /// Implementation of an ideal gas model in terms of the /// logarithm of the cubic thermal de Broglie wavelength /// in units ln(A³) for each component in the system. - fn ln_lambda3 + Copy>(&self, temperature: D2) -> D2; + fn ln_lambda3 + Copy>(&self, temperature: D) -> D; + + /// The name of the ideal gas model. + fn ideal_gas_model(&self) -> &'static str; +} + +/// Ideal gas Helmholtz energy contribution with automatic differentiation with +/// respect to parameters. +pub trait IdealGasAD: Clone { + type Real: IdealGasAD; + type Lifted + Copy>: IdealGasAD; + fn re(&self) -> Self::Real; + fn lift + Copy>(&self) -> Self::Lifted; + + /// Implementation of an ideal gas model in terms of the + /// logarithm of the cubic thermal de Broglie wavelength + /// in units ln(A³) for each component in the system. + fn ln_lambda3(&self, temperature: D) -> D; /// The name of the ideal gas model. fn ideal_gas_model(&self) -> &'static str; @@ -129,45 +146,44 @@ pub trait Total + Copy = f64>: Residual where DefaultAllocator: Allocator, { - type IdealGas: IdealGas; + type RealTotal: Total; + type LiftedTotal + Copy>: Total; + fn re_total(&self) -> Self::RealTotal; + fn lift_total + Copy>(&self) -> Self::LiftedTotal; fn ideal_gas_model(&self) -> &'static str; - fn ideal_gas(&self) -> impl Iterator; - - fn ln_lambda3 + Copy>(&self, temperature: D2) -> OVector { - OVector::from_iterator_generic( - N::from_usize(self.components()), - U1, - self.ideal_gas().map(|i| i.ln_lambda3(temperature)), - ) - } + fn ln_lambda3(&self, temperature: D) -> OVector; - fn ideal_gas_molar_helmholtz_energy + Copy>( + fn ideal_gas_molar_helmholtz_energy( &self, - temperature: D2, - molar_volume: D2, - molefracs: &OVector, - ) -> D2 { + temperature: D, + molar_volume: D, + molefracs: &OVector, + ) -> D { let partial_density = molefracs / molar_volume; - let mut res = D2::from(0.0); - for (i, &r) in self.ideal_gas().zip(partial_density.iter()) { + let mut res = D::from(0.0); + for (&l, &r) in self + .ln_lambda3(temperature) + .iter() + .zip(partial_density.iter()) + { let ln_rho_m1 = if r.re() == 0.0 { - D2::from(0.0) + D::from(0.0) } else { r.ln() - 1.0 }; - res += r * (i.ln_lambda3(temperature) + ln_rho_m1) + res += r * (l + ln_rho_m1) } res * molar_volume * temperature } - fn ideal_gas_helmholtz_energy + Copy>( + fn ideal_gas_helmholtz_energy( &self, - temperature: Temperature, - volume: MolarVolume, - moles: &OVector, - ) -> MolarEnergy { + temperature: Temperature, + volume: MolarVolume, + moles: &OVector, + ) -> MolarEnergy { let total_moles = moles.sum(); let molefracs = moles / total_moles; let molar_volume = volume.into_reduced() / total_moles; @@ -180,32 +196,59 @@ where } impl< - I: IdealGas + Clone + 'static, + I: IdealGas + 'static, C: Deref, R>> + Clone, R: ResidualDyn + 'static, -> Total for C + D: DualNum + Copy, +> Total for C { - type IdealGas = I; + type RealTotal = Self; + type LiftedTotal + Copy> = Self; + fn re_total(&self) -> Self::RealTotal { + self.clone() + } + fn lift_total + Copy>(&self) -> Self::LiftedTotal { + self.clone() + } fn ideal_gas_model(&self) -> &'static str { self.ideal_gas[0].ideal_gas_model() } - fn ideal_gas(&self) -> impl Iterator { - self.ideal_gas.iter() + fn ln_lambda3(&self, temperature: D) -> DVector { + DVector::from_vec( + self.ideal_gas + .iter() + .map(|i| i.ln_lambda3(temperature)) + .collect(), + ) } } -impl + Clone, R: Residual, D>, D: DualNum + Copy, const N: usize> +impl, R: Residual, D>, D: DualNum + Copy, const N: usize> Total, D> for EquationOfState<[I; N], R> { - type IdealGas = I; + type RealTotal = EquationOfState<[I::Real; N], R::Real>; + type LiftedTotal + Copy> = + EquationOfState<[I::Lifted; N], R::Lifted>; + fn re_total(&self) -> Self::RealTotal { + EquationOfState::new( + self.ideal_gas.each_ref().map(|i| i.re()), + self.residual.re(), + ) + } + fn lift_total + Copy>(&self) -> Self::LiftedTotal { + EquationOfState::new( + self.ideal_gas.each_ref().map(|i| i.lift()), + self.residual.lift(), + ) + } fn ideal_gas_model(&self) -> &'static str { self.ideal_gas[0].ideal_gas_model() } - fn ideal_gas(&self) -> impl Iterator { - self.ideal_gas.iter() + fn ln_lambda3(&self, temperature: D) -> SVector { + SVector::from(self.ideal_gas.each_ref().map(|i| i.ln_lambda3(temperature))) } } diff --git a/crates/feos-core/src/lib.rs b/crates/feos-core/src/lib.rs index 48b8306f3..cc280cf0a 100644 --- a/crates/feos-core/src/lib.rs +++ b/crates/feos-core/src/lib.rs @@ -34,8 +34,8 @@ mod phase_equilibria; mod state; pub use ad::{ParametersAD, PropertiesAD}; pub use equation_of_state::{ - EntropyScaling, EquationOfState, IdealGas, Molarweight, NoResidual, Residual, ResidualDyn, - Subset, Total, + EntropyScaling, EquationOfState, IdealGas, IdealGasAD, Molarweight, NoResidual, Residual, + ResidualDyn, Subset, Total, }; pub use errors::{FeosError, FeosResult}; #[cfg(feature = "ndarray")] diff --git a/crates/feos-core/src/phase_equilibria/mod.rs b/crates/feos-core/src/phase_equilibria/mod.rs index 461a1a901..5ed15a767 100644 --- a/crates/feos-core/src/phase_equilibria/mod.rs +++ b/crates/feos-core/src/phase_equilibria/mod.rs @@ -10,7 +10,15 @@ use quantity::{Dimensionless, Energy, Entropy, MolarEnergy, MolarEntropy, Moles} use std::fmt; use std::fmt::Write; +// with empty lines to not mess up the order in the documentation +mod vle_pure; + mod bubble_dew; + +mod tp_flash; + +mod px_flashes; + #[cfg(feature = "ndarray")] mod phase_diagram_binary; #[cfg(feature = "ndarray")] @@ -18,8 +26,7 @@ mod phase_diagram_pure; #[cfg(feature = "ndarray")] mod phase_envelope; mod stability_analysis; -mod tp_flash; -mod vle_pure; + pub use bubble_dew::TemperatureOrPressure; #[cfg(feature = "ndarray")] pub use phase_diagram_binary::PhaseDiagramHetero; @@ -33,22 +40,25 @@ pub use phase_diagram_pure::PhaseDiagram; /// /// ## Contents /// +/// + [Pure component phase equilibria](#pure-component-phase-equilibria) /// + [Bubble and dew point calculations](#bubble-and-dew-point-calculations) -/// + [Heteroazeotropes](#heteroazeotropes) /// + [Flash calculations](#flash-calculations) -/// + [Pure component phase equilibria](#pure-component-phase-equilibria) +/// + [Heteroazeotropes](#heteroazeotropes) /// + [Utility functions](#utility-functions) #[derive(Debug, Clone)] pub struct PhaseEquilibrium + Copy = f64> where DefaultAllocator: Allocator, { - states: [State; P], + pub states: [State; P], pub phase_fractions: [D; P], total_moles: Option>, } -impl fmt::Display for PhaseEquilibrium { +impl, N: Dim, const P: usize> fmt::Display for PhaseEquilibrium +where + DefaultAllocator: Allocator, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (i, s) in self.states.iter().enumerate() { writeln!(f, "phase {i}: {s}")?; @@ -125,12 +135,12 @@ impl, N: Dim, D: DualNum + Copy> PhaseEquilibrium, { - pub(super) fn single_phase(state: State) -> Self { + pub fn single_phase(state: State) -> Self { let total_moles = state.total_moles; Self::with_vapor_phase_fraction(state.clone(), state, D::from(1.0), total_moles) } - pub(super) fn two_phase(vapor: State, liquid: State) -> Self { + pub fn two_phase(vapor: State, liquid: State) -> Self { let (beta, total_moles) = if let (Some(nv), Some(nl)) = (vapor.total_moles, liquid.total_moles) { (nv.convert_into(nl + nv), Some(nl + nv)) @@ -140,7 +150,7 @@ where Self::with_vapor_phase_fraction(vapor, liquid, beta, total_moles) } - pub(super) fn with_vapor_phase_fraction( + pub fn with_vapor_phase_fraction( vapor: State, liquid: State, vapor_phase_fraction: D, @@ -158,11 +168,7 @@ impl, N: Dim, D: DualNum + Copy> PhaseEquilibrium, { - pub(super) fn new( - vapor: State, - liquid1: State, - liquid2: State, - ) -> Self { + pub fn new(vapor: State, liquid1: State, liquid2: State) -> Self { Self { states: [vapor, liquid1, liquid2], phase_fractions: [D::from(1.0), D::from(0.0), D::from(0.0)], @@ -171,7 +177,7 @@ where } } -impl, N: Gradients, const P: usize, D: DualNum + Copy> +impl, N: Gradients, const P: usize, D: DualNum + Copy> PhaseEquilibrium where DefaultAllocator: Allocator, @@ -179,7 +185,13 @@ where pub fn total_moles(&self) -> FeosResult> { self.total_moles.ok_or(FeosError::IntensiveState) } +} +impl, N: Gradients, const P: usize, D: DualNum + Copy> + PhaseEquilibrium +where + DefaultAllocator: Allocator, +{ pub fn molar_enthalpy(&self) -> MolarEnergy { self.states .iter() diff --git a/crates/feos-core/src/phase_equilibria/px_flashes.rs b/crates/feos-core/src/phase_equilibria/px_flashes.rs new file mode 100644 index 000000000..aadeffb76 --- /dev/null +++ b/crates/feos-core/src/phase_equilibria/px_flashes.rs @@ -0,0 +1,465 @@ +#![expect(clippy::toplevel_ref_arg)] +use super::PhaseEquilibrium; +use crate::errors::FeosResult; +use crate::state::State; +use crate::{Composition, FeosError, ReferenceSystem, SolverOptions, Total, Verbosity}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, Dim, DimAdd, OVector, U1, U2, U3, stack, vector}; +use num_dual::linalg::LU; +use num_dual::{ + Dual, Dual64, DualNum, DualStruct, Gradients, first_derivative, implicit_derivative_sp, partial, +}; +use quantity::{Density, MolarEnergy, MolarEntropy, Pressure, Quantity, SIUnit, Temperature}; + +const MAX_ITER_PX: usize = 20; +const TOL_PX: f64 = 1e-11; + +type PXVars = >::Output; +type TPVars = >::Output; + +impl, N: Gradients + DimAdd + DimAdd, D: DualNum + Copy> + PhaseEquilibrium +where + DefaultAllocator: Allocator + + Allocator + + Allocator> + + Allocator> + + Allocator, PXVars> + + Allocator> + + Allocator> + + Allocator, TPVars>, + PXVars: Gradients, + TPVars: Gradients, +{ + /// Perform a ph-flash calculation. An initial temperature is required + /// and the system needs to be in the two-phase region at that initial + /// temperature. + /// + /// based on Michelsen's work [State function based flash specifications](https://doi.org/10.1016/S0378-3812(99)00092-8) + pub fn ph_flash>( + eos: &E, + pressure: Pressure, + molar_enthalpy: MolarEnergy, + feed: X, + initial_temperature: Temperature, + options: SolverOptions, + ) -> FeosResult { + PhaseEquilibrium::px_flash( + eos, + pressure, + molar_enthalpy, + feed, + initial_temperature, + options, + ) + } + + /// Perform a ps-flash calculation. An initial temperature is required + /// and the system needs to be in the two-phase region at that initial + /// temperature. + /// + /// based on Michelsen's work [State function based flash specifications](https://doi.org/10.1016/S0378-3812(99)00092-8) + pub fn ps_flash>( + eos: &E, + pressure: Pressure, + molar_entropy: MolarEntropy, + feed: X, + initial_temperature: Temperature, + options: SolverOptions, + ) -> FeosResult { + PhaseEquilibrium::px_flash( + eos, + pressure, + molar_entropy, + feed, + initial_temperature, + options, + ) + } + + // Generic implementation of ph and ps flashes. + fn px_flash, U: PXFlash>( + eos: &E, + pressure: Pressure, + specification: Quantity, + feed: X, + initial_temperature: Temperature, + options: SolverOptions, + ) -> FeosResult + where + Quantity: ReferenceSystem, + Quantity: ReferenceSystem, + { + let (max_iter, tol, verbosity) = options.unwrap_or(MAX_ITER_PX, TOL_PX); + let (molefracs, total_moles) = feed.into_molefracs(eos)?; + + // initialize with a tp flash + let eos_f64 = eos.re_total(); + let vle = PhaseEquilibrium::tp_flash( + &eos_f64, + initial_temperature, + pressure.re(), + molefracs.map(|x| x.re()), + None, + Default::default(), + None, + )?; + + // extract specifications + let p = pressure.into_reduced().re(); + let hs = specification.into_reduced().re(); + let z = molefracs.map(|x| x.re()); + let specs = (p, hs, z.clone()); + + // extract variables + let t = initial_temperature.into_reduced(); + let beta = vle.vapor_phase_fraction(); + let rho_v = vle.vapor().density.into_reduced(); + let rho_l = vle.liquid().partial_density().into_reduced(); + let mut vars = stack![rho_l; vector![t, beta, rho_v]]; + let mut old_res = None; + + log_iter!( + verbosity, + " iter | method | temperature | residual | phase I mole fractions | phase II mole fractions " + ); + log_iter!(verbosity, "{:-<102}", ""); + log_iter!( + verbosity, + " {:4} | | {:9.5} | | {:10.8?} | {:10.8?}", + 0, + Temperature::from_reduced(t), + (&rho_l / rho_l.sum() + (&z - &rho_l / rho_l.sum()) / beta).as_slice(), + (&rho_l / rho_l.sum()).as_slice(), + ); + + // iterate + for k in 0..max_iter { + // always try a Newton step first + let (grad, new_vars) = U::newton_step(&eos_f64, &vars, &specs)?; + let new_res = grad.norm(); + let (method, res) = if let Some(r) = old_res + && r < new_res + { + // if the residual is not reduced, reject the step and do a tp-flash instead + vars = U::tp_step(&eos_f64, &vars, &specs)?; + ("Tp-flash", None) + } else { + vars = new_vars; + ("Newton", Some(new_res)) + }; + + if let Verbosity::Iter = verbosity { + let (t, _, _, _, x, y) = unpack_variables(&z, &vars); + log_iter!( + verbosity, + " {:4} | {:^8} | {:9.5} | {} | {:10.8?} | {:10.8?}", + k + 1, + method, + Temperature::from_reduced(t), + res.map_or(String::from(" "), |r| format!("{r:14.8e}")), + y.as_slice(), + x.as_slice(), + ); + } + + if let Some(res) = res + && res < tol + { + log_result!( + verbosity, + "px flash: calculation converged in {} step(s)\n", + k + 1 + ); + + // implicit differentiation + let specs = ( + pressure.into_reduced(), + specification.into_reduced(), + molefracs.clone(), + ); + let vars = implicit_derivative_sp( + |variables, specifications| { + U::state_function(&eos.lift_total(), variables, specifications) + }, + vars, + &specs, + ); + let (t, beta, rho_l, rho_v, x, y) = unpack_variables(&molefracs, &vars); + + // store results in PhaseEquilibrium + let liquid = State::new( + eos, + Temperature::from_reduced(t), + Density::from_reduced(rho_l), + x, + )?; + let vapor = State::new( + eos, + Temperature::from_reduced(t), + Density::from_reduced(rho_v), + y, + )?; + return Ok(PhaseEquilibrium::with_vapor_phase_fraction( + vapor, + liquid, + beta, + total_moles, + )); + } + old_res = res; + } + Err(FeosError::NotConverged("px flash".to_owned())) + } +} + +fn unpack_variables + Copy, N: Dim + DimAdd>( + molefracs: &OVector, + variables: &OVector>, +) -> (D, D, D, D, OVector, OVector) +where + DefaultAllocator: Allocator + Allocator>, +{ + let n = molefracs.len(); + let rho_i_l = variables.rows_generic(0, N::from_usize(n)).clone_owned(); + let [[t, beta, rho_v]] = variables.rows_generic(n, U3).clone_owned().data.0; + let rho_l = rho_i_l.sum(); + let x = rho_i_l / rho_l; + let y = &x + (molefracs - &x) / beta; + (t, beta, rho_l, rho_v, x, y) +} + +fn unpack_tp_variables + Copy, N: Dim + DimAdd>( + molefracs: &OVector, + variables: &OVector>, +) -> (D, D, D, OVector, OVector) +where + DefaultAllocator: Allocator + Allocator>, +{ + let n = molefracs.len(); + let rho_i_l = variables.rows_generic(0, N::from_usize(n)).clone_owned(); + let [[beta, rho_v]] = variables.rows_generic(n, U2).clone_owned().data.0; + let rho_l = rho_i_l.sum(); + let x = rho_i_l / rho_l; + let y = &x + (molefracs - &x) / beta; + (beta, rho_l, rho_v, x, y) +} + +trait PXFlash: Sized + Copy { + // potential function for which the flash solution is a saddle point. + fn state_function, N: Dim + DimAdd, D: DualNum + Copy>( + eos: &E, + variables: OVector>, + args: &(D, D, OVector), + ) -> D + where + DefaultAllocator: Allocator + Allocator>; + + fn evaluate_property, N: Gradients, D: DualNum + Copy>( + vle: &PhaseEquilibrium, + ) -> Quantity + where + DefaultAllocator: Allocator; + + // the potential function for a tp-flash specification (Q = A + V*p_spec) + fn tp_state_function, N: Dim + DimAdd, D: DualNum + Copy>( + eos: &E, + variables: OVector>, + &(t, p, ref z): &(D, D, OVector), + ) -> D + where + DefaultAllocator: Allocator + Allocator>, + { + let (beta, rho_l, rho_v, x, y) = unpack_tp_variables(z, &variables); + let potential = |molefracs, rho: D, t| { + let v = rho.recip(); + let a_res = eos.residual_helmholtz_energy(t, v, &molefracs); + let a_ig = eos.ideal_gas_molar_helmholtz_energy(t, v, &molefracs); + a_res + a_ig + v * p + }; + potential(y, rho_v, t) * beta + potential(x, rho_l, t) * (-beta + 1.0) + } + + // An undamped Newton step for the gradients of the potential function. + // Because the ps and ph flashes are saddle points rather then extrema, + // the value of the potential can not be used as convergence criterion. + #[expect(clippy::type_complexity)] + fn newton_step, N: Dim + DimAdd, D: DualNum + Copy>( + eos: &E, + variables: &OVector>, + specifications: &(D, D, OVector), + ) -> FeosResult<(OVector>, OVector>)> + where + DefaultAllocator: Allocator + Allocator> + Allocator, PXVars>, + PXVars: Gradients, + { + let (_, grad, hess) = PXVars::::hessian( + |variables, specifications| { + Self::state_function(&eos.lift_total(), variables, specifications) + }, + variables, + specifications, + ); + let dx = LU::new(hess)?.solve(&grad); + Ok((grad, variables - &dx)) + } + + // A much slower but more robust step that calculates the implicit + // derivative of the temperature only (which is well behaved + // according to Michelsen) and then calculates all other variables + // from a tp-flash. + fn tp_step, N: Gradients + DimAdd + DimAdd>( + eos: &E, + variables: &OVector>, + &(p, hs_spec, ref z): &(f64, f64, OVector), + ) -> FeosResult>> + where + Quantity: ReferenceSystem, + DefaultAllocator: Allocator + + Allocator + + Allocator> + + Allocator> + + Allocator> + + Allocator, TPVars>, + TPVars: Gradients, + { + let (mut t, beta, rho_l, rho_v, x, y) = unpack_variables(z, variables); + let rho_i_l = rho_l * x; + let (hs, dhs) = first_derivative( + partial( + |t: Dual<_, _>, args: &(_, OVector<_, _>)| { + let &(p, ref z) = args; + let args = (t, p, z.clone_owned()); + + // implicit differentiation of the tp stationarity condition + // to obtain the derivative of the other variables w.r.t. t + let tp_vars = implicit_derivative_sp( + |variables, args| { + Self::tp_state_function(&eos.lift_total().lift_total(), variables, args) + }, + stack![rho_i_l; vector![beta, rho_v]], + &args, + ); + let (beta, rho_l, rho_v, x, y) = unpack_tp_variables(z, &tp_vars); + + // Evaluation of the enthalpy/entropy including the derivatives. + let liquid = State::new( + &eos.lift_total(), + Temperature::from_reduced(t), + Density::from_reduced(rho_l), + x, + )?; + let vapor = State::new( + &eos.lift_total(), + Temperature::from_reduced(t), + Density::from_reduced(rho_v), + y, + )?; + Ok::<_, FeosError>( + Self::evaluate_property(&PhaseEquilibrium::with_vapor_phase_fraction( + vapor, liquid, beta, None, + )) + .into_reduced(), + ) + }, + &(p, z.clone_owned()), + ), + t, + )?; + + // Newton step for the temperature + t -= (hs - hs_spec) / dhs; + + // pack variables into PhaseEquilibrium for initial values + let liquid = State::new_density( + eos, + Temperature::from_reduced(t), + Density::from_reduced(rho_i_l), + )?; + let vapor = State::new( + eos, + Temperature::from_reduced(t), + Density::from_reduced(rho_v), + y, + )?; + let vle = PhaseEquilibrium::with_vapor_phase_fraction(vapor, liquid, beta, None); + + // tp-flash for all other variables + let vle = PhaseEquilibrium::tp_flash( + eos, + Temperature::from_reduced(t), + Pressure::from_reduced(p), + z, + Some(&vle), + Default::default(), + None, + )?; + let beta = vle.vapor_phase_fraction(); + let rho_v = vle.vapor().density.into_reduced(); + let rho_l = vle.liquid().partial_density().into_reduced(); + Ok(stack![rho_l; vector![t, beta, rho_v]]) + } +} + +impl PXFlash for SIUnit<-2, 2, 1, 0, 0, -1, 0> { + // the potential function for a ph-flash specification (Q = (A + V*p_spec - H_spec) / T) + fn state_function, N: Dim + DimAdd, D: DualNum + Copy>( + eos: &E, + variables: OVector>, + &(p, h, ref z): &(D, D, OVector), + ) -> D + where + DefaultAllocator: Allocator + Allocator>, + { + let (t, beta, rho_l, rho_v, x, y) = unpack_variables(z, &variables); + let potential = |molefracs, rho: D, t| { + let v = rho.recip(); + let a_res = eos.residual_helmholtz_energy(t, v, &molefracs); + let a_ig = eos.ideal_gas_molar_helmholtz_energy(t, v, &molefracs); + (a_res + a_ig + v * p - h) / t + }; + potential(y, rho_v, t) * beta + potential(x, rho_l, t) * (-beta + 1.0) + } + + fn evaluate_property, N: Gradients, D: DualNum + Copy>( + vle: &PhaseEquilibrium, + ) -> Quantity + where + DefaultAllocator: Allocator, + { + vle.molar_enthalpy() + } +} + +impl PXFlash for SIUnit<-2, 2, 1, 0, -1, -1, 0> { + // the potential function for a ps-flash specification (Q = A + T*S_spec + V*p_spec) + fn state_function, N: Dim + DimAdd, D: DualNum + Copy>( + eos: &E, + variables: OVector>, + &(p, s, ref z): &(D, D, OVector), + ) -> D + where + DefaultAllocator: Allocator + Allocator>, + { + let (t, beta, rho_l, rho_v, x, y) = unpack_variables(z, &variables); + let potential = |molefracs, rho: D, t| { + let v = rho.recip(); + let a_res = eos.residual_helmholtz_energy(t, v, &molefracs); + let a_ig = eos.ideal_gas_molar_helmholtz_energy(t, v, &molefracs); + // Division by t.re() is done to ensure that the state function has the same + // units (and in conclusion same order of magnitude) as the ph state function. + // This allows using the same toelrances for both methods. + (a_res + a_ig + t * s + v * p) / t.re() + }; + potential(y, rho_v, t) * beta + potential(x, rho_l, t) * (-beta + 1.0) + } + + fn evaluate_property, N: Gradients, D: DualNum + Copy>( + vle: &PhaseEquilibrium, + ) -> Quantity + where + DefaultAllocator: Allocator, + { + vle.molar_entropy() + } +} diff --git a/crates/feos-core/src/state/composition.rs b/crates/feos-core/src/state/composition.rs index f6a190e33..973af8ffb 100644 --- a/crates/feos-core/src/state/composition.rs +++ b/crates/feos-core/src/state/composition.rs @@ -1,4 +1,3 @@ -use super::State; use crate::equation_of_state::Residual; use crate::{FeosError, FeosResult}; use nalgebra::allocator::Allocator; @@ -6,10 +5,27 @@ use nalgebra::{DefaultAllocator, Dim, Dyn, OVector, U1, U2, dvector, vector}; use num_dual::{DualNum, DualStruct}; use quantity::Moles; +/// Trait to generalize over different input types for the composition of +/// a state. +/// +/// The trait is implemented for the following data types: +/// +/// |components|input|total_moles?|comment| +/// |:-:|-|-|-| +/// |1|`()`|-|| +/// |1|`Moles`|✅| +/// |2|`f64`|-| +/// |N|`OVector`|-| +/// |N|`&OVector`|-| +/// |N|`OVector`|-|`Dyn` only| +/// |N|`&OVector`|-|`Dyn` only| +/// |N|`Moles>`|✅| +/// |N|`&Moles>`|✅| pub trait Composition + Copy, N: Dim> where DefaultAllocator: Allocator, { + /// Convert the composition into molefracs and total moles if possible. #[expect(clippy::type_complexity)] fn into_molefracs>( self, @@ -42,19 +58,6 @@ where } } -// copy the composition from a given state -impl + Copy, N: Dim> Composition for &State -where - DefaultAllocator: Allocator, -{ - fn into_molefracs>( - self, - _: &E1, - ) -> FeosResult<(OVector, Option>)> { - Ok(((self.molefracs.clone()), self.total_moles)) - } -} - // a pure component needs no specification impl + Copy> Composition for () { fn into_molefracs>( diff --git a/crates/feos-core/src/state/mod.rs b/crates/feos-core/src/state/mod.rs index 716468aa8..daa7a7ff4 100644 --- a/crates/feos-core/src/state/mod.rs +++ b/crates/feos-core/src/state/mod.rs @@ -306,17 +306,9 @@ where /// Return a new `State` for the combination of inputs. /// - /// The function attempts to create a new state using the given input values. If the state - /// is overdetermined, it will choose a method based on the following hierarchy. - /// 1. Create a state non-iteratively from the set of $T$, $V$, $\rho$, $\rho_i$, $N$, $N_i$ and $x_i$. - /// 2. Use a density iteration for a given pressure. - /// - /// The [StateBuilder] provides a convenient way of calling this function without the need to provide - /// all the optional input values. - /// /// # Errors /// - /// When the state cannot be created using the combination of inputs. + /// When the state cannot be created using the combination of inputs (is over- or underdetermined). pub fn build>( eos: &E, temperature: Temperature, @@ -417,18 +409,9 @@ where { /// Return a new `State` for the combination of inputs. /// - /// The function attempts to create a new state using the given input values. If the state - /// is overdetermined, it will choose a method based on the following hierarchy. - /// 1. Create a state non-iteratively from the set of $T$, $V$, $\rho$, $\rho_i$, $N$, $N_i$ and $x_i$. - /// 2. Use a density iteration for a given pressure. - /// 3. Determine the state using a Newton iteration from (in this order): $(p, h)$, $(p, s)$, $(T, h)$, $(T, s)$, $(V, u)$ - /// - /// The [StateBuilder] provides a convenient way of calling this function without the need to provide - /// all the optional input values. - /// /// # Errors /// - /// When the state cannot be created using the combination of inputs. + /// When the state cannot be created using the combination of inputs (is over- or underdetermined). #[expect(clippy::too_many_arguments)] pub fn build_full + Clone>( eos: &E, @@ -461,28 +444,33 @@ where match state { Some(state) => Ok(state), None => { - // Check if new state can be created using molar_enthalpy and temperature - if let (Some(p), Some(h)) = (pressure, molar_enthalpy) { - return State::new_nph(eos, p, h, composition, density_initialization, ti); - } - if let (Some(p), Some(s)) = (pressure, molar_entropy) { - return State::new_nps(eos, p, s, composition, density_initialization, ti); - } - if let (Some(t), Some(h)) = (temperature, molar_enthalpy) { - return State::new_nth(eos, t, h, composition, density_initialization); - } - if let (Some(t), Some(s)) = (temperature, molar_entropy) { - return State::new_nts(eos, t, s, composition, density_initialization); - } - if let (Some(u), Some(v)) = (molar_internal_energy, volume) { - let (molefracs, total_moles) = composition.into_molefracs(eos)?; - if let Some(n) = total_moles { - return State::new_nvu(eos, v, u, (molefracs, n), ti); + match ( + temperature, + pressure, + volume, + molar_enthalpy, + molar_entropy, + molar_internal_energy, + ) { + (Some(t), None, None, Some(h), None, None) => { + State::new_nth(eos, t, h, composition, density_initialization) + } + (Some(t), None, None, None, Some(s), None) => { + State::new_nts(eos, t, s, composition, density_initialization) + } + (None, Some(p), None, Some(h), None, None) => { + State::new_nph(eos, p, h, composition, density_initialization, ti) + } + (None, Some(p), None, None, Some(s), None) => { + State::new_nps(eos, p, s, composition, density_initialization, ti) + } + (None, None, Some(v), None, None, Some(u)) => { + State::new_nvu(eos, v, u, composition, ti) } + _ => Err(FeosError::UndeterminedState(String::from( + "Missing input parameters.", + ))), } - Err(FeosError::UndeterminedState(String::from( - "Missing input parameters.", - ))) } } } diff --git a/crates/feos-core/src/state/properties.rs b/crates/feos-core/src/state/properties.rs index fa0f53b4a..132bad936 100644 --- a/crates/feos-core/src/state/properties.rs +++ b/crates/feos-core/src/state/properties.rs @@ -20,7 +20,9 @@ where let ideal_gas = || { quantity::ad::gradient_copy( partial2( - |n: Dimensionless<_>, &t, &v| self.eos.ideal_gas_helmholtz_energy(t, v, &n), + |n: Dimensionless<_>, &t, &v| { + self.eos.lift_total().ideal_gas_helmholtz_energy(t, v, &n) + }, &self.temperature, &self.molar_volume, ), @@ -38,7 +40,7 @@ where quantity::ad::partial_hessian_copy( partial( |(n, t): (Dimensionless<_>, _), &v| { - self.eos.ideal_gas_helmholtz_energy(t, v, &n) + self.eos.lift_total().ideal_gas_helmholtz_energy(t, v, &n) }, &self.molar_volume, ), @@ -89,7 +91,7 @@ where let ideal_gas = || { -quantity::ad::first_derivative( partial2( - |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), + |t, &v, n| self.eos.lift_total().ideal_gas_helmholtz_energy(t, v, n), &self.molar_volume, &self.molefracs, ), @@ -115,7 +117,7 @@ where let ideal_gas = || { -quantity::ad::second_derivative( partial2( - |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), + |t, &v, n| self.eos.lift_total().ideal_gas_helmholtz_energy(t, v, n), &self.molar_volume, &self.molefracs, ), @@ -135,7 +137,7 @@ where let ideal_gas = || { -quantity::ad::third_derivative( partial2( - |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), + |t, &v, n| self.eos.lift_total().ideal_gas_helmholtz_energy(t, v, n), &self.molar_volume, &self.molefracs, ), @@ -176,7 +178,7 @@ where let ideal_gas = || { quantity::ad::zeroth_derivative( partial2( - |t, &v, n| self.eos.ideal_gas_helmholtz_energy(t, v, n), + |t, &v, n| self.eos.lift_total().ideal_gas_helmholtz_energy(t, v, n), &self.molar_volume, &self.molefracs, ), @@ -253,7 +255,9 @@ where if let Contributions::IdealGas | Contributions::Total = contributions { res.push(( self.eos.ideal_gas_model(), - self.eos.ideal_gas_molar_helmholtz_energy(t, v, &x), + self.eos + .lift_total() + .ideal_gas_molar_helmholtz_energy(t, v, &x), )); } if let Contributions::Residual | Contributions::Total = contributions { diff --git a/crates/feos-derive/src/ideal_gas.rs b/crates/feos-derive/src/ideal_gas.rs index 06196e82c..f9b5eb671 100644 --- a/crates/feos-derive/src/ideal_gas.rs +++ b/crates/feos-derive/src/ideal_gas.rs @@ -42,7 +42,7 @@ fn impl_ideal_gas( }); quote! { impl IdealGas for IdealGasModel { - fn ln_lambda3 + Copy>(&self, temperature: D) -> D { + fn ln_lambda3 + Copy>(&self, temperature: D) -> D { match self { #(#ln_lambda3,)* } diff --git a/crates/feos-dft/src/profile/properties.rs b/crates/feos-dft/src/profile/properties.rs index 6095fb172..d542b27da 100644 --- a/crates/feos-dft/src/profile/properties.rs +++ b/crates/feos-dft/src/profile/properties.rs @@ -184,7 +184,7 @@ where temperature: Dual64, density: &Array, ) -> Array { - let lambda = self.bulk.eos.ln_lambda3(temperature); + let lambda = self.bulk.eos.lift_total().ln_lambda3(temperature); let mut phi = Array::zeros(density.raw_dim().remove_axis(Axis(0))); for (i, rhoi) in density.outer_iter().enumerate() { phi += &rhoi.mapv(|rhoi| (lambda[i] + rhoi.ln() - 1.0) * rhoi); diff --git a/crates/feos/src/ideal_gas/joback.rs b/crates/feos/src/ideal_gas/joback.rs index 8f2279d5b..e30c630b2 100644 --- a/crates/feos/src/ideal_gas/joback.rs +++ b/crates/feos/src/ideal_gas/joback.rs @@ -1,12 +1,13 @@ //! Implementation of the ideal gas heat capacity (de Broglie wavelength) //! of [Joback and Reid, 1987](https://doi.org/10.1080/00986448708960487). use feos_core::parameter::{FromSegments, Parameters}; -use feos_core::{FeosResult, IdealGas, ReferenceSystem}; +use feos_core::{FeosResult, IdealGas, IdealGasAD, ReferenceSystem}; use nalgebra::DVector; use num_dual::*; use quantity::{MolarEntropy, Temperature}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::ops::Mul; /// Coefficients used in the Joback model. /// @@ -96,9 +97,9 @@ impl Joback { } } -impl + Copy> IdealGas for Joback { - fn ln_lambda3 + Copy>(&self, temperature: D2) -> D2 { - let [a, b, c, d, e] = self.0.each_ref().map(D2::from_inner); +impl + Copy> Joback { + fn ln_lambda3 + Copy + Mul>(&self, temperature: D2) -> D2 { + let [a, b, c, d, e] = self.0; let t = temperature; let t2 = t * t; let t4 = t2 * t2; @@ -115,6 +116,31 @@ impl + Copy> IdealGas for Joback { + (t / T0).ln() * a; (h - t * s) / (t * RGAS) + f } +} + +impl IdealGas for Joback { + fn ln_lambda3 + Copy>(&self, temperature: D) -> D { + self.ln_lambda3(temperature) + } + + fn ideal_gas_model(&self) -> &'static str { + "Ideal gas (Joback)" + } +} + +impl + Copy> IdealGasAD for Joback { + type Real = Joback; + type Lifted + Copy> = Joback; + fn re(&self) -> Self::Real { + Joback(self.0.each_ref().map(D::re)) + } + fn lift + Copy>(&self) -> Self::Lifted { + Joback(self.0.each_ref().map(D2::from_inner)) + } + + fn ln_lambda3(&self, temperature: D) -> D { + self.ln_lambda3(temperature) + } fn ideal_gas_model(&self) -> &'static str { "Ideal gas (Joback)" diff --git a/crates/feos/src/multiparameter/mod.rs b/crates/feos/src/multiparameter/mod.rs index b4a6c4f9f..fe30093ac 100644 --- a/crates/feos/src/multiparameter/mod.rs +++ b/crates/feos/src/multiparameter/mod.rs @@ -118,10 +118,10 @@ impl Subset for MultiParameter { } impl IdealGas for MultiParameterIdealGas { - fn ln_lambda3 + Copy>(&self, temperature: D2) -> D2 { + fn ln_lambda3 + Copy>(&self, temperature: D) -> D { let tau = temperature.recip() * self.tc; // bit of a hack to convert from phi^0 into ln Lambda^3 - let delta = D2::from(E / (6.02214076e-7 * self.rhoc)); + let delta = D::from(E / (6.02214076e-7 * self.rhoc)); self.terms.iter().map(|r| r.evaluate(delta, tau)).sum() } diff --git a/crates/feos/tests/pcsaft/mod.rs b/crates/feos/tests/pcsaft/mod.rs index 6193aca74..732e6c0c5 100644 --- a/crates/feos/tests/pcsaft/mod.rs +++ b/crates/feos/tests/pcsaft/mod.rs @@ -1,6 +1,7 @@ mod critical_point; mod dft; mod properties; +mod px_flashes; mod stability_analysis; mod state_creation_mixture; mod state_creation_pure; diff --git a/crates/feos/tests/pcsaft/px_flashes.rs b/crates/feos/tests/pcsaft/px_flashes.rs new file mode 100644 index 000000000..4cfb179b6 --- /dev/null +++ b/crates/feos/tests/pcsaft/px_flashes.rs @@ -0,0 +1,138 @@ +use approx::assert_relative_eq; +use feos::ideal_gas::Joback; +use feos::pcsaft::PcSaftBinary; +use feos_core::{ + Contributions, EquationOfState, FeosResult, IdealGasAD, ParametersAD, PhaseEquilibrium, + ReferenceSystem, SolverOptions, Verbosity, +}; +use nalgebra::U1; +use num_dual::{DualStruct, DualVec}; +use quantity::*; + +#[test] +fn test_ph_flash() -> FeosResult<()> { + let params = [ + [1.5, 3.4, 180.0, 2.2, 0.03, 2500., 2.0, 1.0], + [4.5, 3.6, 250.0, 1.2, 0.015, 1500., 1.0, 2.0], + ]; + let kij = 0.15; + let pcsaft = PcSaftBinary::new(params, kij); + let joback = [ + Joback([380., 0.0, 0.0, 0.0, 0.0]), + Joback([210., 0.0, 0.0, 0.0, 0.0]), + ]; + let eos = EquationOfState::new(joback.clone(), pcsaft); + let p = 50.0 * BAR; + let t0 = Some(500.0 * KELVIN); + let x = 0.3; + let dew = PhaseEquilibrium::dew_point(&eos, p, x, t0, None, Default::default())?; + let bubble = PhaseEquilibrium::bubble_point(&eos, p, x, t0, None, Default::default())?; + let h = 0.2 * dew.molar_enthalpy() + 0.8 * bubble.molar_enthalpy(); + let t0 = 0.8 * dew.vapor().temperature + 0.2 * bubble.vapor().temperature; + let options = SolverOptions { + verbosity: Verbosity::Iter, + ..Default::default() + }; + let vle = PhaseEquilibrium::ph_flash(&eos, p, h, x, t0, options)?; + println!("{vle}"); + println!("{h}\n{}", vle.molar_enthalpy()); + assert_relative_eq!(h, vle.molar_enthalpy(), max_relative = 1e-10); + + let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let joback_ad = joback.each_ref().map(|j| j.lift()); + let eos_ad = EquationOfState::new(joback_ad, pcsaft_ad); + let vle_ad = PhaseEquilibrium::ph_flash( + &eos_ad, + Pressure::from_inner(&p), + MolarEnergy::from_inner(&h), + DualVec::from_inner(&x), + t0, + Default::default(), + )?; + let [[dt]] = vle_ad + .vapor() + .temperature + .into_reduced() + .eps + .unwrap_generic(U1, U1) + .data + .0; + println!("{dt}"); + + let dkij = 1e-7; + let pcsaft_h = PcSaftBinary::new(params, kij + dkij); + let eos_h = EquationOfState::new(joback.clone(), pcsaft_h); + let vle_h = PhaseEquilibrium::ph_flash(&eos_h, p, h, x, t0, Default::default())?; + let dt_h = (vle_h.vapor().temperature - vle.vapor().temperature).into_reduced() / dkij; + println!("{dt_h}"); + assert_relative_eq!(dt, dt_h, max_relative = 1e-4); + + Ok(()) +} + +#[test] +fn test_ps_flash() -> FeosResult<()> { + let params = [ + [1.5, 3.4, 180.0, 2.2, 0.03, 2500., 2.0, 1.0], + [4.5, 3.6, 250.0, 1.2, 0.015, 1500., 1.0, 2.0], + ]; + let kij = 0.15; + let pcsaft = PcSaftBinary::new(params, kij); + let joback = [ + Joback([380., 0.0, 0.0, 0.0, 0.0]), + Joback([210., 0.0, 0.0, 0.0, 0.0]), + ]; + let eos = EquationOfState::new(joback.clone(), pcsaft); + let p = 50.0 * BAR; + println!( + "{}", + PhaseEquilibrium::bubble_point(&eos, 500.0 * KELVIN, 0.5, None, None, Default::default())? + .vapor() + .pressure(Contributions::Total) + ); + let t0 = Some(500.0 * KELVIN); + let x = 0.3; + let dew = PhaseEquilibrium::dew_point(&eos, p, x, t0, None, Default::default())?; + let bubble = PhaseEquilibrium::bubble_point(&eos, p, x, t0, None, Default::default())?; + let s = 0.2 * dew.molar_entropy() + 0.8 * bubble.molar_entropy(); + let t0 = 0.8 * dew.vapor().temperature + 0.2 * bubble.vapor().temperature; + let options = SolverOptions { + verbosity: Verbosity::Iter, + ..Default::default() + }; + let vle = PhaseEquilibrium::ps_flash(&eos, p, s, x, t0, options)?; + println!("{vle}"); + println!("{s}\n{}", vle.molar_entropy()); + assert_relative_eq!(s, vle.molar_entropy(), max_relative = 1e-10); + + let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let joback_ad = joback.each_ref().map(|j| j.lift()); + let eos_ad = EquationOfState::new(joback_ad, pcsaft_ad); + let vle_ad = PhaseEquilibrium::ps_flash( + &eos_ad, + Pressure::from_inner(&p), + MolarEntropy::from_inner(&s), + DualVec::from_inner(&x), + t0, + Default::default(), + )?; + let [[dt]] = vle_ad + .vapor() + .temperature + .into_reduced() + .eps + .unwrap_generic(U1, U1) + .data + .0; + println!("{dt}"); + + let dkij = 1e-7; + let pcsaft_h = PcSaftBinary::new(params, kij + dkij); + let eos_h = EquationOfState::new(joback.clone(), pcsaft_h); + let vle_h = PhaseEquilibrium::ps_flash(&eos_h, p, s, x, t0, Default::default())?; + let dt_h = (vle_h.vapor().temperature - vle.vapor().temperature).into_reduced() / dkij; + println!("{dt_h}"); + assert_relative_eq!(dt, dt_h, max_relative = 1e-4); + + Ok(()) +} diff --git a/crates/feos/tests/pcsaft/tp_flash.rs b/crates/feos/tests/pcsaft/tp_flash.rs index b31a14998..0f28e0c2e 100644 --- a/crates/feos/tests/pcsaft/tp_flash.rs +++ b/crates/feos/tests/pcsaft/tp_flash.rs @@ -32,15 +32,7 @@ fn test_tp_flash() -> FeosResult<()> { println!("{p_propane} {p_butane} {x1} {y1} {z1}"); let mix = PcSaft::new(read_params(vec!["propane", "butane"])?); let options = SolverOptions::new().max_iter(100).tol(1e-12); - let vle = PhaseEquilibrium::tp_flash( - &&mix, - t, - p, - &(dvector![z1, 1.0 - z1] * MOL), - None, - options, - None, - )?; + let vle = PhaseEquilibrium::tp_flash(&&mix, t, p, z1, None, options, None)?; println!( "x1: {}, y1: {}", vle.liquid().molefracs[0], @@ -85,7 +77,7 @@ fn test_tp_flash_zero_component() -> FeosResult<()> { &&eos_full, 300.0 * KELVIN, 1.2 * BAR, - &(dvector![0.0, 0.5, 0.5] * MOL), + dvector![0.0, 0.5, 0.5], None, options, None, @@ -94,7 +86,7 @@ fn test_tp_flash_zero_component() -> FeosResult<()> { &&eos_binary, 300.0 * KELVIN, 1.2 * BAR, - &(dvector![0.5, 0.5] * MOL), + dvector![0.5, 0.5], None, options, None, diff --git a/docs/recipes/index.md b/docs/recipes/index.md index 5a7a95086..65b3a340a 100644 --- a/docs/recipes/index.md +++ b/docs/recipes/index.md @@ -12,6 +12,7 @@ If you are looking for tutorials with explanations, see the [tutorials](/tutoria recipes_critical_point_pure recipes_p_sat_t_boil recipes_phase_equilibrium_pure + recipes_phase_equilibrium_flash recipes_phase_diagram_pure recipes_automatic_differentiation ``` diff --git a/docs/recipes/recipes_phase_equilibrium_flash.ipynb b/docs/recipes/recipes_phase_equilibrium_flash.ipynb new file mode 100644 index 000000000..60158b47f --- /dev/null +++ b/docs/recipes/recipes_phase_equilibrium_flash.ipynb @@ -0,0 +1,149 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "8bec74cc", + "metadata": {}, + "outputs": [], + "source": [ + "import si_units as si\n", + "import feos\n", + "\n", + "parameters = feos.Parameters.from_json(\n", + " substances=['methanol', '1-propanol'], \n", + " pure_path='../../parameters/pcsaft/gross2002.json'\n", + ")\n", + "ideal_gas_parameters = feos.Parameters.from_json(\n", + " substances=['methanol', '1-propanol'], \n", + " pure_path='../../parameters/ideal_gas/poling2000.json'\n", + ")\n", + "eos = feos.EquationOfState.pcsaft(parameters).dippr(ideal_gas_parameters)" + ] + }, + { + "cell_type": "markdown", + "id": "0ace1cfd", + "metadata": {}, + "source": [ + "## Tp-flash" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e11fa945", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " dew point pressure: 0.6356 bar\n", + "bubble point pressure: 0.9245 bar\n" + ] + }, + { + "data": { + "text/markdown": [ + "||temperature|density|molefracs|\n", + "|-|-|-|-|\n", + "|phase 1|350.00000 K|28.75751 mol/m³|[0.59847, 0.40153]|\n", + "|phase 2|350.00000 K|14.29164 kmol/m³|[0.28954, 0.71046]|\n" + ], + "text/plain": [ + "phase 0: T = 350.00000 K, ρ = 28.75751 mol/m³, x = [0.59847, 0.40153]\n", + "phase 1: T = 350.00000 K, ρ = 14.29164 kmol/m³, x = [0.28954, 0.71046]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x1 = 0.4\n", + "temperature = 350*si.KELVIN\n", + "\n", + "p_bubble = feos.PhaseEquilibrium.bubble_point(eos, temperature, x1).liquid.pressure()\n", + "p_dew = feos.PhaseEquilibrium.dew_point(eos, temperature, x1).vapor.pressure()\n", + "print(f\"bubble point pressure: {p_bubble/si.BAR:.4} bar\")\n", + "print(f\" dew point pressure: {p_dew/si.BAR:.4} bar\")\n", + "\n", + "feos.PhaseEquilibrium.tp_flash(eos, temperature, 0.8*si.BAR, x1)" + ] + }, + { + "cell_type": "markdown", + "id": "73c81e37", + "metadata": {}, + "source": [ + "## ph-flash" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3acf88f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bubble point\ttemperature: 352.06 K\tenthalpy: -36.5900 kJ/mol\n", + " dew point\ttemperature: 361.09 K\tenthalpy: 3.9800 kJ/mol\n" + ] + }, + { + "data": { + "text/markdown": [ + "||temperature|density|molefracs|\n", + "|-|-|-|-|\n", + "|phase 1|360.47941 K|34.58586 mol/m³|[0.42362, 0.57638]|\n", + "|phase 2|360.47941 K|13.28059 kmol/m³|[0.17482, 0.82518]|\n" + ], + "text/plain": [ + "phase 0: T = 360.47941 K, ρ = 34.58586 mol/m³, x = [0.42362, 0.57638]\n", + "phase 1: T = 360.47941 K, ρ = 13.28059 kmol/m³, x = [0.17482, 0.82518]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x1 = 0.4\n", + "pressure = si.BAR\n", + "bubble = feos.PhaseEquilibrium.bubble_point(eos, pressure, x1, 300*si.KELVIN).liquid\n", + "dew = feos.PhaseEquilibrium.dew_point(eos, pressure, x1, 300*si.KELVIN).vapor\n", + "print(f\"bubble point\\ttemperature: {bubble.temperature/si.KELVIN:.2f} K\\tenthalpy: {bubble.molar_enthalpy()/(si.KILO*si.JOULE/si.MOL):8.4f} kJ/mol\")\n", + "print(f\" dew point\\ttemperature: {dew.temperature/si.KELVIN:.2f} K\\tenthalpy: {dew.molar_enthalpy()/(si.KILO*si.JOULE/si.MOL):8.4f} kJ/mol\")\n", + "\n", + "feos.PhaseEquilibrium.ph_flash(eos, pressure, 0*si.JOULE/si.MOL, x1, 356*si.KELVIN)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "feos_devel", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.14.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/py-feos/src/phase_equilibria.rs b/py-feos/src/phase_equilibria.rs index c6c857506..feaa38930 100644 --- a/py-feos/src/phase_equilibria.rs +++ b/py-feos/src/phase_equilibria.rs @@ -159,6 +159,128 @@ impl PyPhaseEquilibrium { )) } + /// Create a liquid and vapor state in equilibrium + /// for given pressure, enthalpy and feed composition. + /// + /// Can also be used to calculate liquid liquid phase separation. + /// + /// Parameters + /// ---------- + /// eos : EquationOfState + /// The equation of state. + /// pressure : SINumber + /// The system pressure. + /// molar_enthalpy : SINumber + /// The molar enthalpy of the system. + /// feed : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float] + /// Feed composition. + /// initial_temperature : SINumber + /// The system temperature. + /// max_iter : int, optional + /// The maximum number of iterations. + /// tol: float, optional + /// The solution tolerance. + /// verbosity : Verbosity, optional + /// The verbosity. + /// + /// Returns + /// ------- + /// PhaseEquilibrium + /// + /// Raises + /// ------ + /// RuntimeError + /// When pressure iteration fails or no phase equilibrium is found. + #[staticmethod] + #[pyo3( + text_signature = "(eos, pressure, molar_enthalpy, feed, initial_temperature, max_iter=None, tol=None, verbosity=None)" + )] + #[pyo3(signature = (eos, pressure, molar_enthalpy, feed, initial_temperature, max_iter=None, tol=None, verbosity=None))] + #[expect(clippy::too_many_arguments)] + pub(crate) fn ph_flash( + eos: &PyEquationOfState, + pressure: Pressure, + molar_enthalpy: MolarEnergy, + feed: &Bound<'_, PyAny>, + initial_temperature: Temperature, + max_iter: Option, + tol: Option, + verbosity: Option, + ) -> PyResult { + Ok(Self( + PhaseEquilibrium::ph_flash( + &eos.0, + pressure, + molar_enthalpy, + Compositions::try_from(Some(feed))?, + initial_temperature, + (max_iter, tol, verbosity.map(|v| v.into())).into(), + ) + .map_err(PyFeosError::from)?, + )) + } + + /// Create a liquid and vapor state in equilibrium + /// for given pressure, entropy and feed composition. + /// + /// Can also be used to calculate liquid liquid phase separation. + /// + /// Parameters + /// ---------- + /// eos : EquationOfState + /// The equation of state. + /// pressure : SINumber + /// The system pressure. + /// molar_entropy : SINumber + /// The molar entropy of the system. + /// feed : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float] + /// Feed composition. + /// initial_temperature : SINumber + /// The system temperature. + /// max_iter : int, optional + /// The maximum number of iterations. + /// tol: float, optional + /// The solution tolerance. + /// verbosity : Verbosity, optional + /// The verbosity. + /// + /// Returns + /// ------- + /// PhaseEquilibrium + /// + /// Raises + /// ------ + /// RuntimeError + /// When pressure iteration fails or no phase equilibrium is found. + #[staticmethod] + #[pyo3( + text_signature = "(eos, pressure, molar_entropy, feed, initial_temperature, max_iter=None, tol=None, verbosity=None)" + )] + #[pyo3(signature = (eos, pressure, molar_entropy, feed, initial_temperature, max_iter=None, tol=None, verbosity=None))] + #[expect(clippy::too_many_arguments)] + pub(crate) fn ps_flash( + eos: &PyEquationOfState, + pressure: Pressure, + molar_entropy: MolarEntropy, + feed: &Bound<'_, PyAny>, + initial_temperature: Temperature, + max_iter: Option, + tol: Option, + verbosity: Option, + ) -> PyResult { + Ok(Self( + PhaseEquilibrium::ps_flash( + &eos.0, + pressure, + molar_entropy, + Compositions::try_from(Some(feed))?, + initial_temperature, + (max_iter, tol, verbosity.map(|v| v.into())).into(), + ) + .map_err(PyFeosError::from)?, + )) + } + /// Compute a phase equilibrium for given temperature /// or pressure and liquid mole fractions. /// @@ -345,6 +467,36 @@ impl PyPhaseEquilibrium { PyState(self.0.liquid().clone()) } + #[getter] + fn get_vapor_phase_fraction(&self) -> f64 { + self.0.vapor_phase_fraction() + } + + #[getter] + fn get_total_moles(&self) -> PyResult { + Ok(self.0.total_moles().map_err(PyFeosError::from)?) + } + + #[getter] + fn get_molar_enthalpy(&self) -> MolarEnergy { + self.0.molar_enthalpy() + } + + #[getter] + fn get_enthalpy(&self) -> PyResult { + Ok(self.0.enthalpy().map_err(PyFeosError::from)?) + } + + #[getter] + fn get_molar_entropy(&self) -> MolarEntropy { + self.0.molar_entropy() + } + + #[getter] + fn get_entropy(&self) -> PyResult { + Ok(self.0.entropy().map_err(PyFeosError::from)?) + } + /// Calculate the pure component vapor-liquid equilibria for all /// components in the system. /// From a789b982f107367b28ad00493085e3024808a2d8 Mon Sep 17 00:00:00 2001 From: Gernot Bauer Date: Fri, 22 May 2026 07:52:21 +0200 Subject: [PATCH 07/12] Datasets and properties (#358) --- CHANGELOG.md | 7 + Cargo.toml | 1 + crates/feos-core/Cargo.toml | 1 + crates/feos-core/src/ad/dataset/binary.rs | 304 +++++++++ crates/feos-core/src/ad/dataset/mod.rs | 176 +++++ crates/feos-core/src/ad/dataset/pure.rs | 434 +++++++++++++ crates/feos-core/src/ad/mod.rs | 451 ++----------- .../src/ad/properties/boiling_temperature.rs | 71 ++ .../ad/properties/bubble_point_pressure.rs | 91 +++ .../src/ad/properties/dew_point_pressure.rs | 90 +++ .../ad/properties/enthalpy_of_vaporization.rs | 34 + .../properties/equilibrium_liquid_density.rs | 31 + .../src/ad/properties/liquid_density.rs | 35 + crates/feos-core/src/ad/properties/mod.rs | 189 ++++++ .../residual_isobaric_heat_capacity.rs | 36 ++ .../src/ad/properties/vapor_pressure.rs | 60 ++ crates/feos-core/src/density_iteration.rs | 5 +- crates/feos-core/src/lib.rs | 3 +- crates/feos-derive/src/dft.rs | 2 +- crates/feos/benches/README.md | 9 +- crates/feos/src/gc_pcsaft/dft/mod.rs | 5 +- crates/feos/src/pcsaft/dft/mod.rs | 3 +- crates/feos/src/pcsaft/eos/mod.rs | 85 ++- crates/feos/src/pcsaft/eos/pcsaft_binary.rs | 90 +-- crates/feos/src/pcsaft/eos/pcsaft_pure.rs | 65 +- crates/feos/tests/pcsaft/px_flashes.rs | 14 +- py-feos/src/ad/dataset.rs | 551 ++++++++++++++++ py-feos/src/ad/mod.rs | 612 +++++++++++++----- py-feos/src/lib.rs | 20 +- py-feos/src/user_defined.rs | 26 +- 30 files changed, 2796 insertions(+), 705 deletions(-) create mode 100644 crates/feos-core/src/ad/dataset/binary.rs create mode 100644 crates/feos-core/src/ad/dataset/mod.rs create mode 100644 crates/feos-core/src/ad/dataset/pure.rs create mode 100644 crates/feos-core/src/ad/properties/boiling_temperature.rs create mode 100644 crates/feos-core/src/ad/properties/bubble_point_pressure.rs create mode 100644 crates/feos-core/src/ad/properties/dew_point_pressure.rs create mode 100644 crates/feos-core/src/ad/properties/enthalpy_of_vaporization.rs create mode 100644 crates/feos-core/src/ad/properties/equilibrium_liquid_density.rs create mode 100644 crates/feos-core/src/ad/properties/liquid_density.rs create mode 100644 crates/feos-core/src/ad/properties/mod.rs create mode 100644 crates/feos-core/src/ad/properties/residual_isobaric_heat_capacity.rs create mode 100644 crates/feos-core/src/ad/properties/vapor_pressure.rs create mode 100644 py-feos/src/ad/dataset.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 442f80bd5..5f6849481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,16 +12,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added the `Composition` trait to allow more flexibility in the creation of states and phase equilibria. [#330](https://github.com/feos-org/feos/pull/330) - Added `PhaseEquilibrium::ph_flash` and `PhaseEquilibrium::ps_flash`. [#338](https://github.com/feos-org/feos/pull/338) - Added getters for `vapor_phase_fraction`, `molar_enthalpy`, `molar_entropy`, `total_moles`, `enthalpy`, and `entropy` to `PhaseEquilibrium`. [#338](https://github.com/feos-org/feos/pull/338) +- Added `PropertyAD` trait in `feos_core::ad` with one struct per property for uniform evaluation with or without parameter derivatives, including parallel variants. [#358](https://github.com/feos-org/feos/pull/358) +- Added `feos_core::ad::dataset` module with `PureDataset` and `BinaryDataset` types, constructible from records, CSV files, or readers, for use in parameter fits. [#358](https://github.com/feos-org/feos/pull/358) +- Exposed `Property`, `PureDataset`, and `BinaryDataset` in `py-feos`. [#358](https://github.com/feos-org/feos/pull/358) ### Changed - Removed any assumptions about the total number of moles in a `State` or `PhaseEquilibrium`. Evaluating extensive properties now returns a `Result`. [#330](https://github.com/feos-org/feos/pull/330) - Redesigned the `IdealGas` trait and added `IdealGasAD` in analogy to `ResidualDyn` and `Residual`. [#330](https://github.com/feos-org/feos/pull/330) +- Replaced the `PropertiesAD` blanket-impl trait with per-property `PropertyAD` types. [#358](https://github.com/feos-org/feos/pull/358) +- Replaced `ParametersAD::named_derivatives` with a `build` constructor and `seed_derivatives(&values, names)`. [#358](https://github.com/feos-org/feos/pull/358) +- Removed the per-property `*_derivatives` free functions in Python in favour of static methods on the new `Property` class. [#358](https://github.com/feos-org/feos/pull/358) ### Removed - Removed the `StateBuilder` struct, because it is mostly obsolete with the addition of the `Composition` trait. [#330](https://github.com/feos-org/feos/pull/330) ### Packaging - Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#328](https://github.com/feos-org/feos/pull/328) +- Added `csv` as a `feos-core` dependency for the new dataset module. [#358](https://github.com/feos-org/feos/pull/358) ## [Unreleased] diff --git a/Cargo.toml b/Cargo.toml index b805b446a..038739561 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ approx = "0.5" criterion = "0.8" paste = "1.0" rusqlite = "0.39" +csv = "1.0" feos-core = { version = "0.9", path = "crates/feos-core" } feos-dft = { version = "0.9", path = "crates/feos-dft" } diff --git a/crates/feos-core/Cargo.toml b/crates/feos-core/Cargo.toml index 810dc2996..c1831a1b7 100644 --- a/crates/feos-core/Cargo.toml +++ b/crates/feos-core/Cargo.toml @@ -25,6 +25,7 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["preserve_order"] } indexmap = { workspace = true, features = ["serde"] } rayon = { workspace = true, optional = true } +csv = { workspace = true } itertools = { workspace = true } rusqlite = { workspace = true, features = ["bundled"], optional = true } diff --git a/crates/feos-core/src/ad/dataset/binary.rs b/crates/feos-core/src/ad/dataset/binary.rs new file mode 100644 index 000000000..2bf797f6e --- /dev/null +++ b/crates/feos-core/src/ad/dataset/binary.rs @@ -0,0 +1,304 @@ +use std::{io, path::Path}; + +use nalgebra::U2; +use ndarray::{Array1, Array2, ArrayView1, ArrayView2}; +use serde::{Deserialize, Serialize}; + +use crate::Residual; +use crate::ad::Gradient; +use crate::ad::properties::{BubblePointPressure, DewPointPressure, PropertyAD}; + +use super::{Dataset, DatasetAD, DatasetRecord, DatasetStorage, ParametersAD}; + +/// The pressure column doubles as the initial guess passed to the VLE solver. +#[derive(Deserialize, Serialize)] +pub struct BubblePointRecord { + pub temperature_k: f64, + pub liquid_molefrac_1: f64, + pub bubble_pressure_pa: f64, +} + +impl DatasetRecord for BubblePointRecord { + const N_INPUTS: usize = 3; + + fn input(&self, column: usize) -> f64 { + match column { + 0 => self.temperature_k, + 1 => self.liquid_molefrac_1, + 2 => self.bubble_pressure_pa, + _ => unreachable!("invalid bubble point input column"), + } + } + + fn target(&self) -> f64 { + self.bubble_pressure_pa + } +} + +/// The pressure column doubles as the initial guess passed to the VLE solver. +#[derive(Deserialize, Serialize)] +pub struct DewPointRecord { + pub temperature_k: f64, + pub vapor_molefrac_1: f64, + pub dew_pressure_pa: f64, +} + +impl DatasetRecord for DewPointRecord { + const N_INPUTS: usize = 3; + + fn input(&self, column: usize) -> f64 { + match column { + 0 => self.temperature_k, + 1 => self.vapor_molefrac_1, + 2 => self.dew_pressure_pa, + _ => unreachable!("invalid dew point input column"), + } + } + + fn target(&self) -> f64 { + self.dew_pressure_pa + } +} + +/// Expand a list of binary-mixture property entries into: +/// - the [`BinaryProperty`] enum, metadata and dispatch methods, +/// - constructors, +/// - [`BinaryDataset::from_csv`] and [`BinaryDataset::from_reader`] match arms. +macro_rules! binary_properties { + ($( + $variant:ident { + record: $record:ty, + property: $prop:ty, + default_name: $default:expr, + input_names: $inputs:expr, + target_name: $target:expr, + constructor: $ctor:ident, + } + ),* $(,)?) => { + /// Binary-mixture properties. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum BinaryProperty { + $($variant,)* + } + + impl BinaryProperty { + pub fn default_name(self) -> &'static str { + match self { $(Self::$variant => $default,)* } + } + + pub fn input_names(self) -> &'static [&'static str] { + match self { $(Self::$variant => $inputs,)* } + } + + pub fn target_name(self) -> &'static str { + match self { $(Self::$variant => $target,)* } + } + + fn evaluate_ad, const P: usize>( + self, + names: [String; P], + parameters: &[f64], + inputs: ArrayView2, + ) -> (Array1, Array2, Array1) + where + T::Lifted>: Sync, + { + match self { + $(Self::$variant => <$prop>::evaluate_parallel_derivatives::(names, parameters, inputs),)* + } + } + + fn evaluate(self, eos: &E, inputs: ArrayView2) -> (Array1, Array1) + where + E: Residual + Sync, + { + match self { + $(Self::$variant => <$prop>::evaluate_parallel(eos, inputs),)* + } + } + } + + impl BinaryDataset { + $( + pub fn $ctor(records: Vec<$record>) -> Self { + Self { + property: BinaryProperty::$variant, + storage: DatasetStorage::from_records(records), + } + } + )* + + pub fn from_csv(property: BinaryProperty, path: &Path) -> Result { + let storage = match property { + $(BinaryProperty::$variant => DatasetStorage::from_csv::<$record>(path)?,)* + }; + Ok(Self { property, storage }) + } + + pub fn from_reader( + property: BinaryProperty, + reader: impl io::Read, + ) -> Result { + let storage = match property { + $(BinaryProperty::$variant => DatasetStorage::from_reader::<$record>(reader)?,)* + }; + Ok(Self { property, storage }) + } + } + }; +} + +binary_properties! { + BubblePointPressure { + record: BubblePointRecord, + property: BubblePointPressure, + default_name: "bubble point pressure", + input_names: &["temperature_k", "liquid_molefrac_1"], + target_name: "bubble_pressure_pa", + constructor: bubble_point_pressure, + }, + DewPointPressure { + record: DewPointRecord, + property: DewPointPressure, + default_name: "dew point pressure", + input_names: &["temperature_k", "vapor_molefrac_1"], + target_name: "dew_pressure_pa", + constructor: dew_point_pressure, + }, +} + +/// Binary-mixture dataset: shared data storage plus a property tag. +#[derive(Clone)] +pub struct BinaryDataset { + property: BinaryProperty, + storage: DatasetStorage, +} + +impl BinaryDataset { + pub fn with_name(mut self, name: impl Into) -> Self { + self.storage.set_name(name.into()); + self + } + + pub fn property(&self) -> BinaryProperty { + self.property + } + + pub fn inputs(&self) -> ArrayView2<'_, f64> { + self.storage.inputs() + } + + pub fn target(&self) -> ArrayView1<'_, f64> { + self.storage.target() + } + + pub fn name(&self) -> &str { + self.storage.name().unwrap_or(self.property.default_name()) + } + + pub fn input_names(&self) -> &'static [&'static str] { + self.property.input_names() + } + + pub fn target_name(&self) -> &'static str { + self.property.target_name() + } +} + +impl Dataset for BinaryDataset { + fn inputs(&self) -> ArrayView2<'_, f64> { + self.inputs() + } + + fn target(&self) -> ArrayView1<'_, f64> { + self.target() + } + + fn name(&self) -> &str { + self.name() + } + + fn input_names(&self) -> &'static [&'static str] { + self.input_names() + } + + fn target_name(&self) -> &'static str { + self.target_name() + } + + fn evaluate(&self, eos: &E) -> (Array1, Array1) { + self.property.evaluate(eos, self.inputs()) + } +} + +impl DatasetAD<2> for BinaryDataset { + fn evaluate_ad_const, const P: usize>( + &self, + names: [String; P], + parameters: &[f64], + inputs: ArrayView2, + ) -> (Array1, Array2, Array1) + where + T::Lifted>: Sync, + { + self.property.evaluate_ad::(names, parameters, inputs) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + fn csv(s: &str) -> Cursor<&[u8]> { + Cursor::new(s.as_bytes()) + } + + #[test] + fn bubble_point_from_reader() { + let data = "\ +temperature_k,liquid_molefrac_1,bubble_pressure_pa +300.0,0.3,500000.0 +320.0,0.5,800000.0 +"; + let ds = + BinaryDataset::from_reader(BinaryProperty::BubblePointPressure, csv(data)).unwrap(); + + assert_eq!(ds.inputs().ncols(), 3); + assert_eq!(ds.inputs().nrows(), 2); + assert_eq!(ds.inputs()[[0, 0]], 300.0); + assert_eq!(ds.inputs()[[0, 1]], 0.3); + assert_eq!(ds.inputs()[[0, 2]], 500000.0); + assert_eq!(ds.target()[0], 500000.0); + assert_eq!(ds.target()[1], 800000.0); + assert_eq!(ds.name(), "bubble point pressure"); + } + + #[test] + fn dew_point_from_reader() { + let data = "\ +temperature_k,vapor_molefrac_1,dew_pressure_pa +310.0,0.7,400000.0 +330.0,0.9,700000.0 +"; + let ds = BinaryDataset::from_reader(BinaryProperty::DewPointPressure, csv(data)).unwrap(); + + assert_eq!(ds.inputs().ncols(), 3); + assert_eq!(ds.inputs()[[0, 0]], 310.0); + assert_eq!(ds.inputs()[[0, 1]], 0.7); + assert_eq!(ds.inputs()[[0, 2]], 400000.0); + assert_eq!(ds.target()[1], 700000.0); + assert_eq!(ds.name(), "dew point pressure"); + } + + #[test] + fn dew_point_pressure_doubles_as_initial_guess() { + let records = vec![DewPointRecord { + temperature_k: 310.0, + vapor_molefrac_1: 0.7, + dew_pressure_pa: 400000.0, + }]; + let ds = BinaryDataset::dew_point_pressure(records); + assert_eq!(ds.inputs()[[0, 2]], ds.target()[0]); + } +} diff --git a/crates/feos-core/src/ad/dataset/mod.rs b/crates/feos-core/src/ad/dataset/mod.rs new file mode 100644 index 000000000..7be48a6fb --- /dev/null +++ b/crates/feos-core/src/ad/dataset/mod.rs @@ -0,0 +1,176 @@ +mod binary; +mod pure; + +use std::{io, path::Path, sync::Arc}; + +use nalgebra::Const; +use ndarray::{Array1, Array2, ArrayView1, ArrayView2}; +use serde::de::DeserializeOwned; + +use crate::Residual; +use crate::ad::Gradient; + +use super::ParametersAD; + +pub use binary::*; +pub use pure::*; + +/// Shared numerical data for all datasets. +struct DatasetData { + inputs: Array2, + target: Array1, +} + +/// Shared representation for all datasets. +#[derive(Clone)] +struct DatasetStorage { + data: Arc, + name: Option, +} + +impl DatasetStorage { + fn from_records(records: Vec) -> Self { + let n = records.len(); + let inputs = Array2::from_shape_fn((n, R::N_INPUTS), |(i, j)| records[i].input(j)); + let target = Array1::from_iter(records.iter().map(DatasetRecord::target)); + Self { + data: Arc::new(DatasetData { inputs, target }), + name: None, + } + } + + fn from_csv(path: &Path) -> Result { + let records = csv::Reader::from_path(path)? + .deserialize() + .collect::, _>>()?; + Ok(Self::from_records(records)) + } + + fn from_reader(reader: impl io::Read) -> Result { + let records = csv::Reader::from_reader(reader) + .deserialize() + .collect::, _>>()?; + Ok(Self::from_records(records)) + } + + fn inputs(&self) -> ArrayView2<'_, f64> { + self.data.inputs.view() + } + + fn target(&self) -> ArrayView1<'_, f64> { + self.data.target.view() + } + + fn name(&self) -> Option<&str> { + self.name.as_deref() + } + + fn set_name(&mut self, name: String) { + self.name = Some(name); + } +} + +/// A record that can be collected into a dataset. +pub trait DatasetRecord: DeserializeOwned { + /// Number of columns for inputs. + const N_INPUTS: usize; + + /// Value of EoS input column. + fn input(&self, column: usize) -> f64; + + /// Target value. + fn target(&self) -> f64; +} + +/// Dataset that can be evaluated by an equation of state. +pub trait Dataset { + /// Inputs for EoS evaluation, shape `[n_points, k]`. + fn inputs(&self) -> ArrayView2<'_, f64>; + + /// Target values, shape `[n_points]`. + fn target(&self) -> ArrayView1<'_, f64>; + + /// Property name. + /// + /// Used for logging and diagnostics. + fn name(&self) -> &str; + + /// Names of independent input columns. + fn input_names(&self) -> &'static [&'static str]; + + /// Name of the target property. + fn target_name(&self) -> &'static str; + + /// Evaluate this dataset's property with an equation of state. + /// + /// Returns `(predicted, converged)`: + /// - `predicted`: shape `[n_points]`, in SI units; `NaN` where the + /// underlying solver did not converge. + /// - `converged`: shape `[n_points]`. + fn evaluate(&self, eos: &E) -> (Array1, Array1); +} + +/// Build [`GRADIENT_SLOTS`] and [`DatasetAD`] trait from a single list of 'slots'. +/// +/// For each slot in [`GRADIENT_SLOTS`] a compile-time constant `P` variant is generated. +macro_rules! define_dataset_ad { + ($($p:literal),+ $(,)?) => { + /// Compile-time gradient slot supported by [`DatasetAD::evaluate_ad`]. + pub const GRADIENT_SLOTS: &[usize] = &[$($p),+]; + + /// Dataset that supports parameter-gradient evaluation + /// for equations of state implementing [`ParametersAD`]. + pub trait DatasetAD: Dataset { + /// Evaluate the property and its `P` parameter gradients. + fn evaluate_ad_const>, const P: usize>( + &self, + names: [String; P], + parameters: &[f64], + inputs: ArrayView2, + ) -> (Array1, Array2, Array1) + where + T::Lifted>: Sync; + + /// Evaluate the property and its parameter gradients at the given parameters. + /// + /// - `param_names`: names of the `P` parameters being differentiated. + /// - `params`: the full parameter vector. Only entries listed in `param_names` are seeded. + /// + /// This function dispatches the const-P methods at run-time. + fn evaluate_ad>>( + &self, + param_names: &[String], + parameters: &[f64], + ) -> (Array1, Array2, Array1) + where + $(T::Lifted>: Sync,)* + { + fn to_const(names: &[String]) -> [String; P] { + names.to_vec().try_into().expect("parameter count mismatch") + } + + match param_names.len() { + $( + $p => self.evaluate_ad_const::( + to_const(param_names), + parameters, + self.inputs().view(), + ), + )+ + p => unreachable!( + "parameter count {p} is not a member of GRADIENT_SLOTS={:?}", + GRADIENT_SLOTS, + ), + } + } + } + }; +} + +// We define the number of slots here. +// +// Note: might be good to investigate whether a smaller list makes sense here. +// LLVM vectorises across entries in DualSVec. We might see no perf. difference +// when using e.g. 3 vs 4 slots (even if only 3 are needed by the user). +// If the number of monomophised variants ever gets problematic, we could reduce it that way. +define_dataset_ad!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); diff --git a/crates/feos-core/src/ad/dataset/pure.rs b/crates/feos-core/src/ad/dataset/pure.rs new file mode 100644 index 000000000..4a7e74aa8 --- /dev/null +++ b/crates/feos-core/src/ad/dataset/pure.rs @@ -0,0 +1,434 @@ +use std::{io, path::Path}; + +use nalgebra::U1; +use ndarray::{Array1, Array2, ArrayView1, ArrayView2}; +use serde::{Deserialize, Serialize}; + +use crate::Residual; +use crate::ad::Gradient; +use crate::ad::properties::*; + +use super::{Dataset, DatasetAD, DatasetRecord, DatasetStorage, ParametersAD}; + +#[derive(Deserialize, Serialize)] +pub struct VaporPressureRecord { + pub temperature_k: f64, + pub vapor_pressure_pa: f64, +} + +impl DatasetRecord for VaporPressureRecord { + const N_INPUTS: usize = 1; + + fn input(&self, _column: usize) -> f64 { + self.temperature_k + } + + fn target(&self) -> f64 { + self.vapor_pressure_pa + } +} + +#[derive(Deserialize, Serialize)] +pub struct LiquidDensityRecord { + pub temperature_k: f64, + pub pressure_pa: f64, + pub liquid_density_kmol_m3: f64, +} + +impl DatasetRecord for LiquidDensityRecord { + const N_INPUTS: usize = 2; + + fn input(&self, column: usize) -> f64 { + match column { + 0 => self.temperature_k, + 1 => self.pressure_pa, + _ => unreachable!("invalid liquid density input column"), + } + } + + fn target(&self) -> f64 { + self.liquid_density_kmol_m3 + } +} + +#[derive(Deserialize, Serialize)] +pub struct EquilibriumLiquidDensityRecord { + pub temperature_k: f64, + pub liquid_density_kmol_m3: f64, +} + +impl DatasetRecord for EquilibriumLiquidDensityRecord { + const N_INPUTS: usize = 1; + + fn input(&self, _column: usize) -> f64 { + self.temperature_k + } + + fn target(&self) -> f64 { + self.liquid_density_kmol_m3 + } +} + +#[derive(Deserialize, Serialize)] +pub struct EnthalpyOfVaporizationRecord { + pub temperature_k: f64, + pub dh_vap_j_mol: f64, +} + +impl DatasetRecord for EnthalpyOfVaporizationRecord { + const N_INPUTS: usize = 1; + + fn input(&self, _column: usize) -> f64 { + self.temperature_k + } + + fn target(&self) -> f64 { + self.dh_vap_j_mol + } +} + +#[derive(Deserialize, Serialize)] +pub struct ResidualIsobaricHeatCapacityRecord { + pub temperature_k: f64, + pub pressure_pa: f64, + pub cp_res_j_molk: f64, +} + +impl DatasetRecord for ResidualIsobaricHeatCapacityRecord { + const N_INPUTS: usize = 2; + + fn input(&self, column: usize) -> f64 { + match column { + 0 => self.temperature_k, + 1 => self.pressure_pa, + _ => unreachable!("invalid residual isobaric heat capacity input column"), + } + } + + fn target(&self) -> f64 { + self.cp_res_j_molk + } +} + +/// Expand a list of pure-component property entries into: +/// - the [`PureProperty`] enum, metadata and dispatch methods, +/// - constructors, +/// - [`PureDataset::from_csv`] and [`PureDataset::from_reader`]. +/// +/// Adding a new property: +/// - write the property file (record, `*_ad`, `*_parallel`, `*_parallel_ad`) +/// - add entry here. +macro_rules! pure_properties { + ($( + $variant:ident { + record: $record:ty, + property: $prop:ty, + default_name: $default:expr, + input_names: $inputs:expr, + target_name: $target:expr, + constructor: $ctor:ident, + } + ),* $(,)?) => { + /// Pure-component properties. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum PureProperty { + $($variant,)* + } + + impl PureProperty { + pub fn default_name(self) -> &'static str { + match self { $(Self::$variant => $default,)* } + } + + pub fn input_names(self) -> &'static [&'static str] { + match self { $(Self::$variant => $inputs,)* } + } + + pub fn target_name(self) -> &'static str { + match self { $(Self::$variant => $target,)* } + } + + fn evaluate_ad, const P: usize>( + self, + names: [String; P], + parameters: &[f64], + inputs: ArrayView2, + ) -> (Array1, Array2, Array1) + where T::Lifted>: Sync + { + match self { + $(Self::$variant => <$prop>::evaluate_parallel_derivatives::(names, parameters, inputs),)* + } + } + + fn evaluate(self, eos: &E, inputs: ArrayView2) -> (Array1, Array1) + { + match self { + $(Self::$variant => <$prop>::evaluate_parallel(eos, inputs.view()),)* + } + } + } + + impl PureDataset { + $( + pub fn $ctor(records: Vec<$record>) -> Self { + Self { + property: PureProperty::$variant, + storage: DatasetStorage::from_records(records), + } + } + )* + + pub fn from_csv(property: PureProperty, path: &Path) -> Result { + let storage = match property { + $(PureProperty::$variant => DatasetStorage::from_csv::<$record>(path)?,)* + }; + Ok(Self { property, storage }) + } + + pub fn from_reader( + property: PureProperty, + reader: impl io::Read, + ) -> Result { + let storage = match property { + $(PureProperty::$variant => DatasetStorage::from_reader::<$record>(reader)?,)* + }; + Ok(Self { property, storage }) + } + } + }; +} + +pure_properties! { + VaporPressure { + record: VaporPressureRecord, + property: VaporPressure, + default_name: "vapor pressure", + input_names: &["temperature_k"], + target_name: "vapor_pressure_pa", + constructor: vapor_pressure, + }, + LiquidDensity { + record: LiquidDensityRecord, + property: LiquidDensity, + default_name: "liquid density", + input_names: &["temperature_k", "pressure_pa"], + target_name: "liquid_density_kmol_m3", + constructor: liquid_density, + }, + EquilibriumLiquidDensity { + record: EquilibriumLiquidDensityRecord, + property: EquilibriumLiquidDensity, + default_name: "equilibrium liquid density", + input_names: &["temperature_k"], + target_name: "liquid_density_kmol_m3", + constructor: equilibrium_liquid_density, + }, + EnthalpyOfVaporization { + record: EnthalpyOfVaporizationRecord, + property: EnthalpyOfVaporization, + default_name: "enthalpy of vaporization", + input_names: &["temperature_k"], + target_name: "dh_vap_j_mol", + constructor: enthalpy_of_vaporization, + }, + ResidualIsobaricHeatCapacity { + record: ResidualIsobaricHeatCapacityRecord, + property: ResidualIsobaricHeatCapacity, + default_name: "residual isobaric heat capacity", + input_names: &["temperature_k", "pressure_pa"], + target_name: "cp_res_j_molk", + constructor: residual_isobaric_heat_capacity, + }, +} + +/// Pure-component dataset: shared data storage plus a property tag. +// #[derive(Clone)] +pub struct PureDataset { + property: PureProperty, + storage: DatasetStorage, +} + +impl PureDataset { + pub fn with_name(mut self, name: impl Into) -> Self { + self.storage.set_name(name.into()); + self + } + + pub fn property(&self) -> PureProperty { + self.property + } + + pub fn inputs(&self) -> ArrayView2<'_, f64> { + self.storage.inputs() + } + + pub fn target(&self) -> ArrayView1<'_, f64> { + self.storage.target() + } + + pub fn name(&self) -> &str { + self.storage.name().unwrap_or(self.property.default_name()) + } + + pub fn input_names(&self) -> &'static [&'static str] { + self.property.input_names() + } + + pub fn target_name(&self) -> &'static str { + self.property.target_name() + } +} + +impl Dataset for PureDataset { + fn inputs(&self) -> ArrayView2<'_, f64> { + self.inputs() + } + + fn target(&self) -> ArrayView1<'_, f64> { + self.target() + } + + fn name(&self) -> &str { + self.name() + } + + fn input_names(&self) -> &'static [&'static str] { + self.input_names() + } + + fn target_name(&self) -> &'static str { + self.target_name() + } + + fn evaluate(&self, eos: &E) -> (Array1, Array1) { + self.property.evaluate(eos, self.inputs().view()) + } +} + +impl DatasetAD<1> for PureDataset { + fn evaluate_ad_const, const P: usize>( + &self, + names: [String; P], + parameters: &[f64], + inputs: ArrayView2, + ) -> (Array1, Array2, Array1) + where + T::Lifted>: Sync, + { + self.property.evaluate_ad::(names, parameters, inputs) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + fn csv(s: &str) -> Cursor<&[u8]> { + Cursor::new(s.as_bytes()) + } + + #[test] + fn vapor_pressure_from_reader() { + let data = "\ +temperature_k,vapor_pressure_pa +300.0,3540.0 +350.0,41682.0 +400.0,245600.0 +"; + let ds = PureDataset::from_reader(PureProperty::VaporPressure, csv(data)).unwrap(); + + assert_eq!(ds.target().len(), 3); + assert_eq!(ds.inputs().nrows(), 3); + assert_eq!(ds.inputs().ncols(), 1); + assert_eq!(ds.inputs()[[0, 0]], 300.0); + assert_eq!(ds.inputs()[[1, 0]], 350.0); + assert_eq!(ds.inputs()[[2, 0]], 400.0); + assert_eq!(ds.target()[0], 3540.0); + assert_eq!(ds.target()[1], 41682.0); + assert_eq!(ds.target()[2], 245600.0); + assert_eq!(ds.name(), "vapor pressure"); + } + + #[test] + fn vapor_pressure_from_records() { + let records = vec![ + VaporPressureRecord { + temperature_k: 300.0, + vapor_pressure_pa: 3540.0, + }, + VaporPressureRecord { + temperature_k: 350.0, + vapor_pressure_pa: 41682.0, + }, + ]; + let ds = PureDataset::vapor_pressure(records); + assert_eq!(ds.inputs()[[0, 0]], 300.0); + assert_eq!(ds.target()[1], 41682.0); + } + + #[test] + fn liquid_density_from_reader() { + let data = "\ +temperature_k,pressure_pa,liquid_density_kmol_m3 +300.0,101325.0,15.2 +320.0,200000.0,14.8 +"; + let ds = PureDataset::from_reader(PureProperty::LiquidDensity, csv(data)).unwrap(); + + assert_eq!(ds.inputs().nrows(), 2); + assert_eq!(ds.inputs().ncols(), 2); + assert_eq!(ds.inputs()[[0, 0]], 300.0); + assert_eq!(ds.inputs()[[0, 1]], 101325.0); + assert_eq!(ds.inputs()[[1, 0]], 320.0); + assert_eq!(ds.inputs()[[1, 1]], 200000.0); + assert_eq!(ds.target()[0], 15.2); + assert_eq!(ds.target()[1], 14.8); + assert_eq!(ds.name(), "liquid density"); + } + + #[test] + fn liquid_density_from_records() { + let records = vec![LiquidDensityRecord { + temperature_k: 300.0, + pressure_pa: 101325.0, + liquid_density_kmol_m3: 15.2, + }]; + let ds = PureDataset::liquid_density(records); + assert_eq!(ds.inputs()[[0, 1]], 101325.0); + assert_eq!(ds.target()[0], 15.2); + } + + #[test] + fn equilibrium_liquid_density_from_reader() { + let data = "\ +temperature_k,liquid_density_kmol_m3 +290.0,15.5 +310.0,14.9 +330.0,14.1 +"; + let ds = + PureDataset::from_reader(PureProperty::EquilibriumLiquidDensity, csv(data)).unwrap(); + + assert_eq!(ds.inputs().ncols(), 1); + assert_eq!(ds.inputs().nrows(), 3); + assert_eq!(ds.inputs()[[2, 0]], 330.0); + assert_eq!(ds.target()[2], 14.1); + assert_eq!(ds.name(), "equilibrium liquid density"); + } + + #[test] + fn missing_column_returns_error() { + let data = "temperature_k\n300.0\n"; + let result = PureDataset::from_reader(PureProperty::VaporPressure, csv(data)); + assert!(result.is_err()); + } + + #[test] + fn wrong_type_returns_error() { + let data = "temperature_k,vapor_pressure_pa\n300.0,not_a_number\n"; + let result = PureDataset::from_reader(PureProperty::VaporPressure, csv(data)); + assert!(result.is_err()); + } +} diff --git a/crates/feos-core/src/ad/mod.rs b/crates/feos-core/src/ad/mod.rs index f48fb153c..b8d94ab6c 100644 --- a/crates/feos-core/src/ad/mod.rs +++ b/crates/feos-core/src/ad/mod.rs @@ -1,395 +1,74 @@ -use crate::DensityInitialization::Liquid; -use crate::density_iteration::density_iteration; -use crate::{Composition, FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; -use nalgebra::{Const, SVector, U1, U2}; -#[cfg(feature = "rayon")] -use ndarray::{Array1, Array2, ArrayView2, Zip}; -use num_dual::{Derivative, DualNum, DualSVec, DualStruct, first_derivative, partial2}; -use quantity::{Density, Pressure, Temperature}; -#[cfg(feature = "rayon")] -use quantity::{KELVIN, KILO, METER, MOL, PASCAL}; +use crate::Residual; +use nalgebra::{Const, DefaultAllocator, Dim, U1, allocator::Allocator}; +use num_dual::{Derivative, DualNum, DualSVec}; -type Gradient = DualSVec; +#[cfg(feature = "ndarray")] +mod dataset; +mod properties; +#[cfg(feature = "ndarray")] +pub use dataset::*; +pub use properties::*; -/// A model that can be evaluated with derivatives of its parameters. -pub trait ParametersAD: for<'a> From<&'a [f64]> + Residual> { - /// Return a mutable reference to the parameter named by `index` from the parameter set. - fn index_parameters_mut<'a, const P: usize>( - eos: &'a mut Self::Lifted>, - index: &str, - ) -> &'a mut Gradient

; - - /// Return the parameters with the appropriate derivatives. - fn named_derivatives( - &self, - parameter_names: [&str; P], - ) -> Self::Lifted> { - let mut eos = self.lift::>(); - for (i, p) in parameter_names.into_iter().enumerate() { - Self::index_parameters_mut(&mut eos, p).eps = - Derivative::derivative_generic(Const::

, U1, i) - } - eos - } -} - -/// Properties that can be evaluated with derivatives of model parameters. -pub trait PropertiesAD { - fn vapor_pressure( - &self, - temperature: Temperature, - ) -> FeosResult>> - where - Self: Residual>, - { - let eos_f64 = self.re(); - let (_, [vapor_density, liquid_density]) = - PhaseEquilibrium::pure_t(&eos_f64, temperature, None, Default::default())?; - - // implicit differentiation is implemented here instead of just calling pure_t with dual - // numbers, because for the first derivative, we can avoid calculating density derivatives. - let v1 = 1.0 / liquid_density.to_reduced(); - let v2 = 1.0 / vapor_density.to_reduced(); - let t = temperature.into_reduced(); - let (a1, a2) = { - let t = Gradient::from(t); - let v1 = Gradient::from(v1); - let v2 = Gradient::from(v2); - let x = Self::pure_molefracs(); - - let a1 = self.residual_helmholtz_energy(t, v1, &x); - let a2 = self.residual_helmholtz_energy(t, v2, &x); - (a1, a2) - }; - - let p = -(a1 - a2 + t * (v2 / v1).ln()) / (v1 - v2); - Ok(Pressure::from_reduced(p)) - } - - fn boiling_temperature( - &self, - pressure: Pressure, - ) -> FeosResult>> - where - Self: Residual>, - { - let eos_f64 = self.re(); - let (temperature, [vapor_density, liquid_density]) = - PhaseEquilibrium::pure_p(&eos_f64, pressure, None, Default::default())?; - - // implicit differentiation is implemented here instead of just calling pure_t with dual - // numbers, because for the first derivative, we can avoid calculating density derivatives. - let t = temperature.into_reduced(); - let v1 = 1.0 / liquid_density.to_reduced(); - let v2 = 1.0 / vapor_density.to_reduced(); - let p = pressure.into_reduced(); - let t = Gradient::from(t); - let t = t + { - let v1 = Gradient::from(v1); - let v2 = Gradient::from(v2); - let p = Gradient::from(p); - let x = Self::pure_molefracs(); - - let residual_entropy = |v| { - let (a, s) = first_derivative( - partial2( - |t, &v, x| self.lift().residual_helmholtz_energy(t, v, x), - &v, - &x, - ), - t, - ); - (a, -s) - }; - let (a1, s1) = residual_entropy(v1); - let (a2, s2) = residual_entropy(v2); - - let ln_rho = (v1 / v2).ln(); - (p * (v2 - v1) + (a2 - a1 + t * ln_rho)) / (s2 - s1 - ln_rho) - }; - Ok(Temperature::from_reduced(t)) - } - - fn equilibrium_liquid_density( - &self, - temperature: Temperature, - ) -> FeosResult<(Pressure>, Density>)> - where - Self: Residual>, - { - let t = Temperature::from_inner(&temperature); - PhaseEquilibrium::pure_t(self, t, None, Default::default()).map(|(p, [_, rho])| (p, rho)) - } - - fn liquid_density( - &self, - temperature: Temperature, - pressure: Pressure, - ) -> FeosResult>> - where - Self: Residual>, - { - let x = Self::pure_molefracs(); - let t = Temperature::from_inner(&temperature); - let p = Pressure::from_inner(&pressure); - density_iteration(self, t, p, &x, Some(Liquid)) - } - - #[cfg(feature = "rayon")] - fn vapor_pressure_parallel( - parameter_names: [String; P], - parameters: ArrayView2, - input: ArrayView2, - ) -> (Array1, Array2, Array1) - where - Self: ParametersAD<1>, - { - parallelize::<_, Self, _, _>( - parameter_names, - parameters, - input, - |eos: &Self::Lifted>, inp| { - eos.vapor_pressure(inp[0] * KELVIN) - .map(|p| p.convert_into(PASCAL)) - }, - ) - } - - #[cfg(feature = "rayon")] - fn boiling_temperature_parallel( - parameter_names: [String; P], - parameters: ArrayView2, - input: ArrayView2, - ) -> (Array1, Array2, Array1) - where - Self: ParametersAD<1>, - { - parallelize::<_, Self, _, _>( - parameter_names, - parameters, - input, - |eos: &Self::Lifted>, inp| { - eos.boiling_temperature(inp[0] * PASCAL) - .map(|p| p.convert_into(KELVIN)) - }, - ) - } +pub(crate) type Gradient = DualSVec; - #[cfg(feature = "rayon")] - fn liquid_density_parallel( - parameter_names: [String; P], - parameters: ArrayView2, - input: ArrayView2, - ) -> (Array1, Array2, Array1) - where - Self: ParametersAD<1>, - { - parallelize::<_, Self, _, _>( - parameter_names, - parameters, - input, - |eos: &Self::Lifted>, inp| { - eos.liquid_density(inp[0] * KELVIN, inp[1] * PASCAL) - .map(|d| d.convert_into(KILO * MOL / (METER * METER * METER))) - }, - ) - } - - #[cfg(feature = "rayon")] - fn equilibrium_liquid_density_parallel( - parameter_names: [String; P], - parameters: ArrayView2, - input: ArrayView2, - ) -> (Array1, Array2, Array1) - where - Self: ParametersAD<1>, - { - parallelize::<_, Self, _, _>( - parameter_names, - parameters, - input, - |eos: &Self::Lifted>, inp| { - eos.equilibrium_liquid_density(inp[0] * KELVIN) - .map(|(_, d)| d.convert_into(KILO * MOL / (METER * METER * METER))) - }, - ) - } - - fn bubble_point_pressure>( - &self, - temperature: Temperature, - pressure: Option, - liquid_molefracs: X, - ) -> FeosResult>> - where - Self: Residual>, - { - let eos_f64 = self.re(); - let (liquid_molefracs, _) = liquid_molefracs.into_molefracs(&eos_f64)?; - let vle = PhaseEquilibrium::bubble_point( - &eos_f64, - temperature, - liquid_molefracs, - pressure, - None, - Default::default(), - )?; - - // implicit differentiation is implemented here instead of just calling bubble_point with dual - // numbers, because for the first derivative, we can avoid calculating density derivatives. - let v_l = 1.0 / vle.liquid().density.to_reduced(); - let v_v = 1.0 / vle.vapor().density.to_reduced(); - let y = &vle.vapor().molefracs; - let y: SVector<_, 2> = SVector::from_fn(|i, _| y[i]); - let t = temperature.into_reduced(); - let (a_l, a_v, v_l, v_v) = { - let t = Gradient::from(t); - let v_l = Gradient::from(v_l); - let v_v = Gradient::from(v_v); - let y = y.map(Gradient::from); - let x = liquid_molefracs.map(Gradient::from); - - let a_v = self.residual_helmholtz_energy(t, v_v, &y); - let (p_l, mu_res_l, dp_l, dmu_l) = self.dmu_dv(t, v_l, &x); - let vi_l = dmu_l / dp_l; - let v_l = vi_l.dot(&y); - let a_l = (mu_res_l - vi_l * p_l).dot(&y); - (a_l, a_v, v_l, v_v) - }; - let rho_l = vle.liquid().partial_density().to_reduced(); - let rho_l = [rho_l[0], rho_l[1]]; - let rho_v = vle.vapor().partial_density().to_reduced(); - let rho_v = [rho_v[0], rho_v[1]]; - let p = -(a_v - a_l - + t * (y[0] * (rho_v[0] / rho_l[0]).ln() + y[1] * (rho_v[1] / rho_l[1]).ln() - 1.0)) - / (v_v - v_l); - Ok(Pressure::from_reduced(p)) - } - - fn dew_point_pressure>( - &self, - temperature: Temperature, - pressure: Option, - vapor_molefracs: X, - ) -> FeosResult>> - where - Self: Residual>, - { - let eos_f64 = self.re(); - let (vapor_molefracs, _) = vapor_molefracs.into_molefracs(&eos_f64)?; - let vle = PhaseEquilibrium::dew_point( - &eos_f64, - temperature, - vapor_molefracs, - pressure, - None, - Default::default(), - )?; - - // implicit differentiation is implemented here instead of just calling dew_point with dual - // numbers, because for the first derivative, we can avoid calculating density derivatives. - let v_l = 1.0 / vle.liquid().density.to_reduced(); - let v_v = 1.0 / vle.vapor().density.to_reduced(); - let x = &vle.liquid().molefracs; - let x: SVector<_, 2> = SVector::from_fn(|i, _| x[i]); - let t = temperature.into_reduced(); - let (a_l, a_v, v_l, v_v) = { - let t = Gradient::from(t); - let v_l = Gradient::from(v_l); - let v_v = Gradient::from(v_v); - let x = x.map(Gradient::from); - let y = vapor_molefracs.map(Gradient::from); - - let a_l = self.residual_helmholtz_energy(t, v_l, &x); - let (p_v, mu_res_v, dp_v, dmu_v) = self.dmu_dv(t, v_v, &y); - let vi_v = dmu_v / dp_v; - let v_v = vi_v.dot(&x); - let a_v = (mu_res_v - vi_v * p_v).dot(&x); - (a_l, a_v, v_l, v_v) - }; - let rho_l = vle.liquid().partial_density().to_reduced(); - let rho_l = [rho_l[0], rho_l[1]]; - let rho_v = vle.vapor().partial_density().to_reduced(); - let rho_v = [rho_v[0], rho_v[1]]; - let p = -(a_l - a_v - + t * (x[0] * (rho_l[0] / rho_v[0]).ln() + x[1] * (rho_l[1] / rho_v[1]).ln() - 1.0)) - / (v_l - v_v); - Ok(Pressure::from_reduced(p)) +/// A model that can be evaluated with derivatives of its parameters. +pub trait ParametersAD: Residual +where + DefaultAllocator: Allocator, +{ + /// Build the model by requesting each parameter by name. + /// + /// Call `f(name, differentiable)` for each parameter. The order of calls + /// defines the canonical parameter order. + /// + /// Set `differentiable` to `false` for fixed parameters. + fn build + Copy>( + f: impl FnMut(&'static str, bool) -> D, + ) -> Self::Lifted; + + /// Canonical parameter names in the order defined by [`build`](Self::build). + fn parameter_names() -> Vec<&'static str> { + let mut names = Vec::new(); + let _ = Self::build(|name, _| { + names.push(name); + 0.0 + }); + names } - #[cfg(feature = "rayon")] - fn bubble_point_pressure_parallel( - parameter_names: [String; P], - parameters: ArrayView2, - input: ArrayView2, - ) -> (Array1, Array2, Array1) - where - Self: ParametersAD<2>, - { - parallelize::<_, Self, _, _>( - parameter_names, - parameters, - input, - |eos: &Self::Lifted>, inp| { - eos.bubble_point_pressure(inp[0] * KELVIN, Some(inp[2] * PASCAL), inp[1]) - .map(|p| p.convert_into(PASCAL)) - }, - ) + /// Parameter names that can be differentiated, in canonical order. + fn differentiable_parameters() -> Vec<&'static str> { + let mut names = Vec::new(); + let _ = Self::build(|name, differentiable| { + if differentiable { + names.push(name); + } + 0.0 + }); + names } - #[cfg(feature = "rayon")] - fn dew_point_pressure_parallel( - parameter_names: [String; P], - parameters: ArrayView2, - input: ArrayView2, - ) -> (Array1, Array2, Array1) - where - Self: ParametersAD<2>, - { - parallelize::<_, Self, _, _>( - parameter_names, - parameters, - input, - |eos: &Self::Lifted>, inp| { - eos.dew_point_pressure(inp[0] * KELVIN, Some(inp[2] * PASCAL), inp[1]) - .map(|p| p.convert_into(PASCAL)) - }, - ) + /// Construct the model with derivative seeds for the `P` named parameters. + /// + /// - `parameter_values`: all parameter values in the canonical order + /// defined by [`build`](Self::build). + /// - `derivative_names`: names of the parameters to differentiate with + /// respect to. Gradient component `i` corresponds to + /// `derivative_names[i]`. + fn seed_derivatives( + parameter_values: &[f64], + derivative_names: [&str; P], + ) -> Self::Lifted> { + let mut idx = 0; + Self::build(|name, _differentiable| { + let i = idx; + idx += 1; + let mut d = Gradient::

::from(parameter_values[i]); + if let Some(seed_idx) = derivative_names.iter().position(|&n| n == name) { + d.eps = + Derivative::<_, _, Const

, _>::derivative_generic(Const::

, U1, seed_idx); + } + d + }) } } - -impl PropertiesAD for T {} - -#[cfg(feature = "rayon")] -fn parallelize, const N: usize, const P: usize>( - parameter_names: [String; P], - parameters: ArrayView2, - input: ArrayView2, - f: F, -) -> (Array1, Array2, Array1) -where - F: Fn(&E::Lifted>, &[f64]) -> FeosResult> + Sync, -{ - let parameter_names = parameter_names.each_ref().map(|s| s as &str); - let value_dual = Zip::from(parameters.rows()) - .and(input.rows()) - .par_map_collect(|par, inp| { - let par = par.as_slice().expect("Parameter array is not contiguous!"); - let inp = inp.as_slice().expect("Input array is not contiguous!"); - let eos = E::from(par).named_derivatives(parameter_names); - f(&eos, inp) - }); - let status = value_dual.iter().map(|p| p.is_ok()).collect(); - let value_dual: Array1<_> = value_dual.into_iter().flatten().collect(); - let mut value = Array1::zeros(value_dual.len()); - let mut grad = Array2::zeros([value_dual.len(), P]); - Zip::from(grad.rows_mut()) - .and(&mut value) - .and(&value_dual) - .for_each(|mut grad, p, p_dual| { - *p = p_dual.re; - let eps = p_dual.eps.unwrap_generic(Const::

, U1).data.0[0].to_vec(); - grad.assign(&Array1::from(eps)); - }); - (value, grad, status) -} diff --git a/crates/feos-core/src/ad/properties/boiling_temperature.rs b/crates/feos-core/src/ad/properties/boiling_temperature.rs new file mode 100644 index 000000000..c597fb30e --- /dev/null +++ b/crates/feos-core/src/ad/properties/boiling_temperature.rs @@ -0,0 +1,71 @@ +use super::PropertyAD; +use crate::ad::Gradient; +use crate::{FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, U1}; +use num_dual::{DualNum, DualStruct, Gradients, first_derivative, partial2}; +use quantity::{_Temperature, KELVIN, PASCAL, Pressure, Temperature}; + +/// Boiling temperature of a pure component as function of pressure. +pub struct BoilingTemperature(pub Pressure); + +impl<'a> From<&'a [f64]> for BoilingTemperature { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * PASCAL) + } +} + +impl PropertyAD for BoilingTemperature +where + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Unit = _Temperature; + const REFERENCE: Temperature = KELVIN; + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> { + let p = Pressure::from_inner(&self.0); + PhaseEquilibrium::pure_p(eos, p, None, Default::default()).map(|(t, _)| t) + } + + fn evaluate_gradient>, const P: usize>( + &self, + eos: &E, + ) -> FeosResult, Self::Unit>> { + let eos_f64 = eos.re(); + let (temperature, [vapor_density, liquid_density]) = + PhaseEquilibrium::pure_p(&eos_f64, self.0, None, Default::default())?; + + let t = temperature.into_reduced(); + let v1 = 1.0 / liquid_density.to_reduced(); + let v2 = 1.0 / vapor_density.to_reduced(); + let p = self.0.into_reduced(); + let t = Gradient::from(t); + let t = t + { + let v1 = Gradient::from(v1); + let v2 = Gradient::from(v2); + let p = Gradient::from(p); + let x = E::pure_molefracs(); + + let residual_entropy = |v| { + let (a, s) = first_derivative( + partial2( + |t, &v, x| eos.lift().residual_helmholtz_energy(t, v, x), + &v, + &x, + ), + t, + ); + (a, -s) + }; + let (a1, s1) = residual_entropy(v1); + let (a2, s2) = residual_entropy(v2); + + let ln_rho = (v1 / v2).ln(); + (p * (v2 - v1) + (a2 - a1 + t * ln_rho)) / (s2 - s1 - ln_rho) + }; + Ok(Temperature::from_reduced(t)) + } +} diff --git a/crates/feos-core/src/ad/properties/bubble_point_pressure.rs b/crates/feos-core/src/ad/properties/bubble_point_pressure.rs new file mode 100644 index 000000000..9e838b9f7 --- /dev/null +++ b/crates/feos-core/src/ad/properties/bubble_point_pressure.rs @@ -0,0 +1,91 @@ +use super::PropertyAD; +use crate::Contributions; +use crate::ad::Gradient; +use crate::{Composition, FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, U1}; +use num_dual::{DualNum, DualStruct, Gradients}; +use quantity::{_Pressure, KELVIN, PASCAL, Pressure, Temperature}; + +/// Bubble point pressure of a binary mixture as function of temperature and +/// molefracs of the first component. +/// +/// An initial value for the pressure can be passed as optional argument to +/// increase robustness and speed. +pub struct BubblePointPressure(pub Temperature, pub f64, pub Option); + +impl<'a> From<&'a [f64]> for BubblePointPressure { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * KELVIN, value[1], Some(value[2] * PASCAL)) + } +} + +impl PropertyAD for BubblePointPressure +where + DefaultAllocator: Allocator + Allocator + Allocator, + f64: Composition, +{ + type Unit = _Pressure; + const REFERENCE: Pressure = PASCAL; + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> + where + DefaultAllocator: Allocator + Allocator + Allocator, + { + let t = Temperature::from_inner(&self.0); + let p = Option::from_inner(&self.2); + let (x, _) = self.1.into_molefracs(&eos.re())?; + let x = x.map(D::from); + let vle = PhaseEquilibrium::bubble_point(eos, t, x, p, None, Default::default())?; + Ok(vle.vapor().pressure(Contributions::Total)) + } + + fn evaluate_gradient>, const P: usize>( + &self, + eos: &E, + ) -> FeosResult, Self::Unit>> + where + DefaultAllocator: Allocator + Allocator + Allocator, + { + let eos_f64 = eos.re(); + let (liquid_molefracs, _) = self.1.into_molefracs(&eos_f64)?; + let vle = PhaseEquilibrium::bubble_point( + &eos_f64, + self.0, + &liquid_molefracs, + self.2, + None, + Default::default(), + )?; + + let v_l = 1.0 / vle.liquid().density.to_reduced(); + let v_v = 1.0 / vle.vapor().density.to_reduced(); + let y = &vle.vapor().molefracs; + let t = self.0.into_reduced(); + let (a_l, a_v, v_l, v_v) = { + let t = Gradient::from(t); + let v_l = Gradient::from(v_l); + let v_v = Gradient::from(v_v); + let y = y.map(Gradient::from); + let x = liquid_molefracs.map(Gradient::from); + + let a_v = eos.residual_helmholtz_energy(t, v_v, &y); + let (p_l, mu_res_l, dp_l, dmu_l) = eos.dmu_dv(t, v_l, &x); + let vi_l = dmu_l / dp_l; + let v_l = vi_l.dot(&y); + let a_l = (mu_res_l - vi_l * p_l).dot(&y); + (a_l, a_v, v_l, v_v) + }; + let rho_l = vle.liquid().partial_density().to_reduced(); + let rho_l = [rho_l[0], rho_l[1]]; + let rho_v = vle.vapor().partial_density().to_reduced(); + let rho_v = [rho_v[0], rho_v[1]]; + let p = -(a_v - a_l + + t * (y[0] * (rho_v[0] / rho_l[0]).ln() + y[1] * (rho_v[1] / rho_l[1]).ln() - 1.0)) + / (v_v - v_l); + Ok(Pressure::from_reduced(p)) + } +} diff --git a/crates/feos-core/src/ad/properties/dew_point_pressure.rs b/crates/feos-core/src/ad/properties/dew_point_pressure.rs new file mode 100644 index 000000000..4ce733484 --- /dev/null +++ b/crates/feos-core/src/ad/properties/dew_point_pressure.rs @@ -0,0 +1,90 @@ +use super::PropertyAD; +use crate::ad::Gradient; +use crate::{Composition, Contributions, FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, U1}; +use num_dual::{DualNum, DualStruct, Gradients}; +use quantity::{_Pressure, KELVIN, PASCAL, Pressure, Temperature}; + +/// Dew point pressure of a binary mixture as function of temperature and +/// molefracs of the first component. +/// +/// An initial value for the pressure can be passed as optional argument to +/// increase robustness and speed. +pub struct DewPointPressure(pub Temperature, pub f64, pub Option); + +impl<'a> From<&'a [f64]> for DewPointPressure { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * KELVIN, value[1], Some(value[2] * PASCAL)) + } +} + +impl PropertyAD for DewPointPressure +where + DefaultAllocator: Allocator + Allocator + Allocator, + f64: Composition, +{ + type Unit = _Pressure; + const REFERENCE: Pressure = PASCAL; + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> + where + DefaultAllocator: Allocator + Allocator + Allocator, + { + let t = Temperature::from_inner(&self.0); + let p = Option::from_inner(&self.2); + let (y, _) = self.1.into_molefracs(&eos.re())?; + let y = y.map(D::from); + let vle = PhaseEquilibrium::dew_point(eos, t, y, p, None, Default::default())?; + Ok(vle.vapor().pressure(Contributions::Total)) + } + + fn evaluate_gradient>, const P: usize>( + &self, + eos: &E, + ) -> FeosResult, Self::Unit>> + where + DefaultAllocator: Allocator + Allocator + Allocator, + { + let eos_f64 = eos.re(); + let (vapor_molefracs, _) = self.1.into_molefracs(&eos_f64)?; + let vle = PhaseEquilibrium::dew_point( + &eos_f64, + self.0, + &vapor_molefracs, + self.2, + None, + Default::default(), + )?; + + let v_l = 1.0 / vle.liquid().density.to_reduced(); + let v_v = 1.0 / vle.vapor().density.to_reduced(); + let x = &vle.liquid().molefracs; + let t = self.0.into_reduced(); + let (a_l, a_v, v_l, v_v) = { + let t = Gradient::from(t); + let v_l = Gradient::from(v_l); + let v_v = Gradient::from(v_v); + let x = x.map(Gradient::from); + let y = vapor_molefracs.map(Gradient::from); + + let a_l = eos.residual_helmholtz_energy(t, v_l, &x); + let (p_v, mu_res_v, dp_v, dmu_v) = eos.dmu_dv(t, v_v, &y); + let vi_v = dmu_v / dp_v; + let v_v = vi_v.dot(&x); + let a_v = (mu_res_v - vi_v * p_v).dot(&x); + (a_l, a_v, v_l, v_v) + }; + let rho_l = vle.liquid().partial_density().to_reduced(); + let rho_l = [rho_l[0], rho_l[1]]; + let rho_v = vle.vapor().partial_density().to_reduced(); + let rho_v = [rho_v[0], rho_v[1]]; + let p = -(a_l - a_v + + t * (x[0] * (rho_l[0] / rho_v[0]).ln() + x[1] * (rho_l[1] / rho_v[1]).ln() - 1.0)) + / (v_l - v_v); + Ok(Pressure::from_reduced(p)) + } +} diff --git a/crates/feos-core/src/ad/properties/enthalpy_of_vaporization.rs b/crates/feos-core/src/ad/properties/enthalpy_of_vaporization.rs new file mode 100644 index 000000000..ee0d3d3aa --- /dev/null +++ b/crates/feos-core/src/ad/properties/enthalpy_of_vaporization.rs @@ -0,0 +1,34 @@ +use super::PropertyAD; +use crate::{FeosResult, PhaseEquilibrium, Residual}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, U1}; +use num_dual::{DualNum, DualStruct, Gradients}; +use quantity::{_MolarEnergy, KELVIN, MolarEnergy, Temperature}; + +/// Enthalpy of vaporization of a pure component as function of temperature. +pub struct EnthalpyOfVaporization(pub Temperature); + +impl<'a> From<&'a [f64]> for EnthalpyOfVaporization { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * KELVIN) + } +} + +impl PropertyAD for EnthalpyOfVaporization +where + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Unit = _MolarEnergy; + const REFERENCE: MolarEnergy = MolarEnergy::new(1.0); + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> { + let t = Temperature::from_inner(&self.0); + let vle = PhaseEquilibrium::pure(eos, t, None, Default::default())?; + let h_v = vle.vapor().residual_molar_enthalpy(); + let h_l = vle.liquid().residual_molar_enthalpy(); + Ok(h_v - h_l) + } +} diff --git a/crates/feos-core/src/ad/properties/equilibrium_liquid_density.rs b/crates/feos-core/src/ad/properties/equilibrium_liquid_density.rs new file mode 100644 index 000000000..85ded0885 --- /dev/null +++ b/crates/feos-core/src/ad/properties/equilibrium_liquid_density.rs @@ -0,0 +1,31 @@ +use super::PropertyAD; +use crate::{FeosResult, PhaseEquilibrium, Residual}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, U1}; +use num_dual::{DualNum, DualStruct, Gradients}; +use quantity::{_Density, Density, KELVIN, Temperature}; + +/// Equilibrium liquid density of a pure component as function of temperature. +pub struct EquilibriumLiquidDensity(pub Temperature); + +impl<'a> From<&'a [f64]> for EquilibriumLiquidDensity { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * KELVIN) + } +} + +impl PropertyAD for EquilibriumLiquidDensity +where + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Unit = _Density; + const REFERENCE: Density = Density::new(1000.0); + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> { + let t = Temperature::from_inner(&self.0); + PhaseEquilibrium::pure_t(eos, t, None, Default::default()).map(|(_, [_, r])| r) + } +} diff --git a/crates/feos-core/src/ad/properties/liquid_density.rs b/crates/feos-core/src/ad/properties/liquid_density.rs new file mode 100644 index 000000000..f8992c3fe --- /dev/null +++ b/crates/feos-core/src/ad/properties/liquid_density.rs @@ -0,0 +1,35 @@ +use super::PropertyAD; +use crate::DensityInitialization::Liquid; +use crate::density_iteration::density_iteration; +use crate::{FeosResult, Residual}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, Dim}; +use num_dual::{DualNum, DualStruct}; +use quantity::{_Density, Density, KELVIN, PASCAL, Pressure, Temperature}; + +/// Liquid density of a pure component as function of temperature and pressure. +pub struct LiquidDensity(pub Temperature, pub Pressure); + +impl<'a> From<&'a [f64]> for LiquidDensity { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * KELVIN, value[1] * PASCAL) + } +} + +impl PropertyAD for LiquidDensity +where + DefaultAllocator: Allocator, +{ + type Unit = _Density; + const REFERENCE: Density = Density::new(1000.0); + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> { + let x = E::pure_molefracs(); + let t = Temperature::from_inner(&self.0); + let p = Pressure::from_inner(&self.1); + density_iteration(eos, t, p, &x, Some(Liquid)) + } +} diff --git a/crates/feos-core/src/ad/properties/mod.rs b/crates/feos-core/src/ad/properties/mod.rs new file mode 100644 index 000000000..660716e1d --- /dev/null +++ b/crates/feos-core/src/ad/properties/mod.rs @@ -0,0 +1,189 @@ +use super::Gradient; +use crate::{FeosResult, Residual}; +use nalgebra::{DefaultAllocator, Dim, allocator::Allocator}; +#[cfg(feature = "ndarray")] +use ndarray::{Array1, Array2, ArrayView2}; +use num_dual::DualNum; +use quantity::Quantity; + +mod boiling_temperature; +mod bubble_point_pressure; +mod dew_point_pressure; +mod enthalpy_of_vaporization; +mod equilibrium_liquid_density; +mod liquid_density; +mod residual_isobaric_heat_capacity; +mod vapor_pressure; + +pub use boiling_temperature::BoilingTemperature; +pub use bubble_point_pressure::BubblePointPressure; +pub use dew_point_pressure::DewPointPressure; +pub use enthalpy_of_vaporization::EnthalpyOfVaporization; +pub use equilibrium_liquid_density::EquilibriumLiquidDensity; +pub use liquid_density::LiquidDensity; +pub use residual_isobaric_heat_capacity::ResidualIsobaricHeatCapacity; +pub use vapor_pressure::VaporPressure; + +/// Properties that can be rapidly evaluated in parallel together with +/// their gradients with respect to model parameters +pub trait PropertyAD: for<'a> From<&'a [f64]> +where + DefaultAllocator: Allocator, +{ + type Unit; + const REFERENCE: Quantity; + + /// Evaluate the property for an arbitrary derivative. + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult>; + + /// Evaluate the property for the first derivative w.r.t. model parameters. + /// + /// This can be overridden if there is a more performant implementation than + /// the general implementation in `evaluate`. + fn evaluate_gradient>, const P: usize>( + &self, + eos: &E, + ) -> FeosResult, Self::Unit>> { + self.evaluate(eos) + } + + /// Evaluate the property for all inputs in parallel. + /// + /// Return the property values and the success of the calculations. + #[cfg(feature = "ndarray")] + fn evaluate_parallel + Sync>( + eos: &E, + input: ArrayView2, + ) -> (Array1, Array1) { + #[cfg(feature = "rayon")] + let values = ndarray::Zip::from(input.rows()).par_map_collect(|inp| { + let inp = inp.as_slice().expect("Input array is not contiguous!"); + Self::from(inp) + .evaluate(eos) + .map(|d| d.convert_into(Self::REFERENCE)) + }); + + #[cfg(not(feature = "rayon"))] + let values = ndarray::Zip::from(input.rows()).map_collect(|inp| { + let inp = inp.as_slice().expect("Input array is not contiguous!"); + Self::from(inp) + .evaluate(eos) + .map(|d| d.convert_into(Self::REFERENCE)) + }); + + let n = input.nrows(); + let status: Array1 = values.iter().map(|r| r.is_ok()).collect(); + let mut value = Array1::from_elem(n, f64::NAN); + for (i, result) in values.into_iter().enumerate() { + if let Ok(v) = result { + value[i] = v; + } + } + (value, status) + } + + /// Evaluate the property and its gradients for all inputs in parallel. + /// + /// Return the property values, the gradients, and the success of the calculations. + #[cfg(feature = "ndarray")] + fn evaluate_parallel_derivatives, const P: usize>( + parameter_names: [String; P], + parameters: &[f64], + input: ArrayView2, + ) -> (Array1, Array2, Array1) + where + E::Lifted>: Sync, + { + let parameter_names = parameter_names.each_ref().map(|s| s as &str); + let eos = E::seed_derivatives(parameters, parameter_names); + + #[cfg(feature = "rayon")] + let value_dual = ndarray::Zip::from(input.rows()).par_map_collect(|inp| { + let inp = inp.as_slice().expect("Input array is not contiguous!"); + Self::from(inp) + .evaluate_gradient(&eos) + .map(|d| d.convert_into(Self::REFERENCE)) + }); + + #[cfg(not(feature = "rayon"))] + let value_dual = ndarray::Zip::from(input.rows()).map_collect(|inp| { + let inp = inp.as_slice().expect("Input array is not contiguous!"); + Self::from(inp) + .evaluate_gradient(&eos) + .map(|d| d.convert_into(Self::REFERENCE)) + }); + + let n = input.nrows(); + let status = value_dual.iter().map(|p| p.is_ok()).collect(); + let mut value = Array1::from_elem(n, f64::NAN); + let mut grad = Array2::zeros([n, P]); + for (i, result) in value_dual.into_iter().enumerate() { + if let Ok(p_dual) = result { + value[i] = p_dual.re; + let eps = p_dual + .eps + .unwrap_generic(nalgebra::Const::

, nalgebra::U1); + for (g, &e) in grad.row_mut(i).iter_mut().zip(eps.data.0[0].iter()) { + *g = e; + } + } + } + (value, grad, status) + } + + /// Evaluate the property and its gradients for all inputs and parameters in parallel. + /// + /// Return the property values, the gradients, and the success of the calculations. + #[cfg(feature = "ndarray")] + fn evaluate_parallel_derivatives_params, const P: usize>( + parameter_names: [String; P], + parameters: ArrayView2, + input: ArrayView2, + ) -> (Array1, Array2, Array1) { + let parameter_names = parameter_names.each_ref().map(|s| s as &str); + + #[cfg(feature = "rayon")] + let value_dual = ndarray::Zip::from(parameters.rows()) + .and(input.rows()) + .par_map_collect(|par, inp| { + let par = par.as_slice().expect("Parameter array is not contiguous!"); + let inp = inp.as_slice().expect("Input array is not contiguous!"); + let eos = E::seed_derivatives(par, parameter_names); + Self::from(inp) + .evaluate_gradient(&eos) + .map(|d| d.convert_into(Self::REFERENCE)) + }); + + #[cfg(not(feature = "rayon"))] + let value_dual = ndarray::Zip::from(parameters.rows()) + .and(input.rows()) + .map_collect(|par, inp| { + let par = par.as_slice().expect("Parameter array is not contiguous!"); + let inp = inp.as_slice().expect("Input array is not contiguous!"); + let eos = E::seed_derivatives(par, parameter_names); + Self::from(inp) + .evaluate_gradient(&eos) + .map(|d| d.convert_into(Self::REFERENCE)) + }); + + let n = parameters.nrows(); + let status = value_dual.iter().map(|p| p.is_ok()).collect(); + let mut value = Array1::from_elem(n, f64::NAN); + let mut grad = Array2::zeros([n, P]); + for (i, result) in value_dual.into_iter().enumerate() { + if let Ok(p_dual) = result { + value[i] = p_dual.re; + let eps = p_dual + .eps + .unwrap_generic(nalgebra::Const::

, nalgebra::U1); + for (g, &e) in grad.row_mut(i).iter_mut().zip(eps.data.0[0].iter()) { + *g = e; + } + } + } + (value, grad, status) + } +} diff --git a/crates/feos-core/src/ad/properties/residual_isobaric_heat_capacity.rs b/crates/feos-core/src/ad/properties/residual_isobaric_heat_capacity.rs new file mode 100644 index 000000000..b8f962757 --- /dev/null +++ b/crates/feos-core/src/ad/properties/residual_isobaric_heat_capacity.rs @@ -0,0 +1,36 @@ +use super::PropertyAD; +use crate::DensityInitialization::Liquid; +use crate::{FeosResult, Residual, State}; +use nalgebra::DefaultAllocator; +use nalgebra::allocator::Allocator; +use num_dual::{DualNum, DualStruct, Gradients}; +use quantity::{_MolarEntropy, KELVIN, MolarEntropy, PASCAL, Pressure, Temperature}; + +/// Liquid residual isobaric heat capacity of a pure component as function of temperature +/// and pressure. +pub struct ResidualIsobaricHeatCapacity(pub Temperature, pub Pressure); + +impl<'a> From<&'a [f64]> for ResidualIsobaricHeatCapacity { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * KELVIN, value[1] * PASCAL) + } +} + +impl PropertyAD for ResidualIsobaricHeatCapacity +where + DefaultAllocator: Allocator, +{ + type Unit = _MolarEntropy; + const REFERENCE: MolarEntropy = MolarEntropy::new(1.0); + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> { + let x = E::pure_molefracs(); + let t = Temperature::from_inner(&self.0); + let p = Pressure::from_inner(&self.1); + let state = State::new_npt(eos, t, p, x, Some(Liquid))?; + Ok(state.residual_molar_isobaric_heat_capacity()) + } +} diff --git a/crates/feos-core/src/ad/properties/vapor_pressure.rs b/crates/feos-core/src/ad/properties/vapor_pressure.rs new file mode 100644 index 000000000..8192d3ff0 --- /dev/null +++ b/crates/feos-core/src/ad/properties/vapor_pressure.rs @@ -0,0 +1,60 @@ +use super::PropertyAD; +use crate::ad::Gradient; +use crate::{FeosResult, PhaseEquilibrium, ReferenceSystem, Residual}; +use nalgebra::allocator::Allocator; +use nalgebra::{DefaultAllocator, U1}; +use num_dual::{DualNum, DualStruct, Gradients}; +use quantity::{_Pressure, KELVIN, PASCAL, Pressure, Temperature}; + +/// Vapor pressure of a pure component as function of temperature. +pub struct VaporPressure(pub Temperature); + +impl<'a> From<&'a [f64]> for VaporPressure { + fn from(value: &'a [f64]) -> Self { + Self(value[0] * KELVIN) + } +} + +impl PropertyAD for VaporPressure +where + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Unit = _Pressure; + const REFERENCE: Pressure = PASCAL; + + fn evaluate, D: DualNum + Copy>( + &self, + eos: &E, + ) -> FeosResult> { + let t = Temperature::from_inner(&self.0); + PhaseEquilibrium::pure_t(eos, t, None, Default::default()).map(|(p, _)| p) + } + + fn evaluate_gradient>, const P: usize>( + &self, + eos: &E, + ) -> FeosResult>> { + let eos_f64 = eos.re(); + let (_, [vapor_density, liquid_density]) = + PhaseEquilibrium::pure_t(&eos_f64, self.0, None, Default::default())?; + + // implicit differentiation is implemented here instead of just calling pure_t with dual + // numbers, because for the first derivative, we can avoid calculating density derivatives. + let v1 = 1.0 / liquid_density.to_reduced(); + let v2 = 1.0 / vapor_density.to_reduced(); + let t = self.0.into_reduced(); + let (a1, a2) = { + let t = Gradient::from(t); + let v1 = Gradient::from(v1); + let v2 = Gradient::from(v2); + let x = E::pure_molefracs(); + + let a1 = eos.residual_helmholtz_energy(t, v1, &x); + let a2 = eos.residual_helmholtz_energy(t, v2, &x); + (a1, a2) + }; + + let p = -(a1 - a2 + t * (v2 / v1).ln()) / (v1 - v2); + Ok(Pressure::from_reduced(p)) + } +} diff --git a/crates/feos-core/src/density_iteration.rs b/crates/feos-core/src/density_iteration.rs index aaee68ec1..e27ca0fba 100644 --- a/crates/feos-core/src/density_iteration.rs +++ b/crates/feos-core/src/density_iteration.rs @@ -85,10 +85,7 @@ where let t = Dual::from_re(temperature); let x = molefracs.map(Dual::from); let (a_res, da_res) = first_derivative( - |molar_volume| { - eos.lift() - .residual_helmholtz_energy(t, molar_volume, &x) - }, + |molar_volume| eos.lift().residual_helmholtz_energy(t, molar_volume, &x), molar_volume, ); a_res - da_res * molar_volume + temperature * density.ln() diff --git a/crates/feos-core/src/lib.rs b/crates/feos-core/src/lib.rs index cc280cf0a..156894648 100644 --- a/crates/feos-core/src/lib.rs +++ b/crates/feos-core/src/lib.rs @@ -24,7 +24,7 @@ macro_rules! log_result { } } -mod ad; +pub mod ad; pub mod cubic; mod density_iteration; mod equation_of_state; @@ -32,7 +32,6 @@ mod errors; pub mod parameter; mod phase_equilibria; mod state; -pub use ad::{ParametersAD, PropertiesAD}; pub use equation_of_state::{ EntropyScaling, EquationOfState, IdealGas, IdealGasAD, Molarweight, NoResidual, Residual, ResidualDyn, Subset, Total, diff --git a/crates/feos-derive/src/dft.rs b/crates/feos-derive/src/dft.rs index a9ceb4f6a..d7dcd8e63 100644 --- a/crates/feos-derive/src/dft.rs +++ b/crates/feos-derive/src/dft.rs @@ -1,4 +1,4 @@ -use crate::{implement, OPT_IMPLS}; +use crate::{OPT_IMPLS, implement}; use quote::quote; use syn::DeriveInput; diff --git a/crates/feos/benches/README.md b/crates/feos/benches/README.md index b1921cd1a..4d0f3b756 100644 --- a/crates/feos/benches/README.md +++ b/crates/feos/benches/README.md @@ -7,11 +7,18 @@ For example, to run the benchmarks in `dual_numbers`, which uses PC-SAFT, use ``` cargo bench --profile=release-lto --bench=dual_numbers -``` +``` + +The static-vs-dynamic vector dual benchmark uses mimalloc. Run it with LTO via + +``` +cargo bench --profile=release-lto --bench=dual_static_vs_dynamic --features=pcsaft +``` |Name|Description| |--|--| |`dual_numbers`|Helmholtz energy function evaluated using `StateHD` with different dual number types using the PC-SAFT equation of state.| +|`dual_static_vs_dynamic`|PC-SAFT-like pure-component Helmholtz energy expression evaluated with static (`DualSVec64

`) and dynamic (`DualDVec64`) vector dual numbers.| |`dual_numbers_saftvrmie`|Helmholtz energy function evaluated using `StateHD` with different dual number types using the SAFT-VR-Mie equation of state.| |`state_properties`|Properties of `State`. Including state creation using the natural variables of the Helmholtz energy (no density iteration).| |`state_creation`|Different constructors of `State` and `PhaseEquilibrium` including critical point calculations. For pure substances and mixtures.| diff --git a/crates/feos/src/gc_pcsaft/dft/mod.rs b/crates/feos/src/gc_pcsaft/dft/mod.rs index 5005fa315..18ba4c793 100644 --- a/crates/feos/src/gc_pcsaft/dft/mod.rs +++ b/crates/feos/src/gc_pcsaft/dft/mod.rs @@ -1,6 +1,6 @@ use super::eos::GcPcSaftOptions; use super::record::GcPcSaftAssociationRecord; -use crate::association::{Association, YuWuAssociationFunctional, AssociationStrength}; +use crate::association::{Association, AssociationStrength, YuWuAssociationFunctional}; use crate::gc_pcsaft::GcPcSaftParameters; use crate::hard_sphere::{FMTContribution, FMTVersion, HardSphereProperties, MonomerShape}; use feos_core::{FeosResult, Molarweight, ResidualDyn, StateHD, Subset}; @@ -108,7 +108,8 @@ impl HelmholtzEnergyFunctionalDyn for GcPcSaftFunctional { fn contributions<'a>(&'a self) -> impl Iterator> { let mut contributions = Vec::with_capacity(4); - let assoc = YuWuAssociationFunctional::new(&self.params, &self.parameters, self.association); + let assoc = + YuWuAssociationFunctional::new(&self.params, &self.parameters, self.association); // Hard sphere contribution let hs = FMTContribution::new(&self.params, self.fmt_version); diff --git a/crates/feos/src/pcsaft/dft/mod.rs b/crates/feos/src/pcsaft/dft/mod.rs index a7f6b4566..b3afa5435 100644 --- a/crates/feos/src/pcsaft/dft/mod.rs +++ b/crates/feos/src/pcsaft/dft/mod.rs @@ -103,7 +103,8 @@ impl HelmholtzEnergyFunctionalDyn for PcSaftFunctional { fn contributions<'a>(&'a self) -> impl Iterator> { let mut contributions = Vec::with_capacity(4); - let assoc = YuWuAssociationFunctional::new(&self.params, &self.parameters, self.association); + let assoc = + YuWuAssociationFunctional::new(&self.params, &self.parameters, self.association); if matches!( self.fmt_version, diff --git a/crates/feos/src/pcsaft/eos/mod.rs b/crates/feos/src/pcsaft/eos/mod.rs index 37a885248..6a68f67f7 100644 --- a/crates/feos/src/pcsaft/eos/mod.rs +++ b/crates/feos/src/pcsaft/eos/mod.rs @@ -598,12 +598,25 @@ mod tests_parameter_fit { use super::*; use approx::assert_relative_eq; use feos_core::DensityInitialization::Liquid; - use feos_core::{Contributions, PropertiesAD, ReferenceSystem, SolverOptions}; - use feos_core::{FeosResult, ParametersAD, PhaseEquilibrium, State}; - use nalgebra::{U1, U3, U8, vector}; + use feos_core::ad::{ + BoilingTemperature, BubblePointPressure, DewPointPressure, EquilibriumLiquidDensity, + LiquidDensity, PropertyAD, VaporPressure, + }; + use feos_core::{Contributions, ReferenceSystem, SolverOptions}; + use feos_core::{FeosResult, PhaseEquilibrium, State, ad::ParametersAD}; + use nalgebra::{U1, U3, U8}; use num_dual::{Dual64, DualStruct, DualVec, partial}; use quantity::{BAR, KELVIN, LITER, MOL, PASCAL}; + fn flat_binary_params(b: &PcSaftBinary) -> Vec { + b.0.0 + .iter() + .flatten() + .copied() + .chain(std::iter::once(b.0.1)) + .collect() + } + fn pcsaft_non_assoc() -> PcSaftPure { let m = 1.5; let sigma = 3.4; @@ -626,9 +639,9 @@ mod tests_parameter_fit { "nb", ]; let (pcsaft, _) = pcsaft()?; - let pcsaft_ad = pcsaft.named_derivatives(pcsaft_params); + let pcsaft_ad = PcSaftPure::::seed_derivatives(&pcsaft.0, pcsaft_params); let temperature = 250.0 * KELVIN; - let p = pcsaft_ad.vapor_pressure(temperature)?; + let p = VaporPressure(temperature).evaluate(&pcsaft_ad)?; let p = p.convert_into(PASCAL); let (p, grad) = (p.re, p.eps.unwrap_generic(U8, U1)); @@ -658,9 +671,10 @@ mod tests_parameter_fit { #[test] fn test_vapor_pressure_derivatives_fit() -> FeosResult<()> { let pcsaft = pcsaft_non_assoc(); - let pcsaft_ad = pcsaft.named_derivatives(["m", "sigma", "epsilon_k"]); + let pcsaft_ad = + PcSaftPure::::seed_derivatives(&pcsaft.0, ["m", "sigma", "epsilon_k"]); let temperature = 150.0 * KELVIN; - let p = pcsaft_ad.vapor_pressure(temperature)?; + let p = VaporPressure(temperature).evaluate(&pcsaft_ad)?; let p = p.convert_into(PASCAL); let (p, grad) = (p.re, p.eps.unwrap_generic(U3, U1)); @@ -690,9 +704,10 @@ mod tests_parameter_fit { #[test] fn test_boiling_temperature_derivatives_fit() -> FeosResult<()> { let pcsaft = pcsaft_non_assoc(); - let pcsaft_ad = pcsaft.named_derivatives(["m", "sigma", "epsilon_k"]); + let pcsaft_ad = + PcSaftPure::::seed_derivatives(&pcsaft.0, ["m", "sigma", "epsilon_k"]); let pressure = BAR; - let t = pcsaft_ad.boiling_temperature(pressure)?; + let t = BoilingTemperature(pressure).evaluate(&pcsaft_ad)?; let t = t.convert_into(KELVIN); let (t, grad) = (t.re, t.eps.unwrap_generic(U3, U1)); @@ -734,16 +749,14 @@ mod tests_parameter_fit { #[test] fn test_equilibrium_liquid_density_derivatives_fit() -> FeosResult<()> { let pcsaft = pcsaft_non_assoc(); - let pcsaft_ad = pcsaft.named_derivatives(["m", "sigma", "epsilon_k"]); + let pcsaft_ad = + PcSaftPure::::seed_derivatives(&pcsaft.0, ["m", "sigma", "epsilon_k"]); let temperature = 150.0 * KELVIN; - let (p, rho) = pcsaft_ad.equilibrium_liquid_density(temperature)?; - let p = p.convert_into(PASCAL); + let rho = EquilibriumLiquidDensity(temperature).evaluate(&pcsaft_ad)?; let rho = rho.convert_into(MOL / LITER); - let (p, p_grad) = (p.re, p.eps.unwrap_generic(U3, U1)); let (rho, rho_grad) = (rho.re, rho.eps.unwrap_generic(U3, U1)); - println!("{p:.5} {rho:.5}"); - println!("{p_grad:.5?}"); + println!("{rho:.5}"); println!("{rho_grad:.5?}"); for (i, par) in ["m", "sigma", "epsilon_k"].into_iter().enumerate() { @@ -751,22 +764,17 @@ mod tests_parameter_fit { let h = params[i] * 1e-7; params[i] += h; let pcsaft_h = PcSaftPure(params); - let (p_h, [_, rho_h]) = + let (_, [_, rho_h]) = PhaseEquilibrium::pure_t(&pcsaft_h, temperature, None, Default::default())?; - let dp_h = (p_h.convert_into(PASCAL) - p) / h; let drho_h = (rho_h.convert_into(MOL / LITER) - rho) / h; - let dp = p_grad[i]; let drho = rho_grad[i]; println!( - "{par:12}: {:11.5} {:11.5} {:.3e} {:11.5} {:11.5} {:.3e}", - dp_h, - dp, - ((dp_h - dp) / dp).abs(), + "{par:12}: {:11.5} {:11.5} {:.3e}", drho_h, drho, ((drho_h - drho) / drho).abs() ); - assert_relative_eq!(dp, dp_h, max_relative = 1e-6); + assert_relative_eq!(drho, drho_h, max_relative = 1e-6); } Ok(()) } @@ -774,10 +782,11 @@ mod tests_parameter_fit { #[test] fn test_liquid_density_derivatives_fit() -> FeosResult<()> { let pcsaft = pcsaft_non_assoc(); - let pcsaft_ad = pcsaft.named_derivatives(["m", "sigma", "epsilon_k"]); + let pcsaft_ad = + PcSaftPure::::seed_derivatives(&pcsaft.0, ["m", "sigma", "epsilon_k"]); let temperature = 150.0 * KELVIN; let pressure = BAR; - let rho = pcsaft_ad.liquid_density(temperature, pressure)?; + let rho = LiquidDensity(temperature, pressure).evaluate(&pcsaft_ad)?; let rho = rho.convert_into(MOL / LITER); let (rho, grad) = (rho.re, rho.eps.unwrap_generic(U3, U1)); @@ -806,10 +815,11 @@ mod tests_parameter_fit { #[test] fn test_bubble_point_pressure() -> FeosResult<()> { let (pcsaft, _) = pcsaft_binary()?; - let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let pcsaft_ad = + PcSaftBinary::::seed_derivatives(&flat_binary_params(&pcsaft), ["k_ij"]); let temperature = 500.0 * KELVIN; - let x = vector![0.5, 0.5]; - let p = pcsaft_ad.bubble_point_pressure(temperature, None, x)?; + let x = 0.5; + let p = BubblePointPressure(temperature, x, None).evaluate(&pcsaft_ad)?; let p = p.convert_into(BAR); let (p, [[grad]]) = (p.re, p.eps.unwrap_generic(U1, U1).data.0); @@ -844,10 +854,11 @@ mod tests_parameter_fit { #[test] fn test_dew_point_pressure() -> FeosResult<()> { let (pcsaft, _) = pcsaft_binary()?; - let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let pcsaft_ad = + PcSaftBinary::::seed_derivatives(&flat_binary_params(&pcsaft), ["k_ij"]); let temperature = 500.0 * KELVIN; let y = 0.5; - let p = pcsaft_ad.dew_point_pressure(temperature, None, y)?; + let p = DewPointPressure(temperature, y, None).evaluate(&pcsaft_ad)?; let p = p.convert_into(BAR); let (p, [[grad]]) = (p.re, p.eps.unwrap_generic(U1, U1).data.0); @@ -876,7 +887,8 @@ mod tests_parameter_fit { #[test] fn test_bubble_point_temperature() -> FeosResult<()> { let (pcsaft, _) = pcsaft_binary()?; - let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let pcsaft_ad = + PcSaftBinary::::seed_derivatives(&flat_binary_params(&pcsaft), ["k_ij"]); let pressure = Pressure::from_reduced(DualVec::from(45. * BAR.into_reduced())); let t_init = Temperature::from_reduced(DualVec::from(500.0)); let x = DualVec::from(0.5); @@ -924,7 +936,8 @@ mod tests_parameter_fit { #[test] fn test_dew_point_temperature() -> FeosResult<()> { let (pcsaft, _) = pcsaft_binary()?; - let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let pcsaft_ad = + PcSaftBinary::::seed_derivatives(&flat_binary_params(&pcsaft), ["k_ij"]); let pressure = Pressure::from_reduced(DualVec::from(45. * BAR.into_reduced())); let t_init = Temperature::from_reduced(DualVec::from(500.0)); let x = DualVec::from(0.5); @@ -1060,7 +1073,11 @@ mod tests_parameter_fit { #[test] fn test_tp_flash() -> FeosResult<()> { let (pcsaft, _) = pcsaft_binary()?; - let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let (params, mut kij) = pcsaft.0; + let mut flat_params: Vec = params[0].to_vec(); + flat_params.extend_from_slice(¶ms[1]); + flat_params.push(kij); + let pcsaft_ad = PcSaftBinary::::seed_derivatives(&flat_params, ["k_ij"]); let temperature = 500.0 * KELVIN; let pressure = 44.6 * BAR; let x = 0.5; @@ -1080,8 +1097,6 @@ mod tests_parameter_fit { println!("{beta:.5}"); println!("{grad:.5?}"); - - let (params, mut kij) = pcsaft.0; let h = 1e-7; kij += h; let pcsaft_h = PcSaftBinary::new(params, kij); diff --git a/crates/feos/src/pcsaft/eos/pcsaft_binary.rs b/crates/feos/src/pcsaft/eos/pcsaft_binary.rs index 71b15ae88..160b3bd27 100644 --- a/crates/feos/src/pcsaft/eos/pcsaft_binary.rs +++ b/crates/feos/src/pcsaft/eos/pcsaft_binary.rs @@ -1,8 +1,8 @@ use super::dispersion::{A0, A1, A2, B0, B1, B2}; use super::polar::{AD, BD, CD}; -use feos_core::{ParametersAD, Residual, StateHD}; +use feos_core::{Residual, StateHD, ad::ParametersAD}; use nalgebra::{SVector, U2}; -use num_dual::{DualNum, DualSVec64, DualVec, jacobian}; +use num_dual::{DualNum, DualVec, jacobian}; use std::f64::consts::{FRAC_PI_6, PI}; const PI_SQ_43: f64 = 4.0 / 3.0 * PI * PI; @@ -19,45 +19,59 @@ impl PcSaftBinary { } } -impl + Copy, const N: usize> From<&[f64]> for PcSaftBinary { - fn from(parameters: &[f64]) -> Self { - if parameters.len() != 2 * N + 1 { - panic!( - "This version of PC-SAFT requires exactly {} parameters!", - 2 * N + 1 - ) - } - let (Ok(p1), Ok(p2)): (Result<[f64; N], _>, Result<[f64; N], _>) = - (parameters[..N].try_into(), parameters[N..2 * N].try_into()) - else { - unreachable!() - }; - let kij = D::from(parameters[2 * N]); - Self::new([p1.map(D::from), p2.map(D::from)], kij) - } -} - -impl ParametersAD<2> for PcSaftBinary { - fn index_parameters_mut<'a, const P: usize>( - eos: &'a mut Self::Lifted>, - index: &str, - ) -> &'a mut DualSVec64

{ - match index { - "k_ij" => &mut eos.0.1, - _ => panic!("{index} is not a valid binary PC-SAFT parameter!"), - } +impl ParametersAD for PcSaftBinary { + fn build + Copy>( + mut f: impl FnMut(&'static str, bool) -> D, + ) -> PcSaftBinary { + PcSaftBinary::new( + [ + [ + f("m1", true), + f("sigma1", true), + f("epsilon_k1", true), + f("mu1", true), + ], + [ + f("m2", true), + f("sigma2", true), + f("epsilon_k2", true), + f("mu2", true), + ], + ], + f("k_ij", true), + ) } } -impl ParametersAD<2> for PcSaftBinary { - fn index_parameters_mut<'a, const P: usize>( - eos: &'a mut Self::Lifted>, - index: &str, - ) -> &'a mut DualSVec64

{ - match index { - "k_ij" => &mut eos.0.1, - _ => panic!("{index} is not a valid binary PC-SAFT parameter!"), - } +impl ParametersAD for PcSaftBinary { + fn build + Copy>( + mut f: impl FnMut(&'static str, bool) -> D, + ) -> PcSaftBinary { + PcSaftBinary::new( + [ + [ + f("m1", true), + f("sigma1", true), + f("epsilon_k1", true), + f("mu1", true), + f("kappa_ab1", true), + f("epsilon_k_ab1", true), + f("na1", false), + f("nb1", false), + ], + [ + f("m2", true), + f("sigma2", true), + f("epsilon_k2", true), + f("mu2", true), + f("kappa_ab2", true), + f("epsilon_k_ab2", true), + f("na2", false), + f("nb2", false), + ], + ], + f("k_ij", true), + ) } } diff --git a/crates/feos/src/pcsaft/eos/pcsaft_pure.rs b/crates/feos/src/pcsaft/eos/pcsaft_pure.rs index f9ea2983f..46155f4cf 100644 --- a/crates/feos/src/pcsaft/eos/pcsaft_pure.rs +++ b/crates/feos/src/pcsaft/eos/pcsaft_pure.rs @@ -1,8 +1,8 @@ use super::dispersion::{A0, A1, A2, B0, B1, B2}; use super::polar::{AD, BD, CD}; -use feos_core::{ParametersAD, Residual, StateHD}; +use feos_core::{Residual, StateHD, ad::ParametersAD}; use nalgebra::{SVector, U1}; -use num_dual::{DualNum, DualSVec64}; +use num_dual::DualNum; use std::f64::consts::{FRAC_PI_6, PI}; const PI_SQ_43: f64 = 4.0 / 3.0 * PI * PI; @@ -183,46 +183,33 @@ impl + Copy> Residual for PcSaftPure { } } -impl + Copy, const N: usize> From<&[f64]> for PcSaftPure { - fn from(parameters: &[f64]) -> Self { - let Ok(parameters): Result<[f64; N], _> = parameters.try_into() else { - panic!("This version of PC-SAFT requires exactly {N} parameters!") - }; - Self(parameters.map(D::from)) +impl ParametersAD for PcSaftPure { + fn build + Copy>( + mut f: impl FnMut(&'static str, bool) -> D, + ) -> PcSaftPure { + PcSaftPure([ + f("m", true), + f("sigma", true), + f("epsilon_k", true), + f("mu", true), + ]) } } -impl ParametersAD<1> for PcSaftPure { - fn index_parameters_mut<'a, const P: usize>( - eos: &'a mut Self::Lifted>, - index: &str, - ) -> &'a mut DualSVec64

{ - match index { - "m" => &mut eos.0[0], - "sigma" => &mut eos.0[1], - "epsilon_k" => &mut eos.0[2], - "mu" => &mut eos.0[3], - _ => panic!("{index} is not a valid PC-SAFT parameter!"), - } - } -} - -impl ParametersAD<1> for PcSaftPure { - fn index_parameters_mut<'a, const P: usize>( - eos: &'a mut Self::Lifted>, - index: &str, - ) -> &'a mut DualSVec64

{ - match index { - "m" => &mut eos.0[0], - "sigma" => &mut eos.0[1], - "epsilon_k" => &mut eos.0[2], - "mu" => &mut eos.0[3], - "kappa_ab" => &mut eos.0[4], - "epsilon_k_ab" => &mut eos.0[5], - "na" => &mut eos.0[6], - "nb" => &mut eos.0[7], - _ => panic!("{index} is not a valid PC-SAFT parameter!"), - } +impl ParametersAD for PcSaftPure { + fn build + Copy>( + mut f: impl FnMut(&'static str, bool) -> D, + ) -> PcSaftPure { + PcSaftPure([ + f("m", true), + f("sigma", true), + f("epsilon_k", true), + f("mu", true), + f("kappa_ab", true), + f("epsilon_k_ab", true), + f("na", false), + f("nb", false), + ]) } } diff --git a/crates/feos/tests/pcsaft/px_flashes.rs b/crates/feos/tests/pcsaft/px_flashes.rs index 4cfb179b6..3f102a5e5 100644 --- a/crates/feos/tests/pcsaft/px_flashes.rs +++ b/crates/feos/tests/pcsaft/px_flashes.rs @@ -2,8 +2,8 @@ use approx::assert_relative_eq; use feos::ideal_gas::Joback; use feos::pcsaft::PcSaftBinary; use feos_core::{ - Contributions, EquationOfState, FeosResult, IdealGasAD, ParametersAD, PhaseEquilibrium, - ReferenceSystem, SolverOptions, Verbosity, + Contributions, EquationOfState, FeosResult, IdealGasAD, PhaseEquilibrium, ReferenceSystem, + SolverOptions, Verbosity, ad::ParametersAD, }; use nalgebra::U1; use num_dual::{DualStruct, DualVec}; @@ -38,7 +38,10 @@ fn test_ph_flash() -> FeosResult<()> { println!("{h}\n{}", vle.molar_enthalpy()); assert_relative_eq!(h, vle.molar_enthalpy(), max_relative = 1e-10); - let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let mut flat_params: Vec = params[0].to_vec(); + flat_params.extend_from_slice(¶ms[1]); + flat_params.push(kij); + let pcsaft_ad = PcSaftBinary::::seed_derivatives(&flat_params, ["k_ij"]); let joback_ad = joback.each_ref().map(|j| j.lift()); let eos_ad = EquationOfState::new(joback_ad, pcsaft_ad); let vle_ad = PhaseEquilibrium::ph_flash( @@ -105,7 +108,10 @@ fn test_ps_flash() -> FeosResult<()> { println!("{s}\n{}", vle.molar_entropy()); assert_relative_eq!(s, vle.molar_entropy(), max_relative = 1e-10); - let pcsaft_ad = pcsaft.named_derivatives(["k_ij"]); + let mut flat_params: Vec = params[0].to_vec(); + flat_params.extend_from_slice(¶ms[1]); + flat_params.push(kij); + let pcsaft_ad = PcSaftBinary::::seed_derivatives(&flat_params, ["k_ij"]); let joback_ad = joback.each_ref().map(|j| j.lift()); let eos_ad = EquationOfState::new(joback_ad, pcsaft_ad); let vle_ad = PhaseEquilibrium::ps_flash( diff --git a/py-feos/src/ad/dataset.rs b/py-feos/src/ad/dataset.rs new file mode 100644 index 000000000..27022c58f --- /dev/null +++ b/py-feos/src/ad/dataset.rs @@ -0,0 +1,551 @@ +use feos_core::ad::{ + BinaryDataset, BinaryProperty, BubblePointRecord, Dataset, DewPointRecord, + EnthalpyOfVaporizationRecord, EquilibriumLiquidDensityRecord, LiquidDensityRecord, PureDataset, + PureProperty, ResidualIsobaricHeatCapacityRecord, VaporPressureRecord, +}; +use ndarray::{Array2, ArrayView1}; +use numpy::{PyArray1, PyArray2, PyReadonlyArray1, ToPyArray}; +use pyo3::exceptions::{PyTypeError, PyValueError}; +use pyo3::prelude::*; + +use crate::eos::PyEquationOfState; + +/// Run a `Dataset::evaluate` against a single equation of state or a list of them. +/// +/// If `eos` extracts as a single `PyEquationOfState`, returns the +/// `(predicted, converged)` arrays as 1D. If it extracts as a sequence, +/// returns them stacked as 2D arrays with shape `[n_points, n_eos]`. +fn evaluate_eos<'py, D: Dataset>( + py: Python<'py>, + dataset: &D, + eos: &Bound<'py, PyAny>, +) -> PyResult<(Bound<'py, PyAny>, Bound<'py, PyAny>)> { + // Evaluate a single EoS. + if let Ok(e) = eos.extract::>() { + let (pred, status) = dataset.evaluate(&e.0); + return Ok(( + pred.to_pyarray(py).into_any(), + status.to_pyarray(py).into_any(), + )); + } + + // Construct a references to multiple EoSs. + let eos_refs: Vec> = eos.extract().map_err(|_| { + PyTypeError::new_err( + "expected an EquationOfState or a sequence of EquationOfState instances", + ) + })?; + + let n_points = dataset.target().len(); + let n_eos = eos_refs.len(); + let mut pred = Array2::::from_elem((n_points, n_eos), f64::NAN); + let mut status = Array2::::from_elem((n_points, n_eos), false); + + // Iterate through models > evaluate dataset > collect results as columns. + // Column index is EoS-index. + for (j, e) in eos_refs.iter().enumerate() { + let (p, c) = dataset.evaluate(&e.0); + pred.column_mut(j).assign(&p); + status.column_mut(j).assign(&c); + } + Ok(( + pred.to_pyarray(py).into_any(), + status.to_pyarray(py).into_any(), + )) +} + +fn ensure_same_len(arrays: &[(&str, usize)]) -> PyResult { + let Some((_, n)) = arrays.first() else { + return Ok(0); + }; + if arrays.iter().any(|(_, len)| len != n) { + let names = arrays + .iter() + .map(|(name, _)| *name) + .collect::>() + .join(", "); + return Err(PyValueError::new_err(format!( + "all arrays must have the same length: {names}" + ))); + } + Ok(*n) +} + +fn collect_records_2( + a: (&str, ArrayView1<'_, f64>), + b: (&str, ArrayView1<'_, f64>), + f: impl Fn(f64, f64) -> R, +) -> PyResult> { + ensure_same_len(&[(a.0, a.1.len()), (b.0, b.1.len())])?; + Ok(a.1.iter().zip(b.1.iter()).map(|(&a, &b)| f(a, b)).collect()) +} + +fn collect_records_3( + a: (&str, ArrayView1<'_, f64>), + b: (&str, ArrayView1<'_, f64>), + c: (&str, ArrayView1<'_, f64>), + f: impl Fn(f64, f64, f64) -> R, +) -> PyResult> { + ensure_same_len(&[(a.0, a.1.len()), (b.0, b.1.len()), (c.0, c.1.len())])?; + Ok(a.1 + .iter() + .zip(b.1.iter()) + .zip(c.1.iter()) + .map(|((&a, &b), &c)| f(a, b, c)) + .collect()) +} + +// Parser methods: map from str to enum +// Preferred here to declutter enums/structs exported in feos. + +fn parse_pure_property(property: &str) -> PyResult { + match property { + "vapor_pressure" => Ok(PureProperty::VaporPressure), + "liquid_density" => Ok(PureProperty::LiquidDensity), + "equilibrium_liquid_density" => Ok(PureProperty::EquilibriumLiquidDensity), + "enthalpy_of_vaporization" => Ok(PureProperty::EnthalpyOfVaporization), + "residual_isobaric_heat_capacity" => Ok(PureProperty::ResidualIsobaricHeatCapacity), + _ => Err(PyValueError::new_err(format!( + "unknown pure property '{property}'; valid: \ + 'vapor_pressure', 'liquid_density', 'equilibrium_liquid_density', \ + 'enthalpy_of_vaporization', 'residual_isobaric_heat_capacity'" + ))), + } +} + +fn parse_binary_property(property: &str) -> PyResult { + match property { + "bubble_point_pressure" | "bubble_point" => Ok(BinaryProperty::BubblePointPressure), + "dew_point_pressure" | "dew_point" => Ok(BinaryProperty::DewPointPressure), + _ => Err(PyValueError::new_err(format!( + "unknown binary property '{property}'; valid: \ + 'bubble_point_pressure', 'dew_point_pressure'" + ))), + } +} + +#[pyclass(name = "PureDataset")] +pub struct PyPureDataset { + pub(crate) inner: PureDataset, +} + +#[pymethods] +impl PyPureDataset { + /// Load pure-component data from CSV. + /// + /// Args: + /// path (str): Path to the CSV file. + /// property (str): Property identifier. Valid values are + /// ``"vapor_pressure"``, ``"liquid_density"``, + /// ``"equilibrium_liquid_density"``, ``"enthalpy_of_vaporization"``, + /// and ``"residual_isobaric_heat_capacity"``. + /// name (str, optional): Dataset name used in regressor diagnostics. + #[staticmethod] + #[pyo3(signature = (path, property, name=None))] + pub fn from_csv(path: &str, property: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(parse_pure_property(property)?, path, name) + } + + /// Load vapor pressure data from CSV. + /// + /// CSV columns: ``temperature_k, vapor_pressure_pa``. + #[staticmethod] + #[pyo3(signature = (path, name=None))] + pub fn vapor_pressure_from_csv(path: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(PureProperty::VaporPressure, path, name) + } + + /// Construct vapor pressure data from numpy arrays. + #[staticmethod] + #[pyo3(signature = (temperature_k, vapor_pressure_pa, name=None))] + pub fn vapor_pressure( + temperature_k: PyReadonlyArray1, + vapor_pressure_pa: PyReadonlyArray1, + name: Option<&str>, + ) -> PyResult { + let temperature_k = temperature_k.as_array(); + let vapor_pressure_pa = vapor_pressure_pa.as_array(); + let records = collect_records_2( + ("temperature_k", temperature_k), + ("vapor_pressure_pa", vapor_pressure_pa), + |temperature_k, vapor_pressure_pa| VaporPressureRecord { + temperature_k, + vapor_pressure_pa, + }, + )?; + Ok(Self::with_optional_name( + PureDataset::vapor_pressure(records), + name, + )) + } + + /// Load liquid density data from CSV. + /// + /// CSV columns: ``temperature_k, pressure_pa, liquid_density_kmol_m3``. + #[staticmethod] + #[pyo3(signature = (path, name=None))] + pub fn liquid_density_from_csv(path: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(PureProperty::LiquidDensity, path, name) + } + + /// Construct liquid density data from numpy arrays. + #[staticmethod] + #[pyo3(signature = (temperature_k, pressure_pa, liquid_density_kmol_m3, name=None))] + pub fn liquid_density( + temperature_k: PyReadonlyArray1, + pressure_pa: PyReadonlyArray1, + liquid_density_kmol_m3: PyReadonlyArray1, + name: Option<&str>, + ) -> PyResult { + let temperature_k = temperature_k.as_array(); + let pressure_pa = pressure_pa.as_array(); + let liquid_density_kmol_m3 = liquid_density_kmol_m3.as_array(); + let records = collect_records_3( + ("temperature_k", temperature_k), + ("pressure_pa", pressure_pa), + ("liquid_density_kmol_m3", liquid_density_kmol_m3), + |temperature_k, pressure_pa, liquid_density_kmol_m3| LiquidDensityRecord { + temperature_k, + pressure_pa, + liquid_density_kmol_m3, + }, + )?; + Ok(Self::with_optional_name( + PureDataset::liquid_density(records), + name, + )) + } + + /// Load saturated liquid density data from CSV. + /// + /// CSV columns: ``temperature_k, liquid_density_kmol_m3``. + #[staticmethod] + #[pyo3(signature = (path, name=None))] + pub fn equilibrium_liquid_density_from_csv(path: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(PureProperty::EquilibriumLiquidDensity, path, name) + } + + /// Construct saturated liquid density data from numpy arrays. + #[staticmethod] + #[pyo3(signature = (temperature_k, liquid_density_kmol_m3, name=None))] + pub fn equilibrium_liquid_density( + temperature_k: PyReadonlyArray1, + liquid_density_kmol_m3: PyReadonlyArray1, + name: Option<&str>, + ) -> PyResult { + let temperature_k = temperature_k.as_array(); + let liquid_density_kmol_m3 = liquid_density_kmol_m3.as_array(); + let records = collect_records_2( + ("temperature_k", temperature_k), + ("liquid_density_kmol_m3", liquid_density_kmol_m3), + |temperature_k, liquid_density_kmol_m3| EquilibriumLiquidDensityRecord { + temperature_k, + liquid_density_kmol_m3, + }, + )?; + Ok(Self::with_optional_name( + PureDataset::equilibrium_liquid_density(records), + name, + )) + } + + /// Load enthalpy of vaporization data from CSV. + /// + /// CSV columns: ``temperature_k, dh_vap_j_mol``. + #[staticmethod] + #[pyo3(signature = (path, name=None))] + pub fn enthalpy_of_vaporization_from_csv(path: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(PureProperty::EnthalpyOfVaporization, path, name) + } + + /// Construct enthalpy of vaporization data from numpy arrays. + #[staticmethod] + #[pyo3(signature = (temperature_k, dh_vap_j_mol, name=None))] + pub fn enthalpy_of_vaporization( + temperature_k: PyReadonlyArray1, + dh_vap_j_mol: PyReadonlyArray1, + name: Option<&str>, + ) -> PyResult { + let temperature_k = temperature_k.as_array(); + let dh_vap_j_mol = dh_vap_j_mol.as_array(); + let records = collect_records_2( + ("temperature_k", temperature_k), + ("dh_vap_j_mol", dh_vap_j_mol), + |temperature_k, dh_vap_j_mol| EnthalpyOfVaporizationRecord { + temperature_k, + dh_vap_j_mol, + }, + )?; + Ok(Self::with_optional_name( + PureDataset::enthalpy_of_vaporization(records), + name, + )) + } + + /// Load residual isobaric heat capacity data from CSV. + /// + /// CSV columns: ``temperature_k, pressure_pa, cp_res_j_molk``. + #[staticmethod] + #[pyo3(signature = (path, name=None))] + pub fn residual_isobaric_heat_capacity_from_csv( + path: &str, + name: Option<&str>, + ) -> PyResult { + Self::from_csv_for_property(PureProperty::ResidualIsobaricHeatCapacity, path, name) + } + + /// Construct residual isobaric heat capacity data from numpy arrays. + #[staticmethod] + #[pyo3(signature = (temperature_k, pressure_pa, cp_res_j_molk, name=None))] + pub fn residual_isobaric_heat_capacity( + temperature_k: PyReadonlyArray1, + pressure_pa: PyReadonlyArray1, + cp_res_j_molk: PyReadonlyArray1, + name: Option<&str>, + ) -> PyResult { + let temperature_k = temperature_k.as_array(); + let pressure_pa = pressure_pa.as_array(); + let cp_res_j_molk = cp_res_j_molk.as_array(); + let records = collect_records_3( + ("temperature_k", temperature_k), + ("pressure_pa", pressure_pa), + ("cp_res_j_molk", cp_res_j_molk), + |temperature_k, pressure_pa, cp_res_j_molk| ResidualIsobaricHeatCapacityRecord { + temperature_k, + pressure_pa, + cp_res_j_molk, + }, + )?; + Ok(Self::with_optional_name( + PureDataset::residual_isobaric_heat_capacity(records), + name, + )) + } + + /// Property name. + #[getter] + pub fn name(&self) -> &str { + self.inner.name() + } + + /// Number of data points. + pub fn __len__(&self) -> usize { + self.inner.target().len() + } + + /// Target values. + pub fn target<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> { + self.inner.target().to_owned().to_pyarray(py) + } + + /// Input values. + pub fn inputs<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray2> { + self.inner.inputs().to_owned().to_pyarray(py) + } + + /// Evaluate the dataset's property for one or more equations of state. + /// + /// Args: + /// eos: A single ``EquationOfState`` or a list of them. + /// Each must describe a single substance. + /// + /// Returns: + /// ``(predicted, converged)``. For a single EoS, both are 1D arrays + /// of length ``n_points``. For a list of ``n_eos`` EoS, both are + /// 2D arrays of shape ``[n_points, n_eos]``; column ``k`` + /// corresponds to ``eos[k]``. Non-converged points are reported as + /// ``NaN`` in ``predicted`` and ``False`` in ``converged``. + pub fn evaluate<'py>( + &self, + py: Python<'py>, + eos: &Bound<'py, PyAny>, + ) -> PyResult<(Bound<'py, PyAny>, Bound<'py, PyAny>)> { + evaluate_eos(py, &self.inner, eos) + } + + pub fn __repr__(&self) -> String { + format!( + "PureDataset(property={}, n={})", + self.inner.name(), + self.inner.target().len() + ) + } +} + +impl PyPureDataset { + fn from_csv_for_property( + property: PureProperty, + path: &str, + name: Option<&str>, + ) -> PyResult { + PureDataset::from_csv(property, std::path::Path::new(path)) + .map(|inner| Self::with_optional_name(inner, name)) + .map_err(|e| PyValueError::new_err(e.to_string())) + } + + fn with_optional_name(mut inner: PureDataset, name: Option<&str>) -> Self { + if let Some(n) = name { + inner = inner.with_name(n); + } + Self { inner } + } +} + +#[pyclass(name = "BinaryDataset")] +pub struct PyBinaryDataset { + pub(crate) inner: BinaryDataset, +} + +#[pymethods] +impl PyBinaryDataset { + /// Load binary-mixture data from CSV. + /// + /// Args: + /// path (str): Path to the CSV file. + /// property (str): Property identifier. Valid values are + /// ``"bubble_point_pressure"`` and ``"dew_point_pressure"``. + /// Short aliases ``"bubble_point"`` and ``"dew_point"`` are also accepted. + /// name (str, optional): Dataset name used in regressor diagnostics. + #[staticmethod] + #[pyo3(signature = (path, property, name=None))] + pub fn from_csv(path: &str, property: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(parse_binary_property(property)?, path, name) + } + + /// Load bubble point pressure data from CSV. + /// + /// CSV columns: ``temperature_k, liquid_molefrac_1, bubble_pressure_pa``. + #[staticmethod] + #[pyo3(signature = (path, name=None))] + pub fn bubble_point_pressure_from_csv(path: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(BinaryProperty::BubblePointPressure, path, name) + } + + /// Construct bubble point pressure data from numpy arrays. + #[staticmethod] + #[pyo3(signature = (temperature_k, liquid_molefrac_1, bubble_pressure_pa, name=None))] + pub fn bubble_point_pressure( + temperature_k: PyReadonlyArray1, + liquid_molefrac_1: PyReadonlyArray1, + bubble_pressure_pa: PyReadonlyArray1, + name: Option<&str>, + ) -> PyResult { + let temperature_k = temperature_k.as_array(); + let liquid_molefrac_1 = liquid_molefrac_1.as_array(); + let bubble_pressure_pa = bubble_pressure_pa.as_array(); + let records = collect_records_3( + ("temperature_k", temperature_k), + ("liquid_molefrac_1", liquid_molefrac_1), + ("bubble_pressure_pa", bubble_pressure_pa), + |temperature_k, liquid_molefrac_1, bubble_pressure_pa| BubblePointRecord { + temperature_k, + liquid_molefrac_1, + bubble_pressure_pa, + }, + )?; + Ok(Self::with_optional_name( + BinaryDataset::bubble_point_pressure(records), + name, + )) + } + + /// Load dew point pressure data from CSV. + /// + /// CSV columns: ``temperature_k, vapor_molefrac_1, dew_pressure_pa``. + #[staticmethod] + #[pyo3(signature = (path, name=None))] + pub fn dew_point_pressure_from_csv(path: &str, name: Option<&str>) -> PyResult { + Self::from_csv_for_property(BinaryProperty::DewPointPressure, path, name) + } + + /// Construct dew point pressure data from numpy arrays. + #[staticmethod] + #[pyo3(signature = (temperature_k, vapor_molefrac_1, dew_pressure_pa, name=None))] + pub fn dew_point_pressure( + temperature_k: PyReadonlyArray1, + vapor_molefrac_1: PyReadonlyArray1, + dew_pressure_pa: PyReadonlyArray1, + name: Option<&str>, + ) -> PyResult { + let temperature_k = temperature_k.as_array(); + let vapor_molefrac_1 = vapor_molefrac_1.as_array(); + let dew_pressure_pa = dew_pressure_pa.as_array(); + let records = collect_records_3( + ("temperature_k", temperature_k), + ("vapor_molefrac_1", vapor_molefrac_1), + ("dew_pressure_pa", dew_pressure_pa), + |temperature_k, vapor_molefrac_1, dew_pressure_pa| DewPointRecord { + temperature_k, + vapor_molefrac_1, + dew_pressure_pa, + }, + )?; + Ok(Self::with_optional_name( + BinaryDataset::dew_point_pressure(records), + name, + )) + } + + /// Property name. + #[getter] + pub fn name(&self) -> &str { + self.inner.name() + } + + /// Number of data points. + pub fn __len__(&self) -> usize { + self.inner.target().len() + } + + /// Target values. + pub fn target<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> { + self.inner.target().to_owned().to_pyarray(py) + } + + /// Evaluate the dataset's property for one or more equations of state (no gradients). + /// + /// Args: + /// eos: A single ``EquationOfState`` or a list of them. Each + /// must describe a binary system (``components() == 2``). + /// + /// Returns: + /// ``(predicted, converged)``. For a single EoS, both are 1D arrays + /// of length ``n_points``. For a list of ``n_eos`` EoS, both are + /// 2D arrays of shape ``[n_points, n_eos]``; column ``k`` + /// corresponds to ``eos[k]``. Non-converged points are reported as + /// ``NaN`` in ``predicted`` and ``False`` in ``converged``. + pub fn evaluate<'py>( + &self, + py: Python<'py>, + eos: &Bound<'py, PyAny>, + ) -> PyResult<(Bound<'py, PyAny>, Bound<'py, PyAny>)> { + evaluate_eos(py, &self.inner, eos) + } + + pub fn __repr__(&self) -> String { + format!( + "BinaryDataset(property={}, n={})", + self.inner.name(), + self.inner.target().len() + ) + } +} + +impl PyBinaryDataset { + fn from_csv_for_property( + property: BinaryProperty, + path: &str, + name: Option<&str>, + ) -> PyResult { + BinaryDataset::from_csv(property, std::path::Path::new(path)) + .map(|inner| Self::with_optional_name(inner, name)) + .map_err(|e| PyValueError::new_err(e.to_string())) + } + + fn with_optional_name(mut inner: BinaryDataset, name: Option<&str>) -> Self { + if let Some(n) = name { + inner = inner.with_name(n); + } + Self { inner } + } +} diff --git a/py-feos/src/ad/mod.rs b/py-feos/src/ad/mod.rs index 3f775a2da..71eac67a0 100644 --- a/py-feos/src/ad/mod.rs +++ b/py-feos/src/ad/mod.rs @@ -1,9 +1,19 @@ +use crate::eos::PyEquationOfState; use feos::pcsaft::{PcSaftBinary, PcSaftPure}; -use feos_core::{ParametersAD, PropertiesAD}; -use numpy::{PyArray1, PyArray2, PyReadonlyArray2, ToPyArray}; +use feos_core::ad::{ + BoilingTemperature, BubblePointPressure, DewPointPressure, EnthalpyOfVaporization, + EquilibriumLiquidDensity, LiquidDensity, ParametersAD, PropertyAD, + ResidualIsobaricHeatCapacity, VaporPressure, +}; +use nalgebra::{U1, U2}; +use num_dual::DualSVec; +use numpy::{PyArray1, PyArray2, PyReadonlyArray1, PyReadonlyArray2, ToPyArray}; use paste::paste; use pyo3::prelude::*; +pub mod dataset; +pub use dataset::{PyBinaryDataset, PyPureDataset}; + #[pyclass(name = "EquationOfStateAD", eq, eq_int)] #[derive(Clone, Copy, PartialEq)] pub enum PyEquationOfStateAD { @@ -25,168 +35,411 @@ impl From for BinaryModels { } } +type EvalResult<'py> = (Bound<'py, PyArray1>, Bound<'py, PyArray1>); + type GradResult<'py> = ( Bound<'py, PyArray1>, Bound<'py, PyArray2>, Bound<'py, PyArray1>, ); -/// Calculate vapor pressures and derivatives w.r.t. model parameters. -/// -/// Parameters -/// ---------- -/// model: EquationOfStateAD -/// The equation of state to use. -/// parameter_names: List[string] -/// The name of the parameters for which derivatives are calculated. -/// parameters: np.ndarray[float] -/// The parameters for every data point. -/// input: np.ndarray[float] -/// The temperature (in K) for every data point. -/// -/// Returns -/// ------- -/// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The vapor pressures (in Pa), gradients, and convergence status. -#[pyfunction] -pub fn vapor_pressure_derivatives<'py>( - model: PyEquationOfStateAD, - parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, - input: PyReadonlyArray2, -) -> GradResult<'py> { - _vapor_pressure_derivatives(model, parameter_names, parameters, input) -} +#[pyclass(name = "Property")] +pub struct PyPropertyAD; -/// Calculate boiling temperatures and derivatives w.r.t. model parameters. -/// -/// Parameters -/// ---------- -/// model: EquationOfStateAD -/// The equation of state to use. -/// parameter_names: List[string] -/// The name of the parameters for which derivatives are calculated. -/// parameters: np.ndarray[float] -/// The parameters for every data point. -/// input: np.ndarray[float] -/// The pressure (in Pa) for every data point. -/// -/// Returns -/// ------- -/// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The boiling temperature (in K), gradients, and convergence status. -#[pyfunction] -pub fn boiling_temperature_derivatives<'py>( - model: PyEquationOfStateAD, - parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, - input: PyReadonlyArray2, -) -> GradResult<'py> { - _boiling_temperature_derivatives(model, parameter_names, parameters, input) -} +#[pymethods] +impl PyPropertyAD { + /// Calculate vapor pressures in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The temperature (in K) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): The vapor pressures (in Pa), and convergence status. + #[staticmethod] + pub fn vapor_pressure<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = VaporPressure::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } -/// Calculate liquid densities and derivatives w.r.t. model parameters. -/// -/// Parameters -/// ---------- -/// model: EquationOfStateAD -/// The equation of state to use. -/// parameter_names: List[string] -/// The name of the parameters for which derivatives are calculated. -/// parameters: np.ndarray[float] -/// The parameters for every data point. -/// input: np.ndarray[float] -/// The temperature (in K) and pressure (in Pa) for every data point. -/// -/// Returns -/// ------- -/// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The liquid densities (in kmol/m³), gradients, and convergence status. -#[pyfunction] -pub fn liquid_density_derivatives<'py>( - model: PyEquationOfStateAD, - parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, - input: PyReadonlyArray2, -) -> GradResult<'py> { - _liquid_density_derivatives(model, parameter_names, parameters, input) -} + /// Calculate vapor pressures and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The temperature (in K) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The vapor pressures (in Pa), gradients, and convergence status. + #[staticmethod] + pub fn vapor_pressure_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _vapor_pressure_derivatives(model, parameter_names, parameters, input) + } -/// Calculate liquid densities at saturation and derivatives w.r.t. model parameters. -/// -/// Parameters -/// ---------- -/// model: EquationOfStateAD -/// The equation of state to use. -/// parameter_names: List[string] -/// The name of the parameters for which derivatives are calculated. -/// parameters: np.ndarray[float] -/// The parameters for every data point. -/// input: np.ndarray[float] -/// The temperature (in K) for every data point. -/// -/// Returns -/// ------- -/// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The liquid densities (in kmol/m³), gradients, and convergence status. -#[pyfunction] -pub fn equilibrium_liquid_density_derivatives<'py>( - model: PyEquationOfStateAD, - parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, - input: PyReadonlyArray2, -) -> GradResult<'py> { - _equilibrium_liquid_density_derivatives(model, parameter_names, parameters, input) -} + /// Calculate boiling temperatures in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): The boiling temperature (in K), and convergence status. + #[staticmethod] + pub fn boiling_temperature<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = BoilingTemperature::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } -/// Calculate bubble point pressures of binary mixtures and derivatives w.r.t. model parameters. -/// -/// Parameters -/// ---------- -/// model: EquationOfStateAD -/// The equation of state to use. -/// parameter_names: List[string] -/// The name of the parameters for which derivatives are calculated. -/// parameters: np.ndarray[float] -/// The parameters for every data point. -/// input: np.ndarray[float] -/// The temperature (in K), composition of the first component, and an initial guess for the -/// pressure (in Pa) for every data point. -/// -/// Returns -/// ------- -/// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The bubble point pressures (in Pa), gradients, and convergence status. -#[pyfunction] -pub fn bubble_point_pressure_derivatives<'py>( - model: PyEquationOfStateAD, - parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, - input: PyReadonlyArray2, -) -> GradResult<'py> { - _bubble_point_pressure_derivatives(model, parameter_names, parameters, input) -} + /// Calculate boiling temperatures and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The boiling temperature (in K), gradients, and convergence status. + #[staticmethod] + pub fn boiling_temperature_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _boiling_temperature_derivatives(model, parameter_names, parameters, input) + } + + /// Calculate liquid densities in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The temperature (in K) and pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): The liquid densities (in kmol/m³), and convergence status. + #[staticmethod] + pub fn liquid_density<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = LiquidDensity::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } + + /// Calculate liquid densities and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The temperature (in K) and pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The liquid densities (in kmol/m³), gradients, and convergence status. + #[staticmethod] + pub fn liquid_density_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _liquid_density_derivatives(model, parameter_names, parameters, input) + } -/// Calculate dew point pressures of binary mixtures and derivatives w.r.t. model parameters. -/// -/// Parameters -/// ---------- -/// model: EquationOfStateAD -/// The equation of state to use. -/// parameter_names: List[string] -/// The name of the parameters for which derivatives are calculated. -/// parameters: np.ndarray[float] -/// The parameters for every data point. -/// input: np.ndarray[float] -/// The temperature (in K), composition of the first component, and an initial guess for the -/// pressure (in Pa) for every data point. -/// -/// Returns -/// ------- -/// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The dew point pressures (in Pa), gradients, and convergence status. -#[pyfunction] -pub fn dew_point_pressure_derivatives<'py>( - model: PyEquationOfStateAD, - parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, - input: PyReadonlyArray2, -) -> GradResult<'py> { - _dew_point_pressure_derivatives(model, parameter_names, parameters, input) + /// Calculate liquid densities at saturation in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The temperature (in K) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): The liquid densities (in kmol/m³), and convergence status. + #[staticmethod] + pub fn equilibrium_liquid_density<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = EquilibriumLiquidDensity::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } + + /// Calculate liquid densities at saturation and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The temperature (in K) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The liquid densities (in kmol/m³), gradients, and convergence status. + #[staticmethod] + pub fn equilibrium_liquid_density_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _equilibrium_liquid_density_derivatives(model, parameter_names, parameters, input) + } + + /// Calculate enthalpy of vaporization in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The temperature (in K) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): + /// The enthalpies of vaporization (in J/mol), and convergence status. + #[staticmethod] + pub fn enthalpy_of_vaporization<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = EnthalpyOfVaporization::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } + + /// Calculate enthalpy of vaporization and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The temperature (in K) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): + /// The enthalpies of vaporization (in J/mol), gradients, and convergence status. + #[staticmethod] + pub fn enthalpy_of_vaporization_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _enthalpy_of_vaporization_derivatives(model, parameter_names, parameters, input) + } + + /// Calculate residual isobaric molar heat capacities (liquid phase) in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The temperature (in K) and pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): + /// The residual isobaric heat capacities (in J/(mol·K)), and convergence status. + #[staticmethod] + pub fn residual_isobaric_heat_capacity<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = + ResidualIsobaricHeatCapacity::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } + + /// Calculate residual isobaric molar heat capacities (liquid phase) and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The temperature (in K) and pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): + /// The residual isobaric heat capacities (in J/(mol·K)), gradients, and convergence status. + #[staticmethod] + pub fn residual_isobaric_heat_capacity_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _residual_isobaric_heat_capacity_derivatives(model, parameter_names, parameters, input) + } + + /// Calculate bubble point pressures of binary mixtures in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The temperature (in K), composition of the first component, and an initial guess for the + /// pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): The bubble point pressures (in Pa), and convergence status. + #[staticmethod] + pub fn bubble_point_pressure<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = BubblePointPressure::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } + + /// Calculate bubble point pressures of binary mixtures and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The temperature (in K), composition of the first component, and an initial guess for the + /// pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The bubble point pressures (in Pa), gradients, and convergence status. + #[staticmethod] + pub fn bubble_point_pressure_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _bubble_point_pressure_derivatives(model, parameter_names, parameters, input) + } + + /// Calculate dew point pressures of binary mixtures in parallel. + /// + /// Parameters + /// ---------- + /// eos: EquationOfState + /// The equation of state to use. + /// input: np.ndarray[float] + /// The temperature (in K), composition of the first component, and an initial guess for the + /// pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[bool]): The dew point pressures (in Pa), and convergence status. + #[staticmethod] + pub fn dew_point_pressure<'py>( + py: Python<'py>, + eos: &PyEquationOfState, + input: PyReadonlyArray2, + ) -> EvalResult<'py> { + let (value, status) = DewPointPressure::evaluate_parallel(&eos.0, input.as_array()); + (value.to_pyarray(py), status.to_pyarray(py)) + } + + /// Calculate dew point pressures of binary mixtures and derivatives w.r.t. model parameters. + /// + /// Parameters + /// ---------- + /// model: EquationOfStateAD + /// The equation of state to use. + /// parameter_names: List[string] + /// The name of the parameters for which derivatives are calculated. + /// parameters: np.ndarray[float] + /// The parameters for every data point. + /// input: np.ndarray[float] + /// The temperature (in K), composition of the first component, and an initial guess for the + /// pressure (in Pa) for every data point. + /// + /// Returns + /// ------- + /// (np.ndarray[float], np.ndarray[float], np.ndarray[bool]): The dew point pressures (in Pa), gradients, and convergence status. + #[staticmethod] + pub fn dew_point_pressure_derivatives<'py>( + model: PyEquationOfStateAD, + parameter_names: &Bound<'py, PyAny>, + parameters: &Bound<'py, PyAny>, + input: PyReadonlyArray2, + ) -> GradResult<'py> { + _dew_point_pressure_derivatives(model, parameter_names, parameters, input) + } } macro_rules! expand_models { @@ -196,7 +449,7 @@ macro_rules! expand_models { fn [<_ $prop _derivatives>]<'py>( model: PyEquationOfStateAD, parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, + parameters: &Bound<'py, PyAny>, input: PyReadonlyArray2, ) -> GradResult<'py> { match <$enum>::from(model) { @@ -210,50 +463,67 @@ macro_rules! expand_models { } macro_rules! impl_evaluate_gradients { - (pure, [$($prop:ident),*], $models:tt) => { - $(impl_evaluate_gradients!(1,PyEquationOfStateAD,$prop,$models,0,1,2,3,4,5,max:6);)* + (pure, [$($prop:ident: $prop_type:ty),*], $models:tt) => { + $(impl_evaluate_gradients!(U1,PyEquationOfStateAD,$prop,$prop_type,$models,0,1,2,3,4,5,max:6);)* }; - (binary, [$($prop:ident),*], $models:tt) => { - $(impl_evaluate_gradients!(2,BinaryModels,$prop,$models,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,max:15);)* + (binary, [$($prop:ident: $prop_type:ty),*], $models:tt) => { + $(impl_evaluate_gradients!(U2,BinaryModels,$prop,$prop_type,$models,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,max:15);)* }; - ($n:literal, $enum:ty, $prop:ident, {$($model:ident: $type:ty),*}, $($p:literal,)* max: $max:literal) => { + ($n:ty, $enum:ty, $prop:ident, $prop_type:ty, {$($model:ident: $type:ty),*}, $($p:literal,)* max: $max:literal) => { expand_models!($enum, $prop, $($model: $type),*); - paste!( fn $prop<'py, R: ParametersAD<$n>>( parameter_names: &Bound<'py, PyAny>, - parameters: PyReadonlyArray2, + parameters: &Bound<'py, PyAny>, input: PyReadonlyArray2, ) -> ( Bound<'py, PyArray1>, Bound<'py, PyArray2>, Bound<'py, PyArray1>, - ) { + ) + where + $(R::Lifted>: Sync,)* + R::Lifted>: Sync + { let (value, grad, status) = - $( - if let Ok(p) = parameter_names.extract::<[String; $p]>() { - R::[<$prop _parallel>](p, parameters.as_array(), input.as_array()) - } else)* if let Ok(p) = parameter_names.extract::<[String; $max]>() { - R::[<$prop _parallel>](p, parameters.as_array(), input.as_array()) + if let Ok(pars) = parameters.extract::>() { + let pars = pars.as_slice().expect("Parameter array is not contiguous!"); + $( + if let Ok(p) = parameter_names.extract::<[String; $p]>() { + <$prop_type>::evaluate_parallel_derivatives::(p, pars, input.as_array()) + } else)* if let Ok(p) = parameter_names.extract::<[String; $max]>() { + <$prop_type>::evaluate_parallel_derivatives::(p, pars, input.as_array()) + } else { + panic!("Gradients can only be evaluated for up to {} parameters!", $max) + } + } else if let Ok(pars) = parameters.extract::>() { + $( + if let Ok(p) = parameter_names.extract::<[String; $p]>() { + <$prop_type>::evaluate_parallel_derivatives_params::(p, pars.as_array(), input.as_array()) + } else)* if let Ok(p) = parameter_names.extract::<[String; $max]>() { + <$prop_type>::evaluate_parallel_derivatives_params::(p, pars.as_array(), input.as_array()) + } else { + panic!("Gradients can only be evaluated for up to {} parameters!", $max) + } } else { - panic!("Gradients can only be evaluated for up to {} parameters!", $max) + panic!("Argument `parameters` needs to be a 1D or 2D array!") }; ( value.to_pyarray(parameter_names.py()), grad.to_pyarray(parameter_names.py()), status.to_pyarray(parameter_names.py()), ) - }); + } }; } impl_evaluate_gradients!( pure, - [vapor_pressure, boiling_temperature, liquid_density, equilibrium_liquid_density], + [vapor_pressure: VaporPressure, boiling_temperature: BoilingTemperature, liquid_density: LiquidDensity, equilibrium_liquid_density: EquilibriumLiquidDensity, enthalpy_of_vaporization: EnthalpyOfVaporization, residual_isobaric_heat_capacity: ResidualIsobaricHeatCapacity], {PcSaftNonAssoc: PcSaftPure, PcSaftFull: PcSaftPure} ); impl_evaluate_gradients!( binary, - [bubble_point_pressure, dew_point_pressure], + [bubble_point_pressure: BubblePointPressure, dew_point_pressure: DewPointPressure], {PcSaftNonAssoc: PcSaftBinary, PcSaftFull: PcSaftBinary} ); diff --git a/py-feos/src/lib.rs b/py-feos/src/lib.rs index 948a24d6b..017dbb4a6 100644 --- a/py-feos/src/lib.rs +++ b/py-feos/src/lib.rs @@ -179,25 +179,15 @@ fn feos(m: &Bound<'_, PyModule>) -> PyResult<()> { // Equation of state m.add_class::()?; - // // Estimator - // m.add_class::()?; - // m.add_class::()?; - // m.add_class::()?; - // m.add_class::()?; - // AD #[cfg(feature = "ad")] { - m.add_function(wrap_pyfunction!(ad::vapor_pressure_derivatives, m)?)?; - m.add_function(wrap_pyfunction!(ad::boiling_temperature_derivatives, m)?)?; - m.add_function(wrap_pyfunction!(ad::liquid_density_derivatives, m)?)?; - m.add_function(wrap_pyfunction!( - ad::equilibrium_liquid_density_derivatives, - m - )?)?; - m.add_function(wrap_pyfunction!(ad::bubble_point_pressure_derivatives, m)?)?; - m.add_function(wrap_pyfunction!(ad::dew_point_pressure_derivatives, m)?)?; m.add_class::()?; + m.add_class::()?; + + // Datasets + m.add_class::()?; + m.add_class::()?; } #[cfg(feature = "dft")] diff --git a/py-feos/src/user_defined.rs b/py-feos/src/user_defined.rs index 38bbe38ca..641238f0b 100644 --- a/py-feos/src/user_defined.rs +++ b/py-feos/src/user_defined.rs @@ -14,7 +14,10 @@ impl PyIdealGas { pub fn new(obj: Bound<'_, PyAny>) -> PyResult { let attr = obj.hasattr("ln_lambda3")?; if !attr { - panic!("{}", "Python Class has to have a method 'ln_lambda3' with signature:\n\tdef ln_lambda3(self, temperature: HD) -> HD\nwhere 'HD' has to be any (hyper-) dual number.") + panic!( + "{}", + "Python Class has to have a method 'ln_lambda3' with signature:\n\tdef ln_lambda3(self, temperature: HD) -> HD\nwhere 'HD' has to be any (hyper-) dual number." + ) } Ok(Self(obj.unbind())) } @@ -57,23 +60,34 @@ impl PyResidual { pub fn new(obj: Bound<'_, PyAny>) -> PyResult { let attr = obj.hasattr("components")?; if !attr { - panic!("Python Class has to have a method 'components' with signature:\n\tdef signature(self) -> int") + panic!( + "Python Class has to have a method 'components' with signature:\n\tdef signature(self) -> int" + ) } let attr = obj.hasattr("subset")?; if !attr { - panic!("Python Class has to have a method 'subset' with signature:\n\tdef subset(self, component_list: List[int]) -> Self") + panic!( + "Python Class has to have a method 'subset' with signature:\n\tdef subset(self, component_list: List[int]) -> Self" + ) } let attr = obj.hasattr("molar_weight")?; if !attr { - panic!("Python Class has to have a method 'molar_weight' with signature:\n\tdef molar_weight(self) -> SIArray1\nwhere the size of the returned array has to be 'components'.") + panic!( + "Python Class has to have a method 'molar_weight' with signature:\n\tdef molar_weight(self) -> SIArray1\nwhere the size of the returned array has to be 'components'." + ) } let attr = obj.hasattr("max_density")?; if !attr { - panic!("Python Class has to have a method 'max_density' with signature:\n\tdef max_density(self, moles: numpy.ndarray[float]) -> float\nwhere the size of the input array has to be 'components'.") + panic!( + "Python Class has to have a method 'max_density' with signature:\n\tdef max_density(self, moles: numpy.ndarray[float]) -> float\nwhere the size of the input array has to be 'components'." + ) } let attr = obj.hasattr("helmholtz_energy")?; if !attr { - panic!("{}", "Python Class has to have a method 'helmholtz_energy' with signature:\n\tdef helmholtz_energy(self, state: StateHD) -> HD\nwhere 'HD' has to be any of {{float, Dual64, HyperDual64, HyperDualDual64, Dual3Dual64, Dual3_64}}.") + panic!( + "{}", + "Python Class has to have a method 'helmholtz_energy' with signature:\n\tdef helmholtz_energy(self, state: StateHD) -> HD\nwhere 'HD' has to be any of {{float, Dual64, HyperDual64, HyperDualDual64, Dual3Dual64, Dual3_64}}." + ) } Ok(Self(obj.unbind())) } From 81285e5d980378379b9053d546cc8985af157110 Mon Sep 17 00:00:00 2001 From: Philipp Rehner <69816385+prehner@users.noreply.github.com> Date: Fri, 22 May 2026 07:55:47 +0200 Subject: [PATCH 08/12] Implement IdealGasAD for Dippr (#357) --- CHANGELOG.md | 1 + crates/feos/src/ideal_gas/dippr.rs | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f6849481..8d821f3df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add Rayon global thread pool control via `FEOS_MAX_THREADS` and `set_num_threads()`/ `get_num_threads()` to Python. [#346](https://github.com/feos-org/feos/pull/346) - Added DIPPR107 parameterization for ideal gas heat capacities of Burkhardt et al. [#344](https://github.com/feos-org/feos/pull/344) +- Implemented `IdealGasAD` for `Dippr`. [#357](https://github.com/feos-org/feos/pull/357) ### Fixed - Fixed the calculation of temperature and pressure derivatives of dew and bubble points. [#347](https://github.com/feos-org/feos/pull/347) diff --git a/crates/feos/src/ideal_gas/dippr.rs b/crates/feos/src/ideal_gas/dippr.rs index 89824b07a..f976fbe11 100644 --- a/crates/feos/src/ideal_gas/dippr.rs +++ b/crates/feos/src/ideal_gas/dippr.rs @@ -1,5 +1,5 @@ use feos_core::parameter::Parameters; -use feos_core::{FeosResult, IdealGas}; +use feos_core::{FeosResult, IdealGas, IdealGasAD}; use nalgebra::DVector; use num_dual::DualNum; use quantity::{JOULE, KELVIN, KILO, MOL, MolarEntropy, Temperature}; @@ -152,6 +152,28 @@ impl IdealGas for Dippr { } } +impl + Copy> IdealGasAD for Dippr { + type Real = Self; + + type Lifted + Copy> = Self; + + fn re(&self) -> Self::Real { + self.clone() + } + + fn lift + Copy>(&self) -> Self::Lifted { + self.clone() + } + + fn ln_lambda3(&self, temperature: D) -> D { + IdealGas::ln_lambda3(self, temperature) + } + + fn ideal_gas_model(&self) -> &'static str { + "Ideal gas (DIPPR)" + } +} + #[cfg(test)] mod tests { use approx::assert_relative_eq; From 94248c8ea957d139f9c95ab019e1ea3235e37999 Mon Sep 17 00:00:00 2001 From: Philipp Rehner <69816385+prehner@users.noreply.github.com> Date: Tue, 16 Jun 2026 08:39:45 +0200 Subject: [PATCH 09/12] Update PyO3 to v0.29 (#360) Co-authored-by: Gernot Bauer --- CHANGELOG.md | 6 +++++- Cargo.toml | 10 +++++----- crates/feos-dft/src/adsorption/fea_potential.rs | 11 ++++++++--- py-feos/Cargo.toml | 6 +++--- py-feos/src/ad/mod.rs | 2 +- py-feos/src/dft/adsorption/external_potential.rs | 4 ++-- py-feos/src/dft/mod.rs | 8 ++++---- py-feos/src/dft/solver.rs | 4 ++-- py-feos/src/lib.rs | 2 +- py-feos/src/parameter/chemical_record.rs | 4 ++-- py-feos/src/parameter/fragmentation.rs | 2 +- py-feos/src/parameter/identifier.rs | 4 ++-- py-feos/src/parameter/mod.rs | 4 ++-- py-feos/src/parameter/model_record.rs | 4 ++-- py-feos/src/parameter/segment.rs | 4 ++-- py-feos/src/phase_equilibria.rs | 4 ++-- py-feos/src/state.rs | 4 ++-- py-feos/src/user_defined.rs | 4 ++-- 18 files changed, 48 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d821f3df..954079b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `PropertyAD` trait in `feos_core::ad` with one struct per property for uniform evaluation with or without parameter derivatives, including parallel variants. [#358](https://github.com/feos-org/feos/pull/358) - Added `feos_core::ad::dataset` module with `PureDataset` and `BinaryDataset` types, constructible from records, CSV files, or readers, for use in parameter fits. [#358](https://github.com/feos-org/feos/pull/358) - Exposed `Property`, `PureDataset`, and `BinaryDataset` in `py-feos`. [#358](https://github.com/feos-org/feos/pull/358) +- Implemented `IdealGasAD` for `Dippr`. [#357](https://github.com/feos-org/feos/pull/357) ### Changed - Removed any assumptions about the total number of moles in a `State` or `PhaseEquilibrium`. Evaluating extensive properties now returns a `Result`. [#330](https://github.com/feos-org/feos/pull/330) @@ -29,6 +30,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Packaging - Updated `quantity` dependency to 0.13 and removed the `typenum` dependency. [#328](https://github.com/feos-org/feos/pull/328) - Added `csv` as a `feos-core` dependency for the new dataset module. [#358](https://github.com/feos-org/feos/pull/358) +- Updated `pyo3`, `pythonize` and `numpy` dependencies to 0.29. [#360](https://github.com/feos-org/feos/pull/360) +- Updated `quantity` and `num-dual` dependencies to 0.14. [#360](https://github.com/feos-org/feos/pull/360) +- Updated `nalgebra` dependency to 0.35. [#360](https://github.com/feos-org/feos/pull/360) +- Updated `gauss-quad` dependency to 0.3. [#360](https://github.com/feos-org/feos/pull/360) ## [Unreleased] @@ -44,7 +49,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add Rayon global thread pool control via `FEOS_MAX_THREADS` and `set_num_threads()`/ `get_num_threads()` to Python. [#346](https://github.com/feos-org/feos/pull/346) - Added DIPPR107 parameterization for ideal gas heat capacities of Burkhardt et al. [#344](https://github.com/feos-org/feos/pull/344) -- Implemented `IdealGasAD` for `Dippr`. [#357](https://github.com/feos-org/feos/pull/357) ### Fixed - Fixed the calculation of temperature and pressure derivatives of dew and bubble points. [#347](https://github.com/feos-org/feos/pull/347) diff --git a/Cargo.toml b/Cargo.toml index 038739561..1d31a517b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,10 +22,10 @@ keywords = [ categories = ["science"] [workspace.dependencies] -quantity = "0.13" -num-dual = "0.13" +quantity = "0.14" +num-dual = "0.14" ndarray = "0.17" -nalgebra = "0.34" +nalgebra = "0.35" thiserror = "2.0" conv = "0.3" num-traits = "0.2" @@ -38,11 +38,11 @@ petgraph = "0.8" rustdct = "0.7" rustfft = "6.0" libm = "0.2" -gauss-quad = "0.2" +gauss-quad = "0.3" approx = "0.5" criterion = "0.8" paste = "1.0" -rusqlite = "0.39" +rusqlite = "0.40" csv = "1.0" feos-core = { version = "0.9", path = "crates/feos-core" } diff --git a/crates/feos-dft/src/adsorption/fea_potential.rs b/crates/feos-dft/src/adsorption/fea_potential.rs index ef4b76964..d05ed6a01 100644 --- a/crates/feos-dft/src/adsorption/fea_potential.rs +++ b/crates/feos-dft/src/adsorption/fea_potential.rs @@ -1,11 +1,12 @@ use super::pore3d::{calculate_distance2, evaluate_lj_potential}; -use crate::profile::{CUTOFF_RADIUS, MAX_POTENTIAL}; use crate::Geometry; +use crate::profile::{CUTOFF_RADIUS, MAX_POTENTIAL}; use feos_core::ReferenceSystem; use gauss_quad::GaussLegendre; use ndarray::{Array1, Array2, Zip}; use quantity::Length; use std::f64::consts::PI; +use std::num::NonZero; // Calculate free-energy average potential for given solid structure. #[expect(clippy::too_many_arguments)] @@ -55,7 +56,9 @@ pub fn calculate_fea_potential( } Geometry::Spherical | Geometry::Cylindrical => { let (unscaled_nodes, unscaled_weights) = - GaussLegendre::new(n_grid[0]).unwrap().into_iter().unzip(); + GaussLegendre::new(NonZero::new(n_grid[0]).unwrap()) + .into_iter() + .unzip(); let nodes = PI + Array1::from_vec(unscaled_nodes) * PI; let weights = Array1::from_vec(unscaled_weights) * PI; @@ -80,7 +83,9 @@ pub fn calculate_fea_potential( } Geometry::Spherical => { let (unscaled_nodes, unscaled_weights) = - GaussLegendre::new(n_grid[1]).unwrap().into_iter().unzip(); + GaussLegendre::new(NonZero::new(n_grid[1]).unwrap()) + .into_iter() + .unzip(); let nodes = PI / 2.0 + Array1::from_vec(unscaled_nodes) * PI / 2.0; let weights = Array1::from_vec(unscaled_weights) * PI / 2.0 diff --git a/py-feos/Cargo.toml b/py-feos/Cargo.toml index aa21f4171..bf1d4e217 100644 --- a/py-feos/Cargo.toml +++ b/py-feos/Cargo.toml @@ -15,12 +15,12 @@ name = "feos" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.27", features = [ +pyo3 = { version = "0.29", features = [ "multiple-pymethods", "indexmap" ] } -pythonize = "0.27" -numpy = { version = "0.27" } +pythonize = "0.29" +numpy = { version = "0.29" } quantity = { workspace = true, features = ["python", "python_numpy"] } num-dual = { workspace = true, features = ["python_macro"] } diff --git a/py-feos/src/ad/mod.rs b/py-feos/src/ad/mod.rs index 71eac67a0..12a6c6345 100644 --- a/py-feos/src/ad/mod.rs +++ b/py-feos/src/ad/mod.rs @@ -14,7 +14,7 @@ use pyo3::prelude::*; pub mod dataset; pub use dataset::{PyBinaryDataset, PyPureDataset}; -#[pyclass(name = "EquationOfStateAD", eq, eq_int)] +#[pyclass(name = "EquationOfStateAD", eq, eq_int, from_py_object)] #[derive(Clone, Copy, PartialEq)] pub enum PyEquationOfStateAD { PcSaftNonAssoc, diff --git a/py-feos/src/dft/adsorption/external_potential.rs b/py-feos/src/dft/adsorption/external_potential.rs index bf4d6e209..290fe6c03 100644 --- a/py-feos/src/dft/adsorption/external_potential.rs +++ b/py-feos/src/dft/adsorption/external_potential.rs @@ -1,12 +1,12 @@ use feos_dft::adsorption::ExternalPotential; use ndarray::Array2; -use numpy::prelude::*; use numpy::PyArray1; +use numpy::prelude::*; use pyo3::prelude::*; use quantity::Length; /// A collection of external potentials. -#[pyclass(name = "ExternalPotential")] +#[pyclass(name = "ExternalPotential", from_py_object)] #[derive(Clone)] pub struct PyExternalPotential(pub ExternalPotential); diff --git a/py-feos/src/dft/mod.rs b/py-feos/src/dft/mod.rs index 5986ee0d0..2ff6c4f34 100644 --- a/py-feos/src/dft/mod.rs +++ b/py-feos/src/dft/mod.rs @@ -1,4 +1,4 @@ -use crate::eos::{parse_molefracs, PyEquationOfState}; +use crate::eos::{PyEquationOfState, parse_molefracs}; use crate::ideal_gas::IdealGasModel; use crate::residual::ResidualModel; use feos::hard_sphere::{FMTFunctional, FMTVersion}; @@ -24,7 +24,7 @@ pub(crate) use solver::{PyDFTSolver, PyDFTSolverLog}; /// Geometries of individual axes. #[derive(Clone, Copy, PartialEq)] -#[pyclass(name = "Geometry", eq, eq_int)] +#[pyclass(name = "Geometry", eq, eq_int, from_py_object)] pub enum PyGeometry { Cartesian, Cylindrical, @@ -53,7 +53,7 @@ impl From for Geometry { /// Different versions of fundamental measure theory. #[derive(Clone, Copy, PartialEq)] -#[pyclass(name = "FMTVersion", eq, eq_int)] +#[pyclass(name = "FMTVersion", eq, eq_int, from_py_object)] pub enum PyFMTVersion { /// White Bear ([Roth et al., 2002](https://doi.org/10.1088/0953-8984/14/46/313)) or modified ([Yu and Wu, 2002](https://doi.org/10.1063/1.1520530)) fundamental measure theory WhiteBear, @@ -84,7 +84,7 @@ impl From for FMTVersion { } /// Collection of Helmholtz energy functionals. -#[pyclass(name = "HelmholtzEnergyFunctional")] +#[pyclass(name = "HelmholtzEnergyFunctional", from_py_object)] #[derive(Clone)] pub struct PyHelmholtzEnergyFunctional; diff --git a/py-feos/src/dft/solver.rs b/py-feos/src/dft/solver.rs index 2c23d57c3..4ee5e7410 100644 --- a/py-feos/src/dft/solver.rs +++ b/py-feos/src/dft/solver.rs @@ -16,7 +16,7 @@ use quantity::Time; /// Returns /// ------- /// DFTSolver -#[pyclass(name = "DFTSolver")] +#[pyclass(name = "DFTSolver", from_py_object)] #[derive(Clone)] pub struct PyDFTSolver(pub DFTSolver); @@ -160,7 +160,7 @@ impl PyDFTSolver { } } -#[pyclass(name = "DFTSolverLog")] +#[pyclass(name = "DFTSolverLog", from_py_object)] #[derive(Clone)] pub struct PyDFTSolverLog(pub DFTSolverLog); diff --git a/py-feos/src/lib.rs b/py-feos/src/lib.rs index 017dbb4a6..bd191ed12 100644 --- a/py-feos/src/lib.rs +++ b/py-feos/src/lib.rs @@ -19,7 +19,7 @@ pub(crate) mod user_defined; /// Output level for phase equilibrium solvers. #[derive(Debug, Clone, Copy, PartialEq)] -#[pyclass(name = "Verbosity", eq, eq_int)] +#[pyclass(name = "Verbosity", eq, eq_int, from_py_object)] pub(crate) enum PyVerbosity { /// Do not print output. None, diff --git a/py-feos/src/parameter/chemical_record.rs b/py-feos/src/parameter/chemical_record.rs index b086a0f0f..c3a2d35b9 100644 --- a/py-feos/src/parameter/chemical_record.rs +++ b/py-feos/src/parameter/chemical_record.rs @@ -1,4 +1,4 @@ -use super::fragmentation::{fragment_molecule, PySmartsRecord}; +use super::fragmentation::{PySmartsRecord, fragment_molecule}; use super::identifier::{PyIdentifier, PyIdentifierOption}; use crate::error::PyFeosError; use feos_core::parameter::{ChemicalRecord, Identifier}; @@ -7,7 +7,7 @@ use pyo3::prelude::*; use serde::{Deserialize, Serialize}; /// Information about segments and bonds of a molecule. -#[pyclass(name = "ChemicalRecord")] +#[pyclass(name = "ChemicalRecord", from_py_object)] #[derive(Deserialize, Serialize, Debug, Clone)] pub(crate) struct PyChemicalRecord(ChemicalRecord); diff --git a/py-feos/src/parameter/fragmentation.rs b/py-feos/src/parameter/fragmentation.rs index b011f9fc5..8d9fb9642 100644 --- a/py-feos/src/parameter/fragmentation.rs +++ b/py-feos/src/parameter/fragmentation.rs @@ -9,7 +9,7 @@ use crate::error::PyFeosError; /// SMARTS code, required to fragmentize molecules into segments. #[derive(Clone, Serialize, Deserialize)] -#[pyclass(name = "SmartsRecord")] +#[pyclass(name = "SmartsRecord", from_py_object)] pub(crate) struct PySmartsRecord { group: String, smarts: String, diff --git a/py-feos/src/parameter/identifier.rs b/py-feos/src/parameter/identifier.rs index 4baf195db..e8057c077 100644 --- a/py-feos/src/parameter/identifier.rs +++ b/py-feos/src/parameter/identifier.rs @@ -3,7 +3,7 @@ use pyo3::prelude::*; use serde::{Deserialize, Serialize}; /// Identifier to match on while reading parameters from files. -#[pyclass(name = "IdentifierOption", eq, eq_int)] +#[pyclass(name = "IdentifierOption", eq, eq_int, from_py_object)] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] pub enum PyIdentifierOption { Cas, @@ -43,7 +43,7 @@ impl From for IdentifierOption { } /// Different common identifiers for chemicals. -#[pyclass(name = "Identifier")] +#[pyclass(name = "Identifier", from_py_object)] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PyIdentifier(pub Identifier); diff --git a/py-feos/src/parameter/mod.rs b/py-feos/src/parameter/mod.rs index 5cfbfbe6c..9fb23bfee 100644 --- a/py-feos/src/parameter/mod.rs +++ b/py-feos/src/parameter/mod.rs @@ -23,7 +23,7 @@ pub(crate) use model_record::{PyBinaryRecord, PyPureRecord}; pub(crate) use segment::{PyBinarySegmentRecord, PySegmentRecord}; /// Set of parameters that fully characterizes a mixture. -#[pyclass(name = "Parameters")] +#[pyclass(name = "Parameters", from_py_object)] #[derive(Clone, Serialize, Deserialize)] pub struct PyParameters { pub pure_records: Vec>, @@ -565,7 +565,7 @@ impl PyParameters { /// Combination of chemical information and segment parameters that is used to /// parametrize a group-contribution model. -#[pyclass(name = "GcParameters")] +#[pyclass(name = "GcParameters", from_py_object)] #[derive(Clone, Serialize, Deserialize)] pub struct PyGcParameters { chemical_records: Vec, diff --git a/py-feos/src/parameter/model_record.rs b/py-feos/src/parameter/model_record.rs index eeb06aa8e..93e0fca54 100644 --- a/py-feos/src/parameter/model_record.rs +++ b/py-feos/src/parameter/model_record.rs @@ -15,7 +15,7 @@ use serde_json::Value; #[derive(Serialize, Deserialize, Clone)] #[serde(from = "PureRecord")] #[serde(into = "PureRecord")] -#[pyclass(name = "PureRecord")] +#[pyclass(name = "PureRecord", from_py_object)] pub struct PyPureRecord { #[pyo3(get)] pub identifier: PyIdentifier, @@ -128,7 +128,7 @@ impl PyPureRecord { #[derive(Serialize, Deserialize, Clone)] #[serde(from = "BinaryRecord")] #[serde(into = "BinaryRecord")] -#[pyclass(name = "BinaryRecord")] +#[pyclass(name = "BinaryRecord", from_py_object)] pub struct PyBinaryRecord { #[pyo3(get)] pub id1: PyIdentifier, diff --git a/py-feos/src/parameter/segment.rs b/py-feos/src/parameter/segment.rs index 1816643ec..0bf2fd3f8 100644 --- a/py-feos/src/parameter/segment.rs +++ b/py-feos/src/parameter/segment.rs @@ -10,7 +10,7 @@ use serde_json::Value; #[derive(Serialize, Deserialize, Clone)] #[serde(from = "SegmentRecord")] #[serde(into = "SegmentRecord")] -#[pyclass(name = "SegmentRecord")] +#[pyclass(name = "SegmentRecord", from_py_object)] pub struct PySegmentRecord { #[pyo3(get)] identifier: String, @@ -113,7 +113,7 @@ impl PySegmentRecord { #[derive(Serialize, Deserialize, Clone)] #[serde(from = "BinaryRecord")] #[serde(into = "BinaryRecord")] -#[pyclass(name = "BinarySegmentRecord")] +#[pyclass(name = "BinarySegmentRecord", from_py_object)] pub struct PyBinarySegmentRecord { #[pyo3(get)] pub id1: String, diff --git a/py-feos/src/phase_equilibria.rs b/py-feos/src/phase_equilibria.rs index feaa38930..17b85b91d 100644 --- a/py-feos/src/phase_equilibria.rs +++ b/py-feos/src/phase_equilibria.rs @@ -19,7 +19,7 @@ use std::ops::Deref; use std::sync::Arc; /// A thermodynamic two phase equilibrium state. -#[pyclass(name = "PhaseEquilibrium")] +#[pyclass(name = "PhaseEquilibrium", from_py_object)] #[derive(Clone)] pub struct PyPhaseEquilibrium( pub PhaseEquilibrium, ResidualModel>>, 2>, @@ -582,7 +582,7 @@ impl PyPhaseEquilibrium { } /// A thermodynamic three phase equilibrium state. -#[pyclass(name = "ThreePhaseEquilibrium")] +#[pyclass(name = "ThreePhaseEquilibrium", from_py_object)] #[derive(Clone)] struct PyThreePhaseEquilibrium( PhaseEquilibrium, ResidualModel>>, 3>, diff --git a/py-feos/src/state.rs b/py-feos/src/state.rs index 761055a3b..bb94686e3 100644 --- a/py-feos/src/state.rs +++ b/py-feos/src/state.rs @@ -23,7 +23,7 @@ type InvP = Quantity::Output>; /// Possible contributions that can be computed. #[derive(Clone, Copy, PartialEq)] -#[pyclass(name = "Contributions", eq, eq_int)] +#[pyclass(name = "Contributions", eq, eq_int, from_py_object)] pub enum PyContributions { /// Only compute the ideal gas contribution IdealGas, @@ -94,7 +94,7 @@ impl From for Contributions { /// ------ /// Error /// When the state cannot be created using the combination of input. -#[pyclass(name = "State")] +#[pyclass(name = "State", from_py_object)] #[derive(Clone)] pub struct PyState(pub State, ResidualModel>>>); diff --git a/py-feos/src/user_defined.rs b/py-feos/src/user_defined.rs index 641238f0b..6da88fd30 100644 --- a/py-feos/src/user_defined.rs +++ b/py-feos/src/user_defined.rs @@ -176,7 +176,7 @@ macro_rules! impl_residual { macro_rules! state { ($py_state_id:ident, $py_hd_id:ident, $hd_ty:ty) => { - #[pyclass] + #[pyclass(from_py_object)] #[derive(Clone)] struct $py_state_id(StateHD<$hd_ty>); @@ -233,7 +233,7 @@ macro_rules! state { macro_rules! dual_number { ($py_hd_id:ident, $hd_ty:ty, $py_field_ty:ty) => { - #[pyclass] + #[pyclass(from_py_object)] #[derive(Clone)] struct $py_hd_id($hd_ty); impl_dual_num!($py_hd_id, $hd_ty, $py_field_ty); From 6700d900be7e3940723f740255f756a68e89ef87 Mon Sep 17 00:00:00 2001 From: Philipp Rehner Date: Tue, 14 Jul 2026 13:21:25 +0200 Subject: [PATCH 10/12] Documentation fixes --- Cargo.toml | 2 +- crates/feos-core/src/ad/mod.rs | 1 + crates/feos-core/src/parameter/association.rs | 12 +++- .../src/parameter/chemical_record.rs | 5 ++ crates/feos-core/src/parameter/mod.rs | 9 +++ docs/api/ad.md | 41 +++++++---- .../recipes_automatic_differentiation.ipynb | 36 +++++----- docs/theory/eos/properties.md | 6 +- .../tutorials/eos/core_user_defined_eos.ipynb | 71 +++++++------------ .../eos/pcsaft_entropy_scaling.ipynb | 10 +-- .../tutorials/utility/core_dual_numbers.ipynb | 34 ++++----- py-feos/src/ad/mod.rs | 1 + py-feos/src/state.rs | 2 +- py-feos/src/user_defined.rs | 2 + 14 files changed, 125 insertions(+), 107 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1d31a517b..da167aa03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ num-traits = "0.2" serde = "1.0" serde_json = "1.0" indexmap = "2.0" -itertools = "0.14" +itertools = "0.15" rayon = "1.11" petgraph = "0.8" rustdct = "0.7" diff --git a/crates/feos-core/src/ad/mod.rs b/crates/feos-core/src/ad/mod.rs index b8d94ab6c..1965207da 100644 --- a/crates/feos-core/src/ad/mod.rs +++ b/crates/feos-core/src/ad/mod.rs @@ -1,3 +1,4 @@ +//! Automatic differentiation with respect to model parameters. use crate::Residual; use nalgebra::{Const, DefaultAllocator, Dim, U1, allocator::Allocator}; use num_dual::{Derivative, DualNum, DualSVec}; diff --git a/crates/feos-core/src/parameter/association.rs b/crates/feos-core/src/parameter/association.rs index 00bc76759..ece505d07 100644 --- a/crates/feos-core/src/parameter/association.rs +++ b/crates/feos-core/src/parameter/association.rs @@ -46,15 +46,15 @@ impl AssociationRecord { /// Binary association parameters. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct BinaryAssociationRecord { - // Identifier of the association site on the first molecule. + /// Identifier of the association site on the first molecule. #[serde(skip_serializing_if = "String::is_empty")] #[serde(default)] pub id1: String, - // Identifier of the association site on the second molecule. + /// Identifier of the association site on the second molecule. #[serde(skip_serializing_if = "String::is_empty")] #[serde(default)] pub id2: String, - // Binary association parameters + /// Binary association parameters #[serde(flatten)] pub parameters: A, } @@ -73,10 +73,14 @@ impl BinaryAssociationRecord { } } +/// The definition of an individual association site. #[derive(Clone, Debug)] pub struct AssociationSite { + /// The index of the component (or group) that the association site is on. pub assoc_comp: usize, + /// The identifier of the site (if there are multiple sites on one component/group). pub id: String, + /// The multplicity of the association site (NA/NB/NC). pub n: f64, } @@ -86,6 +90,8 @@ impl AssociationSite { } } +/// The combining rule for association parameters that is used as fallback +/// if no binary association parameters are available. pub trait CombiningRule

{ fn combining_rule(comp_i: &P, comp_j: &P, parameters_i: &Self, parameters_j: &Self) -> Self; } diff --git a/crates/feos-core/src/parameter/chemical_record.rs b/crates/feos-core/src/parameter/chemical_record.rs index 3bbeee438..29e9be143 100644 --- a/crates/feos-core/src/parameter/chemical_record.rs +++ b/crates/feos-core/src/parameter/chemical_record.rs @@ -122,6 +122,11 @@ impl std::fmt::Display for ChemicalRecord { write!(f, "\n\tbonds={:?}\n)", self.bonds) } } + +/// The type that is used to account for the multiplicity of groups. +/// +/// In practice `f64` for group-based models and `()` for segment-based +/// models. pub trait GroupCount: Copy { #[expect(clippy::type_complexity)] fn into_groups( diff --git a/crates/feos-core/src/parameter/mod.rs b/crates/feos-core/src/parameter/mod.rs index 350773c71..99cdd6040 100644 --- a/crates/feos-core/src/parameter/mod.rs +++ b/crates/feos-core/src/parameter/mod.rs @@ -31,6 +31,7 @@ pub use model_record::{ SegmentRecord, }; +/// Parameters for one specific pure component or segment/group. #[derive(Clone)] pub struct PureParameters { pub identifier: String, @@ -65,6 +66,7 @@ impl PureParameters { } } +/// Parameters for one specific binary interaction. #[derive(Clone, Copy)] pub struct BinaryParameters { pub id1: usize, @@ -84,6 +86,10 @@ impl BinaryParameters { } } +/// The most general representation of model parameters. +/// +/// All parameter structs are type aliases of this struct. +/// See [Parameters], [GcParameters] and [IdealGasParameters]. pub struct GenericParameters { pub pure: Vec>, pub binary: Vec>, @@ -93,8 +99,10 @@ pub struct GenericParameters { data: Data, } +/// Representation of component-specific parameters (no bonds, no counts). pub type Parameters = GenericParameters>, Vec>)>; +/// Representation of group-specific parameters. pub type GcParameters = GenericParameters< P, B, @@ -108,6 +116,7 @@ pub type GcParameters = GenericParameters< Vec>, ), >; +/// Representation of parameters for ideal gas models (no association, no binary interactions). pub type IdealGasParameters = Parameters; impl GenericParameters { diff --git a/docs/api/ad.md b/docs/api/ad.md index cdd20c5d9..5aa52dd56 100644 --- a/docs/api/ad.md +++ b/docs/api/ad.md @@ -15,8 +15,8 @@ The currently available models are: |`PcSaftNonAssoc`|The PC-SAFT equation of state including a dipolar contribution but no association|`m`, `sigma`, `epsilon_k`, `mu`|`k_ij`| |`PcSaftFull`|The PC-SAFT equation of state with a dipolar contribution and association|`m`, `sigma`, `epsilon_k`, `mu`, `kappa_ab`, `epsilon_k_ab`, `na`, `nb`|`k_ij`| -## Properties -Currently the following phase equilibrium properties are available in the AD interface of FeOs. We plan to extend the list in the future. +## Properties +All properties that have parallel automatic differentiation with respect to model parameters enabled are available from the `Property` class ```{eval-rst} .. currentmodule:: feos @@ -24,15 +24,26 @@ Currently the following phase equilibrium properties are available in the AD int .. autosummary:: :toctree: generated/ - vapor_pressure_derivatives - liquid_density_derivatives - equilibrium_liquid_density_derivatives - bubble_point_pressure_derivatives - dew_point_pressure_derivatives + Property ``` +Currently the following phase equilibrium properties are available in the AD interface of FeOs. We plan to extend the list in the future. + +|Property|Pure/Binary|Inputs| +|-|-|-| +|`vapor_pressure`|Pure|Temperature| +|`boiling_temperature`|Pure|Pressure| +|`liquid_density`|Pure|Temperature, Pressure| +|`equilibrium_liquid_density`|Pure|Temperature| +|`enthalpy_of_vaporization`|Pure|Temperature| +|`residual_isobaric_heat_capacity`|Pure|Temperature, Pressure| +|`bubble_point_pressure`|Binary|Temperature, x1, Presssure estimate| +|`dew_point_pressure`|Binary|Temperature, y1, Presssure estimate| + +For all properties `xyz`, `Property` contains a method `Property.xyz(eos, input)` which uses a model from the [`EquationOfState`](eos.md#the-equationofstate-class) class. This evaluation can be useful for comparisons of the same data to established models. For parameter estimation or learning, the `Property.xyz_derivatives(model, parameter_names, parameters, input)` methods can be used to determine values and derivatives with respect to the model parameters indicated in `parameter_names`. + ## Examples -The following example calculates pure-component vapor pressures including their derivatives with respect to the core PC-SAFT parameters for 10 Million temperatures in little more than two seconds. +The following example calculates pure-component vapor pressures including their derivatives with respect to the core PC-SAFT parameters for 10 Million temperatures in merely two seconds. ```python import feos @@ -45,14 +56,14 @@ fit_params = ["m", "sigma", "epsilon_k"] parameters = np.array([[1.5, 3.4, 230.0, 2.3]] * n) temperature = np.expand_dims(np.linspace(250.0, 400.0, n), 1) eos = feos.EquationOfStateAD.PcSaftNonAssoc -%time feos.vapor_pressure_derivatives(eos, fit_params, parameters, temperature) +%time feos.Property.vapor_pressure_derivatives(eos, fit_params, parameters, temperature) ``` ``` -CPU times: user 1min 39s, sys: 654 ms, total: 1min 40s -Wall time: 2.3 s +CPU times: user 1min 38s, sys: 611 ms, total: 1min 39s +Wall time: 1.98 s ``` -For the most complex case, a binary mixture of cross-associating mixtures, the following example calculates 100.000 bubble point pressures and their derivative with respect to the binary interaction parameter in 3 seconds. +For the most complex case, a binary mixture of cross-associating mixtures, the following example calculates 100.000 bubble point pressures and their derivative with respect to the binary interaction parameter in less than 5 seconds. ```python import feos import numpy as np @@ -72,9 +83,9 @@ molefracs = np.array([0.5] * n) pressure = np.array([1e5] * n) input = np.stack((temperature, molefracs, pressure), axis=1) eos = feos.EquationOfStateAD.PcSaftFull -%time feos.bubble_point_pressure_derivatives(eos, fit_params, parameters, input) +%time feos.Property.bubble_point_pressure_derivatives(eos, fit_params, parameters, input) ``` ``` -CPU times: user 3min 11s, sys: 15.9 ms, total: 3min 11s -Wall time: 3.17 s +CPU times: user 4min 52s, sys: 87.1 ms, total: 4min 53s +Wall time: 4.74 s ``` \ No newline at end of file diff --git a/docs/recipes/recipes_automatic_differentiation.ipynb b/docs/recipes/recipes_automatic_differentiation.ipynb index f30608925..e217b79b4 100644 --- a/docs/recipes/recipes_automatic_differentiation.ipynb +++ b/docs/recipes/recipes_automatic_differentiation.ipynb @@ -37,8 +37,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 1min 39s, sys: 654 ms, total: 1min 40s\n", - "Wall time: 2.3 s\n" + "CPU times: user 1min 39s, sys: 603 ms, total: 1min 39s\n", + "Wall time: 1.96 s\n" ] }, { @@ -71,7 +71,7 @@ "parameters = np.array([[1.5, 3.4, 230.0, 2.3]] * n)\n", "temperature = np.expand_dims(np.linspace(250.0, 400.0, n), 1)\n", "eos = feos.EquationOfStateAD.PcSaftNonAssoc\n", - "%time feos.vapor_pressure_derivatives(eos, fit_params, parameters, temperature)" + "%time feos.Property.vapor_pressure_derivatives(eos, fit_params, parameters, temperature)" ] }, { @@ -92,8 +92,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 1min 24s, sys: 716 ms, total: 1min 25s\n", - "Wall time: 2.1 s\n" + "CPU times: user 1min 34s, sys: 571 ms, total: 1min 34s\n", + "Wall time: 1.86 s\n" ] }, { @@ -127,7 +127,7 @@ "pressure = np.array([1e5] * n)\n", "input = np.stack((temperature, pressure), axis=1)\n", "eos = feos.EquationOfStateAD.PcSaftNonAssoc\n", - "%time feos.liquid_density_derivatives(eos, fit_params, parameters, input)" + "%time feos.Property.liquid_density_derivatives(eos, fit_params, parameters, input)" ] }, { @@ -148,23 +148,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 11min 21s, sys: 122 ms, total: 11min 21s\n", - "Wall time: 11.3 s\n" + "CPU times: user 13min 32s, sys: 1.48 s, total: 13min 33s\n", + "Wall time: 13.3 s\n" ] }, { "data": { "text/plain": [ "(array([ 5142.13808145, 5142.20830389, 5142.27852715, ...,\n", - " 3828278.20909898, 3828290.44546341, 3828302.68185363],\n", + " 3828278.209099 , 3828290.4454634 , 3828302.68185364],\n", " shape=(1000000,)),\n", " array([[ 40721.23744125],\n", " [ 40721.73456205],\n", " [ 40722.23168782],\n", " ...,\n", - " [10996502.56325178],\n", - " [10996528.1146768 ],\n", - " [10996553.66610128]], shape=(1000000, 1)),\n", + " [10996502.56325193],\n", + " [10996528.11467696],\n", + " [10996553.66610118]], shape=(1000000, 1)),\n", " array([ True, True, True, ..., True, True, True], shape=(1000000,)))" ] }, @@ -189,7 +189,7 @@ "pressure = np.array([1e5] * n)\n", "input = np.stack((temperature, molefracs, pressure), axis=1)\n", "eos = feos.EquationOfStateAD.PcSaftNonAssoc\n", - "%time feos.bubble_point_pressure_derivatives(eos, fit_params, parameters, input)" + "%time feos.Property.bubble_point_pressure_derivatives(eos, fit_params, parameters, input)" ] }, { @@ -210,8 +210,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 3min 11s, sys: 15.9 ms, total: 3min 11s\n", - "Wall time: 3.17 s\n" + "CPU times: user 4min 55s, sys: 55.7 ms, total: 4min 55s\n", + "Wall time: 4.78 s\n" ] }, { @@ -250,13 +250,13 @@ "pressure = np.array([1e5] * n)\n", "input = np.stack((temperature, molefracs, pressure), axis=1)\n", "eos = feos.EquationOfStateAD.PcSaftFull\n", - "%time feos.bubble_point_pressure_derivatives(eos, fit_params, parameters, input)" + "%time feos.Property.bubble_point_pressure_derivatives(eos, fit_params, parameters, input)" ] } ], "metadata": { "kernelspec": { - "display_name": "feos", + "display_name": "feos_devel", "language": "python", "name": "python3" }, @@ -270,7 +270,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.14.0" } }, "nbformat": 4, diff --git a/docs/theory/eos/properties.md b/docs/theory/eos/properties.md index 79f005e25..89ef27fe1 100644 --- a/docs/theory/eos/properties.md +++ b/docs/theory/eos/properties.md @@ -70,20 +70,20 @@ Due to different language paradigms, $\text{FeO}_\text{s}$ handles the ideal gas | Partial derivative of pressure w.r.t. volume | $\left(\frac{\partial p}{\partial V}\right)_{T,n_i}$ | no | yes | | Partial derivative of pressure w.r.t. density | $\left(\frac{\partial p}{\partial \rho}\right)_{T,n_i}$ | no | yes | | Partial derivative of pressure w.r.t. temperature | $\left(\frac{\partial p}{\partial T}\right)_{V,n_i}$ | no | yes | -| Partial derivative of pressure w.r.t. moles | $\left(\frac{\partial p}{\partial n_i}\right)_{T,V,n_j}$ | no | yes | +| Partial derivative of pressure w.r.t. moles | $n\left(\frac{\partial p}{\partial n_i}\right)_{T,V,n_j}$ | no | yes | | Second partial derivative of pressure w.r.t. volume | $\left(\frac{\partial^2 p}{\partial V^2}\right)_{T,n_i}$ | no | yes | | Second partial derivative of pressure w.r.t. density | $\left(\frac{\partial^2 p}{\partial \rho^2}\right)_{T,n_i}$ | no | yes | | Partial molar volume $v_i$ | $\left(\frac{\partial V}{\partial n_i}\right)_{T,p,n_j}$ | no | no | | Chemical potential $\mu_i$ | $\left(\frac{\partial A}{\partial n_i}\right)_{T,V,n_j}$ | yes | yes | | Partial derivative of chemical potential w.r.t. temperature | $\left(\frac{\partial\mu_i}{\partial T}\right)_{V,n_i}$ | yes | yes | -| Partial derivative of chemical potential w.r.t. moles | $\left(\frac{\partial\mu_i}{\partial n_j}\right)_{V,n_k}$ | no | yes | +| Partial derivative of chemical potential w.r.t. moles | $n\left(\frac{\partial\mu_i}{\partial n_j}\right)_{V,n_k}$ | no | yes | | Logarithmic fugacity coefficient $\ln\varphi_i$ | $\beta\mu_i^\mathrm{res}\left(T,p,\lbrace n_i\rbrace\right)$ | no | no | | Pure component logarithmic fugacity coefficient $\ln\varphi_i^\mathrm{pure}$ | $\lim_{x_i\to 1}\ln\varphi_i$ | no | no | | Logarithmic (symmetric) activity coefficient $\ln\gamma_i$ | $\ln\left(\frac{\varphi_i}{\varphi_i^\mathrm{pure}}\right)$ | no | no | | Henry's law constant $H_{i,s}$ | $\lim_{x_i\to 0}\frac{y_ip}{x_i}=p_s^\mathrm{sat}\frac{\varphi_i^{\infty,\mathrm{L}}}{\varphi_i^{\infty,\mathrm{V}}}$ | no | no | | Partial derivative of the logarithmic fugacity coefficient w.r.t. temperature | $\left(\frac{\partial\ln\varphi_i}{\partial T}\right)_{p,n_i}$ | no | no | | Partial derivative of the logarithmic fugacity coefficient w.r.t. pressure | $\left(\frac{\partial\ln\varphi_i}{\partial p}\right)_{T,n_i}=\frac{v_i^\mathrm{res,p}}{RT}$ | no | no | -| Partial derivative of the logarithmic fugacity coefficient w.r.t. moles | $\left(\frac{\partial\ln\varphi_i}{\partial n_j}\right)_{T,p,n_k}$ | no | no | +| Partial derivative of the logarithmic fugacity coefficient w.r.t. moles | $n\left(\frac{\partial\ln\varphi_i}{\partial n_j}\right)_{T,p,n_k}$ | no | no | | Thermodynamic factor $\Gamma_{ij}$ | $\delta_{ij}+x_i\left(\frac{\partial\ln\varphi_i}{\partial x_j}\right)_{T,p,\Sigma}$ | no | no | | Molar isochoric heat capacity $c_v$ | $\left(\frac{\partial u}{\partial T}\right)_{V,n_i}$ | yes | yes | | Partial derivative of the molar isochoric heat capacity w.r.t. temperature | $\left(\frac{\partial c_V}{\partial T}\right)_{V,n_i}$ | yes | yes | diff --git a/docs/tutorials/eos/core_user_defined_eos.ipynb b/docs/tutorials/eos/core_user_defined_eos.ipynb index 58ff52449..9b3eae6fe 100644 --- a/docs/tutorials/eos/core_user_defined_eos.ipynb +++ b/docs/tutorials/eos/core_user_defined_eos.ipynb @@ -225,8 +225,8 @@ "source": [ "### Thermodynamic state: the `State` object\n", "\n", - "Before we can compute a property, we create a `State` object. This can be done in several ways depending on what control variables we need.\n", - "If no total amount of substance is defined, it is set to $n = \\frac{1}{N_{AV}}$.\n", + "Before we can compute a property, we create a `State` object. This can be done in several ways depending on what variables are specified.\n", + "If no total amount of substance is defined, the state can only be used to calculate intesive properties.\n", "For possible input combinations, you can inspect the signature of the constructor using `State?`." ] }, @@ -245,10 +245,7 @@ " temperature=\u001b[38;5;28;01mNone\u001b[39;00m,\n", " volume=\u001b[38;5;28;01mNone\u001b[39;00m,\n", " density=\u001b[38;5;28;01mNone\u001b[39;00m,\n", - " partial_density=\u001b[38;5;28;01mNone\u001b[39;00m,\n", - " total_moles=\u001b[38;5;28;01mNone\u001b[39;00m,\n", - " moles=\u001b[38;5;28;01mNone\u001b[39;00m,\n", - " molefracs=\u001b[38;5;28;01mNone\u001b[39;00m,\n", + " composition=\u001b[38;5;28;01mNone\u001b[39;00m,\n", " pressure=\u001b[38;5;28;01mNone\u001b[39;00m,\n", " molar_enthalpy=\u001b[38;5;28;01mNone\u001b[39;00m,\n", " molar_entropy=\u001b[38;5;28;01mNone\u001b[39;00m,\n", @@ -269,14 +266,8 @@ " Volume.\n", "density : SINumber, optional\n", " Molar density.\n", - "partial_density : SIArray1, optional\n", - " Partial molar densities.\n", - "total_moles : SINumber, optional\n", - " Total amount of substance (of a mixture).\n", - "moles : SIArray1, optional\n", - " Amount of substance for each component.\n", - "molefracs : numpy.ndarray[float]\n", - " Molar fraction of each component.\n", + "composition : float | SINumber | numpy.ndarray[float] | SIArray1 | list[float], optional\n", + " Composition of the mixture.\n", "pressure : SINumber, optional\n", " Pressure.\n", "molar_enthalpy : SINumber, optional\n", @@ -327,10 +318,10 @@ { "data": { "text/latex": [ - "$1.6605\\times10^{-24}\\,\\mathrm{ mol}$" + "$40.755\\,\\mathrm{\\frac{ mol}{m^{3}}}$" ], "text/plain": [ - "1.6605390671738466e-24 mol" + "40.75540388813771 mol/m³" ] }, "execution_count": 5, @@ -339,9 +330,8 @@ } ], "source": [ - "# If no amount of substance is given, it is set to 1/NAV.\n", "s = feos.State(eos, temperature=300*si.KELVIN, pressure=1*si.BAR)\n", - "s.total_moles" + "s.density" ] }, { @@ -351,11 +341,8 @@ "outputs": [ { "data": { - "text/latex": [ - "$1\\,\\mathrm{ mol}$" - ], "text/plain": [ - "1 mol" + "(1 mol, -40.78921616151256 J)" ] }, "execution_count": 6, @@ -364,13 +351,10 @@ } ], "source": [ - "s_pt = feos.State(\n", - " eos, \n", - " temperature=300*si.KELVIN, \n", - " pressure=1*si.BAR, \n", - " total_moles=1*si.MOL\n", - ")\n", - "s_pt.total_moles" + "# The `composition` argument is used to specify the composition AND total amount based on the unit of the argument.\n", + "# With the total amount specified, extensive properties can be evaluated.\n", + "s_pt = feos.State(eos, temperature=300*si.KELVIN, pressure=1*si.BAR, composition=1*si.MOL)\n", + "s_pt.total_moles, s_pt.helmholtz_energy(feos.Contributions.Residual)" ] }, { @@ -511,7 +495,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Heat of vaporization: 14.782343503305126 kJ/mol\n", + "Heat of vaporization: 14.782343503305128 kJ/mol\n", "for T = 300 K\n", "and p = 9.95 bar\n" ] @@ -540,8 +524,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "vapor pressure (T = 300 K): 994.7761635610095 kPa\n", - "boiling temperature (p = 3 bar): 247.84035574956758 K\n" + "vapor pressure (T = 300 K): 994.7761635610082 kPa\n", + "boiling temperature (p = 3 bar): 247.84035574956755 K\n" ] } ], @@ -599,7 +583,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAApUAAAGNCAYAAABAPIrMAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAXxJJREFUeJzt3Xd4VFX+BvD3pvfeOwmZNEogEFoAQ2gCUiIqgoVmwbYKLuoqu66K8rMrLCAqRRQBpRfpBKR3QholpPdCep3k/v4IGRNJQsjM5CaT9/M8PGbu3PKds7M3b+695xxBFEURRERERERK0JK6ACIiIiLq/BgqiYiIiEhpDJVEREREpDSGSiIiIiJSGkMlERERESmNoZKIiIiIlMZQSURERERK05G6gK4oNDQU+fn50NfXh4uLi9TlEBERkYZLTU1FZWUlrKyscPToUbUcg6FSAvn5+aioqEBFRQUKCwulLoeIiIi6iPz8fLXtm6FSAvr6+qioqICBgQG8vLykLoeIiIg0XHx8PCoqKqCvr6+2YzBUSsDFxQWFhYXw8vLC1q1bpS6HiIiINFx4eDiio6PV+tgdO+oQERERkdIYKomIiIhIaQyVRERERKQ0hkoiIiIiUhpDJREREREpjaGSiIiIiJTGUElERERESmOoJCIiIiKlMVQSERERkdI4o46GOpQSi/TSQnQ3t4WfpQMsDYylLomIiIg0GEOlBtqdEIkXIzY0WmZvZAZfSwf4WjrAz9IevpYO6G5uBwMdXYmqJCIiIk3CUKmBCqrK71mWVVaErLIiHEu7oVimLWjB08zmbtisC5q+Vg5wNbGElsAnI4iIiKj1GCo10GPdg3CnogwRaTcQdycThU2ETACoEWtxszAbNwuzsSvxr+XGOnrwuXtVsz5s8hY6ERERtYShUgPpa+vg1d6heLV3KERRRGZZEeLuZCLuTiZi7/73VkE2qmprmty+VF6FSznJuJST3Gi5vaEpfK0cG91C97awh742v0ZERERdHdOAhhMEAY7G5nA0Nkeoi49ieXVtDRKKchGX3zhsppTcaXZfWeXFyEorbnQLXUfQgpe5LQKsnRBg5Qj/u/+sDUzU+rmIiIioY2Go7KJ0tbQhs7CHzMIeE9Fbsby4qgI3CrIUITP2buhs7ha6XKzF9YIsXC/Iwtb4y4rl9kZmCLByRICVE/ytHBFg5Qh3U2toa/FZTSIiIk3EUEmNmOoZIMjOHUF27oplbbmFXt8x6EjqdcUyQx1d+Fk6Nrii6QQ/SwcY6eqp/XMRERGRejFU0n21dAv9VkEOYu5kICYvHTF3MhCdl4H8ytIm91Mur77nWU0BArqZWSuuaNb/czAygyAIav9sREREpBoMldRmulra8LNygJ+VAx716gOg7qpmVnkxovPSEZOfgZj8DETnpyOhKA8ixHv2IULE7aJc3C7Kxa7ESMVyK31j9LR2Qg9rZ8V/3U2tGDSJiIg6KIZKUilBEOBgZAYHIzOEufoqlpdVVyH2TiZi74bM6PwMxN7JQLm8usn95FeW4lj6TRxLv6lYZqZngB5WTuhp7awIm93MbPicJhERUQfAUEntwkhXD0F2bgiyc1Msq6mtRVJx3t2rmXVhMyY/A5llRU3uo6iqAqcyb+NU5u2/9qujhx7WTo3CpreFLXS0tNX+mYiIiOgvDJUkGW0tLXia28LT3BYTuvVSLM+vKEVUXjqi8tMRlZeGyNw0JBbnNbmPMnkVzmUl4lxWomKZvrYO/Cwd0cvGGT2sndDTyhkyS46nSUREpE78LUsdjpWBMYY5e2OYs7diWVFVBaLvhsxreXX/vVWYg1rx3uc0K2vkuJKbgiu5KYplulra8LGwRw9rJwTauKK3jQt8rRygyyuaREREKsFQSZ2CmZ4BBjl4YpCDp2JZWXUVYvIzcC0vDVH5dWHzxp0syMXae7avrq2pu/KZn46NNy8AqLui6W/liN42Lgi0cUFvG1d4mdtw3nMiIqI2YKikTstIVw/97N3Rz/6vMTUr5NWIu5OJqLz0u2EzHbH5GU2Op1lZI8flnBRczvnriqaxjh562jijt43r3aDpAlcTS/Y6JyIiug+GStIoBjq6CLR1RaCtq2JZdW0NbhZkITI3DZF5abiam4qY/AxUNxE0S+VVOJOZgDOZCYplVvrG6GXj3OiKpp2Rabt8HiIios6CoZI0nq6WNvytnOBv5YRp6A+g7ipl3J1MXM1NxdXcFFzJScXNwuwmn9HMryxFRNoNRDSY89zRyBy9bJwRaOOKQNu6oGmmZ9Bun4mIiKijYaikLklfWwe9797eBgYCAEqrKxGVl343aKbiSm4qkprpdZ5RVoiM5ELsT44BUDczkMzCDn1sXdHXzg19bd3gbW7HMTSJiKjLYKgkustYVx8DHLphgEM3xbI7lWWIvBsy64NmVhPjaIoQcb0gC9cLshQdgYx19BBo64q+tm7oa+uKPrZusDE0abfPQ0RE1J4YKolaYKlvhOHOMgx3limWZZYV4WpOSoOgmYLCqop7ti2VV+FkRjxOZsQrlrmbWqGPrVvdFU1bNwRYOUKP42cSEZEG4G8zogfkYGQGB/cAjHEPAADUirW4XZiLyzkpuJSTjEs5yYi9k9nk85lJxflIKs7H9ttXANTdhu9h5aS4Zd7X1g1OxubsbU5ERJ0OQyWRkrQELXS3sEN3Czs85h0EoO75zMi8NFzKTlaEzezy4nu2rayR42JOMi7mJCuW2Ruaop+9B/rbuaOfnTsCrJ04SDsREXV4DJVEamCsq99osHZRFJFeWqi4knkpOxlR+emorJHfs21WeTH2JF7DnsRrAABDHV0E2rii392QGWTnBgt9o3b9PERERPfDUEnUDgRBgLOJBZxNLPDI3XnOq2rkiMnPuBs0U3A5JxlJxfn3bFsur8bpzNs4nXlbsczHwh797NzR394dQXbu8DC15i1zIiKSFEMlkUT0tHUUA7XPvrsst7wEF7KTcD47CReyEnEtL63J2YDqe5r/cuMcAMDW0ARBtnUhs5+dB3paO7EDEBERtSv+1iHqQGwMTTDWPQBj73YCqpBXIzIvDRfuhszz2Um4U1l2z3Y55SXYlxyNfcnRAOo6AAXauCDYvm6IpH527jDR1W/Xz0JERF0LQyVRB2ago4tgew8E23sAPYdDFEXcLsrF+bsB80J2EuILc+7ZrrJGjrNZiTiblYilkUehJQjoae2MAfYeGGDfDcH2HrA0MG7/D0RERBqLoZKoExEEAV7mtvAyt8U0Wd2Uk/kVpbhYf8s8OwlXc1Pv6QBUK4qKcTVXRZ8AUPdc5kCHbnUh06EbHIzM2v3zEBGR5mCoJOrkrAyMMcrNH6Pc/AHUXaWMykvDuazEu/8Smhycvf65zHVxZwAAHqbWGODQDQPv3jJ3NbFk5x8iImo1hkoiDaOvrYMgu7pe4fN6DketWIu4O1k4m5mAs1l1/3LKS+7ZLrE4D4nFedh0d5pJRyNzRcgc5OgJTzMbhkwiImoWQyWRhtMStOBv5Qh/K0fM8h8MURSRUJSLM1kJiqCZWlJwz3YZZYXYfvuKYvYfeyMzDHbwxGBHLwxx9IKbqVX7fhAiIurQGCqJuhhBEOBpbgtPc1tMlwUDAFJL7tR17LkbMpvq/JNVVoRtt69g292Q6WJigcEOXhjsWPfPydi8PT8GERF1MAyVRAQXE0u4mFjiUa8+AICc8mKcy0rEmcwEnM68jbg7mfdsk1pSgM23LmLzrYsA6p7JHOJYHzI9YWto2q6fgYiIpMVQSUT3sDU0xXiPnhjv0RMAkFdRgtOZCTiVEY9TGfG41cSVzPpnMusHZJdZ2GGQQ13AHOzgySGMiIg0HEMlEd2XtYEJJnj0xIS7ITOrrAinMm7jVGY8TmXcRlJx3j3b3CjIxo2CbKyLOw0BAvytHBDi5I1hTt0RbN8Nhjq67f0xiIhIjRgqieiB2RuZYYpXIKZ4BQIA0koK6q5iZsbjZEY80ksLG60vQkR0fgai8zPwXdRx6GvroJ+dO4beDZk9rJ2gJWhJ8EmIiEhVGCqJSGnOJhZ4zDsIj3kHQRRFJBXnK65insqIR3Z5caP1K2vkOJlRF0CXXAQs9Y0Q4tgdQ527Y6hjd7iyZzkRUafDUElEKiUIAjzMrOFhZo3psmCIoohbhTk4kX4Lf6bfxKnM2yiprmy0zZ3KMuxKjMSuxEgAdZ1+hjl7Y6hTdwx28IK5vqEUH4WIiB4AQyURqZUgCPC2sIO3hR1m+Q9GdW0NruSk4Hj6TfyZfguXc1JQI9Y22iaxOA+JcXn4Ke4MtAQBgTauGOrUHcOcvBFk5wYdLW2JPg0RETWHoZKI2pWuljb623ugv70HFvQZhaKqCpzOiMfx9Fs4kXHrnjEya0URl3KScSknGd9cPQIzPQOEOHZHqIsPhjvLOD4mEVEHwVBJRJIy0zPAGPcAjHEPAFDX6efPu1cx/0y/hfzK0kbrF1VVYG9SFPYmRQEAfCzsEerig1BnGfrbe0BPm6c1IiIp8OxLRB2Ks4kFpsn6Y5qsP2rFWsTkZ+B4+i0cT7uJc1kJqKqtabT+9YIsXC/Iwsqo4zDW0cMQRy+EuvjgIWcZO/wQEbUjhkoi6rC0BC30sHZGD2tnvNRzOMqqq3AqMx5HU28gIu06korzG61fKq/CgZRYHEiJBQB0N7fFQ84yhLr4YIB9NxhwbEwiIrVhqCSiTsNIVw8jXf0w0tUPoigioSgPR9OuIyL1Bk5lxqOyRt5o/VuFObhVmIMfYk7CQFsXQxy9MNLVF2EuvnAysZDmQxARaSiGSiLqlARBgKe5DTzNbTDHfwjK5dU4m5WAo6nXEZF2454OPxU11TicGofDqXEAgAArR4x09UOYqy8CbVw4+DoRkZIYKolIIxjq6OIhZxkecpYBAJKL8xGRdgNHU6/jZEY8yuRVjdavn+Hnm6tHYGNggjBXH4S5+GGYszdMdPWl+AhERJ0aQyURaSQ3Uys84zsQz/gORGWNHOeyEnAoJQ6HUmLveRYzt6IEm25exKabF6GnpY1BDp4Ic/XFSFc/uLGzDxFRqzBUEpHG09fWwVAnbwx18sb7wRMQX5iDQylxOJgSiwvZSY0GX6+qrcGx9Js4ln4T/z67CzILO8VznEG2btDW4m1yIqKmMFQSUZciCAK6W9ihu4UdXuw5DHcqy3As7QYOpsQiIvU6CqsqGq1/oyAbNwqysfzaMVjpG2OUmx/GuvkjxMkbhuxNTkSkwFBJRF2apb4RJnsGYrJnIOS1NbiQnYSDKXE4nBKLW3/r7JNfWYpNNy9g080LMNLRw0POMoxxD0CYiw8s9I0k+gRERB2DxoTKnJwcnDx5ElFRUbh27RpiY2NRWVmJ4OBgrF+/vtntRowYgbS0tBb3HRkZCX19PrhPpOl0tLQx0METAx08saj/OCQU5eJwShwOpcThTOZtyBvcJi+TVylm9tERtDDI0RNj3AIw2s2fU0cSUZekMaFyz549+OSTT9q8vUwmg4mJSZPvCYLQ5v0SUefVzcwGcwNCMDcgBIWV5TiSeh37k6NxJPV6o97kcrFWMa3ke2d2oLeNC8a6BWCsewC6m9vyHEJEXYLGhEoTExMMHjwYPXv2RM+ePRETE4Ply5e3evv33nsPAwYMUGOFRNSZmesbYopXIKZ4BaJCXo2TGfHYlxyNA8kxyKtoPD/51dxUXM1Nxf9d2g9PMxuMcQvAOI8eCLRxYcAkIo2lMaFy6tSpmDp1quJ1VlaWhNUQkSYz0NFFmKsvwlx9sWTQFFzMScb+pGjsS46+Z7ii20W5WBF1DCuijsHZ2ALjPXpgvEcv9LHlgOtEpFk0JlQSEUlBW0sLwfYeCLb3wHv9x+F6QRb2JUVjf3IMruU1fl47rbQAq6JPYFX0CTgamWOcRw9M8OiJIDs3Bkwi6vQYKu/auHEjVq9ejYqKCtjY2KBfv3545JFHmn3Okojo7wRBgK+lA3wtHfB6YBjSSgqwLzkafyRF4WxmIkSIinUzygrxY8xJ/BhzEvaGpnj4bsDsb+fBsTCJqFNiqLxr7969jV7v3r0b33zzDb744gsMGTLkvttv3LgRmzdvbtWx4uPj21QjEXUuziYWmOM/BHP8hyC7rBj7kqOxJ/EaTmfeRq34V8DMKi/G2tjTWBt7GraGJnjYvQfGe/TEAHsP6GhpS/gJiIhar8uHyuDgYAwcOBA9e/aEk5MTqqurcfHiRXz77beIiYnBvHnz8OuvvyIgIKDF/eTk5CA6OrqdqiaizsbOyFQxbWRueYkiYJ7KuN1oRp+c8hL8FHcGP8WdgY2BCcZ79MAkz0D04y1yIurg2hQq33nnHZUcXBAEfPzxxyrZV1stWbKk0WtDQ0OEhoZi0KBBmD59OqKjo/HZZ59h7dq1Le7H1tb2vsGzXnx8PCoqKu6/IhFpJBtDEzzlMwBP+QxAfkXp3YAZhZPptxqNhZlbUYJ1cWewLu4MnIzN8Ui33pjUrRd6WjuzFzkRdThtCpXbtm2DIAgQG9y+eRD123aEUNkcAwMDvP7663juuedw9uxZFBYWwty8+QGNp02bhmnTprVq3+Hh4byqSUQAACsDY0yXBWO6LBh3KstwIDkGexKv4c/0W6iurVGsl15aiO+ijuO7qOPoZmaDSZ69Malbb3hb2ElYPRHRX9oUKidPntwl/kru27cvAKC2thYpKSkthkoiImVZ6hvhCe9+eMK7Hwoqy7AvKRo7EyJxIuNWo2cwE4py8fWVw/j6ymH4WzliUrfemNitF1xNrSSsnoi6ujaFyr/fMtZUurq6ip9rampaWJOISLUs9I0wTdYf02T9kVNejD2JUdhx+wrOZyc1Wi8mPwMx+Rn45OI+BNm6YaJnbzzi0Qt2RqYSVU5EXVWX76jTkhs3bih+dnBwkLASIurKbA1NMdNvEGb6DUJaSQF2JlzFjttXEZWf3mi9iznJuJiTjP+e242hTt541KsPxroFwEhXT6LKiagrYahswffffw8A6N69O+zt7SWuhoiobpiieT2HY17P4YgvzMGO21exI+Eq4gtzFOvUiiKOpd3AsbQbMNbRw8PuPfBo9z4Y7ODFMTCJSG3UEipjYmKwa9cuREVFIT+/bsoyKysr9OzZExMmTIC/v786DvvAfvzxR+jp6WHChAmwtLRULL9z5w6++uor7N+/HwDw2muvSVUiEVGzvMxtMb/PSLwRGIaY/Axsv30VOxOuIq20QLFOqbwKv8dfwu/xl+BgZIbJnoF41Ksv/Kx494WIVEsQ29qFuwllZWVYtGiRYiDxv++6vnPPuHHj8OGHH8LIyEhVh0ZGRgYmT56seF1VVYWysjLo6Og0mhVn7ty5eO655wAAixcvxk8//QRBEODs7AwrKytUVFTg9u3bkMvl0NLSwvz58xXrq0p97++AgABs3bpVpfsmoq6tVqzF2axEbLl1CXsSr6G4urLJ9fytHPGoVx9M9gyEvZFZO1dJRO2tPbKHyq5U1tbW4qWXXsLZs2chiiJsbW0xcOBAxbOImZmZOHv2LLKzs7F3717k5+dj9erVKutFXlNTg4KCgnuWy+XyRssbjg85fvx4AEBkZCTS09MRFxcHbW1tuLi4IDg4GNOnT4efn59K6iMiag9aghYGOXhikIMnPhw4CYdSYrEl/hIiUm80GgOzvoPP4gt/YKhjdzzavS8edu8BQx3dFvZORNQ8lYXK7du348yZM9DR0cHbb7+N6dOnQ+tvz+7U1tbi119/xSeffIIzZ85gx44dja4uKsPFxQXXr19/oG0CAwMRGBiokuMTEXU0hjq6eKRbLzzSrRfyKkqw4/ZVbIm/jKu5qYp1akURx9Jv4lj6TZjp7cCkbr3xuHc/BNq4dImh44hIdVQWKnfu3AlBELBw4UI89dRTTa6jpaWFGTNmoKamBh9//DG2b9+uslBJRETNszYwwWz/IZjtPwS3CrKxJf4ytsZfbvT8ZVFVBdZfP4v118/Cx8IeT3j3Q7hXH9gYmjS/YyKiu1TWDbD+1vHjjz9+33Uff/xx6OjoIDY2VlWHJyKiVupuYYe3gsbg9GML8dvDz+MJ734w1mk87ND1gix8cH4P+m36GHMO/4SDyTGQ13K8XiJqnsquVJaWlsLY2BgGBgb3XdfAwADGxsYoKytT1eGJiOgBNXz+8oMBj2BP4jVsunkRZ7MSFOvIxVrsT47B/uQY2BmaItyrD57w7sfpIYnoHioLlZaWlsjNzUVeXh6sra1bXDcvLw9FRUWwsbFR1eGJiEgJxrr6eNy7Hx737ofbhbn47dZF/HbrIjLLihTrZJcXY2XUcayMOo4gWzdMk/XHxG69YKyrL2HlRNRRqOz2d2BgIERRxNKlS++77rfffgtRFBVzaxMRUcfhaW6Dt4LG4Oxjb2P9qFmY4NETulrajda5mJOMf57cgqBNH+Nfp7cjOi+9mb0RUVehsiuVM2bMwIEDB7Bp0yaUlpbilVdegbu7e6N1kpKSsHTpUuzevRuCIGDGjBmqOjwREamYtpYWQl18EOrig/yKUmy7fQWbbl5ATH6GYp2S6kr8FHcGP8WdQaCNK2b4BGNSt96cGpKoC1JZqBwwYACeffZZrFu3Drt378bu3bvh6OgIO7u6526ysrKQmZmpWH/mzJkIDg5W1eGJiEiNrAyMMcd/COb4D0FUXhp+vXEeW+MvNxpc/UpuCq7kpuCDc7sR7tUHM3yC4W/lJGHVRNSeVDpN4zvvvANXV1csXboUhYWFSE9PR3p641siFhYWePXVV3mVkoiok+ph7YzFg5zxbr9x2JlwFb/cOIfLOSmK94urK7Eu7gzWxZ1BH1tXzJAFYyKvXhJpPJXP/f3UU0/hsccew8mTJxEVFYW8vDwAgLW1NXr06IEhQ4ZAX58PdRMRdXZGunqYJuuPabL+iMlPxy/Xz91z9fJyTgou56Tgv+d249HufTHTdxC6s+c4kUZSeagEAH19fYwYMQIjRoxQx+6JiKiD8bdywuJBkxVXL3++fg5XchtfvVwbexprY08jxLE7ZvoNwkhXX+j8rQMQEXVeagmVRETUNTW8ehmdl45fbtRdvSxpcPXyRMYtnMi4BSdjczzjOxBPyvrD2oCz9hB1dmoJlTU1NUhMTERRURHkcnmL6/bv318dJRARkcQCrJ3w8aDJeLffw9h2+wrWxp5G3J2/OmymlxZiycX9+PLyITzSrRdm+g1GH1tXCSsmImWoNFRmZmbiyy+/xMGDB1FRUXHf9QVBQExMjCpLICKiDsZYVx9P+QzADFkwzmUlYm3safyRFAW5WAsAqKqtwZb4y9gSfxm9bVww03cQHunWCwY6uhJXTkQPQmWhMiUlBU8++STy8vIgimKrtmntekRE1PkJgoABDt0wwKEbMsuK8Mv1s/jl+jlklxcr1rmam4o3TvyGD87vwQyfYMz0GwwHIzMJqyai1lJZqPzyyy+Rm5sLKysrLFiwACEhIbCxsYG2Nh/CJiKixhyMzLCgzyi82isU+5KisTbuNM5lJSrev1NZhmWREfgu6k880q0XnvMPQU8bZ+kKJqL7UlmoPHXqFARBwJdffomBAweqardERKTB9LR1MNGzNyZ69kZMfjrWxp7B1vjLqKipBgBU19Zga/xlbI2/jAH23fBcQAhGufpBW0tlswwTkYqoLFRWVVXBwMCAgZKIiNrE38oJnw4Jx7/6jcWvN85jTewppJcWKt4/m5WAs1kJcDe1wmz/IXjCux9MdDnuMVFHobI/9VxcXPiMJBERKc1C3wjzeg7HyakLsXz4k/f0CE8qzsd/zu5C/00f48Nze5BackeiSomoIZWFyocffhiVlZU4ffq0qnZJRERdmK6WNiZ69sauCS9jx/h5mODRE1qCoHi/uLoS30X/icG/f4qXI35FVF6ahNUSkcpC5ezZs+Hr64tFixYhJSXl/hsQERG1UpCdO1aGzsCpqQvxQsBQmDa47V0ritiRcBVjdy7F9P0/4s/0m7xzRiQBlT1TaWBggDVr1mDRokWYOHEixowZg549e8LY2LjF7SZPnqyqEoiISMO5mFhiUfB4vNFnJDbdvIDVMSeRVJyveP94+k0cT7+JHlZOmNdzOMZ79OBUkETtRKWDn6elpSE3Nxfl5eXYsWMHduzY0eL6giAwVBIR0QMz0dXHHP8hmOk7CH8kR2PFtWO4mpuqeD8qPx0vH/sV/3fRCs/3GIonvINgqKMnYcVEmk9loTIuLg5PP/00ysvLAQC6urqwtLTkOJVERKQ22lpamODRE+Pde+BU5m2suHYMEWk3FO8nl+TjvTM78OXlQ5jlPwgzfQfB0qDlO2hE1DYqC5VLly5FWVkZXF1d8eGHHyI4OBhaHEeMiIjagSAIGOLohSGOXojJT8eKa8exMyESNXengsyvLMUXlw9h+bVjeMpnAF7sMQz2nKmHSKVUlvouX74MQRDw1VdfYeDAgQyUREQkCX8rJywdPg0np/4Ts/0Gw7DBHOLl8mp8H30Cg3//FO+e3oG0kgLpCiXSMCpLfuXl5TA0NESPHj1UtUsiIqI2czGxxAcDJ+LcY2/jzT6jYKX/123vyho51sWdRsiWz/DPk1uQWJQnYaVEmkFlodLd3R1yuRw1NTWq2iUREZHSLA2M8XpgGM489hbeD54Ae0NTxXvVtTX49cZ5DN/6Bf5xfBNuFWRLWClR56ayUDl58mRUVVXhyJEjqtolERGRyhjp6mFuQAhOTl2IjwdNhrOxheK9GrEWW+IvI3TbV5h3dANi8jOkK5Sok1JZqHz66acxcOBA/Pvf/8bly5dVtVsiIiKVMtDRxTO+A/Hno2/isyGPwt3UWvGeCBG7EiMxesc3mHP4J0TnpUtYKVHnorLe3ytWrEBgYCBiYmIwffp0BAUFtWrw81deeUVVJRAREbWanrYOnpT1x2Pd+2JnQiSWXj2Km4V/3f7enxyD/ckxGOfeAwv6jIKPpb2E1RJ1fCoLlcuWLYNwd05WURRx4cIFXLx48b7bMVQSEZGUdLS0Ee7VB5M9e2NvUjS+vXqk0e3vvUlR+CMpGhO79cL8PiPhZW4rYbVEHZfKQmX//v1VtSsiIqJ2pyX8NZD6geQYfHHlkCJciqibX3xXYiTCPfvg9cAweJhZ32ePRF2LykLl+vXrVbUrIiIiyQiCgDHuARjl5oe9SdH48vJB3LjbK7xWFPF7/CVsu30Fj3Xvi9cDw+BiYilxxUQdA0coJyIiakL9lcuDk17HsuHT4Glmo3ivRqzFxpsXMHTL5/jX6e3IKiuSsFKijoGhkoiIqAXaWlqY7BmII1PewFchj8Hd1ErxXnVtDX6KO4OQLZ/h04v7UVRVIWGlRNJiqCQiImoFHS1tPOYdhIjwBfh0SHijcS7L5dX4NvIohvz+KVZF/YkKebV0hRJJhKGSiIjoAehqaWO6LBjHH30THw6YCBsDE8V7dyrL8MH5PRi+9Qv8dvMiamprJayUqH0xVBIREbWBvrYOZvkPxsmp/8SCPiNhrKOneC+ttABvnPgNo3d8g4PJMRBFUcJKidoHQyUREZESjHX18UbgSJycuhCz/QZDV0tb8d71gizMOvwTwveuxIWsJAmrJFI/hkoiIiIVsDE0wQcDJ+JY+AKEe/WBAEHx3vnsJEzeuwIvHv0FScV5ElZJpD4MlURERCrkZmqFb4c9gf2TXkWos0+j93YnXkPo1i/x0fm97ClOGoehkoiISA38rZywfvQsbB77HHrbuCiWV9XWYGXUcYT8/hl+ijsDeW2NhFUSqU6bQ+Xp06dRy15tRERELRrs6IVdE17CN8OegKORuWJ5fmUp/nV6O0Zt/wZHUq9LWCGRarQ5VM6aNQuDBg3CwoULsW/fPpSVlamyLiIiIo2hJWjhUa8+OP7oArzZZxQMdXQV790szMYzB9dgxoHViLuTKWGVRMpp89zfPj4+uH79Onbu3Ildu3ZBV1cXgwYNwogRIzBixAjY2tqqsk4iIqJOz1BHD68HhmGarD8+u3QAm29ehIi64YaOpd3AifRbeNZ3IBb0GQVzfUOJqyV6MIKoxOBZ6enpOHz4MA4dOoSLFy9CLpdDEAQIgoAePXpg5MiRCAsLg5eXlypr7vTCw8MRHR2NgIAAbN26VepyiIhIItF56fjvud04lXm70XJrA2O8EzQWj3sHQUtg9wdSXntkD6VCZUNFRUWIiIjAoUOHcOLECZSVlUEQ6oZTcHNzQ1hYGEaMGIGgoCDF8q6KoZKIiOqJoogDyTH44Pzee4YbCrRxxYcDJ6KPratE1ZGm6FShsqGqqiqcOXMGhw4dwtGjR5GTk1N3MEGApaUlQkNDMWLECISEhEBfX1/Vh+/wGCqJiOjvKuTV+D76BL6NPILyv80dPs27H94OGgsbQ5NmtiZqWacNlX8XGRmJQ4cO4fDhw4iPj687sCDAwMAAgwcPRlhYGMLCwmBubn6fPWkGhkoiImpOekkBPrqwFzsTIhstN9MzwII+o/Cs70DoNJi1h6g12iN7tMuDGr169cL8+fOxZ88e7N+/HwsXLkSfPn1QWVmJw4cP491338X69evboxQiIqIOzcnEAssfmo5NY5+Dj4W9YnlRVQX+c3YXHt65FBezkyWskKhp7f70r7u7O2bPno0NGzbgxIkT+OijjxAaGgpDQ/ZyIyIiqjfE0Qv7J72G/w54BGZ6BorlsXcyMXnPCvzr9HYUVpZLWCFRY20eUkgVrKysMHXqVEydOlXKMoiIiDokHS1tzPEfgkndeuOTi39g082LAAARIn6KO4N9SdH4T/AETOzWq8t3giXpcZwCIiKiDs7G0ARfhDyGreNehMzCTrE8u7wYLx/7FU8dWI3EorwW9kCkfgyVREREnUSwvQf2TXwNbweNgb72Xzcbj6XfRNj2r7D06lFU1cglrJC6MoZKIiKiTkRPWwev9ArF4clvYLizTLG8skaO/7u0H2N3fovzWYnSFUhdFkMlERFRJ+RhZo2fR83C/4Y/CdsG41feKMhG+N7v8O8zO1FaXSlhhdTVMFQSERF1UoIgYJJnb0RMWYBnfAdCQF1nHREiVseewsjtX+NE+i2Jq6SuQtLe36qUk5ODkydPIioqCteuXUNsbCwqKysRHBx83zEwq6ursW7dOuzcuRPJycnQ1dWFr68vnn76aYwePbqdPgEREVHbmOsb4uNBk/GoVx+8eWILbhZmAwBSSu5g2v4fMF0WjPf6j2s0NBGRqmlMqNyzZw8++eSTB96usrISs2bNwsWLF6GtrY3u3bujvLwc586dw7lz5/Dcc8/hzTffVEPFREREqhVk5459k17DN1cO43/XjqFGrAUAbLhxDkdS4/B/g8MR5uorcZWkqTTm9reJiQkGDx6MF154AcuWLcNLL73Uqu0+++wzXLx4ES4uLti9ezd27tyJgwcPYvny5dDT08P333+PI0eOqLl6IiIi1dDX1sHCoDHY88jL8LdyVCzPLCvCs4fW4rXjm3CnolTCCklTaUyonDp1KtasWYP58+dj1KhRsLa2vu82ubm52LhxIwBg8eLF8PT0VLwXFhaGuXPnAgCWLVumnqKJiIjUpIe1M/Y88goW9h0NvQZzhW+Nv4yR27/GkdTrElZHmqjNt7/9/PyUPriBgQGsrKzg5+eHCRMmYOzYsUrv80EcOXIE1dXV8PDwwMCBA+95f9q0aVi+fDmio6ORnJwMNze3dq2PiIhIGbpa2nit9wiMcQvAghO/40puCgAgq7wYzxxcg6d8BmBR/3Ew1tWXuFLSBG2+UimKotL/ysvLkZaWhsOHD+ONN97Ayy+/jNraWlV+vhZduXIFABAUFNTk+/b29nBxcWm0LhERUWfjY2mPHePn4b1+4xpdtfz5+lmM3vEtLmQlSVgdaYo2X6l85ZVXlD54dXU1cnNzceHCBSQlJeHIkSP49ddfMWPGDKX33RqJiYkA0OIVSDc3N6SmpiIhIaHFfW3cuBGbN29u1XHj4+NbXSMREZEqaGtp4cWew/CQiwz/OL4J0fkZAICk4jyE/7ESL/UcjvmBI6GnrTF9eKmdSRoqG3r//fexceNG7Ny5s91CZWFhIQDA3Ny82XXq3ysqKmpxXzk5OYiOjlZdcURERGrga+mAXRNextdXDmPZtQjUiiJqRRHLIiNwJPU6vhn6BPysHKQukzqhDvPnyBtvvIGNGzfi9u3b7XbMysq6mQZ0dXWbXUdPTw8AUFFR0eK+bG1tERAQ0KrjxsfH33d/RERE6qJ3t4d4mKsv/nF8MxKL8wAAMfkZGL9rKd4OGou5AUOgJWhMf15qB5KGyuLiYpiamgKouyJobm5+3yuCqqSvX/dgcnV1dbPrVFVVAajrVNSSadOmYdq0aa06bnh4OK9qEhGR5ILs3HFg0j/w0YW9+CnuDACgqrYGH5zfg+PpN/HV0Mdga2gqcZXUWajsT5Cvv/76gdYvLi7GrFmzGi1buXIl1q1bp6qS7svMzAzAX7fBm1L/Xv26REREmsRIVw8fD5qM9aNmwb5BgIxIu4HRO77BsbQbElZHnYnKQuXKlSvx66+/tmrd0tJSzJkz556rdX369EFwcLCqSrovDw8PAEBSUvO93pKTkxutS0REpIlCXXxwcPLrGOX615CBOeUlmHFgNT48vxdVNXIJq6POQGWhUltbGx999BEOHjzY4nqlpaWYO3cuIiMjYWtrq6rDt0lgYCAA4NKlS02+n5WVhdTU1EbrEhERaSorA2OsDnsGHw6YCP0GvcC/izqOyXtW4HZhroTVUUenslD50UcfoaamBm+++SYuXLjQ5DplZWV4/vnncfnyZdjY2GDt2rWqOnybhIWFQVdXF4mJiThz5sw979fPtuPv7w93d/f2Lo+IiKjdCYKAWf6DsWvCy/A2t1Msj8xLw8M7v8XW+MsSVkcdmcpC5ZQpUzB//nxUVlbi5Zdfxs2bNxu9X15ejhdeeAEXL16EtbU11q1b12haRCnY2NjgiSeeAAC8++67jXqeHzlyBD/88AMA4OWXX5akPiIiIqn4Wzli78RXMEP212NppfIqvHZ8E945tQ0V8uY7uVLXpNLe388//zyys7Px888/Y+7cudi4cSMcHR1RUVGBF198EefPn4eVlRXWrFkDLy8vVR4aGRkZmDx5suJ1fa/tS5cuYcCAAYrlc+fOxXPPPad4/c9//hPR0dG4fPkyJkyYAG9vb5SVlSmepZw9ezZGjhyp0lqJiIg6A0MdPfzfkHAMc/bGwpNbUFhVNxze+utncSU3Fd+FzoCbqZXEVVJHofIBqN577z2MHTsWWVlZmDNnDrKysjBv3jycPXsWFhYWWL16NWQymaoPi5qaGhQUFCj+lZWVAQDkcnmj5X8fH9LAwAA//fQTFixYAC8vLyQmJuLOnTsIDg7Gt99+i7feekvltRIREXUm4z164o+Jr6G3jYti2bW7t8MPJMdIWBl1JIIoiqKqd1pdXY05c+bg3LlzMDAwQEVFBczNzbF27Vr4+fndfwcarn6cyoCAAGzdulXqcoiIiFqlskaOD87txrq4xv0Q5vUYjreCRkOnwbzi1LG0R/ZQy1D5urq6WL58Ofz8/FBRUQEzMzOsXr2agZKIiKgT09fWweJBk7Fs+DQY6egplq+IOoYn9n2PrLL2m8CEOp42PVO5bNmyVq3Xs2dPxMbGIigoCEePHsXRo0fvWUfVc4gTERGRek32DESAlRNeOPozbhRkAwDOZiVi3M6lWDXiaQTZuUlcIUmhzaFSEIRWrx8REYGIiIgm32OoJCIi6ny8Leywe8IrePv0NsUwQ1nlxXjsj+/w8aDJmCbrL3GF1N7aFCr79+cXhYiIqKsz0tXDN0MfR5CtG/5zdhfkYi2qamvw5sktuJaXjvcHTIAun7PsMtoUKtevX6/qOoiIiKgTEgQBz/oNgo+lPV44+gvyKkoBAOviTiPuTia+C50BG0MTiauk9qCWjjpERETUtQx08MQfj7yKXtbOimVnsxIwbtdSROamSlgZtReGSiIiIlIJJxMLbBn3IsK9+iiWpZcWYsreldh5+6qElVF7YKgkIiIilTHU0cU3Qx/H+8EToC3UxYzKGjleOvYrvrlyGGoYHps6CIZKIiIiUilBEDA3IAS/jJ4NC30jxfLPLh/E639uRmWNXMLqSF0YKomIiEgtQpy6Y+f4l9DNzEaxbEv8ZTy5/wfk3+3QQ5qDoZKIiIjUxtPcBjsnvISBDt0Uy85lJeKR3ctx6+7A6aQZGCqJiIhIrSz1jbBh9Bw83j1IsSypOA+T9izH6czbElZGqsRQSURERGqnp62DL0Km4u2gsYplhVUVmLH/R+xOvCZhZaQqbQ6Vp0+fRm1trSprISIiIg0mCAJe6fUQVobOgL523fwrVbU1mHd0A9bEnJK4OlJWm2bUAYBZs2bB3Nwcw4cPx4gRIzBs2DAYGRndf0MiIiLq0iZ49IS9oSlmHlqHwqpyiBCx6OxOZJUX4a2+YyAIgtQlUhu0+Uqlj48PCgsLsXPnTrzxxhsYOHAgXnjhBWzatAk5OTmqrJGIiIg0TH97D2wfPw9OxuaKZcsiIzD/xG+orq2RsDJqqzaHyh07duDIkSN49913ERwcjNraWhw7dgzvv/8+hg8fjscffxyrVq1CfHy8KuslIiIiDeFtYYcd41+Cr6WDYtlvty5h1qF1KKuukrAyagtBVNHQ9kVFRYiIiMChQ4dw4sQJlJWVKS5fu7m5ISwsDCNGjEBQUFCXv6wdHh6O6OhoBAQEYOvWrVKXQ0REJKnCynLMOfITzmQmKJb1t3PHulGzYKZnIGFlmqM9sofKQmVDVVVVOHPmDA4dOoSjR48qbocLggBLS0uEhoZixIgRCAkJgb6+vqoP3+ExVBIRETVWIa/GP/7cjD0NeoL3tHbGL6Nnw8rAWMLKNEN7ZI82d9RpiZ6eHoYNG4Zhw4YBACIjI3Ho0CEcPnwY8fHx2LJlC7Zu3QoDAwMMHjwYYWFhCAsLg7m5+X32TERERJrIQEcXy4c/iXf0DLHhxjkAwLW8NEz94ztsGDMXDkZmEldI99Mu41T26tUL8+fPx549e7B//34sXLgQffr0QWVlJQ4fPox3330X69evb49SiIiIqIPS1tLC/w2egucDQhTLbhRk49G9K5FSnC9hZdQa7T74ubu7O2bPno0NGzbgxIkT+OijjxAaGgpDQ8P2LoWIiIg6GEEQsKj/eLwRGKZYllScjyl7VyK+kKPLdGRquf3dWlZWVpg6dSqmTp0qZRlERETUgQiCgAV9RsFYRx8fXdgLAMgsK8Kje7/DxrFzG/UWp46D0zQSERFRh/Riz2H4ZNBkCKgbNSa3ogRP7Pse1+9kSVwZNYWhkoiIiDqsp30H4quhj0Hr7nCEeRWleGLf97hRwGDZ0TBUEhERUYc2tXtffD30cUWwzK0oweN/MFh2NAyVRERE1OGFe/XBV38Llk/s+x43C7IlrozqMVQSERFRp/CoVx98GfKY4hnLnPISPL5vFW4X5kpcGQEMlURERNSJTO3eF18NndooWD65/weklxRIWxgxVBIREVHnMrV7EL4IeVTxOq20ANP2/4Dc8hIJqyKGSiIiIup0Hvfuhw8HTFS8vl2UixkHfkRhZbmEVXVtagmVWVlZyM3l8w1ERESkPrP8B2Nh39GK19H5GXj20FqUVVdJWFXXpbIZdURRxHfffYfvv/8eZWVlAABDQ0P4+PjA399f8c/b2xs6OpJO5ENEREQa4tVeoSisqsB3UccBABeyk/DckfVYM/JZ6Gkzb7QnlbX2hg0b8PXXXzdaVlZWhsuXL+PKlSt/HVBHB97e3oqQOX36dFWVQERERF2MIAh4r9/DKK6qwIYb5wAAx9Jv4s2TW/DN0Mch3B2CiNRPZaFy06ZNAIB+/frhrbfegoWFBZKSkhAbG4uYmBjExMQgOTkZ1dXViteCIDBUEhERkVIEQcAngyajpLoCOxMiAQBb4y/DydgcbweNlbi6rkNloTIlJQWCIOCLL76Avb09AMDV1RUhISGKdUpKShAXF4fo6GhER0cjNjZWVYcnIiKiLkxbSwtfDX0cueUlOJV5GwCwLDICTsYWeMZ3oMTVdQ0qC5UmJibQ09NTBMrm1unXrx/69eunqsMSERERAQD0tXXwQ9gzCN+7EnF3MgEA753ZAXtDU4xxD5C4Os2nst7fvXv3RmlpKaqq2OOKiIiIpGGmZ4CfRs2Cg5EZAKBWFPHysY24mJ0scWWaT2Whctq0aZDL5Th06JCqdklERET0wJyMzbF+1GyY6uoDACpqqjHr0DokFedJXJlma1OonDNnDr788kvs27cPKSkpAICQkBBMmDABS5YsQVZWlkqLJCIiInoQflYO+GHE09DV0gYA5FeWYvahn1BSXSlxZZqrTc9Unjx5EqdOnVK8NjU1hZ+fH7y9vVFVVYXJkyfjo48+QlhYmMoKJSIiInoQQ5y647Mhj+L1PzcDAK4XZOG1YxvxQ9jT0BI4qaCqtSlUvvDCC4iJiUFsbCxyc3NRVFSEs2fP4ty5c4p1XnnlFdja2iI0NBQ9e/ZEQEAABz4nIiKidjW1e19cv5OFFVHHAAAHUmLx6aUDHGpIDdqU8N544w3Fz1lZWYiJiUF0dLRi/MnMzLoeV9nZ2di8eTM2b677C0FXVxfdu3dHQEAA/P39ERAQgF69eqngYxARERE17e2gMbhZmIVDKXEA6oYa8rFwwBSvQGkL0zBKXza0t7eHvb09QkNDFcvy8/MRGxvbKGimpKSgqqpKcYUTqBusNCYmRtkSiIiIiJqlraWFpcOmYfKeFbheUNfv482Tv6ObmTUCbV0lrk5zqOVetJWVFYYMGYIhQ4YolpWUlCgCZv3g54mJieo4PBEREVEjpnoGWD3yGYzf9T8UVJahskaOOUfW449HXoWdkanU5WmEVj2lOm/ePCQnKze+k4mJCYKDgzFz5kx89tln2Lt3Ly5duqTUPomIiIhay93UGt+FzoDO3U46WWVFePnYr5DX1khcmWZoVaiMiIjA+PHjsWTJEhQVFans4AYGBirbFxEREdH9DHH0wn+CJyhen868jc8uHZSwIs3RqlC5fft2BAcHY+3atRg9ejR++eUX1NbWqrs2IiIiIpWb6TcIkzx7K17/71oEDiSzj4eyWhUqfXx88OOPP+KHH36AnZ0dPvzwQ0ycOBHHjx9Xd31EREREKiUIAj4dHA5vczvFstf/3MwZd5T0QCN/hoSEYMeOHVi8eDGKi4vxwgsvYO7cuYiPj1dXfUREREQqZ6yrj1UjnoKRjh4AoKiqAs8f+RmVNXKJK+u8Hng4eUEQ8Oijj2L//v145ZVXcOnSJUyaNAkffPAB7ty5o44aiYiIiFTO28IOnw95VPE6Oj8DSy7uk7Cizq3NcxQZGBjg5ZdfxsGDBxEeHo5NmzZh9OjRWLNmDaqrq1VZIxEREZFaTPTsjWd9Bylefx99AkdTr0tYUeel9MSX1tbW+OCDD7Bz504EBQXh008/xYQJE+6/IREREVEH8F7/cfCxsFe8fuPP35BTXixhRZ2TUqEyJSUFR48exapVq7Bq1SpkZ2dDS0tL6TEtiYiIiNqLoY4u/vfQk9DXrpsTJreiBPP//B2iKEpcWefSqhl1kpOTcevWrUb/EhISUFFRAQAQRRG6urro1q0bxo4dC5lMptaiiYiIiFTJ19IBi/qPx3tndgAAjqZdx+rYU5jjP+Q+W1K9VoXK0aNHQxAEiKIILS0tuLq6IiQkBN7e3vD29oZMJoOHhwe0tbXVXS8RERGRWjzrOxARaddxKCUOAPDxhT8wzMkb3hZ299mSgFaGytmzZ0Mmk0Emk6F79+7Q09NTd13taunSpVi2bFmL67z//vt48skn26kiIiIiam+CIOCLkKkYtf0bZJcXo7JGjvknfsP2cfOgraV0NxSN16pQuXDhQnXX0SFYW1vD3d29yfdsbW3buRoiIiJqb9YGJvi/wVMw6/BPAIDLOSlYFf0n5vUcLnFlHV+rQmVXMWzYMCxZskTqMoiIiEhCo9z8Ee7VB1vjLwMAPr98EKNc/dCdt8FbpPJruUeOHME333yj6t0SERERtZv/DngEdoamAHD3NvjvqKmtlbiqjk3lofLWrVtYuXKlqndLRERE1G4s9Y2wZPAUxetLOcn4IeaEhBV1fLz93UBcXBwWLFiAnJwcGBsbw8fHB+PHj4e3t7fUpREREVE7G+3mjymegdh2+woA4NNLBzDWPQDuptbSFtZBMVQ2EBsbi9jYWMXrI0eOYOXKlXjmmWfw1ltvtThk0saNG7F58+ZWHSc+Pl7pWomIiEj9Phg4EScybiGnvASVNXK8d3onfho1E4IgSF1ah9OqUPnRRx/B398ffn5+8Pb2ho6OZmVROzs7vPbaaxg6dChcXFxgYmKChIQEbNiwARs3bsS6deugo6PTYi/4nJwcREdHt2PVREREpG6W+kb4T/AEvHJsI4C6QdH3JkVhvEdPiSvreFqVDn/++WdFItfR0YGXlxf8/Pzg5+cHf39/+Pr6wsTERK2FqtMTTzxxzzIfHx/897//hYuLCz7//HOsW7cO06dPh4uLS5P7sLW1RUBAQKuOFx8fr5iNiIiIiDq2Sd16Y9PNC/gz/RYA4D9nd2G4swwmuvoSV9axCGIrJra8ffs2YmNjERMTg7i4OMTExODOnTt1OxAECIIAZ2dn+Pn5oaCgAOfPn290G7kzq6mpwUMPPYTs7Gy8++67eOaZZ5TeZ3h4OKKjoxEQEICtW7eqoEoiIiJSp9uFORi5/WtU1dYAAJ4LCMF/gidIXFXrtUf2aNWVSk9PT3h6emL8+PGKZVlZWfcEzQMHDgCARj1noK2tjd69e+PgwYNISkqSuhwiIiKSgKe5LV7u9RC+unIYALA65hSmy4I5hWMDbX440t7eHvb29njooYcUy0pKShRBU5Po6uoCAORyucSVEBERkVRe7vkQtsZfRlJxPmrEWnxwbg/Wj54ldVkdhkrHqTQxMUH//v3x7LPPtrhebW0tjhw5gpdeekmVh1ebmzdvAgAcHBwkroSIiIikYqCji/f6j1O8Ppp2HUdSr0tYUcfSrt24ExMT8fvvv2P79u3Iy8trz0O3WUREhCJUDhkyROJqiIiISEpj3QIwyMETpzNvAwA+PLcHQ526Q1er+WEHuwqVz6jzd+Xl5di6dSumT5+Ohx9+GD/++CNyc3PRiv5B7eLmzZv497//jbi4uEbLa2trsXv3bixYsAAAEBoail69eklRIhEREXUQgiDg/eAJEFDXf+RmYTZ+jjsrcVUdg9quVF65cgW///47/vjjD5SVlSlCpKmpKcLCwrB9+3Z1HfqByOVybNq0CZs2bYKFhQWcnJygra2N5ORkFBYWAgD69euHTz/9VOJKiYiIqCMIsHbCNFk//HrjPADgyyuHMLV7X5jqGUhcmbRUGirz8/Oxfft2bNmyBbdv110WFkUROjo6CAkJweTJkzFixAjI5fIOEyqdnZ3x+uuv48qVK4iPj0dSUhKqqqpgbm6OYcOGYcKECZgwYUKLs+kQERFR17Kw72jsvH0VpfIq3Kksw/fRJzC/z0ipy5KU0qFSFEUcO3YMW7ZswdGjR1FTU6O4KtmjRw9MnjwZ48aNg5WVlWKbjtSL2szMDPPmzZO6DCIiIupEbA1N8VyPofj67hBDq6L/xEy/QbAyMJa4Mum0OVQmJydjy5Yt2LZtG3JychRB0snJCY888ggmTZoET09PlRVKRERE1JE8HzAUa2JOobCqHCXVlfjftWNY1KB3eFfT5lA5evRoCIIAURRhbGyMMWPGYPLkyQgODlZlfUREREQdkpmeAV7u9RA+vvAHAGBt7Ck8FxACByMziSuThtK9v4OCgrBr1y58/PHHDJRERETUpczyGwQ7Q1MAQGWNHN/cvR3eFbU5VOrp6UEURVy6dAmjR4/Giy++iD/++ANVVVWqrI+IiIiowzLU0cNrvUcoXm+8eQEZpYUSViSdNofKEydO4L333oOvry/kcjkiIiIwf/58DB48GO+99x7OnTunyjqJiIiIOqTpsv5wNDIHAFTX1uCH6BMSVySNNodKMzMzPPXUU9i2bRu2bt2KJ598EqampigpKcGWLVvw7LPPYsSIEfjqq68QHx+vypqJiIiIOgw9bR083yNE8frn62dxp7JMwoqkoZIZdfz9/fGf//wHJ06cwKeffor+/fsDANLT07Fq1SpMmDABjz76KH7++Wfk5+er4pBEREREHcZ0WTAs9I0AAKXyKvwUe1riitqfSqdp1NPTw8SJE/HTTz/hwIEDePHFF2Fvbw9RFBEdHY3Fixdj2LBhePXVV1V5WCIiIiJJGevqY5bfIMXrH2NOoVzetfqZqG3ub1dXV7z++us4evQoVq1ahdGjR0NbWxtyuRynTp1S12GJiIiIJDHbbzAMdXQBAPmVpYppHLsKtYXKeoIgYNiwYfj2229x/PhxvPXWW/Dy8lL3YYmIiIjalaWBMWbI/hpecVX0n6iprZWwoval9lDZkJWVFWbNmoXdu3dj48aNmDp1ansenoiIiEitnu8xDNpCXbxKLSnA4dQ4iStqP+0aKhsKDAzEhx9+KNXhiYiIiFTOydgcD7sHKF6vie06j/xJFiqJiIiINNEsv8GKn/9Mv4WbBdkSVtN+GCqJiIiIVCjY3gN+lg6K12u7yPBCDJVEREREKiQIAmY2uFq5Nf5SlxheiKGSiIiISMWmeAbCRFcfAFBcXYm9SdESV6R+DJVEREREKmakq4dHuvVSvN5884KE1bQPhkoiIiIiNXiiez/Fzycz4pFcrNlTVTNUEhEREalBkJ0bvMxtFa8337ooYTXqx1BJREREpAaCIODx7kGK11tvXYYoihJWpF4MlURERERqEu7VBwIEAEByST4i89Ikrkh9GCqJiIiI1MTR2BzB9u6K17sSIiWsRr0YKomIiIjUaILHX73AdydGauwtcIZKIiIiIjUa59FDcQs8taQAV3NTJa5IPRgqiYiIiNTI3sgMwfYeite7Eq9JV4waMVQSERERqVnDgdD3aejsOgyVRERERGo2xs1f8XNScR4SinIlrEY9GCqJiIiI1MzR2By+lg6K1xGpNySsRj0YKomIiIjawUPOMsXPEWkMlURERETUBqENQuWpzHhUyKslrEb1GCqJiIiI2kE/ew8Y6egBAMrl1TifnShtQSrGUElERETUDvS1dTDE0UvxWtOeq2SoJCIiImonDZ+rPJZ+U8JKVI+hkoiIiKidNLxSef1OFgoqyySsRrUYKomIiIjaiZe5Laz0jQEAIkRczE6WuCLVYagkIiIiaieCIKC/vbvi9bmsROmKUTGGSiIiIqJ21HAe8Asa1AOcoZKIiIioHQXZ/nWl8lpeOmpqayWsRnUYKomIiIjakb+VI7QEAQBQJq/CbQ2ZB5yhkoiIiKgdGenqobu5reJ1ZF6ahNWoDkMlERERUTvrae2s+DmKoZKIiIiI2qJhqLzGUElEREREbeFn5aj4+VZBjoSVqA5DJREREVE782rwTGVuRQkKK8slrEY1GCqJiIiI2pm9oSmMdfQUr+OLOv/VSoZKIiIionYmCEKjq5W3CxkqiYiIiKgNPBuFys4/ViVDJREREZEEvMxtFD/Ha8AA6AyVRERERBLwMP0rVKaW3JGwEtVgqCQiIiKSgKWBkeJn9v4mIiIiojYx0zNQ/FxUVSFhJarBUElEREQkAXM9Q8XPRVXlEEVRwmqUx1BJREREJIGGoVIu1qJcXi1hNcpjqCQiIiKSgGmD298AUFjVuZ+rZKgkIiIikoC+tg4MtHUVrzv7c5UMlUREREQSMW/UWYdXKomIiIioDcwaddbp3FcqdaQuoCM5c+YM1qxZg6tXr6KsrAxOTk4YO3Ysnn/+eRgZGd1/B0REREQPoOGwQnymUkOsX78eM2fOREREBPT19eHl5YW0tDSsWLECU6dORUFBgdQlEhERkYbRpCuVDJUAoqKi8PHHHwMAPvjgA0RERGDbtm04dOgQAgICEB8fj0WLFklcJREREWkaM30+U6lRli9fjtraWkyaNAlPPPEEBEEAANjb2+PLL7+ElpYWDhw4gLi4OIkrJSIiIk3ScKzKzj5VY5cPlaWlpfjzzz8BAI8//vg973t4eGDgwIEAgH379rVrbURERKTZ+EylBomNjUVVVRX09PTQq1evJtcJCgoCAFy9erU9SyMiIiINp0nPVHb53t8JCQkAACcnJ+jq6ja5jpubW6N1m7Jx40Zs3ry5VceMj49/wCqJiIhIEzW6/d3Jr1R2+VBZWFgIADA3N292nfr36tdtSk5ODqKjo1VbHBEREWk0X0t7xc8ORmYSVqK8Lh8qKysrAaDZq5QAoKen12jdptja2iIgIKBVx4yPj0dFRee+xE1ERETK62vrhs+HPIr4wlzM6zlM6nKU0uVDpb6+PgCgurq62XWqqqoarduUadOmYdq0aa06Znh4OK9qEhEREQRBwDRZf6nLUIku31GnNbe2W3OLnIiIiKgr6/Kh0sPDAwCQnp7e7NXK5OTkRusSERERUWNdPlT6+flBV1cXVVVViIyMbHKdixcvAgACAwPbsTIiIiKizqPLh0oTExOEhIQAQJNDAiUmJuLMmTMAgLFjx7ZrbURERESdRZcPlQDw0ksvQRAE7NixA5s2bYIoigCA7OxszJ8/H7W1tRg5ciR8fX0lrpSIiIioY2KoBNCrVy+8/fbbAIB///vfCA0NxZQpUxAWFobo6Gh069YNH374ocRVEhEREXVcXX5IoXozZ86Ej48PVq9ejcjISOTl5cHJyQljx47F888/D2NjY6lLJCIiIuqwGCobGDRoEAYNGiR1GURERESdDm9/ExEREZHSGCqJiIiISGm8/S2B1NRUAHVzgIeHh0tcDREREWm6+Ph4AH9lEHVgqJRAZWUlAKCiooJzgBMREVG7qc8g6sBQKQErKyvk5+dDX18fLi4ujd6Lj49HRUUFDAwM4OXlJVGFmo1trF5sX/Vi+6oX21e92L7q1VL7pqamorKyElZWVmo7viDWj/RNHUJ4eDiio6MREBCArVu3Sl2ORmIbqxfbV73YvurF9lUvtq96Sd2+7KhDREREREpjqCQiIiIipTFUEhEREZHSGCqJiIiISGkMlURERESkNIZKIiIiIlIaQyURERERKY2hkoiIiIiUxlBJREREREpjqCQiIiIipXHu7w7m8ccfR05ODmxtbaUuRWOxjdWL7atebF/1YvuqF9tXvaRuX879TURERERK4+1vIiIiIlIaQyURERERKY2hkoiIiIiUxlBJREREREpj728VEEURly9fxpEjR3Dx4kXcvn0bJSUlMDU1hb+/PyZPnoxHHnkEgiA02u7OnTs4dOgQTp06hejoaGRkZEBLSwuOjo4ICQnBzJkz4eLi0uQxly5dimXLlrVY1/vvv48nn3xSZZ9TKm1tXwDw8fFpcd82NjY4efJks+/HxMRg1apVOH/+PIqKimBnZ4fQ0FC89NJLsLKyUvqzdQRtbd+3334b27Zta9Uxjhw5AmdnZ8XrrVu34p133mlxm+eeew5vvvnmg3+gDuqPP/5Q/H89OzsbBQUF0NXVhYeHB4YPH45nn30WlpaWTW5bWlqKVatWYf/+/UhPT4eRkRF69+6N2bNnY8CAAS0e98yZM1izZg2uXr2KsrIyODk5YezYsXj++edhZGSkjo8qiba0L8/BrdfW7y/Pwa3TlvbtiOdg9v5WgdOnT2PmzJmK166urjAzM0NaWhoKCgoAAA899BCWLl0KPT09xXrTp0/HxYsXAQDGxsZwc3NDRUUFUlJSIJfLYWRkhG+++QbDhg2755j1JzRra2u4u7s3WdecOXMwcuRI1X1QibS1fYG/Tmg9evS45z0AsLCwwIoVK5o87oEDBzB//nxUV1fD2toaDg4OSEhIQFlZGWxtbfHrr7/C1dVVNR9SQm1t35UrV+LYsWPN7jcxMRH5+flwdHTEkSNHoKX1142R+hOaiYkJZDJZk9tPnDhRY34hA8CkSZMQFxcHPT092NrawtLSEvn5+UhPTwcAWFtbY/Xq1fD19W20XX5+PqZPn46EhATo6emhe/fuyM/PR2ZmJgRBwKJFizBjxowmj7l+/XosXrwYoijCwcEBVlZWuHXrFqqqquDl5YUNGzbAwsJC3R+9XbSlfXkObr22fn95Dm6dtrRvhzwHi6S0kydPiiNGjBDXrVsn5ubmNnpv27ZtYo8ePUSZTCZ++umnjd576qmnxPnz54vnzp0T5XK5YnlGRoY4a9YsUSaTiX379hVzcnLuOea3334rymQy8a233lLPh+pA2tq+oiiKMplMlMlkYkpKygMdMzMzU+zdu7cok8nEr7/+WqyurhZFURSLiorEOXPmiDKZTAwPDxdra2vb/sE6CGXatzm1tbViWFiYKJPJxC+//PKe97ds2SLKZDLxqaeeUrr+zmLTpk3iuXPnxKqqqkbL4+LixAkTJogymUwcN27cPdu9+OKLokwmE6dMmSJmZmaKoljXvhs3bhRlMpno5+cnxsTE3LPdtWvXRF9fX9HHx0fcuHGj4ruamZkpTpkyRZTJZOIrr7yihk8qjba0L8/BrdfW7y/Pwa3T1vZtjlTnYIZKFSguLr7ni9DQihUrRJlMJgYHB4s1NTWK5fn5+c1uU1RUJA4cOFCUyWTimjVr7nm/K53Q2tq+otj2E9pHH30kymQyccaMGfe8V1BQIAYFBYkymUw8fPjwA+23I1KmfZtz9uxZRdsnJibe835XDJUtuXr1qqK9bt26pVgeHR0tymQy0dfXt8l2/Oc//9lsOJw3b54ok8nEhQsX3vNeQkKC6OvrK8pkMjE2Nla1H6YDaq59eQ5WjebaVxR5DlaFltq3OVKdg9lRRwVMTEygq6vb7Pv1t04KCgqQn5+vWN7c81MAYGpqisDAQABAQkKCagrtpNravsrYv38/gLrZCf7O3NwcY8eOBVD3HExnp472rX/Op2/fvs3eGqS/eHp6Kn4uLy9X/Fz/PRw4cGCT7fjEE08AAI4dO4aysjLF8tLSUvz5558Amv4Oe3h4YODAgQCAffv2qeATdGzNtS/PwarRXPsqoyudg++nLe0r1TmYHXXaQUVFheJnAwODVm9XWVkJADA0NGx2nbi4OCxYsAA5OTkwNjaGj48Pxo8fD29v77YX3Mm0pn2XL1+O7Oxs1NTUwN7eHgMHDsS4ceOafMYnIyMDWVlZAID+/fs3ub9+/frht99+w9WrV1XwCTq2B/3+lpWVKYJKeHh4i+ump6fj7bffRkZGBgwMDODp6YkxY8Yofpl3FfXP9RkZGaFbt26K5VeuXAFQ931rSq9evaCnp4fKykrExsYiKCgIABAbG4uqqiro6emhV69eTW4bFBSEU6dOdYnvcHPtez88B7dOa9qX5+C2e9Dvr5TnYIbKdrBnzx4AgK+vL0xMTFq1TVZWFs6dOweg+V8oQN0vj9jYWMXrI0eOYOXKlXjmmWfw1ltvQVtbW4nKO4fWtO+WLVsavd62bRu+/fZbLF26FAEBAY3eS0xMBADo6urCwcGhyf3VPxyekpKC6urqFq/0dXYP+v3dv38/ysrKYGhoiIcffrjFdVNTU5Gamqp4HRERgdWrV2P8+PFYvHhxi7/MO7va2lrk5OTg5MmT+PzzzwEAb775JoyNjRXr1H8X3dzcmtyHrq4uHB0dkZSUhISEBEWorL+y5uTk1Ox3s36fmnoVrjXt2xKeg1v2oO3Lc/CDUeb7K+U5mKFSzaKiorBx40YAwPPPP9/q7T788ENUV1eje/fuCA0Nved9Ozs7vPbaaxg6dChcXFxgYmKChIQEbNiwARs3bsS6deugo6ODhQsXquyzdET3a9+wsDBMmjQJvr6+cHBwQGlpKU6fPo2vvvoKKSkpmD17NrZv3w5HR0fFNvU9ns3NzZscpgiAosdsbW0tSkpKWryN1pm15fu7detWAMCoUaOaDaFmZmaYO3cuQkND4e7uDnNzc6SlpWH79u344YcfsGfPHtTU1OCbb75RzQfpQNauXYtPPvmk0bJevXphyZIl9/QyLiwsBFD3XWxO/XtFRUVt2q5+XU3xIO3bEp6Dm/ag7ctz8INRxfdXynMwQ6Ua5ebm4tVXX4VcLseoUaMwfvz4Vm23atUqHDx4ELq6uliyZEmTf+nWP0vVkI+PD/773//CxcUFn3/+OdatW4fp06c3O85aZ9ea9l2+fHmj1/r6+hg/fjwGDRqERx99FOnp6Vi2bBkWL16sWKf+lldLf/k2vGVTv76macv3NyUlBefPnwfQ8m2XkSNH3jPUSrdu3fDGG2/Ax8cHb7zxBvbt24cLFy60eJWoM7K3t0ffvn1RU1OD9PR05ObmIjY2Fjt27EBgYCDMzMwU6z7Id7HhYwoPsp2mfX8fpH2bw3Nw8x60fXkOfjDKfn+lPgezo46aFBcX47nnnkN6ejoCAgKwZMmSVm23bds2fPnllxAEAYsXL0bPnj0f+NizZ8+GnZ0d5HI5jhw58sDbdwZtbd96VlZWiitvhw4dgthguFZ9fX0AQHV1dbPbV1VV3bO+Jmlr+27fvh2iKMLZ2VnREeRBjRs3Dr179wYAHDx4sE376Mgefvhh/Prrr9i8eTNOnDiB7du3o3fv3ti9ezeeeeYZ1NTUKNZ9kO9iw+ddH2Q7Tfv+Pkj7NoXn4JYp2771eA5umrLtK/U5mKFSDUpLSzF37lzExMTA29sbP/74Y6ueRdu7dy/effddiKKI//znP5g0aVKbjq+tra34QiQlJbVpHx1ZW9v37/r06QOg7lZL/e0WoPFtQbGZuQHq19fS0mrTsTuytravKIrYvn07gLqBfJu7bdUa9f/baOL39+98fX3x3XffwdLSErGxsYpnWAEorkq0dIu6/r2GVzBac2u7NbfINUFL7ft3PAc/uAdp37/jOfj+HqR9O8I5mKFSxcrLy/HCCy/gypUr8PDwwJo1a1r1rMfBgwfxz3/+EzU1NXjrrbeUnkmk/raBXC5Xaj8dTVvbtykNb600/OvPw8MDQN1fyRkZGU1um5KSAgBwcXHRqAfElWnfc+fOITU1FYIg3LfH4f1o6ve3OSYmJggODgYAREdHK5bXfxebO7FXV1crZtyoX7fhz+np6c1e7UlOTr5nO03VXPs2xHNw27WmfZvCc3DrtLZ9O8I5mKFShSorKzFv3jycP38ezs7OWLt2LWxtbe+73bFjx/DGG29ALpfjtddew+zZs5Wu5ebNmwDQbM+5zqit7duc+jbS19dvNFWdk5MT7OzsAAAXLlxoctv65Zo09I2y7Vs/Llq/fv2UnjpNE7+/91N/8m74y7X++1U/pMjfRUZGorq6Gvr6+vDz81Ms9/Pzg66uLqqqqhAZGdnktvX71KTvcEuaat96PAcrr6X2bQ7Pwa3XmvbtCOdghkoVqa6uxquvvorTp0/D3t4e69ata9SbrTmnT5/Gq6++iurqarzwwgt4+eWXla4lIiJC8YUYMmSI0vvrCNravs2Ry+VYs2YNgLqBpXV0GvdZGzNmDABg8+bN92xbWFioGAOsfgDezk7Z9i0tLVUMVjxlyhSlaomLi1MM3K0p39/7KSgoUAxf0zAc1n8Pz5492+TVyk2bNgGoG6C+4VAjJiYmCAkJAdD0dzgxMRFnzpwBoDnf4ZY0174Az8Gq0FL7Nofn4NZrTft2mHOwSufn6aLkcrn46quvijKZTBwyZIh4+/btVm136dIlMTAwUJTJZOLHH3/c6uPduHFDXLRo0T3Tq9XU1Ii7du0S+/btK8pkMvGFF154oM/RUbW1fT/77DNx69atYnFxcaPl6enpiins/P39xStXrtyzbXp6utirVy/FvLP18wI3nHd28uTJGjHvbFvbt6H6Kb8CAwPFkpKSFtctLi4W//GPf4gXL168p/2OHz8uhoSEiDKZTJw4cWKj+Zg7s7Nnz4r/+9//mpyqLioqSgwPDxdlMpk4dOjQe9rvhRdeUMz9nZWVJYpi47m/fX19xaioqHv2e/XqVdHHx+eeub+zsrIUc3+/9NJLavi07a+t7ctzcOu0tX15Dm4dZc4P9TrKOVgQxWaegqVW2717NxYsWAAAcHZ2hr29fbPrLlq0CP7+/gDq/hJLTExs9FB3U/z9/bFo0SLF69jYWEyePBlA3VhdTk5O0NbWRnJysuLh+379+mHFihWtGj6jo2tr+7700ks4fPgwtLW14erqCnNzcxQXFyMhIQGiKEJfXx8fffQRJk6c2OS+9u3bhwULFkAul8Pa2hoODg5ISEhAWVkZbGxssGHDBo2YgrCt7dvQ008/jXPnzmHKlCn37SleVFSkmCXD2NgYrq6u0NPTUwyfAQDe3t5YtWoVnJyc2vqxOpRDhw4proDZ2trCzs4O2trayMjIQE5ODoC6oUS+++67e65E5Ofn48knn0RiYiL09PTQvXt33LlzBxkZGRAEAe+++y6efvrpJo+7du1aLFmyBKIowtHREZaWlrh16xaqqqrQrVs3bNiwAVZWVur98O2gre3Lc3DrtLV9eQ5uHWXOD/U6yjmY41SqQMOhDdLS0pCWltbsusXFxYqf6x+gr6mpwaVLl5rd5u+3BZydnfH666/jypUriI+PR1JSEqqqqmBubo5hw4ZhwoQJmDBhgsbM5NDW9n3yySdhY2ODqKgoZGdnIy0tDbq6uvD29sagQYPw1FNPNTtTCVB3W8XV1RXfffcdLly4gBs3bsDOzg7h4eF46aWXYG1trZoPKLG2tm+9huOitea2i6GhIRYuXIgrV67gxo0bSE9PR1lZGUxMTDBgwACMGTMGU6dO1ahhQvr06YN33nkHZ8+exa1bt5CYmIiqqiqYmZlhwIABGDFiBKZOndpkL1YrKyts2bIF33//Pfbt24dbt27ByMgIw4YNw5w5c1ocNmTmzJnw8fHB6tWrERkZiby8PDg5OWHs2LF4/vnnWz27TEfX1vblObh12tq+PAe3jjLnB6BjnYN5pZKIiIiIlMaOOkRERESkNIZKIiIiIlIaQyURERERKY2hkoiIiIiUxlBJREREREpjqCQiIiIipTFUEhEREZHSGCqJiIiISGkMlURERESkNIZKIiIiIlIaQyURERERKU1H6gKIiDq7d955B1u3bm3Ttr169cJvv/32QNtMmjQJcXFxWLNmDQYPHnzf9ZcuXYply5YBAK5fv97selu2bMGiRYtQU1ODfv36YeXKlTA1NX2g2oio62KoJCJSUmpqKmxsbO5ZXlZWhrKyMgBo8n0ACA4OfqBjpaWlIS4uDmZmZg+8bUvWrl2LJUuWQBRFhIaG4uuvv4aBgYHK9k9Emo+hkohISevXr29y+aJFi7B582Y4OjoiIiJCJcc6fPgwAGDYsGHQ0VHNKfzrr7/GihUrAACPPPIIlixZorJ9E1HXwbMGEZGaREdHAwD8/f1Vts/6UBkWFqb0vkRRxIcffohffvkFAPDUU0/hvffegyAISu+biLoehkoiIjWQy+W4efMmANWFysLCQly4cAG6uroYNmyYUvuSy+V4++23sWvXLgDAyy+/jNdee00VZRJRF8VQSUSkBrdu3UJVVRUA1YXKiIgIyOVyDB06FCYmJm3eT2VlJf7xj3/g6NGjEAQB//rXv/DMM8+opEYi6roYKomI1KD+1jcABAQEqGSfqrj1XVJSgnnz5uHcuXPQ0dHB4sWLMXnyZJXUR0RdG0MlEZEaxMbGAgCsra1hb2+v9P6qqqrw559/QhAEjBgxos37eeaZZxAdHQ19fX18/fXXSu2LiKghDn5ORKQG9Vcq/fz8VLK/06dPo6ysDD169FAqpNbXNXXqVAZKIlIphkoiIhWrra1FXFwcgI516xsA+vTpAwD45Zdf8NNPPyldFxFRPYZKIiIVS0hIUAx6ropOOqIo4siRIwCAkSNHKrWvH374AX379gUALF68GGvXrlW2PCIiAAyVREQqV/88JaCaK5WRkZHIycmBm5sbvL29ldqXiYkJfvjhBwQFBQEAPvnkE6xZs0bpGomIGCqJiFSs/rlFU1NTuLi4KL0/VQ54DgDGxsb44Ycf0L9/fwDAkiVL8OOPP6pk30TUdTFUEhGpWExMDIC6TjqqmJ3m0KFDAFQXKgHAyMgIq1atUswf/umnn+L7779X2f6JqOthqCQiUjFVdtJJTExEfHw8LC0tFc9Cqkp9sBwwYAAA4PPPP8eqVatUegwi6joYKomIVCg1NRUFBQUAVDOcUP2t79DQUGhrayu9v78zNDTEqlWrMGjQIADAF198gZUrV6r8OESk+RgqiYhUqP7WN6CaK5Wqfp6yKQYGBli5ciWGDBkCAPjqq6+wfPlytR2PiDQTQyURkQrVh0pDQ0N4enoqta/8/HxcvnwZBgYGisCnLgYGBlixYgVCQkIAAN988w2WLVum1mMSkWYRRFEUpS6CiIju9fvvv+Pdd99FaGgob0kTUYfHK5VERB1U/a1vZQc8JyJqDzpSF0BERE3r27cv/P391fo8JRGRqvD2NxEREREpjbe/iYiIiEhpDJVEREREpDSGSiIiIiJSGkMlERERESmNoZKIiIiIlMZQSURERERKY6gkIiIiIqUxVBIRERGR0hgqiYiIiEhpDJVEREREpLT/B7/5ZfXRSDjhAAAAAElFTkSuQmCC", + "image/png": "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", "text/plain": [ "

" ] @@ -889,7 +873,7 @@ "outputs": [ { "data": { - "image/png": "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hH1F+uZGbyQ3eiNJv76IOfzqUyMT7sQtpLGT0ouIiEhpKswiin369GH37t24ubkRGBiIv78/J0+eJC4uDoCAgAC+/vprGjc+94eODRs2MGLECJKTkzGbzURERGA0Gjl27BjJyck89NBDPP/88+dct3DhQp555hny8vIICAggODiYQ4cOkZWVRWBgID/++CPh4eFl+j61iKJUZdn5Fr7etZpJ25eTaskucs7XzZ2Hml7H0CbX4u/u5aSEInKpCnd12EH6hplkrP8VS1zxO6dA4faQPh3uwaf9AFwDyvbfWREREbl0FaaAMGPGDCIjI2nZsiWurq729j179jBmzBj27t1L/fr1mTdvXpHrDh48yJ133onFYuHpp5/m/vvvx939zOJqBw4cIDMzkxYtWhS5LiEhge7du5Odnc0TTzzB8OHDcXFxIT09nVGjRrFixQqaN2/Or7/+WqZzrlVAkKrIUpDPD3vX88nWpRzPTi9yzt/syaPNO/FA445aGFGkEsmN3UXGhlmkb5iJJWZbif08Gl6PT4eBeF9zFy6+gVcwoYiIiPyvClNAKM22bdu4++67AZg/fz716tWzn7vvvvtYv34948aN47777rvge7799ttMmzaNtm3b8t133xU5l5qayk033UR6ejqTJ0+ma9euZfNGUAFBqharzcrvB7fy300LiclILnLOx9XMo80783Cz6/HWfvIilZrl2N7CYsK6X8g9sqX4TkYTns264dvhHrxb34HRw+eKZhQREZEKtAZCaerWrWt/nZ19Ztjz9u3bWb9+PTVq1GDgwIEXdc+//voLgP79+59zzs/Pjx49evDLL7/w559/lmkBQaSq2Hj8CK+tm8PmxJgi7WaTC0OaXMvwq7poqoJIFeEW3JDqt75A9VtfwBK/h7Q1P5G+5seiayZYC8ja/hdZ2//C4OqOV6vb8L3uAbya34LBVCl+nBERESn3KsW/uBs3bgTA09OTyMgzW0ItWbIEgHbt2gHwyy+/sGLFCtLS0ggKCuKGG26ge/fuGI3GIveLj48nISEBoMTFFa+55hp++eUXtm7dWubvR6Qyi8tI4Z2NC/j94JYi7S4GI/c0bMtTLW8i2NPXOeFExOncajWiRt9XCbjjFXIPbyL93x9JW/szBSlx9j62vBwy1v1CxrpfMPkF49vxXnyvux9zeItS7iwiIiKOqrAFBKvV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4ONqplVguJMTiYiIiEh54V6nJeHPL8bkU6OwwWbl2P8NIW31d84NJlIJOVRAcHd3x9PTkzp16pRVHpFznD194bpa9XExaitQERERETnDHN6CsOeXYPKtWdhgs3HsyyGkrfnRucFEKhmHpjAEBQWRkJBQVllK9eeff7J69Wp27tzJ8ePHSUlJwdXVlYiICLp06cKDDz6Iv79/sddmZmbyxRdf8NdffxEXF4enpydXX301Dz30EO3bty/1uWvWrOGbb75h69atZGVlERISQo8ePRg2bBienlrI70r456wFFLto/QMRERERKYY5rDlhLyzl6ISuFKQdLxyJMOUBDEYTPu36OzueSKXg0AiEG264gdzcXNatW1dWeUr0+eefM2PGDPbt24ebmxuNGjWiWrVq7Nq1i8mTJ9O7d2927959znUnT57kzjvv5PPPPyc2NpZ69ephNptZtmwZDz74IN9//32Jz5w+fTqDBw9m2bJlmM1m6tWrR2xsLJMnT+auu+4iJSXlMr5jAYjLSGFf6nH7sRZQFBEREZGSmEOaEPbcoiLTGeI/v4/09TOdG0ykknCogPDoo49SvXp1XnvtNY4fP37+CxwwaNAgvvvuOzZt2sTSpUuZOXMmf//9N3/88QcNGzbkxIkTjB49+pzrXnrpJQ4dOkSzZs1YvHgxv/32G8uWLeONN97AZrPx9ttvExUVdc51O3bs4J133gHgjTfeYNmyZfz2228sXryYZs2aceDAAcaNG3dZ37PA8rOmL9TxCaCOT4AT04iIiIhIeWcOa07Yc4swelUvbLAWEP/5vWRsmu3cYCKVgEMFhAMHDvD0009z7NgxevfuzVtvvcX8+fNZs2YN69evL/HXpejfvz9t27bF1dW1SHujRo14++23Adi/fz8HDhywn9u1axdLly7FaDTy4YcfEhQUBIDBYGDAgAH06dOHgoICJk2adM7zJk2ahNVqpU+fPgwYMACDwQAUTtv44IMPMBqNLFy4sNhRD1J2Vsbvt7/W9AURERERuRDm8BaEPbcQo9epKc4F+cR9NoCMLXOdG0ykgnNoDYT777/f/sEa4Pvvvy91SgAUfnjftWuXI489R926de2vs7Oz7a//+usvADp06FDsQo8DBgxg9uzZLF++nKysLPuaBpmZmaxYsQIoLFz8r4iICDp06MDq1atZsGABjRs3LtP3I4Vs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uuaaazy227t3r9577z1t2bJFaWlpatCggW666SaNHj1a4eHhF+pH9lpsoggAAABAkmx+/mo08kP9/uyfZeRkyJmVolPvPaCmf/9aNh9fq8NDOXjFDIRNmzZpwIABmjlzprZt26bQ0FC1bt1ahmFow4YNevzxxzVy5EjZ7XaP7X/66Sfddtttmj59uo4cOaKmTZuqefPmSktL0xdffKE1a9Z4bLd69Wr17dtXK1askGEYuvzyy5WcnKz58+frrrvu0rFjxy7kj+11cvPzlJSTYZbZAwEAAACo2QKiLlPUoHNHOWbv/05nV061MCJUhNfMQGjatKkeeOAB9ejRQxEREea9Tz/9VJMmTdK3336rN954Q48//rhb299++03Dhw+X3W7XY489pkGDBikoKMi8f+jQIWVmFt0RNCEhQU888YQcDodGjx6tMWPGyM/PT+np6frb3/6m9evX69FHH9WSJUtKnPlQk5zOSnMrN6gValEkAAAAAKqK0OsGKXPnCqX/uFiSlPTJJAXHdldQ86usDQznzStmILRr104rV67U/fff75Y8kKSePXtqzJgxkqQlS5bI6XS63X/mmWeUlZWlJ598UsOHD3dLHkhSq1at1K5duyJ9vv/++8rOztY111yjRx55RH5+BbmW0NBQvfbaawoNDdXu3bu1du3ayvxRvdrp7HTzdXhgbQX4ekV+CgAAAMAFZLPZ1OCBd+VXr0nBhXyHTr13v5z2HGsDw3nzigRCSEiI/P39i73fpUsXSVJKSoqSk5PN6z///LO2bNmi+vXrq3///ufV56pVqyRJffv2LXKvTp06iouLkyStWLHivN63OkvIOpdAaBDM7AMAAAAABXxr11PD4XPMsv3EHiUtmWhhRCgPr0gglCYn51zmynWGwTfffCNJ6tChgyTp448/1l//+lcNHjxYf//737VixYoiMxYk6eTJk0pISJCkYjdXvPrqqyVJO3furJwfohpwnYEQxfIFAAAAAC6CY7qp7i2PmOWU1W8oc8/XFkaE81Ut5ph/+eWXkqQ2bdooJCTEvL57925JUlhYmAYOHKgdO3a4tfv00091zTXX6N1331VYWJh5/ciRI5Ikf39/NWzY0GOfzZo1kyQdO3ZMDoejxBkSCxcu1OLFi8v0sxw6dKhM9aoi1z0QmIEAAAAA4I/q9/mnsvZ8LfuJPZKkhPeHqvnk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", + "image/png": "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hH1F+uZGbyQ3eiNJv76IOfzqUyMT7sQtpLGT0ouIiEhpKswiin369GH37t24ubkRGBiIv78/J0+eJC4uDoCAgAC+/vprGjc+94eODRs2MGLECJKTkzGbzURERGA0Gjl27BjJyck89NBDPP/88+dct3DhQp555hny8vIICAggODiYQ4cOkZWVRWBgID/++CPh4eFl+j61iKJUZdn5Fr7etZpJ25eTaskucs7XzZ2Hml7H0CbX4u/u5aSEInKpCnd12EH6hplkrP8VS1zxO6dA4faQPh3uwaf9AFwDyvbfWREREbl0FaaAMGPGDCIjI2nZsiWurq729j179jBmzBj27t1L/fr1mTdvXpHrDh48yJ133onFYuHpp5/m/vvvx939zOJqBw4cIDMzkxYtWhS5LiEhge7du5Odnc0TTzzB8OHDcXFxIT09nVGjRrFixQqaN2/Or7/+WqZzrlVAkKrIUpDPD3vX88nWpRzPTi9yzt/syaPNO/FA445aGFGkEsmN3UXGhlmkb5iJJWZbif08Gl6PT4eBeF9zFy6+gVcwoYiIiPyvClNAKM22bdu4++67AZg/fz716tWzn7vvvvtYv34948aN47777rvge7799ttMmzaNtm3b8t133xU5l5qayk033UR6ejqTJ0+ma9euZfNGUAFBqharzcrvB7fy300LiclILnLOx9XMo80783Cz6/HWfvIilZrl2N7CYsK6X8g9sqX4TkYTns264dvhHrxb34HRw+eKZhQREZEKtAZCaerWrWt/nZ19Ztjz9u3bWb9+PTVq1GDgwIEXdc+//voLgP79+59zzs/Pjx49evDLL7/w559/lmkBQaSq2Hj8CK+tm8PmxJgi7WaTC0OaXMvwq7poqoJIFeEW3JDqt75A9VtfwBK/h7Q1P5G+5seiayZYC8ja/hdZ2//C4OqOV6vb8L3uAbya34LBVCl+nBERESn3KsW/uBs3bgTA09OTyMgzW0ItWbIEgHbt2gHwyy+/sGLFCtLS0ggKCuKGG26ge/fuGI3GIveLj48nISEBoMTFFa+55hp++eUXtm7dWubvR6Qyi8tI4Z2NC/j94JYi7S4GI/c0bMtTLW8i2NPXOeFExOncajWiRt9XCbjjFXIPbyL93x9JW/szBSlx9j62vBwy1v1CxrpfMPkF49vxXnyvux9zeItS7iwiIiKOqrAFBKvV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4ONqplVguJMTiYiIiEh54V6nJeHPL8bkU6OwwWbl2P8NIW31d84NJlIJOVRAcHd3x9PTkzp16pRVHpFznD194bpa9XExaitQERERETnDHN6CsOeXYPKtWdhgs3HsyyGkrfnRucFEKhmHpjAEBQWRkJBQVllK9eeff7J69Wp27tzJ8ePHSUlJwdXVlYiICLp06cKDDz6Iv79/sddmZmbyxRdf8NdffxEXF4enpydXX301Dz30EO3bty/1uWvWrOGbb75h69atZGVlERISQo8ePRg2bBienlrI70r456wFFLto/QMRERERKYY5rDlhLyzl6ISuFKQdLxyJMOUBDEYTPu36OzueSKXg0AiEG264gdzcXNatW1dWeUr0+eefM2PGDPbt24ebmxuNGjWiWrVq7Nq1i8mTJ9O7d2927959znUnT57kzjvv5PPPPyc2NpZ69ephNptZtmwZDz74IN9//32Jz5w+fTqDBw9m2bJlmM1m6tWrR2xsLJMnT+auu+4iJSXlMr5jAYjLSGFf6nH7sRZQFBEREZGSmEOaEPbcoiLTGeI/v4/09TOdG0ykknCogPDoo49SvXp1XnvtNY4fP37+CxwwaNAgvvvuOzZt2sTSpUuZOXMmf//9N3/88QcNGzbkxIkTjB49+pzrXnrpJQ4dOkSzZs1YvHgxv/32G8uWLeONN97AZrPx9ttvExUVdc51O3bs4J133gHgjTfeYNmyZfz2228sXryYZs2aceDAAcaNG3dZ37PA8rOmL9TxCaCOT4AT04iIiIhIeWcOa07Yc4swelUvbLAWEP/5vWRsmu3cYCKVgEMFhAMHDvD0009z7NgxevfuzVtvvcX8+fNZs2YN69evL/HXpejfvz9t27bF1dW1SHujRo14++23Adi/fz8HDhywn9u1axdLly7FaDTy4YcfEhQUBIDBYGDAgAH06dOHgoICJk2adM7zJk2ahNVqpU+fPgwYMACDwQAUTtv44IMPMBqNLFy4sNhRD1J2Vsbvt7/W9AURERERuRDm8BaEPbcQo9epKc4F+cR9NoCMLXOdG0ykgnNoDYT777/f/sEa4Pvvvy91SgAUfnjftWuXI489R926de2vs7Oz7a//+usvADp06FDsQo8DBgxg9uzZLF++nKysLPuaBpmZmaxYsQIoLFz8r4iICDp06MDq1atZsGABjRs3LtP3I4Vs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uuaaazy227t3r9577z1t2bJFaWlpatCggW666SaNHj1a4eHhF+pH9lpsoggAAABAkmx+/mo08kP9/uyfZeRkyJmVolPvPaCmf/9aNh9fq8NDOXjFDIRNmzZpwIABmjlzprZt26bQ0FC1bt1ahmFow4YNevzxxzVy5EjZ7XaP7X/66Sfddtttmj59uo4cOaKmTZuqefPmSktL0xdffKE1a9Z4bLd69Wr17dtXK1askGEYuvzyy5WcnKz58+frrrvu0rFjxy7kj+11cvPzlJSTYZbZAwEAAACo2QKiLlPUoHNHOWbv/05nV061MCJUhNfMQGjatKkeeOAB9ejRQxEREea9Tz/9VJMmTdK3336rN954Q48//rhb299++03Dhw+X3W7XY489pkGDBikoKMi8f+jQIWVmFt0RNCEhQU888YQcDodGjx6tMWPGyM/PT+np6frb3/6m9evX69FHH9WSJUtKnPlQk5zOSnMrN6gValEkAAAAAKqK0OsGKXPnCqX/uFiSlPTJJAXHdldQ86usDQznzStmILRr104rV67U/fff75Y8kKSePXtqzJgxkqQlS5bI6XS63X/mmWeUlZWlJ598UsOHD3dLHkhSq1at1K5duyJ9vv/++8rOztY111yjRx55RH5+BbmW0NBQvfbaawoNDdXu3bu1du3ayvxRvdrp7HTzdXhgbQX4ekV+CgAAAMAFZLPZ1OCBd+VXr0nBhXyHTr13v5z2HGsDw3nzigRCSEiI/P39i73fpUsXSVJKSoqSk5PN6z///LO2bNmi+vXrq3///ufV56pVqyRJffv2LXKvTp06iouLkyStWLHivN63OkvIOpdAaBDM7AMAAAAABXxr11PD4XPMsv3EHiUtmWhhRCgPr0gglCYn51zmynWGwTfffCNJ6tChgyTp448/1l//+lcNHjxYf//737VixYoiMxYk6eTJk0pISJCkYjdXvPrqqyVJO3furJwfohpwnYEQxfIFAAAAAC6CY7qp7i2PmOWU1W8oc8/XFkaE81Ut5ph/+eWXkqQ2bdooJCTEvL57925JUlhYmAYOHKgdO3a4tfv00091zTXX6N1331VYWJh5/ciRI5Ikf39/NWzY0GOfzZo1kyQdO3ZMDoejxBkSCxcu1OLFi8v0sxw6dKhM9aoi1z0QmIEAAAAA4I/q9/mnsvZ8LfuJPZKkhPeHqvnk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", "text/plain": [ "
" ] @@ -909,7 +893,7 @@ "outputs": [ { "data": { - "image/png": "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qNfhBQtGRUREREREVDs4q0oWEHQWjKR6sYBAlabPy5S/dpKege/E76Cq39iCEREREREREdUekkdD+WtfZzcLRlK9WECgSmswaAHsXLygatAU7j3HQmHHHyMiIiIiIqJij3r4YHnPEJxPicH49j0sHU614W9+VGn2bt7wHrzI0mEQERERERHVWgNadMSAFh0tHUa1Ulo6ACIiIiIiIiKq/VhAICIiIiIiIqJysYBAREREREREROViAYGIiIiIiIiIysUCAhERERERERGViwUEIiIiIiIiIioXCwhEREREREREVC4WEIiIiIiIiIioXCwgEBEREREREVG5WEAgIiIiIiIionKxgEBERERERERE5WIBgYiIiIiIiIjKxQICEREREREREZWLBQQiIiIiIiIiKhcLCERERERERERULhYQiIiIiIiIiKhcLCAQERERERERUbkUQghh6SDonsDAQGRmZsLR0REtW7a0dDhERERERERURzRv3hyffvqp2eP2DzEWqgCNRgMAKCgowKVLlywcDREREREREVERFhBqmfr16yMtLQ0ODg5o3LixpcOpsBs3bqCgoIAjJ2wYc2zbmF/bxxzbPubYtjG/to85tn3WkOPmzZuXeZwFhFrm0KFDlg6hSoKDg3Hp0iW0bNkSO3bssHQ4VAOYY9vG/No+5tj2Mce2jfm1fcyx7bOFHHMRRSIiIiIiIiIqFwsIRERERERERFQuFhCIiIiIiIiIqFwsIBARERERERFRuVhAICIiIiIiIqJysYBAREREREREROViAYGIiIiIiIiIysUCAhERERERERGViwUEIiIiIiIiIioXCwhEREREREREVC57SwdAtmHw4MFITk6Gt7e3pUOhGsIc2zbm1/Yxx7aPObZtzK/tY45tny3kWCGEEJYOgoiIiIiIiIhqN05hICIiIiIiIqJysYBAREREREREROViAYGIiIiIiIiIysUCAhERERERERGVi7sw1GGXL1/GmjVrEBUVhaysLPj4+KBXr16YPHky6tevX+X7njhxAmFhYTh37hzy8vLg7++Pvn37Yvz48XB2di51fkJCArZt24YLFy7g5s2bSEtLg0ajgaenJzp06IDBgwejZ8+eJvtatmwZli9fXmY88+bNw9ChQ6v8PNbMFnJc1T7rgtqS3/T0dOzfvx+///47Ll26hPj4eCiVSvj5+eGZ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", + "image/png": "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qNfhBQtGRUREREREVDs4q0oWEHQWjKR6sYBAlabPy5S/dpKege/E76Cq39iCEREREREREdUekkdD+WtfZzcLRlK9WECgSmswaAHsXLygatAU7j3HQmHHHyMiIiIiIqJij3r4YHnPEJxPicH49j0sHU614W9+VGn2bt7wHrzI0mEQERERERHVWgNadMSAFh0tHUa1Ulo6ACIiIiIiIiKq/VhAICIiIiIiIqJysYBAREREREREROViAYGIiIiIiIiIysUCAhERERERERGViwUEIiIiIiIiIioXCwhEREREREREVC4WEIiIiIiIiIioXCwgEBEREREREVG5WEAgIiIiIiIionKxgEBERERERERE5WIBgYiIiIiIiIjKxQICEREREREREZWLBQQiIiIiIiIiKhcLCERERERERERULhYQiIiIiIiIiKhcLCAQERERERERUbkUQghh6SDonsDAQGRmZsLR0REtW7a0dDhERERERERURzRv3hyffvqp2eP2DzEWqgCNRgMAKCgowKVLlywcDREREREREVERFhBqmfr16yMtLQ0ODg5o3LixpcOpsBs3bqCgoIAjJ2wYc2zbmF/bxxzbPubYtjG/to85tn3WkOPmzZuXeZwFhFrm0KFDlg6hSoKDg3Hp0iW0bNkSO3bssHQ4VAOYY9vG/No+5tj2Mce2jfm1fcyx7bOFHHMRRSIiIiIiIiIqFwsIRERERERERFQuFhCIiIiIiIiIqFwsIBARERERERFRuVhAICIiIiIiIqJysYBAREREREREROViAYGIiIiIiIiIysUCAhERERERERGViwUEIiIiIiIiIioXCwhEREREREREVC57SwdAtmHw4MFITk6Gt7e3pUOhGsIc2zbm1/Yxx7aPObZtzK/tY45tny3kWCGEEJYOgoiIiIiIiIhqN05hICIiIiIiIqJysYBAREREREREROViAYGIiIiIiIiIysUCAhERERERERGVi7sw1GGXL1/GmjVrEBUVhaysLPj4+KBXr16YPHky6tevX+X7njhxAmFhYTh37hzy8vLg7++Pvn37Yvz48XB2di51fkJCArZt24YLFy7g5s2bSEtLg0ajgaenJzp06IDBgwejZ8+eJvtatmwZli9fXmY88+bNw9ChQ6v8PNbMFnJc1T7rgtqS3/T0dOzfvx+///47Ll26hPj4eCiVSvj5+eGZ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", "text/plain": [ "
" ] @@ -974,8 +958,7 @@ " eos, \n", " temperature=300*si.KELVIN, \n", " pressure=1*si.BAR, \n", - " molefracs=np.array([0.5, 0.5]), \n", - " total_moles=si.MOL\n", + " composition=np.array([0.5, 0.5]), \n", ")\n", "s" ] @@ -1025,7 +1008,7 @@ } ], "source": [ - "s.dmu_dni() / (si.KILO * si.JOULE / si.MOL**2)" + "s.n_dmu_dni() / (si.KILO * si.JOULE / si.MOL)" ] }, { @@ -1057,7 +1040,7 @@ } ], "source": [ - "s_cp = feos.State.critical_point(eos, molefracs=np.array([0.5, 0.5]))\n", + "s_cp = feos.State.critical_point(eos, composition=np.array([0.5, 0.5]))\n", "s_cp" ] }, @@ -1123,7 +1106,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABekAAAIwCAYAAAASzuBvAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3WdgVGX6NvBrZjIz6b03kkBIJWVCC1JERUEXRVYRxRVsuCiuq2JX/rsC6rsWRBDBBi7qImsBFQVUikgnvfcC6YX0Mu28H1gmHkMJpJxMcv0+Mfdz5pxriIaZe57zPDJBEAQQEREREREREREREdGAk0sdgIiIiIiIiIiIiIhouGKTnoiIiIiIiIiIiIhIImzSExERERERERERERFJhE16IiIiIiIiIiIiIiKJsElPRERERERERERERCQRNumJiIiIiIiIiIiIiCTCJj0RERERERERERERkUTYpCciIiIiIiIiIiIikgib9EREREREREREREREEmGTnoiIiIiIiIiIiIhIIhZSB7hSP/74Iw4fPoyMjAxUV1ejoaEBSqUSAQEBmDZtGhYuXAgnJ6duzwsJCbnoeV1dXXHo0KH+ik1ERERENKzV1NTg0KFDSE9PR1paGrKystDZ2Ynx48djy5YtvTr30aNHsWnTJqSkpKCtrQ3e3t6YOXMmFi9eDGtr6z56BUREREREfctsm/QbNmxAdnY2VCoV3NzcEBISgvr6emRmZiIzMxPbtm3Dxx9/jNDQ0PM+PzIyEiqVqlvd0dGxn5MTEREREQ1fO3fuxKuvvtrn592yZQtWrVoFQRDg6ekJLy8v5Ofn47333sOePXvw+eef870+EREREQ1KZtukX7BgAQIDAxETEwOlUmmq5+TkYNmyZcjNzcWTTz6JnTt3nvf5a9asga+v70DFJSIiIiIiALa2tpg0aRLGjBmDMWPGIDMzE+vXr+/VOdPT0/HKK68AAF5++WXMmzcPMpkMVVVVWLJkCTIyMvDSSy9h7dq1ffESiIiIiIj6lNk26efNm3feekhICFatWoXbb78d+fn5KCgowMiRIwc4HRERERERnc9tt92G2267zfS4qqqq1+dcv349jEYj5syZgzvuuMNU9/DwwFtvvYVZs2Zhz549yM7OvuCdtkREREREUhmSG8cGBQWZ/tze3i5hEiIiIiIi6k+tra04ePAggPNP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", + "image/png": "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", "text/plain": [ "
" ] @@ -1214,7 +1197,7 @@ "output_type": "stream", "text": [ "Critical point for pure substance\n", - "Python implementation is slower by a factor of 24.\n" + "Python implementation is slower by a factor of 23.\n" ] } ], @@ -1244,7 +1227,7 @@ "output_type": "stream", "text": [ "Phase diagram for pure substance\n", - "Python implementation is slower by a factor of 80.\n" + "Python implementation is slower by a factor of 85.\n" ] } ], @@ -1257,7 +1240,7 @@ ], "metadata": { "kernelspec": { - "display_name": "feos", + "display_name": "feos_devel", "language": "python", "name": "python3" }, @@ -1271,7 +1254,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.14.0" } }, "nbformat": 4, diff --git a/docs/tutorials/eos/pcsaft_entropy_scaling.ipynb b/docs/tutorials/eos/pcsaft_entropy_scaling.ipynb index 1154e36bc..87643fe1b 100644 --- a/docs/tutorials/eos/pcsaft_entropy_scaling.ipynb +++ b/docs/tutorials/eos/pcsaft_entropy_scaling.ipynb @@ -509,7 +509,7 @@ " viscosity_lit = row['Viscosity (Pa*s)'] * si.PASCAL * si.SECOND\n", " \n", " # literature\n", - " state = feos.State(saft, temperature=t, pressure=p, total_moles=si.MOL, density_initialization=row.Phase)\n", + " state = feos.State(saft, temperature=t, pressure=p, density_initialization=row.Phase)\n", " s = state.molar_entropy(feos.Contributions.Residual)\n", " results.append(\n", " {\n", @@ -537,7 +537,7 @@ " )\n", " \n", " # homo GC\n", - " state = feos.State(saft_gc, temperature=t, pressure=p, total_moles=si.MOL)\n", + " state = feos.State(saft_gc, temperature=t, pressure=p)\n", " s = state.molar_entropy(feos.Contributions.Residual)\n", " viscosity = state.viscosity()\n", " ln_viscosity_reduced = state.ln_viscosity_reduced()\n", @@ -564,7 +564,7 @@ "outputs": [ { "data": { - "image/png": "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HowwB4bNoLrah80wcLocmKYZLHfHFqyUNALYXdoDTOR44bLbmXHqGG749D9HPHfGKWNwHaba73hRG8b17duXd999t83Hf//99/H5fJx//vncc889DY7XVha2Rm2HResw63EOV415OJGRkQC43e5QNZt0noMHDwI0uiS6Vu2x2nPbcszacBqCXa1N0zzstYZhEBMTQ0lJSb1rW3vPo9GWr11xcTEbNmxok3mJiJyILMvCvz+3JnCsqXDM24Q3L4tAaWGrxix2RdWEjQnsiggurd4VkUiJKxIMA5fNTmZNp+qf1Syprm0gExvWMb94tyyL8jIPhYWHqhxrw8eDB1r3C1232xkKGlNSIkNLrBMTI1Tl2AEURsoJw2l31nTeDmDWNLlxOGzYbAZerx/DBu4wJ36fiT9gBislLYvqaj82m0FcfDjVVT7276vC5bLjctqpqPRiGAYut4PysmqiosOwLINAIBDsJGvYCXj8OIwANoWSIsc8h83OeT0HcmX/M3lp88omz7tqwJnHfCftWrWBXVP69esHBCsYq6ur6zWeaQt79uwBYOTIkY0eP5ploW538A10SUlJk+fUVqK1VK9evXA6nVRVVbF792569OjRqnGkbdTuMdpUZR8Q6nRee25bjlm38rXu+LXHW3ptS+95NNrytUtOTmbw4MHNuu/27dtVMSwiJywr4A92rc7LCoaN+Vl48jbhy9+EWV3W4vF8ho1cdzy7I4JVjrWh4253AlU1zRZjXeH0jU2hX1wyF8SmcHJcCn1ik+kZldBhVY6BgMm+fZXBJdU1YWNt8Fhd3fJVMIYB8fERNaFjcB/H2iXWkVGuI77PlfajMLIDFRcX8/nnn7N+/Xq+++47srKy8Hg8jBo1ipdeeumoxl65ciXPP/88a9eupbKykvT0dCZMmMC0adMOu/ztROSoqZT0BwKYfgvTtA4tvfYFCJgW4eEOAn4Tf6CmGU7AoqrSh91uIy7ejdfjZ//+KgybQVSUC58vgN9vYrfb2L+/itjYcLxeP4EAuFwOfKYdu9+PDQublm+LHNMiHC7uHnUBo9Myeea7T+st2R6ckM4Np45hfM+Bx00X7doOz02FAj179mTQoEFs3LiRV155hV//+tdtev/acLO4uLjBMY/Hw8svv3zEa5uae0JCArGxsRw8eJCNGzcyaNCgese//vprNm/e3Kp5u91u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4fX62bi+kE+WbCcv99DeaukZMfxwbB8GnZKKy6Vvsccyv9/P7373O8rKyhg1ahT/93//h9vtbnBefHw8l1xyCRdffDFz5sxpcHz16tU8+eSTANxxxx1MnTq1wTnh4eGcf/75jB8/nn/9619t/lwkuK8hQF5eHj6fr9Flzrt27ap37pFkZmYCkJOT0+Q5jY3pcrlIT09nz5497Nq1i+HDhze4Lj8/P1T9WHufzrpnbUA+ZMiQekFkLafTyRlnnMGOHTtYv359k/OSjlU3jLQZAc48q1cnzkZERKQ+lW50oGeffRbTNPn5z3/OpZdeimEEfyuZmprK7NmzsdlsLFq0iE2bNnXyTEVEpCler593FmQx76Vv6wWRAHm5pcx76VveXZiF1+tvYoSuJScnh1tvvZUf/OAHnHrqqUycOJF//vOfoWYX35eVlcWtt97KueeeyymnnMIZZ5zBb37zGxYvXtzo+V9++SX9+/dn7NixALz11ltccsklDBs2jNGjRzNz5kz27NkTOn/ZsmVcffXVjBw5kmHDhnHttdeycePGJufv9Xp54YUXuOSSSxg+fDhDhgxhwoQJPPLII+zbt6/Vr8vbb7/Njh07cDqd/PWvf200iKzLbrdz4403Nnj8qaeewjRNzjzzzEaDyO+Poaqy9jFw4ECcTider5d169Y1es7XX38NwNChQ5s15mmnnQbAnj17KCwsbNGYtV83VQlb+3haWlq9fRg7454VFRWNnt+Y5u63Ke3PDBz6HmQYR25aIyIi0pEURnaQiooKli9fDsDkyZMbHO/duzdnnnkmAB988EGHzk1ERJonEDDZuL6QlSuarkoC+OLzHDauLyQQaDzQ6yqysrKYNGkSH3/8MampqSQkJLBjxw4effRR7r///gbn1waJ77zzDpWVlfTv3x+Xy8Xy5cuZMWMG995772Hv9+ijj3L77bezf/9+evToQVlZGe+++y5XXnkl+/bt46WXXmLatGls376d7t27Y5omn3/+OVdccQU7duxoMF5paSmXX345Dz30EN999x0pKSn06dOHPXv2MHfuXC688MLQXo8t9e677wLBzuNNbYNyJHv37mXlypUAXHnlla0aQ9pGVFQUZ599NgCvv/56g+PZ2dmh/1YTJkxo1piZmZn069cPgNdee63B8S+++IKcnBycTifjxo2rd+z8888Hgu/5Gls2XTvH78+ls+4JsG7dOsrKyhpc5/P5+PLLL+udK12AT2GkiIh0XQojO0hWVhZerxeXy8WQIUMaPae2g+batWtbPP6XX37JTTfdxFVXXcWNN97InDlzyM3NPao5i4hIfYGAydLF25t17qdLt3f5MPJvf/sbEydOZMWKFcyfP59PP/2Uxx9/HMMwmDdvHjt37gydu3XrVu666y78fj/XXHMNK1as4I033mD58uU88sgjOBwOXnnlFd54441G71VYWMirr77Kc889x+LFi1m4cCEffvghPXr0IC8vjzvvvJO//e1vPPzww3z22We8+eabfPLJJwwePJiKigqeeeaZBmPee++9rFu3jm7dujF//nw++OAD3nzzTZYuXcrIkSMpLi7mpptuOmzDjqZ88803AEfV3bq2Qg1g5MiRrR7nRFBSUsLixYtZtGhRaJlxW5sxYwaGYbBgwQJee+01LCsY0BQVFTFz5kxM02T8+PEMGDCg3nVjx45l7Nixjf6yuLYa9rnnnmPJkiWhx3fs2MFdd90FwGWXXUZCQkK968aPH0///v0pKyvjtttuC4V8gUCAJ598klWrVuF2u7n22ms7/Z4TJkzA5XKxf/9+Zs6cWa8i8+DBg9x5553s2LEDwzC48MILG8xXOocVCIT+rDBSRES6Gm1o1UFqf6BLT09vdJ8igJ49e9Y7tyVWrVpV7+uPPvqIZ555hptvvpnrrruuxeOJiEhDVZU+8vNKj3wikLunlKoqf5feO7J379785S9/wW63hx674IILePvtt1myZAmffvppqNJp7ty5+Hw+hg0bxqxZs+qNc9FFF7F582bmzp3LnDlz+MUvftHgXn6/nxtvvJFzzz039FhGRga//vWv+fOf/8zSpUu58sorueiii0LH4+Pjufnmm5k2bRqffvppvfF2794dql68//77GTx4cOhYcnIyTz75JOPGjSM7O5v33nuv3rhHUl5eHlqa2r1792Zf9321oU1MTMwJ20zuwIEDvPnmmwCMGTOGk05q2N33mWee4R//+Ad+/6FKrgsuuIAHH3yw2Z2tm2PIkCHMmjWLhx9+mHvuuYc5c+YQHx/Ptm3b8Hq9ZGZmct999zW4rvaXu7Wd0Os6//zzufrqq3nxxReZPn06PXv2JCIigq1btxIIBBgxYgS33nprg+tsNhtPPvkkl19+OcuWLePcc88lMzOT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LlwYCikba6zWHnr06MFPfvIT3n77be6++26effZZBg4cCASXZ8+cOZOqqip69+7NBRdc0OLxnU4nTz31FL/61a/4+uuvufTSS7nllls455xzQs8bgvs9f/zxx2RnZzc6zqhRo7jxxht56qmneOihh9i+fTvTpk2jR48e9cZYtmwZzz77LLt27eK6665r8Xy7qsTERPLz8xtsF7NmzRrKy8ub3E+yNvxtq320RU40hnVoabbdYe/EmYiIiDTUrmFkWVkZixYtYuXKlXz99deNLrWpuxl9Y+puXA/BUG/o0KGcfvrpjB07lpNPPrlN59yeZsyYwSeffMKCBQsYPnw4kydPxjAMioqKmDlzJqZpMn78+AaB69ixYwH4n//5n3pB5eeff86KFSv45S9/WW9pt9fr5bXXXuORRx4BgntBpqSktP8TFBE5gdjtwdDR5XIQGxve2dNpdyeffDL3338/f/zjH5k7dy7//e9/6dWrF0VFRRQWFgJw2WWXMWnSpA6b0z333ENOTg7r1q3joosuok+fPoSFhbF161Z8Ph9JSUn8/e9/rxcetkS/fv147bXXuOWWW9iyZQszZswgPDycHj16EBkZSWVlJbt376aqqgqAs846q9EqzBtvvJG4uDj+9re/8frrr/P666+Tnp5OYmIi1dXV7NmzJzTGeeed1/oXpAsaOHAgeXl5vPfee9x0002h/xb/7//9PyAY+tbdW7PW7t27AfT+RaQ1zAB1F8DZ7AojRUSka2nzMNI0TT766CMWLlzI8uXL8fl8DQJHh8NBcnIyycnJREdHExYWhsvlIiwsDL/fT3V1NR6PB4/Hw759+ygqKqKsrAwIBpzLly/ns88+44knnqBPnz785Cc/4cILLyQjI6Otn06bGjJkCLNmzeLhhx/mnnvuYc6cOcTHx7Nt2za8Xi+ZmZncd999Da6r7eT5/SViVVVV/POf/+Sf//wnSUlJoa6dO3fuDJ17/vnnc/vtt7fzMxMRkRPBRRddRP/+/fnnP//JV199xaZNm4iMjOTss8/mV7/6VYc3aImJieGVV15h3rx5vP322+zYsQO/309GRgZjx47lN7/5zWG7fzdH3759WbhwIYsWLeKjjz5i7dq15Obm4vP5iI6OZuDAgQwfPpyf/vSnocrMxlxxxRVMmDCB1157jc8//5ydO3eSlZVFWFgYPXv25PTTT+eiiy5iyJAhRzXfrmbChAksXryY/Px8pk6dys9+9jM2bNjAm2++iWEYjB07lvDwhmF+7RLuPn36dMKsRY5xfg8WhwJIhyojRUSkizGsI5UmNpPX6+WNN97gX//6F7m5uaEAsk+fPowYMYKTTz6Zfv360bdv31b9YFBdXU1BQQFbt24lKyuLrKws1q1bR0lJCRAMOM8//3x+85vfHPaHga7giy++YO7cuaxbt47KykrS09OZMGEC06ZNa7QDae0m7g899FC9ipP8/Hxef/111qxZQ05ODvv378fn85GQkMBpp53GxRdfHKqqbGu13bQHDx7M/Pnz2+UeIiIicmyzLIspU6aEwsW6j4eFhfHGG2/Qt2/feteUlpZy1llnEQgE+P3vf8+0adM6etrSBei9ZssFAn42fvcRVB5g24v/4YuI4N+dnn2iufHGMZ08OxERkUPapDLyrbfe4rHHHqO4uJiwsDB+/OMf8+Mf/5gzzjgj1H3zaIWHh9O7d2969+5dbwnT+vXrWbZsGUuWLOHdd9/lvffe49xzz+XOO++kV6/DdzvtLKNHj2b06NHNPn/z5s2NPt6tWzduvvnmtpqWiIiISJsyDIP//d//5a677mLJkiWYZrCrRmpqKvfee2+DIBJg/vz5+P1+DMPgrLPO6ugpixzzTJ8Hs25lpPP4aqgmIiLHvqP6zrR582b+8pe/8M033zBo0CBuvfVWzjvvvEar+9rLKaecwimnnMKMGTPYtm0bb7zxBm+99RY/+9nP+PWvf8306dNbvVeUiIiIiByduLg4nn76afbt28fu3btxu9307dsXm63xRk99+/bloYcewjAMTjnllA6ercixz++rrhdGOh0KI0VEpGtp9XemefPm8fDDDzN8+HCef/75FlX6tZe+ffty++23c/PNN/Of//yHuXPnsnTpUp555pkuv5+kiIiIyPEsISGBhISEI5539tlnd8BsRI5fPr8Hy6gbRmrPSBER6Voa/5X0YXg8HmbOnMnzzz/P7NmzeeGFF7pEEFlXeHg411xzDR9//DFnn302l156KV988UVnT0tERETkhLB//35KS0sbPF5eXs6+ffs6YUYiJw6/93uVkfYW/8gnIiLSrlr0nam8vJxrr72W1NRU3n333Q7vmtlSYWFh3Hbbbfzzn//kwQcfZOXKlZ09JREREZHj2vbt25kwYQJP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Zs2aVa8Kr1evXiQkJLBv3z5ycnKAYOA1a9YsPvzww3bpnN2Y5izBbs4S6LpiYmKaPWbtuRBc5WOz2TBNs8lrLcsKLZWue21n3hOgT58+jV5b+/iRCgamTJnClClTDntOrUmTJqmKshW+v0zbpv0iRUSkCzrmflWWnZ3NNddcwy233HLMvUFZv349Dz74IAD33nsvn3zyCW+++SYff/wxgwcPZvv27dx9990tGvOjjz4KhY3PPvssH330EQsXLuTtt9+me/furFq1itmzZ7fH0xEREREJWbBgAXv37sUwDE455RTeffddPvzwQ/7zn//w4Ycf8u6774Z+kbxv375Qw8GO0Lt3bwDy8vKarCrctWtXvXOPJDMzEyAUsjZ3TJfLRXp6er3j35efnx+aZ+19OuueJ510UujPTa22qa2erN2XUjpRwItlHAoj7VqiLSIiXdAxF0befPPNPPfcc+Tn53PppZdy/fXX8//+3/8jKysLv9/f2dM7rGeffRbTNPn5z3/OpZdeimEE3xykpqYye/ZsbDYbixYtYtOmTc0e8+mnnwbguuuuY9y4caHH+/Tpw/333w/AK6+8wr59+9rwmYiIiIjUt3z5cgDi4+P517/+1aCKrk+fPjz33HMkJiYCsGzZsg6b28CBA3E6nXi9XtatW9foOV9//TUAQ4cObdaYp512GhCsBiwsLGzRmLVfr169utHrah9PS0ur1xCmM+45aNCg0LLt3bt3N3ptbcDZWQ2KpA6/V5WRIiLS5R1zYeQvfvGL0BtKv9/PJ598wj333MOkSZMYNmwYF198MX/84x955ZVX+Oabbzp7uiEVFRWhN+mTJ09ucLx3796ceeaZAHzwwQfNGjM7OzsUXF566aUNjo8ePZpevXrh9XpZvHhxa6cuIiIickSbN2/GMAx+/vOfN7nUOTY2lp///OdYlsWWLVs6bG5RUVGcffbZAI12c87OzmblypUATJgwoVljZmZm0q9fPwBee+21Bse/+OILcnJycDqd9X5hDHD++ecDwfd8jS2brp3j9+fSGfd0u9386Ec/AuCtt95qcJ1lWbz55psAofey0nkMv69eGGnXnpEiItIFHXPfnR544AHmz5/Pt99+y8KFC3n44Ye5+uqrGTVqFBEREWRlZfHGG29w3333cfnll3f2dEOysrLwer24XK4m97ocMWIEQKPdJxuzZs0aAHr06EFqamqbjCkiIiLSGgcOHACCVYiHM2DAgHrnd5QZM2ZgGAYLFizgtddeCzXYKSoqYubMmZimyfjx40PzqzV27FjGjh3b6C+Lb7zxRgCee+45lixZEnp8x44d3HXXXQBcdtllJCQk1Ltu/Pjx9O/fn7KyMm677TbKysoACAQCPPnkk6xatQq32821117bZe7pcDhYvXo1zzzzDIFAAAh22H700UfZtGkTYWFhTJ06tcG10sECXkxDlZEiItK1HXMNbGrZ7Xb69esX+u1wrby8PLKysti4cWOLlju3t507dwKQnp7e5H47PXv2rHfukWRnZ9e7ri3GFBEREWmNqqoqDMMgMjLysOdFREQAdHin5CFDhjBr1iwefvhh7rnnHubMmUN8fDzbtm3D6/WSmZnJfffd1+C63NxcACorKxscO//887n66qt58cUXmT59Oj179iQiIoKtW7cSCAQYMWIEt956a4PrbDYbTz75JJdffjnLli3j3HPPJTMzk4KCAkpKSnA6nTz66KON/rK5M+7Zt29f7r//fv74xz/y97//nZdffpnu3buza9cuDhw4gNPp5MEHH6y3v6R0DlvAi6XKSBER6eK6RBhZ+5vptpCenk56enqDpSmdrTkdGpvT6bG1Y9Z2RzycV199tdGlS43Zvn17s84TERER6SqmTp1K//79mTt3LuvWraOkpIT09HQmTJjAtGnTjhikNubOO+9k2LBhzJs3j6ysLIqKiujTpw8XXnghU6dObfKX0JmZmSxcuJA5c+awdOlStmzZQkxMDOeffz7XX389gwYN6lL3vPjii+nbty///Oc/Wb16NVlZWcTFxfHTn/6U6667rkFFqXQOm9+HyaH//qqMFBGRrqhLhJFdqYKxvXg8HqDpLoQQ7HRY99y2HLM51QfFxcXHXIdyERERkZYYPXo0o0ePbvb5mzdvPuI5EydOZOLEiS2eS1JSEnfffTd33313i6/tjHueeuqpPPnkky2+TjqOLeDFJCz0tbppi4hIV9QlwsgTQVhY8E2Bz+dr8hyv11vv3LYcs7YL4uEkJyczePDgZt17+/btHb68SkRERLo+w1D4IdJZbAEfpnHoRzybXcu0RUSk62l2GFldXc3LL7/MVVddFaq2O5ZYlsW///1vxo0bR/fu3Tv8/s1Zgt2cZdd1xcTENHvM2nMPZ8qUKUyZMqVZ9540aZKqKEVERKSBG264oVnnWZZ1xGY3hmGwcePGtpiWyAnBEfDhq7NnpE2VkSIi0gU1+1dl4eHh9OnTh2uvvZZ9+/a155zaXEFBAVdddRUOh6NTgkiA3r17A8EGO01VMu7ataveuUeSmZkJQE5OTpPntHRMERERkaNlWVaTHxAMGQ3DOOx5dc8XkeaxB3z1G9hoz0gREemCWlS3/6Mf/YjLLruMSZMmsXDhwvaaU5uxLItXXnmFSy65hEsuuYTLL7+80+YycOBAnE4nXq+XdevWNXrO119/DcDQoUObNeZpp50GwJ49eygsLGyTMUVERERaqzkBooJGkfbj+P4ybXXTFhGRLqjFe0ZecMEFdO/enZtuuon//ve/3HzzzYwYMaI95nZUli9fzqOPPkp1dTX/+7//2+y9ENtLVFQUZ599NkuXLuX1119v8JplZ2ezcuVKACZMmNCsMTMzM+nXrx9btmzhtdde46abbqp3/IsvviAnJwen09nluouLiIjI8eVEaEgo0tU5TD9mnXoTVUaKiEhX1KpflQ0ZMoQ33niD6OhorrjiCq6++mqWLFmCaZptPb8WCQQCvPPOO1x88cVMmzaN/v37M3/+/E4PImvNmDEDwzBYsGABr732WqgioKioiJkzZ2KaJuPHj2fAgAH1rhs7dixjx47lgw8+aDDmjTfeCMBzzz3HkiVLQo/v2LGDu+66C4DLLruMhISE9npaIiIiIiLSBTgDPkzqVkYqjBQRka6n1d20ExMTeeaZZ/jkk0944IEHuOGGG0hMTOSiiy7ixz/+MUOGDGnLeTbJsiy+/vpr3n77bT788EMOHjxInz59ePHFFxk1alSHzKG5hgwZwqxZs3j44Ye55557mDNnDvHx8Wzbtg2v10tmZib33Xdfg+tyc3MBqKysbHDs/PPP5+qrr+bFF19k+vTp9OzZk4iICLZu3UogEGDEiBHceuut7f7cRERERESkczlNP5aj7p6RWqYtIiJdT6vDyFo//OEPOeuss3jjjTeYO3cu//znP/nXv/5FXFwcP/jBDxg1ahQDBgygf//+hIWFHfWEKysr2bp1K+vWreOLL75g9erVlJWVYVkWvXr14tZbb2XSpEnY7fYjD9YJpk6dSv/+/Zk7dy7r1q2jpKSE9PR0JkyYwLRp04iMjGzxmHfeeSfDhg1j3rx5ZGVlUVRURJ8+fbjwwguZOnUqTqezHZ6JiIiIiIh0FZZl4Qz4MeuEkTYt0xYRkS7oqMNIAJfLxa9+9SumTJnChx9+yGuvvcZXX33FO++8w7vvvgsEN0/u0aMHGRkZpKSkkJKSQlJSEuHh4YSFhYU+/H4/Ho8Hj8dDZWUlxcXFFBcXU1RUxM6dO8nLywvd17IsDMNgxIgRXH755UyYMAHD6PrfcEePHs3o0aObff7mzZuPeM7EiROZOHHi0UxLRERERESOVQE/dizMut20tUxbRES6oDYJI2sZhsGECROYMGECJSUlvP/++3z00UesWbMGj8dDdnY22dnZrQoM63ZctNvtDBw4kPPPP5+f/vSndOvWrS2fhoiIiIiIyDHF8lUBYBp1KyO1TFtERLqeNg0j60pMTOSKK67giiuuwOfzsX79er755huysrLYvXs3u3fvZt++fUccJyIigh49etCjRw9OPvlkRowYwbBhw1q1nFlEREREROR45PcE95ev28BGlZEiItIVtVsYWZfT6WTYsGEMGzas3uOVlZWUlpZSWVlJVVUVVVVVuFwu3G43brebqKgo4uLiOmKKIiIiIiIix6zq6nIALFQZKSIiXVuHhJFNiYiIICIiojOnICIiIiIicszzVJcB1Nsz0qbKSBER6YL0qzIREREREZFjnMdTAdTfM9KubtoiItIFKYwUERERERE5xnk9wWXaqowUEZGuTmGkiIiIiBy1vLy8zp6CyAnNW9PApu6ekXbtGSkiIl1Qp+4ZKSIiIiLHh3HjxjFw4EDGjh3L+PHjGTBgQGdPSeSE4quuwEn9ZdqqjBQRka5IYaSIiIiIHDXLssjKyiIrK4tnnnmGbt26hYLJUaNGYbOpQkukPfm8wcpIU5WRIiLSxem7k4iIiIgctT//+c+cffbZOBwOLMsiLy+PV155hWuuuYbRo0fzhz/8gQ8//JDKysrOnqrIcclf28CmTr2JKiNFRKQrUmWkiIiIiBy1KVOmMGXKFCoqKli2bBmLFy9m2bJllJaWcvDgQd555x3eeecdnE4nZ555JuPHj2fs2LEkJSV19tRFjgt+bxUAlnGo3kTdtEVEpCtSGCkiIiIibSYyMpKJEycyceJE/H4/q1atYvHixSxevJj8/Hy8Xi/Lly9n+fLl/PnPf2bIkCGMHTuWcePG0adPn86evsgxKxBapq3KSBER6doURoqIiIhIu3A4HIwePZrRo0dz1113sXHjRhYvXszHH3/M5s2bsSyLNWvWsHbtWh5//HF69erFuHHjGDduHMOHD+/s6YscUwLeaqD+npE27RkpIiJdUIeFkVlZWSxbtowtW7ZQWlqKx+M54jWGYfDiiy92wOxEREREpL0NGjSIQYMG8bvf/Y7c3Fw+/vhjFi9ezDfffIPf7yc7O5u5c+cyd+5cEhMT+eEPf8i4ceP4wQ9+gMvl6uzpi3RpZqMNbFQZKSIiXU+7h5FFRUXccccdrFixokXXWZaFYeibp4iIiMjxKCMjg6uvvpqrr76agwcPsnTpUpYsWcLy5cupqqpi7969vPHGG7zxxhuEh4dzzjnn8Pe//72zpy3SZVm+4J6RplF3mbYqI0VEpOtp1zCyoqKCq666ipycHCzLas9biYiIiMgxKjY2losuuoiLLroIr9fLihUrWLx4MUuXLmXv3r1UVVXx0UcfdfY0Rbo00xdcpm2hBjYiItK1tWsY+cILL5CdnY1hGKSlpXH99dfzgx/8gNTUVC21EREREZEGXC4XP/zhD/nhD38Y2lPy448/ZsmSJZ09NZGuzVe7Z6Qa2IiISNfWrmHkxx9/DEBSUhL//e9/SUpKas/biYiIiMhxxDAMhg0bxrBhw/jDH/7Q2dMR6dJMvy/42ai7Z6SWaYuISNfTrt+ddu3ahWEYXHbZZQoiRURERERE2olp+oOf63bTVmWkiIh0Qe0aRpqmCUBmZmZ73kZEREREROSEZtX87GWpm7aIiHRx7RpGZmRkAMFGNiIiIiIiItI+DCsA1F+mrW7aIiLSFbXrd6exY8diWRbffPNNe95GRERERETkxGYFKyNNVUaKiEgX165h5BVXXEFsbCxvv/0227dvb89biYiIiIiInLjMhmGk9owUkf/f3p3HRVX9fxx/zcCwCYgiLuBGKriUpim5lOaWtnz9KpVZlqmVldpi9m3PX/ltXyzbbDXT0rSy0ix3zSV3TVNxR1TcEBVBlhmY+/uD70wgoIDDwOD7+XjwSO4995xzT8zMmc89i0hFVKbByJo1azJu3Di8vLwYOnQo69atK8viRERERERELk2OkZEmb+chs3bTFhGRCsj7wklK78MPPwSgY8eOLFq0iEGDBtGsWTOuvPJKqlWrhsl04Sd1I0eOLMsqioiIiIiIeDyToQ1sRETEM5R5MNIRcDSZTBiGQVxcHHFxccXOQ8FIERERERGRC9A0bRER8RBlGowEMAzjvL+fT3FGToqIiIiIiFzqTIVuYKNp2iIiUvGUaTBy8uTJZZm9iIiIiIiIkCcYadLISBERqdjKNBgZExNTltmLiIiISAWWkZHBd999x7Jly0hMTMQwDEJDQ6lXrx5RUVE0bdqU6OhoatasWd5VFfF8ha4ZqZGRIlI+unXrRmJiIosWLaJu3boAPP300/z000+89tprxMbGlnMNpTyV+TRtEREREbn0pKWlceedd7J79+58xw8dOsTmzZvzHQsJCSE6OpqmTZs6A5SNGzfGYrG4s8oiHq2wadoaGSkiniAuLo6FCxcSERHhcUHKSZMmkZqaSr9+/ZxBV7kwBSNFRERExOU+/vhjdu3a5dzEECAwMJCsrCxsNlu+tKdOnWLNmjWsWbPGeczLy4vIyEhnkNLx37CwsIuq1+rVq/nqq6/YvHkz6enphIeH07t3b4YNG0ZAQECp8pw3bx7ffPMNO3bswGaz0aBBA/r06cOgQYPOG1BNTk5mwoQJLFmyhOPHjxMcHEy7du144IEHaNasWYUrM69vv/2WsWPHArmzoaZMmVLsa6WMGAYGJgyTdtMWkYopLCyMyMhIgoKC8h2Pi4vjww8/JCYmxuOCkZMnTyYxMZGYmBgFI0tAwUgRERERcblFixY5/33//fczdOhQqlWrBsDRo0fZuXNnvp/4+HhycnKc12RnZ7Nnzx727NnDnDlznMerVavmDEw+9dRTJarTlClTeOWVVzAMg9q1a1OnTh327NnDhAkTmD9/PlOnTiUkJKREeb7xxhtMnDgRgPr16+Pv78/u3bt58803WbJkCRMnTsTHx6fAdQkJCdx5552cOHGCgIAAmjRpwtGjR/n9999ZuHAh48ePp3v37hWmzLyOHTvGuHHjStJM4gYme06+UZGgkZEiUrGMHj2a0aNHl3c1pAJwSTDymWeecUU2BZhMJl599dUyyVtEREREys7Ro0cxmUx07NixwBeP2rVrU7t2bbp06eI8ZrVa2bNnDzt27GDnzp3s2LGDXbt2cerUqXzXnjx5klWrVrF69eoSBSO3bt3q7FeOHTuW/v37YzKZOHbsGA899BDbtm3jhRde4IMPPih2ngsWLHAG/t577z1nIG/v3r0MGzaMdevWMW7cOJ5++ul81xmGwaOPPsqJEye49tpreffddwkKCiI7O5uPPvqIjz/+mCeeeIJ58+YVWE+zPMo814svvkhGRgZdu3ZlyZIlxW4vKWsGBvnXiNSakSIiUhG5JBj5008/YTKVzVM3BSNFREREPE9AQABWq5W2bdsWK72Pjw/NmzenefPm+Y4fP368QIDy3FGUxfHxxx9jt9vp27cvt99+u/N4rVq1GDduHDfccAPz589nx44dNG3atFh5fvjhh0DuyM+8IwobNWrEyy+/zODBg/n2228ZNmwY1atXd55ftGgRcXFxBAUF8c477zinq3l7e/Poo4+ybt061q1bx8SJEwsEFcujzLx+++03Fi9ezKBBgwgODlYwsgIx2+3Yz/l6p5GRIlKRFLaBjWOjG4C1a9cSHR3tTB8REcHixYvz5bFr1y6++uor1qxZQ1JSEr6+vjRr1oz+/fvzr3/9q0CZM2fO5JlnniEmJoavvvqKr7/+ml9++YUDBw7g7e3N+vXrnfnOnz+flStXkpiYyMmTJ6lSpQotWrTg9ttvp1evXoXm6zBo0KB85x33eOjQIbp3717ovZyvXUpSd4DExEQmTpzIihUrOHr0KF5eXjRq1MjZ7/H2rlgTo132qMwwDJf/iIiIiIhnatiwIZA73fpi1KxZk86dO3P//ffzzjvvMHv2bDZu3MjMmTOLncfZs2dZvnw5AP379y+0ru3btwdg7ty5xcpz//797NixAyBfcNOhQ4cONGjQAKvVmm/KOsDvv/8OQO/evalatWqBax11dKQrzzLzSklJ4ZVXXqF27do89thjRaaTcmLYsZvOmaatkZEiUsFdfvnlzj5DYGAgbdq0cf5cfvnl+dLOmDGDfv36MXPmTE6fPs1ll12Gv78/a9eu5YknnjjvrF3DMBgxYgRvvvkmZ8+epVGjRgQGBjrPv/rqq3zwwQfs2rWLgIAAoqOj8fX1ZeXKlTzyyCO88cYb+fILDQ2lTZs2zmVRoqKi8tU9NDTURS104bovWbKEm2++mW+++YajR49Sv359QkJC+Pvvvxk7diwPPPAAVqvVZfVxBZeERs/t7IiIiIjIpe3GG29k06ZNbNu2zeV5+/j4lGizlbi4OKxWKz4+PrRs2bLQNFdddRV//vlngZ2+i/LXX38BUK9ePWrVqlVkngkJCWzevJnbbrvNedxRRlGjRh3Hjx49yrFjx5z5l0eZeb3++uucOHGCjz76iCpVqhSaj5Qfk2EvsGakl0ZGikgF9/777ztHADZv3rzIDdFWr17N//3f/+Hn58dLL71EbGwsZnPuA5e1a9fy+OOPM3PmTNq0aZPv889h48aNBAcH8+233zo/8zIzM53nBwwYwFNPPVWgf7Ft2zYef/xxJk6cSM+ePWnTpg0AXbp0oUuXLs6Rnc8//zxXX321S9qkJHXfu3cvo0aNwmq18p///IdBgwY5A6Q7duxg1KhRrFixgo8++ohRo0aVSf1KwyXByIiICFdkIyIiIiKVxK233srXX3/NihUrOHjwIPXq1Su3usTHxwMQHh5e5E7T9evXz5f2Qvbv35/vuuLmabVandPRirq2Tp06WCwWbDYb+/btcwYGy6NMh1WrVjFz5ky6detGjx49iixfyk9uMPKcadraTVvErTKyrVhLuIxIReDj5YW/d8GNzyqSd955B7vdzvPPP88tt9yS71xMTAwvvfQSw4cP54svvig0GJmTk8OLL76Y76Gcn5+f89+9e/cutNwWLVrwf//3fwwZMoRffvnFGYx0p/PV/YMPPiAjI4MRI0Zw33335buuadOmjBs3jn79+vHNN98wfPhwfH193Vr3olSsSeMiIiIiUin4+/vz1ltvMXToUEaPHs2kSZMICAgol7qkpKQAFDo92cFxzpHWlXmeOXPGeSwtLQ273X7ea00mE8HBwSQnJ+e7tjzKhNzRF2PGjCEgIIAxY8YUWfaFfPfdd8yYMaNYaffu3Vvqci5VJsPAMOWflq01I0Xc5//WzOaruD+xe+CSc2aTiSHNOvLS1QXXXKwIjh49ypYtW/D39y90XUjIHalosVjYv39/oSP8AwMD6dmz53nLSUpKYs6cOfz9998kJyeTlZUF4JziHBcX54K7Kbmi6m61Wp1rNxe2fAtAs2bNiIiI4NChQ2zbtq1cgqmFUTBSRERERFzuiSeeoEmTJvTv35+vv/6aO+64g9dee63ABjXu4PgyUdSoSMA5pcmR1pV55p0Gljd/x/mSXuvOMiF3Ct2BAwd45plnqFOnTpHXX0hSUlKZTNuXXOeOjDSbTWW2yaiIFDQpbpVHBiIB7IbBpLhVFTYY6VgvGeCee+65YPrCgpENGzbEy8uriCtyN2h77rnnSE9PLzLN6dOnL1zZMlBU3RMSEsjMzMRsNp93LedTp04BuUHdikLBSDfbvn07n332GevWrePMmTPUrFmTrl27Mnz48Hw7HhbXBx984NxVsSgvvvgid9xxR2mrLCIiIlJiv/76qzMQYjKZ2LlzJ7fccgsxMTF07dqV1q1bExUVhb+/f5nXxTElyWazFZnGMeqhuNOXSpJn3mlgefM/32Ly57vWnWVu376dr7/+mubNm3P33XcXeW1xhIWF0aJFi2Kl3bt3b4GgqJyfyTDyBSO9NEVbxK0GN+vgsSMjvUxmBjfrUN7VKJJjxH5GRgYbN268YPqMjIwCx843O+PgwYM8+eST2Gw2Bg4cSN++fWnYsCFVqlTBy8uLgwcP0qNHj4velK+0iqq7o13sdnux2qUifa4qGOlG8+fP5/HHH8dmsxEaGkqTJk2Ij49nypQpzJ07l2nTppV6PaXQ0FAaNGhQ6LmwsLCLqbaIiIhIqRh5vpCZTCYMw2Dt2rWsXbvWeaxevXo0bdo038/FjL4rTHGmYBdnCnRewcHBxc7TkRZyp1qZzWbsdnuR1xqG4fyCkffa8ijzueeew263M3bs2POOKCmOAQMGMGDAgGKljY2N1SjKEjKfs5u2Y2MHEXGPl67+F09f1UtrRpYBRzCucePGzJkzx+X5//7779hsNnr16lXociSOkYWl4Xgwa5wnSH2+0Zjn49hMzt/f37nJnadQMNJNjh075oy0Dx8+nBEjRuDt7U1qaiqjRo1i+fLlPPbYY/zwww+lmk7RuXNnXn/99TKouYiIiEjJLV68mJ07d+b7SUhIICfPlzTDMDhw4AAHDhxg/vz5zuPBwcFERUXRtGlTnnvuuYuuS8OGDQE4fPgwNput0GnOBw4cyJf2QiIjI4HcKVJFKSxPHx8fwsPDOXToEAcOHCh07aYjR444Rz86yimvMrdv346XlxcPPvhggWscX542bdpEp06dAPjhhx9cHkyWYjIMjDy7aWtkpIj7+Xv74K8oS4ldKAYSFRUF5I5gzMzMzDeC3xUOHToEQLt27Qo9v3nz5lLn7ZgBkpycXGQaxwZ1JdWgQQMsFgsZGRnlvllgSelxmZt88cUXZGRk0K5dOx599FG8vXPfoYKCgnjnnXcICgpi69atzsVHRURERDxZeHg4Xbt25cEHH+Tdd9/lt99+Y9OmTfz444+8+uqr3HPPPbRv355q1aphGEa+n5SUFNatW8c333zjkro0a9YMi8WC1Wply5YthabZsGEDAFdeeWWx8mzVqhWQ+wXm2LFjJcrT8fv69esLvc5xvHbt2tSuXbtcy4TcXTxPnDhR4McRjLTZbM5jOR44IqiyMBt27OQdGalgpIh4BsdyIkVNI65fvz7NmzcnKyuLb7/91uXlO4KbSUlJBc5lZWWdtz/iuLaoulevXp2qVauSlZXF9u3bC5zfsGEDO3fuLE218ff357rrrgPgq6++KlUe5UXBSDeZN28eAP379y9wrmrVqs5t5H///Xe31ktERETEXXx8fGjRogWxsbE888wzTJo0iT///JPly5fzxRdf8J///Ic+ffoQHR193k1aSiowMJBrrrkGoNDdnPfv38/q1asBnH2yC4mMjHSO1Jg+fXqB86tWrSIhIQGLxUL37t3znevVqxcAc+fOLXTatKOO59alPMo8d3Rr3p+RI0cCEBMT4zxWt27dAnmLe5gw8gUjvbz0VU9EPEP9+vUB2LNnDydPniw0zZNPPomXlxfjxo1zDvbK68yZM/zyyy+88cYbJS7fMSJy6tSp+R5aJicn88gjj3DkyJEL1n3dunVFpunSpQsAr7zySr4p39u3b+epp566qD7PY489RkBAAN9++y1vvvlmgc/4jIwMFixYwPPPP1/qMsqCBhC7wZEjR5xPr4sa9tu2bVu+//77Ug//3bFjB6NHjyYpKYkqVaoQHR3NTTfdRJMmTUpdbxERERF3CAsLIywszBkwBMjOzmbfvn0uK2P48OEsXbqUX375hTZt2tC/f39MJhPHjx/n8ccfx26306NHD5o2bZrvum7dugG5X4LODdSNHDmSRx55hM8//5zLL7/cmXbfvn3OTv+dd95ZYJPCHj16EB0dzc6dO3niiScYN24cQUFB5OTk8OGHH7Ju3Tr8/f0ZOnRogfsojzLFM5gMO3ZT/t20RUQ8QfPmzYmMjCQ+Pp4ePXrQuHFjfH19qVGjBu+++y4AHTp04NVXX2XMmDG89dZbjB8/nssuuwwfHx9OnjxJYmIihmEQExNT4vK7detG27ZtWb9+Pf3796dBgwYEBASwe/duAMaMGcMLL7xQ6LU33XQTS5Ys4fPPP2fBggWEhYVhMpm4//776dy5MwCPPPIIf/zxB+vXr6dLly5ERkaSmZnJ/v376dixI61ateLXX38tVds1btyYDz/8kMcee4wvv/ySr7/+msjISAICAkhJSeHgwYPk5OQQERFRqvzLioKRbuCY/2+xWApMe3FwzO0/ePBgkWsZnU9cXBxxcXHO3xcvXswnn3zCoEGDeOqppy56wXERERERd/L29naOAnSFli1b8vTTT/P6668zZswYJkyYQLVq1dizZw9Wq5XIyEj++9//FrguMTERKHxx+V69enHPPffw9ddf89BDD1G/fn3nl5ecnByuuuoqRo8eXeA6s9nM+PHjGThwIMuWLaNz585ERkZy9OhRkpOTsVgsvPXWW9SqVatClCmeIXc37TzTtLVmpIh4CLPZzOeff864ceNYt24dW7duLTSA1rdvX6666iomT57Mn3/+yYEDB7BarVSrVo2OHTty3XXXcf3115e4fC8vLz7//HM++OAD5s6dS2JiIlWrVnUuN3O+ze3+9a9/cebMGX744Qfi4+Od8Z9+/fo509SrV49p06Yxfvx41qxZw759+6hbty6PP/44995770WPWuzUqRO///47U6ZMYdmyZSQkJJCVlUVQUBBt2rShc+fO9OzZ86LKcDWTcb4tfcQlfv/9dx577DFq1KjBypUrC02zd+9ebrzxRgBWr15NtWrVipX39OnTOXHiBNdeey1169YlMDCQ+Ph4pk6dynfffQfAvffey5NPPnnBvL777rtCpy4VVd/MzExatGjBzJkzi3WNiIiISHlbtWoVEydOZMuWLaSnpxMeHk7v3r0ZNmyYc1fKvKKjowF47bXXiI2NLTTP33//nalTpxIXF4fNZqN+/fr06dOHwYMHn/cB84kTJ5gwYQJLlizh+PHjBAcH07ZtWx588EGaN29+3vsojzLP9cEHH/Dhhx8SExPDlClTSnTthTh201Zfs/jWPBBCWk4U84Jyd4KtWTOQJ565rnwrJSIiUggFI93g559/5qmnnqJOnTosXbq00DQHDx6kR48eAPzxxx9FjqAsic8//5y3334bb29v5s2bd8E1fBwdypJQB1FEREREXE3ByJJbNyyY00YLFgTm7kBfu04Qjz/ZpZxrJSIiUpCmaV/AK6+8wuTJk0t8Xd4nxI6doWw2W5HprVar89+O9Bdr6NChTJ48mePHj7N48WIGDRp03vRhYWG0aNGiWHk7RkaKiIiIiEj5O3eatjawERGRikrByAsICAggJCSkxNcFBgY6/+1YXyAlJQXDMDCZCq7fcvr0aSB3rYS8114M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pGzY7Tn5pmgDeGnNSBERqaA0MlJERERELglTpkxh8ODBLF26FF9fXxo1akRiYiITJkzg1ltv5fTp0yXO84033uCRRx5h7dq1hISEUL9+fXbv3s2bb77JkCFDsFqthV6XkJBAnz59mDJlCidPnqRJkyYYhsHvv//ObbfdxqJFiypEmTNnzmTo0KFMnjyZbdu2ERoaSlRUFBkZGSxevJgHHniAMWPGYBhGidtOXMcoJBhp1m7aIiJSQekTSkREREQqva1bt/Lqq68CMHbsWJYuXcpPP/3EwoULadGiBXv37uWFF14oUZ4LFixg4sSJ+Pj48PHHH7NgwQJmzZrF7NmzqVu3LuvWrWPcuHEFrjMMg0cffZQTJ05w7bXXsmzZMmbOnMmyZcsYPnw4NpuNJ554guPHj1eIMqOjo3n55ZdZu3Yt8+bNY+bMmaxZs4Ynn3wSk8nE9OnTmTZtWonaTlzLbs/BbjonGKmRkSIiUkEpGCkiIiIild7HH3+M3W7n3//+N7fffjsmU26gplatWowbNw6z2cz8+fPZsWNHsfP88MMPAbj//vvp3r2783ijRo14+eWXAfj22285efJkvusWLVpEXFwcQUFBvPPOOwQFBQHg7e3No48+Srt27UhPT2fixInlXmbPnj355ZdfuO2225zXOK679957ue222wCYPn16cZtNykBhIyO9NDJSREQqKH1CiYiIiEildvbsWZYvXw5A//79C5xv2LAh7du3B2Du3LnFynP//v3OwOXtt99e4HyHDh1o0KABVqu1wPTn33//HYDevXtTtWrVAtc66uhIV55lhoSEOAO3hencuTMA8fHxRaaRsmcYBTew8dIGNiIiUkEpGCkiIiIilVpcXBxWqxUfHx9atmxZaJqrrroKgM2bNxcrz7/++guAevXqUatWrRLl6fi9bdu2hV7nOH706FGOHTtWrmVeSGZmJgD+/v7FvkZcz8jJwW7KvzeppmmLiEhFpd20RURERKRSc4zaCw8PL3Kn6fr16+dLeyH79+/Pd11x87RarSQmJp732jp16mCxWLDZbOzbt88ZeCyPMi9kzpw5wD9B0PP57rvvmDFjRrHy3bt3b7HSyf8Ydux5xpmYzabzjmgVEREpTwpGioiIiEillpKSAlDo9GQHxzlHWlfmeebMGeextLQ07Hb7ea81mUwEBweTnJyc79ryKPN8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devWDB06lO3btxdZf6vVyqRJk7j11ltp06YNLVu2pHfv3rzxxhucPHmy1O0ye/Zs9u3bh8Vi4c0338Tf//xLAnl5eTn7AXl98MEH2O122rdvz+DBgy+YR0UIRmrNSA92+vRpIHc6dlGb04SEhABgt9tJS0s7b+cxKSlJuxaKiMglwW43SpbeMDCbzVQLDeCXX3Zz16Arsdmy8bNnYrnAzsNSMbRs2ZKnn36a119/nTFjxjBhwgSqVavGnj17sFqtREZG8t///rfAdYmJiQDO9Zfy6tWrF/fccw9ff/01Dz30EPXr1ycgIIDdu3eTk5PDVVddxejRowtcZzabGT9+PAMHDmTZsmV07tyZyMhIjh49SnJyMhaLhbfeeotatWqVe5mTJ092Tv0GCv0S5PD+++8TFhZW5HkpO4Zhz7+btkZGikgFERcXx/Dhw8nOzqZRo0Z4e3uzb98+3nrrLQ4fPsyYMWPypf/555957rnnyM7OJigoiOjoaI4fP87y5ctZvnw5AwcOLHBNXm+99RZffPEFdevWpV69euzbt485c+awadMmfvzxR+bMmcMrr7xCjRo1qFu3Lvv27WPlypX89ddf/PDDD1x22WX58jtz5gz33nuvc5mXyMhI/P392b17NxMnTmT27Nl8+eWXREdHl7ht5syZA+Q++KxTp06Jrwc4ceKEc3bH3XffXao8yoOCkR4s63/TxM63g2He4b1ZF5hWFhYWdsGdmRz27t1LZqZGhIiIiGeqWTMQs9lUrKCkn583gYG+GBj4WMzs3pVEVqaVgwdPExERTBAGXhYLJi91qyq6wYMHEx0dzcSJE9myZQvJycmEh4fTu3dvhg0bVqo1EJ999llat27N1KlTiYuL4/jx4zRq1Ig+ffowePDgIvtpkZGRzJo1iwkTJrBkyRJ27dpFcHAwvXr14sEHH6R58+YVokyr9Z91Vfft23fetrhQX1PK0DkjI81aM1JEKoi3336bvn378swzzzg/Z3/77Tcef/xxpk6dyt133+3cOG337t08//zzZGdnM2TIEB5//HFnTMMRpPz2229p0aIFt9xyS4Gyjh07xnfffcfnn39O586dgdyHivfccw8HDx7k2WefZdWqVbz++uv07dsXgFOnTnHvvfeybds2PvroI9555518eY4dO5YtW7ZQp04dPvroI2fMJCkpiVGjRrFu3Toe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qyFyQeprioiIeKbi9De93VQXqWSysrIAyMzMZNu2beVcGxERERGpTNTXFBERqbwUjJRSqV69OidPnsTX15e6deuWd3U8wt69e8nMzNQT/lJQ25We2q701Halp7YrvbJuu8jISJfnKVIWyrKvqfeosqc2dg+1s3uond1D7ewe7mjn4vQ3NU1bxE1iY2PZtm0bLVq0YObMmeVdHY+itis9tV3pqe1KT21Xemo7kbKn11nZUxu7h9rZPdTO7qF2do+K0s7awEZERERERERERETcQsFIERERERERERERcQsFI0VERERERERERMQtFIwUERERERERERERt1AwUkRERERERERERNxCwUgRERERERERERFxCwUjRURERERERERExC0UjBQRERERERERERG3UDBSRERERERERERE3ELBSBEREREREREREXEL7/KugMilon///iQlJREWFlbeVfE4arvSU9uVntqu9NR2pae2Eyl7ep2VPbWxe6id3UPt7B5qZ/eoKO1sMgzDKNcaiIiIiIiIiIiIyCVB07RFRERERERERETELRSMFBEREREREREREbdQMFJERERERERERETcQsFIERERERERERERcQvtpi3iYmfOnGHFihX8/fffbN26la1bt5Kenk5ERASLFy++6Py3b9/OZ599xrp16zhz5gw1a9aka9euDB8+nOrVq7vgDspfWdzjqVOn+Oqrr1iyZAmHDh3CZrNRvXp1Wrduzd13303btm1dfBfloyz/Pv744w++//57/vrrL06fPk3VqlWpV68eV199NQ8//DDe3p79keKO19Yff/zBsGHDAFz2nlARuLLtDMNg06ZNLF68mA0bNrBv3z7S0tIICgqiefPm9O3bl3/961+YTKYyuhvXWb16NV999RWbN28mPT2d8PBwevfuzbBhwwgICChVnvPmzeObb75hx44d2Gw2GjRoQJ8+fRg0aBAWi8XFdyBSuZTFa1JyVab3bk9TWfsWFUll7wOXt0vle1pZSkpKYuXKlWzdupW///6buLg4srKyiImJYcqUKee91maz8fXXXzNr1iwOHDiAxWKhadOm3H333Vx//fVlVmftpi3iYgsXLmTEiBEFjruiczB//nwef/xxbDYboaGh1K5dm/j4eNLT0wkLC2PatGnUq1fvosoob2Vxj/v37+euu+4iKSkJs9lMREQEgYGBHDhwgLNnz2IymXj66acZPHhw2dyUm5TV30d2djbPPPMMs2bNAqBOnTrUqFGD06dPc/ToUWw2Gxs3bqRKlSquviW3ccdr6+zZs9x8880cPnwYqDxfGFzddqtWrcr3WqxXrx7BwcEkJiZy+vRpAK677jo++OADfHx8XHw3rjNlyhReeeUVDMOgdu3aVK9enT179mC1WmnUqBFTp04lJCSkRHm+8cYbTJw4EYD69evj7+/Pnj17yMnJoV27dkycOLFCt4lIeSqL16T8o7K8d3uaytq3qCguhT5webtUvqeVtUmTJvHaa68VOH6hYGRWVhZDhgxhw4YNeHl50bhxYzIyMjhw4AAA999/P0888UTZVNoQEZdatmyZMXDgQOP11183fv31V+Obb74xoqKijK5du15UvkePHjVatWplREVFGe+9955hs9kMwzCMM2fOGPfee68RFRVlxMbGGna73RW3US7K6h4HDRpkREVFGddff72xe/du5/HMzEzj9ddfN6KioozmzZsb8fHxrrwdtyrLv4/nnnvOiIqKMm655RZj27Zt+c6lp6cbCxcuNKxWq0vuozy467X13//+14iKijIeeughl7wnVARl0XYrV640unXrZnz99dfGiRMn8p376aefjMsvv9yIiooy3nzzTZfeiyv9/fffRtOmTY3o6Gjju+++c97/0aNHjX79+hlRUVHGyJEjS5Tn/PnzjaioKOPyyy83Fi5c6Dy+Z88eo1u3bkZUVJTx2muvufQ+RCqLsnhNSn6V4b3bE1XGvkVFUtn7wBXBpfA9zR2+//57Y/DgwcY777xjzJ8/33jvvfeMqKgo46677jrvdY73kG7duhl79+51Hl+4cKHzfXvRokVlUmcFI0XK2OLFi13SOXj55ZeNqKgoY+DAgQXOnT592rjqqqvK9M3CHcriHlNTU43o6GgjKirKWLBgQYHzdrvd6NmzpxEVFWVMmTLloupfnsrq72PVqlXOv9/U1FRXVbdCccdra9OmTUbTpk2Nhx56yPjxxx8rzReGsnrNnq9jP2HCBCMqKsqIiYkxcnJySlXvsub4Uvjkk08WOBcfH280bdrUiIqKMuLi4oqdZ58+fYyoqChj/PjxBc79+eefzkBlcnLyRdVdpDIqi9ek5FcZ3rs9TWXtW1QUl0IfuLxdKt/TysOUKVMuGIxMSkoyWrRoYURFRRmr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SE9P58iRIyxdupSxY8fSq1cvli9f7pK8RaT4NDJSRKQSevbZZ8nOzmbSpEl06NAh37mDBw9it9vLqWYiIiIi4mnS0tJYvXo1wEXPrvH29iY7Oxur1crs2bO56667zps+JSWFhQsX5ru2uD766KN8v6elpbFjxw5+/vlnTp06xYkTJxg+fDhTpkzhyiuvLPG9iEjpKBgpIlLJHD16lP3799OyZcsCgUiAevXqlUOtRERERMRTLVu2DJvNBkCPHj0uKq/Q0FBq1KjBtm3bmDlz5gWDkb/++itZWVkAdOnSxTlCsziKqusDDzzAfffdx9atW7Farbz22mtMnz69+DchIhdF07RFRCqZwMBAvLy82Lp1K59++mmxp7+IiIiIiBTGEQAMDw+nefPmF53fLbfcAsC2bdvYsWPHedM6pmhfccUVNGnS5KLLBqhWrRpvvPGG8/e//vqLI0eOuCRvEbkwBSNFRCqZwMBAnnrqKby8vBg3bhwdO3Zk0KBBfP3115w6deqC11utVjfUUkRERERKYtiwYURHR9OsWTPOnj1LUlIS7733HgMGDKBDhw5cfvnldOjQgREjRrBx40aXlWuz2Vi2bBkA3bp1c0meN998M76+vgD88MMPRabbsWMH27ZtA/4JYLpK48aNadCggfP3nTt35juflZXFwoULefnllxkwYADt27enRYsWtG7dmuuvv57//Oc/rFy50qV1ErlUKBgpIlIJ3XPPPSxYsICnn36a9u3bs3HjRl599VV69OjB+vXrnensdjutW7dm0qRJvPzyy7Rv356+ffsCuR2w999/n549e9KyZUtiY2PzXQu5U3buuOMO2rZtS5s2bYiNjVWnTERERKQMbN++HYCGDRuydOlSrr/+eiZMmMCmTZs4efIkNpuNkydPsnDhQgYOHMj333/vknLXrVvHmTNngIufou1QtWpVZ16zZ88u8mG4I1Dp6+vLTTfd5JKy86pevbrz36mpqfnO3XjjjYwYMYIpU6awadMmTp06RXZ2Nunp6SQkJDBr1iyGDh3K8OHDOXv2rMvrJlKZac1IEZFKqk6dOgwZMoQhQ4Zw8uRJXnzxRebNm8cnn3zCF198AeRuZpOens4XX3xBz549eeedd7BYLGRnZzNs2DD27t3LyJEjqVevHt9//z3Dhg1j/vz51KhRgzVr1jBixAgeeOABHnnkETIz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hg1Fpk1PT+f555/HMAzMZjOPPvpoqct1TNFu2LAhjRs3LnU+JTFkyBBatWpFq1atuP7662nZsqVbyj1XQECA8995274wH3zwQVlXR6RSUjBSREREREREpAKKi4vDMAzn77Nnz2bLli0F0iUnJzNixAjnhi2jRo0qsElhcWVlZbFixQqAMt245lz9+vVjxowZzJgxo1yDfIGBgTRs2BDIXa9z7ty5BdLk5OTw6quvsmzZMjfXTqRy0JqRIiIiIiIiIhVQ3s1revXqxbx58xg0aBC33XYbrVq1wmq1snXrVn799VdSUlIAuPnmmxk2bFipy/zzzz+du3G7Y4p2RXTPPffw0ksvAfDYY49x44030q5dO6pWrUpCQgKzZ89m7969REVFYbFY8v1/EpELUzBSREREREREpALKu3nNCy+8gN1uZ8GCBUyePLlAWi8vL4YMGcLo0aMvqkzHFO3Q0FDatGlzUXl5qjvuuIOtW7fy448/YhgGc+bMYc6cOfnSREVF8fHHH/Pss8+WUy1FPJeCkSIiHuTchb09nWMqkYiIiIgU5AhG1qxZk7CwMN5//32+//57Zs6cSXx8PFlZWdSuXZuOHTtyxx13EBUVdVHl2e12lixZAsB1112H2XxpruxmMpl49dVXue6665g+fTpbt27l7NmzhISEEBkZSe/evbn11lvLZbdvkcrAZORdgEJEREREREREyl1mZiZt2rQhJyeH6667jk8//bTMy9y4cSN33HEHAB9//PElO01bRMrWpfmYQ0RERERERKQC27FjBzk5OQA0a9bMLWU6pmj7+/vTqVMnt5QpIpceTdMWEfFQH3/8MePHj893zMfHh/DwcNq1a8fw4cMJDw8vp9qJiIiIyMXIuylK8+bN3VKmIxjZqVMn/Pz83FKmiFx6FIwUEfFQcXFxALRt25Z69eoBcPr0adavX8/333/P3Llz+eGHH2jYsGE51lJERERESiPv5jXuGhk5d+5ct5QjIpc2BSNFRDyUIxj52GOP0a5dO+fx48ePExsbS1JSEl988QUvv/xyeVVRRERERErJEYwMDg52PngWEakMtGakiIgHSktL49ChQwA0btw437maNWty6623Av8ELEVERETEc9hsNnbv3g1A06ZNy7k2IiKupZGRIiIeKC4uDsMwCA0NpVq1agXOh4SEALlP0l0lJSWFmJgY6tWrx2+//cakSZP45ZdfOHjwIDVq1OCOO+7g/vvvB2Dnzp189tlnrF69moyMDFq2bMnzzz9fIHAqIiIiIgVZLBa2bt1a3tUQESkTCkaKiHggx7SdooJ7+/btA6Bly5YuK9MxyrJOnToMGDCAY8eOcdVVVxEaGsqaNWt4++23CQkJwWKx8MILL9C8eXNiYmLYtGkTq1at4oEHHuC3337D19fXZXUSERERERERz6JgpIiIB9qxYwcATZo0KXDu0KFDzJo1i5CQEIYMGeKyMh3ByLVr13Lbbbcxbdo0Z2Dxk08+4d1332XChAmcPXuWzz77jA4dOgCQnp7Ov/71Lw4dOsTff/9N27ZtXVYnERERERER8SxaM1JExAM5RkY2atTIeSw9PZ2lS5dy7733Eh4eztSpU53TtYsyZcoUnnvuORYtWnTBMvPu3v3SSy/lG+HYtWtXABITE/m///s/ZyASICAggKuvvhrI3VxHRERERERELl0aGSki4mGsVit79+4F4KWXXuKll17Kd75169ZMmjQJPz+/C+a1YsUKli5dSu3atenevft50zqCkcOHD8fLyyvfubS0NAAuu+wybrzxxgLXpqamAhAaGnrBOomIiIiIiEjlpZGRIiIeZs+ePdhsNiwWC/369aNfv37cdNNNREVFAbBp0ybefvttl5aZlZXFvn37CAoKon379gXOO6aN9+jRo9Drd+3aBeCso4iIiIiIiFyaNDJSRMTDOEYoNmnShNdffz3fuTfffJMvv/ySH374gVGjRlGlSpXz5vXpp58Wq8xdu3aRnZ1N8+bNC4yKBNi2bRtQ+IY5aWlpJCQkUKdOnUJ3/hYREREREZFLh0ZGioh4GEcw8vLLLy9w7uGHHyYoKIiMjAyWLl3q8jJbtGhR6HnHGpbNmzcvcG7Hjh0YhkGzZs1cVh8RERERERHxTApGioh4mPMFI/39/bnhhhsAirUpTXGdL9hotVrZs2cPISEhREREFFnfwq4VERERERGRS4uCkSIiHsQwDOf6jIUFI+GfdRuXL19Odna2S8p1lFlYQHHXrl3YbLZSjZoUERERERGRS4uCkSIiHuTgwYOkpaXh4+NT5GYwHTp0oEqVKpw5c4Z169ZddJl2u52dO3cSEBBAZGRkgfOOYGNRwUiNjBQREREREREHBSNFRDyII/AXHR2NxWIpNI2Pjw9dunQBXDNVOz4+nvT0dJo2bYrZXPBjw7F5zYWmcNepU+ei6yIiIiIiIiKezWQYhlHelRAREREREREREZHKTyMjRURERERERERExC0UjBQRERERERERERG3UDBSRERERERERERE3ELBSBEREREREREREXELBSNFRERERERERETELRSMFBEREREREREREbdQMFJERERERERERETcQsFIERERERERERERcQsFI0VERERERERERMQtFIwUERERERERERERt1AwUkRERERERERERNzi/wE5F7P8SiKuBQAAAABJRU5ErkJggg==", + "image/png": "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HowwB4bNoLrah80wcLocmKYZLHfHFqyUNALYXdoDTOR44bLbmXHqGG749D9HPHfGKWNwHaba73hRG8b17duXd999t83Hf//99/H5fJx//vncc889DY7XVha2Rm2HResw63EOV415OJGRkQC43e5QNZt0noMHDwI0uiS6Vu2x2nPbcszacBqCXa1N0zzstYZhEBMTQ0lJSb1rW3vPo9GWr11xcTEbNmxok3mJiJyILMvCvz+3JnCsqXDM24Q3L4tAaWGrxix2RdWEjQnsiggurd4VkUiJKxIMA5fNTmZNp+qf1Syprm0gExvWMb94tyyL8jIPhYWHqhxrw8eDB1r3C1232xkKGlNSIkNLrBMTI1Tl2AEURsoJw2l31nTeDmDWNLlxOGzYbAZerx/DBu4wJ36fiT9gBislLYvqaj82m0FcfDjVVT7276vC5bLjctqpqPRiGAYut4PysmqiosOwLINAIBDsJGvYCXj8OIwANoWSIsc8h83OeT0HcmX/M3lp88omz7tqwJnHfCftWrWBXVP69esHBCsYq6ur6zWeaQt79uwBYOTIkY0eP5ploW538A10SUlJk+fUVqK1VK9evXA6nVRVVbF792569OjRqnGkbdTuMdpUZR8Q6nRee25bjlm38rXu+LXHW3ptS+95NNrytUtOTmbw4MHNuu/27dtVMSwiJywr4A92rc7LCoaN+Vl48jbhy9+EWV3W4vF8ho1cdzy7I4JVjrWh4253AlU1zRZjXeH0jU2hX1wyF8SmcHJcCn1ik+kZldBhVY6BgMm+fZXBJdU1YWNt8Fhd3fJVMIYB8fERNaFjcB/H2iXWkVGuI77PlfajMLIDFRcX8/nnn7N+/Xq+++47srKy8Hg8jBo1ipdeeumoxl65ciXPP/88a9eupbKykvT0dCZMmMC0adMOu/ztROSoqZT0BwKYfgvTtA4tvfYFCJgW4eEOAn4Tf6CmGU7AoqrSh91uIy7ejdfjZ//+KgybQVSUC58vgN9vYrfb2L+/itjYcLxeP4EAuFwOfKYdu9+PDQublm+LHNMiHC7uHnUBo9Myeea7T+st2R6ckM4Np45hfM+Bx00X7doOz02FAj179mTQoEFs3LiRV155hV//+tdtev/acLO4uLjBMY/Hw8svv3zEa5uae0JCArGxsRw8eJCNGzcyaNCgese//vprNm/e3Kp5u91u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4fX62bi+kE+WbCcv99DeaukZMfxwbB8GnZKKy6Vvsccyv9/P7373O8rKyhg1ahT/93//h9vtbnBefHw8l1xyCRdffDFz5sxpcHz16tU8+eSTANxxxx1MnTq1wTnh4eGcf/75jB8/nn/9619t/lwkuK8hQF5eHj6fr9Flzrt27ap37pFkZmYCkJOT0+Q5jY3pcrlIT09nz5497Nq1i+HDhze4Lj8/P1T9WHufzrpnbUA+ZMiQekFkLafTyRlnnMGOHTtYv359k/OSjlU3jLQZAc48q1cnzkZERKQ+lW50oGeffRbTNPn5z3/OpZdeimEEfyuZmprK7NmzsdlsLFq0iE2bNnXyTEVEpCler593FmQx76Vv6wWRAHm5pcx76VveXZiF1+tvYoSuJScnh1tvvZUf/OAHnHrqqUycOJF//vOfoWYX35eVlcWtt97KueeeyymnnMIZZ5zBb37zGxYvXtzo+V9++SX9+/dn7NixALz11ltccsklDBs2jNGjRzNz5kz27NkTOn/ZsmVcffXVjBw5kmHDhnHttdeycePGJufv9Xp54YUXuOSSSxg+fDhDhgxhwoQJPPLII+zbt6/Vr8vbb7/Njh07cDqd/PWvf200iKzLbrdz4403Nnj8qaeewjRNzjzzzEaDyO+Poaqy9jFw4ECcTider5d169Y1es7XX38NwNChQ5s15mmnnQbAnj17KCwsbNGYtV83VQlb+3haWlq9fRg7454VFRWNnt+Y5u63Ke3PDBz6HmQYR25aIyIi0pEURnaQiooKli9fDsDkyZMbHO/duzdnnnkmAB988EGHzk1ERJonEDDZuL6QlSuarkoC+OLzHDauLyQQaDzQ6yqysrKYNGkSH3/8MampqSQkJLBjxw4effRR7r///gbn1waJ77zzDpWVlfTv3x+Xy8Xy5cuZMWMG995772Hv9+ijj3L77bezf/9+evToQVlZGe+++y5XXnkl+/bt46WXXmLatGls376d7t27Y5omn3/+OVdccQU7duxoMF5paSmXX345Dz30EN999x0pKSn06dOHPXv2MHfuXC688MLQXo8t9e677wLBzuNNbYNyJHv37mXlypUAXHnlla0aQ9pGVFQUZ599NgCvv/56g+PZ2dmh/1YTJkxo1piZmZn069cPgNdee63B8S+++IKcnBycTifjxo2rd+z8888Hgu/5Gls2XTvH78+ls+4JsG7dOsrKyhpc5/P5+PLLL+udK12AT2GkiIh0XQojO0hWVhZerxeXy8WQIUMaPae2g+batWtbPP6XX37JTTfdxFVXXcWNN97InDlzyM3NPao5i4hIfYGAydLF25t17qdLt3f5MPJvf/sbEydOZMWKFcyfP59PP/2Uxx9/HMMwmDdvHjt37gydu3XrVu666y78fj/XXHMNK1as4I033mD58uU88sgjOBwOXnnlFd54441G71VYWMirr77Kc889x+LFi1m4cCEffvghPXr0IC8vjzvvvJO//e1vPPzww3z22We8+eabfPLJJwwePJiKigqeeeaZBmPee++9rFu3jm7dujF//nw++OAD3nzzTZYuXcrIkSMpLi7mpptuOmzDjqZ88803AEfV3bq2Qg1g5MiRrR7nRFBSUsLixYtZtGhRaJlxW5sxYwaGYbBgwQJee+01LCsY0BQVFTFz5kxM02T8+PEMGDCg3nVjx45l7Nixjf6yuLYa9rnnnmPJkiWhx3fs2MFdd90FwGWXXUZCQkK968aPH0///v0pKyvjtttuC4V8gUCAJ598klWrVuF2u7n22ms7/Z4TJkzA5XKxf/9+Zs6cWa8i8+DBg9x5553s2LEDwzC48MILG8xXOocVCIT+rDBSRES6Gm1o1UFqf6BLT09vdJ8igJ49e9Y7tyVWrVpV7+uPPvqIZ555hptvvpnrrruuxeOJiEhDVZU+8vNKj3wikLunlKoqf5feO7J379785S9/wW63hx674IILePvtt1myZAmffvppqNJp7ty5+Hw+hg0bxqxZs+qNc9FFF7F582bmzp3LnDlz+MUvftHgXn6/nxtvvJFzzz039FhGRga//vWv+fOf/8zSpUu58sorueiii0LH4+Pjufnmm5k2bRqffvppvfF2794dql68//77GTx4cOhYcnIyTz75JOPGjSM7O5v33nuv3rhHUl5eHlqa2r1792Zf9321oU1MTMwJ20zuwIEDvPnmmwCMGTOGk05q2N33mWee4R//+Ad+/6FKrgsuuIAHH3yw2Z2tm2PIkCHMmjWLhx9+mHvuuYc5c+YQHx/Ptm3b8Hq9ZGZmct999zW4rvaXu7Wd0Os6//zzufrqq3nxxReZPn06PXv2JCIigq1btxIIBBgxYgS33nprg+tsNhtPPvkkl19+OcuWLePcc88lMzOT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LlwYCikba6zWHnr06MFPfvIT3n77be6++26effZZBg4cCASXZ8+cOZOqqip69+7NBRdc0OLxnU4nTz31FL/61a/4+uuvufTSS7nllls455xzQs8bgvs9f/zxx2RnZzc6zqhRo7jxxht56qmneOihh9i+fTvTpk2jR48e9cZYtmwZzz77LLt27eK6665r8Xy7qsTERPLz8xtsF7NmzRrKy8ub3E+yNvxtq320RU40hnVoabbdYe/EmYiIiDTUrmFkWVkZixYtYuXKlXz99deNLrWpuxl9Y+puXA/BUG/o0KGcfvrpjB07lpNPPrlN59yeZsyYwSeffMKCBQsYPnw4kydPxjAMioqKmDlzJqZpMn78+AaB69ixYwH4n//5n3pB5eeff86KFSv45S9/WW9pt9fr5bXXXuORRx4BgntBpqSktP8TFBE5gdjtwdDR5XIQGxve2dNpdyeffDL3338/f/zjH5k7dy7//e9/6dWrF0VFRRQWFgJw2WWXMWnSpA6b0z333ENOTg7r1q3joosuok+fPoSFhbF161Z8Ph9JSUn8/e9/rxcetkS/fv147bXXuOWWW9iyZQszZswgPDycHj16EBkZSWVlJbt376aqqgqAs846q9EqzBtvvJG4uDj+9re/8frrr/P666+Tnp5OYmIi1dXV7NmzJzTGeeed1/oXpAsaOHAgeXl5vPfee9x0002h/xb/7//9PyAY+tbdW7PW7t27AfT+RaQ1zAB1F8DZ7AojRUSka2nzMNI0TT766CMWLlzI8uXL8fl8DQJHh8NBcnIyycnJREdHExYWhsvlIiwsDL/fT3V1NR6PB4/Hw759+ygqKqKsrAwIBpzLly/ns88+44knnqBPnz785Cc/4cILLyQjI6Otn06bGjJkCLNmzeLhhx/mnnvuYc6cOcTHx7Nt2za8Xi+ZmZncd999Da6r7eT5/SViVVVV/POf/+Sf//wnSUlJoa6dO3fuDJ17/vnnc/vtt7fzMxMRkRPBRRddRP/+/fnnP//JV199xaZNm4iMjOTss8/mV7/6VYc3aImJieGVV15h3rx5vP322+zYsQO/309GRgZjx47lN7/5zWG7fzdH3759WbhwIYsWLeKjjz5i7dq15Obm4vP5iI6OZuDAgQwfPpyf/vSnocrMxlxxxRVMmDCB1157jc8//5ydO3eSlZVFWFgYPXv25PTTT+eiiy5iyJAhRzXfrmbChAksXryY/Px8pk6dys9+9jM2bNjAm2++iWEYjB07lvDwhmF+7RLuPn36dMKsRY5xfg8WhwJIhyojRUSkizGsI5UmNpPX6+WNN97gX//6F7m5uaEAsk+fPowYMYKTTz6Zfv360bdv31b9YFBdXU1BQQFbt24lKyuLrKws1q1bR0lJCRAMOM8//3x+85vfHPaHga7giy++YO7cuaxbt47KykrS09OZMGEC06ZNa7QDae0m7g899FC9ipP8/Hxef/111qxZQ05ODvv378fn85GQkMBpp53GxRdfHKqqbGu13bQHDx7M/Pnz2+UeIiIicmyzLIspU6aEwsW6j4eFhfHGG2/Qt2/feteUlpZy1llnEQgE+P3vf8+0adM6etrSBei9ZssFAn42fvcRVB5g24v/4YuI4N+dnn2iufHGMZ08OxERkUPapDLyrbfe4rHHHqO4uJiwsDB+/OMf8+Mf/5gzzjgj1H3zaIWHh9O7d2969+5dbwnT+vXrWbZsGUuWLOHdd9/lvffe49xzz+XOO++kV6/DdzvtLKNHj2b06NHNPn/z5s2NPt6tWzduvvnmtpqWiIiISJsyDIP//d//5a677mLJkiWYZrCrRmpqKvfee2+DIBJg/vz5+P1+DMPgrLPO6ugpixzzTJ8Hs25lpPP4aqgmIiLHvqP6zrR582b+8pe/8M033zBo0CBuvfVWzjvvvEar+9rLKaecwimnnMKMGTPYtm0bb7zxBm+99RY/+9nP+PWvf8306dNbvVeUiIiIiByduLg4nn76afbt28fu3btxu9307dsXm63xRk99+/bloYcewjAMTjnllA6ercixz++rrhdGOh0KI0VEpGtp9XemefPm8fDDDzN8+HCef/75FlX6tZe+ffty++23c/PNN/Of//yHuXPnsnTpUp555pkuv5+kiIiIyPEsISGBhISEI5539tlnd8BsRI5fPr8Hy6gbRmrPSBER6Voa/5X0YXg8HmbOnMnzzz/P7NmzeeGFF7pEEFlXeHg411xzDR9//DFnn302l156KV988UVnT0tERETkhLB//35KS0sbPF5eXs6+ffs6YUYiJw6/93uVkfYW/8gnIiLSrlr0nam8vJxrr72W1NRU3n333Q7vmtlSYWFh3Hbbbfzzn//kwQcfZOXKlZ09JREREZHj2vbt25kwYQJP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Zs2aVa8Kr1evXiQkJLBv3z5ycnKAYOA1a9YsPvzww3bpnN2Y5izBbs4S6LpiYmKaPWbtuRBc5WOz2TBNs8lrLcsKLZWue21n3hOgT58+jV5b+/iRCgamTJnClClTDntOrUmTJqmKshW+v0zbpv0iRUSkCzrmflWWnZ3NNddcwy233HLMvUFZv349Dz74IAD33nsvn3zyCW+++SYff/wxgwcPZvv27dx9990tGvOjjz4KhY3PPvssH330EQsXLuTtt9+me/furFq1itmzZ7fH0xEREREJWbBgAXv37sUwDE455RTeffddPvzwQ/7zn//w4Ycf8u6774Z+kbxv375Qw8GO0Lt3bwDy8vKarCrctWtXvXOPJDMzEyAUsjZ3TJfLRXp6er3j35efnx+aZ+19OuueJ510UujPTa22qa2erN2XUjpRwItlHAoj7VqiLSIiXdAxF0befPPNPPfcc+Tn53PppZdy/fXX8//+3/8jKysLv9/f2dM7rGeffRbTNPn5z3/OpZdeimEE3xykpqYye/ZsbDYbixYtYtOmTc0e8+mnnwbguuuuY9y4caHH+/Tpw/333w/AK6+8wr59+9rwmYiIiIjUt3z5cgDi4+P517/+1aCKrk+fPjz33HMkJiYCsGzZsg6b28CBA3E6nXi9XtatW9foOV9//TUAQ4cObdaYp512GhCsBiwsLGzRmLVfr169utHrah9PS0ur1xCmM+45aNCg0LLt3bt3N3ptbcDZWQ2KpA6/V5WRIiLS5R1zYeQvfvGL0BtKv9/PJ598wj333MOkSZMYNmwYF198MX/84x955ZVX+Oabbzp7uiEVFRWhN+mTJ09ucLx3796ceeaZAHzwwQfNGjM7OzsUXF566aUNjo8ePZpevXrh9XpZvHhxa6cuIiIickSbN2/GMAx+/vOfN7nUOTY2lp///OdYlsWWLVs6bG5RUVGcffbZAI12c87OzmblypUATJgwoVljZmZm0q9fPwBee+21Bse/+OILcnJycDqd9X5hDHD++ecDwfd8jS2brp3j9+fSGfd0u9386Ec/AuCtt95qcJ1lWbz55psAofey0nkMv69eGGnXnpEiItIFHXPfnR544AHmz5/Pt99+y8KFC3n44Ye5+uqrGTVqFBEREWRlZfHGG29w3333cfnll3f2dEOysrLwer24XK4m97ocMWIEQKPdJxuzZs0aAHr06EFqamqbjCkiIiLSGgcOHACCVYiHM2DAgHrnd5QZM2ZgGAYLFizgtddeCzXYKSoqYubMmZimyfjx40PzqzV27FjGjh3b6C+Lb7zxRgCee+45lixZEnp8x44d3HXXXQBcdtllJCQk1Ltu/Pjx9O/fn7KyMm677TbKysoACAQCPPnkk6xatQq32821117bZe7pcDhYvXo1zzzzDIFAAAh22H700UfZtGkTYWFhTJ06tcG10sECXkxDlZEiItK1HXMNbGrZ7Xb69esX+u1wrby8PLKysti4cWOLlju3t507dwKQnp7e5H47PXv2rHfukWRnZ9e7ri3GFBEREWmNqqoqDMMgMjLysOdFREQAdHin5CFDhjBr1iwefvhh7rnnHubMmUN8fDzbtm3D6/WSmZnJfffd1+C63NxcACorKxscO//887n66qt58cUXmT59Oj179iQiIoKtW7cSCAQYMWIEt956a4PrbDYbTz75JJdffjnLli3j3HPPJTMzk4KCAkpKSnA6nTz66KON/rK5M+7Zt29f7r//fv74xz/y97//nZdffpnu3buza9cuDhw4gNPp5MEHH6y3v6R0DlvAi6XKSBER6eK6RBhZ+5vptpCenk56enqDpSmdrTkdGpvT6bG1Y9Z2RzycV199tdGlS43Zvn17s84TERER6SqmTp1K//79mTt3LuvWraOkpIT09HQmTJjAtGnTjhikNubOO+9k2LBhzJs3j6ysLIqKiujTpw8XXnghU6dObfKX0JmZmSxcuJA5c+awdOlStmzZQkxMDOeffz7XX389gwYN6lL3vPjii+nbty///Oc/Wb16NVlZWcTFxfHTn/6U6667rkFFqXQOm9+HyaH//qqMFBGRrqhLhJFdqYKxvXg8HqDpLoQQ7HRY99y2HLM51QfFxcXHXIdyERERkZYYPXo0o0ePbvb5mzdvPuI5EydOZOLEiS2eS1JSEnfffTd33313i6/tjHueeuqpPPnkky2+TjqOLeDFJCz0tbppi4hIV9QlwsgTQVhY8E2Bz+dr8hyv11vv3LYcs7YL4uEkJyczePDgZt17+/btHb68SkRERLo+w1D4IdJZbAEfpnHoRzybXcu0RUSk62l2GFldXc3LL7/MVVddFaq2O5ZYlsW///1vxo0bR/fu3Tv8/s1Zgt2cZdd1xcTENHvM2nMPZ8qUKUyZMqVZ9540aZKqKEVERKSBG264oVnnWZZ1xGY3hmGwcePGtpiWyAnBEfDhq7NnpE2VkSIi0gU1+1dl4eHh9OnTh2uvvZZ9+/a155zaXEFBAVdddRUOh6NTgkiA3r17A8EGO01VMu7ataveuUeSmZkJQE5OTpPntHRMERERkaNlWVaTHxAMGQ3DOOx5dc8XkeaxB3z1G9hoz0gREemCWlS3/6Mf/YjLLruMSZMmsXDhwvaaU5uxLItXXnmFSy65hEsuuYTLL7+80+YycOBAnE4nXq+XdevWNXrO119/DcDQoUObNeZpp50GwJ49eygsLGyTMUVERERaqzkBooJGkfbj+P4ybXXTFhGRLqjFe0ZecMEFdO/enZtuuon//ve/3HzzzYwYMaI95nZUli9fzqOPPkp1dTX/+7//2+y9ENtLVFQUZ599NkuXLuX1119v8JplZ2ezcuVKACZMmNCsMTMzM+nXrx9btmzhtdde46abbqp3/IsvviAnJwen09nluouLiIjI8eVEaEgo0tU5TD9mnXoTVUaKiEhX1KpflQ0ZMoQ33niD6OhorrjiCq6++mqWLFmCaZptPb8WCQQCvPPOO1x88cVMmzaN/v37M3/+/E4PImvNmDEDwzBYsGABr732WqgioKioiJkzZ2KaJuPHj2fAgAH1rhs7dixjx47lgw8+aDDmjTfeCMBzzz3HkiVLQo/v2LGDu+66C4DLLruMhISE9npaIiIiIiLSBTgDPkzqVkYqjBQRka6n1d20ExMTeeaZZ/jkk0944IEHuOGGG0hMTOSiiy7ixz/+MUOGDGnLeTbJsiy+/vpr3n77bT788EMOHjxInz59ePHFFxk1alSHzKG5hgwZwqxZs3j44Ye55557mDNnDvHx8Wzbtg2v10tmZib33Xdfg+tyc3MBqKysbHDs/PPP5+qrr+bFF19k+vTp9OzZk4iICLZu3UogEGDEiBHceuut7f7cRERERESkczlNP5aj7p6RWqYtIiJdT6vDyFo//OEPOeuss3jjjTeYO3cu//znP/nXv/5FXFwcP/jBDxg1ahQDBgygf//+hIWFHfWEKysr2bp1K+vWreOLL75g9erVlJWVYVkWvXr14tZbb2XSpEnY7fYjD9YJpk6dSv/+/Zk7dy7r1q2jpKSE9PR0JkyYwLRp04iMjGzxmHfeeSfDhg1j3rx5ZGVlUVRURJ8+fbjwwguZOnUqTqezHZ6JiIiIiIh0FZZl4Qz4MeuEkTYt0xYRkS7oqMNIAJfLxa9+9SumTJnChx9+yGuvvcZXX33FO++8w7vvvgsEN0/u0aMHGRkZpKSkkJKSQlJSEuHh4YSFhYU+/H4/Ho8Hj8dDZWUlxcXFFBcXU1RUxM6dO8nLywvd17IsDMNgxIgRXH755UyYMAHD6PrfcEePHs3o0aObff7mzZuPeM7EiROZOHHi0UxLRERERESOVQE/dizMut20tUxbRES6oDYJI2sZhsGECROYMGECJSUlvP/++3z00UesWbMGj8dDdnY22dnZrQoM63ZctNvtDBw4kPPPP5+f/vSndOvWrS2fhoiIiIiIyDHF8lUBYBp1KyO1TFtERLqeNg0j60pMTOSKK67giiuuwOfzsX79er755huysrLYvXs3u3fvZt++fUccJyIigh49etCjRw9OPvlkRowYwbBhw1q1nFlEREREROR45PcE95ev28BGlZEiItIVtVsYWZfT6WTYsGEMGzas3uOVlZWUlpZSWVlJVVUVVVVVuFwu3G43brebqKgo4uLiOmKKIiIiIiIix6zq6nIALFQZKSIiXVuHhJFNiYiIICIiojOnICIiIiIicszzVJcB1Nsz0qbKSBER6YL0qzIREREREZFjnMdTAdTfM9KubtoiItIFKYwUERERERE5xnk9wWXaqowUEZGuTmGkiIiIiBy1vLy8zp6CyAnNW9PApu6ekXbtGSkiIl1Qp+4ZKSIiIiLHh3HjxjFw4EDGjh3L+PHjGTBgQGdPSeSE4quuwEn9ZdqqjBQRka5IYaSIiIiIHDXLssjKyiIrK4tnnnmGbt26hYLJUaNGYbOpQkukPfm8wcpIU5WRIiLSxem7k4iIiIgctT//+c+cffbZOBwOLMsiLy+PV155hWuuuYbRo0fzhz/8gQ8//JDKysrOnqrIcclf28CmTr2JKiNFRKQrUmWkiIiIiBy1KVOmMGXKFCoqKli2bBmLFy9m2bJllJaWcvDgQd555x3eeecdnE4nZ555JuPHj2fs2LEkJSV19tRFjgt+bxUAlnGo3kTdtEVEpCtSGCkiIiIibSYyMpKJEycyceJE/H4/q1atYvHixSxevJj8/Hy8Xi/Lly9n+fLl/PnPf2bIkCGMHTuWcePG0adPn86evsgxKxBapq3KSBER6doURoqIiIhIu3A4HIwePZrRo0dz1113sXHjRhYvXszHH3/M5s2bsSyLNWvWsHbtWh5//HF69erFuHHjGDduHMOHD+/s6YscUwLeaqD+npE27RkpIiJdUIeFkVlZWSxbtowtW7ZQWlqKx+M54jWGYfDiiy92wOxEREREpL0NGjSIQYMG8bvf/Y7c3Fw+/vhjFi9ezDfffIPf7yc7O5u5c+cyd+5cEhMT+eEPf8i4ceP4wQ9+gMvl6uzpi3RpZqMNbFQZKSIiXU+7h5FFRUXccccdrFixokXXWZaFYeibp4iIiMjxKCMjg6uvvpqrr76agwcPsnTpUpYsWcLy5cupqqpi7969vPHGG7zxxhuEh4dzzjnn8Pe//72zpy3SZVm+4J6RplF3mbYqI0VEpOtp1zCyoqKCq666ipycHCzLas9biYiIiMgxKjY2losuuoiLLroIr9fLihUrWLx4MUuXLmXv3r1UVVXx0UcfdfY0Rbo00xdcpm2hBjYiItK1tWsY+cILL5CdnY1hGKSlpXH99dfzgx/8gNTUVC21EREREZEGXC4XP/zhD/nhD38Y2lPy448/ZsmSJZ09NZGuzVe7Z6Qa2IiISNfWrmHkxx9/DEBSUhL//e9/SUpKas/biYiIiMhxxDAMhg0bxrBhw/jDH/7Q2dMR6dJMvy/42ai7Z6SWaYuISNfTrt+ddu3ahWEYXHbZZQoiRURERERE2olp+oOf63bTVmWkiIh0Qe0aRpqmCUBmZmZ73kZEREREROSEZtX87GWpm7aIiHRx7RpGZmRkAMFGNiIiIiIiItI+DCsA1F+mrW7aIiLSFbXrd6exY8diWRbffPNNe95GRERERETkxGYFKyNNVUaKiEgX165h5BVXXEFsbCxvv/0227dvb89biYiIiIiInLjMhmGk9owUkf/f3p3HRVX9fxx/zcCwCYgiLuBGKriUpim5lOaWtnz9KpVZlqmVldpi9m3PX/ltXyzbbDXT0rSy0ix3zSV3TVNxR1TcEBVBlhmY+/uD70wgoIDDwOD7+XjwSO4995xzT8zMmc89i0hFVKbByJo1azJu3Di8vLwYOnQo69atK8viRERERERELk2OkZEmb+chs3bTFhGRCsj7wklK78MPPwSgY8eOLFq0iEGDBtGsWTOuvPJKqlWrhsl04Sd1I0eOLMsqioiIiIiIeDyToQ1sRETEM5R5MNIRcDSZTBiGQVxcHHFxccXOQ8FIERERERGRC9A0bRER8RBlGowEMAzjvL+fT3FGToqIiIiIiFzqTIVuYKNp2iIiUvGUaTBy8uTJZZm9iIiIiIiIkCcYadLISBERqdjKNBgZExNTltmLiIiISAWWkZHBd999x7Jly0hMTMQwDEJDQ6lXrx5RUVE0bdqU6OhoatasWd5VFfF8ha4ZqZGRIlI+unXrRmJiIosWLaJu3boAPP300/z000+89tprxMbGlnMNpTyV+TRtEREREbn0pKWlceedd7J79+58xw8dOsTmzZvzHQsJCSE6OpqmTZs6A5SNGzfGYrG4s8oiHq2wadoaGSkiniAuLo6FCxcSERHhcUHKSZMmkZqaSr9+/ZxBV7kwBSNFRERExOU+/vhjdu3a5dzEECAwMJCsrCxsNlu+tKdOnWLNmjWsWbPGeczLy4vIyEhnkNLx37CwsIuq1+rVq/nqq6/YvHkz6enphIeH07t3b4YNG0ZAQECp8pw3bx7ffPMNO3bswGaz0aBBA/r06cOgQYPOG1BNTk5mwoQJLFmyhOPHjxMcHEy7du144IEHaNasWYUrM69vv/2WsWPHArmzoaZMmVLsa6WMGAYGJgyTdtMWkYopLCyMyMhIgoKC8h2Pi4vjww8/JCYmxuOCkZMnTyYxMZGYmBgFI0tAwUgRERERcblFixY5/33//fczdOhQqlWrBsDRo0fZuXNnvp/4+HhycnKc12RnZ7Nnzx727NnDnDlznMerVavmDEw+9dRTJarTlClTeOWVVzAMg9q1a1OnTh327NnDhAkTmD9/PlOnTiUkJKREeb7xxhtMnDgRgPr16+Pv78/u3bt58803WbJkCRMnTsTHx6fAdQkJCdx5552cOHGCgIAAmjRpwtGjR/n9999ZuHAh48ePp3v37hWmzLyOHTvGuHHjStJM4gYme06+UZGgkZEiUrGMHj2a0aNHl3c1pAJwSTDymWeecUU2BZhMJl599dUyyVtEREREys7Ro0cxmUx07NixwBeP2rVrU7t2bbp06eI8ZrVa2bNnDzt27GDnzp3s2LGDXbt2cerUqXzXnjx5klWrVrF69eoSBSO3bt3q7FeOHTuW/v37YzKZOHbsGA899BDbtm3jhRde4IMPPih2ngsWLHAG/t577z1nIG/v3r0MGzaMdevWMW7cOJ5++ul81xmGwaOPPsqJEye49tpreffddwkKCiI7O5uPPvqIjz/+mCeeeIJ58+YVWE+zPMo814svvkhGRgZdu3ZlyZIlxW4vKWsGBvnXiNSakSIiUhG5JBj5008/YTKVzVM3BSNFREREPE9AQABWq5W2bdsWK72Pjw/NmzenefPm+Y4fP368QIDy3FGUxfHxxx9jt9vp27cvt99+u/N4rVq1GDduHDfccAPz589nx44dNG3atFh5fvjhh0DuyM+8IwobNWrEyy+/zODBg/n2228ZNmwY1atXd55ftGgRcXFxBAUF8c477zinq3l7e/Poo4+ybt061q1bx8SJEwsEFcujzLx+++03Fi9ezKBBgwgODlYwsgIx2+3Yz/l6p5GRIlKRFLaBjWOjG4C1a9cSHR3tTB8REcHixYvz5bFr1y6++uor1qxZQ1JSEr6+vjRr1oz+/fvzr3/9q0CZM2fO5JlnniEmJoavvvqKr7/+ml9++YUDBw7g7e3N+vXrnfnOnz+flStXkpiYyMmTJ6lSpQotWrTg9ttvp1evXoXm6zBo0KB85x33eOjQIbp3717ovZyvXUpSd4DExEQmTpzIihUrOHr0KF5eXjRq1MjZ7/H2rlgTo132qMwwDJf/iIiIiIhnatiwIZA73fpi1KxZk86dO3P//ffzzjvvMHv2bDZu3MjMmTOLncfZs2dZvnw5AP379y+0ru3btwdg7ty5xcpz//797NixAyBfcNOhQ4cONGjQAKvVmm/KOsDvv/8OQO/evalatWqBax11dKQrzzLzSklJ4ZVXXqF27do89thjRaaTcmLYsZvOmaatkZEiUsFdfvnlzj5DYGAgbdq0cf5cfvnl+dLOmDGDfv36MXPmTE6fPs1ll12Gv78/a9eu5YknnjjvrF3DMBgxYgRvvvkmZ8+epVGjRgQGBjrPv/rqq3zwwQfs2rWLgIAAoqOj8fX1ZeXKlTzyyCO88cYb+fILDQ2lTZs2zmVRoqKi8tU9NDTURS104bovWbKEm2++mW+++YajR49Sv359QkJC+Pvvvxk7diwPPPAAVqvVZfVxBZeERs/t7IiIiIjIpe3GG29k06ZNbNu2zeV5+/j4lGizlbi4OKxWKz4+PrRs2bLQNFdddRV//vlngZ2+i/LXX38BUK9ePWrVqlVkngkJCWzevJnbbrvNedxRRlGjRh3Hjx49yrFjx5z5l0eZeb3++uucOHGCjz76iCpVqhSaj5Qfk2EvsGakl0ZGikgF9/777ztHADZv3rzIDdFWr17N//3f/+Hn58dLL71EbGwsZnPuA5e1a9fy+OOPM3PmTNq0aZPv889h48aNBAcH8+233zo/8zIzM53nBwwYwFNPPVWgf7Ft2zYef/xxJk6cSM+ePWnTpg0AXbp0oUuXLs6Rnc8//zxXX321S9qkJHXfu3cvo0aNwmq18p///IdBgwY5A6Q7duxg1KhRrFixgo8++ohRo0aVSf1KwyXByIiICFdkIyIiIiKVxK233srXX3/NihUrOHjwIPXq1Su3usTHxwMQHh5e5E7T9evXz5f2Qvbv35/vuuLmabVandPRirq2Tp06WCwWbDYb+/btcwYGy6NMh1WrVjFz5ky6detGjx49iixfyk9uMPKcadraTVvErTKyrVhLuIxIReDj5YW/d8GNzyqSd955B7vdzvPPP88tt9yS71xMTAwvvfQSw4cP54svvig0GJmTk8OLL76Y76Gcn5+f89+9e/cutNwWLVrwf//3fwwZMoRffvnFGYx0p/PV/YMPPiAjI4MRI0Zw33335buuadOmjBs3jn79+vHNN98wfPhwfH193Vr3olSsSeMiIiIiUin4+/vz1ltvMXToUEaPHs2kSZMICAgol7qkpKQAFDo92cFxzpHWlXmeOXPGeSwtLQ273X7ea00mE8HBwSQnJ+e7tjzKhNzRF2PGjCEgIIAxY8YUWfaFfPfdd8yYMaNYaffu3Vvqci5VJsPAMOWflq01I0Xc5//WzOaruD+xe+CSc2aTiSHNOvLS1QXXXKwIjh49ypYtW/D39y90XUjIHalosVjYv39/oSP8AwMD6dmz53nLSUpKYs6cOfz9998kJyeTlZUF4JziHBcX54K7Kbmi6m61Wp1rNxe2fAtAs2bNiIiI4NChQ2zbtq1cgqmFUTBSRERERFzuiSeeoEmTJvTv35+vv/6aO+64g9dee63ABjXu4PgyUdSoSMA5pcmR1pV55p0Gljd/x/mSXuvOMiF3Ct2BAwd45plnqFOnTpHXX0hSUlKZTNuXXOeOjDSbTWW2yaiIFDQpbpVHBiIB7IbBpLhVFTYY6VgvGeCee+65YPrCgpENGzbEy8uriCtyN2h77rnnSE9PLzLN6dOnL1zZMlBU3RMSEsjMzMRsNp93LedTp04BuUHdikLBSDfbvn07n332GevWrePMmTPUrFmTrl27Mnz48Hw7HhbXBx984NxVsSgvvvgid9xxR2mrLCIiIlJiv/76qzMQYjKZ2LlzJ7fccgsxMTF07dqV1q1bExUVhb+/f5nXxTElyWazFZnGMeqhuNOXSpJn3mlgefM/32Ly57vWnWVu376dr7/+mubNm3P33XcXeW1xhIWF0aJFi2Kl3bt3b4GgqJyfyTDyBSO9NEVbxK0GN+vgsSMjvUxmBjfrUN7VKJJjxH5GRgYbN268YPqMjIwCx843O+PgwYM8+eST2Gw2Bg4cSN++fWnYsCFVqlTBy8uLgwcP0qNHj4velK+0iqq7o13sdnux2qUifa4qGOlG8+fP5/HHH8dmsxEaGkqTJk2Ij49nypQpzJ07l2nTppV6PaXQ0FAaNGhQ6LmwsLCLqbaIiIhIqRh5vpCZTCYMw2Dt2rWsXbvWeaxevXo0bdo038/FjL4rTHGmYBdnCnRewcHBxc7TkRZyp1qZzWbsdnuR1xqG4fyCkffa8ijzueeew263M3bs2POOKCmOAQMGMGDAgGKljY2N1SjKEjKfs5u2Y2MHEXGPl67+F09f1UtrRpYBRzCucePGzJkzx+X5//7779hsNnr16lXociSOkYWl4Xgwa5wnSH2+0Zjn49hMzt/f37nJnadQMNJNjh075oy0Dx8+nBEjRuDt7U1qaiqjRo1i+fLlPPbYY/zwww+lmk7RuXNnXn/99TKouYiIiEjJLV68mJ07d+b7SUhIICfPlzTDMDhw4AAHDhxg/vz5zuPBwcFERUXRtGlTnnvuuYuuS8OGDQE4fPgwNput0GnOBw4cyJf2QiIjI4HcKVJFKSxPHx8fwsPDOXToEAcOHCh07aYjR444Rz86yimvMrdv346XlxcPPvhggWscX542bdpEp06dAPjhhx9cHkyWYjIMjDy7aWtkpIj7+Xv74K8oS4ldKAYSFRUF5I5gzMzMzDeC3xUOHToEQLt27Qo9v3nz5lLn7ZgBkpycXGQaxwZ1JdWgQQMsFgsZGRnlvllgSelxmZt88cUXZGRk0K5dOx599FG8vXPfoYKCgnjnnXcICgpi69atzsVHRURERDxZeHg4Xbt25cEHH+Tdd9/lt99+Y9OmTfz444+8+uqr3HPPPbRv355q1aphGEa+n5SUFNatW8c333zjkro0a9YMi8WC1Wply5YthabZsGEDAFdeeWWx8mzVqhWQ+wXm2LFjJcrT8fv69esLvc5xvHbt2tSuXbtcy4TcXTxPnDhR4McRjLTZbM5jOR44IqiyMBt27OQdGalgpIh4BsdyIkVNI65fvz7NmzcnKyuLb7/91uXlO4KbSUlJBc5lZWWdtz/iuLaoulevXp2qVauSlZXF9u3bC5zfsGEDO3fuLE218ff357rrrgPgq6++KlUe5UXBSDeZN28eAP379y9wrmrVqs5t5H///Xe31ktERETEXXx8fGjRogWxsbE888wzTJo0iT///JPly5fzxRdf8J///Ic+ffoQHR193k1aSiowMJBrrrkGoNDdnPfv38/q1asBnH2yC4mMjHSO1Jg+fXqB86tWrSIhIQGLxUL37t3znevVqxcAc+fOLXTatKOO59alPMo8d3Rr3p+RI0cCEBMT4zxWt27dAnmLe5gw8gUjvbz0VU9EPEP9+vUB2LNnDydPniw0zZNPPomXlxfjxo1zDvbK68yZM/zyyy+88cYbJS7fMSJy6tSp+R5aJicn88gjj3DkyJEL1n3dunVFpunSpQsAr7zySr4p39u3b+epp566qD7PY489RkBAAN9++y1vvvlmgc/4jIwMFixYwPPPP1/qMsqCBhC7wZEjR5xPr4sa9tu2bVu+//77Ug//3bFjB6NHjyYpKYkqVaoQHR3NTTfdRJMmTUpdbxERERF3CAsLIywszBkwBMjOzmbfvn0uK2P48OEsXbqUX375hTZt2tC/f39MJhPHjx/n8ccfx26306NHD5o2bZrvum7dugG5X4LODdSNHDmSRx55hM8//5zLL7/cmXbfvn3OTv+dd95ZYJPCHj16EB0dzc6dO3niiScYN24cQUFB5OTk8OGHH7Ju3Tr8/f0ZOnRogfsojzLFM5gMO3ZT/t20RUQ8QfPmzYmMjCQ+Pp4ePXrQuHFjfH19qVGjBu+++y4AHTp04NVXX2XMmDG89dZbjB8/nssuuwwfHx9OnjxJYmIihmEQExNT4vK7detG27ZtWb9+Pf3796dBgwYEBASwe/duAMaMGcMLL7xQ6LU33XQTS5Ys4fPPP2fBggWEhYVhMpm4//776dy5MwCPPPIIf/zxB+vXr6dLly5ERkaSmZnJ/v376dixI61ateLXX38tVds1btyYDz/8kMcee4wvv/ySr7/+msjISAICAkhJSeHgwYPk5OQQERFRqvzLioKRbuCY/2+xWApMe3FwzO0/ePBgkWsZnU9cXBxxcXHO3xcvXswnn3zCoEGDeOqppy56wXERERERd/L29naOAnSFli1b8vTTT/P6668zZswYJkyYQLVq1dizZw9Wq5XIyEj++9//FrguMTERKHxx+V69enHPPffw9ddf89BDD1G/fn3nl5ecnByuuuoqRo8eXeA6s9nM+PHjGThwIMuWLaNz585ERkZy9OhRkpOTsVgsvPXWW9SqVatClCmeIXc37TzTtLVmpIh4CLPZzOeff864ceNYt24dW7duLTSA1rdvX6666iomT57Mn3/+yYEDB7BarVSrVo2OHTty3XXXcf3115e4fC8vLz7//HM++OAD5s6dS2JiIlWrVnUuN3O+ze3+9a9/cebMGX744Qfi4+Od8Z9+/fo509SrV49p06Yxfvx41qxZw759+6hbty6PP/44995770WPWuzUqRO///47U6ZMYdmyZSQkJJCVlUVQUBBt2rShc+fO9OzZ86LKcDWTcb4tfcQlfv/9dx577DFq1KjBypUrC02zd+9ebrzxRgBWr15NtWrVipX39OnTOXHiBNdeey1169YlMDCQ+Ph4pk6dynfffQfAvffey5NPPnnBvL777rtCpy4VVd/MzExatGjBzJkzi3WNiIiISHlbtWoVEydOZMuWLaSnpxMeHk7v3r0ZNmyYc1fKvKKjowF47bXXiI2NLTTP33//nalTpxIXF4fNZqN+/fr06dOHwYMHn/cB84kTJ5gwYQJLlizh+PHjBAcH07ZtWx588EGaN29+3vsojzLP9cEHH/Dhhx8SExPDlClTSnTthTh201Zfs/jWPBBCWk4U84Jyd4KtWTOQJ565rnwrJSIiUggFI93g559/5qmnnqJOnTosXbq00DQHDx6kR48eAPzxxx9FjqAsic8//5y3334bb29v5s2bd8E1fBwdypJQB1FEREREXE3ByJJbNyyY00YLFgTm7kBfu04Qjz/ZpZxrJSIiUpCmaV/AK6+8wuTJk0t8Xd4nxI6doWw2W5HprVar89+O9Bdr6NChTJ48mePHj7N48WIGDRp03vRhYWG0aNGiWHk7RkaKiIiIiEj5O3eatjawERGRikrByAsICAggJCSkxNcFBgY6/+1YXyAlJQXDMDCZCq7fcvr0aSB3rYS8114M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pGzY7Tn5pmgDeGnNSBERqaA0MlJERERELglTpkxh8ODBLF26FF9fXxo1akRiYiITJkzg1ltv5fTp0yXO84033uCRRx5h7dq1hISEUL9+fXbv3s2bb77JkCFDsFqthV6XkJBAnz59mDJlCidPnqRJkyYYhsHvv//ObbfdxqJFiypEmTNnzmTo0KFMnjyZbdu2ERoaSlRUFBkZGSxevJgHHniAMWPGYBhGidtOXMcoJBhp1m7aIiJSQekTSkREREQqva1bt/Lqq68CMHbsWJYuXcpPP/3EwoULadGiBXv37uWFF14oUZ4LFixg4sSJ+Pj48PHHH7NgwQJmzZrF7NmzqVu3LuvWrWPcuHEFrjMMg0cffZQTJ05w7bXXsmzZMmbOnMmyZcsYPnw4NpuNJ554guPHj1eIMqOjo3n55ZdZu3Yt8+bNY+bMmaxZs4Ynn3wSk8nE9OnTmTZtWonaTlzLbs/BbjonGKmRkSIiUkEpGCkiIiIild7HH3+M3W7n3//+N7fffjsmU26gplatWowbNw6z2cz8+fPZsWNHsfP88MMPAbj//vvp3r2783ijRo14+eWXAfj22285efJkvusWLVpEXFwcQUFBvPPOOwQFBQHg7e3No48+Srt27UhPT2fixInlXmbPnj355ZdfuO2225zXOK679957ue222wCYPn16cZtNykBhIyO9NDJSREQqKH1CiYiIiEildvbsWZYvXw5A//79C5xv2LAh7du3B2Du3LnFynP//v3OwOXtt99e4HyHDh1o0KABVqu1wPTn33//HYDevXtTtWrVAtc66uhIV55lhoSEOAO3hencuTMA8fHxRaaRsmcYBTew8dIGNiIiUkEpGCkiIiIilVpcXBxWqxUfHx9atmxZaJqrrroKgM2bNxcrz7/++guAevXqUatWrRLl6fi9bdu2hV7nOH706FGOHTtWrmVeSGZmJgD+/v7FvkZcz8jJwW7KvzeppmmLiEhFpd20RURERKRSc4zaCw8PL3Kn6fr16+dLeyH79+/Pd11x87RarSQmJp732jp16mCxWLDZbOzbt88ZeCyPMi9kzpw5wD9B0PP57rvvmDFjRrHy3bt3b7HSyf8Ydux5xpmYzabzjmgVEREpTwpGioiIiEillpKSAlDo9GQHxzlHWlfmeebMGeextLQ07Hb7ea81mUwEBweTnJyc79ryKPN8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devWDB06lO3btxdZf6vVyqRJk7j11ltp06YNLVu2pHfv3rzxxhucPHmy1O0ye/Zs9u3bh8Vi4c0338Tf//xLAnl5eTn7AXl98MEH2O122rdvz+DBgy+YR0UIRmrNSA92+vRpIHc6dlGb04SEhABgt9tJS0s7b+cxKSlJuxaKiMglwW43SpbeMDCbzVQLDeCXX3Zz16Arsdmy8bNnYrnAzsNSMbRs2ZKnn36a119/nTFjxjBhwgSqVavGnj17sFqtREZG8t///rfAdYmJiQDO9Zfy6tWrF/fccw9ff/01Dz30EPXr1ycgIIDdu3eTk5PDVVddxejRowtcZzabGT9+PAMHDmTZsmV07tyZyMhIjh49SnJyMhaLhbfeeotatWqVe5mTJ092Tv0GCv0S5PD+++8TFhZW5HkpO4Zhz7+btkZGikgFERcXx/Dhw8nOzqZRo0Z4e3uzb98+3nrrLQ4fPsyYMWPypf/555957rnnyM7OJigoiOjoaI4fP87y5ctZvnw5AwcOLHBNXm+99RZffPEFdevWpV69euzbt485c+awadMmfvzxR+bMmcMrr7xCjRo1qFu3Lvv27WPlypX89ddf/PDDD1x22WX58jtz5gz33nuvc5mXyMhI/P392b17NxMnTmT27Nl8+eWXREdHl7ht5syZA+Q++KxTp06Jrwc4ceKEc3bH3XffXao8yoOCkR4s63/TxM63g2He4b1ZF5hWFhYWdsGdmRz27t1LZqZGhIiIiGeqWTMQs9lUrKCkn583gYG+GBj4WMzs3pVEVqaVgwdPExERTBAGXhYLJi91qyq6wYMHEx0dzcSJE9myZQvJycmEh4fTu3dvhg0bVqo1EJ999llat27N1KlTiYuL4/jx4zRq1Ig+ffowePDgIvtpkZGRzJo1iwkTJrBkyRJ27dpFcHAwvXr14sEHH6R58+YVokyr9Z91Vfft23fetrhQX1PK0DkjI81aM1JEKoi3336bvn378swzzzg/Z3/77Tcef/xxpk6dyt133+3cOG337t08//zzZGdnM2TIEB5//HFnTMMRpPz2229p0aIFt9xyS4Gyjh07xnfffcfnn39O586dgdyHivfccw8HDx7k2WefZdWqVbz++uv07dsXgFOnTnHvvfeybds2PvroI9555518eY4dO5YtW7ZQp04dPvroI2fMJCkpiVGjRrFu3Toe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qyFyQeprioiIeKbi9De93VQXqWSysrIAyMzMZNu2beVcGxERERGpTNTXFBERqbwUjJRSqV69OidPnsTX15e6deuWd3U8wt69e8nMzNQT/lJQ25We2q701Halp7YrvbJuu8jISJfnKVIWyrKvqfeosqc2dg+1s3uond1D7ewe7mjn4vQ3NU1bxE1iY2PZtm0bLVq0YObMmeVdHY+itis9tV3pqe1KT21Xemo7kbKn11nZUxu7h9rZPdTO7qF2do+K0s7awEZERERERERERETcQsFIERERERERERERcQsFI0VERERERERERMQtFIwUERERERERERERt1AwUkRERERERERERNxCwUgRERERERERERFxCwUjRURERERERERExC0UjBQRERERERERERG3UDBSRERERERERERE3ELBSBEREREREREREXEL7/KugMilon///iQlJREWFlbeVfE4arvSU9uVntqu9NR2pae2Eyl7ep2VPbWxe6id3UPt7B5qZ/eoKO1sMgzDKNcaiIiIiIiIiIiIyCVB07RFRERERERERETELRSMFBEREREREREREbdQMFJERERERERERETcQsFIERERERERERERcQvtpi3iYmfOnGHFihX8/fffbN26la1bt5Kenk5ERASLFy++6Py3b9/OZ599xrp16zhz5gw1a9aka9euDB8+nOrVq7vgDspfWdzjqVOn+Oqrr1iyZAmHDh3CZrNRvXp1Wrduzd13303btm1dfBfloyz/Pv744w++//57/vrrL06fPk3VqlWpV68eV199NQ8//DDe3p79keKO19Yff/zBsGHDAFz2nlARuLLtDMNg06ZNLF68mA0bNrBv3z7S0tIICgqiefPm9O3bl3/961+YTKYyuhvXWb16NV999RWbN28mPT2d8PBwevfuzbBhwwgICChVnvPmzeObb75hx44d2Gw2GjRoQJ8+fRg0aBAWi8XFdyBSuZTFa1JyVab3bk9TWfsWFUll7wOXt0vle1pZSkpKYuXKlWzdupW///6buLg4srKyiImJYcqUKee91maz8fXXXzNr1iwOHDiAxWKhadOm3H333Vx//fVlVmftpi3iYgsXLmTEiBEFjruiczB//nwef/xxbDYboaGh1K5dm/j4eNLT0wkLC2PatGnUq1fvosoob2Vxj/v37+euu+4iKSkJs9lMREQEgYGBHDhwgLNnz2IymXj66acZPHhw2dyUm5TV30d2djbPPPMMs2bNAqBOnTrUqFGD06dPc/ToUWw2Gxs3bqRKlSquviW3ccdr6+zZs9x8880cPnwYqDxfGFzddqtWrcr3WqxXrx7BwcEkJiZy+vRpAK677jo++OADfHx8XHw3rjNlyhReeeUVDMOgdu3aVK9enT179mC1WmnUqBFTp04lJCSkRHm+8cYbTJw4EYD69evj7+/Pnj17yMnJoV27dkycOLFCt4lIeSqL16T8o7K8d3uaytq3qCguhT5webtUvqeVtUmTJvHaa68VOH6hYGRWVhZDhgxhw4YNeHl50bhxYzIyMjhw4AAA999/P0888UTZVNoQEZdatmyZMXDgQOP11183fv31V+Obb74xoqKijK5du15UvkePHjVatWplREVFGe+9955hs9kMwzCMM2fOGPfee68RFRVlxMbGGna73RW3US7K6h4HDRpkREVFGddff72xe/du5/HMzEzj9ddfN6KioozmzZsb8fHxrrwdtyrLv4/nnnvOiIqKMm655RZj27Zt+c6lp6cbCxcuNKxWq0vuozy467X13//+14iKijIeeughl7wnVARl0XYrV640unXrZnz99dfGiRMn8p376aefjMsvv9yIiooy3nzzTZfeiyv9/fffRtOmTY3o6Gjju+++c97/0aNHjX79+hlRUVHGyJEjS5Tn/PnzjaioKOPyyy83Fi5c6Dy+Z88eo1u3bkZUVJTx2muvufQ+RCqLsnhNSn6V4b3bE1XGvkVFUtn7wBXBpfA9zR2+//57Y/DgwcY777xjzJ8/33jvvfeMqKgo46677jrvdY73kG7duhl79+51Hl+4cKHzfXvRokVlUmcFI0XK2OLFi13SOXj55ZeNqKgoY+DAgQXOnT592rjqqqvK9M3CHcriHlNTU43o6GgjKirKWLBgQYHzdrvd6NmzpxEVFWVMmTLloupfnsrq72PVqlXOv9/U1FRXVbdCccdra9OmTUbTpk2Nhx56yPjxxx8rzReGsnrNnq9jP2HCBCMqKsqIiYkxcnJySlXvsub4Uvjkk08WOBcfH280bdrUiIqKMuLi4oqdZ58+fYyoqChj/PjxBc79+eefzkBlcnLyRdVdpDIqi9ek5FcZ3rs9TWXtW1QUl0IfuLxdKt/TysOUKVMuGIxMSkoyWrRoYURFRRmr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SE9P58iRIyxdupSxY8fSq1cvli9f7pK8RaT4NDJSRKQSevbZZ8nOzmbSpEl06NAh37mDBw9it9vLqWYiIiIi4mnS0tJYvXo1wEXPrvH29iY7Oxur1crs2bO56667zps+JSWFhQsX5ru2uD766KN8v6elpbFjxw5+/vlnTp06xYkTJxg+fDhTpkzhyiuvLPG9iEjpKBgpIlLJHD16lP3799OyZcsCgUiAevXqlUOtRERERMRTLVu2DJvNBkCPHj0uKq/Q0FBq1KjBtm3bmDlz5gWDkb/++itZWVkAdOnSxTlCsziKqusDDzzAfffdx9atW7Farbz22mtMnz69+DchIhdF07RFRCqZwMBAvLy82Lp1K59++mmxp7+IiIiIiBTGEQAMDw+nefPmF53fLbfcAsC2bdvYsWPHedM6pmhfccUVNGnS5KLLBqhWrRpvvPGG8/e//vqLI0eOuCRvEbkwBSNFRCqZwMBAnnrqKby8vBg3bhwdO3Zk0KBBfP3115w6deqC11utVjfUUkRERERKYtiwYURHR9OsWTPOnj1LUlIS7733HgMGDKBDhw5cfvnldOjQgREjRrBx40aXlWuz2Vi2bBkA3bp1c0meN998M76+vgD88MMPRabbsWMH27ZtA/4JYLpK48aNadCggfP3nTt35juflZXFwoULefnllxkwYADt27enRYsWtG7dmuuvv57//Oc/rFy50qV1ErlUKBgpIlIJ3XPPPSxYsICnn36a9u3bs3HjRl599VV69OjB+vXrnensdjutW7dm0qRJvPzyy7Rv356+ffsCuR2w999/n549e9KyZUtiY2PzXQu5U3buuOMO2rZtS5s2bYiNjVWnTERERKQMbN++HYCGDRuydOlSrr/+eiZMmMCmTZs4efIkNpuNkydPsnDhQgYOHMj333/vknLXrVvHmTNngIufou1QtWpVZ16zZ88u8mG4I1Dp6+vLTTfd5JKy86pevbrz36mpqfnO3XjjjYwYMYIpU6awadMmTp06RXZ2Nunp6SQkJDBr1iyGDh3K8OHDOXv2rMvrJlKZac1IEZFKqk6dOgwZMoQhQ4Zw8uRJXnzxRebNm8cnn3zCF198AeRuZpOens4XX3xBz549eeedd7BYLGRnZzNs2DD27t3LyJEjqVevHt9//z3Dhg1j/vz51KhRgzVr1jBixAgeeOABHnnkETIz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hg1Fpk1PT+f555/HMAzMZjOPPvpoqct1TNFu2LAhjRs3LnU+JTFkyBBatWpFq1atuP7662nZsqVbyj1XQECA8995274wH3zwQVlXR6RSUjBSREREREREpAKKi4vDMAzn77Nnz2bLli0F0iUnJzNixAjnhi2jRo0qsElhcWVlZbFixQqAMt245lz9+vVjxowZzJgxo1yDfIGBgTRs2BDIXa9z7ty5BdLk5OTw6quvsmzZMjfXTqRy0JqRIiIiIiIiIhVQ3s1revXqxbx58xg0aBC33XYbrVq1wmq1snXrVn799VdSUlIAuPnmmxk2bFipy/zzzz+du3G7Y4p2RXTPPffw0ksvAfDYY49x44030q5dO6pWrUpCQgKzZ89m7969REVFYbFY8v1/EpELUzBSREREREREpALKu3nNCy+8gN1uZ8GCBUyePLlAWi8vL4YMGcLo0aMvqkzHFO3Q0FDatGlzUXl5qjvuuIOtW7fy448/YhgGc+bMYc6cOfnSREVF8fHHH/Pss8+WUy1FPJeCkSIiHuTchb09nWMqkYiIiIgU5AhG1qxZk7CwMN5//32+//57Zs6cSXx8PFlZWdSuXZuOHTtyxx13EBUVdVHl2e12lixZAsB1112H2XxpruxmMpl49dVXue6665g+fTpbt27l7NmzhISEEBkZSe/evbn11lvLZbdvkcrAZORdgEJEREREREREyl1mZiZt2rQhJyeH6667jk8//bTMy9y4cSN33HEHAB9//PElO01bRMrWpfmYQ0RERERERKQC27FjBzk5OQA0a9bMLWU6pmj7+/vTqVMnt5QpIpceTdMWEfFQH3/8MePHj893zMfHh/DwcNq1a8fw4cMJDw8vp9qJiIiIyMXIuylK8+bN3VKmIxjZqVMn/Pz83FKmiFx6FIwUEfFQcXFxALRt25Z69eoBcPr0adavX8/333/P3Llz+eGHH2jYsGE51lJERERESiPv5jXuGhk5d+5ct5QjIpc2BSNFRDyUIxj52GOP0a5dO+fx48ePExsbS1JSEl988QUvv/xyeVVRRERERErJEYwMDg52PngWEakMtGakiIgHSktL49ChQwA0btw437maNWty6623Av8ELEVERETEc9hsNnbv3g1A06ZNy7k2IiKupZGRIiIeKC4uDsMwCA0NpVq1agXOh4SEALlP0l0lJSWFmJgY6tWrx2+//cakSZP45ZdfOHjwIDVq1OCOO+7g/vvvB2Dnzp189tlnrF69moyMDFq2bMnzzz9fIHAqIiIiIgVZLBa2bt1a3tUQESkTCkaKiHggx7SdooJ7+/btA6Bly5YuK9MxyrJOnToMGDCAY8eOcdVVVxEaGsqaNWt4++23CQkJwWKx8MILL9C8eXNiYmLYtGkTq1at4oEHHuC3337D19fXZXUSERERERERz6JgpIiIB9qxYwcATZo0KXDu0KFDzJo1i5CQEIYMGeKyMh3ByLVr13Lbbbcxbdo0Z2Dxk08+4d1332XChAmcPXuWzz77jA4dOgCQnp7Ov/71Lw4dOsTff/9N27ZtXVYnERERERER8SxaM1JExAM5RkY2atTIeSw9PZ2lS5dy7733Eh4eztSpU53TtYsyZcoUnnvuORYtWnTBMvPu3v3SSy/lG+HYtWtXABITE/m///s/ZyASICAggKuvvhrI3VxHRERERERELl0aGSki4mGsVit79+4F4KWXXuKll17Kd75169ZMmjQJPz+/C+a1YsUKli5dSu3atenevft50zqCkcOHD8fLyyvfubS0NAAuu+wybrzxxgLXpqamAhAaGnrBOomIiIiIiEjlpZGRIiIeZs+ePdhsNiwWC/369aNfv37cdNNNREVFAbBp0ybefvttl5aZlZXFvn37CAoKon379gXOO6aN9+jRo9Drd+3aBeCso4iIiIiIiFyaNDJSRMTDOEYoNmnShNdffz3fuTfffJMvv/ySH374gVGjRlGlSpXz5vXpp58Wq8xdu3aRnZ1N8+bNC4yKBNi2bRtQ+IY5aWlpJCQkUKdOnUJ3/hYREREREZFLh0ZGioh4GEcw8vLLLy9w7uGHHyYoKIiMjAyWLl3q8jJbtGhR6HnHGpbNmzcvcG7Hjh0YhkGzZs1cVh8RERERERHxTApGioh4mPMFI/39/bnhhhsAirUpTXGdL9hotVrZs2cPISEhREREFFnfwq4VERERERGRS4uCkSIiHsQwDOf6jIUFI+GfdRuXL19Odna2S8p1lFlYQHHXrl3YbLZSjZoUERERERGRS4uCkSIiHuTgwYOkpaXh4+NT5GYwHTp0oEqVKpw5c4Z169ZddJl2u52dO3cSEBBAZGRkgfOOYGNRwUiNjBQREREREREHBSNFRDyII/AXHR2NxWIpNI2Pjw9dunQBXDNVOz4+nvT0dJo2bYrZXPBjw7F5zYWmcNepU+ei6yIiIiIiIiKezWQYhlHelRAREREREREREZHKTyMjRURERERERERExC0UjBQRERERERERERG3UDBSRERERERERERE3ELBSBEREREREREREXELBSNFRERERERERETELRSMFBEREREREREREbdQMFJERERERERERETcQsFIERERERERERERcQsFI0VERERERERERMQtFIwUERERERERERERt1AwUkRERERERERERNzi/wE5F7P8SiKuBQAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -613,7 +613,7 @@ ], "metadata": { "kernelspec": { - "display_name": "feos", + "display_name": "feos_devel", "language": "python", "name": "python3" }, @@ -627,7 +627,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.14.0" } }, "nbformat": 4, diff --git a/docs/tutorials/utility/core_dual_numbers.ipynb b/docs/tutorials/utility/core_dual_numbers.ipynb index 7462308f9..5999e7183 100644 --- a/docs/tutorials/utility/core_dual_numbers.ipynb +++ b/docs/tutorials/utility/core_dual_numbers.ipynb @@ -196,7 +196,7 @@ "metadata": {}, "outputs": [], "source": [ - "state = feos.State(eos, temperature=300*si.KELVIN, volume=40744*si.ANGSTROM**3, total_moles=1/si.NAV)" + "state = feos.State(eos, temperature=300*si.KELVIN, volume=40744*si.ANGSTROM**3, composition=1/si.NAV)" ] }, { @@ -247,14 +247,14 @@ "name": "stderr", "output_type": "stream", "text": [ - "/tmp/ipykernel_216511/809879024.py:1: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", + "/tmp/ipykernel_2182195/809879024.py:1: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", " state.helmholtz_energy() / (si.KB * 300*si.KELVIN)\n" ] }, { "data": { "text/plain": [ - "-5.554978406564659" + "-5.554978406564658" ] }, "execution_count": 4, @@ -295,15 +295,15 @@ "data type : \n", "temperature: 300 + 0ε\n", "molefracs : [1 + 0ε]\n", - "density : 0.000024543491066169247 + -602382953715129600000ε\n", - "A/kT : 0.00012419216233064458 + -3038285859542560000000ε\n" + "density : 0.000024543491066169247 + -0.0010002804280435477ε\n", + "A/kT : 0.00012419216233064458 + -0.005045192367012291ε\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "/tmp/ipykernel_216511/966462947.py:64: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", + "/tmp/ipykernel_2182195/966462947.py:64: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", " ak = ((1.0 - np.sqrt(tr)) * self.kappa + 1.0)**2 * self.a_r\n" ] }, @@ -313,7 +313,7 @@ "$100\\,\\mathrm{kPa}$" ], "text/plain": [ - "100.00005278190892 kPa" + "100.00005278190896 kPa" ] }, "execution_count": 5, @@ -354,7 +354,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/tmp/ipykernel_216511/966462947.py:64: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", + "/tmp/ipykernel_2182195/966462947.py:64: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", " ak = ((1.0 - np.sqrt(tr)) * self.kappa + 1.0)**2 * self.a_r\n" ] }, @@ -407,14 +407,14 @@ "data type : \n", "temperature: 300 + 1ε1 + 0ε2 + 0ε1ε2\n", "molefracs : [1 + 0ε1 + 0ε2 + 0ε1ε2]\n", - "density : 0.000024543491066169247 + 0ε1 + -602382953715129600000ε2 + 0ε1ε2\n", - "A/kT : 0.00012419216233064458 + -0.0000006261809548282462ε1 + -3038285859542560000000ε2 + 15312125055595608000ε1ε2\n", + "density : 0.000024543491066169247 + 0ε1 + -0.0010002804280435475ε2 + 0ε1ε2\n", + "A/kT : 0.00012419216233064458 + -0.0000006261809548282462ε1 + -0.00504519236701229ε2 + 0.000025426381856268008ε1ε2\n", "\n", "data type : \n", "temperature: 300 + 0ε1 + 0ε1²\n", - "molefracs : [1 + 0ε1 + 0ε1²]\n", - "density : 0.000024543491066169247 + -602382953715129600000ε1 + 29569161285839853000000000000000000000000000000ε1²\n", - "A/kT : 0.00012419216233064458 + -3038285859542560000000ε1 + 148659416520544790000000000000000000000000000000ε1²\n", + "molefracs : [1 + 0ε1 + 0.0000000000009094947017729282ε1²]\n", + "density : 0.000024543491066169247 + -0.0010002804280435475ε1 + 0.0815337094490324ε1²\n", + "A/kT : 0.00012419216233064458 + -0.00504519236701229ε1 + 0.4099119875697386ε1²\n", "\n", "data type : \n", "temperature: 300 + 1ε1 + 0ε1²\n", @@ -427,7 +427,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/tmp/ipykernel_216511/966462947.py:64: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", + "/tmp/ipykernel_2182195/966462947.py:64: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", " ak = ((1.0 - np.sqrt(tr)) * self.kappa + 1.0)**2 * self.a_r\n" ] }, @@ -437,7 +437,7 @@ "$-1.4939\\times10^{-4}\\,\\mathrm{\\frac{ms^{2}K}{kg}}$" ], "text/plain": [ - "-1.4938695195180373e-4 m kg^-1 s^2 K" + "-1.4938695195182094e-4 m kg^-1 s^2 K" ] }, "execution_count": 7, @@ -463,7 +463,7 @@ ], "metadata": { "kernelspec": { - "display_name": "feos", + "display_name": "feos_devel", "language": "python", "name": "python3" }, @@ -477,7 +477,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.14.0" } }, "nbformat": 4, diff --git a/py-feos/src/ad/mod.rs b/py-feos/src/ad/mod.rs index 12a6c6345..c18525a68 100644 --- a/py-feos/src/ad/mod.rs +++ b/py-feos/src/ad/mod.rs @@ -44,6 +44,7 @@ type GradResult<'py> = ( ); #[pyclass(name = "Property")] +/// Blibalblub pub struct PyPropertyAD; #[pymethods] diff --git a/py-feos/src/state.rs b/py-feos/src/state.rs index bb94686e3..c562556bb 100644 --- a/py-feos/src/state.rs +++ b/py-feos/src/state.rs @@ -529,7 +529,7 @@ impl PyState { /// ------- /// SIArray1 #[pyo3(signature = (contributions=PyContributions::Total), text_signature = "($self, contributions)")] - fn dp_dni(&self, contributions: PyContributions) -> Pressure> { + fn n_dp_dni(&self, contributions: PyContributions) -> Pressure> { self.0.n_dp_dni(contributions.into()) } diff --git a/py-feos/src/user_defined.rs b/py-feos/src/user_defined.rs index 6da88fd30..12792059d 100644 --- a/py-feos/src/user_defined.rs +++ b/py-feos/src/user_defined.rs @@ -263,6 +263,7 @@ impl_dual_state_helmholtz_energy!( ); impl_dual_state_helmholtz_energy!(PyStateHD, PyHyperDual64, HyperDual64, f64); impl_dual_state_helmholtz_energy!(PyStateD2, PyDual2_64, Dual2_64, f64); +impl_dual_state_helmholtz_energy!(PyStateD2Vec2, PyDual2SVec64_2, Dual2SVec64<2>, f64); impl_dual_state_helmholtz_energy!(PyStateD3, PyDual3_64, Dual3_64, f64); impl_dual_state_helmholtz_energy!(PyStateHDD, PyHyperDualDual64, HyperDual, PyDual64); dual_number!(PyDualVec2, DualSVec64<2>, f64); @@ -334,6 +335,7 @@ impl_residual!( Dual, f64>; PyStateHD, PyHyperDual64, HyperDual64; PyStateD2, PyDual2_64, Dual2_64; + PyStateD2Vec2, PyDual2SVec64_2, Dual2SVec64<2>; PyStateD3, PyDual3_64, Dual3_64; PyStateHDD, PyHyperDualDual64, HyperDual; PyStateHDDVec2, From 13c531bcd1a6b317e13863b4b050bb06ec3515f8 Mon Sep 17 00:00:00 2001 From: Philipp Rehner <69816385+prehner@users.noreply.github.com> Date: Tue, 14 Jul 2026 16:43:52 +0200 Subject: [PATCH 11/12] Implement azeotrope detection (#365) --- .../phase_equilibria/phase_diagram_binary.rs | 146 ++++++++++++++++++ py-feos/src/phase_equilibria.rs | 34 ++++ 2 files changed, 180 insertions(+) diff --git a/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs b/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs index 48a7028d4..03ac6e1cb 100644 --- a/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs +++ b/crates/feos-core/src/phase_equilibria/phase_diagram_binary.rs @@ -6,6 +6,7 @@ use crate::{ReferenceSystem, Residual, SolverOptions, Subset}; use nalgebra::{DVector, dvector, matrix, stack, vector}; use ndarray::{Array1, s}; use num_dual::linalg::LU; +use num_dual::{Dual64, DualNum, first_derivative, partial, partial2}; use quantity::{Density, Moles, Pressure, RGAS, Temperature}; const DEFAULT_POINTS: usize = 51; @@ -772,3 +773,148 @@ impl PhaseEquilibrium { ))) } } + +/// # Azeotrope detection +impl PhaseEquilibrium { + /// Calculate the azeotropic state in a binary system. If no azeotrope is + /// expected, the function returns `None`. + pub fn binary_azeotrope( + eos: &E, + temperature_or_pressure: TP, + ) -> FeosResult> { + // determine the VLEs of both pure components + let vle = Self::vle_pure_comps(eos, temperature_or_pressure); + + // check that there are exactly two components + let mut iter = vle.into_iter(); + let (Some(vle1), Some(vle2), None) = (iter.next(), iter.next(), iter.next()) else { + return Err(FeosError::IncompatibleComponents(eos.components(), 2)); + }; + + // check that both pure components are subcritical (or converge) + let (Some(vle1), Some(vle2)) = (vle1, vle2) else { + return Err(FeosError::SuperCritical); + }; + + // calculate the Henry coefficient and vapor pressures of both binary end systems + let henry1 = + State::henrys_law_constant(eos, vle1.liquid().temperature, &vle1.liquid().molefracs)? + [0]; + let psat1 = vle1.liquid().pressure(Contributions::Total); + let henry2 = + State::henrys_law_constant(eos, vle2.liquid().temperature, &vle2.liquid().molefracs)? + [0]; + let psat2 = vle2.liquid().pressure(Contributions::Total); + + // calculate the relative volatility at both ends of the phase diagram + // logarithms of alpha values are used so that the algorithm behaves exactly the same + // when the two components are flipped + let ln_alpha1 = henry2.convert_into(psat2).ln(); + let ln_alpha2 = psat1.convert_into(henry1).ln(); + + // check whether we expect an azeotrope (technically only a necessary criterion) + if ln_alpha1 * ln_alpha2 > 0.0 { + return Ok(None); + } + + // estimate the azeotropic composition from a straight line in ln(alpha(x)) + let x0 = -ln_alpha1 / (ln_alpha2 - ln_alpha1); + + // solve for the azeotropic composition and return the corresponding VLE state + let (temperature, pressure, iterate_t) = temperature_or_pressure.temperature_pressure(None); + (if iterate_t { + Self::iterate_azeotrope_t(eos, temperature.unwrap(), x0, 10, 1e-10) + } else { + let t_init = vle1.liquid().temperature.min(vle2.liquid().temperature); + Self::iterate_azeotrope_p(eos, pressure.unwrap(), x0, t_init, 10, 1e-10) + }) + .map(Some) + } + + fn iterate_azeotrope_t( + eos: &E, + temperature: Temperature, + x0: f64, + max_iter: usize, + tol: f64, + ) -> FeosResult { + let x = Self::azeotrope_newton( + partial( + |x: Dual64, &t: &Temperature<_>| { + PhaseEquilibrium::bubble_point( + &eos.lift(), + t, + &dvector![x, -x + 1.0], + None, + None, + Default::default(), + ) + .map(|vle| { + (vle.vapor().molefracs[0] + / vle.liquid().molefracs[0] + / (vle.vapor().molefracs[1] / vle.liquid().molefracs[1])) + .ln() + }) + }, + &temperature, + ), + x0, + max_iter, + tol, + )?; + PhaseEquilibrium::bubble_point(eos, temperature, x, None, None, Default::default()) + } + + fn iterate_azeotrope_p( + eos: &E, + pressure: Pressure, + x0: f64, + t_init: Temperature, + max_iter: usize, + tol: f64, + ) -> FeosResult { + let x = Self::azeotrope_newton( + partial2( + |x: Dual64, &p: &Pressure<_>, &t_init| { + PhaseEquilibrium::bubble_point( + &eos.lift(), + p, + &dvector![x, -x + 1.0], + Some(t_init), + None, + Default::default(), + ) + .map(|vle| { + (vle.vapor().molefracs[0] + / vle.liquid().molefracs[0] + / (vle.vapor().molefracs[1] / vle.liquid().molefracs[1])) + .ln() + }) + }, + &pressure, + &t_init, + ), + x0, + max_iter, + tol, + )?; + PhaseEquilibrium::bubble_point(eos, pressure, x, Some(t_init), None, Default::default()) + } + + fn azeotrope_newton FeosResult>( + f: F, + x0: f64, + max_iter: usize, + tol: f64, + ) -> FeosResult { + let mut x = x0; + for _ in 0..max_iter { + let (f, df) = first_derivative(&f, x)?; + x -= f / df; + if f.abs() < tol { + return Ok(x); + } + } + Err(FeosError::NotConverged("binary_azeotrope".into())) + } +} diff --git a/py-feos/src/phase_equilibria.rs b/py-feos/src/phase_equilibria.rs index 17b85b91d..a27ca1a82 100644 --- a/py-feos/src/phase_equilibria.rs +++ b/py-feos/src/phase_equilibria.rs @@ -572,6 +572,40 @@ impl PyPhaseEquilibrium { PhaseEquilibrium::boiling_temperature(&eos.0, pressure) } + /// Calculate the azeotropic state in a binary system. If no azeotrope is + /// expected, the function returns `None`. + /// + /// Parameters + /// ---------- + /// eos : EquationOfState + /// The equation of state. + /// temperature_or_pressure : SINumber + /// The system temperature or pressure. + /// + /// Returns + /// ------- + /// PhaseEquilibrium + #[staticmethod] + fn binary_azeotrope( + eos: &PyEquationOfState, + temperature_or_pressure: Bound<'_, PyAny>, + ) -> PyResult> { + if let Ok(t) = temperature_or_pressure.extract::() { + Ok(PhaseEquilibrium::binary_azeotrope(&eos.0, t) + .map_err(PyFeosError::from)? + .map(Self)) + } else if let Ok(p) = temperature_or_pressure.extract::() { + Ok(PhaseEquilibrium::binary_azeotrope(&eos.0, p) + .map_err(PyFeosError::from)? + .map(Self)) + } else { + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) + } + } + fn _repr_markdown_(&self) -> String { self.0._repr_markdown_() } From 2619325e577a5a116e886ab035d66bb835ad6d78 Mon Sep 17 00:00:00 2001 From: Philipp Rehner Date: Tue, 14 Jul 2026 14:19:58 +0200 Subject: [PATCH 12/12] Release v0.10.0 --- CHANGELOG.md | 8 +++++--- Cargo.toml | 10 +++++----- license-apache | 2 +- license-mit | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 954079b09..9ff3d1aea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Breaking] +## [Unreleased] + +## [0.10.0] - XXXX-XX-XX ### Added - Extended tp-flash algorithm to static numbers of components and enabled automatic differentiation for binary systems. [#336](https://github.com/feos-org/feos/pull/336) - Rewrote `PhaseEquilibrium::pure_p` to mirror `pure_t` and enabled automatic differentiation. [#337](https://github.com/feos-org/feos/pull/337) @@ -16,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `feos_core::ad::dataset` module with `PureDataset` and `BinaryDataset` types, constructible from records, CSV files, or readers, for use in parameter fits. [#358](https://github.com/feos-org/feos/pull/358) - Exposed `Property`, `PureDataset`, and `BinaryDataset` in `py-feos`. [#358](https://github.com/feos-org/feos/pull/358) - Implemented `IdealGasAD` for `Dippr`. [#357](https://github.com/feos-org/feos/pull/357) +- Added `PhaseEquilibrium::binary_azeotrope` that quickly finds azeotropes in binary mixtures for a given temperature or pressure. [#365](https://github.com/feos-org/feos/pull/365) ### Changed - Removed any assumptions about the total number of moles in a `State` or `PhaseEquilibrium`. Evaluating extensive properties now returns a `Result`. [#330](https://github.com/feos-org/feos/pull/330) @@ -34,8 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `quantity` and `num-dual` dependencies to 0.14. [#360](https://github.com/feos-org/feos/pull/360) - Updated `nalgebra` dependency to 0.35. [#360](https://github.com/feos-org/feos/pull/360) - Updated `gauss-quad` dependency to 0.3. [#360](https://github.com/feos-org/feos/pull/360) - -## [Unreleased] +- Update `itertools` dependency to 0.15. ## [0.9.6] - 2026-07-03 ### Added diff --git a/Cargo.toml b/Cargo.toml index da167aa03..32771d66a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = ["crates/*", "py-feos"] default-members = ["crates/feos"] [workspace.package] -version = "0.9.6" +version = "0.10.0" edition = "2024" authors = [ "Gernot Bauer ", @@ -45,10 +45,10 @@ paste = "1.0" rusqlite = "0.40" csv = "1.0" -feos-core = { version = "0.9", path = "crates/feos-core" } -feos-dft = { version = "0.9", path = "crates/feos-dft" } -feos-derive = { version = "0.9", path = "crates/feos-derive" } -feos = { version = "0.9", path = "crates/feos" } +feos-core = { version = "0.10", path = "crates/feos-core" } +feos-dft = { version = "0.10", path = "crates/feos-dft" } +feos-derive = { version = "0.10", path = "crates/feos-derive" } +feos = { version = "0.10", path = "crates/feos" } [profile.release-lto] inherits = "release" diff --git a/license-apache b/license-apache index afc82c3c8..7112d82b1 100644 --- a/license-apache +++ b/license-apache @@ -1,4 +1,4 @@ - Copyright (c) 2021-2025 feos-org and Contributors. https://github.com/feos-org/feos + Copyright (c) 2021-2026 feos-org and Contributors. https://github.com/feos-org/feos Apache License Version 2.0, January 2004 diff --git a/license-mit b/license-mit index ef2c6c20b..e2edf8ace 100644 --- a/license-mit +++ b/license-mit @@ -1,4 +1,4 @@ -Copyright (c) 2021-2025 feos-org and Contributors. https://github.com/feos-org/feos +Copyright (c) 2021-2026 feos-org and Contributors. https://github.com/feos-org/feos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: