|
2 | 2 | use super::super::{input_err_msg, read_csv}; |
3 | 3 | use crate::commodity::{CommodityID, CommodityMap}; |
4 | 4 | use crate::process::{FlowType, ProcessFlow, ProcessFlowsMap, ProcessID, ProcessMap}; |
5 | | -use crate::region::parse_region_str; |
| 5 | +use crate::region::{RegionID, parse_region_str}; |
6 | 6 | use crate::units::{FlowPerActivity, MoneyPerFlow}; |
7 | 7 | use crate::year::parse_year_str; |
8 | 8 | use anyhow::{Context, Result, ensure}; |
@@ -133,6 +133,7 @@ where |
133 | 133 | } |
134 | 134 |
|
135 | 135 | validate_flows_and_update_primary_output(processes, &flows_map)?; |
| 136 | + validate_secondary_io_flows(processes, &flows_map)?; |
136 | 137 |
|
137 | 138 | Ok(flows_map) |
138 | 139 | } |
@@ -230,6 +231,53 @@ fn check_flows_primary_output( |
230 | 231 | Ok(()) |
231 | 232 | } |
232 | 233 |
|
| 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 | + |
233 | 281 | #[cfg(test)] |
234 | 282 | mod tests { |
235 | 283 | use super::*; |
|
0 commit comments