diff --git a/CHANGELOG.md b/CHANGELOG.md index d59fdf3..04766a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.2] - 2022-01-10 +### Changed +- Changed `ChemicalRecord` to an enum that can hold either the full structural information of a molecule or only segment and bond counts and added an `Identifier`. [#19](https://github.com/feos-org/feos-core/pull/19) +- Removed the `chemical_record` field from `PureRecord` and made `model_record` non-optional. [#19](https://github.com/feos-org/feos-core/pull/19) + ## [0.1.1] - 2021-12-22 ### Added - Added `from_multiple_json` function to `Parameter` trait that is able to read parameters from separate JSON files. [#15](https://github.com/feos-org/feos-core/pull/15) diff --git a/Cargo.toml b/Cargo.toml index d7e2f9d..a8fca76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "feos-core" -version = "0.1.1" +version = "0.1.2" authors = ["Gernot Bauer ", "Philipp Rehner ", - "Philipp Rehner { - tc[i] = r.tc; - a[i] = 0.45724 * r.tc.powi(2) * KB_A3 / r.pc; - b[i] = 0.07780 * r.tc * KB_A3 / r.pc; - kappa[i] = - 0.37464 + (1.54226 - 0.26992 * r.acentric_factor) * r.acentric_factor; - } - None => panic!( - "No Peng-Robinson parameters for {} found.", - record.identifier.cas - ), - }; + let r = &record.model_record; + tc[i] = r.tc; + a[i] = 0.45724 * r.tc.powi(2) * KB_A3 / r.pc; + b[i] = 0.07780 * r.tc * KB_A3 / r.pc; + kappa[i] = 0.37464 + (1.54226 - 0.26992 * r.acentric_factor) * r.acentric_factor; } let joback_records = pure_records @@ -315,8 +304,8 @@ mod tests { fn peng_robinson() -> EosResult<()> { let mixture = pure_record_vec(); let propane = mixture[0].clone(); - let tc = propane.model_record.clone().unwrap().tc; - let pc = propane.model_record.clone().unwrap().pc; + let tc = propane.model_record.tc; + let pc = propane.model_record.pc; let parameters = PengRobinsonParameters::from_records(vec![propane.clone()], Array2::zeros((1, 1))); let pr = Rc::new(PengRobinson::new(Rc::new(parameters))); diff --git a/src/joback.rs b/src/joback.rs index 8d58769..82471ce 100644 --- a/src/joback.rs +++ b/src/joback.rs @@ -213,6 +213,7 @@ mod tests { let segment_records: Vec> = serde_json::from_str(segments_json).expect("Unable to parse json."); let segments = ChemicalRecord::new( + Identifier::new("", None, None, None, None, None), vec![ String::from("-Cl"), String::from("-Cl"), diff --git a/src/parameter/chemical_record.rs b/src/parameter/chemical_record.rs index 32a8811..e91014e 100644 --- a/src/parameter/chemical_record.rs +++ b/src/parameter/chemical_record.rs @@ -1,26 +1,62 @@ +use super::identifier::Identifier; use super::segment::SegmentRecord; use super::ParameterError; +use either::Either; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; use std::collections::{HashMap, HashSet}; // Auxiliary structure used to deserialize chemical records without bond information. -#[derive(Deserialize)] +#[derive(Serialize, Deserialize)] struct ChemicalRecordJSON { - pub segments: Vec, - pub bonds: Option>, + identifier: Identifier, + segments: Vec, + bonds: Option>, } /// Chemical information of a substance. #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(from = "ChemicalRecordJSON")] -pub struct ChemicalRecord { - pub segments: Vec, - pub bonds: Vec<[usize; 2]>, +#[serde(into = "ChemicalRecordJSON")] +pub enum ChemicalRecord { + List { + identifier: Identifier, + segments: Vec, + bonds: Vec<[usize; 2]>, + }, + Count { + identifier: Identifier, + segments: HashMap, + bonds: HashMap<[String; 2], f64>, + }, } impl From for ChemicalRecord { fn from(record: ChemicalRecordJSON) -> Self { - Self::new(record.segments, record.bonds) + Self::new(record.identifier, record.segments, record.bonds) + } +} + +impl From for ChemicalRecordJSON { + fn from(record: ChemicalRecord) -> Self { + match record { + ChemicalRecord::List { + identifier, + segments, + bonds, + } => Self { + identifier, + segments, + bonds: Some(bonds), + }, + ChemicalRecord::Count { + identifier: _, + segments: _, + bonds: _, + } => panic!( + "Only chemical records with detailed structural information can be serialized." + ), + } } } @@ -28,14 +64,66 @@ impl ChemicalRecord { /// Create a new `ChemicalRecord`. /// /// If no bonds are given, the molecule is assumed to be linear. - pub fn new(segments: Vec, bonds: Option>) -> ChemicalRecord { + pub fn new( + identifier: Identifier, + segments: Vec, + bonds: Option>, + ) -> ChemicalRecord { let bonds = bonds.unwrap_or_else(|| { (0..segments.len() - 1) .zip(1..segments.len()) .map(|x| [x.0, x.1]) .collect() }); - Self { segments, bonds } + Self::List { + identifier, + segments, + bonds, + } + } + + /// Create a new `ChemicalRecord` from a segment count. + pub fn new_count( + identifier: Identifier, + segments: HashMap, + bonds: Option>, + ) -> ChemicalRecord { + let bonds = bonds.unwrap_or_default(); + Self::Count { + identifier, + segments, + bonds, + } + } + + pub fn segments(&self) -> Either<&Vec, &HashMap> { + match self { + Self::List { + identifier: _, + segments, + bonds: _, + } => Either::Left(segments), + Self::Count { + identifier: _, + segments, + bonds: _, + } => Either::Right(segments), + } + } + + pub fn identifier(&self) -> &Identifier { + match self { + Self::List { + identifier, + segments: _, + bonds: _, + } => identifier, + Self::Count { + identifier, + segments: _, + bonds: _, + } => identifier, + } } /// Count the number of occurences of each individual segment in the @@ -46,27 +134,98 @@ impl ChemicalRecord { &self, segment_records: &[SegmentRecord], ) -> Result, f64>, ParameterError> { - let mut counts = HashMap::with_capacity(self.segments.len()); - let segments = self.segment_map(segment_records)?; - for si in &self.segments { - let key = segments.get(si).unwrap().clone(); - let entry = counts.entry(key).or_insert(0.0); - *entry += 1.0; - } - Ok(counts) + let segment_map = self.segment_map(segment_records)?; + Ok(match self.segments() { + Either::Left(segments) => { + let mut counts = HashMap::with_capacity(segments.len()); + for si in segments { + let key = segment_map.get(si).unwrap().clone(); + let entry = counts.entry(key).or_insert(0.0); + *entry += 1.0; + } + counts + } + Either::Right(segments) => segments + .iter() + .map(|(id, &c)| (segment_map[id].clone(), c)) + .collect(), + }) } /// Count the number of occurences of each individual segment identifier in the /// chemical record. /// /// The map contains the segment identifier as key and the count as (float) value. - pub fn segment_id_count(&self) -> HashMap { - let mut counts = HashMap::with_capacity(self.segments.len()); - for si in &self.segments { - let entry = counts.entry(si.clone()).or_insert(0.0); - *entry += 1.0; + pub fn segment_id_count(&self) -> Cow> { + match self.segments() { + Either::Left(segments) => { + let mut counts = HashMap::with_capacity(segments.len()); + for si in segments { + let entry = counts.entry(si.clone()).or_insert(0.0); + *entry += 1.0; + } + Cow::Owned(counts) + } + Either::Right(segments) => Cow::Borrowed(segments), + } + } + + /// Count the number of occurences of each individual segment identifier and each + /// pair of identifiers for every bond in the chemical record. + #[allow(clippy::type_complexity)] + pub fn segment_and_bond_count( + &self, + ) -> (Cow>, Cow>) { + match self { + Self::List { + identifier: _, + segments, + bonds, + } => { + let mut segment_counts = HashMap::with_capacity(segments.len()); + for si in segments { + let entry = segment_counts.entry(si.clone()).or_insert(0.0); + *entry += 1.0; + } + + let mut bond_counts = HashMap::new(); + for b in bonds { + let s1 = segments[b[0]].clone(); + let s2 = segments[b[1]].clone(); + let indices = if s1 > s2 { [s2, s1] } else { [s1, s2] }; + let entry = bond_counts.entry(indices).or_insert(0.0); + *entry += 1.0; + } + (Cow::Owned(segment_counts), Cow::Owned(bond_counts)) + } + Self::Count { + identifier: _, + segments, + bonds, + } => (Cow::Borrowed(segments), Cow::Borrowed(bonds)), + } + } + + /// Return the full segment and bond information for the molecule + /// if possible. + #[allow(clippy::type_complexity)] + pub fn segment_and_bond_list( + &self, + ) -> Result<(&Vec, &Vec<[usize; 2]>), ParameterError> { + match self { + Self::List { + identifier: _, + segments, + bonds, + } => Ok((segments, bonds)), + Self::Count { + identifier: _, + segments: _, + bonds: _, + } => Err(ParameterError::IncompatibleParameters( + "No detailed structural information available.".into(), + )), } - counts } /// Build a HashMap from SegmentRecords for the segments. @@ -77,7 +236,10 @@ impl ChemicalRecord { &self, segment_records: &[SegmentRecord], ) -> Result>, ParameterError> { - let queried: HashSet = self.segments.iter().cloned().collect(); + let queried: HashSet<_> = match self.segments() { + Either::Left(segments) => segments.iter().cloned().collect(), + Either::Right(segments) => segments.keys().cloned().collect(), + }; let mut segments: HashMap> = segment_records .iter() .map(|r| (r.identifier.clone(), r.clone())) @@ -89,7 +251,7 @@ impl ChemicalRecord { return Err(ParameterError::ComponentsNotFound(msg)); }; Ok(queried - .intersection(&available) + .iter() .map(|s| segments.remove_entry(s).unwrap()) .collect()) } @@ -97,7 +259,27 @@ impl ChemicalRecord { impl std::fmt::Display for ChemicalRecord { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "ChemicalRecord(segments={:?}", self.segments)?; - write!(f, ", bonds={:?})", self.bonds) + match self { + Self::List { + identifier, + segments, + bonds, + } => { + write!(f, "ChemicalRecord(")?; + write!(f, "\n\tidentifier={},", identifier)?; + write!(f, "\n\tsegments={:?},", segments)?; + write!(f, "\n\tbonds={:?}\n)", bonds) + } + Self::Count { + identifier, + segments, + bonds, + } => { + write!(f, "ChemicalRecord(")?; + write!(f, "\n\tidentifier={},", identifier)?; + write!(f, "\n\tsegments={:?},", segments)?; + write!(f, "\n\tbonds={:?}\n)", bonds) + } + } } } diff --git a/src/parameter/mod.rs b/src/parameter/mod.rs index db16c06..273fdc5 100644 --- a/src/parameter/mod.rs +++ b/src/parameter/mod.rs @@ -17,9 +17,7 @@ mod segment; pub use chemical_record::ChemicalRecord; pub use identifier::{Identifier, IdentifierOption}; -pub use model_record::{ - BinaryRecord, FromSegments, FromSegmentsBinary, GroupContributionRecord, NoRecord, PureRecord, -}; +pub use model_record::{BinaryRecord, FromSegments, FromSegmentsBinary, PureRecord}; pub use segment::SegmentRecord; /// Constructor methods for parameters. @@ -102,11 +100,9 @@ where substances.iter().try_for_each(|identifier| { match queried.insert(identifier.to_string()) { true => Ok(()), - false => Err(ParameterError::IncompatibleParameters(String::from( - format!( - "tried to add substance '{}' to system but it is already present.", - identifier.to_string() - ), + false => Err(ParameterError::IncompatibleParameters(format!( + "tried to add substance '{}' to system but it is already present.", + identifier ))), } })?; @@ -181,7 +177,7 @@ where /// The [FromSegments] trait needs to be implemented for both the model record /// and the ideal gas record. fn from_segments( - mut pure_records: Vec>, + chemical_records: Vec, segment_records: Vec>, binary_segment_records: Option>>, ) -> Result @@ -192,15 +188,13 @@ where { // update the pure records with model and ideal gas records // calculated from the gc method - pure_records.iter_mut().try_for_each(|pr| { - let segments = pr - .chemical_record - .clone() - .ok_or(ParameterError::InsufficientInformation)? - .segment_count(&segment_records)?; - pr.update_from_segments(segments); - Ok::<_, ParameterError>(()) - })?; + let pure_records = chemical_records + .iter() + .map(|cr| { + cr.segment_count(&segment_records) + .map(|segments| PureRecord::from_segments(cr.identifier().clone(), segments)) + }) + .collect::, _>>()?; // Map: (id1, id2) -> model_record // empty, if no binary segment records are provided @@ -212,9 +206,9 @@ where .collect(); // For every component: map: id -> count - let segment_id_counts: Vec<_> = pure_records + let segment_id_counts: Vec<_> = chemical_records .iter() - .map(|pr| pr.chemical_record.as_ref().unwrap().segment_id_count()) + .map(|cr| cr.segment_id_count()) .collect(); // full matrix of binary records from the gc method. @@ -263,13 +257,12 @@ where let file = File::open(file_pure)?; let reader = BufReader::new(file); - let pure_records: Vec> = - serde_json::from_reader(reader)?; - let mut record_map: HashMap<_, _> = pure_records + let chemical_records: Vec = serde_json::from_reader(reader)?; + let mut record_map: HashMap<_, _> = chemical_records .into_iter() .filter_map(|record| { record - .identifier + .identifier() .as_string(search_option) .map(|i| (i, record)) }) @@ -287,7 +280,7 @@ where }; // collect all pure records that were queried - let pure_records: Vec<_> = queried + let chemical_records: Vec<_> = queried .iter() .filter_map(|identifier| record_map.remove(&identifier.clone())) .collect(); @@ -308,7 +301,7 @@ where }) .transpose()?; - Self::from_segments(pure_records, segment_records, binary_records) + Self::from_segments(chemical_records, segment_records, binary_records) } fn subset(&self, component_list: &[usize]) -> Self { diff --git a/src/parameter/model_record.rs b/src/parameter/model_record.rs index a38b6c3..8aa4844 100644 --- a/src/parameter/model_record.rs +++ b/src/parameter/model_record.rs @@ -1,4 +1,3 @@ -use super::chemical_record::ChemicalRecord; use super::identifier::Identifier; use super::segment::SegmentRecord; use serde::{Deserialize, Serialize}; @@ -8,12 +7,7 @@ use serde::{Deserialize, Serialize}; pub struct PureRecord { pub identifier: Identifier, pub molarweight: f64, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub chemical_record: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub model_record: Option, + pub model_record: M, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub ideal_gas_record: Option, @@ -24,14 +18,12 @@ impl PureRecord { pub fn new( identifier: Identifier, molarweight: f64, - chemical_record: Option, - model_record: Option, + model_record: M, ideal_gas_record: Option, ) -> Self { Self { identifier, molarweight, - chemical_record, model_record, ideal_gas_record, } @@ -41,24 +33,26 @@ impl PureRecord { /// /// The [FromSegments] trait needs to be implemented for both the model record /// and the ideal gas record. - pub fn update_from_segments(&mut self, segments: S) + pub fn from_segments(identifier: Identifier, segments: S) -> Self where M: FromSegments, I: FromSegments, S: IntoIterator, f64)>, { - self.molarweight = 0.0; + let mut molarweight = 0.0; let mut model_segments = Vec::new(); let mut ideal_gas_segments = Vec::new(); for (s, n) in segments { - self.molarweight += s.molarweight * n; + molarweight += s.molarweight * n; model_segments.push((s.model_record, n)); ideal_gas_segments.push(s.ideal_gas_record.map(|ig| (ig, n))); } - self.model_record = Some(M::from_segments(&model_segments)); + let model_record = M::from_segments(&model_segments); let ideal_gas_segments: Option> = ideal_gas_segments.into_iter().collect(); - self.ideal_gas_record = ideal_gas_segments.as_deref().map(I::from_segments); + let ideal_gas_record = ideal_gas_segments.as_deref().map(I::from_segments); + + Self::new(identifier, molarweight, model_record, ideal_gas_record) } } @@ -71,12 +65,7 @@ where write!(f, "PureRecord(")?; write!(f, "\n\tidentifier={},", self.identifier)?; write!(f, "\n\tmolarweight={},", self.molarweight)?; - if let Some(m) = self.chemical_record.as_ref() { - write!(f, "\n\tchemical_record={},", m)?; - } - if let Some(m) = self.model_record.as_ref() { - write!(f, "\n\tmodel_record={},", m)?; - } + write!(f, "\n\tmodel_record={},", self.model_record)?; if let Some(i) = self.ideal_gas_record.as_ref() { write!(f, "\n\tideal_gas_record={},", i)?; } @@ -84,21 +73,6 @@ where } } -/// Empty record type to be used in models (e.g. group contribution -/// methods) that do not require a model or ideal gas record. -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct NoRecord; - -impl std::fmt::Display for NoRecord { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "") - } -} - -/// Convenience type for models in which molecules are purely described -/// by their chemical record. -pub type GroupContributionRecord = PureRecord; - /// Trait for models that implement a homosegmented group contribution /// method pub trait FromSegments: Clone { diff --git a/src/python/parameter.rs b/src/python/parameter.rs index 08554dd..d4ae3e6 100644 --- a/src/python/parameter.rs +++ b/src/python/parameter.rs @@ -1,7 +1,9 @@ use crate::impl_json_handling; -use crate::parameter::{BinaryRecord, ChemicalRecord, Identifier, NoRecord, ParameterError}; -use pyo3::exceptions::PyRuntimeError; +use crate::parameter::{BinaryRecord, ChemicalRecord, Identifier, ParameterError}; +use either::Either; +use pyo3::exceptions::{PyRuntimeError, PyValueError}; use pyo3::prelude::*; +use std::collections::HashMap; impl From for PyErr { fn from(e: ParameterError) -> PyErr { @@ -124,6 +126,8 @@ impl_json_handling!(PyIdentifier); /// /// Parameters /// ---------- +/// identifier : Identifier +/// The identifier of the pure component. /// segments : [str] /// List of segments, that the molecule consists of. /// bonds : [[int, int]], optional @@ -137,34 +141,69 @@ impl_json_handling!(PyIdentifier); /// ChemicalRecord #[pyclass(name = "ChemicalRecord")] #[derive(Clone)] -#[pyo3(text_signature = "(segments, bonds=None)")] +#[pyo3(text_signature = "(identifier, segments, bonds=None)")] pub struct PyChemicalRecord(pub ChemicalRecord); #[pymethods] impl PyChemicalRecord { #[new] - fn new(segments: Vec, bonds: Option>) -> Self { - Self(ChemicalRecord::new(segments, bonds)) + fn new(identifier: PyIdentifier, segments: &PyAny, bonds: Option<&PyAny>) -> PyResult { + if let Ok(segments) = segments.extract::>() { + let bonds = bonds + .map(|bonds| bonds.extract::>()) + .transpose()?; + Ok(Self(ChemicalRecord::new(identifier.0, segments, bonds))) + } else if let Ok(segments) = segments.extract::>() { + let bonds = bonds + .map(|bonds| bonds.extract::>()) + .transpose()?; + Ok(Self(ChemicalRecord::new_count( + identifier.0, + segments, + bonds, + ))) + } else { + Err(PyValueError::new_err( + "`segments` must either be a list or a dict of strings.", + )) + } } #[getter] - fn get_segments(&self) -> Vec { - self.0.segments.clone() - } - - #[setter] - fn set_segments(&mut self, segments: Vec) { - self.0.segments = segments + fn get_identifier(&self) -> PyIdentifier { + PyIdentifier(self.0.identifier().clone()) } #[getter] - fn get_bonds(&self) -> Vec<[usize; 2]> { - self.0.bonds.clone() + fn get_segments(&self, py: Python) -> PyObject { + match &self.0.segments() { + Either::Left(segments) => segments.to_object(py), + Either::Right(segments) => segments.to_object(py), + } } - #[setter] - fn set_bonds(&mut self, bonds: Vec<[usize; 2]>) { - self.0.bonds = bonds + #[getter] + fn get_bonds(&self, py: Python) -> PyObject { + match &self.0 { + ChemicalRecord::List { + identifier: _, + segments: _, + bonds, + } => bonds + .iter() + .map(|[a, b]| (a, b)) + .collect::>() + .to_object(py), + ChemicalRecord::Count { + identifier: _, + segments: _, + bonds, + } => bonds + .iter() + .map(|([a, b], c)| ((a, b), c)) + .collect::>() + .to_object(py), + } } } @@ -309,10 +348,6 @@ impl pyo3::class::basic::PyObjectProtocol for PyBinarySegmentRecord { impl_json_handling!(PyBinarySegmentRecord); -#[pyclass(name = "NoRecord")] -#[derive(Clone)] -pub struct PyNoRecord(pub NoRecord); - #[macro_export] macro_rules! impl_pure_record { ($model_record:ident, $py_model_record:ident, $ideal_gas_record:ident, $py_ideal_gas_record:ident) => { @@ -324,20 +359,18 @@ macro_rules! impl_pure_record { /// The identifier of the pure component. /// molarweight : float /// The molar weight (in g/mol) of the pure component. - /// model_record : ModelRecord, optional + /// model_record : ModelRecord /// The pure component model parameters. /// ideal_gas_record: IdealGasRecord, optional /// The pure component parameters for the ideal gas model. - /// chemical_record: ChemicalRecord, optional - /// The chemical record of the pure component. /// /// Returns /// ------- /// PureRecord #[pyclass(name = "PureRecord")] - #[pyo3(text_signature = "(identifier, molarweight, model_record=None, ideal_gas_record=None, chemical_record=None)")] + #[pyo3(text_signature = "(identifier, molarweight, model_record, ideal_gas_record=None)")] #[derive(Clone)] - pub struct PyPureRecord(PureRecord<$model_record, $ideal_gas_record>); + pub struct PyPureRecord(pub PureRecord<$model_record, $ideal_gas_record>); #[pymethods] impl PyPureRecord { @@ -345,15 +378,13 @@ macro_rules! impl_pure_record { fn new( identifier: PyIdentifier, molarweight: f64, - model_record: Option<$py_model_record>, + model_record: $py_model_record, ideal_gas_record: Option<$py_ideal_gas_record>, - chemical_record: Option, ) -> PyResult { Ok(Self(PureRecord::new( identifier.0, molarweight, - chemical_record.map(|cr| cr.0), - model_record.map(|mr| mr.0), + model_record.0, ideal_gas_record.map(|ig| ig.0), ))) } @@ -379,13 +410,13 @@ macro_rules! impl_pure_record { } #[getter] - fn get_model_record(&self) -> Option<$py_model_record> { - self.0.model_record.clone().map($py_model_record) + fn get_model_record(&self) -> $py_model_record { + $py_model_record(self.0.model_record.clone()) } #[setter] fn set_model_record(&mut self, model_record: $py_model_record) { - self.0.model_record = Some(model_record.0); + self.0.model_record = model_record.0; } #[getter] @@ -397,16 +428,6 @@ macro_rules! impl_pure_record { fn set_ideal_gas_record(&mut self, ideal_gas_record: $py_ideal_gas_record) { self.0.ideal_gas_record = Some(ideal_gas_record.0); } - - #[getter] - fn get_chemical_record(&self) -> Option { - self.0.chemical_record.clone().map(PyChemicalRecord) - } - - #[setter] - fn set_chemical_record(&mut self, chemical_record: PyChemicalRecord) { - self.0.chemical_record = Some(chemical_record.0); - } } #[pyproto] @@ -645,7 +666,7 @@ macro_rules! impl_parameter_from_segments { /// /// Parameters /// ---------- - /// pure_records : [PureRecord] + /// chemical_records : [ChemicalRecord] /// A list of pure component parameters. /// segment_records : [SegmentRecord] /// A list of records containing the parameters of @@ -653,14 +674,14 @@ macro_rules! impl_parameter_from_segments { /// binary_segment_records : [BinarySegmentRecord], optional /// A list of binary segment-segment parameters. #[staticmethod] - #[pyo3(text_signature = "(pure_records, segment_records, binary_segment_records=None)")] + #[pyo3(text_signature = "(chemical_records, segment_records, binary_segment_records=None)")] fn from_segments( - pure_records: Vec, + chemical_records: Vec, segment_records: Vec, binary_segment_records: Option>, ) -> Result { Ok(Self(Rc::new(<$parameter>::from_segments( - pure_records.into_iter().map(|pr| pr.0).collect(), + chemical_records.into_iter().map(|cr| cr.0).collect(), segment_records.into_iter().map(|sr| sr.0).collect(), binary_segment_records.map(|r| r.into_iter().map(|r| BinaryRecord{id1:r.0.id1,id2:r.0.id2,model_record:r.0.model_record.into()}).collect()), )?)))