Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/feos-core/src/ad/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ pub trait PropertiesAD {
let a_l = (mu_res_l - vi_l * p_l).dot(&y);
(a_l, a_v, v_l, v_v)
};
let rho_l = vle.liquid().partial_density.to_reduced();
let rho_l = vle.liquid().partial_density().to_reduced();
let rho_l = [rho_l[0], rho_l[1]];
let rho_v = vle.vapor().partial_density.to_reduced();
let rho_v = vle.vapor().partial_density().to_reduced();
let rho_v = [rho_v[0], rho_v[1]];
let p = -(a_v - a_l
+ t * (y[0] * (rho_v[0] / rho_l[0]).ln() + y[1] * (rho_v[1] / rho_l[1]).ln() - 1.0))
Expand Down Expand Up @@ -241,9 +241,9 @@ pub trait PropertiesAD {
let a_v = (mu_res_v - vi_v * p_v).dot(&x);
(a_l, a_v, v_l, v_v)
};
let rho_l = vle.liquid().partial_density.to_reduced();
let rho_l = vle.liquid().partial_density().to_reduced();
let rho_l = [rho_l[0], rho_l[1]];
let rho_v = vle.vapor().partial_density.to_reduced();
let rho_v = vle.vapor().partial_density().to_reduced();
let rho_v = [rho_v[0], rho_v[1]];
let p = -(a_l - a_v
+ t * (x[0] * (rho_l[0] / rho_v[0]).ln() + x[1] * (rho_l[1] / rho_v[1]).ln() - 1.0))
Expand Down
2 changes: 1 addition & 1 deletion crates/feos-core/src/cubic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ mod tests {
let parameters = PengRobinsonParameters::new_pure(propane)?;
let pr = PengRobinson::new(parameters);
let options = SolverOptions::new().verbosity(Verbosity::Iter);
let cp = State::critical_point(&&pr, None, None, None, options)?;
let cp = State::critical_point(&&pr, (), None, None, options)?;
println!("{} {}", cp.temperature, cp.pressure(Contributions::Total));
assert_relative_eq!(cp.temperature, tc * KELVIN, max_relative = 1e-4);
assert_relative_eq!(
Expand Down
17 changes: 9 additions & 8 deletions crates/feos-core/src/equation_of_state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::{ReferenceSystem, StateHD};
use crate::ReferenceSystem;
use crate::state::StateHD;
use nalgebra::{
Const, DVector, DefaultAllocator, Dim, Dyn, OVector, SVector, U1, allocator::Allocator,
};
use num_dual::DualNum;
use quantity::{Energy, MolarEnergy, Moles, Temperature, Volume};
use quantity::{Dimensionless, MolarEnergy, MolarVolume, Temperature};
use std::ops::Deref;

mod residual;
Expand Down Expand Up @@ -164,17 +165,17 @@ where
fn ideal_gas_helmholtz_energy<D2: DualNum<f64, Inner = D> + Copy>(
&self,
temperature: Temperature<D2>,
volume: Volume<D2>,
moles: &Moles<OVector<D2, N>>,
) -> Energy<D2> {
volume: MolarVolume<D2>,
moles: &OVector<D2, N>,
) -> MolarEnergy<D2> {
let total_moles = moles.sum();
let molefracs = moles / total_moles;
let molar_volume = volume / total_moles;
let molar_volume = volume.into_reduced() / total_moles;
MolarEnergy::from_reduced(self.ideal_gas_molar_helmholtz_energy(
temperature.into_reduced(),
molar_volume.into_reduced(),
molar_volume,
&molefracs,
)) * total_moles
)) * Dimensionless::new(total_moles)
}
}

Expand Down
109 changes: 50 additions & 59 deletions crates/feos-core/src/equation_of_state/residual.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{FeosError, FeosResult, ReferenceSystem, state::StateHD};
use crate::state::StateHD;
use crate::{Composition, FeosResult, ReferenceSystem};
use nalgebra::{DVector, DefaultAllocator, Dim, Dyn, OMatrix, OVector, U1, allocator::Allocator};
use num_dual::{DualNum, Gradients, partial, partial2, second_derivative, third_derivative};
use quantity::ad::first_derivative;
Expand Down Expand Up @@ -171,62 +172,45 @@ where
self.reduced_residual_helmholtz_energy_density(&state) * temperature * volume
}

/// Evaluate the residual Helmholtz energy $A^\mathrm{res}$.
fn residual_helmholtz_energy_unit(
/// Evaluate the residual molar Helmholtz energy $a^\mathrm{res}$.
///
/// There are no extensive properties internally. Therefore the molefracs are
/// passed and the total number of moles is always assumed to be 1. To get correct
/// partial derivatives, it is still crucial to calculate the actual molefracs from
/// the molefracs that are passed as moles.
fn residual_molar_helmholtz_energy_unit(
&self,
temperature: Temperature<D>,
volume: Volume<D>,
moles: &Moles<OVector<D, N>>,
) -> Energy<D> {
volume: MolarVolume<D>,
moles: &OVector<D, N>,
) -> MolarEnergy<D> {
let temperature = temperature.into_reduced();
let total_moles = moles.sum();
let molar_volume = (volume / total_moles).into_reduced();
let molar_volume = volume.into_reduced() / total_moles;
let molefracs = moles / total_moles;
let state = StateHD::new(temperature, molar_volume, &molefracs);
Pressure::from_reduced(self.reduced_residual_helmholtz_energy_density(&state) * temperature)
* volume
}

/// Check if the provided optional molar concentration is consistent with the
/// equation of state.
///
/// In general, the number of elements in `molefracs` needs to match the number
/// of components of the equation of state. For a pure component, however,
/// no molefracs need to be provided.
fn validate_molefracs(&self, molefracs: &Option<OVector<D, N>>) -> FeosResult<OVector<D, N>> {
let l = molefracs.as_ref().map_or(1, |m| m.len());
if self.components() == l {
match molefracs {
Some(m) => Ok(m.clone()),
None => Ok(OVector::from_element_generic(
N::from_usize(1),
U1,
D::one(),
)),
}
} else {
Err(FeosError::IncompatibleComponents(self.components(), l))
}
}

/// Calculate the maximum density.
///
/// This value is used as an estimate for a liquid phase for phase
/// equilibria and other iterations. It is not explicitly meant to
/// be a mathematical limit for the density (if those exist in the
/// equation of state anyways).
fn max_density(&self, molefracs: &Option<OVector<D, N>>) -> FeosResult<Density<D>> {
let x = self.validate_molefracs(molefracs)?;
fn max_density<X: Composition<D, N>>(&self, composition: X) -> FeosResult<Density<D>> {
let (x, _) = composition.into_molefracs(self);
Ok(Density::from_reduced(self.compute_max_density(&x)))
}

/// Calculate the second virial coefficient $B(T)$
fn second_virial_coefficient(
fn second_virial_coefficient<X: Composition<D, N>>(
&self,
temperature: Temperature<D>,
molefracs: &Option<OVector<D, N>>,
composition: X,
) -> MolarVolume<D> {
let x = self.validate_molefracs(molefracs).unwrap();
let (x, _) = composition.into_molefracs(self);
let (_, _, d2f) = second_derivative(
partial2(
|rho, &t, x| {
Expand All @@ -244,12 +228,12 @@ where
}

/// Calculate the third virial coefficient $C(T)$
fn third_virial_coefficient(
fn third_virial_coefficient<X: Composition<D, N>>(
&self,
temperature: Temperature<D>,
molefracs: &Option<OVector<D, N>>,
composition: X,
) -> Quot<MolarVolume<D>, Density<D>> {
let x = self.validate_molefracs(molefracs).unwrap();
let (x, _) = composition.into_molefracs(self);
let (_, _, _, d3f) = third_derivative(
partial2(
|rho, &t, x| {
Expand All @@ -267,29 +251,34 @@ where
}

/// Calculate the temperature derivative of the second virial coefficient $B'(T)$
fn second_virial_coefficient_temperature_derivative(
fn second_virial_coefficient_temperature_derivative<X: Composition<D, N>>(
&self,
temperature: Temperature<D>,
molefracs: &Option<OVector<D, N>>,
composition: X,
) -> Quot<MolarVolume<D>, Temperature<D>> {
let (molefracs, _) = composition.into_molefracs(self);
let (_, db_dt) = first_derivative(
partial(
|t, x| self.lift().second_virial_coefficient(t, x),
molefracs,
|t, x: &OVector<_, _>| self.lift().second_virial_coefficient(t, x),
&molefracs,
),
temperature,
);
db_dt
}

/// Calculate the temperature derivative of the third virial coefficient $C'(T)$
fn third_virial_coefficient_temperature_derivative(
fn third_virial_coefficient_temperature_derivative<X: Composition<D, N>>(
&self,
temperature: Temperature<D>,
molefracs: &Option<OVector<D, N>>,
composition: X,
) -> Quot<Quot<MolarVolume<D>, Density<D>>, Temperature<D>> {
let (molefracs, _) = composition.into_molefracs(self);
let (_, dc_dt) = first_derivative(
partial(|t, x| self.lift().third_virial_coefficient(t, x), molefracs),
partial(
|t, x: &OVector<_, _>| self.lift().third_virial_coefficient(t, x),
&molefracs,
),
temperature,
);
dc_dt
Expand Down Expand Up @@ -422,22 +411,22 @@ where
fn viscosity_reference(
&self,
temperature: Temperature<D>,
volume: Volume<D>,
moles: &Moles<OVector<D, N>>,
molar_volume: MolarVolume<D>,
molefracs: &OVector<D, N>,
) -> Viscosity<D>;
fn viscosity_correlation(&self, s_res: D, x: &OVector<D, N>) -> D;
fn diffusion_reference(
&self,
temperature: Temperature<D>,
volume: Volume<D>,
moles: &Moles<OVector<D, N>>,
molar_volume: MolarVolume<D>,
molefracs: &OVector<D, N>,
) -> Diffusivity<D>;
fn diffusion_correlation(&self, s_res: D, x: &OVector<D, N>) -> D;
fn thermal_conductivity_reference(
&self,
temperature: Temperature<D>,
volume: Volume<D>,
moles: &Moles<OVector<D, N>>,
molar_volume: MolarVolume<D>,
molefracs: &OVector<D, N>,
) -> ThermalConductivity<D>;
fn thermal_conductivity_correlation(&self, s_res: D, x: &OVector<D, N>) -> D;
}
Expand All @@ -450,33 +439,35 @@ where
fn viscosity_reference(
&self,
temperature: Temperature<D>,
volume: Volume<D>,
moles: &Moles<OVector<D, N>>,
molar_volume: MolarVolume<D>,
molefracs: &OVector<D, N>,
) -> Viscosity<D> {
self.deref().viscosity_reference(temperature, volume, moles)
self.deref()
.viscosity_reference(temperature, molar_volume, molefracs)
}
fn viscosity_correlation(&self, s_res: D, x: &OVector<D, N>) -> D {
self.deref().viscosity_correlation(s_res, x)
}
fn diffusion_reference(
&self,
temperature: Temperature<D>,
volume: Volume<D>,
moles: &Moles<OVector<D, N>>,
molar_volume: MolarVolume<D>,
molefracs: &OVector<D, N>,
) -> Diffusivity<D> {
self.deref().diffusion_reference(temperature, volume, moles)
self.deref()
.diffusion_reference(temperature, molar_volume, molefracs)
}
fn diffusion_correlation(&self, s_res: D, x: &OVector<D, N>) -> D {
self.deref().diffusion_correlation(s_res, x)
}
fn thermal_conductivity_reference(
&self,
temperature: Temperature<D>,
volume: Volume<D>,
moles: &Moles<OVector<D, N>>,
molar_volume: MolarVolume<D>,
molefracs: &OVector<D, N>,
) -> ThermalConductivity<D> {
self.deref()
.thermal_conductivity_reference(temperature, volume, moles)
.thermal_conductivity_reference(temperature, molar_volume, molefracs)
}
fn thermal_conductivity_correlation(&self, s_res: D, x: &OVector<D, N>) -> D {
self.deref().thermal_conductivity_correlation(s_res, x)
Expand Down
2 changes: 1 addition & 1 deletion crates/feos-core/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub enum FeosError {
IncompatibleComponents(usize, usize),
#[error("Invalid state in {0}: {1} = {2}.")]
InvalidState(String, String, f64),
#[error("Undetermined state: {0}.")]
#[error("Undetermined state: {0}")]
UndeterminedState(String),
#[error("System is supercritical.")]
SuperCritical,
Expand Down
32 changes: 12 additions & 20 deletions crates/feos-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub use errors::{FeosError, FeosResult};
#[cfg(feature = "ndarray")]
pub use phase_equilibria::{PhaseDiagram, PhaseDiagramHetero};
pub use phase_equilibria::{PhaseEquilibrium, TemperatureOrPressure};
pub use state::{Contributions, DensityInitialization, State, StateBuilder, StateHD, StateVec};
pub use state::{Composition, Contributions, DensityInitialization, State, StateHD, StateVec};

/// Level of detail in the iteration output.
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Default)]
Expand Down Expand Up @@ -198,7 +198,7 @@ impl<Inner, T: Integer, L: Integer, M: Integer, I: Integer, THETA: Integer, N: I
mod tests {
use crate::Contributions;
use crate::FeosResult;
use crate::StateBuilder;
use crate::State;
use crate::cubic::*;
use crate::equation_of_state::{EquationOfState, IdealGas};
use crate::parameter::*;
Expand Down Expand Up @@ -261,20 +261,12 @@ mod tests {
let parameters = PengRobinsonParameters::new_pure(propane.clone())?;
let residual = PengRobinson::new(parameters);

let sr = StateBuilder::new(&&residual)
.temperature(300.0 * KELVIN)
.pressure(1.0 * BAR)
.total_moles(2.0 * MOL)
.build()?;
let sr = State::new_npt(&&residual, 300.0 * KELVIN, 1.0 * BAR, 2.0 * MOL, None)?;

let parameters = PengRobinsonParameters::new_pure(propane.clone())?;
let residual = PengRobinson::new(parameters);
let eos = EquationOfState::new(vec![NoIdealGas], residual);
let s = StateBuilder::new(&&eos)
.temperature(300.0 * KELVIN)
.pressure(1.0 * BAR)
.total_moles(2.0 * MOL)
.build()?;
let s = State::new_npt(&&eos, 300.0 * KELVIN, 1.0 * BAR, 2.0 * MOL, None)?;

// pressure
assert_relative_eq!(
Expand Down Expand Up @@ -341,7 +333,7 @@ mod tests {
);
assert_relative_eq!(
s.gibbs_energy(Contributions::Residual)
- s.total_moles
- s.total_moles()
* RGAS
* s.temperature
* s.compressibility(Contributions::Total).ln(),
Expand Down Expand Up @@ -417,13 +409,13 @@ mod tests {
max_relative = 1e-15
);
assert_relative_eq!(
s.dp_dni(Contributions::Total),
sr.dp_dni(Contributions::Total),
s.n_dp_dni(Contributions::Total),
sr.n_dp_dni(Contributions::Total),
max_relative = 1e-15
);
assert_relative_eq!(
s.dp_dni(Contributions::Residual),
sr.dp_dni(Contributions::Residual),
s.n_dp_dni(Contributions::Residual),
sr.n_dp_dni(Contributions::Residual),
max_relative = 1e-15
);

Expand All @@ -441,8 +433,8 @@ mod tests {
max_relative = 1e-15
);
assert_relative_eq!(
s.dmu_dni(Contributions::Residual),
sr.dmu_dni(Contributions::Residual),
s.n_dmu_dni(Contributions::Residual),
sr.n_dmu_dni(Contributions::Residual),
max_relative = 1e-15
);
assert_relative_eq!(
Expand All @@ -455,7 +447,7 @@ mod tests {
assert_relative_eq!(s.ln_phi(), sr.ln_phi(), max_relative = 1e-15);
assert_relative_eq!(s.dln_phi_dt(), sr.dln_phi_dt(), max_relative = 1e-15);
assert_relative_eq!(s.dln_phi_dp(), sr.dln_phi_dp(), max_relative = 1e-15);
assert_relative_eq!(s.dln_phi_dnj(), sr.dln_phi_dnj(), max_relative = 1e-15);
assert_relative_eq!(s.n_dln_phi_dnj(), sr.n_dln_phi_dnj(), max_relative = 1e-15);
assert_relative_eq!(
s.thermodynamic_factor(),
sr.thermodynamic_factor(),
Expand Down
Loading