Skip to content

Commit cdf0f9e

Browse files
authored
Add critical_temperature argument to VaporPressure constructor (#86)
* Add critical_temperature to `VaporPressure::new` and Python constructor. Add estimator module to documentation * improvements to documentation * fixed typo in changelog * better docstring for Loss
1 parent d0127e1 commit cdf0f9e

File tree

4 files changed

+80
-35
lines changed

4 files changed

+80
-35
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## [Unreleased]
88
### Added
99
- Added SAFT-VRQ Mie equation of state and Helmholtz energy functional for first order Feynman-Hibbs corrected Mie fluids. [#79](https://github.com/feos-org/feos/pull/79)
10+
- Added `estimator` module to documentation. [#86](https://github.com/feos-org/feos/pull/86)
1011

1112
### Changed
1213
- Export `EosVariant` and `FunctionalVariant` directly in the crate root instead of their own modules. [#62](https://github.com/feos-org/feos/pull/62)
14+
- Changed constructors `VaporPressure::new` and `DataSet.vapor_pressure` (Python) to take a new optional argument `critical_temperature`. [#86](https://github.com/feos-org/feos/pull/86)
1315

1416
## [0.3.0] - 2022-09-14
1517
- Major restructuring of the entire `feos` project. All individual models are reunited in the `feos` crate. `feos-core` and `feos-dft` still live as individual crates within the `feos` workspace.

docs/api/eos.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
The `eos` module contains the `EquationOfState` object that contains all implemented equations of state.
44
The `State` and `PhaseEquilibrium` objects are used to define thermodynamic conditions and -- once created -- can be used to compute properties.
55

6+
If you want to adjust parameters of a model to experimental data you can use classes and utilities from the `estimator` module.
7+
68
## `EquationOfState`
79

810
```{eval-rst}
@@ -34,4 +36,24 @@ The `State` and `PhaseEquilibrium` objects are used to define thermodynamic cond
3436
State
3537
PhaseEquilibrium
3638
PhaseDiagram
37-
```
39+
```
40+
41+
## The `estimator` module
42+
43+
### Import
44+
45+
```python
46+
from feos.eos.estimator import Estimator, DataSet, Loss, Phase
47+
```
48+
49+
```{eval-rst}
50+
.. currentmodule:: feos.eos.estimator
51+
52+
.. autosummary::
53+
:toctree: generated/
54+
55+
Estimator
56+
DataSet
57+
Loss
58+
Phase
59+
```

src/estimator/python.rs

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ impl From<EstimatorError> for PyErr {
1111
#[macro_export]
1212
macro_rules! impl_estimator {
1313
($eos:ty, $py_eos:ty) => {
14+
/// Collection of loss functions that can be applied to residuals
15+
/// to handle outliers.
1416
#[pyclass(name = "Loss")]
1517
#[derive(Clone)]
1618
pub struct PyLoss(Loss);
@@ -121,20 +123,26 @@ macro_rules! impl_estimator {
121123
impl PyDataSet {
122124
/// Compute the cost function for each input value.
123125
///
124-
/// The cost function that is used depends on the
125-
/// property. See the class constructors to learn
126-
/// about the cost functions of the properties.
127-
///
128126
/// Parameters
129127
/// ----------
130-
/// eos : PyEos
128+
/// eos : EquationOfState
131129
/// The equation of state that is used.
130+
/// loss : Loss
131+
/// The loss function that is applied to residuals
132+
/// to handle outliers.
132133
///
133134
/// Returns
134135
/// -------
135136
/// numpy.ndarray[Float]
136137
/// The cost function evaluated for each experimental data point.
137-
#[pyo3(text_signature = "($self, eos)")]
138+
///
139+
/// Note
140+
/// ----
141+
/// The cost function that is used depends on the
142+
/// property. For most properties it is the absolute relative difference.
143+
/// See the constructors of the respective properties
144+
/// to learn about the cost functions that are used.
145+
#[pyo3(text_signature = "($self, eos, loss)")]
138146
fn cost<'py>(
139147
&self,
140148
eos: &$py_eos,
@@ -149,18 +157,12 @@ macro_rules! impl_estimator {
149157
///
150158
/// Parameters
151159
/// ----------
152-
/// eos : PyEos
160+
/// eos : EquationOfState
153161
/// The equation of state that is used.
154162
///
155163
/// Returns
156164
/// -------
157165
/// SIArray1
158-
///
159-
/// See also
160-
/// --------
161-
/// eos_python.saft.estimator.DataSet.vapor_pressure : ``DataSet`` for vapor pressure.
162-
/// eos_python.saft.estimator.DataSet.liquid_density : ``DataSet`` for liquid density.
163-
/// eos_python.saft.estimator.DataSet.equilibrium_liquid_density : ``DataSet`` for liquid density at vapor liquid equilibrium.
164166
#[pyo3(text_signature = "($self, eos)")]
165167
fn predict(&self, eos: &$py_eos) -> PyResult<PySIArray1> {
166168
Ok(self.0.predict(&eos.0)?.into())
@@ -175,7 +177,7 @@ macro_rules! impl_estimator {
175177
///
176178
/// Parameters
177179
/// ----------
178-
/// eos : PyEos
180+
/// eos : EquationOfState
179181
/// The equation of state that is used.
180182
///
181183
/// Returns
@@ -198,7 +200,7 @@ macro_rules! impl_estimator {
198200
///
199201
/// Parameters
200202
/// ----------
201-
/// eos : PyEos
203+
/// eos : EquationOfState
202204
/// The equation of state that is used.
203205
///
204206
/// Returns
@@ -221,6 +223,10 @@ macro_rules! impl_estimator {
221223
/// Use Antoine type equation to extrapolate vapor
222224
/// pressure if experimental data is above critial
223225
/// point of model. Defaults to False.
226+
/// critical_temperature : SINumber, optional
227+
/// Estimate of the critical temperature used as initial
228+
/// value for critical point calculation. Defaults to None.
229+
/// For additional information, see note.
224230
/// max_iter : int, optional
225231
/// The maximum number of iterations for critical point
226232
/// and VLE algorithms.
@@ -234,12 +240,19 @@ macro_rules! impl_estimator {
234240
/// Returns
235241
/// -------
236242
/// ``DataSet``
243+
///
244+
/// Note
245+
/// ----
246+
/// If no critical temperature is provided, the maximum of the `temperature` input
247+
/// is used. If that fails, the default temperatures of the critical point routine
248+
/// are used.
237249
#[staticmethod]
238-
#[pyo3(text_signature = "(target, temperature, extrapolate)")]
250+
#[pyo3(text_signature = "(target, temperature, extrapolate, critical_temperature=None, max_iter=None, verbosity=None)")]
239251
fn vapor_pressure(
240252
target: &PySIArray1,
241253
temperature: &PySIArray1,
242254
extrapolate: Option<bool>,
255+
critical_temperature: Option<&PySINumber>,
243256
max_iter: Option<usize>,
244257
tol: Option<f64>,
245258
verbosity: Option<Verbosity>,
@@ -248,6 +261,7 @@ macro_rules! impl_estimator {
248261
target.clone().into(),
249262
temperature.clone().into(),
250263
extrapolate.unwrap_or(false),
264+
critical_temperature.and_then(|tc| Some(tc.clone().into())),
251265
Some((max_iter, tol, verbosity).into()),
252266
)?)))
253267
}
@@ -439,7 +453,7 @@ macro_rules! impl_estimator {
439453
PySIArray1::from(self.0.target().clone())
440454
}
441455

442-
/// Return `target` as ``SIArray1``.
456+
/// Return number of stored data points.
443457
#[getter]
444458
fn get_datapoints(&self) -> usize {
445459
self.0.datapoints()
@@ -483,22 +497,25 @@ macro_rules! impl_estimator {
483497

484498
/// Compute the cost function for each ``DataSet``.
485499
///
486-
/// The cost function is:
487-
/// - The relative difference between prediction and target value,
488-
/// - to which a loss function is applied,
489-
/// - and which is weighted according to the number of datapoints,
490-
/// - and the relative weights as defined in the Estimator object.
491-
///
492500
/// Parameters
493501
/// ----------
494-
/// eos : PyEos
502+
/// eos : EquationOfState
495503
/// The equation of state that is used.
496504
///
497505
/// Returns
498506
/// -------
499507
/// numpy.ndarray[Float]
500508
/// The cost function evaluated for each experimental data point
501509
/// of each ``DataSet``.
510+
///
511+
/// Note
512+
/// ----
513+
/// The cost function is:
514+
///
515+
/// - The relative difference between prediction and target value,
516+
/// - to which a loss function is applied,
517+
/// - and which is weighted according to the number of datapoints,
518+
/// - and the relative weights as defined in the Estimator object.
502519
#[pyo3(text_signature = "($self, eos)")]
503520
fn cost<'py>(&self, eos: &$py_eos, py: Python<'py>) -> PyResult<&'py PyArray1<f64>> {
504521
Ok(self.0.cost(&eos.0)?.view().to_pyarray(py))
@@ -509,7 +526,7 @@ macro_rules! impl_estimator {
509526
///
510527
/// Parameters
511528
/// ----------
512-
/// eos : PyEos
529+
/// eos : EquationOfState
513530
/// The equation of state that is used.
514531
///
515532
/// Returns
@@ -534,7 +551,7 @@ macro_rules! impl_estimator {
534551
///
535552
/// Parameters
536553
/// ----------
537-
/// eos : PyEos
554+
/// eos : EquationOfState
538555
/// The equation of state that is used.
539556
///
540557
/// Returns
@@ -562,7 +579,7 @@ macro_rules! impl_estimator {
562579
///
563580
/// Parameters
564581
/// ----------
565-
/// eos : PyEos
582+
/// eos : EquationOfState
566583
/// The equation of state that is used.
567584
///
568585
/// Returns

src/estimator/vapor_pressure.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@ impl<U: EosUnit> VaporPressure<U> {
2929
target: QuantityArray1<U>,
3030
temperature: QuantityArray1<U>,
3131
extrapolate: bool,
32+
critical_temperature: Option<QuantityScalar<U>>,
3233
solver_options: Option<SolverOptions>,
3334
) -> Result<Self, EstimatorError> {
3435
let datapoints = target.len();
35-
let max_temperature = temperature
36-
.to_reduced(U::reference_temperature())?
37-
.into_iter()
38-
.reduce(|a, b| a.max(b))
39-
.unwrap()
40-
* U::reference_temperature();
36+
let max_temperature = critical_temperature.unwrap_or(
37+
temperature
38+
.to_reduced(U::reference_temperature())?
39+
.into_iter()
40+
.reduce(|a, b| a.max(b))
41+
.unwrap()
42+
* U::reference_temperature(),
43+
);
4144
Ok(Self {
4245
target,
4346
temperature,
@@ -72,7 +75,8 @@ impl<U: EosUnit, E: EquationOfState> DataSet<U, E> for VaporPressure<U> {
7275
QuantityScalar<U>: std::fmt::Display + std::fmt::LowerExp,
7376
{
7477
let critical_point =
75-
State::critical_point(eos, None, Some(self.max_temperature), self.solver_options)?;
78+
State::critical_point(eos, None, Some(self.max_temperature), self.solver_options)
79+
.or_else(|_| State::critical_point(eos, None, None, self.solver_options))?;
7680
let tc = critical_point.temperature;
7781
let pc = critical_point.pressure(Contributions::Total);
7882

0 commit comments

Comments
 (0)