Skip to content

Commit c2353f2

Browse files
committed
✨ Add validation of secondary flows
1 parent 0f99778 commit c2353f2

1 file changed

Lines changed: 49 additions & 1 deletion

File tree

src/input/process/flow.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use super::super::{input_err_msg, read_csv};
33
use crate::commodity::{CommodityID, CommodityMap};
44
use crate::process::{FlowType, ProcessFlow, ProcessFlowsMap, ProcessID, ProcessMap};
5-
use crate::region::parse_region_str;
5+
use crate::region::{RegionID, parse_region_str};
66
use crate::units::{FlowPerActivity, MoneyPerFlow};
77
use crate::year::parse_year_str;
88
use anyhow::{Context, Result, ensure};
@@ -133,6 +133,7 @@ where
133133
}
134134

135135
validate_flows_and_update_primary_output(processes, &flows_map)?;
136+
validate_secondary_io_flows(processes, &flows_map)?;
136137

137138
Ok(flows_map)
138139
}
@@ -230,6 +231,53 @@ fn check_flows_primary_output(
230231
Ok(())
231232
}
232233

234+
/// Checks that non-primary io are defined for all years (within a region) and that
235+
/// they are only inputs or only outputs in all years.
236+
fn validate_secondary_io_flows(
237+
processes: &mut ProcessMap,
238+
flows_map: &HashMap<ProcessID, ProcessFlowsMap>,
239+
) -> Result<()> {
240+
for (process_id, process) in processes.iter() {
241+
// Get the flows for this process - there should be no error, as was checked already
242+
let map = flows_map
243+
.get(process_id)
244+
.with_context(|| format!("Missing flows map for process {process_id}"))?;
245+
246+
// Get the non-primary io flows for all years, if any, arranged by (commodity, region)
247+
let iter = iproduct!(process.years.iter(), process.regions.iter());
248+
let mut flows: HashMap<(CommodityID, RegionID), Vec<bool>> = HashMap::new();
249+
for (&year, region_id) in iter {
250+
let flow = map[&(region_id.clone(), year)]
251+
.iter()
252+
.filter_map(|(commodity_id, flow)| {
253+
(Some(commodity_id) != process.primary_output.as_ref())
254+
.then_some(((commodity_id.clone(), region_id.clone()), flow.is_input()))
255+
});
256+
257+
for (key, value) in flow {
258+
flows.entry(key).or_insert_with(Vec::new).push(value);
259+
}
260+
}
261+
262+
// Finally we check that the flows for a given commodity and region are defined for all
263+
// years and that they are all inputs or all outputs
264+
for ((commodity_id, region_id), value) in flows.iter() {
265+
ensure!(
266+
value.len() == process.years.len(),
267+
"Flow of commodity {commodity_id} in region {region_id} for process {process_id} \
268+
does not cover all years"
269+
);
270+
ensure!(
271+
value.iter().all(|&x| x == value[0]),
272+
"Flow of commodity {commodity_id} in region {region_id} for process {process_id} \
273+
behaves as input or output in different years."
274+
);
275+
}
276+
}
277+
278+
Ok(())
279+
}
280+
233281
#[cfg(test)]
234282
mod tests {
235283
use super::*;

0 commit comments

Comments
 (0)