1use ndarray::Array2;
4
5use crate::{DataError, RawData, RawFlux, rawdata};
6
7pub struct FlowMapDescriptor {
8 pub flowmap: Array2<f64>,
9 pub neighbors: Array2<usize>, pub volumes: Vec<f64>,
11}
12
13impl FlowMapDescriptor {
14 pub fn from_path(
15 data_flows_path: impl AsRef<std::path::Path>,
16 data_volumes_path: impl AsRef<std::path::Path>,
17 ) -> Result<Self, DataError> {
18 let df = rawdata::RawDataFlux::read_raw(data_flows_path).ok_or(DataError::BadData)?;
19 let dv = rawdata::RawDataScalar::read_raw(data_volumes_path).ok_or(DataError::BadData)?;
20
21 Self::from_raw_data(&df, &dv)
22 }
23
24 pub fn from_raw_data(
25 data_flows: &rawdata::RawDataFlux,
26 data_volumes: &rawdata::RawDataScalar,
27 ) -> Result<Self, DataError> {
28 let n_zone = data_flows.header.n_zone as usize;
29 let mut flowmap = Array2::<f64>::zeros((n_zone, n_zone));
30
31 let mut neighbors: Vec<Vec<usize>> = vec![Vec::with_capacity(10); n_zone];
32
33 if data_flows.header.n_zone != data_volumes.header.n_zone {
34 return Err(DataError::BadData);
35 }
36
37 let mut add_at = |i: usize, j: usize, val: f64| -> Result<(), DataError> {
38 let g = flowmap.get_mut((i, j)).ok_or(DataError::BadData)?;
40 *g += val;
41 Ok(())
42 };
43
44 for &RawFlux {
45 id_source,
46 id_target,
47 flux_source_target,
48 flux_target_source,
49 } in data_flows.fluxes.iter()
50 {
51 let id_source = id_source as usize;
52 let id_target = id_target as usize;
53 assert!(flux_source_target >= 0.);
54 assert!(flux_target_source >= 0.);
55
56 add_at(id_source, id_target, flux_source_target)?;
57 add_at(id_target, id_source, flux_target_source)?;
58
59 neighbors[id_source].push(id_target);
60 neighbors[id_target].push(id_source);
61 }
62
63 let max_size = neighbors
64 .iter()
65 .map(|val| val.len())
66 .max()
67 .ok_or(DataError::BadData)?;
68
69 let mut neighbor_flat = Array2::<usize>::zeros((n_zone, max_size));
70
71 neighbor_flat.fill(n_zone + 1); for (i_zone, neighbors_for_zone) in neighbors.iter().enumerate() {
74 for (i_n, id_neighbor) in neighbors_for_zone.iter().enumerate() {
75 *(neighbor_flat
77 .get_mut((i_zone, i_n))
78 .expect("Flat neighbor out of bound")) = *id_neighbor;
79 }
80 }
81
82 let volumes: Vec<f64> = data_volumes.values.iter().map(|v| v.value).collect();
83
84 Ok(FlowMapDescriptor {
85 flowmap,
86 neighbors: neighbor_flat,
87 volumes,
88 })
89 }
90}
91
92#[cfg(test)]
93mod test {
94 use super::*;
95 use crate::test_utils::chain_of_three;
96
97 fn check_shape(descriptor: &FlowMapDescriptor) {
98 assert!(!descriptor.volumes.is_empty());
99 assert!(descriptor.flowmap.is_square());
100 assert!(descriptor.volumes.len() == descriptor.flowmap.ncols());
101 assert!(descriptor.neighbors.nrows() == descriptor.flowmap.ncols());
102 }
103
104 #[test]
105 fn descriptor_from_synthetic_data() {
106 let (flow, volume) = chain_of_three();
107 let descriptor = FlowMapDescriptor::from_raw_data(&flow, &volume).unwrap();
108
109 check_shape(&descriptor);
110
111 assert_eq!(descriptor.flowmap[[0, 1]], 1.0);
113 assert_eq!(descriptor.flowmap[[1, 0]], 0.5);
114 assert_eq!(descriptor.flowmap[[1, 2]], 2.0);
115 assert_eq!(descriptor.flowmap[[2, 1]], 0.25);
116 assert_eq!(descriptor.flowmap[[0, 2]], 0.0);
118 assert_eq!(descriptor.flowmap[[2, 0]], 0.0);
119
120 assert_eq!(descriptor.volumes, vec![1.0, 2.0, 4.0]);
121
122 let ghost = descriptor.volumes.len() + 1;
124 assert_eq!(descriptor.neighbors.ncols(), 2);
125 assert_eq!(descriptor.neighbors[[0, 0]], 1);
126 assert_eq!(descriptor.neighbors[[0, 1]], ghost);
127 assert_eq!(descriptor.neighbors[[1, 0]], 0);
128 assert_eq!(descriptor.neighbors[[1, 1]], 2);
129 assert_eq!(descriptor.neighbors[[2, 0]], 1);
130 assert_eq!(descriptor.neighbors[[2, 1]], ghost);
131 }
132
133 #[test]
134 fn descriptor_rejects_mismatched_zone_count() {
135 let (flow, _) = chain_of_three();
136 let short_volume = vec![1.0, 2.0].into();
137
138 assert!(FlowMapDescriptor::from_raw_data(&flow, &short_volume).is_err());
139 }
140
141 #[test]
142 fn read_descriptor() {
143 let _flow_cma = std::env::var("CUVE_SLDMSH_FLOW_PATH");
144
145 let _volume_cma = std::env::var("CUVE_SLDMSH_VOLUME_PATH");
146
147 if let (Ok(flow_cma), Ok(volume_cma)) = (_flow_cma, _volume_cma) {
148 let descriptor = FlowMapDescriptor::from_path(flow_cma, volume_cma).unwrap();
149
150 check_shape(&descriptor);
151 }
152 }
153}