feos-campd 0.2.1

Computer-aided molecular and process design using the FeOs framework.
Documentation
use super::{PyGcPcSaft, PyPcSaft};
use crate::process::ProcessModel;
use crate::{ProcessVariables, Variable};
use feos::core::{EosResult, EquationOfState};
use feos::gc_pcsaft::GcPcSaft;
use feos::ideal_gas::Joback;
use feos::pcsaft::PcSaft;
use feos_core::EosError;
use pyo3::prelude::*;
use std::sync::Arc;

#[pyclass(name = "ProcessModel")]
#[derive(Clone)]
pub struct PyProcessModel(Py<PyAny>);

#[pymethods]
impl PyProcessModel {
    #[new]
    pub fn new(obj: Py<PyAny>) -> PyResult<Self> {
        Python::with_gil(|py| {
            let attr = obj.as_ref(py).hasattr("variables")?;
            if !attr {
                panic!("Python class must have a method 'variables' with signature:\n\tdef variables(self) -> List[[int, int, int]]")
            }
            let attr = obj.as_ref(py).hasattr("equality_constraints")?;
            if !attr {
                panic!("Python class must have a method 'equality_constraints' with signature:\n\tdef equality_constraints(self) -> int")
            }
            let attr = obj.as_ref(py).hasattr("inequality_constraints")?;
            if !attr {
                panic!("Python class must have a method 'inequality_constraints' with signature:\n\tdef inequality_constraints(self) -> int")
            }
            let attr = obj.as_ref(py).hasattr("solve")?;
            if !attr {
                panic!("Python class must have a method 'solve' with signature:\n\tdef solve(self, eos: EquationOfState, x: List[float]) -> (float, List[float], List[float])")
            }
            Ok(Self(obj))
        })
    }
}

macro_rules! impl_process_model {
    ($py_eos:ident, $eos:ident) => {
        impl ProcessModel<EquationOfState<Joback, $eos>> for PyProcessModel {
            fn variables(&self) -> ProcessVariables {
                Python::with_gil(|py| {
                    let py_result = self.0.as_ref(py).call_method0("variables").unwrap();
                    if py_result.get_type().name().unwrap() != "list" {
                        panic!(
                            "Expected a list for the variables() method signature, got {}",
                            py_result.get_type().name().unwrap()
                        );
                    }
                    let variables = py_result.extract::<Vec<[f64; 3]>>().unwrap();
                    variables
                        .into_iter()
                        .map(|[l, u, i]| Variable::continuous(l, u, i))
                        .collect()
                })
            }

            fn equality_constraints(&self) -> usize {
                Python::with_gil(|py| {
                    let py_result = self
                        .0
                        .as_ref(py)
                        .call_method0("equality_constraints")
                        .unwrap();
                    if py_result.get_type().name().unwrap() != "int" {
                        panic!(
                            "Expected an integer for the equality_constraints() method signature, got {}",
                            py_result.get_type().name().unwrap()
                        );
                    }
                    py_result.extract().unwrap()
                })
            }

            fn inequality_constraints(&self) -> usize {
                Python::with_gil(|py| {
                    let py_result = self
                        .0
                        .as_ref(py)
                        .call_method0("inequality_constraints")
                        .unwrap();
                    if py_result.get_type().name().unwrap() != "int" {
                        panic!(
                            "Expected an integer for the inequality_constraints() method signature, got {}",
                            py_result.get_type().name().unwrap()
                        );
                    }
                    py_result.extract().unwrap()
                })
            }

            fn solve(
                &self,
                eos: &Arc<EquationOfState<Joback, $eos>>,
                x: &[f64],
            ) -> EosResult<(f64, Vec<f64>, Vec<f64>)> {
                Python::with_gil(|py| {
                    let py_eos = $py_eos(eos.clone());
                    self.0
                        .as_ref(py)
                        .call_method1("solve", (py_eos, x.to_vec()))
                        .and_then(|py_result| py_result.extract::<(f64, Vec<f64>, Vec<f64>)>())
                        .map_err(|_| EosError::NotConverged("Process model".into()))
                })
            }
        }
    };
}

impl_process_model!(PyPcSaft, PcSaft);
impl_process_model!(PyGcPcSaft, GcPcSaft);