Skip to main content

cmtool_data/
rawdata.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use crate::DataError;
4use crate::descriptors::{CMExportType, PhaseCM};
5use serde::{Deserialize, Serialize};
6use std::{
7    fs::File,
8    io::{Read, Write},
9    path::{Path, PathBuf},
10};
11
12///Scalar type (float)
13pub type ScalarValueType = f64; //TODO decide if this alias is needed
14
15// A trait for reading and writing raw data to and from storage.
16pub trait RawData: Sized {
17    /// Attempts to read raw data from a specified path and instantiate an object.
18    ///
19    /// # Arguments
20    ///
21    /// * `path` - A reference to a path from which to read the raw data. It can be any type
22    ///   that implements `AsRef<Path>`, such as `String` or `Path`.
23    ///
24    /// # Returns
25    ///
26    /// Returns an `Option<Self>`, which will be `Some(Self)` if reading and parsing are successful,
27    /// or `None` if an error occurs or the data is not available.
28    fn read_raw(path: impl AsRef<Path>) -> Option<Self>;
29
30    /// Attempts to write the raw data to a specified path.
31    ///
32    /// # Arguments
33    ///
34    /// * `&self` - The instance of the type implementing `RawData`.
35    /// * `path` - A string slice specifying the path where the raw data will be written.
36    ///
37    /// # Returns
38    ///
39    /// Returns a `Result<(), DataError>`, indicating success with `Ok(())` or an error
40    /// of type `DataError` if writing fails.
41    fn write_raw(&self, path: &str) -> Result<(), DataError>;
42}
43
44/// Represents the header of a flux file.
45///
46/// This header contains metadata about the flux data stored in the file,
47/// including the number of zones and the number of flux interactions.
48#[repr(C)]
49#[derive(Deserialize, Serialize, Clone, Copy, Default)]
50pub struct FluxFileHeader {
51    /// The number of zones in the flux file data.
52    pub n_zone: u32,
53    /// The number of flux interactions in the file.
54    pub n_fluxes: u32,
55}
56
57/// Represents a single raw flux interaction.
58///
59/// This struct is used to store individual flux interactions between a source and a target,
60/// including the flux values in both directions.
61#[repr(C)]
62#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
63pub struct RawFlux {
64    /// The identifier for the source in the flux interaction.
65    pub id_source: u32,
66    /// The identifier for the target in the flux interaction.
67    pub id_target: u32,
68    /// The flux value from the source to the target.
69    pub flux_source_target: f64,
70    /// The flux value from the target to the source.
71    pub flux_target_source: f64,
72}
73
74/// Represents a collection of raw flux data along with its header.
75///
76/// The `RawDataFlux` struct combines metadata about the flux data (via `FluxFileHeader`)
77/// with a vector of `RawFlux` instances, representing the actual flux interactions.
78#[derive(Deserialize, Serialize, Clone)]
79pub struct RawDataFlux {
80    /// The header containing metadata about the flux data.
81    pub header: FluxFileHeader,
82    /// A vector of `RawFlux` interactions, representing the actual flux data.
83    pub fluxes: Vec<RawFlux>,
84}
85
86/// Represents the header of a scalar file.
87///
88/// This header contains metadata about the scalar data stored in the file.
89#[repr(C)]
90#[derive(Deserialize, Serialize, Clone, Copy, Default)]
91pub struct ScalarFileHeader {
92    /// The number of zones in the scalar file data.
93    pub n_zone: u32,
94}
95
96/// Represents a single raw scalar value.
97///
98/// This struct is used to store individual floating-point scalar values.
99#[repr(C)]
100#[derive(Deserialize, Serialize, Clone, Copy)]
101pub struct RawScalar {
102    /// The scalar value stored as a floating-point number.
103    pub value: ScalarValueType,
104}
105
106/// Represents a collection of raw scalar data along with its header.
107///
108/// The `RawDataScalar` struct combines metadata about the data (via `ScalarFileHeader`)
109/// with a vector of `RawScalar` instances, representing the actual scalar values.
110#[derive(Deserialize, Serialize, Clone)]
111pub struct RawDataScalar {
112    /// The header containing metadata about the scalar data.
113    pub header: ScalarFileHeader,
114    /// A vector of `RawScalar` values, representing the actual scalar data.
115    pub values: Vec<RawScalar>,
116}
117
118/// Represents a phase in a multi-phase process or system.
119///
120/// The `RawPhase` struct is likely used to capture the state or characteristics
121/// of a specific phase, including flow data, volume information, and an identifier
122/// for the phase itself.
123#[derive(Deserialize, Serialize, Clone)]
124pub struct RawPhase {
125    /// Flow data associated with this phase.
126    pub flow: RawDataFlux,
127
128    /// Volume data associated with this phase.
129    pub volume: RawDataScalar,
130
131    /// Identifier for this phase.
132    pub identifier: PhaseCM,
133}
134
135impl RawPhase {
136    pub fn new_liquid(n_zone: usize, n_fluxes: usize) -> Self {
137        Self::new(n_zone, n_fluxes, PhaseCM::Liquid)
138    }
139    pub fn new_gas(n_zone: usize, n_fluxes: usize) -> Self {
140        Self::new(n_zone, n_fluxes, PhaseCM::Gas)
141    }
142
143    pub fn new(n_zone: usize, n_fluxes: usize, phase: PhaseCM) -> Self {
144        Self {
145            flow: RawDataFlux::new(n_zone, n_fluxes),
146            volume: RawDataScalar::new(n_zone),
147            identifier: phase,
148        }
149    }
150
151    pub fn write(&self, root: impl AsRef<std::path::Path>) -> Result<(String, String), DataError> {
152        let path = PathBuf::from(root.as_ref())
153            .join(CMExportType::Flow(self.identifier).default_filename());
154        self.flow.write_raw(path.to_str().unwrap())?;
155
156        let path = PathBuf::from(root.as_ref())
157            .join(CMExportType::Volume(self.identifier).default_filename());
158        self.volume.write_raw(path.to_str().unwrap())?;
159
160        Ok((
161            CMExportType::Flow(self.identifier).default_filename(),
162            CMExportType::Volume(self.identifier).default_filename(),
163        ))
164    }
165}
166
167impl Default for RawFlux {
168    fn default() -> Self {
169        Self {
170            id_source: 0,
171            id_target: 0,
172            flux_source_target: 0.,
173            flux_target_source: 0.,
174        }
175    }
176}
177
178impl From<ScalarValueType> for RawScalar {
179    #[inline(always)]
180    fn from(value: ScalarValueType) -> Self {
181        Self { value }
182    }
183}
184
185impl RawDataScalar {
186    pub fn new(n_zone: usize) -> Self {
187        Self {
188            header: ScalarFileHeader {
189                n_zone: n_zone.try_into().unwrap(),
190            },
191            values: Vec::with_capacity(n_zone),
192        }
193    }
194}
195
196impl From<Vec<ScalarValueType>> for RawDataScalar {
197    fn from(value: Vec<ScalarValueType>) -> Self {
198        value.as_slice().into()
199    }
200}
201
202// impl Into<Vec<ScalarValueType>> for RawDataScalar {
203//     fn into(self) -> Vec<ScalarValueType> {
204//         self.values.iter().map(|i| i.value).collect()
205//     }
206// }
207
208impl From<&[ScalarValueType]> for RawDataScalar {
209    fn from(value: &[ScalarValueType]) -> Self {
210        let len: u32 = value.len().try_into().unwrap_or_else(|_| {
211            panic!("Array length is too large to convert into u32");
212        });
213        Self {
214            header: ScalarFileHeader { n_zone: len },
215            values: value.iter().copied().map(Into::into).collect(),
216        }
217    }
218}
219
220impl RawDataFlux {
221    pub fn new(n_zone: usize, n_fluxes: usize) -> Self {
222        Self {
223            header: FluxFileHeader {
224                n_zone: n_zone as u32,
225                n_fluxes: n_fluxes as u32,
226            },
227            fluxes: vec![RawFlux::default(); n_fluxes],
228        }
229    }
230}
231
232impl RawData for RawDataScalar {
233    fn read_raw(path: impl AsRef<std::path::Path>) -> Option<Self> {
234        let mut file = File::open(path).ok()?;
235        let mut buffer = Vec::new();
236        file.read_to_end(&mut buffer).ok()?;
237
238        let mut offset = 0;
239        let header = ScalarFileHeader::from_bytes(&buffer, &mut offset)?;
240
241        let mut values = Vec::new();
242        while offset < buffer.len() {
243            values.push(RawScalar::from_bytes(&buffer, &mut offset)?);
244        }
245
246        Some(RawDataScalar { header, values })
247    }
248
249    fn write_raw(&self, path: &str) -> Result<(), DataError> {
250        let mut file = File::create(Path::new(path))?;
251        let mut buffer = Vec::new();
252
253        self.header.to_bytes(&mut buffer);
254        for value in &self.values {
255            value.to_bytes(&mut buffer);
256        }
257
258        file.write_all(&buffer)?;
259        Ok(())
260    }
261}
262
263impl RawData for RawDataFlux {
264    fn read_raw(path: impl AsRef<std::path::Path>) -> Option<Self> {
265        let mut file = File::open(path).ok()?;
266        let mut buffer = Vec::new();
267        file.read_to_end(&mut buffer).ok()?;
268
269        let mut offset = 0;
270        let header = FluxFileHeader::from_bytes(&buffer, &mut offset)?;
271
272        let mut fluxes = Vec::new();
273        while offset < buffer.len() {
274            fluxes.push(RawFlux::from_bytes(&buffer, &mut offset)?);
275        }
276
277        if fluxes.len() as u32 != header.n_fluxes {
278            return None;
279        }
280
281        Some(RawDataFlux { header, fluxes })
282    }
283
284    fn write_raw(&self, path: &str) -> Result<(), DataError> {
285        let mut file = File::create(Path::new(path))?;
286        let mut buffer = Vec::new();
287        self.header.to_bytes(&mut buffer);
288        for flux in &self.fluxes {
289            flux.to_bytes(&mut buffer);
290        }
291
292        file.write_all(&buffer)?;
293        Ok(())
294    }
295}
296
297pub trait FromBytes: Sized {
298    fn from_bytes(buffer: &[u8], offset: &mut usize) -> Option<Self>;
299}
300
301pub trait ToBytes {
302    fn to_bytes(&self, buffer: &mut Vec<u8>);
303}
304
305impl FromBytes for ScalarFileHeader {
306    fn from_bytes(buffer: &[u8], offset: &mut usize) -> Option<Self> {
307        if *offset + size_of::<u32>() > buffer.len() {
308            return None;
309        }
310        let n_zone = u32::from_le_bytes(
311            buffer[*offset..*offset + size_of::<u32>()]
312                .try_into()
313                .unwrap(),
314        );
315        *offset += size_of::<u32>();
316        Some(ScalarFileHeader { n_zone })
317    }
318}
319
320impl ToBytes for ScalarFileHeader {
321    fn to_bytes(&self, buffer: &mut Vec<u8>) {
322        buffer.extend_from_slice(&self.n_zone.to_le_bytes());
323    }
324}
325
326impl FromBytes for FluxFileHeader {
327    fn from_bytes(buffer: &[u8], offset: &mut usize) -> Option<Self> {
328        if *offset + 2 * size_of::<u32>() > buffer.len() {
329            return None;
330        }
331        let n_zone = u32::from_le_bytes(
332            buffer[*offset..*offset + size_of::<u32>()]
333                .try_into()
334                .unwrap(),
335        );
336        *offset += size_of::<u32>();
337        let n_max = u32::from_le_bytes(
338            buffer[*offset..*offset + size_of::<u32>()]
339                .try_into()
340                .unwrap(),
341        );
342        *offset += size_of::<u32>();
343        Some(FluxFileHeader {
344            n_zone,
345            n_fluxes: n_max,
346        })
347    }
348}
349
350impl ToBytes for FluxFileHeader {
351    fn to_bytes(&self, buffer: &mut Vec<u8>) {
352        buffer.extend_from_slice(&self.n_zone.to_le_bytes());
353        buffer.extend_from_slice(&self.n_fluxes.to_le_bytes());
354    }
355}
356
357impl FromBytes for RawScalar {
358    fn from_bytes(buffer: &[u8], offset: &mut usize) -> Option<Self> {
359        if *offset + size_of::<ScalarValueType>() > buffer.len() {
360            return None;
361        }
362        let value = ScalarValueType::from_le_bytes(
363            buffer[*offset..*offset + size_of::<ScalarValueType>()]
364                .try_into()
365                .unwrap(),
366        );
367        *offset += size_of::<ScalarValueType>();
368        Some(RawScalar { value })
369    }
370}
371
372impl ToBytes for RawScalar {
373    fn to_bytes(&self, buffer: &mut Vec<u8>) {
374        buffer.extend_from_slice(&self.value.to_le_bytes());
375    }
376}
377
378impl FromBytes for RawFlux {
379    fn from_bytes(buffer: &[u8], offset: &mut usize) -> Option<Self> {
380        if *offset + 2 * size_of::<u32>() + 2 * size_of::<f64>() > buffer.len() {
381            return None;
382        }
383        let id_source = u32::from_le_bytes(
384            buffer[*offset..*offset + size_of::<u32>()]
385                .try_into()
386                .unwrap(),
387        );
388        *offset += size_of::<u32>();
389        let id_target = u32::from_le_bytes(
390            buffer[*offset..*offset + size_of::<u32>()]
391                .try_into()
392                .unwrap(),
393        );
394        *offset += size_of::<u32>();
395        let flux_source_target = f64::from_le_bytes(
396            buffer[*offset..*offset + size_of::<f64>()]
397                .try_into()
398                .unwrap(),
399        );
400        *offset += size_of::<f64>();
401        let flux_target_source = f64::from_le_bytes(
402            buffer[*offset..*offset + size_of::<f64>()]
403                .try_into()
404                .unwrap(),
405        );
406        *offset += size_of::<f64>();
407
408        if flux_source_target < 0. || flux_target_source < 0. {
409            return None;
410        }
411
412        Some(RawFlux {
413            id_source,
414            id_target,
415            flux_source_target,
416            flux_target_source,
417        })
418    }
419}
420
421impl ToBytes for RawFlux {
422    fn to_bytes(&self, buffer: &mut Vec<u8>) {
423        buffer.extend_from_slice(&self.id_source.to_le_bytes());
424        buffer.extend_from_slice(&self.id_target.to_le_bytes());
425        buffer.extend_from_slice(&self.flux_source_target.to_le_bytes());
426        buffer.extend_from_slice(&self.flux_target_source.to_le_bytes());
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use crate::rawdata::{FromBytes, RawData, ToBytes};
433
434    use super::*;
435
436    #[test]
437    fn test_flux_file_header_serialization() {
438        let header = FluxFileHeader {
439            n_zone: 10,
440            n_fluxes: 100,
441        };
442
443        let mut buffer = Vec::new();
444        header.to_bytes(&mut buffer);
445        let mut offset = 0;
446        let deserialized = FluxFileHeader::from_bytes(&buffer, &mut offset).unwrap();
447
448        assert_eq!(header.n_zone, deserialized.n_zone);
449        assert_eq!(header.n_fluxes, deserialized.n_fluxes);
450    }
451
452    #[test]
453    fn test_scalar_file_header_serialization() {
454        let header = ScalarFileHeader { n_zone: 5 };
455
456        let mut buffer = Vec::new();
457        header.to_bytes(&mut buffer);
458        let mut offset = 0;
459        let deserialized = ScalarFileHeader::from_bytes(&buffer, &mut offset).unwrap();
460
461        assert_eq!(header.n_zone, deserialized.n_zone);
462    }
463
464    #[test]
465    fn test_raw_flux_serialization() {
466        let raw_flux = RawFlux {
467            id_source: 1,
468            id_target: 2,
469            flux_source_target: 0.1,
470            flux_target_source: 2.71,
471        };
472
473        let mut buffer = Vec::new();
474        raw_flux.to_bytes(&mut buffer);
475        let mut offset = 0;
476        let deserialized = RawFlux::from_bytes(&buffer, &mut offset).unwrap();
477
478        assert_eq!(raw_flux.id_source, deserialized.id_source);
479        assert_eq!(raw_flux.id_target, deserialized.id_target);
480        assert_eq!(raw_flux.flux_source_target, deserialized.flux_source_target);
481        assert_eq!(raw_flux.flux_target_source, deserialized.flux_target_source);
482    }
483
484    #[test]
485    fn test_raw_scalar_serialization() {
486        let raw_scalar = RawScalar { value: 0.1 };
487
488        let mut buffer = Vec::new();
489        raw_scalar.to_bytes(&mut buffer);
490        let mut offset = 0;
491        let deserialized = RawScalar::from_bytes(&buffer, &mut offset).unwrap();
492
493        assert_eq!(raw_scalar.value, deserialized.value);
494    }
495
496    #[test]
497    fn test_raw_data_scalar_serialization() {
498        let raw_data_scalar = RawDataScalar {
499            header: ScalarFileHeader { n_zone: 5 },
500            values: vec![RawScalar { value: 0.1 }, RawScalar { value: 2.71 }],
501        };
502
503        let mut buffer = Vec::new();
504        raw_data_scalar.header.to_bytes(&mut buffer);
505        for value in &raw_data_scalar.values {
506            value.to_bytes(&mut buffer);
507        }
508
509        let mut offset = 0;
510        let header = ScalarFileHeader::from_bytes(&buffer, &mut offset).unwrap();
511        let mut values = Vec::new();
512        while offset < buffer.len() {
513            values.push(RawScalar::from_bytes(&buffer, &mut offset).unwrap());
514        }
515
516        assert_eq!(raw_data_scalar.header.n_zone, header.n_zone);
517        assert_eq!(raw_data_scalar.values.len(), values.len());
518        assert_eq!(raw_data_scalar.values[0].value, values[0].value);
519        assert_eq!(raw_data_scalar.values[1].value, values[1].value);
520    }
521
522    #[test]
523    fn test_raw_data_flux_serialization() {
524        let raw_data_flux = RawDataFlux {
525            header: FluxFileHeader {
526                n_zone: 10,
527                n_fluxes: 100,
528            },
529            fluxes: vec![RawFlux {
530                id_source: 1,
531                id_target: 2,
532                flux_source_target: 0.1,
533                flux_target_source: 2.71,
534            }],
535        };
536
537        let mut buffer = Vec::new();
538        raw_data_flux.header.to_bytes(&mut buffer);
539        for flux in &raw_data_flux.fluxes {
540            flux.to_bytes(&mut buffer);
541        }
542
543        let mut offset = 0;
544        let header = FluxFileHeader::from_bytes(&buffer, &mut offset).unwrap();
545        let mut fluxes = Vec::new();
546        while offset < buffer.len() {
547            fluxes.push(RawFlux::from_bytes(&buffer, &mut offset).unwrap());
548        }
549
550        assert_eq!(raw_data_flux.header.n_zone, header.n_zone);
551        assert_eq!(raw_data_flux.header.n_fluxes, header.n_fluxes);
552        assert_eq!(raw_data_flux.fluxes.len(), fluxes.len());
553        assert_eq!(raw_data_flux.fluxes[0].id_source, fluxes[0].id_source);
554        assert_eq!(raw_data_flux.fluxes[0].id_target, fluxes[0].id_target);
555        assert_eq!(
556            raw_data_flux.fluxes[0].flux_source_target,
557            fluxes[0].flux_source_target
558        );
559        assert_eq!(
560            raw_data_flux.fluxes[0].flux_target_source,
561            fluxes[0].flux_target_source
562        );
563    }
564
565    #[test]
566    fn test_raw_data_trait() {
567        let raw_data_scalar = RawDataScalar {
568            header: ScalarFileHeader { n_zone: 5 },
569            values: vec![RawScalar { value: 0.1 }, RawScalar { value: 2.71 }],
570        };
571
572        let path = "./test.raw";
573        raw_data_scalar.write_raw(path).unwrap();
574        let deserialized = RawDataScalar::read_raw(path).unwrap();
575
576        assert_eq!(raw_data_scalar.header.n_zone, deserialized.header.n_zone);
577        assert_eq!(raw_data_scalar.values.len(), deserialized.values.len());
578        assert_eq!(
579            raw_data_scalar.values[0].value,
580            deserialized.values[0].value
581        );
582        assert_eq!(
583            raw_data_scalar.values[1].value,
584            deserialized.values[1].value
585        );
586
587        std::fs::remove_file(path).unwrap();
588    }
589
590    #[test]
591    fn test_raw_data_flux_write() {
592        let raw_data_flux = RawDataFlux {
593            header: FluxFileHeader {
594                n_zone: 10,
595                n_fluxes: 2,
596            },
597            fluxes: vec![
598                RawFlux {
599                    id_source: 1,
600                    id_target: 2,
601                    flux_source_target: 0.1,
602                    flux_target_source: 2.71,
603                },
604                RawFlux {
605                    id_source: 1,
606                    id_target: 2,
607                    flux_source_target: 0.1,
608                    flux_target_source: 2.71,
609                },
610            ],
611        };
612
613        let path = "./tes2t.raw";
614        raw_data_flux.write_raw(path).unwrap();
615        let deserialized = RawDataFlux::read_raw(path).unwrap();
616
617        assert_eq!(raw_data_flux.header.n_zone, deserialized.header.n_zone);
618        assert_eq!(raw_data_flux.header.n_fluxes, deserialized.header.n_fluxes);
619        assert_eq!(raw_data_flux.fluxes.len(), deserialized.fluxes.len());
620        assert_eq!(
621            raw_data_flux.fluxes[0].id_source,
622            deserialized.fluxes[0].id_source
623        );
624        assert_eq!(
625            raw_data_flux.fluxes[0].id_target,
626            deserialized.fluxes[0].id_target
627        );
628        assert_eq!(
629            raw_data_flux.fluxes[0].flux_source_target,
630            deserialized.fluxes[0].flux_source_target
631        );
632        assert_eq!(
633            raw_data_flux.fluxes[0].flux_target_source,
634            deserialized.fluxes[0].flux_target_source
635        );
636
637        std::fs::remove_file(path).unwrap();
638    }
639}