From 03b59f221475d5af00c49f36200f05b5bc59b203 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Fri, 10 Jan 2025 17:01:02 +0000 Subject: [PATCH 01/13] Rename: flow_raw => flow --- src/input/process/flow.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/input/process/flow.rs b/src/input/process/flow.rs index cdc93374e..7054c4381 100644 --- a/src/input/process/flow.rs +++ b/src/input/process/flow.rs @@ -46,17 +46,17 @@ fn read_process_flows_from_iter( where I: Iterator, { - iter.map(|flow_raw| -> Result { + iter.map(|flow| -> Result { let commodity = commodities - .get(flow_raw.commodity_id.as_str()) - .with_context(|| format!("{} is not a valid commodity ID", &flow_raw.commodity_id))?; + .get(flow.commodity_id.as_str()) + .with_context(|| format!("{} is not a valid commodity ID", &flow.commodity_id))?; Ok(ProcessFlow { - process_id: flow_raw.process_id, + process_id: flow.process_id, commodity: Rc::clone(commodity), - flow: flow_raw.flow, - flow_type: flow_raw.flow_type, - flow_cost: flow_raw.flow_cost.unwrap_or(0.0), + flow: flow.flow, + flow_type: flow.flow_type, + flow_cost: flow.flow_cost.unwrap_or(0.0), }) }) .process_results(|iter| iter.into_id_map(process_ids))? From f9d7280637eec508f1f5592c915bff88bcca3d99 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Fri, 10 Jan 2025 17:16:10 +0000 Subject: [PATCH 02/13] Check process flows are valid numbers Closes #277. --- docs/dispatch_optimisation.md | 3 --- src/input/process/flow.rs | 46 ++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/docs/dispatch_optimisation.md b/docs/dispatch_optimisation.md index 8927fa972..bbb6c5eee 100644 --- a/docs/dispatch_optimisation.md +++ b/docs/dispatch_optimisation.md @@ -52,9 +52,6 @@ for all commodity flows that the process has (except *pac1*). Where *pac1* is th primary activity commodity for the asset (i.e. all input and output flows are made proportional to *pac1* flow). -Note – need to handle cases where a flow is set to zero in the input data – should raise a -warning that the value has been ignored, specifying which region/asset/commodity. - **TBD** - cases where time slice level of the commodity is seasonal or annual. ### Commodity-flexible assets diff --git a/src/input/process/flow.rs b/src/input/process/flow.rs index 7054c4381..1920fdb73 100644 --- a/src/input/process/flow.rs +++ b/src/input/process/flow.rs @@ -3,7 +3,7 @@ use super::define_process_id_getter; use crate::commodity::Commodity; use crate::input::*; use crate::process::{FlowType, ProcessFlow}; -use anyhow::{Context, Result}; +use anyhow::{ensure, Context, Result}; use itertools::Itertools; use serde::Deserialize; use std::collections::{HashMap, HashSet}; @@ -51,6 +51,13 @@ where .get(flow.commodity_id.as_str()) .with_context(|| format!("{} is not a valid commodity ID", &flow.commodity_id))?; + // Check that flow is not zero, infinity, etc. + ensure!( + flow.flow.is_normal(), + "Invalid value for flow: {}", + flow.flow + ); + Ok(ProcessFlow { process_id: flow.process_id, commodity: Rc::clone(commodity), @@ -67,6 +74,7 @@ mod test { use super::*; use crate::commodity::{CommodityCostMap, CommodityType}; use crate::time_slice::TimeSliceLevel; + use std::iter; #[test] fn test_read_process_flows_from_iter_good() { @@ -190,4 +198,40 @@ mod test { .is_err() ); } + + #[test] + fn test_read_process_flows_from_iter_bad_flow() { + let process_ids = iter::once("id1".into()).collect(); + let commodities = iter::once(Commodity { + id: "commodity1".into(), + description: "Some description".into(), + kind: CommodityType::InputCommodity, + time_slice_level: TimeSliceLevel::Annual, + costs: CommodityCostMap::new(), + demand_by_region: HashMap::new(), + }) + .map(|c| (c.id.clone(), Rc::new(c))) + .collect(); + + macro_rules! check_bad_flow { + ($flow:expr) => { + let flow = ProcessFlowRaw { + process_id: "id1".into(), + commodity_id: "commodity1".into(), + flow: $flow, + flow_type: FlowType::Fixed, + flow_cost: Some(1.0), + }; + assert!( + read_process_flows_from_iter(iter::once(flow), &process_ids, &commodities) + .is_err() + ); + }; + } + + check_bad_flow!(0.0); + check_bad_flow!(f64::NEG_INFINITY); + check_bad_flow!(f64::INFINITY); + check_bad_flow!(f64::NAN); + } } From c9e4ca78ccd46593940a36d03d4c5f511eb242b9 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Fri, 10 Jan 2025 16:57:36 +0000 Subject: [PATCH 03/13] Disallow commodity flexible assets for now --- src/input/process/flow.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/input/process/flow.rs b/src/input/process/flow.rs index 1920fdb73..64fbccf9f 100644 --- a/src/input/process/flow.rs +++ b/src/input/process/flow.rs @@ -58,6 +58,12 @@ where flow.flow ); + // **TODO**: https://github.com/EnergySystemsModellingLab/MUSE_2.0/issues/300 + ensure!( + flow.flow_type == FlowType::Fixed, + "Commodity flexible assets are not currently supported" + ); + Ok(ProcessFlow { process_id: flow.process_id, commodity: Rc::clone(commodity), From 4d86d2dd1b4c84719cb730c0089a8d90342417a0 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Fri, 10 Jan 2025 18:12:52 +0000 Subject: [PATCH 04/13] Check flow cost is valid Closes #278. --- src/input/process/flow.rs | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/input/process/flow.rs b/src/input/process/flow.rs index 64fbccf9f..220c8c4ed 100644 --- a/src/input/process/flow.rs +++ b/src/input/process/flow.rs @@ -64,6 +64,13 @@ where "Commodity flexible assets are not currently supported" ); + if let Some(flow_cost) = flow.flow_cost { + ensure!( + (0.0..f64::INFINITY).contains(&flow_cost), + "Invalid value for flow cost ({flow_cost}). Must be >=0." + ) + } + Ok(ProcessFlow { process_id: flow.process_id, commodity: Rc::clone(commodity), @@ -240,4 +247,40 @@ mod test { check_bad_flow!(f64::INFINITY); check_bad_flow!(f64::NAN); } + + #[test] + fn test_read_process_flows_from_iter_flow_cost() { + let process_ids = iter::once("id1".into()).collect(); + let commodities = iter::once(Commodity { + id: "commodity1".into(), + description: "Some description".into(), + kind: CommodityType::InputCommodity, + time_slice_level: TimeSliceLevel::Annual, + costs: CommodityCostMap::new(), + demand_by_region: HashMap::new(), + }) + .map(|c| (c.id.clone(), Rc::new(c))) + .collect(); + + macro_rules! is_flow_cost_ok { + ($flow_cost:expr) => {{ + let flow = ProcessFlowRaw { + process_id: "id1".into(), + commodity_id: "commodity1".into(), + flow: 1.0, + flow_type: FlowType::Fixed, + flow_cost: Some($flow_cost), + }; + + read_process_flows_from_iter(iter::once(flow), &process_ids, &commodities).is_ok() + }}; + } + + assert!(is_flow_cost_ok!(0.0)); + assert!(is_flow_cost_ok!(1.0)); + assert!(is_flow_cost_ok!(100.0)); + assert!(!is_flow_cost_ok!(f64::NEG_INFINITY)); + assert!(!is_flow_cost_ok!(f64::INFINITY)); + assert!(!is_flow_cost_ok!(f64::NAN)); + } } From 4b7e1ec096fa1fe056fedd4029c0472f0d33c516 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Mon, 13 Jan 2025 12:03:12 +0000 Subject: [PATCH 05/13] Ensure every process has associated flows Closes #279. --- src/input/process.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/input/process.rs b/src/input/process.rs index ee26e15a9..880cdf152 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -70,12 +70,16 @@ pub fn read_processes( let mut parameters = read_process_parameters(model_dir, &process_ids, year_range)?; let mut regions = read_process_regions(model_dir, &process_ids, region_ids)?; - Ok(process_ids + process_ids .into_iter() .map(|id| { // We know entry is present let desc = descriptions.remove(&id).unwrap(); + let flows = flows + .remove(&id) + .with_context(|| format!("No commodity flows defined for process {id}"))?; + // We've already checked that these exist for each process let parameter = parameters.remove(&id).unwrap(); let regions = regions.remove(&id).unwrap(); @@ -84,13 +88,13 @@ pub fn read_processes( id: desc.id, description: desc.description, availabilities: availabilities.remove(&id).unwrap_or_default(), - flows: flows.remove(&id).unwrap_or_default(), + flows, pacs: pacs.remove(&id).unwrap_or_default(), parameter, regions, }; - (id, process.into()) + Ok((id, process.into())) }) - .collect()) + .process_results(|iter| iter.collect()) } From 2244093dbf3341f3604b5ec340a679725fb4e0b7 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Mon, 13 Jan 2025 12:06:01 +0000 Subject: [PATCH 06/13] Ensure every process has at least one PAC --- src/input/process.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/input/process.rs b/src/input/process.rs index 880cdf152..a86127369 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -79,6 +79,9 @@ pub fn read_processes( let flows = flows .remove(&id) .with_context(|| format!("No commodity flows defined for process {id}"))?; + let pacs = pacs + .remove(&id) + .with_context(|| format!("No PACs defined for process {id}"))?; // We've already checked that these exist for each process let parameter = parameters.remove(&id).unwrap(); @@ -89,7 +92,7 @@ pub fn read_processes( description: desc.description, availabilities: availabilities.remove(&id).unwrap_or_default(), flows, - pacs: pacs.remove(&id).unwrap_or_default(), + pacs, parameter, regions, }; From 0654e2107f7767229b7adfb40402e97fe63bd139 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Thu, 16 Jan 2025 10:39:36 +0000 Subject: [PATCH 07/13] Add separate check whether flow is zero --- src/input/process/flow.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/input/process/flow.rs b/src/input/process/flow.rs index 220c8c4ed..b8c118fb4 100644 --- a/src/input/process/flow.rs +++ b/src/input/process/flow.rs @@ -51,10 +51,12 @@ where .get(flow.commodity_id.as_str()) .with_context(|| format!("{} is not a valid commodity ID", &flow.commodity_id))?; - // Check that flow is not zero, infinity, etc. + ensure!(flow.flow != 0.0, "Flow cannot be zero"); + + // Check that flow is not infinity, nan, etc. ensure!( flow.flow.is_normal(), - "Invalid value for flow: {}", + "Invalid value for flow ({})", flow.flow ); From c2f31bf749bf9d29105e52534cdd7afbbcd60786 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Thu, 16 Jan 2025 10:49:24 +0000 Subject: [PATCH 08/13] Fix: `availabilities` will not be empty here --- src/input/process.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/input/process.rs b/src/input/process.rs index a86127369..526845176 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -86,11 +86,12 @@ pub fn read_processes( // We've already checked that these exist for each process let parameter = parameters.remove(&id).unwrap(); let regions = regions.remove(&id).unwrap(); + let availabilities = availabilities.remove(&id).unwrap(); let process = Process { id: desc.id, description: desc.description, - availabilities: availabilities.remove(&id).unwrap_or_default(), + availabilities, flows, pacs, parameter, From ec65c442827fce72c966a3b6d4e309b43d3fc54b Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Thu, 16 Jan 2025 10:46:35 +0000 Subject: [PATCH 09/13] Refactor `read_processes` to consume `descriptions` --- src/input/process.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/input/process.rs b/src/input/process.rs index 526845176..79744e6db 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -61,7 +61,7 @@ pub fn read_processes( year_range: &RangeInclusive, ) -> Result, Rc>> { let file_path = model_dir.join(PROCESSES_FILE_NAME); - let mut descriptions = read_csv_id_file::(&file_path)?; + let descriptions = read_csv_id_file::(&file_path)?; let process_ids = HashSet::from_iter(descriptions.keys().cloned()); let mut availabilities = read_process_availabilities(model_dir, &process_ids, time_slice_info)?; @@ -70,12 +70,9 @@ pub fn read_processes( let mut parameters = read_process_parameters(model_dir, &process_ids, year_range)?; let mut regions = read_process_regions(model_dir, &process_ids, region_ids)?; - process_ids + descriptions .into_iter() - .map(|id| { - // We know entry is present - let desc = descriptions.remove(&id).unwrap(); - + .map(|(id, description)| { let flows = flows .remove(&id) .with_context(|| format!("No commodity flows defined for process {id}"))?; @@ -89,8 +86,8 @@ pub fn read_processes( let availabilities = availabilities.remove(&id).unwrap(); let process = Process { - id: desc.id, - description: desc.description, + id: description.id, + description: description.description, availabilities, flows, pacs, From cc53fda29feecb7c9c89e6fce40e45afa0a02d8d Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Thu, 16 Jan 2025 11:06:50 +0000 Subject: [PATCH 10/13] Refactor creation of `Process`es into separate function --- src/input/process.rs | 62 +++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/src/input/process.rs b/src/input/process.rs index 79744e6db..7a84547df 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -1,7 +1,8 @@ //! Code for reading process-related information from CSV files. use crate::commodity::Commodity; use crate::input::*; -use crate::process::Process; +use crate::process::{Process, ProcessAvailability, ProcessFlow, ProcessParameter}; +use crate::region::RegionSelection; use crate::time_slice::TimeSliceInfo; use anyhow::Result; use serde::Deserialize; @@ -40,6 +41,9 @@ struct ProcessDescription { } define_id_getter! {ProcessDescription} +/// A map of process-related data structures, grouped by process ID +type GroupedMap = HashMap, Vec>; + /// Read process information from the specified CSV files. /// /// # Arguments @@ -64,29 +68,57 @@ pub fn read_processes( let descriptions = read_csv_id_file::(&file_path)?; let process_ids = HashSet::from_iter(descriptions.keys().cloned()); - let mut availabilities = read_process_availabilities(model_dir, &process_ids, time_slice_info)?; - let mut flows = read_process_flows(model_dir, &process_ids, commodities)?; - let mut pacs = read_process_pacs(model_dir, &process_ids, commodities, &flows)?; - let mut parameters = read_process_parameters(model_dir, &process_ids, year_range)?; - let mut regions = read_process_regions(model_dir, &process_ids, region_ids)?; + let availabilities = read_process_availabilities(model_dir, &process_ids, time_slice_info)?; + let flows = read_process_flows(model_dir, &process_ids, commodities)?; + let pacs = read_process_pacs(model_dir, &process_ids, commodities, &flows)?; + let parameters = read_process_parameters(model_dir, &process_ids, year_range)?; + let regions = read_process_regions(model_dir, &process_ids, region_ids)?; + + create_process_map( + descriptions.into_values(), + availabilities, + flows, + pacs, + parameters, + regions, + ) +} + +fn create_process_map( + descriptions: I, + availabilities: GroupedMap, + flows: GroupedMap, + pacs: GroupedMap>, + parameters: HashMap, ProcessParameter>, + regions: HashMap, RegionSelection>, +) -> Result, Rc>> +where + I: Iterator, +{ + // Need to be mutable as we remove elements as we go along + let mut availabilities = availabilities; + let mut flows = flows; + let mut pacs = pacs; + let mut parameters = parameters; + let mut regions = regions; descriptions - .into_iter() - .map(|(id, description)| { + .map(|description| { + let id = &description.id; let flows = flows - .remove(&id) + .remove(id) .with_context(|| format!("No commodity flows defined for process {id}"))?; let pacs = pacs - .remove(&id) + .remove(id) .with_context(|| format!("No PACs defined for process {id}"))?; // We've already checked that these exist for each process - let parameter = parameters.remove(&id).unwrap(); - let regions = regions.remove(&id).unwrap(); - let availabilities = availabilities.remove(&id).unwrap(); + let parameter = parameters.remove(id).unwrap(); + let regions = regions.remove(id).unwrap(); + let availabilities = availabilities.remove(id).unwrap(); let process = Process { - id: description.id, + id: Rc::clone(id), description: description.description, availabilities, flows, @@ -95,7 +127,7 @@ pub fn read_processes( regions, }; - Ok((id, process.into())) + Ok((description.id, process.into())) }) .process_results(|iter| iter.collect()) } From 4622e145a91db8cd9f5e064a440961bee72de68c Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Thu, 16 Jan 2025 11:31:14 +0000 Subject: [PATCH 11/13] Add tests for `create_process_map` --- src/input/process.rs | 121 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/src/input/process.rs b/src/input/process.rs index 7a84547df..ed0f46aab 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -131,3 +131,124 @@ where }) .process_results(|iter| iter.collect()) } + +#[cfg(test)] +mod tests { + use super::*; + + struct ProcessData { + descriptions: Vec, + availabilities: GroupedMap, + flows: GroupedMap, + pacs: GroupedMap>, + parameters: HashMap, ProcessParameter>, + regions: HashMap, RegionSelection>, + } + + /// Returns example data (without errors) for processes + fn get_process_data() -> ProcessData { + let descriptions = vec![ + ProcessDescription { + id: Rc::from("process1"), + description: "Process 1".to_string(), + }, + ProcessDescription { + id: Rc::from("process2"), + description: "Process 2".to_string(), + }, + ]; + + let availabilities = ["process1", "process2"] + .into_iter() + .map(|id| (id.into(), vec![])) + .collect(); + + let flows = ["process1", "process2"] + .into_iter() + .map(|id| (id.into(), vec![])) + .collect(); + + let pacs = ["process1", "process2"] + .into_iter() + .map(|id| (id.into(), vec![])) + .collect(); + + let parameters = ["process1", "process2"] + .into_iter() + .map(|id| { + let parameter = ProcessParameter { + process_id: id.to_string(), + years: 2010..=2020, + capital_cost: 0.0, + fixed_operating_cost: 0.0, + variable_operating_cost: 0.0, + lifetime: 1, + discount_rate: 1.0, + cap2act: 0.0, + }; + + (id.into(), parameter) + }) + .collect(); + + let regions = ["process1", "process2"] + .into_iter() + .map(|id| (id.into(), RegionSelection::All)) + .collect(); + + ProcessData { + descriptions, + availabilities, + flows, + pacs, + parameters, + regions, + } + } + + #[test] + fn test_create_process_map_success() { + let data = get_process_data(); + let result = create_process_map( + data.descriptions.into_iter(), + data.availabilities, + data.flows, + data.pacs, + data.parameters, + data.regions, + ) + .unwrap(); + + assert_eq!(result.len(), 2); + assert!(result.contains_key("process1")); + assert!(result.contains_key("process2")); + } + + /// Generate code for a test with data missing for a given field + macro_rules! test_missing { + ($field:ident) => { + let mut data = get_process_data(); + data.$field.remove("process1"); + + let result = create_process_map( + data.descriptions.into_iter(), + data.availabilities, + data.flows, + data.pacs, + data.parameters, + data.regions, + ); + assert!(result.is_err()); + }; + } + + #[test] + fn test_create_process_map_missing_pacs() { + test_missing!(pacs); + } + + #[test] + fn test_create_process_map_missing_flows() { + test_missing!(flows); + } +} From 518bb1228ecb69b9a0727d759edd8ac50fdad913 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Thu, 16 Jan 2025 11:34:46 +0000 Subject: [PATCH 12/13] Move check for missing params to create_process_map --- src/input/process.rs | 9 ++++++++- src/input/process/parameter.rs | 29 ----------------------------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/src/input/process.rs b/src/input/process.rs index ed0f46aab..27b1ad102 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -111,9 +111,11 @@ where let pacs = pacs .remove(id) .with_context(|| format!("No PACs defined for process {id}"))?; + let parameter = parameters + .remove(id) + .with_context(|| format!("No parameters defined for process {id}"))?; // We've already checked that these exist for each process - let parameter = parameters.remove(id).unwrap(); let regions = regions.remove(id).unwrap(); let availabilities = availabilities.remove(id).unwrap(); @@ -251,4 +253,9 @@ mod tests { fn test_create_process_map_missing_flows() { test_missing!(flows); } + + #[test] + fn test_create_process_map_missing_parameters() { + test_missing!(parameters); + } } diff --git a/src/input/process/parameter.rs b/src/input/process/parameter.rs index 3fdf51d48..d4bd0a2d0 100644 --- a/src/input/process/parameter.rs +++ b/src/input/process/parameter.rs @@ -134,10 +134,6 @@ where "More than one parameter provided for process {id}" ); } - ensure!( - params.len() == process_ids.len(), - "Each process must have an associated parameter" - ); Ok(params) } @@ -398,29 +394,4 @@ mod tests { ) .is_err()); } - - #[test] - fn test_read_process_parameters_from_iter_bad_process_missing_param() { - let year_range = 2000..=2100; - let process_ids = ["A".into(), "B".into()].into_iter().collect(); - - let params_raw = [ProcessParameterRaw { - process_id: "A".into(), - start_year: Some(2010), - end_year: Some(2020), - capital_cost: 1.0, - fixed_operating_cost: 1.0, - variable_operating_cost: 1.0, - lifetime: 10, - discount_rate: Some(1.0), - cap2act: Some(1.0), - }]; - - assert!(read_process_parameters_from_iter( - params_raw.into_iter(), - &process_ids, - &year_range - ) - .is_err()); - } } From d4fca15bc33c7c7772b2201edc9a75917b188cce Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Thu, 16 Jan 2025 11:37:17 +0000 Subject: [PATCH 13/13] Move check for missing availabilities to create_process_map --- src/input/process.rs | 11 +++++++++-- src/input/process/availability.rs | 30 +++++++++++------------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/input/process.rs b/src/input/process.rs index 27b1ad102..199c08d44 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -105,6 +105,9 @@ where descriptions .map(|description| { let id = &description.id; + let availabilities = availabilities + .remove(id) + .with_context(|| format!("No availabilities defined for process {id}"))?; let flows = flows .remove(id) .with_context(|| format!("No commodity flows defined for process {id}"))?; @@ -115,9 +118,8 @@ where .remove(id) .with_context(|| format!("No parameters defined for process {id}"))?; - // We've already checked that these exist for each process + // We've already checked that regions are defined for each process let regions = regions.remove(id).unwrap(); - let availabilities = availabilities.remove(id).unwrap(); let process = Process { id: Rc::clone(id), @@ -244,6 +246,11 @@ mod tests { }; } + #[test] + fn test_create_process_map_missing_availabilities() { + test_missing!(availabilities); + } + #[test] fn test_create_process_map_missing_pacs() { test_missing!(pacs); diff --git a/src/input/process/availability.rs b/src/input/process/availability.rs index 63dd828ae..d678a39df 100644 --- a/src/input/process/availability.rs +++ b/src/input/process/availability.rs @@ -3,7 +3,7 @@ use super::define_process_id_getter; use crate::input::*; use crate::process::{LimitType, ProcessAvailability}; use crate::time_slice::TimeSliceInfo; -use anyhow::{ensure, Context, Result}; +use anyhow::{Context, Result}; use itertools::Itertools; use serde::Deserialize; use std::collections::{HashMap, HashSet}; @@ -44,23 +44,15 @@ fn read_process_availabilities_from_iter( where I: Iterator, { - let availabilities = iter - .map(|record| -> Result<_> { - let time_slice = time_slice_info.get_selection(&record.time_slice)?; - - Ok(ProcessAvailability { - process_id: record.process_id, - limit_type: record.limit_type, - time_slice, - value: record.value, - }) + iter.map(|record| -> Result<_> { + let time_slice = time_slice_info.get_selection(&record.time_slice)?; + + Ok(ProcessAvailability { + process_id: record.process_id, + limit_type: record.limit_type, + time_slice, + value: record.value, }) - .process_results(|iter| iter.into_id_map(process_ids))??; - - ensure!( - availabilities.len() >= process_ids.len(), - "Every process must have at least one availability period" - ); - - Ok(availabilities) + }) + .process_results(|iter| iter.into_id_map(process_ids))? }