From 57f256474f3636d5f7d8c206ab849f203bd057d7 Mon Sep 17 00:00:00 2001 From: Philipp Rehner Date: Wed, 9 Mar 2022 11:47:12 +0100 Subject: [PATCH] Add conversions between error types to improve error messages --- src/errors.rs | 3 ++ src/joback.rs | 14 ++++---- src/python/utils.rs | 46 +++++++++++------------- src/state/builder.rs | 6 ++-- src/utils/dataset.rs | 82 ++++++++++++++++++++++-------------------- src/utils/estimator.rs | 4 ++- 6 files changed, 79 insertions(+), 76 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 38eb108..c746d34 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,3 +1,4 @@ +use crate::parameter::ParameterError; use argmin::core::Error as ArgminError; use num_dual::linalg::LinAlgError; use quantity::QuantityError; @@ -25,6 +26,8 @@ pub enum EosError { #[error(transparent)] QuantityError(#[from] QuantityError), #[error(transparent)] + ParameterError(#[from] ParameterError), + #[error(transparent)] ArgminError(#[from] ArgminError), #[error(transparent)] LinAlgError(#[from] LinAlgError), diff --git a/src/joback.rs b/src/joback.rs index 82471ce..e136d7e 100644 --- a/src/joback.rs +++ b/src/joback.rs @@ -171,7 +171,7 @@ mod tests { struct ModelRecord; #[test] - fn paper_example() { + fn paper_example() -> EosResult<()> { let segments_json = r#"[ { "identifier": "-Cl", @@ -226,8 +226,7 @@ mod tests { ], None, ) - .segment_count(&segment_records) - .unwrap(); + .segment_count(&segment_records)?; assert_eq!(segments.get(&segment_records[0]), Some(&2.0)); assert_eq!(segments.get(&segment_records[1]), Some(&4.0)); assert_eq!(segments.get(&segment_records[2]), Some(&2.0)); @@ -264,17 +263,16 @@ mod tests { 1000.0 * KELVIN, 1.0 * ANGSTROM.powi(3), &(arr1(&[1.0]) * MOL), - ) - .unwrap(); + )?; assert!( (state .c_p(Contributions::IdealGas) - .to_reduced(JOULE / MOL / KELVIN) - .unwrap() + .to_reduced(JOULE / MOL / KELVIN)? - 224.6) .abs() < 1.0 - ) + ); + Ok(()) } #[test] diff --git a/src/python/utils.rs b/src/python/utils.rs index ea42cd4..e1b7811 100644 --- a/src/python/utils.rs +++ b/src/python/utils.rs @@ -138,15 +138,12 @@ macro_rules! impl_estimator { target: &PySIArray1, temperature: &PySIArray1, std_parameters: Option>, - ) -> Self { - Self(Rc::new( - VaporPressure::::new( - target.clone().into(), - temperature.clone().into(), - std_parameters.unwrap_or(vec![0.0, 0.0, 0.0]), - ) - .unwrap(), - )) + ) -> PyResult { + Ok(Self(Rc::new(VaporPressure::::new( + target.clone().into(), + temperature.clone().into(), + std_parameters.unwrap_or(vec![0.0, 0.0, 0.0]), + )?))) } /// Create a DataSet with experimental data for liquid density. @@ -177,15 +174,12 @@ macro_rules! impl_estimator { target: &PySIArray1, temperature: &PySIArray1, pressure: &PySIArray1, - ) -> Self { - Self(Rc::new( - LiquidDensity::::new( - target.clone().into(), - temperature.clone().into(), - pressure.clone().into(), - ) - .unwrap(), - )) + ) -> PyResult { + Ok(Self(Rc::new(LiquidDensity::::new( + target.clone().into(), + temperature.clone().into(), + pressure.clone().into(), + )?))) } /// Create a DataSet with experimental data for liquid density @@ -211,14 +205,14 @@ macro_rules! impl_estimator { /// eos_python.saft.estimator.DataSet.relative_difference #[staticmethod] #[pyo3(text_signature = "(target, temperature)")] - fn equilibrium_liquid_density(target: &PySIArray1, temperature: &PySIArray1) -> Self { - Self(Rc::new( - EquilibriumLiquidDensity::::new( - target.clone().into(), - temperature.clone().into(), - ) - .unwrap(), - )) + fn equilibrium_liquid_density( + target: &PySIArray1, + temperature: &PySIArray1, + ) -> PyResult { + Ok(Self(Rc::new(EquilibriumLiquidDensity::::new( + target.clone().into(), + temperature.clone().into(), + )?))) } /// Return `input` as ``Dict[str, SIArray1]``. diff --git a/src/state/builder.rs b/src/state/builder.rs index 5e6da59..7ed7c6e 100644 --- a/src/state/builder.rs +++ b/src/state/builder.rs @@ -18,7 +18,7 @@ use std::rc::Rc; /// # use approx::assert_relative_eq; /// # fn main() -> EosResult<()> { /// // Create a state for given T,V,N -/// let eos = Rc::new(PengRobinson::new(Rc::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0]).unwrap()))); +/// let eos = Rc::new(PengRobinson::new(Rc::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?))); /// let state = StateBuilder::new(&eos) /// .temperature(300.0 * KELVIN) /// .volume(12.5 * METER.powi(3)) @@ -27,7 +27,7 @@ use std::rc::Rc; /// assert_eq!(state.density, 0.2 * MOL / METER.powi(3)); /// /// // For a pure component, the composition does not need to be specified. -/// let eos = Rc::new(PengRobinson::new(Rc::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0]).unwrap()))); +/// let eos = Rc::new(PengRobinson::new(Rc::new(PengRobinsonParameters::new_simple(&[369.8], &[41.9 * 1e5], &[0.15], &[15.0])?))); /// let state = StateBuilder::new(&eos) /// .temperature(300.0 * KELVIN) /// .volume(12.5 * METER.powi(3)) @@ -42,7 +42,7 @@ use std::rc::Rc; /// &[41.9 * 1e5, 48.2 * 1e5], /// &[0.15, 0.10], /// &[15.0, 30.0] -/// ).unwrap()) +/// )?) /// )); /// let state = StateBuilder::new(&eos) /// .temperature(300.0 * KELVIN) diff --git a/src/utils/dataset.rs b/src/utils/dataset.rs index 289884c..bb3aa7d 100644 --- a/src/utils/dataset.rs +++ b/src/utils/dataset.rs @@ -100,8 +100,7 @@ impl VaporPressure { ) -> Result { let datapoints = target.len(); let max_temperature = temperature - .to_reduced(U::reference_temperature()) - .unwrap() + .to_reduced(U::reference_temperature())? .into_iter() .reduce(|a, b| a.max(b)) .unwrap() @@ -147,10 +146,13 @@ impl DataSet for VaporPressure { where QuantityScalar: std::fmt::Display + std::fmt::LowerExp, { - let tc = - State::critical_point(eos, None, Some(self.max_temperature), SolverOptions::default()) - .unwrap() - .temperature; + let tc = State::critical_point( + eos, + None, + Some(self.max_temperature), + SolverOptions::default(), + )? + .temperature; let unit = self.target.get(0); let mut prediction = Array1::zeros(self.datapoints) * unit; @@ -160,12 +162,12 @@ impl DataSet for VaporPressure { if let Some(pvap) = PhaseEquilibrium::vapor_pressure(eos, self.temperature.get(i))[0] { - prediction.try_set(i, pvap).unwrap(); + prediction.try_set(i, pvap)?; } else { - prediction.try_set(i, f64::NAN * unit).unwrap(); + prediction.try_set(i, f64::NAN * unit)?; } } else { - prediction.try_set(i, f64::NAN * unit).unwrap(); + prediction.try_set(i, f64::NAN * unit)?; } } Ok(prediction) @@ -176,14 +178,18 @@ impl DataSet for VaporPressure { QuantityScalar: std::fmt::Display + std::fmt::LowerExp, { let tc_inv = 1.0 - / State::critical_point(eos, None, Some(self.max_temperature), SolverOptions::default()) - .unwrap() - .temperature; - - let reduced_temperatures = (0..self.datapoints) - .map(|i| (self.temperature.get(i) * tc_inv).into_value().unwrap()) + / State::critical_point( + eos, + None, + Some(self.max_temperature), + SolverOptions::default(), + )? + .temperature; + + let reduced_temperatures: Result<_, _> = (0..self.datapoints) + .map(|i| (self.temperature.get(i) * tc_inv).into_value()) .collect(); - let mut weights = self.weight_from_std(&reduced_temperatures); + let mut weights = self.weight_from_std(&reduced_temperatures?); weights /= weights.sum(); let prediction = &self.predict(eos)?; @@ -193,8 +199,7 @@ impl DataSet for VaporPressure { cost[i] = weights[i] * 5.0 * (self.temperature.get(i) - 1.0 / tc_inv) - .to_reduced(U::reference_temperature()) - .unwrap(); + .to_reduced(U::reference_temperature())?; } else { cost[i] = weights[i] * ((self.target.get(i) - prediction.get(i)) / self.target.get(i)) @@ -275,9 +280,9 @@ impl> DataSet for LiquidDe DensityInitialization::Liquid, ); if let Ok(s) = state { - prediction.try_set(i, s.mass_density()).unwrap(); + prediction.try_set(i, s.mass_density())?; } else { - prediction.try_set(i, 1.0e10 * unit).unwrap(); + prediction.try_set(i, 1.0e10 * unit)?; } } Ok(prediction) @@ -322,8 +327,7 @@ impl EquilibriumLiquidDensity { ) -> Result { let datapoints = target.len(); let max_temperature = temperature - .to_reduced(U::reference_temperature()) - .unwrap() + .to_reduced(U::reference_temperature())? .into_iter() .reduce(|a, b| a.max(b)) .unwrap() @@ -362,10 +366,13 @@ impl> DataSet where QuantityScalar: std::fmt::Display + std::fmt::LowerExp, { - let tc = - State::critical_point(eos, None, Some(self.max_temperature), SolverOptions::default()) - .unwrap() - .temperature; + let tc = State::critical_point( + eos, + None, + Some(self.max_temperature), + SolverOptions::default(), + )? + .temperature; let unit = self.target.get(0); let mut prediction = Array1::zeros(self.datapoints) * unit; @@ -373,12 +380,10 @@ impl> DataSet let t: QuantityScalar = self.temperature.get(i); if t < tc { let state: PhaseEquilibrium = - PhaseEquilibrium::pure_t(eos, t, None, SolverOptions::default()).unwrap(); - prediction - .try_set(i, state.liquid().mass_density()) - .unwrap(); + PhaseEquilibrium::pure_t(eos, t, None, SolverOptions::default())?; + prediction.try_set(i, state.liquid().mass_density())?; } else { - prediction.try_set(i, f64::NAN * unit).unwrap(); + prediction.try_set(i, f64::NAN * unit)?; } } Ok(prediction) @@ -388,10 +393,13 @@ impl> DataSet where QuantityScalar: std::fmt::Display + std::fmt::LowerExp, { - let tc = - State::critical_point(eos, None, Some(self.max_temperature), SolverOptions::default()) - .unwrap() - .temperature; + let tc = State::critical_point( + eos, + None, + Some(self.max_temperature), + SolverOptions::default(), + )? + .temperature; let n_inv = 1.0 / self.datapoints as f64; let prediction = &self.predict(eos)?; let mut cost = Array1::zeros(self.datapoints); @@ -399,9 +407,7 @@ impl> DataSet if prediction.get(i).is_nan() { cost[i] = n_inv * 5.0 - * (self.temperature.get(i) - tc) - .to_reduced(U::reference_temperature()) - .unwrap(); + * (self.temperature.get(i) - tc).to_reduced(U::reference_temperature())?; } else { cost[i] = n_inv * ((self.target.get(i) - prediction.get(i)) / self.target.get(i)) diff --git a/src/utils/estimator.rs b/src/utils/estimator.rs index b8d678b..fd9a241 100644 --- a/src/utils/estimator.rs +++ b/src/utils/estimator.rs @@ -2,7 +2,7 @@ //! optimization. use super::dataset::*; use crate::equation_of_state::EquationOfState; -use crate::EosUnit; +use crate::{EosError, EosUnit}; use ndarray::{arr1, concatenate, Array1, ArrayView1, Axis}; use quantity::{QuantityArray1, QuantityError, QuantityScalar}; use std::fmt; @@ -25,6 +25,8 @@ pub enum FitError { ParseError(#[from] ParseFloatError), #[error(transparent)] QuantityError(#[from] QuantityError), + #[error(transparent)] + EosError(#[from] EosError), } /// A collection of [`DataSet`]s and weights that can be used to