From a167f8f09352216888aa6f0cee56bcc4514a2fe1 Mon Sep 17 00:00:00 2001 From: Philipp Rehner <69816385+prehner@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:46:09 +0100 Subject: [PATCH] Migrate FeOs to new quantity crate (#257) --- .github/workflows/wheels.yml | 9 +- Cargo.toml | 45 +- benches/contributions.rs | 2 +- benches/dft_pore.rs | 2 +- benches/dual_numbers.rs | 2 +- benches/dual_numbers_saftvrmie.rs | 2 +- benches/state_creation.rs | 2 +- benches/state_properties.rs | 2 +- feos-core/Cargo.toml | 30 +- feos-core/src/cubic.rs | 4 +- feos-core/src/density_iteration.rs | 3 +- feos-core/src/equation_of_state/mod.rs | 8 +- feos-core/src/equation_of_state/residual.rs | 8 +- feos-core/src/lib.rs | 90 +++- feos-core/src/parameter/identifier.rs | 4 +- feos-core/src/phase_equilibria/bubble_dew.rs | 11 +- feos-core/src/phase_equilibria/mod.rs | 6 +- .../phase_equilibria/phase_diagram_binary.rs | 4 +- .../phase_equilibria/phase_diagram_pure.rs | 4 +- .../src/phase_equilibria/phase_envelope.rs | 2 +- .../phase_equilibria/stability_analysis.rs | 4 +- feos-core/src/phase_equilibria/tp_flash.rs | 6 +- feos-core/src/phase_equilibria/vle_pure.rs | 4 +- feos-core/src/python/equation_of_state.rs | 36 +- feos-core/src/python/mod.rs | 2 +- .../src/python/parameter/fragmentation.rs | 6 +- feos-core/src/python/parameter/mod.rs | 12 +- feos-core/src/python/phase_equilibria.rs | 268 ++++----- feos-core/src/python/state.rs | 360 ++++++------- feos-core/src/python/user_defined.rs | 31 +- feos-core/src/si/array.rs | 220 -------- feos-core/src/si/fmt.rs | 199 ------- feos-core/src/si/mod.rs | 383 ------------- feos-core/src/si/ops.rs | 507 ------------------ feos-core/src/si/python.rs | 169 ------ feos-core/src/state/builder.rs | 4 +- feos-core/src/state/critical_point.rs | 4 +- feos-core/src/state/mod.rs | 7 +- feos-core/src/state/properties.rs | 6 +- feos-core/src/state/residual_properties.rs | 7 +- feos-core/src/state/statevec.rs | 2 +- feos-dft/Cargo.toml | 22 +- feos-dft/src/adsorption/external_potential.rs | 4 +- feos-dft/src/adsorption/fea_potential.rs | 3 +- feos-dft/src/adsorption/mod.rs | 6 +- feos-dft/src/adsorption/pore.rs | 10 +- feos-dft/src/adsorption/pore2d.rs | 5 +- feos-dft/src/adsorption/pore3d.rs | 5 +- feos-dft/src/convolver/periodic_convolver.rs | 2 +- feos-dft/src/functional.rs | 14 +- feos-dft/src/geometry.rs | 8 +- feos-dft/src/ideal_chain_contribution.rs | 4 +- feos-dft/src/interface/mod.rs | 4 +- .../src/interface/surface_tension_diagram.rs | 4 +- feos-dft/src/pdgt.rs | 8 +- feos-dft/src/profile/mod.rs | 6 +- feos-dft/src/profile/properties.rs | 16 +- .../python/adsorption/external_potential.rs | 20 +- feos-dft/src/python/adsorption/mod.rs | 64 +-- feos-dft/src/python/adsorption/pore.rs | 99 ++-- feos-dft/src/python/interface/mod.rs | 41 +- .../interface/surface_tension_diagram.rs | 17 +- feos-dft/src/python/profile.rs | 102 ++-- feos-dft/src/python/solvation.rs | 41 +- feos-dft/src/python/solver.rs | 18 +- feos-dft/src/solvation/pair_correlation.rs | 4 +- feos-dft/src/solvation/solvation_profile.rs | 4 +- feos-dft/src/solver.rs | 14 +- src/association/mod.rs | 3 +- src/eos.rs | 2 +- src/epcsaft/eos/mod.rs | 8 +- src/epcsaft/python.rs | 3 +- src/estimator/binary_vle.rs | 10 +- src/estimator/diffusion.rs | 10 +- src/estimator/liquid_density.rs | 6 +- src/estimator/mod.rs | 4 +- src/estimator/python.rs | 164 +++--- src/estimator/thermal_conductivity.rs | 10 +- src/estimator/vapor_pressure.rs | 4 +- src/estimator/viscosity.rs | 12 +- src/functional.rs | 2 +- src/gc_pcsaft/dft/mod.rs | 2 +- src/gc_pcsaft/eos/dispersion.rs | 3 +- src/gc_pcsaft/eos/hard_chain.rs | 3 +- src/gc_pcsaft/eos/mod.rs | 5 +- src/gc_pcsaft/eos/parameter.rs | 2 +- src/gc_pcsaft/python/mod.rs | 3 +- src/hard_sphere/dft.rs | 6 +- src/ideal_gas/dippr.rs | 12 +- src/ideal_gas/joback.rs | 8 +- src/lib.rs | 2 +- src/pcsaft/dft/mod.rs | 2 +- src/pcsaft/eos/mod.rs | 10 +- src/pcsaft/eos/polar.rs | 4 +- src/pcsaft/parameters.rs | 2 +- src/pcsaft/python.rs | 5 +- src/pets/dft/mod.rs | 2 +- src/pets/eos/mod.rs | 4 +- src/pets/python.rs | 9 +- src/python/dft.rs | 8 +- src/python/eos.rs | 8 +- src/python/mod.rs | 17 +- src/saftvrmie/eos/mod.rs | 8 +- src/saftvrmie/python.rs | 5 +- src/saftvrqmie/dft/mod.rs | 2 +- src/saftvrqmie/dft/non_additive_hs.rs | 4 +- src/saftvrqmie/eos/mod.rs | 12 +- src/saftvrqmie/parameters.rs | 3 +- src/saftvrqmie/python.rs | 57 +- src/uvtheory/eos/mod.rs | 8 +- tests/gc_pcsaft/binary.rs | 2 +- tests/gc_pcsaft/dft.rs | 2 +- tests/pcsaft/critical_point.rs | 2 +- tests/pcsaft/dft.rs | 2 +- tests/pcsaft/properties.rs | 2 +- tests/pcsaft/stability_analysis.rs | 2 +- tests/pcsaft/state_creation_mixture.rs | 2 +- tests/pcsaft/state_creation_pure.rs | 2 +- tests/pcsaft/tp_flash.rs | 2 +- tests/pcsaft/vle_pure.rs | 2 +- tests/saftvrmie/critical_properties.rs | 4 +- 121 files changed, 1086 insertions(+), 2414 deletions(-) delete mode 100644 feos-core/src/si/array.rs delete mode 100644 feos-core/src/si/fmt.rs delete mode 100644 feos-core/src/si/mod.rs delete mode 100644 feos-core/src/si/ops.rs delete mode 100644 feos-core/src/si/python.rs diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d28e90a0f..4b35fb355 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -61,22 +61,19 @@ jobs: path: dist windows: runs-on: windows-latest - strategy: - matrix: - target: [x64, x86] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: 3.9 - architecture: ${{ matrix.target }} + architecture: x64 - name: Build wheels uses: PyO3/maturin-action@v1 with: - target: ${{ matrix.target }} + target: x64 args: --release --out dist - name: Upload wheels uses: actions/upload-artifact@v4 with: - name: wheel-${{ matrix.target }} + name: wheel-windows-x86_64 path: dist diff --git a/Cargo.toml b/Cargo.toml index 2c15ff7ef..69723af1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,27 @@ [package] name = "feos" version = "0.7.0" -authors = ["Gernot Bauer ", "Philipp Rehner "] +authors = [ + "Gernot Bauer ", + "Philipp Rehner ", +] edition = "2021" readme = "README.md" license = "MIT OR Apache-2.0" description = "FeOs - A framework for equations of state and classical density functional theory." homepage = "https://github.com/feos-org" repository = "https://github.com/feos-org/feos" -keywords = ["physics", "thermodynamics", "equations_of_state", "phase_equilibria"] +keywords = [ + "physics", + "thermodynamics", + "equations_of_state", + "phase_equilibria", +] categories = ["science"] [package.metadata.docs.rs] features = ["all_models", "rayon"] -rustdoc-args = [ "--html-in-header", "./docs-header.html" ] +rustdoc-args = ["--html-in-header", "./docs-header.html"] [workspace] members = ["feos-core", "feos-dft", "feos-derive"] @@ -22,13 +30,13 @@ members = ["feos-core", "feos-dft", "feos-derive"] crate-type = ["rlib", "cdylib"] [dependencies] -quantity = { version = "0.8", optional = true } -num-dual = "0.9" +quantity = "0.9" +num-dual = "0.10" feos-core = { version = "0.7", path = "feos-core" } feos-dft = { version = "0.7", path = "feos-dft", optional = true } feos-derive = { version = "0.5", path = "feos-derive" } -numpy = { version = "0.21", optional = true } -ndarray = { version = "0.15", features = ["approx"] } +numpy = { version = "0.22", optional = true } +ndarray = { version = "0.16", features = ["approx"] } petgraph = { version = "0.6", optional = true } thiserror = "1.0" conv = "0.3" @@ -42,7 +50,7 @@ itertools = "0.13" typenum = "1.16" [dependencies.pyo3] -version = "0.21" +version = "0.22" features = ["extension-module", "abi3", "abi3-py37"] optional = true @@ -74,8 +82,25 @@ pets = [] saftvrqmie = [] saftvrmie = [] rayon = ["dep:rayon", "ndarray/rayon", "feos-core/rayon", "feos-dft?/rayon"] -python = ["pyo3", "numpy", "quantity/python", "feos-core/python", "feos-dft?/python", "rayon"] -all_models = ["dft", "estimator", "pcsaft", "epcsaft", "gc_pcsaft", "uvtheory", "pets", "saftvrqmie", "saftvrmie"] +python = [ + "pyo3", + "numpy", + "quantity/python_numpy", + "feos-core/python", + "feos-dft?/python", + "rayon", +] +all_models = [ + "dft", + "estimator", + "pcsaft", + "epcsaft", + "gc_pcsaft", + "uvtheory", + "pets", + "saftvrqmie", + "saftvrmie", +] [[bench]] name = "state_properties" diff --git a/benches/contributions.rs b/benches/contributions.rs index 1d62235f8..92f71651e 100644 --- a/benches/contributions.rs +++ b/benches/contributions.rs @@ -8,7 +8,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter}; -use feos_core::si::*; +use quantity::*; use feos_core::{DensityInitialization, Derivative, Residual, State}; use ndarray::arr1; use std::sync::Arc; diff --git a/benches/dft_pore.rs b/benches/dft_pore.rs index 5ae343a1a..719d9b7c3 100644 --- a/benches/dft_pore.rs +++ b/benches/dft_pore.rs @@ -5,11 +5,11 @@ use feos::gc_pcsaft::{GcPcSaftFunctional, GcPcSaftFunctionalParameters}; use feos::hard_sphere::{FMTFunctional, FMTVersion}; use feos::pcsaft::{PcSaftFunctional, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter, ParameterHetero}; -use feos_core::si::{ANGSTROM, KELVIN, NAV}; use feos_core::{PhaseEquilibrium, State, StateBuilder}; use feos_dft::adsorption::{ExternalPotential, Pore1D, PoreSpecification}; use feos_dft::{DFTSolver, Geometry}; use ndarray::arr1; +use quantity::{ANGSTROM, KELVIN, NAV}; use std::sync::Arc; use typenum::P3; diff --git a/benches/dual_numbers.rs b/benches/dual_numbers.rs index 03f9a92aa..2e367b3da 100644 --- a/benches/dual_numbers.rs +++ b/benches/dual_numbers.rs @@ -5,7 +5,7 @@ //! creation. use criterion::{criterion_group, criterion_main, Criterion}; use feos::pcsaft::{PcSaft, PcSaftParameters}; -use feos_core::si::*; +use quantity::*; use feos_core::{ parameter::{IdentifierOption, Parameter}, Derivative, Residual, State, StateHD, diff --git a/benches/dual_numbers_saftvrmie.rs b/benches/dual_numbers_saftvrmie.rs index ca6a85cd5..5b6306713 100644 --- a/benches/dual_numbers_saftvrmie.rs +++ b/benches/dual_numbers_saftvrmie.rs @@ -6,10 +6,10 @@ use criterion::{criterion_group, criterion_main, Criterion}; use feos::hard_sphere::HardSphereProperties; use feos::saftvrmie::{test_utils::test_parameters, SaftVRMie, SaftVRMieParameters}; -use feos_core::si::*; use feos_core::{Derivative, Residual, State, StateHD}; use ndarray::{Array, ScalarOperand}; use num_dual::DualNum; +use quantity::*; use std::sync::Arc; /// Helper function to create a state for given parameters. diff --git a/benches/state_creation.rs b/benches/state_creation.rs index f4d190c16..7ad01e5e1 100644 --- a/benches/state_creation.rs +++ b/benches/state_creation.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] use criterion::{criterion_group, criterion_main, Criterion}; use feos::pcsaft::{PcSaft, PcSaftParameters}; -use feos_core::si::*; +use quantity::*; use feos_core::{ parameter::{IdentifierOption, Parameter}, Contributions, DensityInitialization, PhaseEquilibrium, Residual, State, TemperatureOrPressure, diff --git a/benches/state_properties.rs b/benches/state_properties.rs index 0e352e7cf..1c8c44e7f 100644 --- a/benches/state_properties.rs +++ b/benches/state_properties.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] use criterion::{criterion_group, criterion_main, Criterion}; use feos::pcsaft::{PcSaft, PcSaftParameters}; -use feos_core::si::*; +use quantity::*; use feos_core::{ parameter::{IdentifierOption, Parameter}, Contributions, Residual, State, diff --git a/feos-core/Cargo.toml b/feos-core/Cargo.toml index 672abfce7..709cbc3cf 100644 --- a/feos-core/Cargo.toml +++ b/feos-core/Cargo.toml @@ -1,8 +1,10 @@ [package] name = "feos-core" version = "0.7.0" -authors = ["Gernot Bauer ", - "Philipp Rehner ", + "Philipp Rehner Vec> { diff --git a/feos-core/src/density_iteration.rs b/feos-core/src/density_iteration.rs index f8c81cafd..59b2d76bf 100644 --- a/feos-core/src/density_iteration.rs +++ b/feos-core/src/density_iteration.rs @@ -1,8 +1,9 @@ use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::{Density, Moles, Pressure, Temperature}; use crate::state::State; +use crate::ReferenceSystem; use ndarray::Array1; +use quantity::{Density, Moles, Pressure, Temperature}; use std::sync::Arc; pub fn density_iteration( diff --git a/feos-core/src/equation_of_state/mod.rs b/feos-core/src/equation_of_state/mod.rs index 139031e67..745bf6ccd 100644 --- a/feos-core/src/equation_of_state/mod.rs +++ b/feos-core/src/equation_of_state/mod.rs @@ -1,8 +1,8 @@ -use crate::{ - si::{Diffusivity, MolarWeight, Moles, Temperature, ThermalConductivity, Viscosity, Volume}, - EosResult, -}; +use crate::EosResult; use ndarray::{Array1, ScalarOperand}; +use quantity::{ + Diffusivity, MolarWeight, Moles, Temperature, ThermalConductivity, Viscosity, Volume, +}; use std::sync::Arc; mod ideal_gas; diff --git a/feos-core/src/equation_of_state/residual.rs b/feos-core/src/equation_of_state/residual.rs index 7c2c99141..a1f8d90fb 100644 --- a/feos-core/src/equation_of_state/residual.rs +++ b/feos-core/src/equation_of_state/residual.rs @@ -1,12 +1,12 @@ use super::Components; -use crate::si::*; -use crate::StateHD; -use crate::{EosError, EosResult}; +use crate::{EosError, EosResult, ReferenceSystem, StateHD}; use ndarray::prelude::*; use ndarray::ScalarOperand; use num_dual::*; use num_traits::{One, Zero}; +use quantity::*; use std::ops::Div; +use typenum::Quot; /// A reisdual Helmholtz energy model. pub trait Residual: Components + Send + Sync { @@ -75,7 +75,7 @@ pub trait Residual: Components + Send + Sync { &self, temperature: Temperature, moles: Option<&Moles>>, - ) -> EosResult<>::Output> { + ) -> EosResult> { let mr = self.validate_moles(moles)?; let x = (&mr / mr.sum()).into_value(); let mut rho = HyperDual64::zero(); diff --git a/feos-core/src/lib.rs b/feos-core/src/lib.rs index 30ee2e7ba..9467ff7bc 100644 --- a/feos-core/src/lib.rs +++ b/feos-core/src/lib.rs @@ -1,6 +1,9 @@ #![warn(clippy::all)] #![allow(clippy::reversed_empty_ranges)] #![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] @@ -28,7 +31,6 @@ mod equation_of_state; mod errors; pub mod parameter; mod phase_equilibria; -pub mod si; mod state; pub use equation_of_state::{ Components, EntropyScaling, EquationOfState, IdealGas, NoResidual, Residual, @@ -46,7 +48,7 @@ pub mod python; /// Level of detail in the iteration output. #[derive(Copy, Clone, PartialOrd, PartialEq, Eq)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum Verbosity { /// Do not print output. None, @@ -115,18 +117,100 @@ impl SolverOptions { } } +/// Reference values used for reduced properties in feos +const REFERENCE_VALUES: [f64; 7] = [ + 1e-12, // 1 ps + 1e-10, // 1 Å + 1.380649e-27, // Fixed through k_B + 1.0, // 1 A + 1.0, // 1 K + 1.0 / 6.02214076e23, // 1/N_AV + 1.0, // 1 Cd +]; + +pub trait ReferenceSystem { + fn from_reduced(value: Inner) -> Self + where + Inner: Mul; + fn to_reduced(&self) -> Inner + where + for<'a> &'a Inner: Div; + fn into_reduced(self) -> Inner + where + Inner: Div; +} + +/// Conversion to and from reduced units +impl< + Inner, + T: Integer, + L: Integer, + M: Integer, + I: Integer, + THETA: Integer, + N: Integer, + J: Integer, + > ReferenceSystem for Quantity> +{ + fn from_reduced(value: Inner) -> Self + where + Inner: Mul, + { + Self::new( + value + * REFERENCE_VALUES[0].powi(T::I32) + * REFERENCE_VALUES[1].powi(L::I32) + * REFERENCE_VALUES[2].powi(M::I32) + * REFERENCE_VALUES[3].powi(I::I32) + * REFERENCE_VALUES[4].powi(THETA::I32) + * REFERENCE_VALUES[5].powi(N::I32) + * REFERENCE_VALUES[6].powi(J::I32), + ) + } + + fn to_reduced(&self) -> Inner + where + for<'a> &'a Inner: Div, + { + self.convert_to(Quantity::new( + REFERENCE_VALUES[0].powi(T::I32) + * REFERENCE_VALUES[1].powi(L::I32) + * REFERENCE_VALUES[2].powi(M::I32) + * REFERENCE_VALUES[3].powi(I::I32) + * REFERENCE_VALUES[4].powi(THETA::I32) + * REFERENCE_VALUES[5].powi(N::I32) + * REFERENCE_VALUES[6].powi(J::I32), + )) + } + + fn into_reduced(self) -> Inner + where + Inner: Div, + { + self.convert_into(Quantity::new( + REFERENCE_VALUES[0].powi(T::I32) + * REFERENCE_VALUES[1].powi(L::I32) + * REFERENCE_VALUES[2].powi(M::I32) + * REFERENCE_VALUES[3].powi(I::I32) + * REFERENCE_VALUES[4].powi(THETA::I32) + * REFERENCE_VALUES[5].powi(N::I32) + * REFERENCE_VALUES[6].powi(J::I32), + )) + } +} + #[cfg(test)] mod tests { use crate::cubic::*; use crate::equation_of_state::{Components, EquationOfState, IdealGas}; use crate::parameter::*; - use crate::si::{BAR, KELVIN, MOL, RGAS}; use crate::Contributions; use crate::EosResult; use crate::StateBuilder; use approx::*; use ndarray::Array1; use num_dual::DualNum; + use quantity::{BAR, KELVIN, MOL, RGAS}; use std::sync::Arc; // Only to be able to instantiate an `EquationOfState` diff --git a/feos-core/src/parameter/identifier.rs b/feos-core/src/parameter/identifier.rs index e15e008d8..fbb56c500 100644 --- a/feos-core/src/parameter/identifier.rs +++ b/feos-core/src/parameter/identifier.rs @@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; /// Possible variants to identify a substance. -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum IdentifierOption { Cas, Name, diff --git a/feos-core/src/phase_equilibria/bubble_dew.rs b/feos-core/src/phase_equilibria/bubble_dew.rs index 610afcd1b..560a1021f 100644 --- a/feos-core/src/phase_equilibria/bubble_dew.rs +++ b/feos-core/src/phase_equilibria/bubble_dew.rs @@ -1,16 +1,15 @@ use super::PhaseEquilibrium; use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::{Density, Dimensionless, Moles, Pressure, Quantity, SIUnit, Temperature, RGAS}; -use crate::state::TPSpec; use crate::state::{ Contributions, DensityInitialization::{InitialDensity, Liquid, Vapor}, - State, StateBuilder, + State, StateBuilder, TPSpec, }; -use crate::{SolverOptions, Verbosity}; +use crate::{ReferenceSystem, SolverOptions, Verbosity}; use ndarray::*; use num_dual::linalg::{norm, LU}; +use quantity::{Density, Dimensionless, Moles, Pressure, Quantity, SIUnit, Temperature, RGAS}; use std::fmt; use std::sync::Arc; use typenum::{N1, N2, P1, Z0}; @@ -323,7 +322,7 @@ impl TemperatureOrPressure for Quantity> // Derivative w.r.t. temperature let ln_phi_1_dt = state1.dln_phi_dt(); let ln_phi_2_dt = state2.dln_phi_dt(); - let df = ((ln_phi_1_dt - ln_phi_2_dt) * Dimensionless::from(&state1.molefracs * &k)).sum(); + let df = ((ln_phi_1_dt - ln_phi_2_dt) * Dimensionless::new(&state1.molefracs * &k)).sum(); let mut tstep = -f / df; // catch too big t-steps @@ -550,7 +549,7 @@ impl PhaseEquilibrium { let p_l = liquid.pressure(Contributions::Total); let mu_l = liquid.residual_chemical_potential(); let p_i = (liquid_molefracs * temperature * density * RGAS) - * Dimensionless::from( + * Dimensionless::new( ((mu_l - p_l * v_l) / (RGAS * temperature)) .into_value() .mapv(f64::exp), diff --git a/feos-core/src/phase_equilibria/mod.rs b/feos-core/src/phase_equilibria/mod.rs index ba72fb6fc..4d2ceb193 100644 --- a/feos-core/src/phase_equilibria/mod.rs +++ b/feos-core/src/phase_equilibria/mod.rs @@ -1,9 +1,9 @@ use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::{Dimensionless, Energy, Moles, Pressure, Temperature, RGAS}; use crate::state::{DensityInitialization, State}; -use crate::Contributions; +use crate::{Contributions, ReferenceSystem}; use ndarray::Array1; +use quantity::{Dimensionless, Energy, Moles, Pressure, Temperature, RGAS}; use std::fmt; use std::fmt::Write; use std::sync::Arc; @@ -193,7 +193,7 @@ impl PhaseEquilibrium { let ln_rho = s.partial_density.to_reduced().mapv(f64::ln); acc + s.residual_helmholtz_energy() + s.pressure(Contributions::Total) * s.volume - + RGAS * s.temperature * (s.moles.clone() * Dimensionless::from(ln_rho - 1.0)).sum() + + RGAS * s.temperature * (s.moles.clone() * Dimensionless::new(ln_rho - 1.0)).sum() }) } } diff --git a/feos-core/src/phase_equilibria/phase_diagram_binary.rs b/feos-core/src/phase_equilibria/phase_diagram_binary.rs index e5ff22756..190b38844 100644 --- a/feos-core/src/phase_equilibria/phase_diagram_binary.rs +++ b/feos-core/src/phase_equilibria/phase_diagram_binary.rs @@ -2,11 +2,11 @@ use super::bubble_dew::TemperatureOrPressure; use super::{PhaseDiagram, PhaseEquilibrium}; use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::{Density, Moles, Pressure, Temperature, RGAS}; use crate::state::{Contributions, DensityInitialization, State, StateBuilder, TPSpec}; -use crate::SolverOptions; +use crate::{ReferenceSystem, SolverOptions}; use ndarray::{arr1, arr2, concatenate, s, Array1, Array2, Axis}; use num_dual::linalg::{norm, LU}; +use quantity::{Density, Moles, Pressure, Temperature, RGAS}; use std::sync::Arc; const DEFAULT_POINTS: usize = 51; diff --git a/feos-core/src/phase_equilibria/phase_diagram_pure.rs b/feos-core/src/phase_equilibria/phase_diagram_pure.rs index 831d4a6f5..a64a2f890 100644 --- a/feos-core/src/phase_equilibria/phase_diagram_pure.rs +++ b/feos-core/src/phase_equilibria/phase_diagram_pure.rs @@ -1,11 +1,13 @@ use super::PhaseEquilibrium; use crate::equation_of_state::Residual; use crate::errors::EosResult; -use crate::si::Temperature; use crate::state::{State, StateVec}; +#[cfg(feature = "rayon")] +use crate::ReferenceSystem; use crate::SolverOptions; #[cfg(feature = "rayon")] use ndarray::{Array1, ArrayView1, Axis}; +use quantity::Temperature; #[cfg(feature = "rayon")] use rayon::{prelude::*, ThreadPool}; use std::sync::Arc; diff --git a/feos-core/src/phase_equilibria/phase_envelope.rs b/feos-core/src/phase_equilibria/phase_envelope.rs index e69fe1a47..ddfcab4e4 100644 --- a/feos-core/src/phase_equilibria/phase_envelope.rs +++ b/feos-core/src/phase_equilibria/phase_envelope.rs @@ -1,7 +1,7 @@ use super::{PhaseDiagram, PhaseEquilibrium}; use crate::equation_of_state::Residual; use crate::errors::EosResult; -use crate::si::{Moles, Pressure, Temperature}; +use quantity::{Moles, Pressure, Temperature}; use crate::state::{Contributions, State}; use crate::SolverOptions; use ndarray::Array1; diff --git a/feos-core/src/phase_equilibria/stability_analysis.rs b/feos-core/src/phase_equilibria/stability_analysis.rs index 6cdefc0e4..f5f52f355 100644 --- a/feos-core/src/phase_equilibria/stability_analysis.rs +++ b/feos-core/src/phase_equilibria/stability_analysis.rs @@ -1,12 +1,12 @@ use super::PhaseEquilibrium; use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::Moles; use crate::state::{Contributions, DensityInitialization, State}; -use crate::{SolverOptions, Verbosity}; +use crate::{ReferenceSystem, SolverOptions, Verbosity}; use ndarray::*; use num_dual::linalg::smallest_ev; use num_dual::linalg::LU; +use quantity::Moles; use std::ops::MulAssign; const X_DOMINANT: f64 = 0.99; diff --git a/feos-core/src/phase_equilibria/tp_flash.rs b/feos-core/src/phase_equilibria/tp_flash.rs index 3fe90a003..212636f45 100644 --- a/feos-core/src/phase_equilibria/tp_flash.rs +++ b/feos-core/src/phase_equilibria/tp_flash.rs @@ -1,11 +1,11 @@ use super::PhaseEquilibrium; use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::{Dimensionless, Moles, Pressure, Temperature}; use crate::state::{Contributions, DensityInitialization, State}; use crate::{SolverOptions, Verbosity}; use ndarray::*; use num_dual::linalg::norm; +use quantity::{Dimensionless, Moles, Pressure, Temperature}; use std::sync::Arc; const MAX_ITER_TP: usize = 400; @@ -315,9 +315,9 @@ impl PhaseEquilibrium { beta = rachford_rice(&feed_state.molefracs, k, Some(beta))?; // update VLE - let v = feed_state.moles.clone() * Dimensionless::from(beta * k / (1.0 - beta + beta * k)); + let v = feed_state.moles.clone() * Dimensionless::new(beta * k / (1.0 - beta + beta * k)); let l = - feed_state.moles.clone() * Dimensionless::from((1.0 - beta) / (1.0 - beta + beta * k)); + feed_state.moles.clone() * Dimensionless::new((1.0 - beta) / (1.0 - beta + beta * k)); self.update_moles(feed_state.pressure(Contributions::Total), [&v, &l])?; Ok(()) } diff --git a/feos-core/src/phase_equilibria/vle_pure.rs b/feos-core/src/phase_equilibria/vle_pure.rs index c2df4cc5f..326ba896c 100644 --- a/feos-core/src/phase_equilibria/vle_pure.rs +++ b/feos-core/src/phase_equilibria/vle_pure.rs @@ -1,10 +1,10 @@ use super::PhaseEquilibrium; use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::{Moles, Pressure, Temperature, RGAS}; use crate::state::{Contributions, DensityInitialization, State, TPSpec}; -use crate::{SolverOptions, TemperatureOrPressure, Verbosity}; +use crate::{ReferenceSystem, SolverOptions, TemperatureOrPressure, Verbosity}; use ndarray::{arr1, Array1}; +use quantity::{Moles, Pressure, Temperature, RGAS}; use std::sync::Arc; const SCALE_T_NEW: f64 = 0.7; diff --git a/feos-core/src/python/equation_of_state.rs b/feos-core/src/python/equation_of_state.rs index f5dba0756..3afcb08c6 100644 --- a/feos-core/src/python/equation_of_state.rs +++ b/feos-core/src/python/equation_of_state.rs @@ -13,8 +13,8 @@ macro_rules! impl_equation_of_state { /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(moles=None)")] - fn max_density(&self, moles: Option) -> PyResult { + #[pyo3(text_signature = "(moles=None)", signature = (moles=None))] + fn max_density(&self, moles: Option>>) -> PyResult { let m = moles.map(|m| m.try_into()).transpose()?; Ok(self.0.max_density(m.as_ref())?.into()) } @@ -39,12 +39,12 @@ macro_rules! impl_virial_coefficients { /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, moles=None)")] + #[pyo3(text_signature = "(temperature, moles=None)", signature = (temperature, moles=None))] fn second_virial_coefficient( &self, - temperature: PySINumber, - moles: Option, - ) -> PyResult { + temperature: Temperature, + moles: Option>>, + ) -> PyResult> { let m = moles.map(|m| m.try_into()).transpose()?; Ok(self .0 @@ -64,12 +64,12 @@ macro_rules! impl_virial_coefficients { /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, moles=None)")] + #[pyo3(text_signature = "(temperature, moles=None)", signature = (temperature, moles=None))] fn third_virial_coefficient( &self, - temperature: PySINumber, - moles: Option, - ) -> PyResult { + temperature: Temperature, + moles: Option>>, + ) -> PyResult, Density>> { let m = moles.map(|m| m.try_into()).transpose()?; Ok(self .0 @@ -90,12 +90,12 @@ macro_rules! impl_virial_coefficients { /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, moles=None)")] + #[pyo3(text_signature = "(temperature, moles=None)", signature = (temperature, moles=None))] fn second_virial_coefficient_temperature_derivative( &self, - temperature: PySINumber, - moles: Option, - ) -> PyResult { + temperature: Temperature, + moles: Option>>, + ) -> PyResult, Temperature>> { let m = moles.map(|m| m.try_into()).transpose()?; Ok(self .0 @@ -119,12 +119,12 @@ macro_rules! impl_virial_coefficients { /// Returns /// ------- /// SINumber - #[pyo3(text_signature = "(temperature, moles=None)")] + #[pyo3(text_signature = "(temperature, moles=None)", signature = (temperature, moles=None))] fn third_virial_coefficient_temperature_derivative( &self, - temperature: PySINumber, - moles: Option, - ) -> PyResult { + temperature: Temperature, + moles: Option>>, + ) -> PyResult, Density>, Temperature>> { let m = moles.map(|m| m.try_into()).transpose()?; Ok(self .0 diff --git a/feos-core/src/python/mod.rs b/feos-core/src/python/mod.rs index eea09d344..b790f0403 100644 --- a/feos-core/src/python/mod.rs +++ b/feos-core/src/python/mod.rs @@ -1,6 +1,6 @@ use crate::EosError; use pyo3::exceptions::PyRuntimeError; -use pyo3::PyErr; +use pyo3::prelude::*; pub mod cubic; mod equation_of_state; diff --git a/feos-core/src/python/parameter/fragmentation.rs b/feos-core/src/python/parameter/fragmentation.rs index 0817f07ab..90346551b 100644 --- a/feos-core/src/python/parameter/fragmentation.rs +++ b/feos-core/src/python/parameter/fragmentation.rs @@ -48,7 +48,7 @@ pub struct PySmartsRecord(pub SmartsRecord); #[pymethods] impl PySmartsRecord { #[new] - #[pyo3(text_signature = "(group, smarts, max=None)")] + #[pyo3(text_signature = "(group, smarts, max=None)", signature = (group, smarts, max=None))] fn new(group: String, smarts: String, max: Option) -> Self { Self(SmartsRecord::new(group, smarts, max)) } @@ -124,7 +124,7 @@ fn fragment_molecule( let m = chem.call_method1("MolFromSmarts", (s.0.smarts,))?; let matches = mol .call_method1("GetSubstructMatches", (m,))? - .extract::>()?; + .extract::>>()?; let mut matches: Vec<_> = matches .into_iter() .map(|m| m.extract::>()) @@ -158,7 +158,7 @@ fn fragment_molecule( let builtins = py.import_bound("builtins")?; let bonds = builtins .call_method1("list", (bonds,))? - .extract::>()?; + .extract::>>()?; let bonds: Vec<_> = bonds .into_iter() .map(|b| { diff --git a/feos-core/src/python/parameter/mod.rs b/feos-core/src/python/parameter/mod.rs index e583ba680..b02d8e15f 100644 --- a/feos-core/src/python/parameter/mod.rs +++ b/feos-core/src/python/parameter/mod.rs @@ -39,7 +39,8 @@ pub struct PyIdentifier(pub Identifier); impl PyIdentifier { #[new] #[pyo3( - text_signature = "(cas=None, name=None, iupac_name=None, smiles=None, inchi=None, formula=None)" + text_signature = "(cas=None, name=None, iupac_name=None, smiles=None, inchi=None, formula=None)", + signature = (cas=None, name=None, iupac_name=None, smiles=None, inchi=None, formula=None) )] fn new( cas: Option<&str>, @@ -145,7 +146,7 @@ pub struct PyChemicalRecord(pub ChemicalRecord); #[pymethods] impl PyChemicalRecord { #[new] - #[pyo3(text_signature = "(identifier, segments, bonds=None)")] + #[pyo3(text_signature = "(identifier, segments, bonds=None)", signature = (identifier, segments, bonds=None))] fn new( identifier: PyIdentifier, segments: Vec, @@ -268,6 +269,7 @@ macro_rules! impl_binary_record { } #[getter] + #[expect(irrefutable_let_patterns)] fn get_model_record<'py>(&self, py: Python<'py>) -> PyResult> { Ok(if let Ok(mr) = f64::try_from(self.0.model_record.clone()) { pyo3::types::PyFloat::new_bound(py, mr).into_any() @@ -631,7 +633,7 @@ macro_rules! impl_parameter { /// binary_record : float or BinaryRecord, optional /// The binary interaction parameter or binary interaction record. #[staticmethod] - #[pyo3(text_signature = "(pure_records, binary_record=None)")] + #[pyo3(text_signature = "(pure_records, binary_record=None)", signature = (pure_records, binary_record=None))] fn new_binary( pure_records: Vec, binary_record: Option<&Bound<'_, PyAny>>, @@ -767,7 +769,8 @@ macro_rules! impl_parameter_from_segments { /// binary_segment_records : [BinarySegmentRecord], optional /// A list of binary segment-segment parameters. #[staticmethod] - #[pyo3(text_signature = "(chemical_records, segment_records, binary_segment_records=None)")] + #[pyo3(text_signature = "(chemical_records, segment_records, binary_segment_records=None)", + signature = (chemical_records, segment_records, binary_segment_records=None))] fn from_segments( chemical_records: Vec, segment_records: Vec, @@ -834,6 +837,7 @@ macro_rules! impl_parameter_from_segments { /// A list of binary segment-segment parameters. #[staticmethod] #[pyo3(text_signature = "(identifier, smarts_records, segment_records, binary_segment_records=None)")] + #[pyo3(signature = (identifier, smarts_records, segment_records, binary_segment_records=None))] fn from_smiles( identifier: Vec>, smarts_records: Vec, diff --git a/feos-core/src/python/phase_equilibria.rs b/feos-core/src/python/phase_equilibria.rs index 42baea704..42e20e763 100644 --- a/feos-core/src/python/phase_equilibria.rs +++ b/feos-core/src/python/phase_equilibria.rs @@ -37,22 +37,23 @@ macro_rules! impl_phase_equilibrium { /// When pressure iteration fails or no phase equilibrium is found. #[staticmethod] #[pyo3(text_signature = "(eos, temperature_or_pressure, initial_state=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, temperature_or_pressure, initial_state=None, max_iter=None, tol=None, verbosity=None))] pub fn pure( eos: $py_eos, - temperature_or_pressure: PySINumber, + temperature_or_pressure: Bound<'_, PyAny>, initial_state: Option<&PyPhaseEquilibrium>, max_iter: Option, tol: Option, verbosity: Option, ) -> PyResult { - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(Self(PhaseEquilibrium::pure( &eos.0, t, initial_state.and_then(|s| Some(&s.0)), (max_iter, tol, verbosity).into(), )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(Self(PhaseEquilibrium::pure( &eos.0, p, @@ -60,9 +61,10 @@ macro_rules! impl_phase_equilibrium { (max_iter, tol, verbosity).into(), )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } @@ -101,12 +103,13 @@ macro_rules! impl_phase_equilibrium { /// When pressure iteration fails or no phase equilibrium is found. #[staticmethod] #[pyo3(text_signature = "(eos, temperature, pressure, feed, initial_state=None, max_iter=None, tol=None, verbosity=None, non_volatile_components=None)")] + #[pyo3(signature = (eos, temperature, pressure, feed, initial_state=None, max_iter=None, tol=None, verbosity=None, non_volatile_components=None))] #[expect(clippy::too_many_arguments)] pub fn tp_flash( eos: $py_eos, - temperature: PySINumber, - pressure: PySINumber, - feed: PySIArray1, + temperature: Temperature, + pressure: Pressure, + feed: Moles>, initial_state: Option<&PyPhaseEquilibrium>, max_iter: Option, tol: Option, @@ -115,7 +118,7 @@ macro_rules! impl_phase_equilibrium { ) -> PyResult { Ok(Self(PhaseEquilibrium::tp_flash( &eos.0, - temperature.try_into()?, + temperature, pressure.try_into()?, &feed.try_into()?, initial_state.and_then(|s| Some(&s.0)), @@ -156,12 +159,13 @@ macro_rules! impl_phase_equilibrium { /// PhaseEquilibrium #[staticmethod] #[pyo3(text_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)")] + #[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 fn bubble_point<'py>( eos: $py_eos, - temperature_or_pressure: PySINumber, + temperature_or_pressure: Bound<'_, PyAny>, liquid_molefracs: &Bound<'py, PyArray1>, - tp_init: Option, + tp_init: Option>, vapor_molefracs: Option<&Bound<'py, PyArray1>>, max_iter_inner: Option, max_iter_outer: Option, @@ -170,24 +174,24 @@ macro_rules! impl_phase_equilibrium { verbosity: Option, ) -> PyResult { let x = vapor_molefracs.and_then(|m| Some(m.to_owned_array())); - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(Self(PhaseEquilibrium::bubble_point( &eos.0, t, &liquid_molefracs.to_owned_array(), - tp_init.map(|p| p.try_into()).transpose()?, + tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( (max_iter_inner, tol_inner, verbosity).into(), (max_iter_outer, tol_outer, verbosity).into() ) )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(Self(PhaseEquilibrium::bubble_point( &eos.0, p, &liquid_molefracs.to_owned_array(), - tp_init.map(|p| p.try_into()).transpose()?, + tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( (max_iter_inner, tol_inner, verbosity).into(), @@ -195,9 +199,10 @@ macro_rules! impl_phase_equilibrium { ) )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } @@ -234,12 +239,13 @@ macro_rules! impl_phase_equilibrium { /// PhaseEquilibrium #[staticmethod] #[pyo3(text_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)")] + #[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 fn dew_point<'py>( eos: $py_eos, - temperature_or_pressure: PySINumber, + temperature_or_pressure: Bound<'_, PyAny>, vapor_molefracs: &Bound<'py, PyArray1>, - tp_init: Option, + tp_init: Option>, liquid_molefracs: Option<&Bound<'py, PyArray1>>, max_iter_inner: Option, max_iter_outer: Option, @@ -248,24 +254,24 @@ macro_rules! impl_phase_equilibrium { verbosity: Option, ) -> PyResult { let x = liquid_molefracs.and_then(|m| Some(m.to_owned_array())); - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(Self(PhaseEquilibrium::dew_point( &eos.0, t, &vapor_molefracs.to_owned_array(), - tp_init.map(|p| p.try_into()).transpose()?, + tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( (max_iter_inner, tol_inner, verbosity).into(), (max_iter_outer, tol_outer, verbosity).into() ) )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(Self(PhaseEquilibrium::dew_point( &eos.0, p, &vapor_molefracs.to_owned_array(), - tp_init.map(|p| p.try_into()).transpose()?, + tp_init.map(|p| p.extract()).transpose()?, x.as_ref(), ( (max_iter_inner, tol_inner, verbosity).into(), @@ -273,9 +279,10 @@ macro_rules! impl_phase_equilibrium { ) )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } @@ -305,14 +312,14 @@ macro_rules! impl_phase_equilibrium { #[staticmethod] pub fn new_npt( eos: $py_eos, - temperature: PySINumber, - pressure: PySINumber, - vapor_moles: PySIArray1, - liquid_moles: PySIArray1 + temperature: Temperature, + pressure: Pressure, + vapor_moles: Moles>, + liquid_moles: Moles> ) -> PyResult { Ok(Self(PhaseEquilibrium::new_npt( &eos.0, - temperature.try_into()?, + temperature, pressure.try_into()?, &vapor_moles.try_into()?, &liquid_moles.try_into()? @@ -343,21 +350,22 @@ macro_rules! impl_phase_equilibrium { /// ------- /// list[PhaseEquilibrium] #[staticmethod] - fn vle_pure_comps(eos: $py_eos, temperature_or_pressure: PySINumber) -> PyResult>> { - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + fn vle_pure_comps(eos: $py_eos, temperature_or_pressure: Bound<'_, PyAny>) -> PyResult>> { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(PhaseEquilibrium::vle_pure_comps(&eos.0, t) .into_iter() .map(|o| o.map(Self)) .collect()) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(PhaseEquilibrium::vle_pure_comps(&eos.0, p) .into_iter() .map(|o| o.map(Self)) .collect()) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } @@ -375,11 +383,8 @@ macro_rules! impl_phase_equilibrium { /// ------- /// list[SINumber] #[staticmethod] - fn vapor_pressure(eos: $py_eos, temperature: PySINumber) -> PyResult>> { - Ok(PhaseEquilibrium::vapor_pressure(&eos.0, temperature.try_into()?) - .into_iter() - .map(|o| o.map(|n| n.into())) - .collect()) + fn vapor_pressure(eos: $py_eos, temperature: Temperature) -> Vec> { + PhaseEquilibrium::vapor_pressure(&eos.0, temperature) } /// Calculate the pure component boiling temperatures for all the @@ -396,11 +401,8 @@ macro_rules! impl_phase_equilibrium { /// ------- /// list[SINumber] #[staticmethod] - fn boiling_temperature(eos: $py_eos, pressure: PySINumber) -> PyResult>> { - Ok(PhaseEquilibrium::boiling_temperature(&eos.0, pressure.try_into()?) - .into_iter() - .map(|o| o.map(|n| n.into())) - .collect()) + fn boiling_temperature(eos: $py_eos, pressure: Pressure) -> Vec> { + PhaseEquilibrium::boiling_temperature(&eos.0, pressure) } fn _repr_markdown_(&self) -> String { @@ -452,12 +454,13 @@ macro_rules! impl_phase_equilibrium { /// The verbosity of the bubble/dew point iteration. #[staticmethod] #[pyo3(text_signature = "(eos, temperature_or_pressure, x_init, tp_init=None, max_iter=None, tol=None, verbosity=None, max_iter_bd_inner=None, max_iter_bd_outer=None, tol_bd_inner=None, tol_bd_outer=None, verbosity_bd=None)")] + #[pyo3(signature = (eos, temperature_or_pressure, x_init, tp_init=None, max_iter=None, tol=None, verbosity=None, max_iter_bd_inner=None, max_iter_bd_outer=None, tol_bd_inner=None, tol_bd_outer=None, verbosity_bd=None))] #[expect(clippy::too_many_arguments)] fn heteroazeotrope( eos: $py_eos, - temperature_or_pressure: PySINumber, + temperature_or_pressure: Bound<'_, PyAny>, x_init: (f64, f64), - tp_init: Option, + tp_init: Option>, max_iter: Option, tol: Option, verbosity: Option, @@ -467,24 +470,24 @@ macro_rules! impl_phase_equilibrium { tol_bd_outer: Option, verbosity_bd: Option, ) -> PyResult { - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(PyThreePhaseEquilibrium(PhaseEquilibrium::heteroazeotrope( &eos.0, t, x_init, - tp_init.map(|t| t.try_into()).transpose()?, + tp_init.map(|t| t.extract()).transpose()?, (max_iter, tol, verbosity).into(), ( (max_iter_bd_inner, tol_bd_inner, verbosity_bd).into(), (max_iter_bd_outer, tol_bd_outer, verbosity_bd).into(), ) )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(PyThreePhaseEquilibrium(PhaseEquilibrium::heteroazeotrope( &eos.0, p, x_init, - tp_init.map(|t| t.try_into()).transpose()?, + tp_init.map(|t| t.extract()).transpose()?, (max_iter, tol, verbosity).into(), ( (max_iter_bd_inner, tol_bd_inner, verbosity_bd).into(), @@ -492,9 +495,10 @@ macro_rules! impl_phase_equilibrium { ) )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } } @@ -550,6 +554,7 @@ macro_rules! impl_phase_equilibrium { /// RuntimeError /// When pressure iteration fails or no phase equilibrium is found. #[pyo3(text_signature = "($self, initial_state=None, max_iter=None, tol=None, verbosity=None, non_volatile_components=None)")] + #[pyo3(signature = (initial_state=None, max_iter=None, tol=None, verbosity=None, non_volatile_components=None))] pub fn tp_flash( &self, initial_state: Option<&PyPhaseEquilibrium>, @@ -612,18 +617,19 @@ macro_rules! impl_phase_equilibrium { /// PhaseDiagram #[staticmethod] #[pyo3(text_signature = "(eos, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None))] pub fn pure( eos: &$py_eos, - min_temperature: PySINumber, + min_temperature: Temperature, npoints: usize, - critical_temperature: Option, + critical_temperature: Option, max_iter: Option, tol: Option, verbosity: Option, ) -> PyResult { let dia = PhaseDiagram::pure( &eos.0, - min_temperature.try_into()?, + min_temperature, npoints, critical_temperature.map(|t| t.try_into()).transpose()?, (max_iter, tol, verbosity).into(), @@ -663,14 +669,15 @@ macro_rules! impl_phase_equilibrium { #[cfg(feature = "rayon")] #[staticmethod] #[pyo3(text_signature = "(eos, min_temperature, npoints, chunksize, nthreads, critical_temperature=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, min_temperature, npoints, chunksize, nthreads, critical_temperature=None, max_iter=None, tol=None, verbosity=None))] #[expect(clippy::too_many_arguments)] pub fn par_pure( eos: &$py_eos, - min_temperature: PySINumber, + min_temperature: Temperature, npoints: usize, chunksize: usize, nthreads: usize, - critical_temperature: Option, + critical_temperature: Option, max_iter: Option, tol: Option, verbosity: Option, @@ -680,11 +687,11 @@ macro_rules! impl_phase_equilibrium { .build()?; let dia = PhaseDiagram::par_pure( &eos.0, - min_temperature.try_into()?, + min_temperature, npoints, chunksize, thread_pool, - critical_temperature.map(|t| t.try_into()).transpose()?, + critical_temperature, (max_iter, tol, verbosity).into(), )?; Ok(Self(dia)) @@ -726,13 +733,14 @@ macro_rules! impl_phase_equilibrium { /// PhaseDiagram #[staticmethod] #[pyo3(text_signature = "(eos, moles, 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, moles, 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 fn bubble_point_line( eos: &$py_eos, - moles: PySIArray1, - min_temperature: PySINumber, + moles: Moles>, + min_temperature: Temperature, npoints: usize, - critical_temperature: Option, + critical_temperature: Option, max_iter_inner: Option, max_iter_outer: Option, tol_inner: Option, @@ -742,7 +750,7 @@ macro_rules! impl_phase_equilibrium { let dia = PhaseDiagram::bubble_point_line( &eos.0, &moles.try_into()?, - min_temperature.try_into()?, + min_temperature, npoints, critical_temperature.map(|t| t.try_into()).transpose()?, ( @@ -789,13 +797,14 @@ macro_rules! impl_phase_equilibrium { /// PhaseDiagram #[staticmethod] #[pyo3(text_signature = "(eos, moles, 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, moles, 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 fn dew_point_line( eos: &$py_eos, - moles: PySIArray1, - min_temperature: PySINumber, + moles: Moles>, + min_temperature: Temperature, npoints: usize, - critical_temperature: Option, + critical_temperature: Option, max_iter_inner: Option, max_iter_outer: Option, tol_inner: Option, @@ -805,7 +814,7 @@ macro_rules! impl_phase_equilibrium { let dia = PhaseDiagram::dew_point_line( &eos.0, &moles.try_into()?, - min_temperature.try_into()?, + min_temperature, npoints, critical_temperature.map(|t| t.try_into()).transpose()?, ( @@ -844,13 +853,14 @@ macro_rules! impl_phase_equilibrium { /// PhaseDiagram #[staticmethod] #[pyo3(text_signature = "(eos, moles, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, moles, min_temperature, npoints, critical_temperature=None, max_iter=None, tol=None, verbosity=None))] #[expect(clippy::too_many_arguments)] pub fn spinodal( eos: &$py_eos, - moles: PySIArray1, - min_temperature: PySINumber, + moles: Moles>, + min_temperature: Temperature, npoints: usize, - critical_temperature: Option, + critical_temperature: Option, max_iter: Option, tol: Option, verbosity: Option, @@ -858,7 +868,7 @@ macro_rules! impl_phase_equilibrium { let dia = PhaseDiagram::spinodal( &eos.0, &moles.try_into()?, - min_temperature.try_into()?, + min_temperature, npoints, critical_temperature.map(|t| t.try_into()).transpose()?, (max_iter, tol, verbosity).into(), @@ -923,11 +933,11 @@ macro_rules! impl_phase_equilibrium { dict.insert(String::from(format!("y{}", i)), ys.column(i).to_vec()); } } - dict.insert(String::from("temperature"), self.0.vapor().temperature().convert_into(KELVIN).into_raw_vec()); + dict.insert(String::from("temperature"), self.0.vapor().temperature().convert_to(KELVIN).into_raw_vec_and_offset().0); // Check if liquid and vapor pressures are different (e.g. if ) - let p_v = self.0.vapor().pressure().convert_into(PASCAL).into_raw_vec(); - let p_l = self.0.liquid().pressure().convert_into(PASCAL).into_raw_vec(); + let p_v = self.0.vapor().pressure().convert_to(PASCAL).into_raw_vec_and_offset().0; + let p_l = self.0.liquid().pressure().convert_to(PASCAL).into_raw_vec_and_offset().0; let different_pressures = p_v.iter().zip(p_l.iter()).any(|(pv, pl)| (pv - pl).abs() / pv > 1e-3); if different_pressures { @@ -936,18 +946,18 @@ macro_rules! impl_phase_equilibrium { }else { dict.insert(String::from("pressure"), p_v); } - dict.insert(String::from("density liquid"), self.0.liquid().density().convert_into(MOL / METER.powi::()).into_raw_vec()); - dict.insert(String::from("density vapor"), self.0.vapor().density().convert_into(MOL / METER.powi::()).into_raw_vec()); - dict.insert(String::from("mass density liquid"), self.0.liquid().mass_density().convert_into(KILOGRAM / METER.powi::()).into_raw_vec()); - dict.insert(String::from("mass density vapor"), self.0.vapor().mass_density().convert_into(KILOGRAM / METER.powi::()).into_raw_vec()); - dict.insert(String::from("molar enthalpy liquid"), self.0.liquid().molar_enthalpy(contributions).convert_into(KILO * JOULE / MOL).into_raw_vec()); - dict.insert(String::from("molar enthalpy vapor"), self.0.vapor().molar_enthalpy(contributions).convert_into(KILO * JOULE / MOL).into_raw_vec()); - dict.insert(String::from("molar entropy liquid"), self.0.liquid().molar_entropy(contributions).convert_into(KILO * JOULE / KELVIN / MOL).into_raw_vec()); - dict.insert(String::from("molar entropy vapor"), self.0.vapor().molar_entropy(contributions).convert_into(KILO * JOULE / KELVIN / MOL).into_raw_vec()); - dict.insert(String::from("specific enthalpy liquid"), self.0.liquid().specific_enthalpy(contributions).convert_into(KILO * JOULE / KILOGRAM).into_raw_vec()); - dict.insert(String::from("specific enthalpy vapor"), self.0.vapor().specific_enthalpy(contributions).convert_into(KILO * JOULE / KILOGRAM).into_raw_vec()); - dict.insert(String::from("specific entropy liquid"), self.0.liquid().specific_entropy(contributions).convert_into(KILO * JOULE / KELVIN / KILOGRAM).into_raw_vec()); - dict.insert(String::from("specific entropy vapor"), self.0.vapor().specific_entropy(contributions).convert_into(KILO * JOULE / KELVIN / KILOGRAM).into_raw_vec()); + dict.insert(String::from("density liquid"), self.0.liquid().density().convert_to(MOL / METER.powi::()).into_raw_vec_and_offset().0); + dict.insert(String::from("density vapor"), self.0.vapor().density().convert_to(MOL / METER.powi::()).into_raw_vec_and_offset().0); + dict.insert(String::from("mass density liquid"), self.0.liquid().mass_density().convert_to(KILOGRAM / METER.powi::()).into_raw_vec_and_offset().0); + dict.insert(String::from("mass density vapor"), self.0.vapor().mass_density().convert_to(KILOGRAM / METER.powi::()).into_raw_vec_and_offset().0); + dict.insert(String::from("molar enthalpy liquid"), self.0.liquid().molar_enthalpy(contributions).convert_to(KILO * JOULE / MOL).into_raw_vec_and_offset().0); + dict.insert(String::from("molar enthalpy vapor"), self.0.vapor().molar_enthalpy(contributions).convert_to(KILO * JOULE / MOL).into_raw_vec_and_offset().0); + dict.insert(String::from("molar entropy liquid"), self.0.liquid().molar_entropy(contributions).convert_to(KILO * JOULE / KELVIN / MOL).into_raw_vec_and_offset().0); + dict.insert(String::from("molar entropy vapor"), self.0.vapor().molar_entropy(contributions).convert_to(KILO * JOULE / KELVIN / MOL).into_raw_vec_and_offset().0); + dict.insert(String::from("specific enthalpy liquid"), self.0.liquid().specific_enthalpy(contributions).convert_to(KILO * JOULE / KILOGRAM).into_raw_vec_and_offset().0); + dict.insert(String::from("specific enthalpy vapor"), self.0.vapor().specific_enthalpy(contributions).convert_to(KILO * JOULE / KILOGRAM).into_raw_vec_and_offset().0); + dict.insert(String::from("specific entropy liquid"), self.0.liquid().specific_entropy(contributions).convert_to(KILO * JOULE / KELVIN / KILOGRAM).into_raw_vec_and_offset().0); + dict.insert(String::from("specific entropy vapor"), self.0.vapor().specific_entropy(contributions).convert_to(KILO * JOULE / KELVIN / KILOGRAM).into_raw_vec_and_offset().0); dict } @@ -980,10 +990,11 @@ macro_rules! impl_phase_equilibrium { /// PhaseDiagram #[staticmethod] #[pyo3(text_signature = "(eos, temperature_or_pressure, npoints=None, x_lle=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None)")] + #[pyo3(signature = (eos, temperature_or_pressure, npoints=None, x_lle=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] #[expect(clippy::too_many_arguments)] pub fn binary_vle( eos: $py_eos, - temperature_or_pressure: PySINumber, + temperature_or_pressure: Bound<'_, PyAny>, npoints: Option, x_lle: Option<(f64, f64)>, max_iter_inner: Option, @@ -992,7 +1003,7 @@ macro_rules! impl_phase_equilibrium { tol_outer: Option, verbosity: Option, ) -> PyResult { - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(Self(PhaseDiagram::binary_vle( &eos.0, t, @@ -1003,7 +1014,7 @@ macro_rules! impl_phase_equilibrium { (max_iter_outer, tol_outer, verbosity).into(), ) )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(Self(PhaseDiagram::binary_vle( &eos.0, p, @@ -1015,9 +1026,10 @@ macro_rules! impl_phase_equilibrium { ) )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } @@ -1048,36 +1060,38 @@ macro_rules! impl_phase_equilibrium { /// PhaseDiagram #[staticmethod] #[pyo3(text_signature = "(eos, temperature_or_pressure, feed, min_tp, max_tp, npoints=None)")] + #[pyo3(signature = (eos, temperature_or_pressure, feed, min_tp, max_tp, npoints=None))] pub fn lle( eos: $py_eos, - temperature_or_pressure: PySINumber, - feed: PySIArray1, - min_tp: PySINumber, - max_tp: PySINumber, + temperature_or_pressure: Bound<'_, PyAny>, + feed: Moles>, + min_tp: Bound<'_, PyAny>, + max_tp: Bound<'_, PyAny>, npoints: Option, ) -> PyResult { - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(Self(PhaseDiagram::lle( &eos.0, t, - &feed.try_into()?, - min_tp.try_into()?, - max_tp.try_into()?, + &feed, + min_tp.extract()?, + max_tp.extract()?, npoints, )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(Self(PhaseDiagram::lle( &eos.0, p, - &feed.try_into()?, - min_tp.try_into()?, - max_tp.try_into()?, + &feed, + min_tp.extract()?, + max_tp.extract()?, npoints, )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } } @@ -1123,14 +1137,15 @@ macro_rules! impl_phase_equilibrium { /// ------- /// PhaseDiagramHetero #[staticmethod] - #[pyo3(text_signature = "(eos, temperature_or_pressure, x_lle, tp_lim_lle=None, tp_init_vlle=None, npoints_vle=None, npoints_lle=None, max_iter_bd_inner=None, max_iter_bd_outer=None, tol_bd_inner=None, tol_bd_outer=None, verbosity_bd=None)")] + #[pyo3(text_signature = "(eos, temperature_or_pressure, x_lle, tp_lim_lle=None, tp_init_vlle=None, npoints_vle=None, npoints_lle=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None)")] + #[pyo3(signature = (eos, temperature_or_pressure, x_lle, tp_lim_lle=None, tp_init_vlle=None, npoints_vle=None, npoints_lle=None, max_iter_inner=None, max_iter_outer=None, tol_inner=None, tol_outer=None, verbosity=None))] #[expect(clippy::too_many_arguments)] pub fn binary_vlle( eos: $py_eos, - temperature_or_pressure: PySINumber, + temperature_or_pressure: Bound<'_, PyAny>, x_lle: (f64, f64), - tp_lim_lle: Option, - tp_init_vlle: Option, + tp_lim_lle: Option>, + tp_init_vlle: Option>, npoints_vle: Option, npoints_lle: Option, max_iter_inner: Option, @@ -1139,13 +1154,13 @@ macro_rules! impl_phase_equilibrium { tol_outer: Option, verbosity: Option, ) -> PyResult { - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(PyPhaseDiagramHetero(PhaseDiagram::binary_vlle( &eos.0, t, x_lle, - tp_lim_lle.map(|t| t.try_into()).transpose()?, - tp_init_vlle.map(|t| t.try_into()).transpose()?, + tp_lim_lle.map(|t| t.extract()).transpose()?, + tp_init_vlle.map(|t| t.extract()).transpose()?, npoints_vle, npoints_lle, ( @@ -1153,13 +1168,13 @@ macro_rules! impl_phase_equilibrium { (max_iter_outer, tol_outer, verbosity).into(), ) )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(PyPhaseDiagramHetero(PhaseDiagram::binary_vlle( &eos.0, p, x_lle, - tp_lim_lle.map(|t| t.try_into()).transpose()?, - tp_init_vlle.map(|t| t.try_into()).transpose()?, + tp_lim_lle.map(|t| t.extract()).transpose()?, + tp_init_vlle.map(|t| t.extract()).transpose()?, npoints_vle, npoints_lle, ( @@ -1168,9 +1183,10 @@ macro_rules! impl_phase_equilibrium { ) )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } } diff --git a/feos-core/src/python/state.rs b/feos-core/src/python/state.rs index fb71aa04b..4beea59f3 100644 --- a/feos-core/src/python/state.rs +++ b/feos-core/src/python/state.rs @@ -54,22 +54,23 @@ macro_rules! impl_state { 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)")] + #[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))] #[expect(clippy::too_many_arguments)] pub fn new<'py>( eos: $py_eos, - temperature: Option, - volume: Option, - density: Option, - partial_density: Option, - total_moles: Option, - moles: Option, + temperature: Option, + volume: Option, + density: Option, + partial_density: Option>>, + total_moles: Option, + moles: Option>>, molefracs: Option<&Bound<'py, PyArray1>>, - pressure: Option, - molar_enthalpy: Option, - molar_entropy: Option, - molar_internal_energy: Option, + pressure: Option, + molar_enthalpy: Option, + molar_entropy: Option, + molar_internal_energy: Option, density_initialization: Option<&Bound<'py, PyAny>>, - initial_temperature: Option, + initial_temperature: Option, ) -> PyResult { let x = molefracs.and_then(|m| Some(m.to_owned_array())); let density_init = if let Some(di) = density_initialization { @@ -81,7 +82,7 @@ macro_rules! impl_state { "`density_initialization` must be 'vapor' or 'liquid'." ))), } - } else if let Ok(d) = di.extract::() { + } else if let Ok(d) = di.extract::() { Ok(DensityInitialization::InitialDensity(d.try_into()?)) } else { Err(PyErr::new::(format!( @@ -131,9 +132,10 @@ macro_rules! impl_state { /// State : tate at critical conditions #[staticmethod] #[pyo3(text_signature = "(eos, initial_temperature=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, initial_temperature=None, max_iter=None, tol=None, verbosity=None))] fn critical_point_pure( eos: $py_eos, - initial_temperature: Option, + initial_temperature: Option, max_iter: Option, tol: Option, verbosity: Option, @@ -166,10 +168,11 @@ macro_rules! impl_state { /// State : State at critical conditions. #[staticmethod] #[pyo3(text_signature = "(eos, moles=None, initial_temperature=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, moles=None, initial_temperature=None, max_iter=None, tol=None, verbosity=None))] fn critical_point( eos: $py_eos, - moles: Option, - initial_temperature: Option, + moles: Option>>, + initial_temperature: Option, max_iter: Option, tol: Option, verbosity: Option, @@ -206,16 +209,17 @@ macro_rules! impl_state { /// State : State at critical conditions. #[staticmethod] #[pyo3(text_signature = "(eos, temperature_or_pressure, initial_temperature=None, initial_molefracs=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, temperature_or_pressure, initial_temperature=None, initial_molefracs=None, max_iter=None, tol=None, verbosity=None))] fn critical_point_binary( eos: $py_eos, - temperature_or_pressure: PySINumber, - initial_temperature: Option, + temperature_or_pressure: Bound<'_, PyAny>, + initial_temperature: Option, initial_molefracs: Option<[f64; 2]>, max_iter: Option, tol: Option, verbosity: Option, ) -> PyResult { - if let Ok(t) = Temperature::::try_from(temperature_or_pressure) { + if let Ok(t) = temperature_or_pressure.extract::() { Ok(PyState(State::critical_point_binary( &eos.0, t, @@ -223,7 +227,7 @@ macro_rules! impl_state { initial_molefracs, (max_iter, tol, verbosity).into(), )?)) - } else if let Ok(p) = Pressure::::try_from(temperature_or_pressure) { + } else if let Ok(p) = temperature_or_pressure.extract::() { Ok(PyState(State::critical_point_binary( &eos.0, p, @@ -232,9 +236,10 @@ macro_rules! impl_state { (max_iter, tol, verbosity).into(), )?)) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(temperature_or_pressure).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } @@ -261,17 +266,18 @@ macro_rules! impl_state { /// State : State at critical conditions. #[staticmethod] #[pyo3(text_signature = "(eos, temperature, moles=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (eos, temperature, moles=None, max_iter=None, tol=None, verbosity=None))] fn spinodal( eos: $py_eos, - temperature: PySINumber, - moles: Option, + temperature: Temperature, + moles: Option>>, max_iter: Option, tol: Option, verbosity: Option, ) -> PyResult<(Self, Self)> { let [state1, state2] = State::spinodal( &eos.0, - temperature.try_into()?, + temperature, moles.map(|m| m.try_into()).transpose()?.as_ref(), (max_iter, tol, verbosity).into(), )?; @@ -294,6 +300,7 @@ macro_rules! impl_state { /// ------- /// State #[pyo3(text_signature = "(max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (max_iter=None, tol=None, verbosity=None))] fn stability_analysis(&self, max_iter: Option, tol: Option, @@ -323,6 +330,7 @@ macro_rules! impl_state { /// ------- /// bool #[pyo3(text_signature = "(max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (max_iter=None, tol=None, verbosity=None))] fn is_stable(&self, max_iter: Option, tol: Option, @@ -343,8 +351,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn pressure(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.pressure(contributions)) + fn pressure(&self, contributions: Contributions) -> Pressure { + self.0.pressure(contributions) } /// Return pressure contributions. @@ -352,12 +360,8 @@ macro_rules! impl_state { /// Returns /// ------- /// List[Tuple[str, SINumber]] - fn pressure_contributions(&self) -> Vec<(String, PySINumber)> { - self.0 - .pressure_contributions() - .into_iter() - .map(|(s, q)| (s, PySINumber::from(q))) - .collect() + fn pressure_contributions(&self) -> Vec<(String, Pressure)> { + self.0.pressure_contributions() } /// Return compressibility. @@ -388,8 +392,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn dp_dv(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.dp_dv(contributions)) + fn dp_dv(&self, contributions: Contributions) -> Quot { + self.0.dp_dv(contributions) } /// Return partial derivative of pressure w.r.t. density. @@ -404,8 +408,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn dp_drho(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.dp_drho(contributions)) + fn dp_drho(&self, contributions: Contributions) -> Quot { + self.0.dp_drho(contributions) } /// Return partial derivative of pressure w.r.t. temperature. @@ -420,8 +424,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn dp_dt(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.dp_dt(contributions)) + fn dp_dt(&self, contributions: Contributions) -> Quot { + self.0.dp_dt(contributions) } /// Return partial derivative of pressure w.r.t. amount of substance. @@ -436,8 +440,8 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn dp_dni(&self, contributions: Contributions) -> PySIArray1 { - PySIArray1::from(self.0.dp_dni(contributions)) + fn dp_dni(&self, contributions: Contributions) -> Quot>> { + self.0.dp_dni(contributions) } /// Return second partial derivative of pressure w.r.t. volume. @@ -452,8 +456,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn d2p_dv2(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.d2p_dv2(contributions)) + fn d2p_dv2(&self, contributions: Contributions) -> Quot, Volume> { + self.0.d2p_dv2(contributions) } /// Return second partial derivative of pressure w.r.t. density. @@ -468,8 +472,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn d2p_drho2(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.d2p_drho2(contributions)) + fn d2p_drho2(&self, contributions: Contributions) -> Quot, Density> { + self.0.d2p_drho2(contributions) } /// Return partial molar volume of each component. @@ -477,8 +481,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SIArray1 - fn partial_molar_volume(&self) -> PySIArray1 { - PySIArray1::from(self.0.partial_molar_volume()) + fn partial_molar_volume(&self) -> MolarVolume> { + self.0.partial_molar_volume() } /// Return chemical potential of each component. @@ -493,8 +497,8 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn chemical_potential(&self, contributions: Contributions) -> PySIArray1 { - PySIArray1::from(self.0.chemical_potential(contributions)) + fn chemical_potential(&self, contributions: Contributions) -> MolarEnergy> { + self.0.chemical_potential(contributions) } /// Return chemical potential contributions. @@ -512,12 +516,8 @@ macro_rules! impl_state { /// ------- /// List[Tuple[str, SINumber]] #[pyo3(signature = (component, contributions=Contributions::Total), text_signature = "($self, component, contributions)")] - fn chemical_potential_contributions(&self, component: usize, contributions: Contributions) -> Vec<(String, PySINumber)> { - self.0 - .chemical_potential_contributions(component, contributions) - .into_iter() - .map(|(s, q)| (s, PySINumber::from(q))) - .collect() + fn chemical_potential_contributions(&self, component: usize, contributions: Contributions) -> Vec<(String, MolarEnergy)> { + self.0.chemical_potential_contributions(component, contributions) } /// Return derivative of chemical potential w.r.t temperature. @@ -532,8 +532,8 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn dmu_dt(&self, contributions: Contributions) -> PySIArray1 { - PySIArray1::from(self.0.dmu_dt(contributions)) + fn dmu_dt(&self, contributions: Contributions) -> Quot>, Temperature> { + self.0.dmu_dt(contributions) } /// Return derivative of chemical potential w.r.t amount of substance. @@ -548,8 +548,8 @@ macro_rules! impl_state { /// ------- /// SIArray2 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn dmu_dni(&self, contributions: Contributions) -> PySIArray2 { - PySIArray2::from(self.0.dmu_dni(contributions)) + fn dmu_dni(&self, contributions: Contributions) -> Quot>, Moles> { + self.0.dmu_dni(contributions) } /// Return logarithmic fugacity coefficient. @@ -595,8 +595,8 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[staticmethod] - fn henrys_law_constant(eos: $py_eos, temperature: PySINumber, molefracs: &Bound<'_, PyArray1>) -> PyResult { - Ok(State::henrys_law_constant(&eos.0, temperature.try_into()?, &molefracs.to_owned_array())?.into()) + fn henrys_law_constant(eos: $py_eos, temperature: Temperature, molefracs: &Bound<'_, PyArray1>) -> PyResult>> { + Ok(State::henrys_law_constant(&eos.0, temperature, &molefracs.to_owned_array())?) } /// Return Henry's law constant of a binary system, assuming the first @@ -613,8 +613,8 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[staticmethod] - fn henrys_law_constant_binary(eos: $py_eos, temperature: PySINumber) -> PyResult { - Ok(State::henrys_law_constant_binary(&eos.0, temperature.try_into()?)?.into()) + fn henrys_law_constant_binary(eos: $py_eos, temperature: Temperature) -> PyResult { + Ok(State::henrys_law_constant_binary(&eos.0, temperature)?) } /// Return derivative of logarithmic fugacity coefficient w.r.t. temperature. @@ -622,8 +622,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SIArray1 - fn dln_phi_dt(&self) -> PySIArray1 { - PySIArray1::from(self.0.dln_phi_dt()) + fn dln_phi_dt(&self) -> Quot>> { + self.0.dln_phi_dt() } /// Return derivative of logarithmic fugacity coefficient w.r.t. pressure. @@ -631,8 +631,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SIArray1 - fn dln_phi_dp(&self) -> PySIArray1 { - PySIArray1::from(self.0.dln_phi_dp()) + fn dln_phi_dp(&self) -> Quot>> { + self.0.dln_phi_dp() } /// Return derivative of logarithmic fugacity coefficient w.r.t. amount of substance. @@ -640,8 +640,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SIArray2 - fn dln_phi_dnj(&self) -> PySIArray2 { - PySIArray2::from(self.0.dln_phi_dnj()) + fn dln_phi_dnj(&self) -> Quot>> { + self.0.dln_phi_dnj() } /// Return thermodynamic factor. @@ -665,8 +665,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_isochoric_heat_capacity(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.molar_isochoric_heat_capacity(contributions)) + fn molar_isochoric_heat_capacity(&self, contributions: Contributions) -> MolarEntropy { + self.0.molar_isochoric_heat_capacity(contributions) } /// Return derivative of isochoric heat capacity w.r.t. temperature. @@ -681,8 +681,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn dc_v_dt(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.dc_v_dt(contributions)) + fn dc_v_dt(&self, contributions: Contributions) -> Quot { + self.0.dc_v_dt(contributions) } /// Return molar isobaric heat capacity. @@ -697,8 +697,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_isobaric_heat_capacity(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.molar_isobaric_heat_capacity(contributions)) + fn molar_isobaric_heat_capacity(&self, contributions: Contributions) -> MolarEntropy { + self.0.molar_isobaric_heat_capacity(contributions) } /// Return entropy. @@ -713,8 +713,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn entropy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.entropy(contributions)) + fn entropy(&self, contributions: Contributions) -> Entropy { + self.0.entropy(contributions) } /// Return derivative of entropy with respect to temperature. @@ -729,8 +729,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn ds_dt(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.ds_dt(contributions)) + fn ds_dt(&self, contributions: Contributions) -> Quot { + self.0.ds_dt(contributions) } /// Return molar entropy. @@ -745,8 +745,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_entropy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.molar_entropy(contributions)) + fn molar_entropy(&self, contributions: Contributions) -> MolarEntropy { + self.0.molar_entropy(contributions) } @@ -755,8 +755,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SIArray1 - fn partial_molar_entropy(&self) -> PySIArray1 { - PySIArray1::from(self.0.partial_molar_entropy()) + fn partial_molar_entropy(&self) -> MolarEntropy> { + self.0.partial_molar_entropy() } /// Return enthalpy. @@ -771,8 +771,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn enthalpy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.enthalpy(contributions)) + fn enthalpy(&self, contributions: Contributions) -> Energy { + self.0.enthalpy(contributions) } /// Return molar enthalpy. @@ -787,8 +787,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_enthalpy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.molar_enthalpy(contributions)) + fn molar_enthalpy(&self, contributions: Contributions) -> MolarEnergy { + self.0.molar_enthalpy(contributions) } @@ -797,8 +797,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SIArray1 - fn partial_molar_enthalpy(&self) -> PySIArray1 { - PySIArray1::from(self.0.partial_molar_enthalpy()) + fn partial_molar_enthalpy(&self) -> MolarEnergy> { + self.0.partial_molar_enthalpy() } /// Return Helmholtz energy. @@ -813,8 +813,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn helmholtz_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.helmholtz_energy(contributions)) + fn helmholtz_energy(&self, contributions: Contributions) -> Energy { + self.0.helmholtz_energy(contributions) } /// Return molar Helmholtz energy. @@ -829,8 +829,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_helmholtz_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.molar_helmholtz_energy(contributions)) + fn molar_helmholtz_energy(&self, contributions: Contributions) -> MolarEnergy { + self.0.molar_helmholtz_energy(contributions) } /// Return residual Helmholtz energy contributions. @@ -838,12 +838,8 @@ macro_rules! impl_state { /// Returns /// ------- /// List[Tuple[str, SINumber]] - fn residual_helmholtz_energy_contributions(&self) -> Vec<(String, PySINumber)> { - self.0 - .residual_helmholtz_energy_contributions() - .into_iter() - .map(|(s, q)| (s, PySINumber::from(q))) - .collect() + fn residual_helmholtz_energy_contributions(&self) -> Vec<(String, Energy)> { + self.0.residual_helmholtz_energy_contributions() } /// Return Gibbs energy. @@ -858,8 +854,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn gibbs_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.gibbs_energy(contributions)) + fn gibbs_energy(&self, contributions: Contributions) -> Energy { + self.0.gibbs_energy(contributions) } /// Return molar Gibbs energy. @@ -874,8 +870,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_gibbs_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.molar_gibbs_energy(contributions)) + fn molar_gibbs_energy(&self, contributions: Contributions) -> MolarEnergy { + self.0.molar_gibbs_energy(contributions) } @@ -891,8 +887,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn internal_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.internal_energy(contributions)) + fn internal_energy(&self, contributions: Contributions) -> Energy { + self.0.internal_energy(contributions) } /// Return molar internal energy. @@ -907,8 +903,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_internal_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.molar_internal_energy(contributions)) + fn molar_internal_energy(&self, contributions: Contributions) -> MolarEnergy { + self.0.molar_internal_energy(contributions) } /// Return Joule Thomson coefficient. @@ -916,8 +912,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn joule_thomson(&self) -> PySINumber { - PySINumber::from(self.0.joule_thomson()) + fn joule_thomson(&self) -> Quot { + self.0.joule_thomson() } /// Return isentropy compressibility coefficient. @@ -925,8 +921,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn isentropic_compressibility(&self) -> PySINumber { - PySINumber::from(self.0.isentropic_compressibility()) + fn isentropic_compressibility(&self) -> Quot { + self.0.isentropic_compressibility() } /// Return isothermal compressibility coefficient. @@ -934,8 +930,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn isothermal_compressibility(&self) -> PySINumber { - PySINumber::from(self.0.isothermal_compressibility()) + fn isothermal_compressibility(&self) -> Quot { + self.0.isothermal_compressibility() } /// Return isenthalpic compressibility coefficient. @@ -943,8 +939,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn isenthalpic_compressibility(&self) -> PySINumber { - PySINumber::from(self.0.isenthalpic_compressibility()) + fn isenthalpic_compressibility(&self) -> Quot { + self.0.isenthalpic_compressibility() } /// Return thermal expansivity coefficient. @@ -952,8 +948,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn thermal_expansivity(&self) -> PySINumber { - PySINumber::from(self.0.thermal_expansivity()) + fn thermal_expansivity(&self) -> Quot { + self.0.thermal_expansivity() } /// Return Grueneisen parameter. @@ -979,8 +975,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn total_molar_weight(&self) -> PySINumber { - PySINumber::from(self.0.total_molar_weight()) + fn total_molar_weight(&self) -> MolarWeight { + self.0.total_molar_weight() } /// Return speed of sound. @@ -988,8 +984,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn speed_of_sound(&self) -> PySINumber { - PySINumber::from(self.0.speed_of_sound()) + fn speed_of_sound(&self) -> Velocity { + self.0.speed_of_sound() } /// Returns mass of each component in the system. @@ -997,8 +993,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SIArray1 - fn mass(&self) -> PySIArray1 { - PySIArray1::from(self.0.mass()) + fn mass(&self) -> Mass> { + self.0.mass() } /// Returns system's total mass. @@ -1006,8 +1002,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn total_mass(&self) -> PySINumber { - PySINumber::from(self.0.total_mass()) + fn total_mass(&self) -> Mass { + self.0.total_mass() } /// Returns system's mass density. @@ -1015,8 +1011,8 @@ macro_rules! impl_state { /// Returns /// ------- /// SINumber - fn mass_density(&self) -> PySINumber { - PySINumber::from(self.0.mass_density()) + fn mass_density(&self) -> MassDensity { + self.0.mass_density() } /// Returns mass fractions for each component. @@ -1040,8 +1036,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_helmholtz_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.specific_helmholtz_energy(contributions)) + fn specific_helmholtz_energy(&self, contributions: Contributions) -> SpecificEnergy { + self.0.specific_helmholtz_energy(contributions) } /// Return mass specific entropy. @@ -1056,8 +1052,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_entropy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.specific_entropy(contributions)) + fn specific_entropy(&self, contributions: Contributions) -> SpecificEntropy { + self.0.specific_entropy(contributions) } /// Return mass specific internal_energy. @@ -1072,8 +1068,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_internal_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.specific_internal_energy(contributions)) + fn specific_internal_energy(&self, contributions: Contributions) -> SpecificEnergy { + self.0.specific_internal_energy(contributions) } /// Return mass specific gibbs_energy. @@ -1088,8 +1084,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_gibbs_energy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.specific_gibbs_energy(contributions)) + fn specific_gibbs_energy(&self, contributions: Contributions) -> SpecificEnergy { + self.0.specific_gibbs_energy(contributions) } /// Return mass specific enthalpy. @@ -1104,8 +1100,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_enthalpy(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.specific_enthalpy(contributions)) + fn specific_enthalpy(&self, contributions: Contributions) -> SpecificEnergy { + self.0.specific_enthalpy(contributions) } /// Return mass specific isochoric heat capacity. @@ -1120,8 +1116,8 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_isochoric_heat_capacity(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.specific_isochoric_heat_capacity(contributions)) + fn specific_isochoric_heat_capacity(&self, contributions: Contributions) -> SpecificEntropy { + self.0.specific_isochoric_heat_capacity(contributions) } /// Return mass specific isobaric heat capacity. @@ -1136,38 +1132,38 @@ macro_rules! impl_state { /// ------- /// SINumber #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_isobaric_heat_capacity(&self, contributions: Contributions) -> PySINumber { - PySINumber::from(self.0.specific_isobaric_heat_capacity(contributions)) + fn specific_isobaric_heat_capacity(&self, contributions: Contributions) -> SpecificEntropy { + self.0.specific_isobaric_heat_capacity(contributions) } #[getter] - fn get_total_moles(&self) -> PySINumber { - PySINumber::from(self.0.total_moles) + fn get_total_moles(&self) -> Moles { + self.0.total_moles } #[getter] - fn get_temperature(&self) -> PySINumber { - PySINumber::from(self.0.temperature) + fn get_temperature(&self) -> Temperature { + self.0.temperature } #[getter] - fn get_volume(&self) -> PySINumber { - PySINumber::from(self.0.volume) + fn get_volume(&self) -> Volume { + self.0.volume } #[getter] - fn get_density(&self) -> PySINumber { - PySINumber::from(self.0.density) + fn get_density(&self) -> Density { + self.0.density } #[getter] - fn get_moles(&self) -> PySIArray1 { - PySIArray1::from(self.0.moles.clone()) + fn get_moles(&self) -> Moles> { + self.0.moles.clone() } #[getter] - fn get_partial_density(&self) -> PySIArray1 { - PySIArray1::from(self.0.partial_density.clone()) + fn get_partial_density(&self) -> Density> { + self.0.partial_density.clone() } #[getter] @@ -1257,7 +1253,7 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_entropy(&self, contributions: Contributions) -> PySIArray1 { + fn molar_entropy(&self, contributions: Contributions) -> MolarEntropy> { StateVec::from(self).molar_entropy(contributions).into() } @@ -1273,7 +1269,7 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_entropy(&self, contributions: Contributions) -> PySIArray1 { + fn specific_entropy(&self, contributions: Contributions) -> SpecificEntropy> { StateVec::from(self).specific_entropy(contributions).into() } @@ -1289,7 +1285,7 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn molar_enthalpy(&self, contributions: Contributions) -> PySIArray1 { + fn molar_enthalpy(&self, contributions: Contributions) -> MolarEnergy> { StateVec::from(self).molar_enthalpy(contributions).into() } @@ -1305,18 +1301,18 @@ macro_rules! impl_state { /// ------- /// SIArray1 #[pyo3(signature = (contributions=Contributions::Total), text_signature = "($self, contributions)")] - fn specific_enthalpy(&self, contributions: Contributions) -> PySIArray1 { + fn specific_enthalpy(&self, contributions: Contributions) -> SpecificEnergy> { StateVec::from(self).specific_enthalpy(contributions).into() } #[getter] - fn get_temperature(&self) -> PySIArray1{ + fn get_temperature(&self) -> Temperature> { StateVec::from(self).temperature().into() } #[getter] - fn get_pressure(&self) -> PySIArray1 { + fn get_pressure(&self) -> Pressure> { StateVec::from(self).pressure().into() } @@ -1326,12 +1322,12 @@ macro_rules! impl_state { } #[getter] - fn get_density(&self) -> PySIArray1 { + fn get_density(&self) -> Density> { StateVec::from(self).density().into() } #[getter] - fn get_moles<'py>(&self, py: Python<'py>) -> PySIArray2 { + fn get_moles<'py>(&self, py: Python<'py>) -> Moles> { StateVec::from(self).moles().into() } @@ -1341,7 +1337,7 @@ macro_rules! impl_state { } #[getter] - fn get_mass_density(&self) -> PySIArray1 { + fn get_mass_density(&self) -> MassDensity> { StateVec::from(self).mass_density().into() } @@ -1386,14 +1382,14 @@ macro_rules! impl_state { dict.insert(String::from(format!("x{}", i)), xs.column(i).to_vec()); } } - dict.insert(String::from("temperature"), states.temperature().convert_into(KELVIN).into_raw_vec()); - dict.insert(String::from("pressure"), states.pressure().convert_into(PASCAL).into_raw_vec()); - dict.insert(String::from("density"), states.density().convert_into(MOL / METER.powi::()).into_raw_vec()); - dict.insert(String::from("mass density"), states.mass_density().convert_into(KILOGRAM / METER.powi::()).into_raw_vec()); - dict.insert(String::from("molar enthalpy"), states.molar_enthalpy(contributions).convert_into(KILO * JOULE / MOL).into_raw_vec()); - dict.insert(String::from("molar entropy"), states.molar_entropy(contributions).convert_into(KILO * JOULE / KELVIN / MOL).into_raw_vec()); - dict.insert(String::from("specific enthalpy"), states.specific_enthalpy(contributions).convert_into(KILO * JOULE / KILOGRAM).into_raw_vec()); - dict.insert(String::from("specific entropy"), states.specific_entropy(contributions).convert_into(KILO * JOULE / KELVIN / KILOGRAM).into_raw_vec()); + dict.insert(String::from("temperature"), states.temperature().convert_to(KELVIN).into_raw_vec_and_offset().0); + dict.insert(String::from("pressure"), states.pressure().convert_to(PASCAL).into_raw_vec_and_offset().0); + dict.insert(String::from("density"), states.density().convert_to(MOL / METER.powi::()).into_raw_vec_and_offset().0); + dict.insert(String::from("mass density"), states.mass_density().convert_to(KILOGRAM / METER.powi::()).into_raw_vec_and_offset().0); + dict.insert(String::from("molar enthalpy"), states.molar_enthalpy(contributions).convert_to(KILO * JOULE / MOL).into_raw_vec_and_offset().0); + dict.insert(String::from("molar entropy"), states.molar_entropy(contributions).convert_to(KILO * JOULE / KELVIN / MOL).into_raw_vec_and_offset().0); + dict.insert(String::from("specific enthalpy"), states.specific_enthalpy(contributions).convert_to(KILO * JOULE / KILOGRAM).into_raw_vec_and_offset().0); + dict.insert(String::from("specific entropy"), states.specific_entropy(contributions).convert_to(KILO * JOULE / KELVIN / KILOGRAM).into_raw_vec_and_offset().0); dict } } @@ -1410,8 +1406,8 @@ macro_rules! impl_state_entropy_scaling { /// Returns /// ------- /// SINumber - fn viscosity(&self) -> PyResult { - Ok(PySINumber::from(self.0.viscosity()?)) + fn viscosity(&self) -> PyResult { + Ok(self.0.viscosity()?) } /// Return reference viscosity for entropy scaling. @@ -1419,8 +1415,8 @@ macro_rules! impl_state_entropy_scaling { /// Returns /// ------- /// SINumber - fn viscosity_reference(&self) -> PyResult { - Ok(PySINumber::from(self.0.viscosity_reference()?)) + fn viscosity_reference(&self) -> PyResult { + Ok(self.0.viscosity_reference()?) } /// Return logarithmic reduced viscosity. @@ -1440,8 +1436,8 @@ macro_rules! impl_state_entropy_scaling { /// Returns /// ------- /// SINumber - fn diffusion(&self) -> PyResult { - Ok(PySINumber::from(self.0.diffusion()?)) + fn diffusion(&self) -> PyResult { + Ok(self.0.diffusion()?) } /// Return reference diffusion for entropy scaling. @@ -1449,8 +1445,8 @@ macro_rules! impl_state_entropy_scaling { /// Returns /// ------- /// SINumber - fn diffusion_reference(&self) -> PyResult { - Ok(PySINumber::from(self.0.diffusion_reference()?)) + fn diffusion_reference(&self) -> PyResult { + Ok(self.0.diffusion_reference()?) } /// Return logarithmic reduced diffusion. @@ -1470,8 +1466,8 @@ macro_rules! impl_state_entropy_scaling { /// Returns /// ------- /// SINumber - fn thermal_conductivity(&self) -> PyResult { - Ok(PySINumber::from(self.0.thermal_conductivity()?)) + fn thermal_conductivity(&self) -> PyResult { + Ok(self.0.thermal_conductivity()?) } /// Return reference thermal conductivity for entropy scaling. @@ -1479,8 +1475,8 @@ macro_rules! impl_state_entropy_scaling { /// Returns /// ------- /// SINumber - fn thermal_conductivity_reference(&self) -> PyResult { - Ok(PySINumber::from(self.0.thermal_conductivity_reference()?)) + fn thermal_conductivity_reference(&self) -> PyResult { + Ok(self.0.thermal_conductivity_reference()?) } /// Return logarithmic reduced thermal conductivity. diff --git a/feos-core/src/python/user_defined.rs b/feos-core/src/python/user_defined.rs index 80f0f07f0..a88ad3695 100644 --- a/feos-core/src/python/user_defined.rs +++ b/feos-core/src/python/user_defined.rs @@ -1,15 +1,13 @@ #![allow(non_snake_case)] -use crate::si::MolarWeight; use crate::{Components, IdealGas, Residual, StateHD}; use ndarray::{Array1, ScalarOperand}; use num_dual::*; use numpy::convert::IntoPyArray; -use numpy::{PyArray, PyReadonlyArray1, PyReadonlyArrayDyn}; +use numpy::{PyArray, PyReadonlyArray1, PyReadonlyArrayDyn, PyReadwriteArrayDyn}; use pyo3::exceptions::PyTypeError; use pyo3::prelude::*; -use quantity::python::PySIArray1; +use quantity::MolarWeight; use std::any::Any; -use std::convert::TryInto; use std::fmt; pub struct PyIdealGas(Py); @@ -83,7 +81,7 @@ macro_rules! impl_ideal_gas { r.as_array().mapv(|ri| <$hd_ty>::from(ri)) // anything but f64 } else if let Ok(r) = py_result.extract::>() { - r.as_array().mapv(|ri| <$hd_ty>::from(ri.extract::<$py_hd_id>(py).unwrap())) + r.as_array().map(|ri| <$hd_ty>::from(ri.extract::<$py_hd_id>(py).unwrap())) } else { panic!("ln_lambda3: data type of result must be one-dimensional numpy ndarray") } @@ -209,16 +207,14 @@ macro_rules! impl_residual { fn molar_weight(&self) -> MolarWeight> { Python::with_gil(|py| { let py_result = self.0.bind(py).call_method0("molar_weight").unwrap(); - if py_result.get_type().name().unwrap() != "SIArray1" { + if py_result.get_type().name().unwrap() != "si_units.SIObject" { panic!( - "Expected an 'SIArray1' for the 'molar_weight' method return type, got {}", + "Expected an 'SIObject' for the 'molar_weight' method return type, got {}", py_result.get_type().name().unwrap() ); } py_result - .extract::() - .unwrap() - .try_into() + .extract::>>() .unwrap() }) } @@ -258,7 +254,11 @@ macro_rules! state { #[getter] pub fn get_moles(&self) -> Vec<$py_hd_id> { - self.0.moles.mapv(<$py_hd_id>::from).into_raw_vec() + self.0 + .moles + .mapv(<$py_hd_id>::from) + .into_raw_vec_and_offset() + .0 } #[getter] @@ -266,12 +266,17 @@ macro_rules! state { self.0 .partial_density .mapv(<$py_hd_id>::from) - .into_raw_vec() + .into_raw_vec_and_offset() + .0 } #[getter] pub fn get_molefracs(&self) -> Vec<$py_hd_id> { - self.0.molefracs.mapv(<$py_hd_id>::from).into_raw_vec() + self.0 + .molefracs + .mapv(<$py_hd_id>::from) + .into_raw_vec_and_offset() + .0 } #[getter] diff --git a/feos-core/src/si/array.rs b/feos-core/src/si/array.rs deleted file mode 100644 index 10a0b053f..000000000 --- a/feos-core/src/si/array.rs +++ /dev/null @@ -1,220 +0,0 @@ -use super::Quantity; -use ndarray::iter::LanesMut; -use ndarray::{ - Array, Array1, ArrayBase, ArrayView, Axis, Data, DataMut, Dimension, NdIndex, RemoveAxis, - ShapeBuilder, -}; -use std::iter::FromIterator; -use std::marker::PhantomData; - -impl Quantity, U> { - /// Create a one-dimensional array from a vector of scalar quantities. - pub fn from_vec(v: Vec>) -> Self { - Self(v.iter().map(|e| e.0).collect(), PhantomData) - } - - /// Create a one-dimensional array with n evenly spaced elements from `start` to `end` (inclusive). - /// - /// # Example - /// ``` - /// # use feos_core::si::{Length, METER}; - /// # use ndarray::arr1; - /// # use approx::assert_relative_eq; - /// let x = Length::linspace(1.0 * METER, 3.0 * METER, 5); - /// assert_relative_eq!(x, &(arr1(&[1.0, 1.5, 2.0, 2.5, 3.0]) * METER)); - /// ``` - pub fn linspace(start: Quantity, end: Quantity, n: usize) -> Self { - Self(Array1::linspace(start.0, end.0, n), PhantomData) - } - - /// Create a one-dimensional array with n logarithmically spaced elements from `start` to `end` (inclusive). - /// - /// # Example - /// ``` - /// # use feos_core::si::{Length, METER}; - /// # use ndarray::arr1; - /// # use approx::assert_relative_eq; - /// let x = Length::logspace(1.0 * METER, 16.0 * METER, 5); - /// assert_relative_eq!(x, &(arr1(&[1.0, 2.0, 4.0, 8.0, 16.0]) * METER)); - /// ``` - pub fn logspace(start: Quantity, end: Quantity, n: usize) -> Self { - Self( - Array1::logspace(10.0, start.0.log10(), end.0.log10(), n), - PhantomData, - ) - } -} - -impl Quantity, U> { - /// Create an array with all elements set to 0. - pub fn zeros>(shape: Sh) -> Self { - Quantity(Array::zeros(shape), PhantomData) - } - - /// Create an array with values created by the function f. - pub fn from_shape_fn(shape: Sh, mut f: F) -> Self - where - Sh: ShapeBuilder, - F: FnMut(D::Pattern) -> Quantity, - { - Quantity(Array::from_shape_fn(shape, |x| f(x).0), PhantomData) - } -} - -impl, U, D: Dimension> Quantity, U> { - /// Return the total number of elements in the array. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Return whether the array has any elements - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Return the sum of all elements in the array. - /// - /// # Example - /// ``` - /// # use feos_core::si::BAR; - /// # use ndarray::arr1; - /// # use approx::assert_relative_eq; - /// let x = arr1(&[1.5, 2.5]) * BAR; - /// assert_relative_eq!(x.sum(), &(4.0 * BAR)); - /// ``` - pub fn sum(&self) -> Quantity { - Quantity(self.0.sum(), PhantomData) - } - - /// Return an uniquely owned copy of the array. - pub fn to_owned(&self) -> Quantity, U> { - Quantity(self.0.to_owned(), PhantomData) - } - - /// Return the shape of the array as a slice. - pub fn shape(&self) -> &[usize] { - self.0.shape() - } - - /// Return the shape of the array as it’s stored in the array. - pub fn raw_dim(&self) -> D { - self.0.raw_dim() - } - - /// Call f by value on each element and create a new array with the new values. - pub fn mapv(&self, mut f: F) -> Quantity, U2> - where - S: DataMut, - F: FnMut(Quantity) -> Quantity, - { - Quantity(self.0.mapv(|x| f(Quantity(x, PhantomData)).0), PhantomData) - } - - /// Returns a view restricted to index along the axis, with the axis removed. - pub fn index_axis( - &self, - axis: Axis, - index: usize, - ) -> Quantity, U> - where - D: RemoveAxis, - { - Quantity(self.0.index_axis(axis, index), PhantomData) - } - - /// Return a producer and iterable that traverses over all 1D lanes pointing in the direction of axis. - pub fn lanes_mut(&mut self, axis: Axis) -> LanesMut - where - S: DataMut, - { - self.0.lanes_mut(axis) - } - - /// Return sum along axis. - pub fn sum_axis(&self, axis: Axis) -> Quantity, U> - where - D: RemoveAxis, - { - Quantity(self.0.sum_axis(axis), PhantomData) - } - - /// Insert new array axis at axis and return the result. - pub fn insert_axis(self, axis: Axis) -> Quantity, U> { - Quantity(self.0.insert_axis(axis), PhantomData) - } - - /// Return the element at `index`. - /// - /// The `Index` trait can not be implemented, because a new instance has to be created, - /// when indexing a quantity array. This serves as replacement for it. - pub fn get>(&self, index: I) -> Quantity { - Quantity(self.0[index], PhantomData) - } - - /// Set the element at `index` to `scalar`. - pub fn set>(&mut self, index: I, value: Quantity) - where - S: DataMut, - { - self.0[index] = value.0; - } -} - -pub struct QuantityIter { - inner: I, - unit: PhantomData, -} - -impl<'a, I: Iterator, U: Copy> Iterator for QuantityIter { - type Item = Quantity; - - fn next(&mut self) -> Option { - self.inner.next().map(|value| Quantity(*value, PhantomData)) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -impl<'a, I: Iterator + ExactSizeIterator, U: Copy> ExactSizeIterator - for QuantityIter -{ - fn len(&self) -> usize { - self.inner.len() - } -} - -impl<'a, I: Iterator + DoubleEndedIterator, U: Copy> DoubleEndedIterator - for QuantityIter -{ - fn next_back(&mut self) -> Option { - self.inner - .next_back() - .map(|value| Quantity(*value, PhantomData)) - } -} - -impl<'a, F, U: Copy> IntoIterator for &'a Quantity -where - &'a F: IntoIterator, -{ - type Item = Quantity; - type IntoIter = QuantityIter<<&'a F as IntoIterator>::IntoIter, U>; - - fn into_iter(self) -> Self::IntoIter { - QuantityIter { - inner: self.0.into_iter(), - unit: PhantomData, - } - } -} - -impl FromIterator> for Quantity, U> { - fn from_iter(iter: I) -> Self - where - I: IntoIterator>, - { - Self(iter.into_iter().map(|v| v.0).collect(), PhantomData) - } -} diff --git a/feos-core/src/si/fmt.rs b/feos-core/src/si/fmt.rs deleted file mode 100644 index add0e072c..000000000 --- a/feos-core/src/si/fmt.rs +++ /dev/null @@ -1,199 +0,0 @@ -use super::*; -use ndarray::{Array, Dimension}; -use std::collections::HashMap; -use std::fmt; -use std::sync::LazyLock; -use typenum::{Quot, N1, N2, N3, P2, P3, P4}; - -const UNIT_SYMBOLS: [&str; 7] = ["s", "m", "kg", "A", "K", "mol", "cd"]; - -impl< - Inner: fmt::Debug, - T: Integer, - L: Integer, - M: Integer, - I: Integer, - THETA: Integer, - N: Integer, - J: Integer, - > fmt::Debug for Quantity> -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f)?; - let unit = [T::I8, L::I8, M::I8, I::I8, THETA::I8, N::I8, J::I8] - .iter() - .zip(UNIT_SYMBOLS.iter()) - .filter_map(|(&u, &s)| match u { - 0 => None, - 1 => Some(s.to_owned()), - _ => Some(format!("{s}^{u}")), - }) - .collect::>() - .join(" "); - - write!(f, " {}", unit) - } -} - -macro_rules! impl_fmt { - ($t:ident, $l:ident, $m:ident, $i:ident, $theta:ident, $n:ident, $unit:expr, $symbol:expr, $has_prefix:expr) => { - impl fmt::LowerExp for Quantity> - where - for<'a> &'a T: Div, - for<'a> Quot<&'a T, f64>: fmt::LowerExp, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self / $unit).into_value().fmt(f)?; - write!(f, " {}", $symbol) - } - } - - impl fmt::UpperExp for Quantity> - where - for<'a> &'a T: Div, - for<'a> Quot<&'a T, f64>: fmt::UpperExp, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self / $unit).into_value().fmt(f)?; - write!(f, " {}", $symbol) - } - } - - impl fmt::Display - for Quantity, SIUnit<$t, $l, $m, $i, $theta, $n, Z0>> - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self / $unit).into_value().fmt(f)?; - write!(f, " {}", $symbol) - } - } - - impl fmt::Display for Quantity> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (value, prefix) = get_prefix((self / $unit).into_value(), $has_prefix); - if !((1e-2..1e4).contains(&value.abs()) || value == 0.0) { - write!(f, "{:e} {}{}", value, prefix, $symbol) - } else { - value.fmt(f)?; - write!(f, " {}{}", prefix, $symbol) - } - } - } - }; -} - -impl_fmt!(P1, Z0, Z0, Z0, Z0, Z0, SECOND, "s", Some(KILO)); -impl_fmt!(Z0, P1, Z0, Z0, Z0, Z0, METER, "m", Some(MEGA)); -impl_fmt!(Z0, Z0, P1, Z0, Z0, Z0, GRAM, "g", Some(MEGA)); -impl_fmt!(Z0, Z0, Z0, Z0, Z0, P1, MOL, "mol", Some(MEGA)); -impl_fmt!(Z0, Z0, Z0, Z0, P1, Z0, KELVIN, "K", None); -impl_fmt!(N1, Z0, Z0, Z0, Z0, Z0, HERTZ, "Hz", Some(PETA)); -impl_fmt!(N2, P1, P1, Z0, Z0, Z0, NEWTON, "N", Some(PETA)); -impl_fmt!(N2, N1, P1, Z0, Z0, Z0, PASCAL, "Pa", Some(PETA)); -impl_fmt!(N2, P2, P1, Z0, Z0, Z0, JOULE, "J", Some(PETA)); -impl_fmt!(N3, P2, P1, Z0, Z0, Z0, WATT, "W", Some(PETA)); -impl_fmt!(P1, Z0, Z0, P1, Z0, Z0, COULOMB, "C", None); -impl_fmt!(N3, P2, P1, N1, Z0, Z0, VOLT, "V", Some(PETA)); -impl_fmt!(P4, N2, N1, P2, Z0, Z0, FARAD, "F", Some(PETA)); -impl_fmt!(N3, P2, P1, N2, Z0, Z0, OHM, "Ω", Some(PETA)); -impl_fmt!(P3, N2, N1, P2, Z0, Z0, SIEMENS, "S", Some(PETA)); -impl_fmt!(N2, P2, P1, N1, Z0, Z0, WEBER, "Wb", Some(PETA)); -impl_fmt!(N2, Z0, P1, N1, Z0, Z0, TESLA, "T", Some(PETA)); -impl_fmt!(N2, P2, P1, N2, Z0, Z0, HENRY, "H", Some(PETA)); - -const M2: Area = Quantity(1.0, PhantomData); -const M3: Volume = Quantity(1.0, PhantomData); -const KG: Mass = KILOGRAM; -const JMK: MolarEntropy = Quantity(1.0, PhantomData); -const JKGK: SpecificEntropy = Quantity(1.0, PhantomData); -const WMK: ThermalConductivity = Quantity(1.0, PhantomData); - -impl_fmt!(Z0, N3, Z0, Z0, Z0, P1, MOL / M3, "mol/m³", Some(MEGA)); -impl_fmt!(Z0, N2, Z0, Z0, Z0, P1, MOL / M2, "mol/m²", Some(MEGA)); -impl_fmt!(Z0, N1, Z0, Z0, Z0, P1, MOL / METER, "mol/m", Some(MEGA)); -impl_fmt!(Z0, P3, Z0, Z0, Z0, N1, M3 / MOL, "m³/mol", None); -impl_fmt!(Z0, P3, Z0, Z0, N1, N1, M3 / MOL / KELVIN, "m³/mol/K", None); -impl_fmt!(Z0, N3, P1, Z0, Z0, Z0, GRAM / M3, "g/m³", Some(MEGA)); -impl_fmt!(N2, Z0, P1, Z0, Z0, Z0, NEWTON / METER, "N/m", Some(PETA)); -impl_fmt!(N1, P2, P1, Z0, Z0, Z0, JOULE * SECOND, "J*s", Some(PETA)); -impl_fmt!(N2, P2, P1, Z0, Z0, N1, JOULE / MOL, "J/mol", Some(PETA)); -impl_fmt!(N2, P2, P1, Z0, N1, Z0, JOULE / KELVIN, "J/K", Some(PETA)); -impl_fmt!(N2, P2, P1, Z0, N1, N1, JMK, "J/mol/K", Some(PETA)); -impl_fmt!(N2, P2, Z0, Z0, Z0, Z0, JOULE / KG, "J/kg", Some(PETA)); -impl_fmt!(N2, P2, Z0, Z0, N1, Z0, JKGK, "J/kg/K", Some(PETA)); -impl_fmt!(N1, N1, P1, Z0, Z0, Z0, PASCAL * SECOND, "Pa*s", Some(PETA)); -impl_fmt!(N1, P1, Z0, Z0, Z0, Z0, METER / SECOND, "m/s", Some(MEGA)); -impl_fmt!(N1, P2, Z0, Z0, Z0, Z0, M2 / SECOND, "m²/s", None); -impl_fmt!(N3, P1, P1, Z0, N1, Z0, WMK, "W/m/K", Some(PETA)); -impl_fmt!(Z0, Z0, P1, Z0, Z0, N1, GRAM / MOL, "g/mol", Some(MEGA)); -impl_fmt!(Z0, P2, Z0, Z0, Z0, Z0, M2, "m²", None); -impl_fmt!(Z0, P3, Z0, Z0, Z0, Z0, M3, "m³", None); -impl_fmt!(N1, P3, N1, Z0, Z0, Z0, M3 / KG / SECOND, "m³/kg/s²", None); - -fn get_prefix(value: f64, has_prefix: Option) -> (f64, &'static str) { - if let Some(p) = has_prefix { - let abs_value = value.abs(); - let e: i8 = if abs_value > PICO && abs_value < p { - (abs_value.log10().floor() as i8).div_euclid(3) * 3 - } else { - 0 - }; - let prefix = 10.0f64.powi(e as i32); - return (value / prefix, PREFIX_SYMBOLS.get(&e).unwrap()); - } - (value, "") -} - -static PREFIX_SYMBOLS: LazyLock> = LazyLock::new(|| { - let mut m = HashMap::new(); - m.insert(0, " "); - m.insert(-24, "y"); - m.insert(-21, "z"); - m.insert(-18, "a"); - m.insert(-15, "f"); - m.insert(-12, "p"); - m.insert(-9, "n"); - m.insert(-6, "µ"); - m.insert(-3, "m"); - m.insert(3, "k"); - m.insert(6, "M"); - m.insert(9, "G"); - m.insert(12, "T"); - m.insert(15, "P"); - m.insert(18, "E"); - m.insert(21, "Z"); - m.insert(24, "Y"); - m -}); - -#[cfg(test)] -mod tests { - use crate::si::*; - use ndarray::arr1; - - #[test] - fn test_fmt_si() { - assert_eq!(format!("{:.3}", RGAS), "8.314 J/mol/K"); - } - - #[test] - fn test_fmt_exp() { - assert_eq!(format!("{:e}", PICO * METER), "1e-12 m"); - assert_eq!(format!("{:E}", 50.0 * KILO * GRAM), "5E4 g"); - } - - #[test] - fn test_fmt_arr() { - assert_eq!( - format!("{}", arr1(&[273.15, 323.15]) * KELVIN), - "[273.15, 323.15] K" - ); - assert_eq!(format!("{:e}", arr1(&[3.0, 5.0]) * BAR), "[3e5, 5e5] Pa"); - } - - #[test] - fn test_fmt_zero() { - assert_eq!(format!("{}", 0.0 * KELVIN), "0 K"); - assert_eq!(format!("{:.2}", 0.0 * PASCAL), "0.00 Pa"); - } -} diff --git a/feos-core/src/si/mod.rs b/feos-core/src/si/mod.rs deleted file mode 100644 index 4b67b26f9..000000000 --- a/feos-core/src/si/mod.rs +++ /dev/null @@ -1,383 +0,0 @@ -//! Physical quantities with compile-time checked units. - -#![allow(clippy::type_complexity)] -use ang::{Angle, Degrees, Radians}; -use ndarray::{Array, ArrayBase, Data, Dimension}; -use num_traits::Zero; -use std::marker::PhantomData; -use std::ops::{Div, Mul, Sub}; -use typenum::{ATerm, Diff, Integer, Negate, Quot, Sum, TArr, N1, N2, P1, P3, Z0}; - -mod array; -mod fmt; -mod ops; -#[cfg(feature = "python")] -mod python; - -pub type SIUnit = - TArr>>>>>>; - -/// Physical quantity with compile-time checked unit. -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct Quantity(T, PhantomData); - -pub type _Dimensionless = SIUnit; -pub type _Time = SIUnit; -pub type _Length = SIUnit; -pub type _Mass = SIUnit; -pub type _Current = SIUnit; -pub type _Temperature = SIUnit; -pub type _Moles = SIUnit; -pub type _LuminousIntensity = SIUnit; - -pub type Dimensionless = Quantity; -pub type Time = Quantity; -pub type Length = Quantity; -pub type Mass = Quantity; -pub type Current = Quantity; -pub type Temperature = Quantity; -pub type Moles = Quantity; -pub type LuminousIntensity = Quantity; - -pub type _Frequency = Negate<_Time>; -pub type Frequency = Quantity; -pub type _Velocity = Diff<_Length, _Time>; -pub type Velocity = Quantity; -pub type _Acceleration = Diff<_Velocity, _Time>; -pub type Acceleration = Quantity; -pub type _Force = Sum<_Mass, _Acceleration>; -pub type Force = Quantity; -pub type _Area = Sum<_Length, _Length>; -pub type Area = Quantity; -pub type _Volume = Sum<_Area, _Length>; -pub type Volume = Quantity; -pub type _Energy = Sum<_Force, _Length>; -pub type Energy = Quantity; -pub type _Pressure = Diff<_Energy, _Volume>; -pub type Pressure = Quantity; -pub type _Power = Diff<_Energy, _Time>; -pub type Power = Quantity; -pub type _Charge = Sum<_Current, _Time>; -pub type Charge = Quantity; -pub type _ElectricPotential = Diff<_Power, _Current>; -pub type ElectricPotential = Quantity; -pub type _Capacitance = Diff<_Charge, _ElectricPotential>; -pub type Capacitance = Quantity; -pub type _Resistance = Diff<_ElectricPotential, _Current>; -pub type Resistance = Quantity; -pub type _ElectricalConductance = Negate<_Resistance>; -pub type ElectricalConductance = Quantity; -pub type _MagneticFlux = Sum<_ElectricPotential, _Time>; -pub type MagneticFlux = Quantity; -pub type _MagneticFluxDensity = Diff<_MagneticFlux, _Area>; -pub type MagneticFluxDensity = Quantity; -pub type _Inductance = Diff<_MagneticFlux, _Current>; -pub type Inductance = Quantity; - -pub type _Entropy = Diff<_Energy, _Temperature>; -pub type Entropy = Quantity; -pub type _EntropyPerTemperature = Diff<_Entropy, _Temperature>; -pub type EntropyPerTemperature = Quantity; -pub type _MolarEntropy = Diff<_Entropy, _Moles>; -pub type MolarEntropy = Quantity; -pub type _MolarEnergy = Diff<_Energy, _Moles>; -pub type MolarEnergy = Quantity; -pub type _SpecificEntropy = Diff<_Entropy, _Mass>; -pub type SpecificEntropy = Quantity; -pub type _SpecificEnergy = Diff<_Energy, _Mass>; -pub type SpecificEnergy = Quantity; -pub type _MolarWeight = Diff<_Mass, _Moles>; -pub type MolarWeight = Quantity; -pub type _Density = Diff<_Moles, _Volume>; -pub type Density = Quantity; -pub type _MassDensity = Diff<_Mass, _Volume>; -pub type MassDensity = Quantity; -pub type _PressurePerVolume = Diff<_Pressure, _Volume>; -pub type PressurePerVolume = Quantity; -pub type _PressurePerTemperature = Diff<_Pressure, _Temperature>; -pub type PressurePerTemperature = Quantity; -pub type _Compressibility = Negate<_Pressure>; -pub type Compressibility = Quantity; -pub type _MolarVolume = Diff<_Volume, _Moles>; -pub type MolarVolume = Quantity; -pub type _EntropyDensity = Diff<_Entropy, _Volume>; -pub type EntropyDensity = Quantity; -pub type _Action = Sum<_Energy, _Time>; -pub type Action = Quantity; -pub type _HeatCapacityRate = Diff<_Power, _Temperature>; -pub type HeatCapacityRate = Quantity; -pub type _MassFlowRate = Diff<_Mass, _Time>; -pub type MassFlowRate = Quantity; -pub type _MoleFlowRate = Diff<_Moles, _Time>; -pub type MoleFlowRate = Quantity; - -pub type _Viscosity = Sum<_Pressure, _Time>; -pub type Viscosity = Quantity; -pub type _Diffusivity = Sum<_Velocity, _Length>; -pub type Diffusivity = Quantity; -pub type _ThermalConductivity = Diff<_Power, Sum<_Length, _Temperature>>; -pub type ThermalConductivity = Quantity; -pub type _SurfaceTension = Diff<_Force, _Length>; -pub type SurfaceTension = Quantity; - -/// SI base unit second $\\left(\text{s}\\right)$ -pub const SECOND: Time = Quantity(1.0, PhantomData); -/// SI base unit meter $\\left(\text{m}\\right)$ -pub const METER: Length = Quantity(1.0, PhantomData); -/// SI base unit kilogram $\\left(\text{kg}\\right)$ -pub const KILOGRAM: Mass = Quantity(1.0, PhantomData); -/// SI base unit Ampere $\\left(\text{A}\\right)$ -pub const AMPERE: Current = Quantity(1.0, PhantomData); -/// SI base unit Kelvin $\\left(\text{K}\\right)$ -pub const KELVIN: Temperature = Quantity(1.0, PhantomData); -/// SI base unit mol $\\left(\text{mol}\\right)$ -pub const MOL: Moles = Quantity(1.0, PhantomData); -/// SI base unit candela $\\left(\text{cd}\\right)$ -pub const CANDELA: LuminousIntensity = Quantity(1.0, PhantomData); - -/// Derived unit Hertz $\\left(1\\,\text{Hz}=1\\,\text{s}^{-1}\\right)$ -pub const HERTZ: Frequency = Quantity(1.0, PhantomData); -/// Derived unit Newton $\\left(1\\,\text{N}=1\\,\text{kg}\\frac{\text{m}}{\text{s}^2}\\right)$ -pub const NEWTON: Force = Quantity(1.0, PhantomData); -/// Derived unit Pascal $\\left(1\\,\text{Pa}=1\\,\\frac{\text{kg}}{\text{m}\\cdot\text{s}^2}\\right)$ -pub const PASCAL: Pressure = Quantity(1.0, PhantomData); -/// Derived unit Joule $\\left(1\\,\text{J}=1\\,\text{kg}\\frac{\text{m}^2}{\text{s}^2}\\right)$ -pub const JOULE: Energy = Quantity(1.0, PhantomData); -/// Derived unit Watt $\\left(1\\,\text{J}=1\\,\text{kg}\\frac{\text{m}^2}{\text{s}^3}\\right)$ -pub const WATT: Power = Quantity(1.0, PhantomData); -/// Derived unit Coulomb $\\left(1\\,\text{C}=1\\,\text{A}\cdot\text{s}\\right)$ -pub const COULOMB: Charge = Quantity(1.0, PhantomData); -/// Derived unit Volt $\\left(1\\,\text{V}=1\\,\\frac{\text{W}}{\text{A}}\\right)$ -pub const VOLT: ElectricPotential = Quantity(1.0, PhantomData); -/// Derived unit Farad $\\left(1\\,\text{F}=1\\,\\frac{\text{C}}{\text{V}}\\right)$ -pub const FARAD: Capacitance = Quantity(1.0, PhantomData); -/// Derived unit Ohm $\\left(1\\,\text{Ω}=1\\,\\frac{\text{V}}{\text{A}}\\right)$ -pub const OHM: Resistance = Quantity(1.0, PhantomData); -/// Derived unit Siemens $\\left(1\\,\text{S}=1\\,\text{Ω}^{-1}\\right)$ -pub const SIEMENS: ElectricalConductance = Quantity(1.0, PhantomData); -/// Derived unit Weber $\\left(1\\,\text{Wb}=1\\,\text{V}\\cdot\text{s}\\right)$ -pub const WEBER: MagneticFlux = Quantity(1.0, PhantomData); -/// Derived unit Tesla $\\left(1\\,\text{T}=1\\,\\frac{\text{Wb}}{\text{m}^2}\\right)$ -pub const TESLA: MagneticFluxDensity = Quantity(1.0, PhantomData); -/// Derived unit Henry $\\left(1\\,\text{T}=1\\,\\frac{\text{Wb}}{\text{A}}\\right)$ -pub const HENRY: Inductance = Quantity(1.0, PhantomData); - -/// Additional unit Ångstrom $\\left(1\\,\text{\\AA}=10^{-10}\\,\text{m}\\right)$ -pub const ANGSTROM: Length = Quantity(1e-10, PhantomData); -/// Additional unit unified atomic mass $\\left(1\\,\text{u}\\approx 1.660539\\times 10^{-27}\\,\text{kg}\\right)$ -pub const AMU: Mass = Quantity(1.6605390671738466e-27, PhantomData); -/// Additional unit astronomical unit $\\left(1\\,\text{au}=149597870700\\,\text{m}\\right)$ -pub const AU: Length = Quantity(149597870700.0, PhantomData); -/// Additional unit bar $\\left(1\\,\text{bar}=10^5\\,\text{Pa}\\right)$ -pub const BAR: Pressure = Quantity(1e5, PhantomData); -/// Additional unit calorie $\\left(1\\,\text{cal}=4.184\\,\text{J}\\right)$ -pub const CALORIE: Energy = Quantity(4.184, PhantomData); -/// Additional unit day $\\left(1\\,\text{d}=86400,\text{s}\\right)$ -pub const DAY: Time = Quantity(86400.0, PhantomData); -/// Additional unit gram $\\left(1\\,\text{g}=10^{-3}\\,\text{kg}\\right)$ -pub const GRAM: Mass = Quantity(1e-3, PhantomData); -/// Additional unit hour $\\left(1\\,\text{h}=3600,\text{s}\\right)$ -pub const HOUR: Time = Quantity(3600.0, PhantomData); -/// Additional unit liter $\\left(1\\,\text{l}=10^{-3}\\,\text{m}^3\\right)$ -pub const LITER: Volume = Quantity(1e-3, PhantomData); -/// Additional unit minute $\\left(1\\,\text{min}=60,\text{s}\\right)$ -pub const MINUTE: Time = Quantity(60.0, PhantomData); - -/// Angle unit radian $\\left(\text{rad}\\right)$ -pub const RADIANS: Angle = Radians(1.0); -/// Angle unit degree $\\left(1^\\circ=\frac{\pi}{180}\\,\text{rad}\\approx 0.0174532925\\,\text{rad}\\right)$ -pub const DEGREES: Angle = Degrees(1.0); - -/// Boltzmann constant $\\left(k_\text{B}=1.380649\times 10^{-23}\\,\\frac{\text{J}}{\text{K}}\\right)$ -pub const KB: Entropy = Quantity(1.380649e-23, PhantomData); -/// Avogadro constant $\\left(N_\text{A}=6.02214076\times 10^{23}\\,\text{mol}^{-1}\\right)$ -pub const NAV: Quantity> = Quantity(6.02214076e23, PhantomData); -/// Planck constant $\\left(h=6.62607015\times 10^{-34}\\,\text{J}\\cdot\text{s}\\right)$ -pub const PLANCK: Action = Quantity(6.62607015e-34, PhantomData); -/// Ideal gas constant $\\left(R=8.31446261815324\\,\\frac{\text{J}}{\text{molK}}\\right)$ -pub const RGAS: MolarEntropy = Quantity(8.31446261815324, PhantomData); -/// Hyperfine transition frequency of Cs $\\left(\Delta\\nu_\text{Cs}=9192631770\\,\text{Hz}\\right)$ -pub const DVCS: Frequency = Quantity(9192631770.0, PhantomData); -/// Elementary charge $\\left(e=1.602176634\\times 10^{-19}\\,\text{C}\\right)$ -pub const QE: Charge = Quantity(1.602176634e-19, PhantomData); -/// Speed of light $\\left(c=299792458\\,\\frac{\text{m}}{\text{s}}\\right)$ -pub const CLIGHT: Velocity = Quantity(299792458.0, PhantomData); -/// Luminous efficacy of $540\\,\text{THz}$ radiation $\\left(K_\text{cd}=683\\,\\frac{\text{lm}}{\text{W}}\\right)$ -pub const KCD: Quantity> = Quantity(683.0, PhantomData); -/// Gravitational constant $\\left(G=6.6743\\times 10^{-11}\\,\\frac{\text{m}^3}{\text{kg}\cdot\text{s}^2}\\right)$ -pub const G: Quantity> = Quantity(6.6743e-11, PhantomData); - -/// Prefix quecto $\\left(\text{q}=10^{-30}\\right)$ -pub const QUECTO: f64 = 1e-30; -/// Prefix ronto $\\left(\text{r}=10^{-27}\\right)$ -pub const RONTO: f64 = 1e-27; -/// Prefix yocto $\\left(\text{y}=10^{-24}\\right)$ -pub const YOCTO: f64 = 1e-24; -/// Prefix zepto $\\left(\text{z}=10^{-21}\\right)$ -pub const ZEPTO: f64 = 1e-21; -/// Prefix atto $\\left(\text{a}=10^{-18}\\right)$ -pub const ATTO: f64 = 1e-18; -/// Prefix femto $\\left(\text{f}=10^{-15}\\right)$ -pub const FEMTO: f64 = 1e-15; -/// Prefix pico $\\left(\text{p}=10^{-12}\\right)$ -pub const PICO: f64 = 1e-12; -/// Prefix nano $\\left(\text{n}=10^{-9}\\right)$ -pub const NANO: f64 = 1e-9; -/// Prefix micro $\\left(\text{µ}=10^{-6}\\right)$ -pub const MICRO: f64 = 1e-6; -/// Prefix milli $\\left(\text{m}=10^{-3}\\right)$ -pub const MILLI: f64 = 1e-3; -/// Prefix centi $\\left(\text{c}=10^{-2}\\right)$ -pub const CENTI: f64 = 1e-2; -/// Prefix deci $\\left(\text{d}=10^{-1}\\right)$ -pub const DECI: f64 = 1e-1; -/// Prefix deca $\\left(\text{da}=10^{1}\\right)$ -pub const DECA: f64 = 1e1; -/// Prefix hecto $\\left(\text{h}=10^{2}\\right)$ -pub const HECTO: f64 = 1e2; -/// Prefix kilo $\\left(\text{k}=10^{3}\\right)$ -pub const KILO: f64 = 1e3; -/// Prefix mega $\\left(\text{M}=10^{6}\\right)$ -pub const MEGA: f64 = 1e6; -/// Prefix giga $\\left(\text{G}=10^{9}\\right)$ -pub const GIGA: f64 = 1e9; -/// Prefix tera $\\left(\text{T}=10^{12}\\right)$ -pub const TERA: f64 = 1e12; -/// Prefix peta $\\left(\text{P}=10^{15}\\right)$ -pub const PETA: f64 = 1e15; -/// Prefix exa $\\left(\text{E}=10^{18}\\right)$ -pub const EXA: f64 = 1e18; -/// Prefix zetta $\\left(\text{Z}=10^{21}\\right)$ -pub const ZETTA: f64 = 1e21; -/// Prefix yotta $\\left(\text{Y}=10^{24}\\right)$ -pub const YOTTA: f64 = 1e24; -/// Prefix ronna $\\left(\text{R}=10^{27}\\right)$ -pub const RONNA: f64 = 1e27; -/// Prefix quetta $\\left(\text{Q}=10^{30}\\right)$ -pub const QUETTA: f64 = 1e30; - -/// Additional unit degrees Celsius -pub struct CELSIUS; - -impl Mul for f64 { - type Output = Temperature; - #[allow(clippy::suspicious_arithmetic_impl)] - fn mul(self, _: CELSIUS) -> Temperature { - Quantity(self + 273.15, PhantomData) - } -} - -impl, D: Dimension> Mul for ArrayBase { - type Output = Temperature>; - #[allow(clippy::suspicious_arithmetic_impl)] - fn mul(self, _: CELSIUS) -> Temperature> { - Quantity(&self + 273.15, PhantomData) - } -} - -impl Div for Temperature { - type Output = f64; - #[allow(clippy::suspicious_arithmetic_impl)] - fn div(self, _: CELSIUS) -> Self::Output { - self.0 - 273.15 - } -} - -impl Div for Temperature> { - type Output = Array; - #[allow(clippy::suspicious_arithmetic_impl)] - fn div(self, _: CELSIUS) -> Self::Output { - self.0 - 273.15 - } -} - -impl Dimensionless { - /// Return the value of a dimensionless quantity. - pub fn into_value(self) -> T { - self.0 - } -} - -impl Quantity { - /// Convert a quantity into the given unit and return it - /// as a float or array. - pub fn convert_into(self, unit: Quantity) -> Quot - where - T: Div, - U: Sub, - { - (self / unit).into_value() - } -} - -impl From for Dimensionless { - fn from(value: T) -> Self { - Quantity(value, PhantomData) - } -} - -impl Zero for Quantity { - fn zero() -> Self { - Quantity(0.0, PhantomData) - } - - fn is_zero(&self) -> bool { - self.0.is_zero() - } -} - -/// Reference values used for reduced properties in feos -const REFERENCE_VALUES: [f64; 7] = [ - 1e-12, // 1 ps - 1e-10, // 1 Å - 1.380649e-27, // Fixed through k_B - 1.0, // 1 A - 1.0, // 1 K - 1.0 / 6.02214076e23, // 1/N_AV - 1.0, // 1 Cd -]; - -/// Conversion to and from reduced units -impl< - Inner, - T: Integer, - L: Integer, - M: Integer, - I: Integer, - THETA: Integer, - N: Integer, - J: Integer, - > Quantity> -{ - pub fn from_reduced(value: Inner) -> Self - where - Inner: Mul, - { - Self( - value - * (REFERENCE_VALUES[0].powi(T::I32) - * REFERENCE_VALUES[1].powi(L::I32) - * REFERENCE_VALUES[2].powi(M::I32) - * REFERENCE_VALUES[3].powi(I::I32) - * REFERENCE_VALUES[4].powi(THETA::I32) - * REFERENCE_VALUES[5].powi(N::I32) - * REFERENCE_VALUES[6].powi(J::I32)), - PhantomData, - ) - } - - pub fn to_reduced<'a>(&'a self) -> Inner - where - &'a Inner: Div, - { - &self.0 - / (REFERENCE_VALUES[0].powi(T::I32) - * REFERENCE_VALUES[1].powi(L::I32) - * REFERENCE_VALUES[2].powi(M::I32) - * REFERENCE_VALUES[3].powi(I::I32) - * REFERENCE_VALUES[4].powi(THETA::I32) - * REFERENCE_VALUES[5].powi(N::I32) - * REFERENCE_VALUES[6].powi(J::I32)) - } -} diff --git a/feos-core/src/si/ops.rs b/feos-core/src/si/ops.rs deleted file mode 100644 index 4344745da..000000000 --- a/feos-core/src/si/ops.rs +++ /dev/null @@ -1,507 +0,0 @@ -use super::Quantity; -use approx::{AbsDiffEq, RelativeEq}; -use ndarray::{Array, ArrayBase, Data, DataMut, DataOwned, Dimension}; -use std::marker::PhantomData; -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use typenum::{Diff, Integer, Negate, Prod, Quot, Sum, P2, P3}; - -// Multiplication -impl Mul> for Quantity -where - T1: Mul, - U1: Add, -{ - type Output = Quantity, Sum>; - fn mul(self, other: Quantity) -> Self::Output { - Quantity(self.0 * other.0, PhantomData) - } -} - -impl<'a, T1, T2, U1, U2> Mul> for &'a Quantity -where - &'a T1: Mul, - U1: Add, -{ - type Output = Quantity, Sum>; - fn mul(self, other: Quantity) -> Self::Output { - Quantity(&self.0 * other.0, PhantomData) - } -} - -impl<'b, T1, T2, U1, U2> Mul<&'b Quantity> for Quantity -where - T1: Mul<&'b T2>, - U1: Add, -{ - type Output = Quantity, Sum>; - fn mul(self, other: &'b Quantity) -> Self::Output { - Quantity(self.0 * &other.0, PhantomData) - } -} - -impl<'a, 'b, T1, T2, U1, U2> Mul<&'b Quantity> for &'a Quantity -where - &'a T1: Mul<&'b T2>, - U1: Add, -{ - type Output = Quantity, Sum>; - fn mul(self, other: &'b Quantity) -> Self::Output { - Quantity(&self.0 * &other.0, PhantomData) - } -} - -impl Mul> for f64 -where - f64: Mul, -{ - type Output = Quantity, U>; - fn mul(self, other: Quantity) -> Self::Output { - Quantity(self * other.0, PhantomData) - } -} - -impl, U> Mul for Quantity { - type Output = Quantity, U>; - fn mul(self, other: f64) -> Self::Output { - Quantity(self.0 * other, PhantomData) - } -} - -impl<'a, T, U> Mul for &'a Quantity -where - &'a T: Mul, -{ - type Output = Quantity, U>; - fn mul(self, other: f64) -> Self::Output { - Quantity(&self.0 * other, PhantomData) - } -} - -impl, D: Dimension> Mul> for &ArrayBase { - type Output = Quantity, U>; - fn mul(self, other: Quantity) -> Self::Output { - Quantity(self * other.0, PhantomData) - } -} - -impl + DataMut, D: Dimension> Mul> - for ArrayBase -{ - type Output = Quantity, U>; - fn mul(self, other: Quantity) -> Self::Output { - Quantity(self * other.0, PhantomData) - } -} - -impl MulAssign for Quantity -where - T1: MulAssign, -{ - fn mul_assign(&mut self, other: T2) { - self.0 *= other; - } -} - -// Division -impl Div> for Quantity -where - T1: Div, - U1: Sub, -{ - type Output = Quantity, Diff>; - fn div(self, other: Quantity) -> Self::Output { - Quantity(self.0 / other.0, PhantomData) - } -} - -impl<'a, T1, T2, U1, U2> Div> for &'a Quantity -where - &'a T1: Div, - U1: Sub, -{ - type Output = Quantity, Diff>; - fn div(self, other: Quantity) -> Self::Output { - Quantity(&self.0 / other.0, PhantomData) - } -} - -impl<'b, T1, T2, U1, U2> Div<&'b Quantity> for Quantity -where - T1: Div<&'b T2>, - U1: Sub, -{ - type Output = Quantity, Diff>; - fn div(self, other: &'b Quantity) -> Self::Output { - Quantity(self.0 / &other.0, PhantomData) - } -} - -impl<'a, 'b, T1, T2, U1, U2> Div<&'b Quantity> for &'a Quantity -where - &'a T1: Div<&'b T2>, - U1: Sub, -{ - type Output = Quantity, Diff>; - fn div(self, other: &'b Quantity) -> Self::Output { - Quantity(&self.0 / &other.0, PhantomData) - } -} - -impl Div> for f64 -where - U: Neg, - f64: Div, -{ - type Output = Quantity, Negate>; - fn div(self, other: Quantity) -> Self::Output { - Quantity(self / other.0, PhantomData) - } -} - -impl, U> Div for Quantity { - type Output = Quantity, U>; - fn div(self, other: f64) -> Self::Output { - Quantity(self.0 / other, PhantomData) - } -} - -impl<'a, T, U> Div for &'a Quantity -where - &'a T: Div, -{ - type Output = Quantity, U>; - fn div(self, other: f64) -> Self::Output { - Quantity(&self.0 / other, PhantomData) - } -} - -impl, D: Dimension> Div> for &ArrayBase { - type Output = Quantity, Negate>; - fn div(self, other: Quantity) -> Self::Output { - Quantity(self / other.0, PhantomData) - } -} - -impl + DataMut, D: Dimension> Div> - for ArrayBase -{ - type Output = Quantity, Negate>; - fn div(self, other: Quantity) -> Self::Output { - Quantity(self / other.0, PhantomData) - } -} - -impl DivAssign for Quantity -where - T1: DivAssign, -{ - fn div_assign(&mut self, other: T2) { - self.0 /= other; - } -} - -// Addition -impl Add> for Quantity -where - T1: Add, -{ - type Output = Quantity, U>; - fn add(self, other: Quantity) -> Self::Output { - Quantity(self.0 + other.0, PhantomData) - } -} - -impl<'a, T1, T2, U> Add> for &'a Quantity -where - &'a T1: Add, -{ - type Output = Quantity, U>; - fn add(self, other: Quantity) -> Self::Output { - Quantity(&self.0 + other.0, PhantomData) - } -} - -impl<'b, T1, T2, U> Add<&'b Quantity> for Quantity -where - T1: Add<&'b T2>, -{ - type Output = Quantity, U>; - fn add(self, other: &'b Quantity) -> Self::Output { - Quantity(self.0 + &other.0, PhantomData) - } -} - -impl<'a, 'b, T1, T2, U> Add<&'b Quantity> for &'a Quantity -where - &'a T1: Add<&'b T2>, -{ - type Output = Quantity, U>; - fn add(self, other: &'b Quantity) -> Self::Output { - Quantity(&self.0 + &other.0, PhantomData) - } -} - -impl AddAssign> for Quantity -where - T1: AddAssign, -{ - fn add_assign(&mut self, rhs: Quantity) { - self.0 += rhs.0; - } -} - -impl<'a, T1, T2, U> AddAssign<&'a Quantity> for Quantity -where - T1: AddAssign<&'a T2>, -{ - fn add_assign(&mut self, rhs: &'a Quantity) { - self.0 += &rhs.0; - } -} - -// Subtraction -impl Sub> for Quantity -where - T1: Sub, -{ - type Output = Quantity, U>; - fn sub(self, other: Quantity) -> Self::Output { - Quantity(self.0 - other.0, PhantomData) - } -} - -impl<'a, T1, T2, U> Sub> for &'a Quantity -where - &'a T1: Sub, -{ - type Output = Quantity, U>; - fn sub(self, other: Quantity) -> Self::Output { - Quantity(&self.0 - other.0, PhantomData) - } -} - -impl<'b, T1, T2, U> Sub<&'b Quantity> for Quantity -where - T1: Sub<&'b T2>, -{ - type Output = Quantity, U>; - fn sub(self, other: &'b Quantity) -> Self::Output { - Quantity(self.0 - &other.0, PhantomData) - } -} - -impl<'a, 'b, T1, T2, U> Sub<&'b Quantity> for &'a Quantity -where - &'a T1: Sub<&'b T2>, -{ - type Output = Quantity, U>; - fn sub(self, other: &'b Quantity) -> Self::Output { - Quantity(&self.0 - &other.0, PhantomData) - } -} - -impl SubAssign> for Quantity -where - T1: SubAssign, -{ - fn sub_assign(&mut self, rhs: Quantity) { - self.0 -= rhs.0; - } -} - -impl<'a, T1, T2, U> SubAssign<&'a Quantity> for Quantity -where - T1: SubAssign<&'a T2>, -{ - fn sub_assign(&mut self, rhs: &'a Quantity) { - self.0 -= &rhs.0; - } -} - -// Negation -impl Neg for Quantity -where - T: Neg, -{ - type Output = Quantity, U>; - fn neg(self) -> Self::Output { - Quantity(-self.0, PhantomData) - } -} - -impl Quantity { - /// Calculate the integer power of self. - /// - /// # Example - /// ``` - /// # use feos_core::si::METER; - /// # use ndarray::arr1; - /// # use approx::assert_relative_eq; - /// # use typenum::P2; - /// let x = 3.0 * METER; - /// assert_relative_eq!(x.powi::(), 9.0 * METER * METER); - /// ``` - pub fn powi(self) -> Quantity> - where - U: Mul, - { - Quantity(self.0.powi(E::I32), PhantomData) - } - - /// Calculate the square root of self. - /// - /// # Example - /// ``` - /// # use feos_core::si::METER; - /// # use ndarray::arr1; - /// # use approx::assert_relative_eq; - /// let x = 9.0 * METER * METER; - /// assert_relative_eq!(x.sqrt(), 3.0 * METER); - /// ``` - pub fn sqrt(self) -> Quantity> - where - U: Div, - { - Quantity(self.0.sqrt(), PhantomData) - } - - /// Calculate the cubic root of self. - /// - /// # Example - /// ``` - /// # use feos_core::si::METER; - /// # use ndarray::arr1; - /// # use approx::assert_relative_eq; - /// let x = 27.0 * METER * METER * METER; - /// assert_relative_eq!(x.cbrt(), 3.0 * METER); - /// ``` - pub fn cbrt(self) -> Quantity> - where - U: Div, - { - Quantity(self.0.cbrt(), PhantomData) - } - - /// Calculate the integer root of self. - /// - /// # Example - /// ``` - /// # use feos_core::si::METER; - /// # use ndarray::arr1; - /// # use approx::assert_relative_eq; - /// # use typenum::P4; - /// let x = 81.0 * METER * METER * METER * METER; - /// assert_relative_eq!(x.root::(), 3.0 * METER); - /// ``` - pub fn root(self) -> Quantity> - where - U: Div, - { - Quantity(self.0.powf(1.0 / R::I32 as f64), PhantomData) - } - - /// Return the absolute value of `self`. - /// - /// # Example - /// ``` - /// # use feos_core::si::KELVIN; - /// # use approx::assert_relative_eq; - /// let t = -50.0 * KELVIN; - /// assert_relative_eq!(t.abs(), &(50.0 * KELVIN)); - pub fn abs(self) -> Self { - Self(self.0.abs(), PhantomData) - } - - /// Returns a number that represents the sign of `self`. - /// - /// - `1.0` if the number is positive, `+0.0` or `INFINITY` - /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` - /// - `NAN` if the number is `NAN` - pub fn signum(self) -> f64 { - self.0.signum() - } - - /// Returns `true` if `self` has a negative sign, including `-0.0`, `NaN`s with - /// negative sign bit and negative infinity. - pub fn is_sign_negative(&self) -> bool { - self.0.is_sign_negative() - } - - /// Returns `true` if `self` has a positive sign, including `+0.0`, `NaN`s with - /// positive sign bit and positive infinity. - pub fn is_sign_positive(&self) -> bool { - self.0.is_sign_positive() - } - - /// Returns true if this value is NaN. - pub fn is_nan(&self) -> bool { - self.0.is_nan() - } - - /// Return the minimum of `self` and `other`. - /// - /// # Example - /// ``` - /// # use feos_core::si::{KILO, PASCAL, BAR}; - /// # use approx::assert_relative_eq; - /// let p1 = 110.0 * KILO * PASCAL; - /// let p2 = BAR; - /// assert_relative_eq!(p1.min(p2), &p2); - /// ``` - pub fn min(self, other: Self) -> Self { - Self(self.0.min(other.0), PhantomData) - } - - /// Return the maximum of `self` and `other`. - /// - /// # Example - /// ``` - /// # use feos_core::si::{KILO, PASCAL, BAR}; - /// # use approx::assert_relative_eq; - /// let p1 = 110.0 * KILO * PASCAL; - /// let p2 = BAR; - /// assert_relative_eq!(p1.max(p2), &p1); - /// ``` - pub fn max(self, other: Self) -> Self { - Self(self.0.max(other.0), PhantomData) - } -} - -impl PartialEq for Quantity { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl PartialOrd for Quantity { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl AbsDiffEq for Quantity { - type Epsilon = T::Epsilon; - - fn default_epsilon() -> Self::Epsilon { - T::default_epsilon() - } - - fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { - self.0.abs_diff_eq(&other.0, epsilon) - } -} - -impl RelativeEq for Quantity { - fn default_max_relative() -> Self::Epsilon { - T::default_max_relative() - } - - fn relative_eq( - &self, - other: &Self, - epsilon: Self::Epsilon, - max_relative: Self::Epsilon, - ) -> bool { - self.0.relative_eq(&other.0, epsilon, max_relative) - } -} diff --git a/feos-core/src/si/python.rs b/feos-core/src/si/python.rs deleted file mode 100644 index 877032311..000000000 --- a/feos-core/src/si/python.rs +++ /dev/null @@ -1,169 +0,0 @@ -use super::{Pressure, Quantity, SIUnit, Temperature}; -use crate::state::TPSpec; -use crate::{EosError, EosResult}; -use ndarray::{Array1, Array2, Array3, Array4}; -use quantity::python::{PySIArray1, PySIArray2, PySIArray3, PySIArray4, PySINumber}; -use quantity::si; -use quantity::Quantity as PyQuantity; -use std::convert::TryFrom; -use std::marker::PhantomData; -use std::ops::{Div, Mul}; -use typenum::Integer; - -impl< - Inner, - T: Integer, - L: Integer, - M: Integer, - I: Integer, - THETA: Integer, - N: Integer, - J: Integer, - > TryFrom> for Quantity> -where - for<'a> &'a Inner: Div, - PyQuantity: std::fmt::Display, -{ - type Error = EosError; - fn try_from(quantity: PyQuantity) -> EosResult { - let (value, unit_from) = quantity.into_raw_parts(); - let unit_into = [L::I8, M::I8, T::I8, I::I8, N::I8, THETA::I8, J::I8]; - if unit_into == unit_from { - Ok(Quantity(value, PhantomData)) - } else { - Err(EosError::WrongUnits( - si::SIUnit::from_raw_parts(unit_into).to_string(), - PyQuantity::from_raw_parts(value, unit_from).to_string(), - )) - } - } -} - -impl - TryFrom for Quantity> -{ - type Error = >>::Error; - - fn try_from(value: PySINumber) -> Result { - Self::try_from(PyQuantity::from(value)) - } -} - -impl - TryFrom for Quantity, SIUnit> -{ - type Error = , si::SIUnit>>>::Error; - - fn try_from(value: PySIArray1) -> Result { - Self::try_from(PyQuantity::from(value)) - } -} - -impl - TryFrom for Quantity, SIUnit> -{ - type Error = , si::SIUnit>>>::Error; - - fn try_from(value: PySIArray2) -> Result { - Self::try_from(PyQuantity::from(value)) - } -} - -impl - TryFrom for Quantity, SIUnit> -{ - type Error = , si::SIUnit>>>::Error; - - fn try_from(value: PySIArray3) -> Result { - Self::try_from(PyQuantity::from(value)) - } -} - -impl - TryFrom for Quantity, SIUnit> -{ - type Error = , si::SIUnit>>>::Error; - - fn try_from(value: PySIArray4) -> Result { - Self::try_from(PyQuantity::from(value)) - } -} - -impl< - Inner, - T: Integer, - L: Integer, - M: Integer, - I: Integer, - THETA: Integer, - N: Integer, - J: Integer, - > From>> for PyQuantity -where - Inner: Mul>, -{ - fn from(quantity: Quantity>) -> Self { - Self::from_raw_parts( - quantity.0, - [L::I8, M::I8, T::I8, I::I8, N::I8, THETA::I8, J::I8], - ) - } -} - -impl - From>> for PySINumber -{ - fn from(quantity: Quantity>) -> Self { - Self::from(PyQuantity::from(quantity)) - } -} - -impl - From, SIUnit>> for PySIArray1 -{ - fn from(quantity: Quantity, SIUnit>) -> Self { - Self::from(PyQuantity::from(quantity)) - } -} - -impl - From, SIUnit>> for PySIArray2 -{ - fn from(quantity: Quantity, SIUnit>) -> Self { - Self::from(PyQuantity::from(quantity)) - } -} - -impl - From, SIUnit>> for PySIArray3 -{ - fn from(quantity: Quantity, SIUnit>) -> Self { - Self::from(PyQuantity::from(quantity)) - } -} - -impl - From, SIUnit>> for PySIArray4 -{ - fn from(quantity: Quantity, SIUnit>) -> Self { - Self::from(PyQuantity::from(quantity)) - } -} - -impl TryFrom for TPSpec { - type Error = EosError; - - fn try_from(quantity: PySINumber) -> EosResult { - let quantity = PyQuantity::from(quantity); - if let Ok(t) = Temperature::::try_from(quantity) { - return Ok(TPSpec::Temperature(t)); - } - if let Ok(p) = Pressure::::try_from(quantity) { - return Ok(TPSpec::Pressure(p)); - } - Err(EosError::WrongUnits( - "temperature or pressure".into(), - quantity.to_string(), - )) - } -} diff --git a/feos-core/src/state/builder.rs b/feos-core/src/state/builder.rs index 235075096..c557bc297 100644 --- a/feos-core/src/state/builder.rs +++ b/feos-core/src/state/builder.rs @@ -1,8 +1,8 @@ use super::{DensityInitialization, State}; use crate::equation_of_state::{IdealGas, Residual}; use crate::errors::EosResult; -use crate::si::*; use ndarray::Array1; +use quantity::*; use std::sync::Arc; /// A simple tool to construct [State]s with arbitrary input parameters. @@ -11,7 +11,7 @@ use std::sync::Arc; /// ``` /// # use feos_core::{EosResult, StateBuilder}; /// # use feos_core::cubic::{PengRobinson, PengRobinsonParameters}; -/// # use feos_core::si::*; +/// # use quantity::*; /// # use std::sync::Arc; /// # use ndarray::arr1; /// # use approx::assert_relative_eq; diff --git a/feos-core/src/state/critical_point.rs b/feos-core/src/state/critical_point.rs index 67fa2b1a1..59a566731 100644 --- a/feos-core/src/state/critical_point.rs +++ b/feos-core/src/state/critical_point.rs @@ -1,8 +1,7 @@ use super::{DensityInitialization, State, StateHD, TPSpec}; use crate::equation_of_state::Residual; use crate::errors::{EosError, EosResult}; -use crate::si::{Density, Moles, Pressure, Temperature, Volume}; -use crate::{SolverOptions, TemperatureOrPressure, Verbosity}; +use crate::{ReferenceSystem, SolverOptions, TemperatureOrPressure, Verbosity}; use nalgebra::SVector; use ndarray::{arr1, Array1, Array2}; use num_dual::linalg::smallest_ev; @@ -11,6 +10,7 @@ use num_dual::{ DualVec, HyperDual, }; use num_traits::{One, Zero}; +use quantity::{Density, Moles, Pressure, Temperature, Volume}; use std::sync::Arc; const MAX_ITER_CRIT_POINT: usize = 50; diff --git a/feos-core/src/state/mod.rs b/feos-core/src/state/mod.rs index f9b0ea43e..512d08c08 100644 --- a/feos-core/src/state/mod.rs +++ b/feos-core/src/state/mod.rs @@ -9,10 +9,11 @@ use crate::density_iteration::density_iteration; use crate::equation_of_state::{IdealGas, Residual}; use crate::errors::{EosError, EosResult}; -use crate::si::*; +use crate::ReferenceSystem; use cache::Cache; use ndarray::prelude::*; use num_dual::*; +use quantity::*; use std::fmt; use std::ops::Sub; use std::sync::{Arc, Mutex}; @@ -27,8 +28,8 @@ pub use builder::StateBuilder; pub use statevec::StateVec; /// Possible contributions that can be computed. -#[derive(Clone, Copy)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum Contributions { /// Only compute the ideal gas contribution IdealGas, diff --git a/feos-core/src/state/properties.rs b/feos-core/src/state/properties.rs index 14bb9d617..bb9510c4c 100644 --- a/feos-core/src/state/properties.rs +++ b/feos-core/src/state/properties.rs @@ -1,9 +1,9 @@ -use std::ops::Div; - use super::{Contributions, Derivative::*, PartialDerivative, State}; use crate::equation_of_state::{IdealGas, Residual}; -use crate::si::*; +use crate::ReferenceSystem; use ndarray::Array1; +use quantity::*; +use std::ops::Div; use typenum::P2; impl State { diff --git a/feos-core/src/state/residual_properties.rs b/feos-core/src/state/residual_properties.rs index fad7cfeae..047b04668 100644 --- a/feos-core/src/state/residual_properties.rs +++ b/feos-core/src/state/residual_properties.rs @@ -1,9 +1,10 @@ use super::{Contributions, Derivative::*, PartialDerivative, State}; use crate::equation_of_state::{EntropyScaling, Residual}; use crate::errors::EosResult; -use crate::si::*; -use crate::PhaseEquilibrium; +use crate::phase_equilibria::PhaseEquilibrium; +use crate::ReferenceSystem; use ndarray::{arr1, Array1, Array2}; +use quantity::*; use std::ops::{Add, Div}; use std::sync::Arc; use typenum::P2; @@ -486,7 +487,7 @@ impl State { /// Total molar weight: $MW=\sum_ix_iMW_i$ pub fn total_molar_weight(&self) -> MolarWeight { - (self.eos.molar_weight() * Dimensionless::from(&self.molefracs)).sum() + (self.eos.molar_weight() * Dimensionless::new(&self.molefracs)).sum() } /// Mass of each component: $m_i=n_iMW_i$ diff --git a/feos-core/src/state/statevec.rs b/feos-core/src/state/statevec.rs index 73fceab58..d90b8faaa 100644 --- a/feos-core/src/state/statevec.rs +++ b/feos-core/src/state/statevec.rs @@ -1,6 +1,6 @@ use super::{Contributions, State}; use crate::equation_of_state::{IdealGas, Residual}; -use crate::si::{ +use quantity::{ Density, MassDensity, MolarEnergy, MolarEntropy, Moles, Pressure, SpecificEnergy, SpecificEntropy, Temperature, }; diff --git a/feos-dft/Cargo.toml b/feos-dft/Cargo.toml index ee3270b60..63cc16c89 100644 --- a/feos-dft/Cargo.toml +++ b/feos-dft/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "feos-dft" version = "0.7.0" -authors = ["Philipp Rehner ", "Gernot Bauer "] +authors = [ + "Philipp Rehner ", + "Gernot Bauer ", +] edition = "2021" license = "MIT OR Apache-2.0" description = "Generic classical DFT implementations for the `feos` project." @@ -13,25 +16,24 @@ categories = ["science"] exclude = ["/.github/*", "*.ipynb"] [package.metadata.docs.rs] -rustdoc-args = [ "--html-in-header", "./docs-header.html" ] -features = [ "rayon" ] +rustdoc-args = ["--html-in-header", "./docs-header.html"] +features = ["rayon"] [dependencies] -quantity = { version = "0.8", optional = true } -num-dual = "0.9" +quantity = "0.9" +num-dual = "0.10" feos-core = { version = "0.7", path = "../feos-core" } -ndarray = "0.15" -nalgebra = "0.32" +ndarray = "0.16" +nalgebra = "0.33" rustdct = "0.7" rustfft = "6.0" -ang = "0.6" num-traits = "0.2" libm = "0.2" gauss-quad = { version = "0.1", optional = true } petgraph = "0.6" typenum = "1.16" -numpy = { version = "0.21", optional = true } -pyo3 = { version = "0.21", optional = true } +numpy = { version = "0.22", optional = true } +pyo3 = { version = "0.22", optional = true } [features] default = [] diff --git a/feos-dft/src/adsorption/external_potential.rs b/feos-dft/src/adsorption/external_potential.rs index 6396332db..a33dfaf7e 100644 --- a/feos-dft/src/adsorption/external_potential.rs +++ b/feos-dft/src/adsorption/external_potential.rs @@ -3,10 +3,10 @@ use crate::adsorption::fea_potential::calculate_fea_potential; use crate::functional::HelmholtzEnergyFunctional; #[cfg(feature = "rayon")] use crate::geometry::Geometry; -#[cfg(feature = "rayon")] -use feos_core::si::Length; use libm::tgamma; use ndarray::{Array1, Array2, Axis as Axis_nd}; +#[cfg(feature = "rayon")] +use quantity::Length; use std::f64::consts::PI; const DELTA_STEELE: f64 = 3.35; diff --git a/feos-dft/src/adsorption/fea_potential.rs b/feos-dft/src/adsorption/fea_potential.rs index 4b300b054..7b2663e45 100644 --- a/feos-dft/src/adsorption/fea_potential.rs +++ b/feos-dft/src/adsorption/fea_potential.rs @@ -1,9 +1,10 @@ use super::pore3d::{calculate_distance2, evaluate_lj_potential}; use crate::profile::{CUTOFF_RADIUS, MAX_POTENTIAL}; use crate::Geometry; -use feos_core::si::Length; +use feos_core::ReferenceSystem; use gauss_quad::GaussLegendre; use ndarray::{Array1, Array2, Zip}; +use quantity::Length; use std::f64::consts::PI; // Calculate free-energy average potential for given solid structure. diff --git a/feos-dft/src/adsorption/mod.rs b/feos-dft/src/adsorption/mod.rs index cd75e9762..c96f3628b 100644 --- a/feos-dft/src/adsorption/mod.rs +++ b/feos-dft/src/adsorption/mod.rs @@ -1,12 +1,12 @@ //! Adsorption profiles and isotherms. use super::functional::{HelmholtzEnergyFunctional, DFT}; use super::solver::DFTSolver; -use feos_core::si::{Energy, MolarEnergy, Moles, Pressure, Temperature}; use feos_core::{ - Components, Contributions, DensityInitialization, EosError, EosResult, Residual, SolverOptions, - State, StateBuilder, + Components, Contributions, DensityInitialization, EosError, EosResult, ReferenceSystem, + Residual, SolverOptions, State, StateBuilder, }; use ndarray::{Array1, Array2, Dimension, Ix1, Ix3, RemoveAxis}; +use quantity::{Energy, MolarEnergy, Moles, Pressure, Temperature}; use std::iter; use std::sync::Arc; diff --git a/feos-dft/src/adsorption/pore.rs b/feos-dft/src/adsorption/pore.rs index 7efe91ec6..9890424b8 100644 --- a/feos-dft/src/adsorption/pore.rs +++ b/feos-dft/src/adsorption/pore.rs @@ -6,15 +6,15 @@ use crate::geometry::{Axis, Geometry, Grid}; use crate::profile::{DFTProfile, MAX_POTENTIAL}; use crate::solver::DFTSolver; use crate::WeightFunctionInfo; -use feos_core::si::{ - Density, Dimensionless, Energy, Length, MolarEnergy, MolarWeight, Temperature, Volume, KELVIN, -}; -use feos_core::{Components, Contributions, EosResult, State, StateBuilder}; +use feos_core::{Components, Contributions, EosResult, ReferenceSystem, State, StateBuilder}; use ndarray::prelude::*; use ndarray::Axis as Axis_nd; use ndarray::RemoveAxis; use num_dual::linalg::LU; use num_dual::DualNum; +use quantity::{ + Density, Dimensionless, Energy, Length, MolarEnergy, MolarWeight, Temperature, Volume, KELVIN, +}; use std::fmt::Display; use std::sync::Arc; @@ -143,7 +143,7 @@ where pub fn enthalpy_of_adsorption(&self) -> EosResult { Ok((self.partial_molar_enthalpy_of_adsorption()? - * Dimensionless::from(&self.profile.bulk.molefracs)) + * Dimensionless::new(&self.profile.bulk.molefracs)) .sum()) } } diff --git a/feos-dft/src/adsorption/pore2d.rs b/feos-dft/src/adsorption/pore2d.rs index 452585c8d..e6d80de5a 100644 --- a/feos-dft/src/adsorption/pore2d.rs +++ b/feos-dft/src/adsorption/pore2d.rs @@ -1,9 +1,8 @@ use super::{FluidParameters, PoreProfile, PoreSpecification}; use crate::{Axis, ConvolverFFT, DFTProfile, Grid, HelmholtzEnergyFunctional, DFT}; -use ang::Angle; -use feos_core::si::{Density, Length}; -use feos_core::{EosResult, State}; +use feos_core::{EosResult, ReferenceSystem, State}; use ndarray::{Array3, Ix2}; +use quantity::{Angle, Density, Length}; pub struct Pore2D { system_size: [Length; 2], diff --git a/feos-dft/src/adsorption/pore3d.rs b/feos-dft/src/adsorption/pore3d.rs index 87fc4c32e..8ddd14fd8 100644 --- a/feos-dft/src/adsorption/pore3d.rs +++ b/feos-dft/src/adsorption/pore3d.rs @@ -4,11 +4,10 @@ use crate::convolver::ConvolverFFT; use crate::functional::{HelmholtzEnergyFunctional, DFT}; use crate::geometry::{Axis, Grid}; use crate::profile::{DFTProfile, CUTOFF_RADIUS, MAX_POTENTIAL}; -use ang::Angle; -use feos_core::si::{Density, Length, DEGREES}; -use feos_core::{EosError, EosResult, State}; +use feos_core::{EosError, EosResult, ReferenceSystem, State}; use ndarray::prelude::*; use ndarray::Zip; +use quantity::{Angle, Density, Length, DEGREES}; /// Parameters required to specify a 3D pore. pub struct Pore3D { diff --git a/feos-dft/src/convolver/periodic_convolver.rs b/feos-dft/src/convolver/periodic_convolver.rs index 7e6804ace..8ea216462 100644 --- a/feos-dft/src/convolver/periodic_convolver.rs +++ b/feos-dft/src/convolver/periodic_convolver.rs @@ -1,10 +1,10 @@ use super::{Convolver, FFTWeightFunctions}; use crate::geometry::Axis; use crate::weight_functions::{WeightFunction, WeightFunctionInfo}; -use ang::Angle; use ndarray::Axis as Axis_nd; use ndarray::*; use num_dual::DualNum; +use quantity::Angle; use rustfft::num_complex::Complex; use rustfft::{Fft, FftDirection, FftNum, FftPlanner}; use std::f64::consts::PI; diff --git a/feos-dft/src/functional.rs b/feos-dft/src/functional.rs index a96fd01a5..188202769 100644 --- a/feos-dft/src/functional.rs +++ b/feos-dft/src/functional.rs @@ -4,13 +4,13 @@ use crate::functional_contribution::*; use crate::ideal_chain_contribution::IdealChainContribution; use crate::solvation::PairPotential; use crate::weight_functions::{WeightFunction, WeightFunctionInfo, WeightFunctionShape}; -use feos_core::si::MolarWeight; use feos_core::{Components, EosResult, EquationOfState, IdealGas, Residual, StateHD}; use ndarray::*; use num_dual::*; use petgraph::graph::{Graph, UnGraph}; use petgraph::visit::EdgeRef; use petgraph::Directed; +use quantity::MolarWeight; use std::borrow::Cow; use std::ops::{Deref, MulAssign}; use std::sync::Arc; @@ -220,9 +220,9 @@ pub trait HelmholtzEnergyFunctional: Components + Sized + Send + Sync { let mut pd = Array::zeros(wd.raw_dim()); c.first_partial_derivatives( temperature, - wd.into_shape((nwd, ngrid)).unwrap(), - phi.view_mut().into_shape(ngrid).unwrap(), - pd.view_mut().into_shape((nwd, ngrid)).unwrap(), + wd.into_shape_with_order((nwd, ngrid)).unwrap(), + phi.view_mut().into_shape_with_order(ngrid).unwrap(), + pd.view_mut().into_shape_with_order((nwd, ngrid)).unwrap(), )?; partial_derivatives.push(pd); helmholtz_energy_density += φ @@ -257,9 +257,9 @@ pub trait HelmholtzEnergyFunctional: Components + Sized + Send + Sync { let mut pd = Array::zeros(wd.raw_dim()); c.first_partial_derivatives_dual( temperature_dual, - wd.into_shape((nwd, ngrid)).unwrap(), - phi.view_mut().into_shape(ngrid).unwrap(), - pd.view_mut().into_shape((nwd, ngrid)).unwrap(), + wd.into_shape_with_order((nwd, ngrid)).unwrap(), + phi.view_mut().into_shape_with_order(ngrid).unwrap(), + pd.view_mut().into_shape_with_order((nwd, ngrid)).unwrap(), )?; partial_derivatives.push(pd); helmholtz_energy_density += φ diff --git a/feos-dft/src/geometry.rs b/feos-dft/src/geometry.rs index 5f743eea6..7c5389339 100644 --- a/feos-dft/src/geometry.rs +++ b/feos-dft/src/geometry.rs @@ -1,6 +1,6 @@ -use ang::Angle; -use feos_core::si::{Length, Quantity}; +use feos_core::ReferenceSystem; use ndarray::{Array1, Array2}; +use quantity::{Angle, Length, Quantity}; use std::f64::consts::{FRAC_PI_3, PI}; /// Grids with up to three dimensions. @@ -72,8 +72,8 @@ impl Grid { } /// Geometries of individual axes. -#[derive(Copy, Clone)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Copy, Clone, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum Geometry { Cartesian, Cylindrical, diff --git a/feos-dft/src/ideal_chain_contribution.rs b/feos-dft/src/ideal_chain_contribution.rs index 5965b2553..cf4a0caa2 100644 --- a/feos-dft/src/ideal_chain_contribution.rs +++ b/feos-dft/src/ideal_chain_contribution.rs @@ -1,7 +1,7 @@ -use feos_core::si::{Density, Pressure, Temperature}; -use feos_core::{EosResult, StateHD}; +use feos_core::{EosResult, ReferenceSystem, StateHD}; use ndarray::*; use num_dual::DualNum; +use quantity::{Density, Pressure, Temperature}; use std::fmt; #[derive(Clone)] diff --git a/feos-dft/src/interface/mod.rs b/feos-dft/src/interface/mod.rs index 8ad41b2f3..ab753d407 100644 --- a/feos-dft/src/interface/mod.rs +++ b/feos-dft/src/interface/mod.rs @@ -4,9 +4,9 @@ use crate::functional::{HelmholtzEnergyFunctional, DFT}; use crate::geometry::{Axis, Grid}; use crate::profile::{DFTProfile, DFTSpecifications}; use crate::solver::DFTSolver; -use feos_core::si::{Area, Density, Length, Moles, SurfaceTension, Temperature}; -use feos_core::{Contributions, EosError, EosResult, PhaseEquilibrium}; +use feos_core::{Contributions, EosError, EosResult, PhaseEquilibrium, ReferenceSystem}; use ndarray::{s, Array1, Array2, Axis as Axis_nd, Ix1}; +use quantity::{Area, Density, Length, Moles, SurfaceTension, Temperature}; mod surface_tension_diagram; pub use surface_tension_diagram::SurfaceTensionDiagram; diff --git a/feos-dft/src/interface/surface_tension_diagram.rs b/feos-dft/src/interface/surface_tension_diagram.rs index 65883a51b..9f4db6ef3 100644 --- a/feos-dft/src/interface/surface_tension_diagram.rs +++ b/feos-dft/src/interface/surface_tension_diagram.rs @@ -1,9 +1,9 @@ use super::PlanarInterface; use crate::functional::{HelmholtzEnergyFunctional, DFT}; use crate::solver::DFTSolver; -use feos_core::si::{Length, Moles, SurfaceTension, Temperature}; -use feos_core::{PhaseEquilibrium, StateVec}; +use feos_core::{PhaseEquilibrium, ReferenceSystem, StateVec}; use ndarray::{Array1, Array2}; +use quantity::{Length, Moles, SurfaceTension, Temperature}; const DEFAULT_GRID_POINTS: usize = 2048; diff --git a/feos-dft/src/pdgt.rs b/feos-dft/src/pdgt.rs index 190a9819c..83e423e64 100644 --- a/feos-dft/src/pdgt.rs +++ b/feos-dft/src/pdgt.rs @@ -1,13 +1,13 @@ use super::functional::{HelmholtzEnergyFunctional, DFT}; use super::functional_contribution::FunctionalContribution; use super::weight_functions::WeightFunctionInfo; -use feos_core::si::{ +use feos_core::{Components, Contributions, EosResult, PhaseEquilibrium, ReferenceSystem}; +use ndarray::*; +use num_dual::Dual2_64; +use quantity::{ Density, Length, Pressure, Quantity, SurfaceTension, Temperature, _Area, _Density, _MolarEnergy, RGAS, }; -use feos_core::{Components, Contributions, EosResult, PhaseEquilibrium}; -use ndarray::*; -use num_dual::Dual2_64; use std::ops::{Add, AddAssign, Sub}; use typenum::{Diff, Sum, P2}; diff --git a/feos-dft/src/profile/mod.rs b/feos-dft/src/profile/mod.rs index 77ff26449..fc6c675ee 100644 --- a/feos-dft/src/profile/mod.rs +++ b/feos-dft/src/profile/mod.rs @@ -2,11 +2,11 @@ use crate::convolver::{BulkConvolver, Convolver}; use crate::functional::{HelmholtzEnergyFunctional, DFT}; use crate::geometry::Grid; use crate::solver::{DFTSolver, DFTSolverLog}; -use feos_core::si::{Density, Length, Moles, Quantity, Temperature, Volume, _Volume, DEGREES}; -use feos_core::{Components, EosError, EosResult, State}; +use feos_core::{Components, EosError, EosResult, ReferenceSystem, State}; use ndarray::{ Array, Array1, Array2, Array3, ArrayBase, Axis as Axis_nd, Data, Dimension, Ix1, Ix2, Ix3, }; +use quantity::{Density, Length, Moles, Quantity, Temperature, Volume, _Volume, DEGREES}; use std::ops::{Add, MulAssign}; use std::sync::Arc; use typenum::Sum; @@ -168,7 +168,7 @@ impl DFTProfile { let v_grid = Array::from_shape_fn(shape, |(_, j, _)| v.grid[j]); let w_grid = Array::from_shape_fn(shape, |(_, _, k)| w.grid[k]); let xi = (alpha.cos() - gamma.cos() * beta.cos()) / gamma.sin(); - let zeta = (1.0 - beta.cos().powi(2) - xi * xi).sqrt(); + let zeta = (1.0_f64 - beta.cos().powi(2) - xi * xi).sqrt(); let x = Length::from_reduced(u_grid + &v_grid * gamma.cos() + &w_grid * beta.cos()); let y = Length::from_reduced(v_grid * gamma.sin() + &w_grid * xi); let z = Length::from_reduced(w_grid * zeta); diff --git a/feos-dft/src/profile/properties.rs b/feos-dft/src/profile/properties.rs index 503bb1481..2fd169024 100644 --- a/feos-dft/src/profile/properties.rs +++ b/feos-dft/src/profile/properties.rs @@ -3,12 +3,12 @@ use super::DFTProfile; use crate::convolver::{BulkConvolver, Convolver}; use crate::functional_contribution::FunctionalContribution; use crate::{ConvolverFFT, DFTSolverLog, HelmholtzEnergyFunctional, WeightFunctionInfo}; -use feos_core::si::{ - Density, Energy, Entropy, EntropyDensity, MolarEnergy, Moles, Pressure, Quantity, Temperature, -}; -use feos_core::{Contributions, EosResult, IdealGas, Verbosity}; +use feos_core::{Contributions, EosResult, IdealGas, ReferenceSystem, Verbosity}; use ndarray::{Array, Array1, Array2, Axis, Dimension, RemoveAxis, ScalarOperand}; use num_dual::{Dual64, DualNum}; +use quantity::{ + Density, Energy, Entropy, EntropyDensity, MolarEnergy, Moles, Pressure, Quantity, Temperature, +}; use std::ops::{AddAssign, Div}; use std::sync::Arc; @@ -92,11 +92,11 @@ where let ngrid = wd.len() / nwd; helmholtz_energy_density .view_mut() - .into_shape(ngrid) + .into_shape_with_order(ngrid) .unwrap() .add_assign(&c.helmholtz_energy_density( temperature, - wd.into_shape((nwd, ngrid)).unwrap().view(), + wd.into_shape_with_order((nwd, ngrid)).unwrap().view(), )?); } Ok(helmholtz_energy_density * temperature) @@ -150,9 +150,9 @@ where helmholtz_energy_density.push( c.helmholtz_energy_density( temperature_dual, - wd.into_shape((nwd, ngrid)).unwrap().view(), + wd.into_shape_with_order((nwd, ngrid)).unwrap().view(), )? - .into_shape(density.raw_dim().remove_axis(Axis(0))) + .into_shape_with_order(density.raw_dim().remove_axis(Axis(0))) .unwrap(), ); } diff --git a/feos-dft/src/python/adsorption/external_potential.rs b/feos-dft/src/python/adsorption/external_potential.rs index d2fc4b9b3..682683f0a 100644 --- a/feos-dft/src/python/adsorption/external_potential.rs +++ b/feos-dft/src/python/adsorption/external_potential.rs @@ -1,8 +1,9 @@ use crate::adsorption::ExternalPotential; +use ndarray::Array2; use numpy::prelude::*; use numpy::PyArray1; use pyo3::prelude::*; -use quantity::python::{PySIArray2, PySINumber}; +use quantity::Length; /// A collection of external potentials. #[pyclass(name = "ExternalPotential")] @@ -127,6 +128,7 @@ impl PyExternalPotential { /// #[staticmethod] #[pyo3(text_signature = "(sigma_ss, epsilon_k_ss, rho_s, xi=None)")] + #[pyo3(signature = (sigma_ss, epsilon_k_ss, rho_s, xi=None))] pub fn Steele(sigma_ss: f64, epsilon_k_ss: f64, rho_s: f64, xi: Option) -> Self { Self(ExternalPotential::Steele { sigma_ss, @@ -157,6 +159,7 @@ impl PyExternalPotential { /// #[staticmethod] #[pyo3(text_signature = "(sigma_sf, epsilon_k_sf, rho_s, xi=None)")] + #[pyo3(signature = (sigma_sf, epsilon_k_sf, rho_s, xi=None))] pub fn CustomSteele( sigma_sf: &Bound<'_, PyArray1>, epsilon_k_sf: &Bound<'_, PyArray1>, @@ -226,27 +229,24 @@ impl PyExternalPotential { /// #[staticmethod] #[pyo3( - text_signature = "(coordinates, sigma_ss, epsilon_k_ss, pore_center, system_size, n_grid, cutoff_radius=None)" + text_signature = "(coordinates, sigma_ss, epsilon_k_ss, pore_center, system_size, n_grid, cutoff_radius=None)", + signature = (coordinates, sigma_ss, epsilon_k_ss, pore_center, system_size, n_grid, cutoff_radius=None) )] pub fn FreeEnergyAveraged( - coordinates: PySIArray2, + coordinates: Length>, sigma_ss: &Bound<'_, PyArray1>, epsilon_k_ss: &Bound<'_, PyArray1>, pore_center: [f64; 3], - system_size: [PySINumber; 3], + system_size: [Length; 3], n_grid: [usize; 2], cutoff_radius: Option, ) -> PyResult { Ok(Self(ExternalPotential::FreeEnergyAveraged { - coordinates: coordinates.try_into()?, + coordinates, sigma_ss: sigma_ss.to_owned_array(), epsilon_k_ss: epsilon_k_ss.to_owned_array(), pore_center, - system_size: [ - system_size[0].try_into()?, - system_size[1].try_into()?, - system_size[2].try_into()?, - ], + system_size, n_grid, cutoff_radius, })) diff --git a/feos-dft/src/python/adsorption/mod.rs b/feos-dft/src/python/adsorption/mod.rs index 23aaba07d..b123b0b5e 100644 --- a/feos-dft/src/python/adsorption/mod.rs +++ b/feos-dft/src/python/adsorption/mod.rs @@ -49,18 +49,19 @@ macro_rules! impl_adsorption_isotherm { /// #[staticmethod] #[pyo3(text_signature = "(functional, temperature, pressure, pore, molefracs=None, solver=None)")] + #[pyo3(signature = (functional, temperature, pressure, pore, molefracs=None, solver=None))] pub fn adsorption_isotherm( functional: &$py_func, - temperature: PySINumber, - pressure: PySIArray1, + temperature: Temperature, + pressure: Pressure>, pore: &$py_pore, molefracs: Option<&Bound<'_, PyArray1>>, solver: Option, ) -> PyResult { Ok(Self(Adsorption::adsorption_isotherm( &functional.0, - temperature.try_into()?, - &pressure.try_into()?, + temperature, + &pressure, &pore.0, molefracs.map(|x| x.to_owned_array()).as_ref(), solver.map(|s| s.0).as_ref(), @@ -92,18 +93,19 @@ macro_rules! impl_adsorption_isotherm { /// #[staticmethod] #[pyo3(text_signature = "(functional, temperature, pressure, pore, molefracs=None, solver=None)")] + #[pyo3(signature = (functional, temperature, pressure, pore, molefracs=None, solver=None))] pub fn desorption_isotherm( functional: &$py_func, - temperature: PySINumber, - pressure: PySIArray1, + temperature: Temperature, + pressure: Pressure>, pore: &$py_pore, molefracs: Option<&Bound<'_, PyArray1>>, solver: Option, ) -> PyResult { Ok(Self(Adsorption::desorption_isotherm( &functional.0, - temperature.try_into()?, - &pressure.try_into()?, + temperature, + &pressure, &pore.0, molefracs.map(|x| x.to_owned_array()).as_ref(), solver.map(|s| s.0).as_ref(), @@ -138,18 +140,19 @@ macro_rules! impl_adsorption_isotherm { /// #[staticmethod] #[pyo3(text_signature = "(functional, temperature, pressure, pore, molefracs=None, solver=None)")] + #[pyo3(signature = (functional, temperature, pressure, pore, molefracs=None, solver=None))] pub fn equilibrium_isotherm( functional: &$py_func, - temperature: PySINumber, - pressure: PySIArray1, + temperature: Temperature, + pressure: Pressure>, pore: &$py_pore, molefracs: Option<&Bound<'_, PyArray1>>, solver: Option, ) -> PyResult { Ok(Self(Adsorption::equilibrium_isotherm( &functional.0, - temperature.try_into()?, - &pressure.try_into()?, + temperature, + &pressure, &pore.0, molefracs.map(|x| x.to_owned_array()).as_ref(), solver.map(|s| s.0).as_ref(), @@ -187,12 +190,13 @@ macro_rules! impl_adsorption_isotherm { /// #[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))] #[expect(clippy::too_many_arguments)] pub fn phase_equilibrium( functional: &$py_func, - temperature: PySINumber, - p_min: PySINumber, - p_max: PySINumber, + temperature: Temperature, + p_min: Pressure, + p_max: Pressure, pore: &$py_pore, molefracs: Option<&Bound<'_, PyArray1>>, solver: Option, @@ -202,9 +206,9 @@ macro_rules! impl_adsorption_isotherm { ) -> PyResult { Ok(Self(Adsorption::phase_equilibrium( &functional.0, - temperature.try_into()?, - p_min.try_into()?, - p_max.try_into()?, + temperature, + p_min, + p_max, &pore.0, molefracs.map(|x| x.to_owned_array()).as_ref(), solver.map(|s| s.0).as_ref(), @@ -226,33 +230,33 @@ macro_rules! impl_adsorption_isotherm { } #[getter] - fn get_pressure(&self) -> PySIArray1 { - self.0.pressure().into() + fn get_pressure(&self) -> Pressure> { + self.0.pressure() } #[getter] - fn get_adsorption(&self) -> PySIArray2 { - self.0.adsorption().into() + fn get_adsorption(&self) -> Moles> { + self.0.adsorption() } #[getter] - fn get_total_adsorption(&self) -> PySIArray1 { - self.0.total_adsorption().into() + fn get_total_adsorption(&self) -> Moles> { + self.0.total_adsorption() } #[getter] - fn get_grand_potential(&mut self) -> PySIArray1 { - self.0.grand_potential().into() + fn get_grand_potential(&mut self) -> Energy> { + self.0.grand_potential() } #[getter] - fn get_partial_molar_enthalpy_of_adsorption(&self) -> PySIArray2 { - self.0.partial_molar_enthalpy_of_adsorption().into() + fn get_partial_molar_enthalpy_of_adsorption(&self) -> MolarEnergy> { + self.0.partial_molar_enthalpy_of_adsorption() } #[getter] - fn get_enthalpy_of_adsorption(&self) -> PySIArray1 { - self.0.enthalpy_of_adsorption().into() + fn get_enthalpy_of_adsorption(&self) -> MolarEnergy> { + self.0.enthalpy_of_adsorption() } } }; diff --git a/feos-dft/src/python/adsorption/pore.rs b/feos-dft/src/python/adsorption/pore.rs index 3ad7cacc7..1dffe4b3c 100644 --- a/feos-dft/src/python/adsorption/pore.rs +++ b/feos-dft/src/python/adsorption/pore.rs @@ -33,16 +33,17 @@ macro_rules! impl_pore { impl PyPore1D { #[new] #[pyo3(text_signature = "(geometry, pore_size, potential, n_grid=None, potential_cutoff=None)")] + #[pyo3(signature = (geometry, pore_size, potential, n_grid=None, potential_cutoff=None))] fn new( geometry: Geometry, - pore_size: PySINumber, + pore_size: Length, potential: PyExternalPotential, n_grid: Option, potential_cutoff: Option, ) -> PyResult { Ok(Self(Pore1D::new( geometry, - pore_size.try_into()?, + pore_size, potential.0, n_grid, potential_cutoff, @@ -66,10 +67,11 @@ macro_rules! impl_pore { /// ------- /// PoreProfile1D #[pyo3(text_signature = "($self, bulk, density=None, external_potential=None)")] + #[pyo3(signature = (bulk, density=None, external_potential=None))] fn initialize( &self, bulk: &PyState, - density: Option, + density: Option>>, external_potential: Option<&Bound<'_ ,PyArray2>>, ) -> PyResult { Ok(PyPoreProfile1D(self.0.initialize( @@ -85,7 +87,7 @@ macro_rules! impl_pore { } #[getter] - fn get_pore_size(&self)-> PySINumber { + fn get_pore_size(&self)-> Length { self.0.pore_size.into() } @@ -106,7 +108,7 @@ macro_rules! impl_pore { /// The pore volume using Helium at 298 K as reference. #[getter] - fn get_pore_volume(&self) -> PyResult { + fn get_pore_volume(&self) -> PyResult { Ok(self.0.pore_volume()?.into()) } } @@ -114,22 +116,22 @@ macro_rules! impl_pore { #[pymethods] impl PyPoreProfile1D { #[getter] - fn get_grand_potential(&self) -> Option { - self.0.grand_potential.map(PySINumber::from) + fn get_grand_potential(&self) -> Option { + self.0.grand_potential } #[getter] - fn get_interfacial_tension(&self) -> Option { - self.0.interfacial_tension.map(PySINumber::from) + fn get_interfacial_tension(&self) -> Option { + self.0.interfacial_tension } #[getter] - fn get_partial_molar_enthalpy_of_adsorption(&self) -> PyResult { + fn get_partial_molar_enthalpy_of_adsorption(&self) -> PyResult>> { Ok(self.0.partial_molar_enthalpy_of_adsorption()?.into()) } #[getter] - fn get_enthalpy_of_adsorption(&self) -> PyResult { + fn get_enthalpy_of_adsorption(&self) -> PyResult { Ok(self.0.enthalpy_of_adsorption()?.into()) } } @@ -147,13 +149,13 @@ macro_rules! impl_pore { #[new] #[pyo3(text_signature = "(system_size, angle, n_grid)")] fn new( - system_size: [PySINumber; 2], - angle: PyAngle, + system_size: [Length; 2], + angle: Angle, n_grid: [usize; 2], ) -> PyResult { Ok(Self(Pore2D::new( - [system_size[0].try_into()?, system_size[1].try_into()?], - angle.into(), + [system_size[0], system_size[1]], + angle, n_grid, ))) } @@ -175,10 +177,11 @@ macro_rules! impl_pore { /// ------- /// PoreProfile2D #[pyo3(text_signature = "($self, bulk, density=None, external_potential=None)")] + #[pyo3(signature = (bulk, density=None, external_potential=None))] fn initialize( &self, bulk: &PyState, - density: Option, + density: Option>>, external_potential: Option<&Bound<'_, PyArray3>>, ) -> PyResult { Ok(PyPoreProfile2D(self.0.initialize( @@ -190,31 +193,31 @@ macro_rules! impl_pore { /// The pore volume using Helium at 298 K as reference. #[getter] - fn get_pore_volume(&self) -> PyResult { - Ok(self.0.pore_volume()?.into()) + fn get_pore_volume(&self) -> PyResult { + Ok(self.0.pore_volume()?) } } #[pymethods] impl PyPoreProfile2D { #[getter] - fn get_grand_potential(&self) -> Option { - self.0.grand_potential.map(PySINumber::from) + fn get_grand_potential(&self) -> Option { + self.0.grand_potential } #[getter] - fn get_interfacial_tension(&self) -> Option { - self.0.interfacial_tension.map(PySINumber::from) + fn get_interfacial_tension(&self) -> Option { + self.0.interfacial_tension } #[getter] - fn get_partial_molar_enthalpy_of_adsorption(&self) -> PyResult { - Ok(self.0.partial_molar_enthalpy_of_adsorption()?.into()) + fn get_partial_molar_enthalpy_of_adsorption(&self) -> PyResult>> { + Ok(self.0.partial_molar_enthalpy_of_adsorption()?) } #[getter] - fn get_enthalpy_of_adsorption(&self) -> PyResult { - Ok(self.0.enthalpy_of_adsorption()?.into()) + fn get_enthalpy_of_adsorption(&self) -> PyResult { + Ok(self.0.enthalpy_of_adsorption()?) } } @@ -256,27 +259,28 @@ macro_rules! impl_pore { impl PyPore3D { #[new] #[pyo3(text_signature = "(system_size, n_grid, coordinates, sigma_ss, epsilon_k_ss, angles=None, potential_cutoff=None, cutoff_radius=None)")] + #[pyo3(signature = (system_size, n_grid, coordinates, sigma_ss, epsilon_k_ss, angles=None, potential_cutoff=None, cutoff_radius=None))] #[expect(clippy::too_many_arguments)] fn new( - system_size: [PySINumber; 3], + system_size: [Length; 3], n_grid: [usize; 3], - coordinates: PySIArray2, + coordinates: Length>, sigma_ss: &Bound<'_, PyArray1>, epsilon_k_ss: &Bound<'_, PyArray1>, - angles: Option<[PyAngle; 3]>, + angles: Option<[Angle; 3]>, potential_cutoff: Option, - cutoff_radius: Option, - ) -> PyResult { - Ok(Self(Pore3D::new( - [system_size[0].try_into()?, system_size[1].try_into()?, system_size[2].try_into()?], + cutoff_radius: Option, + ) -> Self { + Self(Pore3D::new( + system_size, n_grid, - coordinates.try_into()?, + coordinates, sigma_ss.to_owned_array(), epsilon_k_ss.to_owned_array(), - angles.map(|angles| [angles[0].into(), angles[1].into(), angles[2].into()]), + angles, potential_cutoff, - cutoff_radius.map(|c| c.try_into()).transpose()?, - ))) + cutoff_radius, + )) } /// Initialize the pore for the given bulk state. @@ -296,10 +300,11 @@ macro_rules! impl_pore { /// ------- /// PoreProfile3D #[pyo3(text_signature = "($self, bulk, density=None, external_potential=None)")] + #[pyo3(signature = (bulk, density=None, external_potential=None))] fn initialize( &self, bulk: &PyState, - density: Option, + density: Option>>, external_potential: Option<&Bound<'_, PyArray4>>, ) -> PyResult { Ok(PyPoreProfile3D(self.0.initialize( @@ -311,7 +316,7 @@ macro_rules! impl_pore { /// The pore volume using Helium at 298 K as reference. #[getter] - fn get_pore_volume(&self) -> PyResult { + fn get_pore_volume(&self) -> PyResult { Ok(self.0.pore_volume()?.into()) } } @@ -319,23 +324,23 @@ macro_rules! impl_pore { #[pymethods] impl PyPoreProfile3D { #[getter] - fn get_grand_potential(&self) -> Option { - self.0.grand_potential.map(PySINumber::from) + fn get_grand_potential(&self) -> Option { + self.0.grand_potential } #[getter] - fn get_interfacial_tension(&self) -> Option { - self.0.interfacial_tension.map(PySINumber::from) + fn get_interfacial_tension(&self) -> Option { + self.0.interfacial_tension } #[getter] - fn get_partial_molar_enthalpy_of_adsorption(&self) -> PyResult { - Ok(self.0.partial_molar_enthalpy_of_adsorption()?.into()) + fn get_partial_molar_enthalpy_of_adsorption(&self) -> PyResult>> { + Ok(self.0.partial_molar_enthalpy_of_adsorption()?) } #[getter] - fn get_enthalpy_of_adsorption(&self) -> PyResult { - Ok(self.0.enthalpy_of_adsorption()?.into()) + fn get_enthalpy_of_adsorption(&self) -> PyResult { + Ok(self.0.enthalpy_of_adsorption()?) } } }; diff --git a/feos-dft/src/python/interface/mod.rs b/feos-dft/src/python/interface/mod.rs index 5bcddb355..0519afda8 100644 --- a/feos-dft/src/python/interface/mod.rs +++ b/feos-dft/src/python/interface/mod.rs @@ -35,21 +35,22 @@ macro_rules! impl_planar_interface { /// #[staticmethod] #[pyo3(text_signature = "(vle, n_grid, l_grid, critical_temperature, fix_equimolar_surface=None)")] + #[pyo3(signature = (vle, n_grid, l_grid, critical_temperature, fix_equimolar_surface=None))] fn from_tanh( vle: &PyPhaseEquilibrium, n_grid: usize, - l_grid: PySINumber, - critical_temperature: PySINumber, + l_grid: Length, + critical_temperature: Temperature, fix_equimolar_surface: Option, - ) -> PyResult { + ) -> Self { let profile = PlanarInterface::from_tanh( &vle.0, n_grid, - l_grid.try_into()?, - critical_temperature.try_into()?, + l_grid, + critical_temperature, fix_equimolar_surface.unwrap_or(false), ); - Ok(PyPlanarInterface(profile)) + PyPlanarInterface(profile) } /// Initialize a planar interface with a pDGT calculation. @@ -71,6 +72,7 @@ macro_rules! impl_planar_interface { /// #[staticmethod] #[pyo3(text_signature = "(vle, n_grid, fix_equimolar_surface=None)")] + #[pyo3(signature = (vle, n_grid, fix_equimolar_surface=None))] fn from_pdgt(vle: &PyPhaseEquilibrium, n_grid: usize, fix_equimolar_surface: Option) -> PyResult { let profile = PlanarInterface::from_pdgt(&vle.0, n_grid, fix_equimolar_surface.unwrap_or(false))?; Ok(PyPlanarInterface(profile)) @@ -97,11 +99,11 @@ macro_rules! impl_planar_interface { fn from_density_profile( vle: &PyPhaseEquilibrium, n_grid: usize, - l_grid: PySINumber, - density_profile: PySIArray2, + l_grid: Length, + density_profile: Density>, ) -> PyResult { - let mut profile = PlanarInterface::new(&vle.0, n_grid, l_grid.try_into()?); - profile.profile.density = density_profile.try_into()?; + let mut profile = PlanarInterface::new(&vle.0, n_grid, l_grid); + profile.profile.density = density_profile; Ok(PyPlanarInterface(profile)) } } @@ -109,23 +111,20 @@ macro_rules! impl_planar_interface { #[pymethods] impl PyPlanarInterface { #[getter] - fn get_surface_tension(&mut self) -> Option { - self.0.surface_tension.map(PySINumber::from) + fn get_surface_tension(&mut self) -> Option { + self.0.surface_tension } #[getter] - fn get_equimolar_radius(&mut self) -> Option { - self.0.equimolar_radius.map(PySINumber::from) + fn get_equimolar_radius(&mut self) -> Option { + self.0.equimolar_radius } #[getter] fn get_vle(&self) -> PyPhaseEquilibrium { PyPhaseEquilibrium(self.0.vle.clone()) } - } - #[pymethods] - impl PyPlanarInterface { /// Calculates the Gibbs' relative adsorption of component `i' with /// respect to `j': \Gamma_i^(j) /// @@ -133,8 +132,8 @@ macro_rules! impl_planar_interface { /// ------- /// SIArray2 /// - fn relative_adsorption(&self) -> PySIArray2 { - self.0.relative_adsorption().into() + fn relative_adsorption(&self) -> Moles> { + self.0.relative_adsorption() } /// Calculates the interfacial enrichment E_i. @@ -153,8 +152,8 @@ macro_rules! impl_planar_interface { /// ------- /// SINumber /// - fn interfacial_thickness(&self) -> PyResult { - Ok(self.0.interfacial_thickness()?.into()) + fn interfacial_thickness(&self) -> PyResult { + Ok(self.0.interfacial_thickness()?) } } }; diff --git a/feos-dft/src/python/interface/surface_tension_diagram.rs b/feos-dft/src/python/interface/surface_tension_diagram.rs index 836ecfd10..8ed743a2c 100644 --- a/feos-dft/src/python/interface/surface_tension_diagram.rs +++ b/feos-dft/src/python/interface/surface_tension_diagram.rs @@ -37,12 +37,13 @@ macro_rules! impl_surface_tension_diagram { impl PySurfaceTensionDiagram { #[new] #[pyo3(text_signature = "(dia, init_densities=None, n_grid=None, l_grid=None, critical_temperature=None, fix_equimolar_surface=None, solver=None)")] + #[pyo3(signature = (dia, init_densities=None, n_grid=None, l_grid=None, critical_temperature=None, fix_equimolar_surface=None, solver=None))] pub fn isotherm( dia: Vec, init_densities: Option, n_grid: Option, - l_grid: Option, - critical_temperature: Option, + l_grid: Option, + critical_temperature: Option, fix_equimolar_surface: Option, solver: Option, ) -> PyResult { @@ -78,13 +79,13 @@ macro_rules! impl_surface_tension_diagram { } #[getter] - pub fn get_surface_tension(&mut self) -> PySIArray1 { - self.0.surface_tension().into() + pub fn get_surface_tension(&mut self) -> SurfaceTension> { + self.0.surface_tension() } #[getter] - pub fn get_relative_adsorption(&self) -> Vec { - self.0.relative_adsorption().iter().cloned().map(|i| i.into()).collect() + pub fn get_relative_adsorption(&self) -> Vec>> { + self.0.relative_adsorption() } #[getter] @@ -93,8 +94,8 @@ macro_rules! impl_surface_tension_diagram { } #[getter] - pub fn interfacial_thickness(&self) -> PySIArray1 { - self.0.interfacial_thickness().into() + pub fn interfacial_thickness(&self) -> Length> { + self.0.interfacial_thickness() } } }; diff --git a/feos-dft/src/python/profile.rs b/feos-dft/src/python/profile.rs index bbebfa482..15e55d1cd 100644 --- a/feos-dft/src/python/profile.rs +++ b/feos-dft/src/python/profile.rs @@ -48,28 +48,28 @@ macro_rules! impl_profile { $( #[getter] - fn $ax(&self) -> PySIArray1 { - PySIArray1::from(Length::from_reduced(self.0.profile.grid.grids()[$ind].clone())) + fn $ax(&self) -> Length> { + Length::from_reduced(self.0.profile.grid.grids()[$ind].clone()) })+ #[getter] - fn get_temperature(&self) -> PySINumber { - PySINumber::from(self.0.profile.temperature) + fn get_temperature(&self) -> Temperature { + self.0.profile.temperature } #[getter] - fn get_density(&self) -> $si_arr2 { - $si_arr2::from(self.0.profile.density.clone()) + fn get_density(&self) -> Density<$si_arr2> { + self.0.profile.density.clone() } #[getter] - fn get_moles(&self) -> PySIArray1 { - PySIArray1::from(self.0.profile.moles()) + fn get_moles(&self) -> Moles> { + self.0.profile.moles() } #[getter] - fn get_total_moles(&self) -> PySINumber { - PySINumber::from(self.0.profile.total_moles()) + fn get_total_moles(&self) -> Moles { + self.0.profile.total_moles() } #[getter] @@ -119,10 +119,8 @@ macro_rules! impl_profile { fn entropy_density( &mut self, contributions: Contributions, - ) -> PyResult<$si_arr> { - Ok($si_arr::from( - self.0.profile.entropy_density(contributions)?, - )) + ) -> PyResult>, Volume>> { + Ok(self.0.profile.entropy_density(contributions)?) } /// Calculate the entropy of the inhomogeneous system. @@ -140,10 +138,8 @@ macro_rules! impl_profile { fn entropy( &mut self, contributions: Contributions, - ) -> PyResult { - Ok(PySINumber::from( - self.0.profile.entropy(contributions)?, - )) + ) -> PyResult { + Ok(self.0.profile.entropy(contributions)?) } /// Calculate the internal energy of the inhomogeneous system. @@ -161,48 +157,44 @@ macro_rules! impl_profile { fn internal_energy( &mut self, contributions: Contributions, - ) -> PyResult { - Ok(PySINumber::from( - self.0.profile.internal_energy(contributions)?, - )) + ) -> PyResult { + Ok(self.0.profile.internal_energy(contributions)?) } #[getter] - fn get_grand_potential_density(&self) -> PyResult<$si_arr> { - Ok($si_arr::from( - self.0.profile.grand_potential_density()?, - )) + fn get_grand_potential_density(&self) -> PyResult>> { + Ok(self.0.profile.grand_potential_density()?) } $( #[getter] - fn get_drho_dmu(&self) -> PyResult<$si_arr3> { - Ok(($si_arr3::from(self.0.profile.drho_dmu()?))) + fn get_drho_dmu(&self) -> PyResult>, MolarEnergy>> { + Ok(self.0.profile.drho_dmu()?) } )? #[getter] - fn get_dn_dmu(&self) -> PyResult { - Ok((PySIArray2::from(self.0.profile.dn_dmu()?))) + fn get_dn_dmu(&self) -> PyResult>, MolarEnergy>> { + Ok(self.0.profile.dn_dmu()?) } #[getter] - fn get_drho_dp(&self) -> PyResult<$si_arr2> { - Ok(($si_arr2::from(self.0.profile.drho_dp()?))) + fn get_drho_dp(&self) -> PyResult>, Pressure>> { + Ok(self.0.profile.drho_dp()?) } #[getter] - fn get_dn_dp(&self) -> PyResult { - Ok((PySIArray1::from(self.0.profile.dn_dp()?))) + fn get_dn_dp(&self) -> PyResult>, Pressure>> { + Ok(self.0.profile.dn_dp()?) } #[getter] - fn get_drho_dt(&self) -> PyResult<$si_arr2> { - Ok(($si_arr2::from(self.0.profile.drho_dt()?))) + fn get_drho_dt(&self) -> PyResult>, Temperature>> { + Ok(self.0.profile.drho_dt()?) } #[getter] - fn get_dn_dt(&self) -> PyResult { - Ok((PySIArray1::from(self.0.profile.dn_dt()?))) + fn get_dn_dt(&self) -> PyResult>, Temperature>> { + Ok(self.0.profile.dn_dt()?) } } }; @@ -215,11 +207,11 @@ macro_rules! impl_1d_profile { $struct, PyArray1, PyArray2, - PySIArray1, - PySIArray2, + Array1, + Array2, PyArray2, [$([0, $ax]),+], - PySIArray3 + Array3 ); }; } @@ -231,8 +223,8 @@ macro_rules! impl_2d_profile { $struct, PyArray2, PyArray3, - PySIArray2, - PySIArray3, + Array2, + Array3, PyArray3, [[0, $ax1], [1, $ax2]] ); @@ -240,15 +232,13 @@ macro_rules! impl_2d_profile { #[pymethods] impl $struct { #[getter] - fn get_edges(&self) -> [PySIArray1; 2] { - let [edge1, edge2] = self.0.profile.edges(); - [edge1.into(), edge2.into()] + fn get_edges(&self) -> [Length>; 2] { + self.0.profile.edges() } #[getter] - fn get_meshgrid(&self) -> [PySIArray2; 2] { - let [x, y] = self.0.profile.meshgrid(); - [x.into(), y.into()] + fn get_meshgrid(&self) -> [Length>; 2] { + self.0.profile.meshgrid() } } }; @@ -261,8 +251,8 @@ macro_rules! impl_3d_profile { $struct, PyArray3, PyArray4, - PySIArray3, - PySIArray4, + Array3, + Array4, PyArray4, [[0, $ax1], [1, $ax2], [2, $ax3]] ); @@ -270,15 +260,13 @@ macro_rules! impl_3d_profile { #[pymethods] impl $struct { #[getter] - fn get_edges(&self) -> [PySIArray1; 3] { - let [edge1, edge2, edge3] = self.0.profile.edges(); - [edge1.into(), edge2.into(), edge3.into()] + fn get_edges(&self) -> [Length>; 3] { + self.0.profile.edges() } #[getter] - fn get_meshgrid(&self) -> [PySIArray3; 3] { - let [x, y, z] = self.0.profile.meshgrid(); - [x.into(), y.into(), z.into()] + fn get_meshgrid(&self) -> [Length>; 3] { + self.0.profile.meshgrid() } } }; diff --git a/feos-dft/src/python/solvation.rs b/feos-dft/src/python/solvation.rs index b7e989d2d..bea784771 100644 --- a/feos-dft/src/python/solvation.rs +++ b/feos-dft/src/python/solvation.rs @@ -35,42 +35,38 @@ macro_rules! impl_solvation_profile { impl PySolvationProfile { #[new] #[pyo3(text_signature = "(bulk, n_grid, coordinates, sigma, epsilon_k, system_size=None, cutoff_radius=None, potential_cutoff=None)")] + #[pyo3(signature = (bulk, n_grid, coordinates, sigma, epsilon_k, system_size=None, cutoff_radius=None, potential_cutoff=None))] #[expect(clippy::too_many_arguments)] fn new<'py>( bulk: &PyState, n_grid: [usize; 3], - coordinates: &PySIArray2, + coordinates: Length>, sigma: &Bound<'py, PyArray1>, epsilon_k: &Bound<'py, PyArray1>, - system_size: Option<[PySINumber; 3]>, - cutoff_radius: Option, + system_size: Option<[Length; 3]>, + cutoff_radius: Option, potential_cutoff: Option, ) -> PyResult { - let s = match system_size { - Some(s) => Some([s[0].try_into()?, s[1].try_into()?, s[2].try_into()?]), - None => None - }; - Ok(Self(SolvationProfile::new( &bulk.0, n_grid, - coordinates.clone().try_into()?, + coordinates, sigma.to_owned_array(), epsilon_k.to_owned_array(), - s, - cutoff_radius.map(|r| r.try_into()).transpose()?, + system_size, + cutoff_radius, potential_cutoff, )?)) } #[getter] - fn get_grand_potential(&self) -> Option { - self.0.grand_potential.map(PySINumber::from) + fn get_grand_potential(&self) -> Option { + self.0.grand_potential } #[getter] - fn get_solvation_free_energy(&self) -> Option { - self.0.solvation_free_energy.map(PySINumber::from) + fn get_solvation_free_energy(&self) -> Option { + self.0.solvation_free_energy } } }; @@ -104,15 +100,8 @@ macro_rules! impl_pair_correlation { #[pymethods] impl PyPairCorrelation { #[new] - fn new( - bulk: PyState, - test_particle: usize, - n_grid: usize, - width: PySINumber, - ) -> PyResult { - let profile = - PairCorrelation::new(&bulk.0, test_particle, n_grid, width.try_into()?); - Ok(PyPairCorrelation(profile)) + fn new(bulk: PyState, test_particle: usize, n_grid: usize, width: Length) -> Self { + Self(PairCorrelation::new(&bulk.0, test_particle, n_grid, width)) } #[getter] @@ -127,8 +116,8 @@ macro_rules! impl_pair_correlation { } #[getter] - fn get_self_solvation_free_energy(&self) -> Option { - self.0.self_solvation_free_energy.map(PySINumber::from) + fn get_self_solvation_free_energy(&self) -> Option { + self.0.self_solvation_free_energy } #[getter] diff --git a/feos-dft/src/python/solver.rs b/feos-dft/src/python/solver.rs index 2b8a5e743..ab5f3c1e0 100644 --- a/feos-dft/src/python/solver.rs +++ b/feos-dft/src/python/solver.rs @@ -1,8 +1,9 @@ use crate::{DFTSolver, DFTSolverLog}; use feos_core::Verbosity; +use ndarray::Array1; use numpy::{PyArray1, ToPyArray}; use pyo3::prelude::*; -use quantity::python::PySIArray1; +use quantity::Time; /// Settings for the DFT solver. /// @@ -22,7 +23,7 @@ pub struct PyDFTSolver(pub DFTSolver); #[pymethods] impl PyDFTSolver { #[new] - #[pyo3(text_signature = "(verbosity=None)")] + #[pyo3(text_signature = "(verbosity=None)", signature = (verbosity=None))] fn new(verbosity: Option) -> Self { Self(DFTSolver::new(verbosity)) } @@ -59,6 +60,7 @@ impl PyDFTSolver { /// ------- /// DFTSolver #[pyo3(text_signature = "($self, log=None, max_iter=None, tol=None, damping_coefficient=None)")] + #[pyo3(signature = (log=None, max_iter=None, tol=None, damping_coefficient=None))] fn picard_iteration( &self, log: Option, @@ -97,7 +99,8 @@ impl PyDFTSolver { /// ------- /// DFTSolver #[pyo3( - text_signature = "($self, log=None, max_iter=None, tol=None, damping_coefficient=None, mmax=None)" + text_signature = "($self, log=None, max_iter=None, tol=None, damping_coefficient=None, mmax=None)", + signature = (log=None, max_iter=None, tol=None, damping_coefficient=None, mmax=None) )] fn anderson_mixing( &self, @@ -134,7 +137,10 @@ impl PyDFTSolver { /// Returns /// ------- /// DFTSolver - #[pyo3(text_signature = "($self, log=None, max_iter=None, max_iter_gmres=None, tol=None)")] + #[pyo3( + text_signature = "($self, log=None, max_iter=None, max_iter_gmres=None, tol=None)", + signature = (log=None, max_iter=None, max_iter_gmres=None, tol=None) + )] fn newton( &self, log: Option, @@ -166,8 +172,8 @@ impl PyDFTSolverLog { } #[getter] - fn get_time(&self) -> PySIArray1 { - self.0.time().into() + fn get_time(&self) -> Time> { + self.0.time() } #[getter] diff --git a/feos-dft/src/solvation/pair_correlation.rs b/feos-dft/src/solvation/pair_correlation.rs index 23dcad1d1..6229e5fca 100644 --- a/feos-dft/src/solvation/pair_correlation.rs +++ b/feos-dft/src/solvation/pair_correlation.rs @@ -4,9 +4,9 @@ use crate::functional::{HelmholtzEnergyFunctional, DFT}; use crate::profile::MAX_POTENTIAL; use crate::solver::DFTSolver; use crate::{Axis, DFTProfile, Grid}; -use feos_core::si::{Energy, Length}; -use feos_core::{Contributions, EosResult, State}; +use feos_core::{Contributions, EosResult, ReferenceSystem, State}; use ndarray::prelude::*; +use quantity::{Energy, Length}; /// The underlying pair potential, that the Helmholtz energy functional /// models. diff --git a/feos-dft/src/solvation/solvation_profile.rs b/feos-dft/src/solvation/solvation_profile.rs index 75b56be4c..2b43ab23d 100644 --- a/feos-dft/src/solvation/solvation_profile.rs +++ b/feos-dft/src/solvation/solvation_profile.rs @@ -4,10 +4,10 @@ use crate::functional::{HelmholtzEnergyFunctional, DFT}; use crate::geometry::{Axis, Grid}; use crate::profile::{DFTProfile, CUTOFF_RADIUS, MAX_POTENTIAL}; use crate::solver::DFTSolver; -use feos_core::si::{Energy, Length, MolarEnergy, Moles}; -use feos_core::{Contributions, EosResult, State}; +use feos_core::{Contributions, EosResult, ReferenceSystem, State}; use ndarray::prelude::*; use ndarray::Zip; +use quantity::{Energy, Length, MolarEnergy, Moles}; /// Density profile and properties of a solute in a inhomogeneous bulk fluid. pub struct SolvationProfile { diff --git a/feos-dft/src/solver.rs b/feos-dft/src/solver.rs index ed0bd07c3..88fa6476f 100644 --- a/feos-dft/src/solver.rs +++ b/feos-dft/src/solver.rs @@ -2,14 +2,14 @@ use crate::{ DFTProfile, FunctionalContribution, HelmholtzEnergyFunctional, WeightFunction, WeightFunctionShape, }; -use feos_core::si::{Time, SECOND}; -use feos_core::{log_iter, log_result, EosError, EosResult, Verbosity}; +use feos_core::{log_iter, log_result, EosError, EosResult, ReferenceSystem, Verbosity}; use nalgebra::{DMatrix, DVector}; use ndarray::prelude::*; use ndarray::RemoveAxis; use petgraph::graph::Graph; use petgraph::visit::EdgeRef; use petgraph::Directed; +use quantity::{Time, SECOND}; use std::collections::VecDeque; use std::fmt; use std::ops::AddAssign; @@ -573,10 +573,12 @@ where let mut pd2 = Array::zeros(dim).into_dimensionality().unwrap(); c.second_partial_derivatives( temperature, - wd.view().into_shape((nwd, ngrid)).unwrap(), - phi.view_mut().into_shape(ngrid).unwrap(), - pd.view_mut().into_shape((nwd, ngrid)).unwrap(), - pd2.view_mut().into_shape((nwd, nwd, ngrid)).unwrap(), + wd.view().into_shape_with_order((nwd, ngrid)).unwrap(), + phi.view_mut().into_shape_with_order(ngrid).unwrap(), + pd.view_mut().into_shape_with_order((nwd, ngrid)).unwrap(), + pd2.view_mut() + .into_shape_with_order((nwd, nwd, ngrid)) + .unwrap(), )?; second_partial_derivatives.push(pd2); } diff --git a/src/association/mod.rs b/src/association/mod.rs index 98f875e56..e0edcdaf8 100644 --- a/src/association/mod.rs +++ b/src/association/mod.rs @@ -647,9 +647,10 @@ mod tests_gc_pcsaft { use super::*; use crate::gc_pcsaft::eos::parameter::test::*; use approx::assert_relative_eq; - use feos_core::si::{Pressure, METER, MOL, PASCAL}; + use feos_core::ReferenceSystem; use ndarray::arr1; use num_dual::Dual64; + use quantity::{Pressure, METER, MOL, PASCAL}; use typenum::P3; #[test] diff --git a/src/eos.rs b/src/eos.rs index 7ad8c063b..52abf3b65 100644 --- a/src/eos.rs +++ b/src/eos.rs @@ -15,7 +15,7 @@ use crate::uvtheory::UVTheory; use feos_core::cubic::PengRobinson; #[cfg(feature = "python")] use feos_core::python::user_defined::PyResidual; -use feos_core::si::*; +use quantity::*; use feos_core::*; use feos_derive::{Components, Residual}; use ndarray::{Array1, ScalarOperand}; diff --git a/src/epcsaft/eos/mod.rs b/src/epcsaft/eos/mod.rs index c6a8604fa..ce4a7ef5e 100644 --- a/src/epcsaft/eos/mod.rs +++ b/src/epcsaft/eos/mod.rs @@ -2,10 +2,11 @@ use crate::association::Association; use crate::epcsaft::parameters::ElectrolytePcSaftParameters; use crate::hard_sphere::{HardSphere, HardSphereProperties}; use feos_core::parameter::Parameter; -use feos_core::{si::*, StateHD}; +use feos_core::StateHD; use feos_core::{Components, Residual}; use ndarray::Array1; use num_dual::DualNum; +use quantity::*; use std::f64::consts::FRAC_PI_6; use std::fmt; use std::sync::Arc; @@ -21,8 +22,8 @@ use hard_chain::HardChain; use ionic::Ionic; /// Implemented variants of the ePC-SAFT equation of state. -#[derive(Copy, Clone)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Copy, Clone, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum ElectrolytePcSaftVariants { Advanced, Revised, @@ -205,7 +206,6 @@ mod tests { butane_parameters, propane_butane_parameters, propane_parameters, water_parameters, }; use approx::assert_relative_eq; - use feos_core::si::{BAR, KELVIN, METER, PASCAL, RGAS}; use feos_core::*; use ndarray::arr1; use typenum::P3; diff --git a/src/epcsaft/python.rs b/src/epcsaft/python.rs index 258b9ae96..9a807702a 100644 --- a/src/epcsaft/python.rs +++ b/src/epcsaft/python.rs @@ -47,7 +47,8 @@ pub struct PyElectrolytePcSaftRecord(ElectrolytePcSaftRecord); impl PyElectrolytePcSaftRecord { #[new] #[pyo3( - text_signature = "(m, sigma, epsilon_k, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, permittivity_record=None)" + text_signature = "(m, sigma, epsilon_k, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, z=None, permittivity_record=None)", + signature = (m, sigma, epsilon_k, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, z=None, permittivity_record=None) )] #[expect(clippy::too_many_arguments)] fn new( diff --git a/src/estimator/binary_vle.rs b/src/estimator/binary_vle.rs index b3c9d39b2..7e95b2c3a 100644 --- a/src/estimator/binary_vle.rs +++ b/src/estimator/binary_vle.rs @@ -1,13 +1,11 @@ use super::{DataSet, EstimatorError, Phase}; -use feos_core::si::{ - MolarEnergy, Moles, Pressure, Quantity, Temperature, _Dimensionless, PASCAL, RGAS, -}; use feos_core::{ - Contributions, DensityInitialization, PhaseDiagram, PhaseEquilibrium, Residual, State, - TemperatureOrPressure, + Contributions, DensityInitialization, PhaseDiagram, PhaseEquilibrium, ReferenceSystem, + Residual, State, TemperatureOrPressure, }; use itertools::izip; use ndarray::{arr1, s, Array1, ArrayView1, Axis}; +use quantity::{MolarEnergy, Moles, Pressure, Quantity, Temperature, _Dimensionless, PASCAL, RGAS}; use std::fmt; use std::iter::FromIterator; use std::ops::Sub; @@ -177,7 +175,7 @@ impl DataSet for BinaryVlePressure { Ok(vle .vapor() .pressure(Contributions::Total) - .convert_into(self.unit)) + .convert_to(self.unit)) }) .collect() } diff --git a/src/estimator/diffusion.rs b/src/estimator/diffusion.rs index c47798a32..df07f8deb 100644 --- a/src/estimator/diffusion.rs +++ b/src/estimator/diffusion.rs @@ -1,8 +1,8 @@ use super::{DataSet, EstimatorError, Phase}; -use feos_core::si::{self, Moles, Pressure, Temperature, CENTI, METER, SECOND}; -use feos_core::{DensityInitialization, EntropyScaling, Residual, State}; +use feos_core::{DensityInitialization, EntropyScaling, ReferenceSystem, Residual, State}; use itertools::izip; use ndarray::{arr1, Array1}; +use quantity::{self, Moles, Pressure, Temperature, CENTI, METER, SECOND}; use std::sync::Arc; use typenum::P2; @@ -10,7 +10,7 @@ use typenum::P2; #[derive(Clone)] pub struct Diffusion { pub target: Array1, - unit: si::Diffusivity, + unit: quantity::Diffusivity, temperature: Temperature>, pressure: Pressure>, initial_density: Vec, @@ -19,7 +19,7 @@ pub struct Diffusion { impl Diffusion { /// Create a new data set for experimental diffusion data. pub fn new( - target: si::Diffusivity>, + target: quantity::Diffusivity>, temperature: Temperature>, pressure: Pressure>, phase: Option<&Vec>, @@ -68,7 +68,7 @@ impl DataSet for Diffusion { .map(|(t, p, &initial_density)| { State::new_npt(eos, t, p, &moles, initial_density)? .diffusion() - .map(|lambda| (lambda / self.unit).into_value()) + .map(|lambda| lambda.convert_to(self.unit)) .map_err(EstimatorError::from) }) .collect() diff --git a/src/estimator/liquid_density.rs b/src/estimator/liquid_density.rs index 156c37459..3c20084d2 100644 --- a/src/estimator/liquid_density.rs +++ b/src/estimator/liquid_density.rs @@ -1,7 +1,9 @@ use super::{DataSet, EstimatorError}; -use feos_core::si::{MassDensity, Moles, Pressure, Temperature, KILOGRAM, METER}; -use feos_core::{DensityInitialization, PhaseEquilibrium, Residual, SolverOptions, State}; +use feos_core::{ + DensityInitialization, PhaseEquilibrium, ReferenceSystem, Residual, SolverOptions, State, +}; use ndarray::{arr1, Array1}; +use quantity::{MassDensity, Moles, Pressure, Temperature, KILOGRAM, METER}; use std::sync::Arc; use typenum::P3; diff --git a/src/estimator/mod.rs b/src/estimator/mod.rs index e223f429f..ff0c85ad0 100644 --- a/src/estimator/mod.rs +++ b/src/estimator/mod.rs @@ -30,8 +30,8 @@ pub use diffusion::Diffusion; pub mod python; /// Different phases of experimental data points. -#[derive(Clone, Copy)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum Phase { Vapor, Liquid, diff --git a/src/estimator/python.rs b/src/estimator/python.rs index d01d0335c..3031f1c24 100644 --- a/src/estimator/python.rs +++ b/src/estimator/python.rs @@ -247,23 +247,24 @@ macro_rules! impl_estimator { /// is used. If that fails, the default temperatures of the critical point routine /// are used. #[staticmethod] - #[pyo3(text_signature = "(target, temperature, extrapolate, critical_temperature=None, max_iter=None, verbosity=None)")] + #[pyo3(text_signature = "(target, temperature, extrapolate, critical_temperature=None, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (target, temperature, extrapolate, critical_temperature=None, max_iter=None, tol=None, verbosity=None))] fn vapor_pressure( - target: &PySIArray1, - temperature: &PySIArray1, + target: Pressure>, + temperature: Temperature>, extrapolate: Option, - critical_temperature: Option<&PySINumber>, + critical_temperature: Option, max_iter: Option, tol: Option, verbosity: Option, - ) -> PyResult { - Ok(Self(Arc::new(VaporPressure::new( - target.clone().try_into()?, - temperature.clone().try_into()?, + ) -> Self { + Self(Arc::new(VaporPressure::new( + target, + temperature, extrapolate.unwrap_or(false), - critical_temperature.and_then(|tc| tc.clone().try_into().ok()), + critical_temperature, Some((max_iter, tol, verbosity).into()), - )))) + ))) } /// Create a DataSet with experimental data for liquid density. @@ -283,15 +284,15 @@ macro_rules! impl_estimator { #[staticmethod] #[pyo3(text_signature = "(target, temperature, pressure)")] fn liquid_density( - target: &PySIArray1, - temperature: &PySIArray1, - pressure: &PySIArray1, - ) -> PyResult { - Ok(Self(Arc::new(LiquidDensity::new( - target.clone().try_into()?, - temperature.clone().try_into()?, - pressure.clone().try_into()?, - )))) + target: MassDensity>, + temperature: Temperature>, + pressure: Pressure>, + ) -> Self { + Self(Arc::new(LiquidDensity::new( + target, + temperature, + pressure, + ))) } /// Create a DataSet with experimental data for liquid density @@ -317,19 +318,20 @@ macro_rules! impl_estimator { /// ------- /// DataSet #[staticmethod] - #[pyo3(text_signature = "(target, temperature)")] + #[pyo3(text_signature = "(target, temperature, max_iter=None, tol=None, verbosity=None)")] + #[pyo3(signature = (target, temperature, max_iter=None, tol=None, verbosity=None))] fn equilibrium_liquid_density( - target: &PySIArray1, - temperature: &PySIArray1, + target: MassDensity>, + temperature: Temperature>, max_iter: Option, tol: Option, verbosity: Option, - ) -> PyResult { - Ok(Self(Arc::new(EquilibriumLiquidDensity::new( - target.clone().try_into()?, - temperature.clone().try_into()?, + ) -> Self { + Self(Arc::new(EquilibriumLiquidDensity::new( + target, + temperature, Some((max_iter, tol, verbosity).into()), - )))) + ))) } /// Create a DataSet with experimental data for binary @@ -352,17 +354,17 @@ macro_rules! impl_estimator { #[staticmethod] #[pyo3(text_signature = "(temperature, pressure, liquid_molefracs, vapor_molefracs)")] fn binary_vle_chemical_potential( - temperature: &PySIArray1, - pressure: &PySIArray1, + temperature: Temperature>, + pressure: Pressure>, liquid_molefracs: &Bound<'_, PyArray1>, vapor_molefracs: &Bound<'_, PyArray1>, - ) -> PyResult { - Ok(Self(Arc::new(BinaryVleChemicalPotential::new( - temperature.clone().try_into()?, - pressure.clone().try_into()?, + ) -> Self { + Self(Arc::new(BinaryVleChemicalPotential::new( + temperature, + pressure, liquid_molefracs.to_owned_array(), vapor_molefracs.to_owned_array(), - )))) + ))) } /// Create a DataSet with experimental data for binary @@ -385,17 +387,17 @@ macro_rules! impl_estimator { #[staticmethod] #[pyo3(text_signature = "(temperature, pressure, molefracs, phase)")] fn binary_vle_pressure( - temperature: &PySIArray1, - pressure: &PySIArray1, + temperature: Temperature>, + pressure: Pressure>, molefracs: &Bound<'_, PyArray1>, phase: Phase, - ) -> PyResult { - Ok(Self(Arc::new(BinaryVlePressure::new( - temperature.clone().try_into()?, - pressure.clone().try_into()?, + ) -> Self { + Self(Arc::new(BinaryVlePressure::new( + temperature, + pressure, molefracs.to_owned_array(), phase, - )))) + ))) } /// Create a DataSet with experimental data for binary @@ -421,33 +423,35 @@ macro_rules! impl_estimator { /// DataSet #[staticmethod] #[pyo3(text_signature = "(specification, temperature_or_pressure, liquid_molefracs=None, vapor_molefracs=None, npoints=None)")] + #[pyo3(signature = (specification, temperature_or_pressure, liquid_molefracs=None, vapor_molefracs=None, npoints=None))] fn binary_phase_diagram( - specification: PySINumber, - temperature_or_pressure: PySIArray1, + specification: Bound<'_, PyAny>, + temperature_or_pressure: Bound<'_, PyAny>, liquid_molefracs: Option<&Bound<'_, PyArray1>>, vapor_molefracs: Option<&Bound<'_, PyArray1>>, npoints: Option, ) -> PyResult { - if let Ok(t) = Temperature::::try_from(specification) { + if let Ok(t) = specification.extract::() { Ok(Self(Arc::new(BinaryPhaseDiagram::new( t, - temperature_or_pressure.try_into()?, + temperature_or_pressure.extract()?, liquid_molefracs.map(|x| x.to_owned_array()), vapor_molefracs.map(|x| x.to_owned_array()), npoints, )))) - } else if let Ok(p) = Pressure::::try_from(specification) { + } else if let Ok(p) = specification.extract::() { Ok(Self(Arc::new(BinaryPhaseDiagram::new( p, - temperature_or_pressure.try_into()?, + temperature_or_pressure.extract()?, liquid_molefracs.map(|x| x.to_owned_array()), vapor_molefracs.map(|x| x.to_owned_array()), npoints, )))) } else { - Ok(Err(EosError::WrongUnits("temperature or pressure".into(), - quantity::si::SINumber::from(specification).to_string() - ))?) + Err(PyErr::new::(format!( + "Wrong units! Expected K or Pa, got {}.", + temperature_or_pressure.call_method0("__repr__")? + ))) } } @@ -652,18 +656,20 @@ macro_rules! impl_estimator_entropy_scaling { /// ------- /// DataSet #[staticmethod] + #[pyo3(text_signature = "(target, temperature, pressure, phase=None)")] + #[pyo3(signature = (target, temperature, pressure, phase=None))] fn viscosity( - target: &PySIArray1, - temperature: &PySIArray1, - pressure: &PySIArray1, + target: quantity::Viscosity>, + temperature: Temperature>, + pressure: Pressure>, phase: Option>, - ) -> PyResult { - Ok(Self(Arc::new($crate::estimator::Viscosity::new( - target.clone().try_into()?, - temperature.clone().try_into()?, - pressure.clone().try_into()?, + ) -> Self { + Self(Arc::new($crate::estimator::Viscosity::new( + target, + temperature, + pressure, phase.as_ref(), - )))) + ))) } /// Create a DataSet with experimental data for thermal conductivity. @@ -685,18 +691,20 @@ macro_rules! impl_estimator_entropy_scaling { /// ------- /// DataSet #[staticmethod] + #[pyo3(text_signature = "(target, temperature, pressure, phase=None)")] + #[pyo3(signature = (target, temperature, pressure, phase=None))] fn thermal_conductivity( - target: &PySIArray1, - temperature: &PySIArray1, - pressure: &PySIArray1, + target: quantity::ThermalConductivity>, + temperature: Temperature>, + pressure: Pressure>, phase: Option>, - ) -> PyResult { - Ok(Self(Arc::new($crate::estimator::ThermalConductivity::new( - target.clone().try_into()?, - temperature.clone().try_into()?, - pressure.clone().try_into()?, + ) -> Self { + Self(Arc::new($crate::estimator::ThermalConductivity::new( + target, + temperature, + pressure, phase.as_ref(), - )))) + ))) } /// Create a DataSet with experimental data for diffusion coefficient. @@ -718,18 +726,20 @@ macro_rules! impl_estimator_entropy_scaling { /// ------- /// DataSet #[staticmethod] + #[pyo3(text_signature = "(target, temperature, pressure, phase=None)")] + #[pyo3(signature = (target, temperature, pressure, phase=None))] fn diffusion( - target: &PySIArray1, - temperature: &PySIArray1, - pressure: &PySIArray1, + target: quantity::Diffusivity>, + temperature: Temperature>, + pressure: Pressure>, phase: Option>, - ) -> PyResult { - Ok(Self(Arc::new(Diffusion::new( - target.clone().try_into()?, - temperature.clone().try_into()?, - pressure.clone().try_into()?, + ) -> Self { + Self(Arc::new(Diffusion::new( + target, + temperature, + pressure, phase.as_ref(), - )))) + ))) } } }; diff --git a/src/estimator/thermal_conductivity.rs b/src/estimator/thermal_conductivity.rs index 0686be6a1..c2baec2b0 100644 --- a/src/estimator/thermal_conductivity.rs +++ b/src/estimator/thermal_conductivity.rs @@ -1,15 +1,15 @@ use super::{DataSet, EstimatorError, Phase}; -use feos_core::si::{self, Moles, Pressure, Temperature, KELVIN, METER, WATT}; -use feos_core::{DensityInitialization, EntropyScaling, Residual, State}; +use feos_core::{DensityInitialization, EntropyScaling, ReferenceSystem, Residual, State}; use itertools::izip; use ndarray::{arr1, Array1}; +use quantity::{self, Moles, Pressure, Temperature, KELVIN, METER, WATT}; use std::sync::Arc; /// Store experimental thermal conductivity data. #[derive(Clone)] pub struct ThermalConductivity { pub target: Array1, - unit: si::ThermalConductivity, + unit: quantity::ThermalConductivity, temperature: Temperature>, pressure: Pressure>, initial_density: Vec, @@ -18,7 +18,7 @@ pub struct ThermalConductivity { impl ThermalConductivity { /// Create a new data set for experimental thermal conductivity data. pub fn new( - target: si::ThermalConductivity>, + target: quantity::ThermalConductivity>, temperature: Temperature>, pressure: Pressure>, phase: Option<&Vec>, @@ -66,7 +66,7 @@ impl DataSet for ThermalConductivity { .map(|(t, p, &initial_density)| { State::new_npt(eos, t, p, &moles, initial_density)? .thermal_conductivity() - .map(|lambda| (lambda / self.unit).into_value()) + .map(|lambda| lambda.convert_to(self.unit)) .map_err(EstimatorError::from) }) .collect() diff --git a/src/estimator/vapor_pressure.rs b/src/estimator/vapor_pressure.rs index 82d01b92f..2083e1d65 100644 --- a/src/estimator/vapor_pressure.rs +++ b/src/estimator/vapor_pressure.rs @@ -1,7 +1,7 @@ use super::{DataSet, EstimatorError}; -use feos_core::si::{Pressure, Temperature, PASCAL}; -use feos_core::{Contributions, PhaseEquilibrium, Residual, SolverOptions, State}; +use feos_core::{Contributions, PhaseEquilibrium, ReferenceSystem, Residual, SolverOptions, State}; use ndarray::{arr1, Array1}; +use quantity::{Pressure, Temperature, PASCAL}; use std::sync::Arc; /// Store experimental vapor pressure data. diff --git a/src/estimator/viscosity.rs b/src/estimator/viscosity.rs index bf2a49b54..375bf9052 100644 --- a/src/estimator/viscosity.rs +++ b/src/estimator/viscosity.rs @@ -1,17 +1,15 @@ use super::{DataSet, EstimatorError, Phase}; -use feos_core::{ - si::{self, Moles, Pressure, Temperature, MILLI, PASCAL, SECOND}, - DensityInitialization, EntropyScaling, Residual, State, -}; +use feos_core::{DensityInitialization, EntropyScaling, ReferenceSystem, Residual, State}; use itertools::izip; use ndarray::{arr1, Array1}; +use quantity::{Moles, Pressure, Temperature, MILLI, PASCAL, SECOND}; use std::sync::Arc; /// Store experimental viscosity data. #[derive(Clone)] pub struct Viscosity { pub target: Array1, - unit: si::Viscosity, + unit: quantity::Viscosity, temperature: Temperature>, pressure: Pressure>, initial_density: Vec, @@ -20,7 +18,7 @@ pub struct Viscosity { impl Viscosity { /// Create a new data set for experimental viscosity data. pub fn new( - target: si::Viscosity>, + target: quantity::Viscosity>, temperature: Temperature>, pressure: Pressure>, phase: Option<&Vec>, @@ -68,7 +66,7 @@ impl DataSet for Viscosity { .map(|(t, p, &initial_density)| { State::new_npt(eos, t, p, &moles, initial_density)? .viscosity() - .map(|viscosity| (viscosity / self.unit).into_value()) + .map(|viscosity| viscosity.convert_to(self.unit)) .map_err(EstimatorError::from) }) .collect() diff --git a/src/functional.rs b/src/functional.rs index 06cb2c6a8..784ba4166 100644 --- a/src/functional.rs +++ b/src/functional.rs @@ -7,7 +7,7 @@ use crate::pcsaft::{PcSaftFunctional, PcSaftFunctionalContribution}; use crate::pets::{PetsFunctional, PetsFunctionalContribution}; #[cfg(feature = "saftvrqmie")] use crate::saftvrqmie::{SaftVRQMieFunctional, SaftVRQMieFunctionalContribution}; -use feos_core::si::MolarWeight; +use quantity::MolarWeight; use feos_core::*; use feos_derive::FunctionalContribution; use feos_derive::{Components, HelmholtzEnergyFunctional}; diff --git a/src/gc_pcsaft/dft/mod.rs b/src/gc_pcsaft/dft/mod.rs index 4961906b0..127f4b7d9 100644 --- a/src/gc_pcsaft/dft/mod.rs +++ b/src/gc_pcsaft/dft/mod.rs @@ -3,7 +3,7 @@ use super::record::GcPcSaftAssociationRecord; use crate::association::{Association, AssociationStrength}; use crate::hard_sphere::{FMTContribution, FMTVersion, HardSphereProperties, MonomerShape}; use feos_core::parameter::ParameterHetero; -use feos_core::si::{MolarWeight, GRAM, MOL}; +use quantity::{MolarWeight, GRAM, MOL}; use feos_core::{Components, EosResult}; use feos_derive::FunctionalContribution; use feos_dft::adsorption::FluidParameters; diff --git a/src/gc_pcsaft/eos/dispersion.rs b/src/gc_pcsaft/eos/dispersion.rs index 5caf1b3c4..a46160830 100644 --- a/src/gc_pcsaft/eos/dispersion.rs +++ b/src/gc_pcsaft/eos/dispersion.rs @@ -132,9 +132,10 @@ mod test { use super::*; use crate::gc_pcsaft::eos::parameter::test::*; use approx::assert_relative_eq; - use feos_core::si::{Pressure, METER, MOL, PASCAL}; + use feos_core::ReferenceSystem; use ndarray::arr1; use num_dual::Dual64; + use quantity::{Pressure, METER, MOL, PASCAL}; use typenum::P3; #[test] diff --git a/src/gc_pcsaft/eos/hard_chain.rs b/src/gc_pcsaft/eos/hard_chain.rs index e88255f28..bd9f048d4 100644 --- a/src/gc_pcsaft/eos/hard_chain.rs +++ b/src/gc_pcsaft/eos/hard_chain.rs @@ -47,9 +47,10 @@ mod test { use super::*; use crate::gc_pcsaft::eos::parameter::test::*; use approx::assert_relative_eq; - use feos_core::si::{Pressure, METER, MOL, PASCAL}; + use feos_core::ReferenceSystem; use ndarray::arr1; use num_dual::Dual64; + use quantity::{Pressure, METER, MOL, PASCAL}; use typenum::P3; #[test] diff --git a/src/gc_pcsaft/eos/mod.rs b/src/gc_pcsaft/eos/mod.rs index c425fa3db..df7760f1d 100644 --- a/src/gc_pcsaft/eos/mod.rs +++ b/src/gc_pcsaft/eos/mod.rs @@ -1,9 +1,9 @@ use crate::association::Association; use crate::hard_sphere::{HardSphere, HardSphereProperties}; use feos_core::parameter::ParameterHetero; -use feos_core::si::{MolarWeight, GRAM, MOL}; use feos_core::{Components, Residual}; use ndarray::Array1; +use quantity::{MolarWeight, GRAM, MOL}; use std::f64::consts::FRAC_PI_6; use std::sync::Arc; @@ -151,10 +151,11 @@ mod test { use crate::gc_pcsaft::eos::parameter::test::*; use crate::hard_sphere::HardSphereProperties; use approx::assert_relative_eq; - use feos_core::si::{Pressure, METER, MOL, PASCAL}; + use feos_core::ReferenceSystem; use feos_core::StateHD; use ndarray::arr1; use num_dual::Dual64; + use quantity::{Pressure, METER, MOL, PASCAL}; use typenum::P3; #[test] diff --git a/src/gc_pcsaft/eos/parameter.rs b/src/gc_pcsaft/eos/parameter.rs index 5ba0311b7..6642fc2a9 100644 --- a/src/gc_pcsaft/eos/parameter.rs +++ b/src/gc_pcsaft/eos/parameter.rs @@ -5,7 +5,7 @@ use feos_core::parameter::{ BinaryRecord, ChemicalRecord, Identifier, ParameterError, ParameterHetero, SegmentCount, SegmentRecord, }; -use feos_core::si::{JOULE, KB, KELVIN}; +use quantity::{JOULE, KB, KELVIN}; use indexmap::IndexMap; use ndarray::{Array1, Array2}; use num_dual::DualNum; diff --git a/src/gc_pcsaft/python/mod.rs b/src/gc_pcsaft/python/mod.rs index 80d7c15c0..c56339311 100644 --- a/src/gc_pcsaft/python/mod.rs +++ b/src/gc_pcsaft/python/mod.rs @@ -25,7 +25,8 @@ pub struct PyGcPcSaftRecord(GcPcSaftRecord); impl PyGcPcSaftRecord { #[new] #[pyo3( - text_signature = "(m, sigma, epsilon_k, mu=None, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, psi_dft=None)" + text_signature = "(m, sigma, epsilon_k, mu=None, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, psi_dft=None)", + signature = (m, sigma, epsilon_k, mu=None, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, psi_dft=None) )] #[expect(clippy::too_many_arguments)] fn new( diff --git a/src/hard_sphere/dft.rs b/src/hard_sphere/dft.rs index 2f4a2b7de..a5095069b 100644 --- a/src/hard_sphere/dft.rs +++ b/src/hard_sphere/dft.rs @@ -1,4 +1,3 @@ -use feos_core::si::MolarWeight; use feos_core::{Components, EosResult}; use feos_dft::adsorption::FluidParameters; use feos_dft::solvation::PairPotential; @@ -8,6 +7,7 @@ use feos_dft::{ }; use ndarray::*; use num_dual::DualNum; +use quantity::MolarWeight; use std::f64::consts::PI; use std::fmt; use std::sync::Arc; @@ -18,8 +18,8 @@ const PI36M1: f64 = 1.0 / (36.0 * PI); const N3_CUTOFF: f64 = 1e-5; /// Different versions of fundamental measure theory. -#[derive(Clone, Copy)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum FMTVersion { /// 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, diff --git a/src/ideal_gas/dippr.rs b/src/ideal_gas/dippr.rs index e6356bd63..5cf2fa946 100644 --- a/src/ideal_gas/dippr.rs +++ b/src/ideal_gas/dippr.rs @@ -1,8 +1,8 @@ use feos_core::parameter::{NoBinaryModelRecord, Parameter, ParameterError, PureRecord}; -use feos_core::si::{MolarEntropy, Temperature, JOULE, KELVIN, KILO, MOL}; use feos_core::{Components, EosResult, IdealGas}; use ndarray::{Array1, Array2}; use num_dual::DualNum; +use quantity::{MolarEntropy, Temperature, JOULE, KELVIN, KILO, MOL}; use serde::{Deserialize, Serialize}; use std::fmt; @@ -156,7 +156,7 @@ impl Dippr { temperature: Temperature, molefracs: &Array1, ) -> EosResult { - let t = temperature.convert_into(KELVIN); + let t = temperature.convert_to(KELVIN); let c_p: f64 = molefracs .iter() .zip(&self.0) @@ -206,9 +206,9 @@ impl IdealGas for Dippr { mod tests { use approx::assert_relative_eq; use feos_core::parameter::Identifier; - use feos_core::si::*; use feos_core::{Contributions, EquationOfState, StateBuilder}; use num_dual::first_derivative; + use quantity::*; use std::sync::Arc; use typenum::P3; @@ -231,7 +231,7 @@ mod tests { .total_moles(MOL) .build()?; - let t = temperature.convert_into(KELVIN); + let t = temperature.convert_to(KELVIN); let c_p_direct = record.model_record.c_p(t); let (_, c_p) = first_derivative(|t| record.model_record.c_p_integral(t), t); let (_, c_p_t) = first_derivative(|t| record.model_record.c_p_t_integral(t), t); @@ -275,7 +275,7 @@ mod tests { .total_moles(MOL) .build()?; - let t = temperature.convert_into(KELVIN); + let t = temperature.convert_to(KELVIN); let c_p_direct = record.model_record.c_p(t); let (_, c_p) = first_derivative(|t| record.model_record.c_p_integral(t), t); let (_, c_p_t) = first_derivative(|t| record.model_record.c_p_t_integral(t), t); @@ -321,7 +321,7 @@ mod tests { .total_moles(MOL) .build()?; - let t = temperature.convert_into(KELVIN); + let t = temperature.convert_to(KELVIN); let c_p_direct = record.model_record.c_p(t); let (_, c_p) = first_derivative(|t| record.model_record.c_p_integral(t), t); let (_, c_p_t) = first_derivative(|t| record.model_record.c_p_t_integral(t), t); diff --git a/src/ideal_gas/joback.rs b/src/ideal_gas/joback.rs index 2d4498b93..da62b803a 100644 --- a/src/ideal_gas/joback.rs +++ b/src/ideal_gas/joback.rs @@ -3,10 +3,10 @@ use conv::ValueInto; use feos_core::parameter::*; -use feos_core::si::{MolarEntropy, Temperature}; -use feos_core::{Components, EosResult, IdealGas}; +use feos_core::{Components, EosResult, IdealGas, ReferenceSystem}; use ndarray::{Array1, Array2}; use num_dual::*; +use quantity::{MolarEntropy, Temperature}; use serde::{Deserialize, Serialize}; use std::fmt; @@ -105,7 +105,7 @@ impl Joback { x * (m.a + m.b * t + m.c * t.powi(2) + m.d * t.powi(3) + m.e * t.powi(4)) }) .sum(); - Ok(c_p / RGAS * feos_core::si::RGAS) + Ok(c_p / RGAS * quantity::RGAS) } } @@ -163,9 +163,9 @@ const KB: f64 = 1.38064852e-23; #[cfg(test)] mod tests { use approx::assert_relative_eq; - use feos_core::si::*; use feos_core::{Contributions, EquationOfState, State, StateBuilder}; use ndarray::arr1; + use quantity::*; use std::sync::Arc; use typenum::P3; diff --git a/src/lib.rs b/src/lib.rs index a74552957..39f741a3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! use feos::pcsaft::{PcSaft, PcSaftParameters}; //! use feos_core::parameter::{IdentifierOption, Parameter}; //! use feos_core::{Contributions, State}; -//! use feos_core::si::KELVIN; +//! use quantity::KELVIN; //! use std::sync::Arc; //! //! // Read parameters from json file. diff --git a/src/pcsaft/dft/mod.rs b/src/pcsaft/dft/mod.rs index 5d0051856..81e3d241e 100644 --- a/src/pcsaft/dft/mod.rs +++ b/src/pcsaft/dft/mod.rs @@ -3,7 +3,7 @@ use crate::association::Association; use crate::hard_sphere::{FMTContribution, FMTVersion}; use crate::pcsaft::eos::PcSaftOptions; use feos_core::parameter::Parameter; -use feos_core::si::{MolarWeight, GRAM, MOL}; +use quantity::{MolarWeight, GRAM, MOL}; use feos_core::{Components, EosResult}; use feos_derive::FunctionalContribution; use feos_dft::adsorption::FluidParameters; diff --git a/src/pcsaft/eos/mod.rs b/src/pcsaft/eos/mod.rs index 88518270f..08f641ed8 100644 --- a/src/pcsaft/eos/mod.rs +++ b/src/pcsaft/eos/mod.rs @@ -2,10 +2,12 @@ use super::parameters::PcSaftParameters; use crate::association::Association; use crate::hard_sphere::{HardSphere, HardSphereProperties}; use feos_core::parameter::Parameter; -use feos_core::{si::*, StateHD}; -use feos_core::{Components, EntropyScaling, EosError, EosResult, Residual, State}; +use feos_core::{ + Components, EntropyScaling, EosError, EosResult, ReferenceSystem, Residual, State, StateHD, +}; use ndarray::Array1; use num_dual::DualNum; +use quantity::*; use std::f64::consts::{FRAC_PI_6, PI}; use std::fmt; use std::sync::Arc; @@ -211,7 +213,7 @@ fn chapman_enskog_thermal_conductivity( epsilon_k: f64, ) -> ThermalConductivity { let t = temperature.to_reduced(); - 0.083235 * (t * m / molarweight.convert_into(GRAM / MOL)).sqrt() + 0.083235 * (t * m / molarweight.convert_to(GRAM / MOL)).sqrt() / sigma.powi(2) / omega22(t / epsilon_k) * WATT @@ -373,9 +375,9 @@ mod tests { butane_parameters, propane_butane_parameters, propane_parameters, water_parameters, }; use approx::assert_relative_eq; - use feos_core::si::{BAR, KELVIN, METER, MILLI, PASCAL, RGAS, SECOND}; use feos_core::*; use ndarray::arr1; + use quantity::{BAR, KELVIN, METER, MILLI, PASCAL, RGAS, SECOND}; use typenum::P3; #[test] diff --git a/src/pcsaft/eos/polar.rs b/src/pcsaft/eos/polar.rs index 403ef122f..2b67f16f7 100644 --- a/src/pcsaft/eos/polar.rs +++ b/src/pcsaft/eos/polar.rs @@ -326,8 +326,8 @@ impl fmt::Display for Quadrupole { } /// Different combination rules used in the dipole-quadrupole contribution. -#[derive(Clone, Copy)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum DQVariants { DQ35, DQ44, diff --git a/src/pcsaft/parameters.rs b/src/pcsaft/parameters.rs index 5ba9a77a1..b185b6c43 100644 --- a/src/pcsaft/parameters.rs +++ b/src/pcsaft/parameters.rs @@ -6,7 +6,7 @@ use conv::ValueInto; use feos_core::parameter::{ FromSegments, FromSegmentsBinary, Parameter, ParameterError, PureRecord, }; -use feos_core::si::{JOULE, KB, KELVIN}; +use quantity::{JOULE, KB, KELVIN}; use ndarray::{Array, Array1, Array2}; use num_dual::DualNum; use num_traits::Zero; diff --git a/src/pcsaft/python.rs b/src/pcsaft/python.rs index 05d6137cd..a9eee6ff7 100644 --- a/src/pcsaft/python.rs +++ b/src/pcsaft/python.rs @@ -50,7 +50,8 @@ pub struct PyPcSaftRecord(PcSaftRecord); impl PyPcSaftRecord { #[new] #[pyo3( - text_signature = "(m, sigma, epsilon_k, mu=None, q=None, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, viscosity=None, diffusion=None, thermal_conductivity=None)" + text_signature = "(m, sigma, epsilon_k, mu=None, q=None, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, viscosity=None, diffusion=None, thermal_conductivity=None)", + signature = (m, sigma, epsilon_k, mu=None, q=None, kappa_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, viscosity=None, diffusion=None, thermal_conductivity=None) )] #[expect(clippy::too_many_arguments)] fn new( @@ -168,6 +169,8 @@ pub struct PyPcSaftBinaryRecord(PcSaftBinaryRecord); #[pymethods] impl PyPcSaftBinaryRecord { #[new] + #[pyo3(text_signature = "(k_ij=None, kappa_ab=None, epsilon_k_ab=None)")] + #[pyo3(signature = (k_ij=None, kappa_ab=None, epsilon_k_ab=None))] fn new(k_ij: Option, kappa_ab: Option, epsilon_k_ab: Option) -> Self { Self(PcSaftBinaryRecord::new(k_ij, kappa_ab, epsilon_k_ab)) } diff --git a/src/pets/dft/mod.rs b/src/pets/dft/mod.rs index 39db0c524..44cb18622 100644 --- a/src/pets/dft/mod.rs +++ b/src/pets/dft/mod.rs @@ -3,7 +3,7 @@ use super::parameters::PetsParameters; use crate::hard_sphere::{FMTContribution, FMTVersion}; use dispersion::AttractiveFunctional; use feos_core::parameter::Parameter; -use feos_core::si::{MolarWeight, GRAM, MOL}; +use quantity::{MolarWeight, GRAM, MOL}; use feos_core::{Components, EosResult}; use feos_derive::FunctionalContribution; use feos_dft::adsorption::FluidParameters; diff --git a/src/pets/eos/mod.rs b/src/pets/eos/mod.rs index e935f4737..3166d3de8 100644 --- a/src/pets/eos/mod.rs +++ b/src/pets/eos/mod.rs @@ -1,7 +1,7 @@ use super::parameters::PetsParameters; use crate::hard_sphere::HardSphere; use feos_core::parameter::Parameter; -use feos_core::si::{MolarWeight, GRAM, MOL}; +use quantity::{MolarWeight, GRAM, MOL}; use feos_core::{Components, Residual}; use ndarray::Array1; use std::f64::consts::FRAC_PI_6; @@ -314,7 +314,7 @@ mod tests { argon_krypton_parameters, argon_parameters, krypton_parameters, }; use approx::assert_relative_eq; - use feos_core::si::{BAR, KELVIN, METER, PASCAL, RGAS}; + use quantity::{BAR, KELVIN, METER, PASCAL, RGAS}; use feos_core::{Contributions, DensityInitialization, PhaseEquilibrium, State, StateHD}; use ndarray::arr1; use typenum::P3; diff --git a/src/pets/python.rs b/src/pets/python.rs index d04e19e1c..5819b50f3 100644 --- a/src/pets/python.rs +++ b/src/pets/python.rs @@ -18,7 +18,8 @@ pub struct PyPetsRecord(PetsRecord); impl PyPetsRecord { #[new] #[pyo3( - text_signature = "(sigma, epsilon_k, viscosity=None, diffusion=None, thermal_conductivity=None)" + text_signature = "(sigma, epsilon_k, viscosity=None, diffusion=None, thermal_conductivity=None)", + signature = (sigma, epsilon_k, viscosity=None, diffusion=None, thermal_conductivity=None) )] fn new( sigma: f64, @@ -103,7 +104,8 @@ impl PyPetsParameters { /// ------- /// PetsParameters #[pyo3( - text_signature = "(sigma, epsilon_k, k_ij=None, molarweight=None, viscosity=None, diffusion=None, thermal_conductivity=None)" + text_signature = "(sigma, epsilon_k, k_ij=None, molarweight=None, viscosity=None, diffusion=None, thermal_conductivity=None)", + signature = (sigma, epsilon_k, k_ij=None, molarweight=None, viscosity=None, diffusion=None, thermal_conductivity=None) )] #[staticmethod] fn from_lists( @@ -192,7 +194,8 @@ impl PyPetsParameters { /// ------- /// PetsParameters #[pyo3( - text_signature = "(sigma, epsilon_k, molarweight=None, viscosity=None, diffusion=None, thermal_conductivity=None)" + text_signature = "(sigma, epsilon_k, molarweight=None, viscosity=None, diffusion=None, thermal_conductivity=None)", + signature = (sigma, epsilon_k, molarweight=None, viscosity=None, diffusion=None, thermal_conductivity=None) )] #[staticmethod] fn from_values( diff --git a/src/python/dft.rs b/src/python/dft.rs index cbc03009e..6536a8800 100644 --- a/src/python/dft.rs +++ b/src/python/dft.rs @@ -22,24 +22,24 @@ use crate::saftvrqmie::python::PySaftVRQMieParameters; #[cfg(feature = "saftvrqmie")] use crate::saftvrqmie::{SaftVRQMieFunctional, SaftVRQMieOptions}; -use feos_core::si::*; use feos_core::*; use feos_dft::adsorption::*; use feos_dft::interface::*; use feos_dft::python::*; use feos_dft::solvation::*; use feos_dft::*; +use ndarray::{Array1, Array2, Array3, Array4}; use numpy::prelude::*; use numpy::{PyArray1, PyArray2, PyArray3, PyArray4}; use pyo3::exceptions::{PyIndexError, PyValueError}; use pyo3::prelude::*; #[cfg(feature = "estimator")] use pyo3::wrap_pymodule; -use quantity::python::{PyAngle, PySIArray1, PySIArray2, PySIArray3, PySIArray4, PySINumber}; +use quantity::*; use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryInto; use std::sync::Arc; -use typenum::P3; +use typenum::{Quot, P3}; type Functional = EquationOfState; diff --git a/src/python/eos.rs b/src/python/eos.rs index 3494855c1..c6411a42e 100644 --- a/src/python/eos.rs +++ b/src/python/eos.rs @@ -40,19 +40,19 @@ use super::joback::PyJoback; use feos_core::cubic::PengRobinson; use feos_core::python::cubic::PyPengRobinsonParameters; use feos_core::python::user_defined::{PyIdealGas, PyResidual}; -use feos_core::si::*; use feos_core::*; +use ndarray::{Array1, Array2}; use numpy::prelude::*; use numpy::{PyArray1, PyArray2}; use pyo3::exceptions::{PyIndexError, PyValueError}; use pyo3::prelude::*; #[cfg(feature = "estimator")] use pyo3::wrap_pymodule; -use quantity::python::{PySIArray1, PySIArray2, PySINumber}; +use quantity::*; use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryInto; use std::sync::Arc; -use typenum::P3; +use typenum::{Quot, P3}; /// Collection of equations of state. #[pyclass(name = "EquationOfState")] diff --git a/src/python/mod.rs b/src/python/mod.rs index e1613b673..9ed8a844d 100644 --- a/src/python/mod.rs +++ b/src/python/mod.rs @@ -15,7 +15,6 @@ use crate::uvtheory::python::uvtheory as uvtheory_module; use pyo3::prelude::*; use pyo3::wrap_pymodule; -use quantity::python::quantity as quantity_module; mod cubic; mod dippr; @@ -34,7 +33,7 @@ use dft::dft as dft_module; #[pymodule] pub fn feos(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add("__version__", env!("CARGO_PKG_VERSION"))?; - m.add_wrapped(wrap_pymodule!(quantity_module))?; + // m.add_wrapped(wrap_pymodule!(quantity_module))?; m.add_wrapped(wrap_pymodule!(eos_module))?; #[cfg(feature = "dft")] @@ -57,7 +56,7 @@ pub fn feos(m: &Bound<'_, PyModule>) -> PyResult<()> { #[cfg(feature = "saftvrmie")] m.add_wrapped(wrap_pymodule!(saftvrmie_module))?; - set_path(m, "feos.si", "quantity")?; + // set_path(m, "feos.si", "quantity")?; set_path(m, "feos.eos", "eos")?; #[cfg(feature = "estimator")] set_path(m, "feos.eos.estimator", "eos.estimator_eos")?; @@ -83,15 +82,15 @@ pub fn feos(m: &Bound<'_, PyModule>) -> PyResult<()> { #[cfg(feature = "saftvrmie")] set_path(m, "feos.saftvrmie", "saftvrmie")?; + // re-export si_units within feos. Overriding __module__ is required for pickling. m.py().run_bound( "\ import sys -quantity.SINumber.__module__ = 'feos.si' -quantity.SIArray1.__module__ = 'feos.si' -quantity.SIArray2.__module__ = 'feos.si' -quantity.SIArray3.__module__ = 'feos.si' -quantity.SIArray4.__module__ = 'feos.si' - ", +import si_units +sys.modules['feos.si'] = si_units +si_units.SIObject.__module__ = 'feos.si' +si_units.Angle.__module__ = 'feos.si' + ", None, Some(&m.dict()), )?; diff --git a/src/saftvrmie/eos/mod.rs b/src/saftvrmie/eos/mod.rs index 527fcf713..4b730b196 100644 --- a/src/saftvrmie/eos/mod.rs +++ b/src/saftvrmie/eos/mod.rs @@ -2,13 +2,11 @@ use crate::hard_sphere::HardSphere; use super::SaftVRMieParameters; use association::Association; -use feos_core::{ - parameter::Parameter, - si::{MolarWeight, GRAM, MOL}, - Components, Residual, StateHD, -}; +use feos_core::parameter::Parameter; +use feos_core::{Components, Residual, StateHD}; use ndarray::{Array1, ScalarOperand}; use num_dual::DualNum; +use quantity::{MolarWeight, GRAM, MOL}; use std::{f64::consts::FRAC_PI_6, sync::Arc}; pub(super) mod association; diff --git a/src/saftvrmie/python.rs b/src/saftvrmie/python.rs index d1800bdca..69740f9af 100644 --- a/src/saftvrmie/python.rs +++ b/src/saftvrmie/python.rs @@ -96,7 +96,8 @@ pub struct PySaftVRMieRecord(SaftVRMieRecord); impl PySaftVRMieRecord { #[new] #[pyo3( - text_signature = "(m, sigma, epsilon_k, lr, la, rc_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, viscosity=None, diffusion=None, thermal_conductivity=None)" + text_signature = "(m, sigma, epsilon_k, lr, la, rc_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, viscosity=None, diffusion=None, thermal_conductivity=None)", + signature = (m, sigma, epsilon_k, lr, la, rc_ab=None, epsilon_k_ab=None, na=None, nb=None, nc=None, viscosity=None, diffusion=None, thermal_conductivity=None) )] #[expect(clippy::too_many_arguments)] fn new( @@ -212,6 +213,8 @@ pub struct PySaftVRMieBinaryRecord(SaftVRMieBinaryRecord); #[pymethods] impl PySaftVRMieBinaryRecord { #[new] + #[pyo3(text_signature = "(k_ij=None, gamma_ij=None, rc_ab=None, epsilon_k_ab=None)")] + #[pyo3(signature = (k_ij=None, gamma_ij=None, rc_ab=None, epsilon_k_ab=None))] fn new( k_ij: Option, gamma_ij: Option, diff --git a/src/saftvrqmie/dft/mod.rs b/src/saftvrqmie/dft/mod.rs index da604f8d5..346dfaf53 100644 --- a/src/saftvrqmie/dft/mod.rs +++ b/src/saftvrqmie/dft/mod.rs @@ -3,7 +3,7 @@ use crate::saftvrqmie::eos::SaftVRQMieOptions; use crate::saftvrqmie::parameters::SaftVRQMieParameters; use dispersion::AttractiveFunctional; use feos_core::parameter::Parameter; -use feos_core::si::{MolarWeight, GRAM, MOL}; +use quantity::{MolarWeight, GRAM, MOL}; use feos_core::{Components, EosResult}; use feos_derive::FunctionalContribution; use feos_dft::adsorption::FluidParameters; diff --git a/src/saftvrqmie/dft/non_additive_hs.rs b/src/saftvrqmie/dft/non_additive_hs.rs index b9c3a81a7..6cb39afc7 100644 --- a/src/saftvrqmie/dft/non_additive_hs.rs +++ b/src/saftvrqmie/dft/non_additive_hs.rs @@ -128,7 +128,7 @@ impl FunctionalContribution for NonAddHardSphereFunctional { Ok(rho0 .view() - .into_shape([n, rho0.len() / n]) + .into_shape_with_order([n, rho0.len() / n]) .unwrap() .axis_iter(Axis(1)) .zip(n2.iter()) @@ -138,7 +138,7 @@ impl FunctionalContribution for NonAddHardSphereFunctional { non_additive_hs_energy_density(p, &d_hs_ij, &d_hs_add_ij, &rho0, n2, n3i, xi) }) .collect::>() - .into_shape(n2.raw_dim()) + .into_shape_with_order(n2.raw_dim()) .unwrap()) } } diff --git a/src/saftvrqmie/eos/mod.rs b/src/saftvrqmie/eos/mod.rs index 849517cfa..d34675e58 100644 --- a/src/saftvrqmie/eos/mod.rs +++ b/src/saftvrqmie/eos/mod.rs @@ -1,9 +1,11 @@ use super::parameters::SaftVRQMieParameters; use feos_core::parameter::{Parameter, ParameterError}; -use feos_core::si::*; -use feos_core::{Components, EntropyScaling, EosError, EosResult, Residual, State}; +use feos_core::{ + Components, EntropyScaling, EosError, EosResult, ReferenceSystem, Residual, State, +}; use ndarray::{Array1, Array2}; use num_dual::DualNum; +use quantity::*; use std::convert::TryFrom; use std::f64::consts::{FRAC_PI_6, PI}; use std::sync::Arc; @@ -33,8 +35,8 @@ impl Default for SaftVRQMieOptions { } /// Order of Feynman-Hibbs potential -#[derive(Copy, Clone)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Copy, Clone, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq, eq_int))] pub enum FeynmanHibbsOrder { /// Mie potential FH0 = 0, @@ -209,7 +211,7 @@ fn chapman_enskog_thermal_conductivity( epsilon_k: f64, ) -> ThermalConductivity { let t = temperature.to_reduced(); - 0.083235 * (t * m / molarweight.convert_into(GRAM / MOL)).sqrt() + 0.083235 * (t * m / molarweight.convert_to(GRAM / MOL)).sqrt() / sigma.powi(2) / omega22(t / epsilon_k) * WATT diff --git a/src/saftvrqmie/parameters.rs b/src/saftvrqmie/parameters.rs index baa96d8d0..be12229b2 100644 --- a/src/saftvrqmie/parameters.rs +++ b/src/saftvrqmie/parameters.rs @@ -1,9 +1,10 @@ use crate::saftvrqmie::eos::FeynmanHibbsOrder; use core::cmp::max; use feos_core::parameter::{Parameter, ParameterError, PureRecord}; -use feos_core::si::{Length, Temperature, CALORIE, GRAM, KELVIN, KILO, KILOGRAM, MOL, NAV, RGAS}; +use feos_core::ReferenceSystem; use ndarray::{Array, Array1, Array2}; use num_traits::Zero; +use quantity::{Length, Temperature, CALORIE, GRAM, KELVIN, KILO, KILOGRAM, MOL, NAV, RGAS}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::convert::TryFrom; diff --git a/src/saftvrqmie/python.rs b/src/saftvrqmie/python.rs index df4a670f9..e8196bb14 100644 --- a/src/saftvrqmie/python.rs +++ b/src/saftvrqmie/python.rs @@ -7,14 +7,13 @@ use feos_core::parameter::{ BinaryRecord, Identifier, IdentifierOption, Parameter, ParameterError, PureRecord, }; use feos_core::python::parameter::PyIdentifier; -use feos_core::si::{Temperature, AMU, ANGSTROM, KB, KELVIN, PLANCK}; use feos_core::*; use ndarray::{Array1, Array2}; use numpy::prelude::*; use numpy::{PyArray1, PyArray2, PyReadonlyArray2}; use pyo3::exceptions::{PyIOError, PyTypeError}; use pyo3::prelude::*; -use quantity::python::{PySIArray2, PySINumber}; +use quantity::{Area, Length, Temperature, AMU, ANGSTROM, KB, KELVIN, PLANCK}; use std::convert::{TryFrom, TryInto}; use std::sync::Arc; @@ -52,7 +51,8 @@ pub struct PySaftVRQMieRecord(SaftVRQMieRecord); impl PySaftVRQMieRecord { #[new] #[pyo3( - text_signature = "(m, sigma, epsilon_k, lr, la, fh, viscosity=None, diffusion=None, thermal_conductivity=None)" + text_signature = "(m, sigma, epsilon_k, lr, la, fh, viscosity=None, diffusion=None, thermal_conductivity=None)", + signature = (m, sigma, epsilon_k, lr, la, fh, viscosity=None, diffusion=None, thermal_conductivity=None) )] #[expect(clippy::too_many_arguments)] fn new( @@ -194,13 +194,13 @@ impl PySaftVRQMieParameters { /// Returns /// ------- /// PySIArray2 - fn sigma_eff(&self, temperature: PySINumber) -> PyResult { + fn sigma_eff(&self, temperature: Temperature) -> Length> { let n = self.0.m.len(); - let t: Temperature = temperature.try_into()?; + let t = temperature.to_reduced(); let sigma_eff_ij = Array2::from_shape_fn((n, n), |(i, j)| -> f64 { - self.0.calc_sigma_eff_ij(i, j, t.to_reduced()) + self.0.calc_sigma_eff_ij(i, j, t) }); - Ok(PySIArray2::from(sigma_eff_ij * ANGSTROM)) + sigma_eff_ij * ANGSTROM } /// Calculate effective epsilon_k. @@ -213,13 +213,13 @@ impl PySaftVRQMieParameters { /// Returns /// ------- /// PySIArray2 - fn epsilon_k_eff(&self, temperature: PySINumber) -> PyResult { + fn epsilon_k_eff(&self, temperature: Temperature) -> Temperature> { let n = self.0.m.len(); - let t: Temperature = temperature.try_into()?; + let t = temperature.to_reduced(); let epsilon_k_eff = Array2::from_shape_fn((n, n), |(i, j)| -> f64 { - self.0.calc_epsilon_k_eff_ij(i, j, t.to_reduced()) + self.0.calc_epsilon_k_eff_ij(i, j, t) }); - Ok(PySIArray2::from(epsilon_k_eff * KELVIN)) + epsilon_k_eff * KELVIN } /// Calculate temperature dependent diameter. @@ -232,17 +232,16 @@ impl PySaftVRQMieParameters { /// Returns /// ------- /// PySIArray2 - fn diameter(&self, temperature: PySINumber) -> PyResult { + fn diameter(&self, temperature: Temperature) -> Length> { let n = self.0.m.len(); - let t: Temperature = temperature.try_into()?; + let t = temperature.to_reduced(); let sigma_eff_ij = Array2::from_shape_fn((n, n), |(i, j)| -> f64 { - self.0.calc_sigma_eff_ij(i, j, t.to_reduced()) + self.0.calc_sigma_eff_ij(i, j, t) }); let diameter = Array2::from_shape_fn((n, n), |(i, j)| -> f64 { - self.0 - .hs_diameter_ij(i, j, t.to_reduced(), sigma_eff_ij[[i, j]]) + self.0.hs_diameter_ij(i, j, t, sigma_eff_ij[[i, j]]) }); - Ok(PySIArray2::from(diameter * ANGSTROM)) + diameter * ANGSTROM } /// Calculate FH pre-factor D. @@ -255,13 +254,12 @@ impl PySaftVRQMieParameters { /// Returns /// ------- /// PySIArray2 - fn quantum_d(&self, temperature: PySINumber) -> PyResult { + fn quantum_d(&self, temperature: Temperature) -> Area> { let n = self.0.m.len(); - let t: Temperature = temperature.try_into()?; - let quantum_d = Array2::from_shape_fn((n, n), |(i, j)| -> f64 { - self.0.quantum_d_ij(i, j, t.to_reduced()) - }); - Ok(PySIArray2::from(quantum_d * (ANGSTROM * ANGSTROM))) + let t = temperature.to_reduced(); + let quantum_d = + Array2::from_shape_fn((n, n), |(i, j)| -> f64 { self.0.quantum_d_ij(i, j, t) }); + quantum_d * (ANGSTROM * ANGSTROM) } /// Calculate de Boer parameter. @@ -321,18 +319,13 @@ impl PySaftVRQMieParameters { #[pyo3(text_signature = "($self, temperature, n, r_min, r_max)")] fn lammps_tables( &self, - temperature: PySINumber, + temperature: Temperature, n: usize, - r_min: PySINumber, - r_max: PySINumber, + r_min: Length, + r_max: Length, ) -> PyResult<()> { self.0 - .lammps_tables( - temperature.try_into()?, - n, - r_min.try_into()?, - r_max.try_into()?, - ) + .lammps_tables(temperature, n, r_min, r_max) .map_err(PyIOError::new_err) } diff --git a/src/uvtheory/eos/mod.rs b/src/uvtheory/eos/mod.rs index b709cc9ce..d317c96b0 100644 --- a/src/uvtheory/eos/mod.rs +++ b/src/uvtheory/eos/mod.rs @@ -5,9 +5,9 @@ use self::bh::BarkerHenderson; use self::wca::{WeeksChandlerAndersen, WeeksChandlerAndersenB3}; use super::parameters::UVTheoryParameters; -use feos_core::si::{MolarWeight, GRAM, MOL}; use feos_core::{parameter::Parameter, Components, Residual}; use ndarray::Array1; +use quantity::{MolarWeight, GRAM, MOL}; use std::f64::consts::FRAC_PI_6; use std::sync::Arc; @@ -15,8 +15,8 @@ mod bh; mod wca; /// Type of perturbation. -#[derive(Clone)] -#[cfg_attr(feature = "python", pyo3::pyclass)] +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "python", pyo3::pyclass(eq))] pub enum Perturbation { BarkerHenderson, WeeksChandlerAndersen, @@ -132,9 +132,9 @@ mod test { use crate::uvtheory::parameters::*; use approx::assert_relative_eq; use feos_core::parameter::{Identifier, Parameter, PureRecord}; - use feos_core::si::{ANGSTROM, KELVIN, MOL, NAV, RGAS}; use feos_core::{EosResult, State}; use ndarray::arr1; + use quantity::{ANGSTROM, KELVIN, MOL, NAV, RGAS}; use typenum::P3; #[test] diff --git a/tests/gc_pcsaft/binary.rs b/tests/gc_pcsaft/binary.rs index aa8828909..48592e9cd 100644 --- a/tests/gc_pcsaft/binary.rs +++ b/tests/gc_pcsaft/binary.rs @@ -3,9 +3,9 @@ use feos::gc_pcsaft::{GcPcSaft, GcPcSaftEosParameters}; #[cfg(feature = "dft")] use feos::gc_pcsaft::{GcPcSaftFunctional, GcPcSaftFunctionalParameters}; use feos_core::parameter::{IdentifierOption, ParameterHetero}; -use feos_core::si::{KELVIN, METER, MOL}; use feos_core::{Contributions, EosResult, State}; use ndarray::arr1; +use quantity::{KELVIN, METER, MOL}; use std::sync::Arc; use typenum::P3; diff --git a/tests/gc_pcsaft/dft.rs b/tests/gc_pcsaft/dft.rs index 8b9d8a6d9..18b375380 100644 --- a/tests/gc_pcsaft/dft.rs +++ b/tests/gc_pcsaft/dft.rs @@ -7,12 +7,12 @@ use feos::gc_pcsaft::{ use feos_core::parameter::{ ChemicalRecord, Identifier, IdentifierOption, ParameterHetero, SegmentRecord, }; -use feos_core::si::*; use feos_core::{PhaseEquilibrium, State, StateBuilder, Verbosity}; use feos_dft::adsorption::{ExternalPotential, Pore1D, PoreSpecification}; use feos_dft::interface::PlanarInterface; use feos_dft::{DFTSolver, Geometry}; use ndarray::arr1; +use quantity::*; use std::error::Error; use std::sync::Arc; use typenum::P3; diff --git a/tests/pcsaft/critical_point.rs b/tests/pcsaft/critical_point.rs index 1e74f5d23..f5de39fe4 100644 --- a/tests/pcsaft/critical_point.rs +++ b/tests/pcsaft/critical_point.rs @@ -1,9 +1,9 @@ use approx::assert_relative_eq; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter}; -use feos_core::si::*; use feos_core::State; use ndarray::arr1; +use quantity::*; use std::error::Error; use std::sync::Arc; use typenum::P3; diff --git a/tests/pcsaft/dft.rs b/tests/pcsaft/dft.rs index 3267d1f22..be1953899 100644 --- a/tests/pcsaft/dft.rs +++ b/tests/pcsaft/dft.rs @@ -5,11 +5,11 @@ use feos::hard_sphere::FMTVersion; use feos::ideal_gas::Joback; use feos::pcsaft::{PcSaft, PcSaftFunctional, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter}; -use feos_core::si::*; use feos_core::{Contributions, PhaseEquilibrium, State, Verbosity}; use feos_dft::interface::PlanarInterface; use feos_dft::DFTSolver; use ndarray::{arr1, Axis}; +use quantity::*; use std::error::Error; use std::sync::Arc; use typenum::P3; diff --git a/tests/pcsaft/properties.rs b/tests/pcsaft/properties.rs index c068276f1..4d84ecb75 100644 --- a/tests/pcsaft/properties.rs +++ b/tests/pcsaft/properties.rs @@ -1,9 +1,9 @@ use approx::assert_relative_eq; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter}; -use feos_core::si::*; use feos_core::{Residual, StateBuilder}; use ndarray::*; +use quantity::*; use std::error::Error; use std::sync::Arc; diff --git a/tests/pcsaft/stability_analysis.rs b/tests/pcsaft/stability_analysis.rs index 0830863b0..35e44d357 100644 --- a/tests/pcsaft/stability_analysis.rs +++ b/tests/pcsaft/stability_analysis.rs @@ -1,8 +1,8 @@ use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter}; -use feos_core::si::*; use feos_core::{DensityInitialization, PhaseEquilibrium, State}; use ndarray::arr1; +use quantity::*; use std::error::Error; use std::sync::Arc; diff --git a/tests/pcsaft/state_creation_mixture.rs b/tests/pcsaft/state_creation_mixture.rs index b44d572ad..3563be0a6 100644 --- a/tests/pcsaft/state_creation_mixture.rs +++ b/tests/pcsaft/state_creation_mixture.rs @@ -2,10 +2,10 @@ use approx::assert_relative_eq; use feos::ideal_gas::Joback; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter, ParameterError}; -use feos_core::si::*; use feos_core::{Contributions, EquationOfState, StateBuilder}; use ndarray::prelude::*; use ndarray::Zip; +use quantity::*; use std::error::Error; use std::sync::Arc; use typenum::P3; diff --git a/tests/pcsaft/state_creation_pure.rs b/tests/pcsaft/state_creation_pure.rs index c571f6af7..af94f32d4 100644 --- a/tests/pcsaft/state_creation_pure.rs +++ b/tests/pcsaft/state_creation_pure.rs @@ -2,11 +2,11 @@ use approx::assert_relative_eq; use feos::ideal_gas::Joback; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter, ParameterError}; -use feos_core::si::*; use feos_core::{ Contributions, DensityInitialization, EquationOfState, IdealGas, PhaseEquilibrium, Residual, State, StateBuilder, }; +use quantity::*; use std::error::Error; use std::sync::Arc; use typenum::P3; diff --git a/tests/pcsaft/tp_flash.rs b/tests/pcsaft/tp_flash.rs index 168f26996..f9991249b 100644 --- a/tests/pcsaft/tp_flash.rs +++ b/tests/pcsaft/tp_flash.rs @@ -1,9 +1,9 @@ use approx::assert_relative_eq; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter, ParameterError}; -use feos_core::si::*; use feos_core::{Contributions, PhaseEquilibrium, SolverOptions}; use ndarray::*; +use quantity::*; use std::error::Error; use std::sync::Arc; diff --git a/tests/pcsaft/vle_pure.rs b/tests/pcsaft/vle_pure.rs index f25ab5c9c..60b8641e8 100644 --- a/tests/pcsaft/vle_pure.rs +++ b/tests/pcsaft/vle_pure.rs @@ -1,8 +1,8 @@ use approx::assert_relative_eq; use feos::pcsaft::{PcSaft, PcSaftParameters}; use feos_core::parameter::{IdentifierOption, Parameter}; -use feos_core::si::*; use feos_core::{Contributions, PhaseEquilibrium}; +use quantity::*; use std::error::Error; use std::sync::Arc; diff --git a/tests/saftvrmie/critical_properties.rs b/tests/saftvrmie/critical_properties.rs index 329bf9f17..e6ab44c69 100644 --- a/tests/saftvrmie/critical_properties.rs +++ b/tests/saftvrmie/critical_properties.rs @@ -1,6 +1,6 @@ use feos::saftvrmie::{test_utils, SaftVRMie}; -use feos_core::State; -use feos_core::{si::*, SolverOptions}; +use feos_core::{SolverOptions, State}; +use quantity::*; use std::collections::HashMap; use std::sync::Arc; use typenum::P3;