diff --git a/src/asset.rs b/src/asset.rs index 1df57ff60..b1a0059dc 100644 --- a/src/asset.rs +++ b/src/asset.rs @@ -440,7 +440,7 @@ impl Asset { // The cost for all commodity flows (including levies/incentives) let flows_cost = self .iter_flows() - .map(|flow| flow.get_total_cost_per_activity(&self.region_id, year, time_slice)) + .map(|flow| flow.get_total_cost_per_activity(&flow.region_id, year, time_slice)) .sum(); self.process_parameter.variable_operating_cost + flows_cost @@ -504,7 +504,7 @@ impl Asset { .map(|flow| { flow.coeff * prices - .get(&flow.commodity.id, &self.region_id, time_slice) + .get(&flow.commodity.id, &flow.region_id, time_slice) .unwrap_or(MoneyPerFlow(0.0)) }) .sum() @@ -534,7 +534,7 @@ impl Asset { let flow_costs = self .iter_flows() .filter(excludes_sed_svd_output) - .map(|flow| flow.get_total_cost_per_activity(&self.region_id, year, time_slice)) + .map(|flow| flow.get_total_cost_per_activity(&flow.region_id, year, time_slice)) .sum(); cost_of_inputs + flow_costs + self.process_parameter.variable_operating_cost @@ -583,7 +583,7 @@ impl Asset { Box::new(output_flows_iter.map(move |flow| { // Get the costs for this specific commodity flow let commodity_specific_costs_per_flow = - flow.get_total_cost_per_flow(&self.region_id, year, time_slice); + flow.get_total_cost_per_flow(&flow.region_id, year, time_slice); // Add these to the generic costs to get total cost for this commodity let marginal_cost = generic_cost_per_flow + commodity_specific_costs_per_flow; @@ -1293,6 +1293,7 @@ mod tests { let commodity_rc = Rc::new(svd_commodity); let process_flow = ProcessFlow { commodity: Rc::clone(&commodity_rc), + region_id: region_id.clone(), coeff: FlowPerActivity(-2.0), // Input kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), diff --git a/src/input/process/flow.rs b/src/input/process/flow.rs index 004ee10c6..741fcc52d 100644 --- a/src/input/process/flow.rs +++ b/src/input/process/flow.rs @@ -184,23 +184,22 @@ where .get(record.commodity_id.as_str()) .with_context(|| format!("{} is not a valid commodity ID", &record.commodity_id))?; - // Create ProcessFlow object - let process_flow = ProcessFlow { - commodity: Rc::clone(commodity), - coeff: record.coeff, - kind: FlowType::Fixed, - cost: record.cost.unwrap_or(MoneyPerFlow(0.0)), - }; - // Insert flow into the map let region_year_map = flows_map.entry(id.clone()).or_default(); for (year, region_id) in iproduct!(record_years, record_regions.iter()) { + let process_flow = ProcessFlow { + commodity: Rc::clone(commodity), + region_id: region_id.clone(), + coeff: record.coeff, + kind: FlowType::Fixed, + cost: record.cost.unwrap_or(MoneyPerFlow(0.0)), + }; let flows_map = region_year_map .entry((region_id.clone(), year)) .or_default(); let existing = Rc::get_mut(flows_map) .unwrap() // safe: there will only be one copy - .insert(commodity.id.clone(), process_flow.clone()) + .insert(commodity.id.clone(), process_flow) .is_some(); ensure!( !existing, @@ -412,6 +411,7 @@ mod tests { fn flow(commodity: Rc, coeff: f64) -> ProcessFlow { ProcessFlow { commodity, + region_id: "GBR".into(), coeff: FlowPerActivity(coeff), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), diff --git a/src/process.rs b/src/process.rs index f54f3c7f3..e2378b277 100644 --- a/src/process.rs +++ b/src/process.rs @@ -391,6 +391,8 @@ impl ActivityLimits { pub struct ProcessFlow { /// The commodity produced or consumed by this flow pub commodity: Rc, + /// The region in which this flow occurs + pub region_id: RegionID, /// Maximum annual commodity flow quantity relative to other commodity flows. /// /// Positive value indicates flow out and negative value indicates flow in. @@ -674,7 +676,7 @@ mod tests { } #[fixture] - fn flow_with_cost() -> ProcessFlow { + fn flow_with_cost(region_id: RegionID) -> ProcessFlow { ProcessFlow { commodity: Rc::new(Commodity { id: "test_commodity".into(), @@ -687,6 +689,7 @@ mod tests { demand: DemandMap::new(), units: "PJ".into(), }), + region_id, coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(5.0), @@ -696,7 +699,7 @@ mod tests { #[fixture] fn flow_with_cost_and_levy(region_id: RegionID, time_slice: TimeSliceID) -> ProcessFlow { let mut levies = CommodityLevyMap::new(); - levies.insert((region_id, 2020, time_slice), MoneyPerFlow(10.0)); + levies.insert((region_id.clone(), 2020, time_slice), MoneyPerFlow(10.0)); ProcessFlow { commodity: Rc::new(Commodity { @@ -710,6 +713,7 @@ mod tests { demand: DemandMap::new(), units: "PJ".into(), }), + region_id, coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(5.0), @@ -719,7 +723,7 @@ mod tests { #[fixture] fn flow_with_cost_and_incentive(region_id: RegionID, time_slice: TimeSliceID) -> ProcessFlow { let mut levies = CommodityLevyMap::new(); - levies.insert((region_id, 2020, time_slice), MoneyPerFlow(-3.0)); + levies.insert((region_id.clone(), 2020, time_slice), MoneyPerFlow(-3.0)); ProcessFlow { commodity: Rc::new(Commodity { @@ -733,6 +737,7 @@ mod tests { demand: DemandMap::new(), units: "PJ".into(), }), + region_id, coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(5.0), @@ -747,6 +752,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_no_levies, + region_id: region_id.clone(), coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -766,6 +772,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_with_levy, + region_id: region_id.clone(), coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -785,6 +792,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_with_incentive, + region_id: region_id.clone(), coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -800,6 +808,7 @@ mod tests { fn get_levy_different_region(commodity_with_levy: Rc, time_slice: TimeSliceID) { let flow = ProcessFlow { commodity: commodity_with_levy, + region_id: "GBR".into(), coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -819,6 +828,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_with_levy, + region_id: region_id.clone(), coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -834,6 +844,7 @@ mod tests { fn get_levy_different_time_slice(commodity_with_levy: Rc, region_id: RegionID) { let flow = ProcessFlow { commodity: commodity_with_levy, + region_id: region_id.clone(), coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -858,6 +869,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_with_consumption_levy, + region_id: region_id.clone(), coeff: FlowPerActivity(1.0), // Positive coefficient means production kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -877,6 +889,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_with_consumption_levy, + region_id: region_id.clone(), coeff: FlowPerActivity(-1.0), // Negative coefficient means consumption kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -896,6 +909,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_with_production_levy, + region_id: region_id.clone(), coeff: FlowPerActivity(1.0), // Positive coefficient means production kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -915,6 +929,7 @@ mod tests { ) { let flow = ProcessFlow { commodity: commodity_with_production_levy, + region_id: region_id.clone(), coeff: FlowPerActivity(-1.0), // Negative coefficient means consumption kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -1004,18 +1019,21 @@ mod tests { let flow_in = ProcessFlow { commodity: Rc::clone(&commodity), + region_id: "GBR".into(), coeff: FlowPerActivity(-1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), }; let flow_out = ProcessFlow { commodity: Rc::clone(&commodity), + region_id: "GBR".into(), coeff: FlowPerActivity(1.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), }; let flow_zero = ProcessFlow { commodity: Rc::clone(&commodity), + region_id: "GBR".into(), coeff: FlowPerActivity(0.0), kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), diff --git a/src/simulation/investment.rs b/src/simulation/investment.rs index 1d1c02639..c56e67926 100644 --- a/src/simulation/investment.rs +++ b/src/simulation/investment.rs @@ -943,6 +943,7 @@ mod tests { let commodity_rc = Rc::new(svd_commodity); let process_flow = ProcessFlow { commodity: Rc::clone(&commodity_rc), + region_id: "GBR".into(), coeff: FlowPerActivity(2.0), // 2 units of flow per unit of activity kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), @@ -979,6 +980,7 @@ mod tests { let commodity_rc = Rc::new(svd_commodity); let process_flow = ProcessFlow { commodity: Rc::clone(&commodity_rc), + region_id: "GBR".into(), coeff: FlowPerActivity(1.0), // 1 unit of flow per unit of activity kind: FlowType::Fixed, cost: MoneyPerFlow(0.0), diff --git a/src/simulation/optimisation.rs b/src/simulation/optimisation.rs index d03fbe5f5..babbe996e 100644 --- a/src/simulation/optimisation.rs +++ b/src/simulation/optimisation.rs @@ -196,7 +196,7 @@ fn create_flow_map<'a>( let flow_key = ( asset.clone(), flow.commodity.id.clone(), - asset.region_id().clone(), + flow.region_id.clone(), time_slice.clone(), ); let flow_value = activity * flow.coeff / n_units; @@ -207,19 +207,19 @@ fn create_flow_map<'a>( // Copy flows for each child asset for asset in existing_assets { if let Some(parent) = asset.parent() { - for commodity_id in asset.iter_flows().map(|flow| &flow.commodity.id) { + for asset_flow in asset.iter_flows() { for time_slice in time_slice_info.iter_ids() { let flow = flows[&( parent.clone(), - commodity_id.clone(), - parent.region_id().clone(), + asset_flow.commodity.id.clone(), + asset_flow.region_id.clone(), time_slice.clone(), )]; flows.insert( ( asset.clone(), - commodity_id.clone(), - asset.region_id().clone(), + asset_flow.commodity.id.clone(), + asset_flow.region_id.clone(), time_slice.clone(), ), flow, diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index 13d74ef8f..5192d5b00 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -1166,9 +1166,15 @@ mod tests { use std::collections::{HashMap, HashSet}; use std::rc::Rc; - fn build_process_flow(commodity: &Commodity, coeff: f64, cost: MoneyPerFlow) -> ProcessFlow { + fn build_process_flow( + commodity: &Commodity, + coeff: f64, + cost: MoneyPerFlow, + region_id: &RegionID, + ) -> ProcessFlow { ProcessFlow { commodity: Rc::new(commodity.clone()), + region_id: region_id.clone(), coeff: FlowPerActivity(coeff), kind: FlowType::Fixed, cost, @@ -1307,11 +1313,20 @@ mod tests { let mut flows = IndexMap::new(); flows.insert( a.id.clone(), - build_process_flow(&a, -1.0, MoneyPerFlow(0.0)), + build_process_flow(&a, -1.0, MoneyPerFlow(0.0), ®ion_id), + ); + flows.insert( + b.id.clone(), + build_process_flow(&b, 1.0, MoneyPerFlow(0.0), ®ion_id), + ); + flows.insert( + c.id.clone(), + build_process_flow(&c, 2.0, MoneyPerFlow(3.0), ®ion_id), + ); + flows.insert( + d.id.clone(), + build_process_flow(&d, 1.0, MoneyPerFlow(4.0), ®ion_id), ); - flows.insert(b.id.clone(), build_process_flow(&b, 1.0, MoneyPerFlow(0.0))); - flows.insert(c.id.clone(), build_process_flow(&c, 2.0, MoneyPerFlow(3.0))); - flows.insert(d.id.clone(), build_process_flow(&d, 1.0, MoneyPerFlow(4.0))); let process = build_process( flows, @@ -1390,11 +1405,20 @@ mod tests { let mut flows = IndexMap::new(); flows.insert( a.id.clone(), - build_process_flow(&a, -1.0, MoneyPerFlow(0.0)), + build_process_flow(&a, -1.0, MoneyPerFlow(0.0), ®ion_id), + ); + flows.insert( + b.id.clone(), + build_process_flow(&b, 1.0, MoneyPerFlow(0.0), ®ion_id), + ); + flows.insert( + c.id.clone(), + build_process_flow(&c, 2.0, MoneyPerFlow(3.0), ®ion_id), + ); + flows.insert( + d.id.clone(), + build_process_flow(&d, 1.0, MoneyPerFlow(4.0), ®ion_id), ); - flows.insert(b.id.clone(), build_process_flow(&b, 1.0, MoneyPerFlow(0.0))); - flows.insert(c.id.clone(), build_process_flow(&c, 2.0, MoneyPerFlow(3.0))); - flows.insert(d.id.clone(), build_process_flow(&d, 1.0, MoneyPerFlow(4.0))); let process = build_process( flows,