1use super::{AssociationRecord, BinaryAssociationRecord, Identifier, IdentifierOption};
2use crate::FeosResult;
3use crate::errors::FeosError;
4use indexmap::IndexSet;
5use num_traits::Zero;
6use serde::de::DeserializeOwned;
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, HashSet};
9use std::fmt;
10use std::fs::File;
11use std::io::BufReader;
12use std::ops::Deref;
13use std::path::Path;
14
15#[derive(Serialize, Deserialize, Clone, Debug)]
17pub struct Record<I, M, A> {
18 pub identifier: I,
19 #[serde(skip_serializing_if = "f64::is_zero")]
20 #[serde(default)]
21 pub molarweight: f64,
22 #[serde(flatten)]
23 pub model_record: M,
24 #[serde(skip_serializing_if = "Vec::is_empty")]
25 #[serde(default = "Vec::new")]
26 pub association_sites: Vec<AssociationRecord<A>>,
27}
28
29pub type PureRecord<M, A> = Record<Identifier, M, A>;
31
32pub type SegmentRecord<M, A> = Record<String, M, A>;
34
35impl<I, M, A> Record<I, M, A> {
36 pub fn new(identifier: I, molarweight: f64, model_record: M) -> Self {
38 Self::with_association(identifier, molarweight, model_record, vec![])
39 }
40
41 pub fn with_association(
43 identifier: I,
44 molarweight: f64,
45 model_record: M,
46 association_sites: Vec<AssociationRecord<A>>,
47 ) -> Self {
48 Self {
49 identifier,
50 molarweight,
51 model_record,
52 association_sites,
53 }
54 }
55
56 pub fn from_segments<S>(identifier: I, segments: S) -> FeosResult<Self>
60 where
61 M: FromSegments,
62 S: IntoIterator<Item = (SegmentRecord<M, A>, f64)>,
63 {
64 let mut molarweight = 0.0;
65 let mut model_segments = Vec::new();
66 let association_sites = segments
67 .into_iter()
68 .flat_map(|(s, n)| {
69 molarweight += s.molarweight * n;
70 model_segments.push((s.model_record, n));
71 s.association_sites.into_iter().map(move |record| {
72 AssociationRecord::with_id(
73 record.id,
74 record.parameters,
75 record.na * n,
76 record.nb * n,
77 record.nc * n,
78 )
79 })
80 })
81 .collect();
82 let model_record = M::from_segments(&model_segments)?;
83
84 Ok(Self::with_association(
85 identifier,
86 molarweight,
87 model_record,
88 association_sites,
89 ))
90 }
91}
92
93impl<M, A> PureRecord<M, A> {
94 pub fn from_json<P, S>(
96 substances: &[S],
97 file: P,
98 identifier_option: IdentifierOption,
99 ) -> FeosResult<Vec<Self>>
100 where
101 P: AsRef<Path>,
102 S: Deref<Target = str>,
103 M: DeserializeOwned,
104 A: DeserializeOwned,
105 {
106 let mut queried: HashSet<&str> = substances.iter().map(|s| s.deref()).collect();
108 if queried.len() != substances.len() {
110 return Err(FeosError::IncompatibleParameters(
111 "A substance was defined more than once.".to_string(),
112 ));
113 }
114
115 let f = File::open(file)?;
116 let reader = BufReader::new(f);
117 let file_records: Vec<Self> = serde_json::from_reader(reader)?;
119 let mut records: HashMap<&str, Self> = HashMap::with_capacity(substances.len());
120
121 for record in file_records {
123 if let Some(id) = record.identifier.as_str(identifier_option) {
124 queried.take(id).map(|id| records.insert(id, record));
125 }
126 if queried.is_empty() {
128 break;
129 }
130 }
131
132 if !queried.is_empty() {
134 return Err(FeosError::ComponentsNotFound(format!("{queried:?}")));
135 };
136
137 Ok(substances
139 .iter()
140 .map(|s| records.remove(s.deref()).unwrap())
141 .collect())
142 }
143
144 pub fn from_multiple_json<P, S>(
146 input: &[(Vec<S>, P)],
147 identifier_option: IdentifierOption,
148 ) -> FeosResult<Vec<Self>>
149 where
150 P: AsRef<Path>,
151 S: Deref<Target = str>,
152 M: DeserializeOwned,
153 A: DeserializeOwned,
154 {
155 let nsubstances = input
157 .iter()
158 .fold(0, |acc, (substances, _)| acc + substances.len());
159
160 let queried: IndexSet<String> = input
162 .iter()
163 .flat_map(|(substances, _)| substances)
164 .map(|substance| substance.to_string())
165 .collect();
166
167 if queried.len() != nsubstances {
169 return Err(FeosError::IncompatibleParameters(
170 "A substance was defined more than once.".to_string(),
171 ));
172 }
173
174 let mut records: Vec<Self> = Vec::with_capacity(nsubstances);
175
176 for (substances, file) in input {
178 records.extend(Self::from_json(substances, file, identifier_option)?);
179 }
180
181 Ok(records)
182 }
183}
184
185impl<M, A> SegmentRecord<M, A> {
186 pub fn from_json<P: AsRef<Path>>(file: P) -> FeosResult<Vec<Self>>
188 where
189 M: DeserializeOwned,
190 A: DeserializeOwned,
191 {
192 Ok(serde_json::from_reader(BufReader::new(File::open(file)?))?)
193 }
194}
195
196impl<M: Serialize, A: Serialize> fmt::Display for PureRecord<M, A> {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 let s = serde_json::to_string(self).unwrap().replace("\"", "");
199 let s = s.replace(",", ", ").replace(":", ": ");
200 write!(f, "PureRecord({})", &s[1..s.len() - 1])
201 }
202}
203
204impl<M: Serialize, A: Serialize> fmt::Display for SegmentRecord<M, A> {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 let s = serde_json::to_string(self).unwrap().replace("\"", "");
207 let s = s.replace(",", ", ").replace(":", ": ");
208 write!(f, "SegmentRecord({})", &s[1..s.len() - 1])
209 }
210}
211
212pub trait FromSegments: Clone {
215 fn from_segments(segments: &[(Self, f64)]) -> FeosResult<Self>;
218}
219
220pub trait FromSegmentsBinary: Clone {
223 fn from_segments_binary(segments: &[(Self, f64, f64)]) -> FeosResult<Self>;
226}
227
228impl FromSegmentsBinary for () {
229 fn from_segments_binary(_: &[(Self, f64, f64)]) -> FeosResult<Self> {
230 Ok(())
231 }
232}
233
234#[derive(Serialize, Deserialize, Clone, Debug)]
236pub struct BinaryRecord<I, B, A> {
237 pub id1: I,
239 pub id2: I,
241 #[serde(flatten)]
243 pub model_record: Option<B>,
244 #[serde(skip_serializing_if = "Vec::is_empty")]
246 #[serde(default = "Vec::new")]
247 pub association_sites: Vec<BinaryAssociationRecord<A>>,
248}
249
250pub type BinarySegmentRecord<M, A> = BinaryRecord<String, M, A>;
252
253impl<I, B, A> BinaryRecord<I, B, A> {
254 pub fn new(id1: I, id2: I, model_record: Option<B>) -> Self {
256 Self::with_association(id1, id2, model_record, vec![])
257 }
258
259 pub fn with_association(
261 id1: I,
262 id2: I,
263 model_record: Option<B>,
264 association_sites: Vec<BinaryAssociationRecord<A>>,
265 ) -> Self {
266 Self {
267 id1,
268 id2,
269 model_record,
270 association_sites,
271 }
272 }
273
274 pub fn from_json<P: AsRef<Path>>(file: P) -> FeosResult<Vec<Self>>
276 where
277 I: DeserializeOwned,
278 B: DeserializeOwned,
279 A: DeserializeOwned,
280 {
281 Ok(serde_json::from_reader(BufReader::new(File::open(file)?))?)
282 }
283}
284
285impl<I: Serialize + Clone, B: Serialize + Clone, A: Serialize + Clone> fmt::Display
286 for BinaryRecord<I, B, A>
287{
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 let s = serde_json::to_string(self).unwrap().replace("\"", "");
290 let s = s.replace(",", ", ").replace(":", ": ");
291 write!(f, "BinaryRecord({})", &s[1..s.len() - 1])
292 }
293}
294
295#[cfg(test)]
296mod test {
297 use super::*;
298
299 #[derive(Serialize, Deserialize, Debug, Default, Clone)]
300 struct TestModelRecordSegments {
301 a: f64,
302 }
303
304 #[test]
305 fn deserialize() {
306 let r = r#"
307 {
308 "identifier": {
309 "cas": "123-4-5"
310 },
311 "molarweight": 16.0426,
312 "a": 0.1
313 }
314 "#;
315 let record: PureRecord<TestModelRecordSegments, ()> =
316 serde_json::from_str(r).expect("Unable to parse json.");
317 assert_eq!(record.identifier.cas, Some("123-4-5".into()))
318 }
319
320 #[test]
321 fn deserialize_list() {
322 let r = r#"
323 [
324 {
325 "identifier": {
326 "cas": "1"
327 },
328 "molarweight": 1.0,
329 "a": 1.0
330 },
331 {
332 "identifier": {
333 "cas": "2"
334 },
335 "molarweight": 2.0,
336 "a": 2.0
337 }
338 ]"#;
339 let records: Vec<PureRecord<TestModelRecordSegments, ()>> =
340 serde_json::from_str(r).expect("Unable to parse json.");
341 assert_eq!(records[0].identifier.cas, Some("1".into()));
342 assert_eq!(records[1].identifier.cas, Some("2".into()))
343 }
344}