From 8067328120355ed045ac6e60af6972b4b2b75eb0 Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Tue, 17 Mar 2026 10:57:24 +0000 Subject: [PATCH 01/12] Add new strategies --- src/simulation/prices.rs | 297 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index be6c2e340..613b7690f 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -625,6 +625,142 @@ where expand_selection_prices(&all_group_prices, time_slice_info) } +/// Calculate marginal cost prices for a set of commodities using a load-weighted average across +/// assets. +/// +/// Similar to `calculate_marginal_cost_prices`, but takes a weighted average across assets +/// according to output rather than taking the max. +/// +/// Candidate assets are treated the same way (i.e. take the min across candidate assets). +/// +/// TODO: existing assets should weight by OUTPUT rather than activity +fn calculate_marginal_cost_average_prices<'a, I, J>( + activity_for_existing: I, + activity_keys_for_candidates: J, + upstream_prices: &CommodityPrices, + year: u32, + markets_to_price: &HashSet<(CommodityID, RegionID)>, + commodities: &CommodityMap, + time_slice_info: &TimeSliceInfo, +) -> IndexMap<(CommodityID, RegionID, TimeSliceID), MoneyPerFlow> +where + I: Iterator, + J: Iterator, +{ + // Accumulator map to collect marginal costs from existing assets. Collects a weighted average + // for each (commodity, region, ts selection), across all contributing assets, weighted + // according to output. The granularity of the selection depends on the time slice level of the + // commodity (i.e. individual, season, year). + let mut existing_accum: IndexMap< + (CommodityID, RegionID, TimeSliceSelection), + WeightedAverageAccumulator, + > = IndexMap::new(); + + // Iterate over existing assets and their activities + for (asset, time_slice, activity) in activity_for_existing { + let region_id = asset.region_id(); + + // Iterate over the marginal costs for commodities we need prices for + for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( + upstream_prices, + year, + time_slice, + |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), + ) { + // Get the time slice selection according to the commodity's time slice level + let time_slice_selection = commodities[&commodity_id] + .time_slice_level + .containing_selection(time_slice); + + // Accumulate marginal cost for this group, weighted by output + existing_accum + .entry(( + commodity_id.clone(), + region_id.clone(), + time_slice_selection, + )) + .or_default() + .add(marginal_cost, Dimensionless(activity.value())); + } + } + + // For each group, finalise weighted averages + let group_prices: IndexMap<_, MoneyPerFlow> = existing_accum + .into_iter() + .filter_map(|(key, accum)| accum.finalise().map(|v| (key, v))) + .collect(); + + // Accumulator map to collect marginal costs from candidate assets. For each (commodity, region, + // ts selection), this maps each candidate to a weighted average of the marginal costs for that + // commodity across all time slices in the selection, weighted by activity limits. + let mut cand_accum: IndexMap< + (CommodityID, RegionID, TimeSliceSelection), + IndexMap, + > = IndexMap::new(); + + // Iterate over candidate assets (assuming full utilisation) + for (asset, time_slice) in activity_keys_for_candidates { + let region_id = asset.region_id(); + + // Get activity limits: used to weight marginal costs for seasonal/annual commodities + let output_limit = *asset + .get_activity_limits_for_selection(&TimeSliceSelection::Single(time_slice.clone())) + .end(); + + // Iterate over the marginal costs for commodities we need prices for + for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( + upstream_prices, + year, + time_slice, + |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), + ) { + // Get the time slice selection according to the commodity's time slice level + let time_slice_selection = commodities[&commodity_id] + .time_slice_level + .containing_selection(time_slice); + + // Skip groups already covered by existing assets + if group_prices.contains_key(&( + commodity_id.clone(), + region_id.clone(), + time_slice_selection.clone(), + )) { + continue; + } + + // Accumulate marginal cost for this candidate, weighted by the activity limit + cand_accum + .entry(( + commodity_id.clone(), + region_id.clone(), + time_slice_selection, + )) + .or_default() + .entry(asset.clone()) + .or_default() + .add(marginal_cost, Dimensionless(output_limit.value())); + } + } + + // For each group, finalise per-candidate weighted averages then reduce to the min across candidates + let cand_group_prices: IndexMap<_, MoneyPerFlow> = cand_accum + .into_iter() + .filter_map(|(key, per_candidate)| { + per_candidate + .into_values() + .filter_map(|inner| inner.finalise()) + .reduce(|current, value| current.min(value)) + .map(|v| (key, v)) + }) + .collect(); + + // Merge existing and candidate group prices, then expand to individual time slices + let mut all_group_prices = group_prices; + all_group_prices.extend(cand_group_prices); + + expand_selection_prices(&all_group_prices, time_slice_info) +} + /// Calculate annual activities for each asset by summing across all time slices fn calculate_annual_activities<'a, I>(activities: I) -> HashMap where @@ -868,6 +1004,167 @@ where expand_selection_prices(&all_group_prices, time_slice_info) } +/// Calculate full cost prices for a set of commodities using a load-weighted average across +/// assets. +/// +/// Similar to `calculate_full_cost_prices`, but takes a weighted average across assets +/// according to output rather than taking the max. +/// +/// Candidate assets are treated the same way (i.e. take the min across candidate assets). +/// +/// TODO: existing assets should weight by OUTPUT rather than activity +#[allow(clippy::too_many_arguments)] +fn calculate_full_cost_average_prices<'a, I, J>( + activity_for_existing: I, + activity_keys_for_candidates: J, + annual_activities: &HashMap, + upstream_prices: &CommodityPrices, + year: u32, + markets_to_price: &HashSet<(CommodityID, RegionID)>, + commodities: &CommodityMap, + time_slice_info: &TimeSliceInfo, +) -> IndexMap<(CommodityID, RegionID, TimeSliceID), MoneyPerFlow> +where + I: Iterator, + J: Iterator, +{ + // Accumulator map to collect full costs from existing assets. Collects a weighted average + // for each (commodity, region, ts selection), across all contributing assets, weighted + // according to output. The granularity of the selection depends on the time slice level of the + // commodity (i.e. individual, season, year). + let mut existing_accum: IndexMap< + (CommodityID, RegionID, TimeSliceSelection), + WeightedAverageAccumulator, + > = IndexMap::new(); + + // Cache of annual fixed costs per flow for each asset, to avoid recalculating + let mut annual_fixed_costs: HashMap<_, _> = HashMap::new(); + + // Iterate over existing assets and their activities + for (asset, time_slice, activity) in activity_for_existing { + let annual_activity = annual_activities[asset]; + let region_id = asset.region_id(); + + // If annual activity is zero, we can't calculate a capital cost per flow, so skip this + // asset. + if annual_activity < Activity::EPSILON { + continue; + } + + // Iterate over the marginal costs for commodities we need prices for + for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( + upstream_prices, + year, + time_slice, + |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), + ) { + // Get the time slice selection according to the commodity's time slice level + let ts_selection = commodities[&commodity_id] + .time_slice_level + .containing_selection(time_slice); + + // Get/calculate fixed costs per flow for this asset + let annual_fixed_costs_per_flow = annual_fixed_costs + .entry(asset.clone()) + .or_insert_with(|| asset.get_annual_fixed_costs_per_flow(annual_activity)); + + // Accumulate full costs (marginal cost and fixed cost per flow), weighted by activity + existing_accum + .entry((commodity_id.clone(), region_id.clone(), ts_selection)) + .or_default() + .add( + marginal_cost + *annual_fixed_costs_per_flow, + Dimensionless(activity.value()), + ); + } + } + + // For each group, finalise weighted averages + let group_prices: IndexMap<_, MoneyPerFlow> = existing_accum + .into_iter() + .filter_map(|(key, accum)| accum.finalise().map(|v| (key, v))) + .collect(); + + // Accumulator map to collect marginal costs from candidate assets. For each (commodity, region, + // ts selection), this maps each candidate to a weighted average of the full costs for that + // commodity across all time slices in the selection, weighted by activity limits. + let mut cand_accum: IndexMap< + (CommodityID, RegionID, TimeSliceSelection), + IndexMap, + > = IndexMap::new(); + + // Iterate over candidate assets (assuming full utilization) + for (asset, time_slice) in activity_keys_for_candidates { + let region_id = asset.region_id(); + + // Get activity limits: used to weight marginal costs for seasonal/annual commodities + let activity_limit = *asset + .get_activity_limits_for_selection(&TimeSliceSelection::Single(time_slice.clone())) + .end(); + + // Iterate over the marginal costs for commodities we need prices for + for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( + upstream_prices, + year, + time_slice, + |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), + ) { + // Get the time slice selection according to the commodity's time slice level + let ts_selection = commodities[&commodity_id] + .time_slice_level + .containing_selection(time_slice); + + // Skip groups already covered by existing assets + if group_prices.contains_key(&( + commodity_id.clone(), + region_id.clone(), + ts_selection.clone(), + )) { + continue; + } + + // Get/calculate fixed costs per flow for this asset (assume full utilisation) + let annual_fixed_costs_per_flow = + annual_fixed_costs.entry(asset.clone()).or_insert_with(|| { + asset.get_annual_fixed_costs_per_flow( + *asset + .get_activity_limits_for_selection(&TimeSliceSelection::Annual) + .end(), + ) + }); + + // Accumulate full costs for this group, weighted by the activity limit + cand_accum + .entry((commodity_id.clone(), region_id.clone(), ts_selection)) + .or_default() + .entry(asset.clone()) + .or_default() + .add( + marginal_cost + *annual_fixed_costs_per_flow, + Dimensionless(activity_limit.value()), + ); + } + } + + // For each group, finalise per-candidate weighted averages then reduce to the min across candidates + let cand_group_prices: IndexMap<_, MoneyPerFlow> = cand_accum + .into_iter() + .filter_map(|(key, per_candidate)| { + per_candidate + .into_values() + .filter_map(|inner| inner.finalise()) + .reduce(|current, value| current.min(value)) + .map(|v| (key, v)) + }) + .collect(); + + // Merge existing and candidate group prices, then expand to individual time slices + let mut all_group_prices = group_prices; + all_group_prices.extend(cand_group_prices); + + expand_selection_prices(&all_group_prices, time_slice_info) +} + #[cfg(test)] mod tests { use super::*; From 0d5497b75e1183f94ec464f6062e16864582a902 Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Tue, 17 Mar 2026 13:12:14 +0000 Subject: [PATCH 02/12] Integrate new strategies --- schemas/input/commodities.yaml | 25 +- src/commodity.rs | 6 + src/example/patches.rs | 30 + src/graph/investment.rs | 11 +- src/simulation/prices.rs | 36 +- tests/data/simple_full_average/assets.csv | 10 + .../simple_full_average/commodity_flows.csv | 689 +++++++++++++++++ .../simple_full_average/commodity_prices.csv | 177 +++++ tests/data/simple_marginal_average/assets.csv | 10 + .../commodity_flows.csv | 721 ++++++++++++++++++ .../commodity_prices.csv | 177 +++++ tests/regression.rs | 2 + 12 files changed, 1882 insertions(+), 12 deletions(-) create mode 100644 tests/data/simple_full_average/assets.csv create mode 100644 tests/data/simple_full_average/commodity_flows.csv create mode 100644 tests/data/simple_full_average/commodity_prices.csv create mode 100644 tests/data/simple_marginal_average/assets.csv create mode 100644 tests/data/simple_marginal_average/commodity_flows.csv create mode 100644 tests/data/simple_marginal_average/commodity_prices.csv diff --git a/schemas/input/commodities.yaml b/schemas/input/commodities.yaml index d9e40244b..76b31c5a7 100644 --- a/schemas/input/commodities.yaml +++ b/schemas/input/commodities.yaml @@ -29,16 +29,29 @@ fields: of day) - name: pricing_strategy type: string - enum: [shadow, marginal, full, scarcity, unpriced] + enum: + [ + shadow, + marginal, + marginal_average, + full, + full_average, + scarcity, + unpriced, + ] description: The pricing strategy for this commodity notes: | Optional. If specified, must be one of `shadow` (priced using shadow prices from supply-demand - constraints), `marginal` (priced at the marginal cost of production) , `full` (priced at the - full cost of production including capital costs), `scarcity` (priced adjusted for scarcity), - or `unpriced` (not priced at all). + constraints), `marginal` (priced at the marginal cost of the highest cost active asset), + `marginal_average` (priced at the load-weighted average marginal cost of production across + assets), `full` (priced at the full cost of production of the highest cost active asset, + including capital costs), `full_average` (priced at the load-weighted average full cost of + production across assets), `scarcity` (priced adjusted for scarcity), or `unpriced` (not + priced at all). - For `svd` and `sed` commodities, this must be one of `shadow`, `marginal` or `full`. For `oth` - commodities, this must be `unpriced`. + For `svd` and `sed` commodities, this must be one of `shadow`, `marginal`, + `marginal_average`, `full` or `full_average`. For `oth` commodities, this must be + `unpriced`. If unspecified, the commodity will use the default pricing strategy according to the commodity type: `shadow` for `svd` and `sed` commodities, and `unpriced` for `oth` commodities. diff --git a/src/commodity.rs b/src/commodity.rs index c949e0226..0898fba7d 100644 --- a/src/commodity.rs +++ b/src/commodity.rs @@ -102,9 +102,15 @@ pub enum PricingStrategy { /// Use marginal cost of highest-cost active asset producing the commodity #[serde(rename = "marginal")] MarginalCost, + /// Use load-weighted average marginal cost across active assets producing the commodity + #[serde(rename = "marginal_average")] + MarginalCostAverage, /// Use full cost of highest-cost active asset producing the commodity #[serde(rename = "full")] FullCost, + /// Use load-weighted average full cost across active assets producing the commodity + #[serde(rename = "full_average")] + FullCostAverage, /// Commodities that should not have prices calculated #[serde(rename = "unpriced")] Unpriced, diff --git a/src/example/patches.rs b/src/example/patches.rs index 0ede64fba..c25ab3906 100644 --- a/src/example/patches.rs +++ b/src/example/patches.rs @@ -68,6 +68,36 @@ fn get_all_patches() -> PatchMap { None, ), ), + ( + // The simple example with electricity priced using average marginal costs + "simple_marginal_average", + ( + vec![FilePatch::new("commodities.csv").with_replacement(&[ + "id,description,type,time_slice_level,pricing_strategy,units", + "GASPRD,Gas produced,sed,season,shadow,PJ", + "GASNAT,Natural gas,sed,season,shadow,PJ", + "ELCTRI,Electricity,sed,daynight,marginal_average,PJ", + "RSHEAT,Residential heating,svd,daynight,shadow,PJ", + "CO2EMT,CO2 emitted,oth,annual,unpriced,ktCO2", + ])], + None, + ), + ), + ( + // The simple example with gas commodities priced using average full costs + "simple_full_average", + ( + vec![FilePatch::new("commodities.csv").with_replacement(&[ + "id,description,type,time_slice_level,pricing_strategy,units", + "GASPRD,Gas produced,sed,season,full_average,PJ", + "GASNAT,Natural gas,sed,season,full_average,PJ", + "ELCTRI,Electricity,sed,daynight,shadow,PJ", + "RSHEAT,Residential heating,svd,daynight,shadow,PJ", + "CO2EMT,CO2 emitted,oth,annual,unpriced,ktCO2", + ])], + None, + ), + ), // The simple example with the ironing-out loop turned on ( "simple_ironing_out", diff --git a/src/graph/investment.rs b/src/graph/investment.rs index 4af0d9797..10a6a600b 100644 --- a/src/graph/investment.rs +++ b/src/graph/investment.rs @@ -125,7 +125,7 @@ fn compress_cycles(graph: &InvestmentGraph, commodities: &CommodityMap) -> Resul // Order nodes within each strongly connected component order_sccs(&mut condensed_graph, graph); - // Pre-scan SCCs for offending pricing strategies (FullCost / MarginalCost). + // Pre-scan SCCs for offending pricing strategies. for node_weight in condensed_graph.node_weights() { if node_weight.len() <= 1 { continue; @@ -136,7 +136,10 @@ fn compress_cycles(graph: &InvestmentGraph, commodities: &CommodityMap) -> Resul .filter(|(cid, _)| { matches!( &commodities[cid].pricing_strategy, - PricingStrategy::MarginalCost | PricingStrategy::FullCost + PricingStrategy::MarginalCost + | PricingStrategy::MarginalCostAverage + | PricingStrategy::FullCost + | PricingStrategy::FullCostAverage ) }) .map(|(cid, _)| cid.clone()) @@ -144,8 +147,8 @@ fn compress_cycles(graph: &InvestmentGraph, commodities: &CommodityMap) -> Resul ensure!( offenders.is_empty(), - "Cannot use FullCost/MarginalCost pricing strategies for commodities with circular \ - dependencies. Offending commodities: {offenders:?}" + "Cannot use marginal, marginal_average, full or full_average pricing strategies for \ + commodities with circular dependencies. Offending commodities: {offenders:?}" ); } diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index 613b7690f..0c628a4e4 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -119,8 +119,8 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result let investment_order = &model.investment_order[&year]; // Iterate over investment sets in reverse order. Markets within the same set can be priced - // simultaneously, since they are independent (apart from Cycle sets when using the "marginal" - // and "full" strategies, which get flagged at the validation stage). + // simultaneously, since they are independent (apart from Cycle sets when using full/marginal + // cost pricing strategies, which get flagged at the validation stage). for investment_set in investment_order.iter().rev() { // Partition markets by pricing strategy into a map keyed by `PricingStrategy`. // For now, commodities use a single strategy for all regions, but this may change in the future. @@ -172,6 +172,20 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result result.extend(marginal_cost_prices); } + // Add prices for marginal average commodities + if let Some(marginal_avg_set) = pricing_sets.get(&PricingStrategy::MarginalCostAverage) { + let marginal_avg_prices = calculate_marginal_cost_average_prices( + solution.iter_activity_for_existing(), + solution.iter_activity_keys_for_candidates(), + &result, + year, + marginal_avg_set, + &model.commodities, + &model.time_slice_info, + ); + result.extend(marginal_avg_prices); + } + // Add prices for full cost commodities if let Some(fullcost_set) = pricing_sets.get(&PricingStrategy::FullCost) { let annual_activities = annual_activities.get_or_insert_with(|| { @@ -189,6 +203,24 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result ); result.extend(full_cost_prices); } + + // Add prices for full average commodities + if let Some(full_avg_set) = pricing_sets.get(&PricingStrategy::FullCostAverage) { + let annual_activities = annual_activities.get_or_insert_with(|| { + calculate_annual_activities(solution.iter_activity_for_existing()) + }); + let full_avg_prices = calculate_full_cost_average_prices( + solution.iter_activity_for_existing(), + solution.iter_activity_keys_for_candidates(), + annual_activities, + &result, + year, + full_avg_set, + &model.commodities, + &model.time_slice_info, + ); + result.extend(full_avg_prices); + } } // Return the completed prices map diff --git a/tests/data/simple_full_average/assets.csv b/tests/data/simple_full_average/assets.csv new file mode 100644 index 000000000..8aaac8226 --- /dev/null +++ b/tests/data/simple_full_average/assets.csv @@ -0,0 +1,10 @@ +asset_id,process_id,region_id,agent_id,group_id,commission_year,decommission_year,capacity +0,GASDRV,GBR,A0_GEX,,2020,,4002.26 +1,GASPRC,GBR,A0_GPR,,2020,,3782.13 +2,WNDFRM,GBR,A0_ELC,,2020,2040,3.964844 +3,GASCGT,GBR,A0_ELC,,2020,2040,2.43 +4,RGASBR,GBR,A0_RES,,2020,2035,2900.0 +5,RELCHP,GBR,A0_RES,,2020,2035,399.98 +6,RGASBR,GBR,A0_RES,,2030,,355.83840587648046 +7,WNDFRM,GBR,A0_ELC,,2030,2040,5.353359296518287 +8,RGASBR,GBR,A0_RES,,2040,,3655.8189696 diff --git a/tests/data/simple_full_average/commodity_flows.csv b/tests/data/simple_full_average/commodity_flows.csv new file mode 100644 index 000000000..8b2c74af2 --- /dev/null +++ b/tests/data/simple_full_average/commodity_flows.csv @@ -0,0 +1,689 @@ +milestone_year,asset_id,commodity_id,time_slice,flow +2020,0,GASPRD,winter.night,0.0 +2020,0,CO2EMT,winter.night,0.0 +2020,0,GASPRD,winter.day,151.10360181069285 +2020,0,CO2EMT,winter.day,772.5927160580726 +2020,0,GASPRD,winter.peak,125.070625 +2020,0,CO2EMT,winter.peak,639.486105625 +2020,0,GASPRD,winter.evening,166.76083466742 +2020,0,CO2EMT,winter.evening,852.6481476545185 +2020,0,GASPRD,peak.night,0.0 +2020,0,CO2EMT,peak.night,0.0 +2020,0,GASPRD,peak.day,0.0 +2020,0,CO2EMT,peak.day,0.0 +2020,0,GASPRD,peak.peak,58.67582562255714 +2020,0,CO2EMT,peak.peak,300.0094964081347 +2020,0,GASPRD,peak.evening,166.76083466742 +2020,0,CO2EMT,peak.evening,852.6481476545185 +2020,0,GASPRD,summer.night,0.0 +2020,0,CO2EMT,summer.night,0.0 +2020,0,GASPRD,summer.day,0.0 +2020,0,CO2EMT,summer.day,0.0 +2020,0,GASPRD,summer.peak,0.0 +2020,0,CO2EMT,summer.peak,0.0 +2020,0,GASPRD,summer.evening,0.16861964795988577 +2020,0,CO2EMT,summer.evening,0.862152260018896 +2020,0,GASPRD,autumn.night,0.0 +2020,0,CO2EMT,autumn.night,0.0 +2020,0,GASPRD,autumn.day,0.0 +2020,0,CO2EMT,autumn.day,0.0 +2020,0,GASPRD,autumn.peak,0.0 +2020,0,CO2EMT,autumn.peak,0.0 +2020,0,GASPRD,autumn.evening,163.3883025525992 +2020,0,CO2EMT,autumn.evening,835.4043909514398 +2020,1,GASPRD,winter.night,-0.0 +2020,1,GASNAT,winter.night,0.0 +2020,1,CO2EMT,winter.night,0.0 +2020,1,GASPRD,winter.day,-153.36573202936736 +2020,1,GASNAT,winter.day,146.06260193273081 +2020,1,CO2EMT,winter.day,373.40904184102635 +2020,1,GASPRD,winter.peak,-124.10114062500001 +2020,1,GASNAT,winter.peak,118.1915625 +2020,1,CO2EMT,winter.peak,302.15672953125005 +2020,1,GASPRD,winter.evening,-165.4681888237455 +2020,1,GASNAT,winter.evening,157.58875126070998 +2020,1,CO2EMT,winter.evening,402.87564259800513 +2020,1,GASPRD,peak.night,-0.0 +2020,1,GASNAT,peak.night,0.0 +2020,1,CO2EMT,peak.night,0.0 +2020,1,GASPRD,peak.day,-0.0 +2020,1,GASNAT,peak.day,0.0 +2020,1,CO2EMT,peak.day,0.0 +2020,1,GASPRD,peak.peak,-59.96847146623166 +2020,1,GASNAT,peak.peak,57.11282996783967 +2020,1,CO2EMT,peak.peak,146.00894981278213 +2020,1,GASPRD,peak.evening,-165.4681888237455 +2020,1,GASNAT,peak.evening,157.58875126070998 +2020,1,CO2EMT,peak.evening,402.87564259800513 +2020,1,GASPRD,summer.night,-0.0 +2020,1,GASNAT,summer.night,0.0 +2020,1,CO2EMT,summer.night,0.0 +2020,1,GASPRD,summer.day,-0.0 +2020,1,GASNAT,summer.day,0.0 +2020,1,CO2EMT,summer.day,0.0 +2020,1,GASPRD,summer.peak,-0.0 +2020,1,GASNAT,summer.peak,0.0 +2020,1,CO2EMT,summer.peak,0.0 +2020,1,GASPRD,summer.evening,-0.16861964795988577 +2020,1,GASNAT,summer.evening,0.16059014091417692 +2020,1,CO2EMT,summer.evening,0.4105486952470933 +2020,1,GASPRD,autumn.night,-0.0 +2020,1,GASNAT,autumn.night,0.0 +2020,1,CO2EMT,autumn.night,0.0 +2020,1,GASPRD,autumn.day,-0.0 +2020,1,GASNAT,autumn.day,0.0 +2020,1,CO2EMT,autumn.day,0.0 +2020,1,GASPRD,autumn.peak,-0.0 +2020,1,GASNAT,autumn.peak,0.0 +2020,1,CO2EMT,autumn.peak,0.0 +2020,1,GASPRD,autumn.evening,-163.3883025525992 +2020,1,GASNAT,autumn.evening,155.60790719295161 +2020,1,CO2EMT,autumn.evening,397.81161473878086 +2020,2,ELCTRI,winter.night,4.435312795545212 +2020,2,ELCTRI,winter.day,7.075379933645912 +2020,2,ELCTRI,winter.peak,1.9712501261051125 +2020,2,ELCTRI,winter.evening,2.5696653598405335 +2020,2,ELCTRI,peak.night,2.851272517283696 +2020,2,ELCTRI,peak.day,6.3713620320039785 +2020,2,ELCTRI,peak.peak,1.7776452018191917 +2020,2,ELCTRI,peak.evening,1.72484387381507 +2020,2,ELCTRI,summer.night,1.6368416242136155 +2020,2,ELCTRI,summer.day,2.9055524196533997 +2020,2,ELCTRI,summer.peak,0.9567924409494001 +2020,2,ELCTRI,summer.evening,0.7124084843502 +2020,2,ELCTRI,autumn.night,3.203281465982185 +2020,2,ELCTRI,autumn.day,6.001752635595889 +2020,2,ELCTRI,autumn.peak,1.5488393825638174 +2020,2,ELCTRI,autumn.evening,1.9008483513729915 +2020,3,GASNAT,winter.night,-7.783808997678886 +2020,3,ELCTRI,winter.night,5.18920599845259 +2020,3,CO2EMT,winter.night,397.98615405132136 +2020,3,GASNAT,winter.day,-10.010898915527836 +2020,3,ELCTRI,winter.day,6.673932610351891 +2020,3,CO2EMT,winter.day,511.8572615509382 +2020,3,GASNAT,winter.peak,-3.2303154358423316 +2020,3,ELCTRI,winter.peak,2.1535436238948877 +2020,3,CO2EMT,winter.peak,165.1660282346184 +2020,3,GASNAT,winter.evening,-4.3950895262359 +2020,3,ELCTRI,winter.evening,2.930059684157267 +2020,3,CO2EMT,winter.evening,224.72092747644155 +2020,3,GASNAT,peak.night,-8.382740663321101 +2020,3,ELCTRI,peak.night,5.5884937755474 +2020,3,CO2EMT,peak.night,428.6095301156078 +2020,3,GASNAT,peak.day,-11.066925767990735 +2020,3,ELCTRI,peak.day,7.377950511993824 +2020,3,CO2EMT,peak.day,565.8519145173663 +2020,3,GASNAT,peak.peak,-3.520722822271213 +2020,3,ELCTRI,peak.peak,2.3471485481808085 +2020,3,CO2EMT,peak.peak,180.0145579027271 +2020,3,GASNAT,peak.evening,-4.790137538321099 +2020,3,ELCTRI,peak.evening,3.1934250255473997 +2020,3,CO2EMT,peak.evening,244.9197323343578 +2020,3,GASNAT,summer.night,-0.16059014091417692 +2020,3,ELCTRI,summer.night,0.10706009394278461 +2020,3,CO2EMT,summer.night,8.210973904941865 +2020,3,GASNAT,summer.day,-0.0 +2020,3,ELCTRI,summer.day,0.0 +2020,3,CO2EMT,summer.day,0.0 +2020,3,GASNAT,summer.peak,-0.0 +2020,3,ELCTRI,summer.peak,0.0 +2020,3,CO2EMT,summer.peak,0.0 +2020,3,GASNAT,summer.evening,-0.0 +2020,3,ELCTRI,summer.evening,0.0 +2020,3,CO2EMT,summer.evening,0.0 +2020,3,GASNAT,autumn.night,-8.382740663321101 +2020,3,ELCTRI,autumn.night,5.5884937755474 +2020,3,CO2EMT,autumn.night,428.6095301156078 +2020,3,GASNAT,autumn.day,-11.62133986260287 +2020,3,ELCTRI,autumn.day,7.7475599084019136 +2020,3,CO2EMT,autumn.day,594.1991071748847 +2020,3,GASNAT,autumn.peak,-3.592603125 +2020,3,ELCTRI,autumn.peak,2.39506875 +2020,3,CO2EMT,autumn.peak,183.68979778124998 +2020,3,GASNAT,autumn.evening,-4.790137538321099 +2020,3,ELCTRI,autumn.evening,3.1934250255473997 +2020,3,CO2EMT,autumn.evening,244.9197323343578 +2020,4,GASNAT,winter.night,-36.31286857370999 +2020,4,RSHEAT,winter.night,31.576407455399995 +2020,4,CO2EMT,winter.night,1856.6769701737921 +2020,4,GASNAT,winter.day,-193.26973453857997 +2020,4,RSHEAT,winter.day,168.06063872919998 +2020,4,CO2EMT,winter.day,9881.881526957595 +2020,4,GASNAT,winter.peak,-104.21872974118598 +2020,4,RSHEAT,winter.peak,90.62498238363999 +2020,4,CO2EMT,winter.peak,5328.7036516668395 +2020,4,GASNAT,winter.evening,-62.62146996467999 +2020,4,RSHEAT,winter.evening,54.453452143199996 +2020,4,CO2EMT,winter.evening,3201.8357592940883 +2020,4,GASNAT,peak.night,-19.52227251698524 +2020,4,RSHEAT,peak.night,16.975889145204558 +2020,4,CO2EMT,peak.night,998.1737937934555 +2020,4,GASNAT,peak.day,-82.18418839575197 +2020,4,RSHEAT,peak.day,71.46451164847998 +2020,4,CO2EMT,peak.day,4202.077552674799 +2020,4,GASNAT,peak.peak,-58.291277616306004 +2020,4,RSHEAT,peak.peak,50.688067492440005 +2020,4,CO2EMT,peak.peak,2980.433024521726 +2020,4,GASNAT,peak.evening,-26.943315907602276 +2020,4,RSHEAT,peak.evening,23.428970354436764 +2020,4,CO2EMT,peak.evening,1377.6117423557046 +2020,4,GASNAT,summer.night,-0.0 +2020,4,RSHEAT,summer.night,0.0 +2020,4,CO2EMT,summer.night,0.0 +2020,4,GASNAT,summer.day,-0.0 +2020,4,RSHEAT,summer.day,0.0 +2020,4,CO2EMT,summer.day,0.0 +2020,4,GASNAT,summer.peak,-0.0 +2020,4,RSHEAT,summer.peak,0.0 +2020,4,CO2EMT,summer.peak,0.0 +2020,4,GASNAT,summer.evening,-0.0 +2020,4,RSHEAT,summer.evening,0.0 +2020,4,CO2EMT,summer.evening,0.0 +2020,4,GASNAT,autumn.night,-9.10896122967321 +2020,4,RSHEAT,autumn.night,7.920835851889748 +2020,4,CO2EMT,autumn.night,465.7411876731913 +2020,4,GASNAT,autumn.day,-56.69422338503598 +2020,4,RSHEAT,autumn.day,49.29932468263999 +2020,4,CO2EMT,autumn.day,2898.7756416768902 +2020,4,GASNAT,autumn.peak,-44.38022605095635 +2020,4,RSHEAT,autumn.peak,38.591500913875095 +2020,4,CO2EMT,autumn.peak,2269.160957985399 +2020,4,GASNAT,autumn.evening,-17.037675338040994 +2020,4,RSHEAT,autumn.evening,14.815369859166085 +2020,4,CO2EMT,autumn.evening,871.1363400340363 +2020,5,ELCTRI,winter.night,-9.624518793997803 +2020,5,RSHEAT,winter.night,29.165208466660005 +2020,5,ELCTRI,winter.day,-13.749312543997803 +2020,5,RSHEAT,winter.day,41.664583466660005 +2020,5,ELCTRI,winter.peak,-4.12479375 +2020,5,RSHEAT,winter.peak,12.499375 +2020,5,ELCTRI,winter.evening,-5.499725043997801 +2020,5,RSHEAT,winter.evening,16.66583346666 +2020,5,ELCTRI,peak.night,-8.439766292831097 +2020,5,RSHEAT,peak.night,25.575049372215442 +2020,5,ELCTRI,peak.day,-13.749312543997803 +2020,5,RSHEAT,peak.day,41.664583466660005 +2020,5,ELCTRI,peak.peak,-4.12479375 +2020,5,RSHEAT,peak.peak,12.499375 +2020,5,ELCTRI,peak.evening,-4.91826889936247 +2020,5,RSHEAT,peak.evening,14.903845149583242 +2020,5,ELCTRI,summer.night,-1.7439017181564 +2020,5,RSHEAT,summer.night,5.28455066108 +2020,5,ELCTRI,summer.day,-2.9055524196533997 +2020,5,RSHEAT,summer.day,8.80470430198 +2020,5,ELCTRI,summer.peak,-0.9567924409494001 +2020,5,RSHEAT,summer.peak,2.89937103318 +2020,5,ELCTRI,summer.evening,-0.7124084843502 +2020,5,RSHEAT,summer.evening,2.1588135889399998 +2020,5,ELCTRI,autumn.night,-8.791775241529585 +2020,5,RSHEAT,autumn.night,26.641743156150255 +2020,5,ELCTRI,autumn.day,-13.749312543997803 +2020,5,RSHEAT,autumn.day,41.664583466660005 +2020,5,ELCTRI,autumn.peak,-3.9439081325638172 +2020,5,RSHEAT,autumn.peak,11.9512367653449 +2020,5,ELCTRI,autumn.evening,-5.094273376920391 +2020,5,RSHEAT,autumn.evening,15.437192051273913 +2030,0,GASPRD,winter.night,0.0 +2030,0,CO2EMT,winter.night,0.0 +2030,0,GASPRD,winter.day,182.31624506589384 +2030,0,CO2EMT,winter.day,932.1829610219153 +2030,0,GASPRD,winter.peak,125.070625 +2030,0,CO2EMT,winter.peak,639.486105625 +2030,0,GASPRD,winter.evening,166.76083466742 +2030,0,CO2EMT,winter.evening,852.6481476545185 +2030,0,GASPRD,peak.night,0.0 +2030,0,CO2EMT,peak.night,0.0 +2030,0,GASPRD,peak.day,0.0 +2030,0,CO2EMT,peak.day,0.0 +2030,0,GASPRD,peak.peak,63.435296381704575 +2030,0,CO2EMT,peak.peak,324.3446703996555 +2030,0,GASPRD,peak.evening,166.76083466742 +2030,0,CO2EMT,peak.evening,852.6481476545185 +2030,0,GASPRD,summer.night,0.0 +2030,0,CO2EMT,summer.night,0.0 +2030,0,GASPRD,summer.day,0.0 +2030,0,CO2EMT,summer.day,0.0 +2030,0,GASPRD,summer.peak,0.0 +2030,0,CO2EMT,summer.peak,0.0 +2030,0,GASPRD,summer.evening,0.0 +2030,0,CO2EMT,summer.evening,0.0 +2030,0,GASPRD,autumn.night,0.0 +2030,0,CO2EMT,autumn.night,0.0 +2030,0,GASPRD,autumn.day,0.0 +2030,0,CO2EMT,autumn.day,0.0 +2030,0,GASPRD,autumn.peak,0.0 +2030,0,CO2EMT,autumn.peak,0.0 +2030,0,GASPRD,autumn.evening,160.94459622561885 +2030,0,CO2EMT,autumn.evening,822.9097205015893 +2030,1,GASPRD,winter.night,-0.0 +2030,1,GASNAT,winter.night,0.0 +2030,1,CO2EMT,winter.night,0.0 +2030,1,GASPRD,winter.day,-184.57837528456835 +2030,1,GASNAT,winter.day,175.78892884244604 +2030,1,CO2EMT,winter.day,449.40439658571336 +2030,1,GASPRD,winter.peak,-124.10114062500001 +2030,1,GASNAT,winter.peak,118.1915625 +2030,1,CO2EMT,winter.peak,302.15672953125005 +2030,1,GASPRD,winter.evening,-165.4681888237455 +2030,1,GASNAT,winter.evening,157.58875126070998 +2030,1,CO2EMT,winter.evening,402.87564259800513 +2030,1,GASPRD,peak.night,-0.0 +2030,1,GASNAT,peak.night,0.0 +2030,1,CO2EMT,peak.night,0.0 +2030,1,GASPRD,peak.day,-0.0 +2030,1,GASNAT,peak.day,0.0 +2030,1,CO2EMT,peak.day,0.0 +2030,1,GASPRD,peak.peak,-64.72794222537907 +2030,1,GASNAT,peak.peak,61.64565926226578 +2030,1,CO2EMT,peak.peak,157.59712790398248 +2030,1,GASPRD,peak.evening,-165.4681888237455 +2030,1,GASNAT,peak.evening,157.58875126070998 +2030,1,CO2EMT,peak.evening,402.87564259800513 +2030,1,GASPRD,summer.night,-0.0 +2030,1,GASNAT,summer.night,0.0 +2030,1,CO2EMT,summer.night,0.0 +2030,1,GASPRD,summer.day,-0.0 +2030,1,GASNAT,summer.day,0.0 +2030,1,CO2EMT,summer.day,0.0 +2030,1,GASPRD,summer.peak,-0.0 +2030,1,GASNAT,summer.peak,0.0 +2030,1,CO2EMT,summer.peak,0.0 +2030,1,GASPRD,summer.evening,-0.0 +2030,1,GASNAT,summer.evening,0.0 +2030,1,CO2EMT,summer.evening,0.0 +2030,1,GASPRD,autumn.night,-0.0 +2030,1,GASNAT,autumn.night,0.0 +2030,1,CO2EMT,autumn.night,0.0 +2030,1,GASPRD,autumn.day,-0.0 +2030,1,GASNAT,autumn.day,0.0 +2030,1,CO2EMT,autumn.day,0.0 +2030,1,GASPRD,autumn.peak,-0.0 +2030,1,GASNAT,autumn.peak,0.0 +2030,1,CO2EMT,autumn.peak,0.0 +2030,1,GASPRD,autumn.evening,-160.94459622561885 +2030,1,GASNAT,autumn.evening,153.2805678339227 +2030,1,CO2EMT,autumn.evening,391.8617716674234 +2030,2,ELCTRI,winter.night,3.6359293344885577 +2030,2,ELCTRI,winter.day,4.196086504289235 +2030,2,ELCTRI,winter.peak,1.463198441748869 +2030,2,ELCTRI,winter.evening,2.030145422947558 +2030,2,ELCTRI,peak.night,2.851272517283696 +2030,2,ELCTRI,peak.day,5.146656256509028 +2030,2,ELCTRI,peak.peak,1.7246051254110142 +2030,2,ELCTRI,peak.evening,1.72484387381507 +2030,2,ELCTRI,summer.night,0.0 +2030,2,ELCTRI,summer.day,0.0 +2030,2,ELCTRI,summer.peak,0.0 +2030,2,ELCTRI,summer.evening,0.0 +2030,2,ELCTRI,autumn.night,3.203281465982185 +2030,2,ELCTRI,autumn.day,5.645705373786218 +2030,2,ELCTRI,autumn.peak,1.5488393825638174 +2030,2,ELCTRI,autumn.evening,1.9008483513729915 +2030,3,GASNAT,winter.night,-0.0 +2030,3,ELCTRI,winter.night,0.0 +2030,3,CO2EMT,winter.night,0.0 +2030,3,GASNAT,winter.day,-0.0 +2030,3,ELCTRI,winter.day,0.0 +2030,3,CO2EMT,winter.day,0.0 +2030,3,GASNAT,winter.peak,-0.0 +2030,3,ELCTRI,winter.peak,0.0 +2030,3,CO2EMT,winter.peak,0.0 +2030,3,GASNAT,winter.evening,-0.0 +2030,3,ELCTRI,winter.evening,0.0 +2030,3,CO2EMT,winter.evening,0.0 +2030,3,GASNAT,peak.night,-4.385158138672477 +2030,3,ELCTRI,peak.night,2.923438759114984 +2030,3,CO2EMT,peak.night,224.2131356303237 +2030,3,GASNAT,peak.day,-0.0 +2030,3,ELCTRI,peak.day,0.0 +2030,3,CO2EMT,peak.day,0.0 +2030,3,GASNAT,peak.peak,-0.0 +2030,3,ELCTRI,peak.peak,0.0 +2030,3,CO2EMT,peak.peak,0.0 +2030,3,GASNAT,peak.evening,-2.168977885907286 +2030,3,ELCTRI,peak.evening,1.4459852572715242 +2030,3,CO2EMT,peak.evening,110.89983930643953 +2030,3,GASNAT,summer.night,-0.0 +2030,3,ELCTRI,summer.night,0.0 +2030,3,CO2EMT,summer.night,0.0 +2030,3,GASNAT,summer.day,-0.0 +2030,3,ELCTRI,summer.day,0.0 +2030,3,CO2EMT,summer.day,0.0 +2030,3,GASNAT,summer.peak,-0.0 +2030,3,ELCTRI,summer.peak,0.0 +2030,3,CO2EMT,summer.peak,0.0 +2030,3,GASNAT,summer.evening,-0.0 +2030,3,ELCTRI,summer.evening,0.0 +2030,3,CO2EMT,summer.evening,0.0 +2030,3,GASNAT,autumn.night,-3.1442174057585732 +2030,3,ELCTRI,autumn.night,2.096144937172382 +2030,3,CO2EMT,autumn.night,160.76383595643583 +2030,3,GASNAT,autumn.day,-0.0 +2030,3,ELCTRI,autumn.day,0.0 +2030,3,CO2EMT,autumn.day,0.0 +2030,3,GASNAT,autumn.peak,-0.72705137095236 +2030,3,ELCTRI,autumn.peak,0.48470091396824 +2030,3,CO2EMT,autumn.peak,37.17413659679416 +2030,3,GASNAT,autumn.evening,-1.5485075081387536 +2030,3,ELCTRI,autumn.evening,1.0323383387591691 +2030,3,CO2EMT,autumn.evening,79.17518889113447 +2030,4,GASNAT,winter.night,-43.845148578709995 +2030,4,RSHEAT,winter.night,38.1262161554 +2030,4,CO2EMT,winter.night,2241.8024468294425 +2030,4,GASNAT,winter.day,-219.27676569358 +2030,4,RSHEAT,winter.day,190.6754484292 +2030,4,CO2EMT,winter.day,11211.621029912745 +2030,4,GASNAT,winter.peak,-104.21874999999999 +2030,4,RSHEAT,winter.peak,90.625 +2030,4,CO2EMT,winter.peak,5328.704687500001 +2030,4,GASNAT,winter.evening,-71.44063561968 +2030,4,RSHEAT,winter.evening,62.1222918432 +2030,4,CO2EMT,winter.evening,3652.7596992342387 +2030,4,GASNAT,peak.night,-0.0 +2030,4,RSHEAT,peak.night,0.0 +2030,4,CO2EMT,peak.night,0.0 +2030,4,GASNAT,peak.day,-96.21279149075198 +2030,4,RSHEAT,peak.day,83.66329694848 +2030,4,CO2EMT,peak.day,4919.3600289221495 +2030,4,GASNAT,peak.peak,-66.12685298630602 +2030,4,RSHEAT,peak.peak,57.50161129244002 +2030,4,CO2EMT,peak.peak,3381.0659931898267 +2030,4,GASNAT,peak.evening,-29.670500177964005 +2030,4,RSHEAT,peak.evening,25.800434937360006 +2030,4,CO2EMT,peak.evening,1517.0526740992998 +2030,4,GASNAT,summer.night,-0.0 +2030,4,RSHEAT,summer.night,0.0 +2030,4,CO2EMT,summer.night,0.0 +2030,4,GASNAT,summer.day,-0.0 +2030,4,RSHEAT,summer.day,0.0 +2030,4,CO2EMT,summer.day,0.0 +2030,4,GASNAT,summer.peak,-0.0 +2030,4,RSHEAT,summer.peak,0.0 +2030,4,CO2EMT,summer.peak,0.0 +2030,4,GASNAT,summer.evening,-0.0 +2030,4,RSHEAT,summer.evening,0.0 +2030,4,CO2EMT,summer.evening,0.0 +2030,4,GASNAT,autumn.night,-0.0 +2030,4,RSHEAT,autumn.night,0.0 +2030,4,CO2EMT,autumn.night,0.0 +2030,4,GASNAT,autumn.day,-67.97422666003601 +2030,4,RSHEAT,autumn.day,59.10802318264001 +2030,4,CO2EMT,autumn.day,3475.5222091276414 +2030,4,GASNAT,autumn.peak,-50.017432516103 +2030,4,RSHEAT,autumn.peak,43.49341957922 +2030,4,CO2EMT,autumn.peak,2557.391324548347 +2030,4,GASNAT,autumn.evening,-19.376214580347 +2030,4,RSHEAT,autumn.evening,16.84888224378 +2030,4,CO2EMT,autumn.evening,990.7058514931422 +2030,5,ELCTRI,winter.night,-9.624518793997803 +2030,5,RSHEAT,winter.night,29.165208466660005 +2030,5,ELCTRI,winter.day,-13.749312543997803 +2030,5,RSHEAT,winter.day,41.664583466660005 +2030,5,ELCTRI,winter.peak,-4.12479375 +2030,5,RSHEAT,winter.peak,12.499375 +2030,5,ELCTRI,winter.evening,-5.499725043997801 +2030,5,RSHEAT,winter.evening,16.66583346666 +2030,5,ELCTRI,peak.night,-9.624518793997803 +2030,5,RSHEAT,peak.night,29.165208466660005 +2030,5,ELCTRI,peak.day,-13.749312543997803 +2030,5,RSHEAT,peak.day,41.664583466660005 +2030,5,ELCTRI,peak.peak,-4.12479375 +2030,5,RSHEAT,peak.peak,12.499375 +2030,5,ELCTRI,peak.evening,-5.499725043997801 +2030,5,RSHEAT,peak.evening,16.66583346666 +2030,5,ELCTRI,summer.night,-1.9319477961564002 +2030,5,RSHEAT,summer.night,5.85438726108 +2030,5,ELCTRI,summer.day,-3.2188600626534005 +2030,5,RSHEAT,summer.day,9.75412140198 +2030,5,ELCTRI,summer.peak,-1.0599640039494003 +2030,5,RSHEAT,summer.peak,3.2120121331800005 +2030,5,ELCTRI,summer.evening,-0.7892279633502001 +2030,5,RSHEAT,summer.evening,2.39159988894 +2030,5,ELCTRI,autumn.night,-9.624518793997803 +2030,5,RSHEAT,autumn.night,29.165208466660005 +2030,5,ELCTRI,autumn.day,-13.749312543997803 +2030,5,RSHEAT,autumn.day,41.664583466660005 +2030,5,ELCTRI,autumn.peak,-4.12479375 +2030,5,RSHEAT,autumn.peak,12.499375 +2030,5,ELCTRI,autumn.evening,-5.499725043997801 +2030,5,RSHEAT,autumn.evening,16.66583346666 +2030,6,GASNAT,winter.night,-0.0 +2030,6,RSHEAT,winter.night,0.0 +2030,6,CO2EMT,winter.night,0.0 +2030,6,GASNAT,winter.day,-0.0 +2030,6,RSHEAT,winter.day,0.0 +2030,6,CO2EMT,winter.day,0.0 +2030,6,GASNAT,winter.peak,-12.787942711186016 +2030,6,RSHEAT,winter.peak,11.119950183640015 +2030,6,CO2EMT,winter.peak,653.8475108229411 +2030,6,GASNAT,winter.evening,-0.0 +2030,6,RSHEAT,winter.evening,0.0 +2030,6,CO2EMT,winter.evening,0.0 +2030,6,GASNAT,peak.night,-20.670129843374 +2030,6,RSHEAT,peak.night,17.97402595076 +2030,6,CO2EMT,peak.night,1056.8637388917127 +2030,6,GASNAT,peak.day,-0.0 +2030,6,RSHEAT,peak.day,0.0 +2030,6,CO2EMT,peak.day,0.0 +2030,6,GASNAT,peak.peak,-0.0 +2030,6,RSHEAT,peak.peak,0.0 +2030,6,CO2EMT,peak.peak,0.0 +2030,6,GASNAT,peak.evening,-0.0 +2030,6,RSHEAT,peak.evening,0.0 +2030,6,CO2EMT,peak.evening,0.0 +2030,6,GASNAT,summer.night,-0.0 +2030,6,RSHEAT,summer.night,0.0 +2030,6,CO2EMT,summer.night,0.0 +2030,6,GASNAT,summer.day,-0.0 +2030,6,RSHEAT,summer.day,0.0 +2030,6,CO2EMT,summer.day,0.0 +2030,6,GASNAT,summer.peak,-0.0 +2030,6,RSHEAT,summer.peak,0.0 +2030,6,CO2EMT,summer.peak,0.0 +2030,6,GASNAT,summer.evening,-0.0 +2030,6,RSHEAT,summer.evening,0.0 +2030,6,CO2EMT,summer.evening,0.0 +2030,6,GASNAT,autumn.night,-10.492917792587003 +2030,6,RSHEAT,autumn.night,9.124276341380003 +2030,6,CO2EMT,autumn.night,536.5028867349736 +2030,6,GASNAT,autumn.day,-0.0 +2030,6,RSHEAT,autumn.day,0.0 +2030,6,CO2EMT,autumn.day,0.0 +2030,6,GASNAT,autumn.peak,-0.0 +2030,6,RSHEAT,autumn.peak,0.0 +2030,6,CO2EMT,autumn.peak,0.0 +2030,6,GASNAT,autumn.evening,-0.0 +2030,6,RSHEAT,autumn.evening,0.0 +2030,6,CO2EMT,autumn.evening,0.0 +2030,7,ELCTRI,winter.night,5.988589459509244 +2030,7,ELCTRI,winter.day,9.553226039708568 +2030,7,ELCTRI,winter.peak,2.6615953082511314 +2030,7,ELCTRI,winter.evening,3.4695796210502423 +2030,7,ELCTRI,peak.night,3.8498075175991215 +2030,7,ELCTRI,peak.day,8.602656287488774 +2030,7,ELCTRI,peak.peak,2.4001886245889863 +2030,7,ELCTRI,peak.evening,2.3288959129112063 +2030,7,ELCTRI,summer.night,1.9319477961564002 +2030,7,ELCTRI,summer.day,3.2188600626534005 +2030,7,ELCTRI,summer.peak,1.0599640039494003 +2030,7,ELCTRI,summer.evening,0.7892279633502001 +2030,7,ELCTRI,autumn.night,4.325092390843235 +2030,7,ELCTRI,autumn.day,8.103607170211584 +2030,7,ELCTRI,autumn.peak,2.0912534534679432 +2030,7,ELCTRI,autumn.evening,2.5665383538656403 +2040,0,GASPRD,winter.night,0.0 +2040,0,CO2EMT,winter.night,0.0 +2040,0,GASPRD,winter.day,360.9639698231197 +2040,0,CO2EMT,winter.day,1845.6087777056114 +2040,0,GASPRD,winter.peak,125.070625 +2040,0,CO2EMT,winter.peak,639.486105625 +2040,0,GASPRD,winter.evening,166.76083466742 +2040,0,CO2EMT,winter.evening,852.6481476545185 +2040,0,GASPRD,peak.night,0.0 +2040,0,CO2EMT,peak.night,0.0 +2040,0,GASPRD,peak.day,85.71569060312174 +2040,0,CO2EMT,peak.day,438.2643260537615 +2040,0,GASPRD,peak.peak,125.070625 +2040,0,CO2EMT,peak.peak,639.486105625 +2040,0,GASPRD,peak.evening,166.76083466742 +2040,0,CO2EMT,peak.evening,852.6481476545185 +2040,0,GASPRD,summer.night,0.0 +2040,0,CO2EMT,summer.night,0.0 +2040,0,GASPRD,summer.day,0.0 +2040,0,CO2EMT,summer.day,0.0 +2040,0,GASPRD,summer.peak,0.0 +2040,0,CO2EMT,summer.peak,0.0 +2040,0,GASPRD,summer.evening,28.10673815560485 +2040,0,CO2EMT,summer.evening,143.70975218960763 +2040,0,GASPRD,autumn.night,0.0 +2040,0,CO2EMT,autumn.night,0.0 +2040,0,GASPRD,autumn.day,11.030571054582595 +2040,0,CO2EMT,autumn.day,56.39930980208081 +2040,0,GASPRD,autumn.peak,125.070625 +2040,0,CO2EMT,autumn.peak,639.486105625 +2040,0,GASPRD,autumn.evening,166.76083466742 +2040,0,CO2EMT,autumn.evening,852.6481476545185 +2040,1,GASPRD,winter.night,-289.5693294487456 +2040,1,GASNAT,winter.night,275.78031376071004 +2040,1,CO2EMT,winter.night,705.0323721292552 +2040,1,GASPRD,winter.day,-73.65677059304869 +2040,1,GASNAT,winter.day,70.14930532671303 +2040,1,CO2EMT,winter.day,179.33669906774188 +2040,1,GASPRD,winter.peak,-124.10114062500001 +2040,1,GASNAT,winter.peak,118.1915625 +2040,1,CO2EMT,winter.peak,302.15672953125005 +2040,1,GASPRD,winter.evening,-165.4681888237455 +2040,1,GASNAT,winter.evening,157.58875126070998 +2040,1,CO2EMT,winter.evening,402.87564259800513 +2040,1,GASPRD,peak.night,-0.0 +2040,1,GASNAT,peak.night,0.0 +2040,1,CO2EMT,peak.night,0.0 +2040,1,GASPRD,peak.day,-87.97782082179623 +2040,1,GASNAT,peak.day,83.78840078266308 +2040,1,CO2EMT,peak.day,214.20504660087818 +2040,1,GASPRD,peak.peak,-124.10114062500001 +2040,1,GASNAT,peak.peak,118.1915625 +2040,1,CO2EMT,peak.peak,302.15672953125005 +2040,1,GASPRD,peak.evening,-165.4681888237455 +2040,1,GASNAT,peak.evening,157.58875126070998 +2040,1,CO2EMT,peak.evening,402.87564259800513 +2040,1,GASPRD,summer.night,-0.0 +2040,1,GASNAT,summer.night,0.0 +2040,1,CO2EMT,summer.night,0.0 +2040,1,GASPRD,summer.day,-0.0 +2040,1,GASNAT,summer.day,0.0 +2040,1,CO2EMT,summer.day,0.0 +2040,1,GASPRD,summer.peak,-0.0 +2040,1,GASNAT,summer.peak,0.0 +2040,1,CO2EMT,summer.peak,0.0 +2040,1,GASPRD,summer.evening,-28.10673815560485 +2040,1,GASNAT,summer.evening,26.768322052957 +2040,1,CO2EMT,summer.evening,68.43321532838458 +2040,1,GASPRD,autumn.night,-0.0 +2040,1,GASNAT,autumn.night,0.0 +2040,1,CO2EMT,autumn.night,0.0 +2040,1,GASPRD,autumn.day,-13.292701273257066 +2040,1,GASNAT,autumn.day,12.659715498340063 +2040,1,CO2EMT,autumn.day,32.36456267150637 +2040,1,GASPRD,autumn.peak,-124.10114062500001 +2040,1,GASNAT,autumn.peak,118.1915625 +2040,1,CO2EMT,autumn.peak,302.15672953125005 +2040,1,GASPRD,autumn.evening,-165.4681888237455 +2040,1,GASNAT,autumn.evening,157.58875126070998 +2040,1,CO2EMT,autumn.evening,402.87564259800513 +2040,6,GASNAT,winter.night,-29.838533129172095 +2040,6,RSHEAT,winter.night,25.94655054710617 +2040,6,CO2EMT,winter.night,1525.6441988945694 +2040,6,GASNAT,winter.day,-42.62647584035811 +2040,6,RSHEAT,winter.day,37.066500730746185 +2040,6,CO2EMT,winter.day,2179.4917097175103 +2040,6,GASNAT,winter.peak,-12.787942711186016 +2040,6,RSHEAT,winter.peak,11.119950183640015 +2040,6,CO2EMT,winter.peak,653.8475108229411 +2040,6,GASNAT,winter.evening,-17.050590417986076 +2040,6,RSHEAT,winter.evening,14.826600363466154 +2040,6,CO2EMT,winter.evening,871.7966880716282 +2040,6,GASNAT,peak.night,-29.838533129172095 +2040,6,RSHEAT,peak.night,25.94655054710617 +2040,6,CO2EMT,peak.night,1525.6441988945694 +2040,6,GASNAT,peak.day,-42.62647584035811 +2040,6,RSHEAT,peak.day,37.066500730746185 +2040,6,CO2EMT,peak.day,2179.4917097175103 +2040,6,GASNAT,peak.peak,-12.787942711186016 +2040,6,RSHEAT,peak.peak,11.119950183640015 +2040,6,CO2EMT,peak.peak,653.8475108229411 +2040,6,GASNAT,peak.evening,-17.050590417986076 +2040,6,RSHEAT,peak.evening,14.826600363466154 +2040,6,CO2EMT,peak.evening,871.7966880716282 +2040,6,GASNAT,summer.night,-0.0 +2040,6,RSHEAT,summer.night,0.0 +2040,6,CO2EMT,summer.night,0.0 +2040,6,GASNAT,summer.day,-0.0 +2040,6,RSHEAT,summer.day,0.0 +2040,6,CO2EMT,summer.day,0.0 +2040,6,GASNAT,summer.peak,-0.0 +2040,6,RSHEAT,summer.peak,0.0 +2040,6,CO2EMT,summer.peak,0.0 +2040,6,GASNAT,summer.evening,-0.0 +2040,6,RSHEAT,summer.evening,0.0 +2040,6,CO2EMT,summer.evening,0.0 +2040,6,GASNAT,autumn.night,-29.838533129172095 +2040,6,RSHEAT,autumn.night,25.94655054710617 +2040,6,CO2EMT,autumn.night,1525.6441988945694 +2040,6,GASNAT,autumn.day,-42.62647584035811 +2040,6,RSHEAT,autumn.day,37.066500730746185 +2040,6,CO2EMT,autumn.day,2179.4917097175103 +2040,6,GASNAT,autumn.peak,-12.787942711186016 +2040,6,RSHEAT,autumn.peak,11.119950183640015 +2040,6,CO2EMT,autumn.peak,653.8475108229411 +2040,6,GASNAT,autumn.evening,-17.050590417986076 +2040,6,RSHEAT,autumn.evening,14.826600363466154 +2040,6,CO2EMT,autumn.evening,871.7966880716282 +2040,8,GASNAT,winter.night,-55.07888519119691 +2040,8,RSHEAT,winter.night,47.894682774953836 +2040,8,CO2EMT,winter.night,2816.183399825898 +2040,8,GASNAT,winter.day,-250.5715919948809 +2040,8,RSHEAT,winter.day,217.88834086511383 +2040,8,CO2EMT,winter.day,12811.72549869826 +2040,8,GASNAT,winter.peak,-131.38099422 +2040,8,RSHEAT,winter.peak,114.2443428 +2040,8,CO2EMT,winter.peak,6717.5102344686 +2040,8,GASNAT,winter.evening,-82.37491934335293 +2040,8,RSHEAT,winter.evening,71.63036464639386 +2040,8,CO2EMT,winter.evening,4211.829626025636 +2040,8,GASNAT,peak.night,-29.648126735860906 +2040,8,RSHEAT,peak.night,25.780979770313834 +2040,8,CO2EMT,peak.night,1515.9087200045683 +2040,8,GASNAT,peak.day,-115.5291897320529 +2040,8,RSHEAT,peak.day,100.46016498439383 +2040,8,CO2EMT,peak.day,5907.007470999865 +2040,8,GASNAT,peak.peak,-75.54876689511998 +2040,8,RSHEAT,peak.peak,65.6945799088 +2040,8,CO2EMT,peak.peak,3862.8084513474855 +2040,8,GASNAT,peak.evening,-36.53908908163693 +2040,8,RSHEAT,peak.evening,31.773120940553852 +2040,8,CO2EMT,peak.evening,1868.2436247440962 +2040,8,GASNAT,summer.night,-7.3878574402420005 +2040,8,RSHEAT,summer.night,6.424223861080001 +2040,8,CO2EMT,summer.night,377.7411509195735 +2040,8,GASNAT,summer.day,-12.309069277277 +2040,8,RSHEAT,summer.day,10.70353850198 +2040,8,CO2EMT,summer.day,629.362712147173 +2040,8,GASNAT,summer.peak,-4.053351218157 +2040,8,RSHEAT,summer.peak,3.5246532331800005 +2040,8,CO2EMT,summer.peak,207.24784778436745 +2040,8,GASNAT,summer.evening,-3.0180441172809997 +2040,8,RSHEAT,summer.evening,2.62438618894 +2040,8,CO2EMT,summer.evening,154.31259571657753 +2040,8,GASNAT,autumn.night,-18.480316070073908 +2040,8,RSHEAT,autumn.night,16.069840060933835 +2040,8,CO2EMT,autumn.night,944.898560662879 +2040,8,GASNAT,autumn.day,-84.54202508133689 +2040,8,RSHEAT,autumn.day,73.51480441855382 +2040,8,CO2EMT,autumn.day,4322.633742408756 +2040,8,GASNAT,autumn.peak,-57.871336489916985 +2040,8,RSHEAT,autumn.peak,50.32290129557999 +2040,8,CO2EMT,autumn.peak,2958.9614347294555 +2040,8,GASNAT,autumn.evening,-25.24280951901992 +2040,8,RSHEAT,autumn.evening,21.950269146973845 +2040,8,CO2EMT,autumn.evening,1290.6648507074885 diff --git a/tests/data/simple_full_average/commodity_prices.csv b/tests/data/simple_full_average/commodity_prices.csv new file mode 100644 index 000000000..a6bc20412 --- /dev/null +++ b/tests/data/simple_full_average/commodity_prices.csv @@ -0,0 +1,177 @@ +milestone_year,commodity_id,region_id,time_slice,price +2020,GASPRD,GBR,winter.night,8.947788406778768 +2020,GASPRD,GBR,winter.day,8.947788406778768 +2020,GASPRD,GBR,winter.peak,8.947788406778768 +2020,GASPRD,GBR,winter.evening,8.947788406778768 +2020,GASPRD,GBR,peak.night,8.947788406778768 +2020,GASPRD,GBR,peak.day,8.947788406778768 +2020,GASPRD,GBR,peak.peak,8.947788406778768 +2020,GASPRD,GBR,peak.evening,8.947788406778768 +2020,GASPRD,GBR,summer.night,8.947788406778768 +2020,GASPRD,GBR,summer.day,8.947788406778768 +2020,GASPRD,GBR,summer.peak,8.947788406778768 +2020,GASPRD,GBR,summer.evening,8.947788406778768 +2020,GASPRD,GBR,autumn.night,8.947788406778768 +2020,GASPRD,GBR,autumn.day,8.947788406778768 +2020,GASPRD,GBR,autumn.peak,8.947788406778768 +2020,GASPRD,GBR,autumn.evening,8.947788406778768 +2020,GASNAT,GBR,winter.night,14.681136724623657 +2020,GASNAT,GBR,winter.day,14.681136724623657 +2020,GASNAT,GBR,winter.peak,14.681136724623657 +2020,GASNAT,GBR,winter.evening,14.681136724623657 +2020,GASNAT,GBR,peak.night,14.681136724623657 +2020,GASNAT,GBR,peak.day,14.681136724623657 +2020,GASNAT,GBR,peak.peak,14.681136724623657 +2020,GASNAT,GBR,peak.evening,14.681136724623657 +2020,GASNAT,GBR,summer.night,14.681136724623657 +2020,GASNAT,GBR,summer.day,14.681136724623657 +2020,GASNAT,GBR,summer.peak,14.681136724623657 +2020,GASNAT,GBR,summer.evening,14.681136724623657 +2020,GASNAT,GBR,autumn.night,14.681136724623657 +2020,GASNAT,GBR,autumn.day,14.681136724623657 +2020,GASNAT,GBR,autumn.peak,14.681136724623657 +2020,GASNAT,GBR,autumn.evening,14.681136724623657 +2020,ELCTRI,GBR,winter.night,7.993309 +2020,ELCTRI,GBR,winter.day,7.993309 +2020,ELCTRI,GBR,winter.peak,7.993309 +2020,ELCTRI,GBR,winter.evening,7.993309 +2020,ELCTRI,GBR,peak.night,17.262233030303033 +2020,ELCTRI,GBR,peak.day,7.993309 +2020,ELCTRI,GBR,peak.peak,7.993309 +2020,ELCTRI,GBR,peak.evening,17.262233030303033 +2020,ELCTRI,GBR,summer.night,7.993309 +2020,ELCTRI,GBR,summer.day,0.4 +2020,ELCTRI,GBR,summer.peak,0.4 +2020,ELCTRI,GBR,summer.evening,0.4 +2020,ELCTRI,GBR,autumn.night,17.262233030303033 +2020,ELCTRI,GBR,autumn.day,7.993309 +2020,ELCTRI,GBR,autumn.peak,17.262233030303033 +2020,ELCTRI,GBR,autumn.evening,17.262233030303033 +2020,RSHEAT,GBR,winter.night,5.866536900000001 +2020,RSHEAT,GBR,winter.day,5.866536900000001 +2020,RSHEAT,GBR,winter.peak,5.866536900000001 +2020,RSHEAT,GBR,winter.evening,5.866536900000001 +2020,RSHEAT,GBR,peak.night,5.866536900000001 +2020,RSHEAT,GBR,peak.day,5.866536900000001 +2020,RSHEAT,GBR,peak.peak,5.866536900000001 +2020,RSHEAT,GBR,peak.evening,5.866536900000001 +2020,RSHEAT,GBR,summer.night,2.8077919700000002 +2020,RSHEAT,GBR,summer.day,0.30200000000000005 +2020,RSHEAT,GBR,summer.peak,0.30200000000000005 +2020,RSHEAT,GBR,summer.evening,0.30200000000000005 +2020,RSHEAT,GBR,autumn.night,5.866536900000001 +2020,RSHEAT,GBR,autumn.day,5.866536900000001 +2020,RSHEAT,GBR,autumn.peak,5.866536900000001 +2020,RSHEAT,GBR,autumn.evening,5.866536900000001 +2030,GASPRD,GBR,winter.night,8.687784851194666 +2030,GASPRD,GBR,winter.day,8.687784851194666 +2030,GASPRD,GBR,winter.peak,8.687784851194666 +2030,GASPRD,GBR,winter.evening,8.687784851194666 +2030,GASPRD,GBR,peak.night,8.687784851194666 +2030,GASPRD,GBR,peak.day,8.687784851194666 +2030,GASPRD,GBR,peak.peak,8.687784851194666 +2030,GASPRD,GBR,peak.evening,8.687784851194666 +2030,GASPRD,GBR,autumn.night,8.687784851194666 +2030,GASPRD,GBR,autumn.day,8.687784851194666 +2030,GASPRD,GBR,autumn.peak,8.687784851194666 +2030,GASPRD,GBR,autumn.evening,8.687784851194666 +2030,GASPRD,GBR,summer.night,3.761943018103873 +2030,GASPRD,GBR,summer.day,3.761943018103873 +2030,GASPRD,GBR,summer.peak,3.761943018103873 +2030,GASPRD,GBR,summer.evening,3.761943018103873 +2030,GASNAT,GBR,winter.night,14.227541264416862 +2030,GASNAT,GBR,winter.day,14.227541264416862 +2030,GASNAT,GBR,winter.peak,14.227541264416862 +2030,GASNAT,GBR,winter.evening,14.227541264416862 +2030,GASNAT,GBR,peak.night,14.22754126441686 +2030,GASNAT,GBR,peak.day,14.22754126441686 +2030,GASNAT,GBR,peak.peak,14.22754126441686 +2030,GASNAT,GBR,peak.evening,14.22754126441686 +2030,GASNAT,GBR,autumn.night,14.22754126441686 +2030,GASNAT,GBR,autumn.day,14.22754126441686 +2030,GASNAT,GBR,autumn.peak,14.22754126441686 +2030,GASNAT,GBR,autumn.evening,14.22754126441686 +2030,GASNAT,GBR,summer.night,5.642496281681778 +2030,GASNAT,GBR,summer.day,5.642496281681778 +2030,GASNAT,GBR,summer.peak,5.642496281681778 +2030,GASNAT,GBR,summer.evening,5.642496281681778 +2030,ELCTRI,GBR,winter.night,0.4 +2030,ELCTRI,GBR,winter.day,0.4 +2030,ELCTRI,GBR,winter.peak,0.4 +2030,ELCTRI,GBR,winter.evening,0.4 +2030,ELCTRI,GBR,peak.night,7.993309 +2030,ELCTRI,GBR,peak.day,0.4 +2030,ELCTRI,GBR,peak.peak,0.4 +2030,ELCTRI,GBR,peak.evening,7.993309 +2030,ELCTRI,GBR,summer.night,0.4 +2030,ELCTRI,GBR,summer.day,0.4 +2030,ELCTRI,GBR,summer.peak,0.4 +2030,ELCTRI,GBR,summer.evening,0.4 +2030,ELCTRI,GBR,autumn.night,7.993309 +2030,ELCTRI,GBR,autumn.day,0.4 +2030,ELCTRI,GBR,autumn.peak,7.993309 +2030,ELCTRI,GBR,autumn.evening,7.993309 +2030,RSHEAT,GBR,winter.night,5.866536900000001 +2030,RSHEAT,GBR,winter.day,5.866536900000001 +2030,RSHEAT,GBR,winter.peak,5.866536900000001 +2030,RSHEAT,GBR,winter.evening,5.866536900000001 +2030,RSHEAT,GBR,peak.night,5.866536900000001 +2030,RSHEAT,GBR,peak.day,5.866536900000001 +2030,RSHEAT,GBR,peak.peak,5.866536900000001 +2030,RSHEAT,GBR,peak.evening,5.866536900000001 +2030,RSHEAT,GBR,summer.night,0.30200000000000005 +2030,RSHEAT,GBR,summer.day,0.30200000000000005 +2030,RSHEAT,GBR,summer.peak,0.30200000000000005 +2030,RSHEAT,GBR,summer.evening,0.30200000000000005 +2030,RSHEAT,GBR,autumn.night,5.866536900000001 +2030,RSHEAT,GBR,autumn.day,5.866536900000001 +2030,RSHEAT,GBR,autumn.peak,5.866536900000001 +2030,RSHEAT,GBR,autumn.evening,5.866536900000001 +2040,GASPRD,GBR,winter.night,6.325466094838786 +2040,GASPRD,GBR,winter.day,6.325466094838786 +2040,GASPRD,GBR,winter.peak,6.325466094838786 +2040,GASPRD,GBR,winter.evening,6.325466094838786 +2040,GASPRD,GBR,peak.night,6.325466094838786 +2040,GASPRD,GBR,peak.day,6.325466094838786 +2040,GASPRD,GBR,peak.peak,6.325466094838786 +2040,GASPRD,GBR,peak.evening,6.325466094838786 +2040,GASPRD,GBR,summer.night,6.325466094838786 +2040,GASPRD,GBR,summer.day,6.325466094838786 +2040,GASPRD,GBR,summer.peak,6.325466094838786 +2040,GASPRD,GBR,summer.evening,6.325466094838786 +2040,GASPRD,GBR,autumn.night,6.325466094838785 +2040,GASPRD,GBR,autumn.day,6.325466094838785 +2040,GASPRD,GBR,autumn.peak,6.325466094838785 +2040,GASPRD,GBR,autumn.evening,6.325466094838785 +2040,GASNAT,GBR,winter.night,10.106301219664726 +2040,GASNAT,GBR,winter.day,10.106301219664726 +2040,GASNAT,GBR,winter.peak,10.106301219664726 +2040,GASNAT,GBR,winter.evening,10.106301219664726 +2040,GASNAT,GBR,peak.night,10.106301219664724 +2040,GASNAT,GBR,peak.day,10.106301219664724 +2040,GASNAT,GBR,peak.peak,10.106301219664724 +2040,GASNAT,GBR,peak.evening,10.106301219664724 +2040,GASNAT,GBR,summer.night,10.106301219664724 +2040,GASNAT,GBR,summer.day,10.106301219664724 +2040,GASNAT,GBR,summer.peak,10.106301219664724 +2040,GASNAT,GBR,summer.evening,10.106301219664724 +2040,GASNAT,GBR,autumn.night,10.106301219664722 +2040,GASNAT,GBR,autumn.day,10.106301219664722 +2040,GASNAT,GBR,autumn.peak,10.106301219664722 +2040,GASNAT,GBR,autumn.evening,10.106301219664722 +2040,RSHEAT,GBR,winter.night,5.866536900000001 +2040,RSHEAT,GBR,winter.day,5.866536900000001 +2040,RSHEAT,GBR,winter.peak,5.866536900000001 +2040,RSHEAT,GBR,winter.evening,5.866536900000001 +2040,RSHEAT,GBR,peak.night,5.866536900000001 +2040,RSHEAT,GBR,peak.day,5.866536900000001 +2040,RSHEAT,GBR,peak.peak,5.866536900000001 +2040,RSHEAT,GBR,peak.evening,5.866536900000001 +2040,RSHEAT,GBR,summer.night,5.866536900000001 +2040,RSHEAT,GBR,summer.day,5.866536900000001 +2040,RSHEAT,GBR,summer.peak,5.866536900000001 +2040,RSHEAT,GBR,summer.evening,5.866536900000001 +2040,RSHEAT,GBR,autumn.night,5.866536900000001 +2040,RSHEAT,GBR,autumn.day,5.866536900000001 +2040,RSHEAT,GBR,autumn.peak,5.866536900000001 +2040,RSHEAT,GBR,autumn.evening,5.866536900000001 diff --git a/tests/data/simple_marginal_average/assets.csv b/tests/data/simple_marginal_average/assets.csv new file mode 100644 index 000000000..5f4559fa2 --- /dev/null +++ b/tests/data/simple_marginal_average/assets.csv @@ -0,0 +1,10 @@ +asset_id,process_id,region_id,agent_id,group_id,commission_year,decommission_year,capacity +0,GASDRV,GBR,A0_GEX,,2020,,4002.26 +1,GASPRC,GBR,A0_GPR,,2020,,3782.13 +2,WNDFRM,GBR,A0_ELC,,2020,2040,3.964844 +3,GASCGT,GBR,A0_ELC,,2020,2040,2.43 +4,RGASBR,GBR,A0_RES,,2020,2035,2900.0 +5,RELCHP,GBR,A0_RES,,2020,2035,399.98 +6,RGASBR,GBR,A0_RES,,2030,,355.83840587648046 +7,GASCGT,GBR,A0_ELC,,2030,2040,0.5151564434825014 +8,RGASBR,GBR,A0_RES,,2040,,3655.8189696 diff --git a/tests/data/simple_marginal_average/commodity_flows.csv b/tests/data/simple_marginal_average/commodity_flows.csv new file mode 100644 index 000000000..196543694 --- /dev/null +++ b/tests/data/simple_marginal_average/commodity_flows.csv @@ -0,0 +1,721 @@ +milestone_year,asset_id,commodity_id,time_slice,flow +2020,0,GASPRD,winter.night,0.0 +2020,0,CO2EMT,winter.night,0.0 +2020,0,GASPRD,winter.day,151.10360181069285 +2020,0,CO2EMT,winter.day,772.5927160580726 +2020,0,GASPRD,winter.peak,125.070625 +2020,0,CO2EMT,winter.peak,639.486105625 +2020,0,GASPRD,winter.evening,166.76083466742 +2020,0,CO2EMT,winter.evening,852.6481476545185 +2020,0,GASPRD,peak.night,0.0 +2020,0,CO2EMT,peak.night,0.0 +2020,0,GASPRD,peak.day,0.0 +2020,0,CO2EMT,peak.day,0.0 +2020,0,GASPRD,peak.peak,58.67582562255714 +2020,0,CO2EMT,peak.peak,300.0094964081347 +2020,0,GASPRD,peak.evening,166.76083466742 +2020,0,CO2EMT,peak.evening,852.6481476545185 +2020,0,GASPRD,summer.night,0.0 +2020,0,CO2EMT,summer.night,0.0 +2020,0,GASPRD,summer.day,0.0 +2020,0,CO2EMT,summer.day,0.0 +2020,0,GASPRD,summer.peak,0.0 +2020,0,CO2EMT,summer.peak,0.0 +2020,0,GASPRD,summer.evening,0.16861964795988577 +2020,0,CO2EMT,summer.evening,0.862152260018896 +2020,0,GASPRD,autumn.night,0.0 +2020,0,CO2EMT,autumn.night,0.0 +2020,0,GASPRD,autumn.day,0.0 +2020,0,CO2EMT,autumn.day,0.0 +2020,0,GASPRD,autumn.peak,0.0 +2020,0,CO2EMT,autumn.peak,0.0 +2020,0,GASPRD,autumn.evening,163.3883025525992 +2020,0,CO2EMT,autumn.evening,835.4043909514398 +2020,1,GASPRD,winter.night,-0.0 +2020,1,GASNAT,winter.night,0.0 +2020,1,CO2EMT,winter.night,0.0 +2020,1,GASPRD,winter.day,-153.36573202936736 +2020,1,GASNAT,winter.day,146.06260193273081 +2020,1,CO2EMT,winter.day,373.40904184102635 +2020,1,GASPRD,winter.peak,-124.10114062500001 +2020,1,GASNAT,winter.peak,118.1915625 +2020,1,CO2EMT,winter.peak,302.15672953125005 +2020,1,GASPRD,winter.evening,-165.4681888237455 +2020,1,GASNAT,winter.evening,157.58875126070998 +2020,1,CO2EMT,winter.evening,402.87564259800513 +2020,1,GASPRD,peak.night,-0.0 +2020,1,GASNAT,peak.night,0.0 +2020,1,CO2EMT,peak.night,0.0 +2020,1,GASPRD,peak.day,-0.0 +2020,1,GASNAT,peak.day,0.0 +2020,1,CO2EMT,peak.day,0.0 +2020,1,GASPRD,peak.peak,-59.96847146623166 +2020,1,GASNAT,peak.peak,57.11282996783967 +2020,1,CO2EMT,peak.peak,146.00894981278213 +2020,1,GASPRD,peak.evening,-165.4681888237455 +2020,1,GASNAT,peak.evening,157.58875126070998 +2020,1,CO2EMT,peak.evening,402.87564259800513 +2020,1,GASPRD,summer.night,-0.0 +2020,1,GASNAT,summer.night,0.0 +2020,1,CO2EMT,summer.night,0.0 +2020,1,GASPRD,summer.day,-0.0 +2020,1,GASNAT,summer.day,0.0 +2020,1,CO2EMT,summer.day,0.0 +2020,1,GASPRD,summer.peak,-0.0 +2020,1,GASNAT,summer.peak,0.0 +2020,1,CO2EMT,summer.peak,0.0 +2020,1,GASPRD,summer.evening,-0.16861964795988577 +2020,1,GASNAT,summer.evening,0.16059014091417692 +2020,1,CO2EMT,summer.evening,0.4105486952470933 +2020,1,GASPRD,autumn.night,-0.0 +2020,1,GASNAT,autumn.night,0.0 +2020,1,CO2EMT,autumn.night,0.0 +2020,1,GASPRD,autumn.day,-0.0 +2020,1,GASNAT,autumn.day,0.0 +2020,1,CO2EMT,autumn.day,0.0 +2020,1,GASPRD,autumn.peak,-0.0 +2020,1,GASNAT,autumn.peak,0.0 +2020,1,CO2EMT,autumn.peak,0.0 +2020,1,GASPRD,autumn.evening,-163.3883025525992 +2020,1,GASNAT,autumn.evening,155.60790719295161 +2020,1,CO2EMT,autumn.evening,397.81161473878086 +2020,2,ELCTRI,winter.night,4.435312795545212 +2020,2,ELCTRI,winter.day,7.075379933645912 +2020,2,ELCTRI,winter.peak,1.9712501261051125 +2020,2,ELCTRI,winter.evening,2.5696653598405335 +2020,2,ELCTRI,peak.night,2.851272517283696 +2020,2,ELCTRI,peak.day,6.3713620320039785 +2020,2,ELCTRI,peak.peak,1.7776452018191917 +2020,2,ELCTRI,peak.evening,1.72484387381507 +2020,2,ELCTRI,summer.night,1.6368416242136155 +2020,2,ELCTRI,summer.day,2.9055524196533997 +2020,2,ELCTRI,summer.peak,0.9567924409494001 +2020,2,ELCTRI,summer.evening,0.7124084843502 +2020,2,ELCTRI,autumn.night,3.203281465982185 +2020,2,ELCTRI,autumn.day,6.001752635595889 +2020,2,ELCTRI,autumn.peak,1.5488393825638174 +2020,2,ELCTRI,autumn.evening,1.9008483513729915 +2020,3,GASNAT,winter.night,-7.783808997678886 +2020,3,ELCTRI,winter.night,5.18920599845259 +2020,3,CO2EMT,winter.night,397.98615405132136 +2020,3,GASNAT,winter.day,-10.010898915527836 +2020,3,ELCTRI,winter.day,6.673932610351891 +2020,3,CO2EMT,winter.day,511.8572615509382 +2020,3,GASNAT,winter.peak,-3.2303154358423316 +2020,3,ELCTRI,winter.peak,2.1535436238948877 +2020,3,CO2EMT,winter.peak,165.1660282346184 +2020,3,GASNAT,winter.evening,-4.3950895262359 +2020,3,ELCTRI,winter.evening,2.930059684157267 +2020,3,CO2EMT,winter.evening,224.72092747644155 +2020,3,GASNAT,peak.night,-8.382740663321101 +2020,3,ELCTRI,peak.night,5.5884937755474 +2020,3,CO2EMT,peak.night,428.6095301156078 +2020,3,GASNAT,peak.day,-11.066925767990735 +2020,3,ELCTRI,peak.day,7.377950511993824 +2020,3,CO2EMT,peak.day,565.8519145173663 +2020,3,GASNAT,peak.peak,-3.520722822271213 +2020,3,ELCTRI,peak.peak,2.3471485481808085 +2020,3,CO2EMT,peak.peak,180.0145579027271 +2020,3,GASNAT,peak.evening,-4.790137538321099 +2020,3,ELCTRI,peak.evening,3.1934250255473997 +2020,3,CO2EMT,peak.evening,244.9197323343578 +2020,3,GASNAT,summer.night,-0.16059014091417692 +2020,3,ELCTRI,summer.night,0.10706009394278461 +2020,3,CO2EMT,summer.night,8.210973904941865 +2020,3,GASNAT,summer.day,-0.0 +2020,3,ELCTRI,summer.day,0.0 +2020,3,CO2EMT,summer.day,0.0 +2020,3,GASNAT,summer.peak,-0.0 +2020,3,ELCTRI,summer.peak,0.0 +2020,3,CO2EMT,summer.peak,0.0 +2020,3,GASNAT,summer.evening,-0.0 +2020,3,ELCTRI,summer.evening,0.0 +2020,3,CO2EMT,summer.evening,0.0 +2020,3,GASNAT,autumn.night,-8.382740663321101 +2020,3,ELCTRI,autumn.night,5.5884937755474 +2020,3,CO2EMT,autumn.night,428.6095301156078 +2020,3,GASNAT,autumn.day,-11.62133986260287 +2020,3,ELCTRI,autumn.day,7.7475599084019136 +2020,3,CO2EMT,autumn.day,594.1991071748847 +2020,3,GASNAT,autumn.peak,-3.592603125 +2020,3,ELCTRI,autumn.peak,2.39506875 +2020,3,CO2EMT,autumn.peak,183.68979778124998 +2020,3,GASNAT,autumn.evening,-4.790137538321099 +2020,3,ELCTRI,autumn.evening,3.1934250255473997 +2020,3,CO2EMT,autumn.evening,244.9197323343578 +2020,4,GASNAT,winter.night,-36.31286857370999 +2020,4,RSHEAT,winter.night,31.576407455399995 +2020,4,CO2EMT,winter.night,1856.6769701737921 +2020,4,GASNAT,winter.day,-193.26973453857997 +2020,4,RSHEAT,winter.day,168.06063872919998 +2020,4,CO2EMT,winter.day,9881.881526957595 +2020,4,GASNAT,winter.peak,-104.21872974118598 +2020,4,RSHEAT,winter.peak,90.62498238363999 +2020,4,CO2EMT,winter.peak,5328.7036516668395 +2020,4,GASNAT,winter.evening,-62.62146996467999 +2020,4,RSHEAT,winter.evening,54.453452143199996 +2020,4,CO2EMT,winter.evening,3201.8357592940883 +2020,4,GASNAT,peak.night,-19.52227251698524 +2020,4,RSHEAT,peak.night,16.975889145204558 +2020,4,CO2EMT,peak.night,998.1737937934555 +2020,4,GASNAT,peak.day,-82.18418839575197 +2020,4,RSHEAT,peak.day,71.46451164847998 +2020,4,CO2EMT,peak.day,4202.077552674799 +2020,4,GASNAT,peak.peak,-58.291277616306004 +2020,4,RSHEAT,peak.peak,50.688067492440005 +2020,4,CO2EMT,peak.peak,2980.433024521726 +2020,4,GASNAT,peak.evening,-26.943315907602276 +2020,4,RSHEAT,peak.evening,23.428970354436764 +2020,4,CO2EMT,peak.evening,1377.6117423557046 +2020,4,GASNAT,summer.night,-0.0 +2020,4,RSHEAT,summer.night,0.0 +2020,4,CO2EMT,summer.night,0.0 +2020,4,GASNAT,summer.day,-0.0 +2020,4,RSHEAT,summer.day,0.0 +2020,4,CO2EMT,summer.day,0.0 +2020,4,GASNAT,summer.peak,-0.0 +2020,4,RSHEAT,summer.peak,0.0 +2020,4,CO2EMT,summer.peak,0.0 +2020,4,GASNAT,summer.evening,-0.0 +2020,4,RSHEAT,summer.evening,0.0 +2020,4,CO2EMT,summer.evening,0.0 +2020,4,GASNAT,autumn.night,-9.10896122967321 +2020,4,RSHEAT,autumn.night,7.920835851889748 +2020,4,CO2EMT,autumn.night,465.7411876731913 +2020,4,GASNAT,autumn.day,-56.69422338503598 +2020,4,RSHEAT,autumn.day,49.29932468263999 +2020,4,CO2EMT,autumn.day,2898.7756416768902 +2020,4,GASNAT,autumn.peak,-44.38022605095635 +2020,4,RSHEAT,autumn.peak,38.591500913875095 +2020,4,CO2EMT,autumn.peak,2269.160957985399 +2020,4,GASNAT,autumn.evening,-17.037675338040994 +2020,4,RSHEAT,autumn.evening,14.815369859166085 +2020,4,CO2EMT,autumn.evening,871.1363400340363 +2020,5,ELCTRI,winter.night,-9.624518793997803 +2020,5,RSHEAT,winter.night,29.165208466660005 +2020,5,ELCTRI,winter.day,-13.749312543997803 +2020,5,RSHEAT,winter.day,41.664583466660005 +2020,5,ELCTRI,winter.peak,-4.12479375 +2020,5,RSHEAT,winter.peak,12.499375 +2020,5,ELCTRI,winter.evening,-5.499725043997801 +2020,5,RSHEAT,winter.evening,16.66583346666 +2020,5,ELCTRI,peak.night,-8.439766292831097 +2020,5,RSHEAT,peak.night,25.575049372215442 +2020,5,ELCTRI,peak.day,-13.749312543997803 +2020,5,RSHEAT,peak.day,41.664583466660005 +2020,5,ELCTRI,peak.peak,-4.12479375 +2020,5,RSHEAT,peak.peak,12.499375 +2020,5,ELCTRI,peak.evening,-4.91826889936247 +2020,5,RSHEAT,peak.evening,14.903845149583242 +2020,5,ELCTRI,summer.night,-1.7439017181564 +2020,5,RSHEAT,summer.night,5.28455066108 +2020,5,ELCTRI,summer.day,-2.9055524196533997 +2020,5,RSHEAT,summer.day,8.80470430198 +2020,5,ELCTRI,summer.peak,-0.9567924409494001 +2020,5,RSHEAT,summer.peak,2.89937103318 +2020,5,ELCTRI,summer.evening,-0.7124084843502 +2020,5,RSHEAT,summer.evening,2.1588135889399998 +2020,5,ELCTRI,autumn.night,-8.791775241529585 +2020,5,RSHEAT,autumn.night,26.641743156150255 +2020,5,ELCTRI,autumn.day,-13.749312543997803 +2020,5,RSHEAT,autumn.day,41.664583466660005 +2020,5,ELCTRI,autumn.peak,-3.9439081325638172 +2020,5,RSHEAT,autumn.peak,11.9512367653449 +2020,5,ELCTRI,autumn.evening,-5.094273376920391 +2020,5,RSHEAT,autumn.evening,15.437192051273913 +2030,0,GASPRD,winter.night,0.0 +2030,0,CO2EMT,winter.night,0.0 +2030,0,GASPRD,winter.day,209.007363584943 +2030,0,CO2EMT,winter.day,1068.6546500098136 +2030,0,GASPRD,winter.peak,125.070625 +2030,0,CO2EMT,winter.peak,639.486105625 +2030,0,GASPRD,winter.evening,166.76083466742 +2030,0,CO2EMT,winter.evening,852.6481476545185 +2030,0,GASPRD,peak.night,0.0 +2030,0,CO2EMT,peak.night,0.0 +2030,0,GASPRD,peak.day,0.0 +2030,0,CO2EMT,peak.day,0.0 +2030,0,GASPRD,peak.peak,88.48378530453337 +2030,0,CO2EMT,peak.peak,452.41759426207915 +2030,0,GASPRD,peak.evening,166.76083466742 +2030,0,CO2EMT,peak.evening,852.6481476545185 +2030,0,GASPRD,summer.night,0.0 +2030,0,CO2EMT,summer.night,0.0 +2030,0,GASPRD,summer.day,0.0 +2030,0,CO2EMT,summer.day,0.0 +2030,0,GASPRD,summer.peak,0.0 +2030,0,CO2EMT,summer.peak,0.0 +2030,0,GASPRD,summer.evening,0.4647922208098859 +2030,0,CO2EMT,summer.evening,2.3764826250009468 +2030,0,GASPRD,autumn.night,0.0 +2030,0,CO2EMT,autumn.night,0.0 +2030,0,GASPRD,autumn.day,0.0 +2030,0,CO2EMT,autumn.day,0.0 +2030,0,GASPRD,autumn.peak,20.534211026060348 +2030,0,CO2EMT,autumn.peak,104.99142097624657 +2030,0,GASPRD,autumn.evening,166.76083466742 +2030,0,CO2EMT,autumn.evening,852.6481476545185 +2030,1,GASPRD,winter.night,-0.0 +2030,1,GASNAT,winter.night,0.0 +2030,1,CO2EMT,winter.night,0.0 +2030,1,GASPRD,winter.day,-211.26949380361748 +2030,1,GASNAT,winter.day,201.2090417177309 +2030,1,CO2EMT,winter.day,514.3909151513791 +2030,1,GASPRD,winter.peak,-124.10114062500001 +2030,1,GASNAT,winter.peak,118.1915625 +2030,1,CO2EMT,winter.peak,302.15672953125005 +2030,1,GASPRD,winter.evening,-165.4681888237455 +2030,1,GASNAT,winter.evening,157.58875126070998 +2030,1,CO2EMT,winter.evening,402.87564259800513 +2030,1,GASPRD,peak.night,-0.0 +2030,1,GASNAT,peak.night,0.0 +2030,1,CO2EMT,peak.night,0.0 +2030,1,GASPRD,peak.day,-0.0 +2030,1,GASNAT,peak.day,0.0 +2030,1,CO2EMT,peak.day,0.0 +2030,1,GASPRD,peak.peak,-89.77643114820788 +2030,1,GASNAT,peak.peak,85.50136299829322 +2030,1,CO2EMT,peak.peak,218.58423450513664 +2030,1,GASPRD,peak.evening,-165.4681888237455 +2030,1,GASNAT,peak.evening,157.58875126070998 +2030,1,CO2EMT,peak.evening,402.87564259800513 +2030,1,GASPRD,summer.night,-0.0 +2030,1,GASNAT,summer.night,0.0 +2030,1,CO2EMT,summer.night,0.0 +2030,1,GASPRD,summer.day,-0.0 +2030,1,GASNAT,summer.day,0.0 +2030,1,CO2EMT,summer.day,0.0 +2030,1,GASPRD,summer.peak,-0.0 +2030,1,GASNAT,summer.peak,0.0 +2030,1,CO2EMT,summer.peak,0.0 +2030,1,GASPRD,summer.evening,-0.4647922208098859 +2030,1,GASNAT,summer.evening,0.44265925791417704 +2030,1,CO2EMT,summer.evening,1.1316583928575936 +2030,1,GASPRD,autumn.night,-0.0 +2030,1,GASNAT,autumn.night,0.0 +2030,1,CO2EMT,autumn.night,0.0 +2030,1,GASPRD,autumn.day,-0.0 +2030,1,GASNAT,autumn.day,0.0 +2030,1,CO2EMT,autumn.day,0.0 +2030,1,GASPRD,autumn.peak,-21.826856869734865 +2030,1,GASNAT,autumn.peak,20.78748273308082 +2030,1,CO2EMT,autumn.peak,53.143199607121126 +2030,1,GASPRD,autumn.evening,-165.4681888237455 +2030,1,GASNAT,autumn.evening,157.58875126070998 +2030,1,CO2EMT,autumn.evening,402.87564259800513 +2030,2,ELCTRI,winter.night,4.435312795545212 +2030,2,ELCTRI,winter.day,7.075379933645912 +2030,2,ELCTRI,winter.peak,1.9712501261051125 +2030,2,ELCTRI,winter.evening,2.5696653598405335 +2030,2,ELCTRI,peak.night,2.851272517283696 +2030,2,ELCTRI,peak.day,6.3713620320039785 +2030,2,ELCTRI,peak.peak,1.7776452018191917 +2030,2,ELCTRI,peak.evening,1.72484387381507 +2030,2,ELCTRI,summer.night,1.6368416242136155 +2030,2,ELCTRI,summer.day,3.2188600626534005 +2030,2,ELCTRI,summer.peak,1.0599640039494003 +2030,2,ELCTRI,summer.evening,0.7892279633502001 +2030,2,ELCTRI,autumn.night,3.203281465982185 +2030,2,ELCTRI,autumn.day,6.001752635595889 +2030,2,ELCTRI,autumn.peak,1.5488393825638174 +2030,2,ELCTRI,autumn.evening,1.9008483513729915 +2030,3,GASNAT,winter.night,-7.783808997678886 +2030,3,ELCTRI,winter.night,5.18920599845259 +2030,3,CO2EMT,winter.night,397.98615405132136 +2030,3,GASNAT,winter.day,-10.010898915527836 +2030,3,ELCTRI,winter.day,6.673932610351891 +2030,3,CO2EMT,winter.day,511.8572615509382 +2030,3,GASNAT,winter.peak,-2.4686888314311712 +2030,3,ELCTRI,winter.peak,1.6457925542874474 +2030,3,CO2EMT,winter.peak,126.22405995107577 +2030,3,GASNAT,winter.evening,-3.379587378897002 +2030,3,ELCTRI,winter.evening,2.2530582525980014 +2030,3,CO2EMT,winter.evening,172.7983026830037 +2030,3,GASNAT,peak.night,-8.382740663321101 +2030,3,ELCTRI,peak.night,5.5884937755474 +2030,3,CO2EMT,peak.night,428.6095301156078 +2030,3,GASNAT,peak.day,-11.066925767990735 +2030,3,ELCTRI,peak.day,7.377950511993824 +2030,3,CO2EMT,peak.day,565.8519145173663 +2030,3,GASNAT,peak.peak,-3.520722822271213 +2030,3,ELCTRI,peak.peak,2.3471485481808085 +2030,3,CO2EMT,peak.peak,180.0145579027271 +2030,3,GASNAT,peak.evening,-4.646819607935197 +2030,3,ELCTRI,peak.evening,3.097879738623465 +2030,3,CO2EMT,peak.evening,237.59188655372662 +2030,3,GASNAT,summer.night,-0.0 +2030,3,ELCTRI,summer.night,0.0 +2030,3,CO2EMT,summer.night,0.0 +2030,3,GASNAT,summer.day,-0.0 +2030,3,ELCTRI,summer.day,0.0 +2030,3,CO2EMT,summer.day,0.0 +2030,3,GASNAT,summer.peak,-0.0 +2030,3,ELCTRI,summer.peak,0.0 +2030,3,CO2EMT,summer.peak,0.0 +2030,3,GASNAT,summer.evening,-0.0 +2030,3,ELCTRI,summer.evening,0.0 +2030,3,CO2EMT,summer.evening,0.0 +2030,3,GASNAT,autumn.night,-7.854727240273366 +2030,3,ELCTRI,autumn.night,5.236484826848911 +2030,3,CO2EMT,autumn.night,401.6122037951772 +2030,3,GASNAT,autumn.day,-11.62133986260287 +2030,3,ELCTRI,autumn.day,7.7475599084019136 +2030,3,CO2EMT,autumn.day,594.1991071748847 +2030,3,GASNAT,autumn.peak,-3.592603125 +2030,3,ELCTRI,autumn.peak,2.39506875 +2030,3,CO2EMT,autumn.peak,183.68979778124998 +2030,3,GASNAT,autumn.evening,-4.382812891598315 +2030,3,ELCTRI,autumn.evening,2.9218752610655434 +2030,3,CO2EMT,autumn.evening,224.09322314742184 +2030,4,GASNAT,winter.night,-43.845148578709995 +2030,4,RSHEAT,winter.night,38.1262161554 +2030,4,CO2EMT,winter.night,2241.8024468294425 +2030,4,GASNAT,winter.day,-219.27676569358 +2030,4,RSHEAT,winter.day,190.6754484292 +2030,4,CO2EMT,winter.day,11211.621029912745 +2030,4,GASNAT,winter.peak,-104.21874999999999 +2030,4,RSHEAT,winter.peak,90.625 +2030,4,CO2EMT,winter.peak,5328.704687500001 +2030,4,GASNAT,winter.evening,-71.44063561968 +2030,4,RSHEAT,winter.evening,62.1222918432 +2030,4,CO2EMT,winter.evening,3652.7596992342387 +2030,4,GASNAT,peak.night,-0.0 +2030,4,RSHEAT,peak.night,0.0 +2030,4,CO2EMT,peak.night,0.0 +2030,4,GASNAT,peak.day,-96.21279149075198 +2030,4,RSHEAT,peak.day,83.66329694848 +2030,4,CO2EMT,peak.day,4919.3600289221495 +2030,4,GASNAT,peak.peak,-66.12685298630602 +2030,4,RSHEAT,peak.peak,57.50161129244002 +2030,4,CO2EMT,peak.peak,3381.0659931898267 +2030,4,GASNAT,peak.evening,-29.670500177964005 +2030,4,RSHEAT,peak.evening,25.800434937360006 +2030,4,CO2EMT,peak.evening,1517.0526740992998 +2030,4,GASNAT,summer.night,-0.0 +2030,4,RSHEAT,summer.night,0.0 +2030,4,CO2EMT,summer.night,0.0 +2030,4,GASNAT,summer.day,-0.0 +2030,4,RSHEAT,summer.day,0.0 +2030,4,CO2EMT,summer.day,0.0 +2030,4,GASNAT,summer.peak,-0.0 +2030,4,RSHEAT,summer.peak,0.0 +2030,4,CO2EMT,summer.peak,0.0 +2030,4,GASNAT,summer.evening,-0.0 +2030,4,RSHEAT,summer.evening,0.0 +2030,4,CO2EMT,summer.evening,0.0 +2030,4,GASNAT,autumn.night,-0.0 +2030,4,RSHEAT,autumn.night,0.0 +2030,4,CO2EMT,autumn.night,0.0 +2030,4,GASNAT,autumn.day,-67.97422666003601 +2030,4,RSHEAT,autumn.day,59.10802318264001 +2030,4,CO2EMT,autumn.day,3475.5222091276414 +2030,4,GASNAT,autumn.peak,-50.017432516103 +2030,4,RSHEAT,autumn.peak,43.49341957922 +2030,4,CO2EMT,autumn.peak,2557.391324548347 +2030,4,GASNAT,autumn.evening,-19.376214580347 +2030,4,RSHEAT,autumn.evening,16.84888224378 +2030,4,CO2EMT,autumn.evening,990.7058514931422 +2030,5,ELCTRI,winter.night,-9.624518793997803 +2030,5,RSHEAT,winter.night,29.165208466660005 +2030,5,ELCTRI,winter.day,-13.749312543997803 +2030,5,RSHEAT,winter.day,41.664583466660005 +2030,5,ELCTRI,winter.peak,-4.12479375 +2030,5,RSHEAT,winter.peak,12.499375 +2030,5,ELCTRI,winter.evening,-5.499725043997801 +2030,5,RSHEAT,winter.evening,16.66583346666 +2030,5,ELCTRI,peak.night,-9.624518793997803 +2030,5,RSHEAT,peak.night,29.165208466660005 +2030,5,ELCTRI,peak.day,-13.749312543997803 +2030,5,RSHEAT,peak.day,41.664583466660005 +2030,5,ELCTRI,peak.peak,-4.12479375 +2030,5,RSHEAT,peak.peak,12.499375 +2030,5,ELCTRI,peak.evening,-5.499725043997801 +2030,5,RSHEAT,peak.evening,16.66583346666 +2030,5,ELCTRI,summer.night,-1.9319477961564002 +2030,5,RSHEAT,summer.night,5.85438726108 +2030,5,ELCTRI,summer.day,-3.2188600626534005 +2030,5,RSHEAT,summer.day,9.75412140198 +2030,5,ELCTRI,summer.peak,-1.0599640039494003 +2030,5,RSHEAT,summer.peak,3.2120121331800005 +2030,5,ELCTRI,summer.evening,-0.7892279633502001 +2030,5,RSHEAT,summer.evening,2.39159988894 +2030,5,ELCTRI,autumn.night,-9.624518793997803 +2030,5,RSHEAT,autumn.night,29.165208466660005 +2030,5,ELCTRI,autumn.day,-13.749312543997803 +2030,5,RSHEAT,autumn.day,41.664583466660005 +2030,5,ELCTRI,autumn.peak,-4.12479375 +2030,5,RSHEAT,autumn.peak,12.499375 +2030,5,ELCTRI,autumn.evening,-5.499725043997801 +2030,5,RSHEAT,autumn.evening,16.66583346666 +2030,6,GASNAT,winter.night,-0.0 +2030,6,RSHEAT,winter.night,0.0 +2030,6,CO2EMT,winter.night,0.0 +2030,6,GASNAT,winter.day,-0.0 +2030,6,RSHEAT,winter.day,0.0 +2030,6,CO2EMT,winter.day,0.0 +2030,6,GASNAT,winter.peak,-12.787942711186016 +2030,6,RSHEAT,winter.peak,11.119950183640015 +2030,6,CO2EMT,winter.peak,653.8475108229411 +2030,6,GASNAT,winter.evening,-0.0 +2030,6,RSHEAT,winter.evening,0.0 +2030,6,CO2EMT,winter.evening,0.0 +2030,6,GASNAT,peak.night,-20.670129843374 +2030,6,RSHEAT,peak.night,17.97402595076 +2030,6,CO2EMT,peak.night,1056.8637388917127 +2030,6,GASNAT,peak.day,-0.0 +2030,6,RSHEAT,peak.day,0.0 +2030,6,CO2EMT,peak.day,0.0 +2030,6,GASNAT,peak.peak,-0.0 +2030,6,RSHEAT,peak.peak,0.0 +2030,6,CO2EMT,peak.peak,0.0 +2030,6,GASNAT,peak.evening,-0.0 +2030,6,RSHEAT,peak.evening,0.0 +2030,6,CO2EMT,peak.evening,0.0 +2030,6,GASNAT,summer.night,-0.0 +2030,6,RSHEAT,summer.night,0.0 +2030,6,CO2EMT,summer.night,0.0 +2030,6,GASNAT,summer.day,-0.0 +2030,6,RSHEAT,summer.day,0.0 +2030,6,CO2EMT,summer.day,0.0 +2030,6,GASNAT,summer.peak,-0.0 +2030,6,RSHEAT,summer.peak,0.0 +2030,6,CO2EMT,summer.peak,0.0 +2030,6,GASNAT,summer.evening,-0.0 +2030,6,RSHEAT,summer.evening,0.0 +2030,6,CO2EMT,summer.evening,0.0 +2030,6,GASNAT,autumn.night,-10.492917792587003 +2030,6,RSHEAT,autumn.night,9.124276341380003 +2030,6,CO2EMT,autumn.night,536.5028867349736 +2030,6,GASNAT,autumn.day,-0.0 +2030,6,RSHEAT,autumn.day,0.0 +2030,6,CO2EMT,autumn.day,0.0 +2030,6,GASNAT,autumn.peak,-0.0 +2030,6,RSHEAT,autumn.peak,0.0 +2030,6,CO2EMT,autumn.peak,0.0 +2030,6,GASNAT,autumn.evening,-0.0 +2030,6,RSHEAT,autumn.evening,0.0 +2030,6,CO2EMT,autumn.evening,0.0 +2030,7,GASNAT,winter.night,-0.0 +2030,7,ELCTRI,winter.night,0.0 +2030,7,CO2EMT,winter.night,0.0 +2030,7,GASNAT,winter.day,-0.0 +2030,7,ELCTRI,winter.day,0.0 +2030,7,CO2EMT,winter.day,0.0 +2030,7,GASNAT,winter.peak,-0.7616266044111606 +2030,7,ELCTRI,winter.peak,0.5077510696074404 +2030,7,CO2EMT,winter.peak,38.94196828354264 +2030,7,GASNAT,winter.evening,-1.0155021473388979 +2030,7,ELCTRI,winter.evening,0.6770014315592653 +2030,7,CO2EMT,winter.evening,51.92262479343785 +2030,7,GASNAT,peak.night,-1.7771287517500585 +2030,7,ELCTRI,peak.night,1.1847525011667057 +2030,7,CO2EMT,peak.night,90.86459307698048 +2030,7,GASNAT,peak.day,-0.0 +2030,7,ELCTRI,peak.day,0.0 +2030,7,CO2EMT,peak.day,0.0 +2030,7,GASNAT,peak.peak,-0.0 +2030,7,ELCTRI,peak.peak,0.0 +2030,7,CO2EMT,peak.peak,0.0 +2030,7,GASNAT,peak.evening,-1.0155021473388979 +2030,7,ELCTRI,peak.evening,0.6770014315592653 +2030,7,CO2EMT,peak.evening,51.92262479343785 +2030,7,GASNAT,summer.night,-0.44265925791417704 +2030,7,ELCTRI,summer.night,0.2951061719427847 +2030,7,CO2EMT,summer.night,22.63316785715187 +2030,7,GASNAT,summer.day,-0.0 +2030,7,ELCTRI,summer.day,0.0 +2030,7,CO2EMT,summer.day,0.0 +2030,7,GASNAT,summer.peak,-0.0 +2030,7,ELCTRI,summer.peak,0.0 +2030,7,CO2EMT,summer.peak,0.0 +2030,7,GASNAT,summer.evening,-0.0 +2030,7,ELCTRI,summer.evening,0.0 +2030,7,CO2EMT,summer.evening,0.0 +2030,7,GASNAT,autumn.night,-1.7771287517500585 +2030,7,ELCTRI,autumn.night,1.1847525011667057 +2030,7,CO2EMT,autumn.night,90.86459307698048 +2030,7,GASNAT,autumn.day,-0.0 +2030,7,ELCTRI,autumn.day,0.0 +2030,7,CO2EMT,autumn.day,0.0 +2030,7,GASNAT,autumn.peak,-0.2713284261542747 +2030,7,ELCTRI,autumn.peak,0.18088561743618314 +2030,7,CO2EMT,autumn.peak,13.873022429268065 +2030,7,GASNAT,autumn.evening,-1.0155021473388979 +2030,7,ELCTRI,autumn.evening,0.6770014315592653 +2030,7,CO2EMT,autumn.evening,51.92262479343785 +2040,0,GASPRD,winter.night,0.0 +2040,0,CO2EMT,winter.night,0.0 +2040,0,GASPRD,winter.day,360.9639698231197 +2040,0,CO2EMT,winter.day,1845.6087777056114 +2040,0,GASPRD,winter.peak,125.070625 +2040,0,CO2EMT,winter.peak,639.486105625 +2040,0,GASPRD,winter.evening,166.76083466742 +2040,0,CO2EMT,winter.evening,852.6481476545185 +2040,0,GASPRD,peak.night,0.0 +2040,0,CO2EMT,peak.night,0.0 +2040,0,GASPRD,peak.day,85.71569060312174 +2040,0,CO2EMT,peak.day,438.2643260537615 +2040,0,GASPRD,peak.peak,125.070625 +2040,0,CO2EMT,peak.peak,639.486105625 +2040,0,GASPRD,peak.evening,166.76083466742 +2040,0,CO2EMT,peak.evening,852.6481476545185 +2040,0,GASPRD,summer.night,0.0 +2040,0,CO2EMT,summer.night,0.0 +2040,0,GASPRD,summer.day,0.0 +2040,0,CO2EMT,summer.day,0.0 +2040,0,GASPRD,summer.peak,0.0 +2040,0,CO2EMT,summer.peak,0.0 +2040,0,GASPRD,summer.evening,28.10673815560485 +2040,0,CO2EMT,summer.evening,143.70975218960763 +2040,0,GASPRD,autumn.night,0.0 +2040,0,CO2EMT,autumn.night,0.0 +2040,0,GASPRD,autumn.day,11.030571054582595 +2040,0,CO2EMT,autumn.day,56.39930980208081 +2040,0,GASPRD,autumn.peak,125.070625 +2040,0,CO2EMT,autumn.peak,639.486105625 +2040,0,GASPRD,autumn.evening,166.76083466742 +2040,0,CO2EMT,autumn.evening,852.6481476545185 +2040,1,GASPRD,winter.night,-289.5693294487456 +2040,1,GASNAT,winter.night,275.78031376071004 +2040,1,CO2EMT,winter.night,705.0323721292552 +2040,1,GASPRD,winter.day,-73.65677059304869 +2040,1,GASNAT,winter.day,70.14930532671303 +2040,1,CO2EMT,winter.day,179.33669906774188 +2040,1,GASPRD,winter.peak,-124.10114062500001 +2040,1,GASNAT,winter.peak,118.1915625 +2040,1,CO2EMT,winter.peak,302.15672953125005 +2040,1,GASPRD,winter.evening,-165.4681888237455 +2040,1,GASNAT,winter.evening,157.58875126070998 +2040,1,CO2EMT,winter.evening,402.87564259800513 +2040,1,GASPRD,peak.night,-0.0 +2040,1,GASNAT,peak.night,0.0 +2040,1,CO2EMT,peak.night,0.0 +2040,1,GASPRD,peak.day,-87.97782082179623 +2040,1,GASNAT,peak.day,83.78840078266308 +2040,1,CO2EMT,peak.day,214.20504660087818 +2040,1,GASPRD,peak.peak,-124.10114062500001 +2040,1,GASNAT,peak.peak,118.1915625 +2040,1,CO2EMT,peak.peak,302.15672953125005 +2040,1,GASPRD,peak.evening,-165.4681888237455 +2040,1,GASNAT,peak.evening,157.58875126070998 +2040,1,CO2EMT,peak.evening,402.87564259800513 +2040,1,GASPRD,summer.night,-0.0 +2040,1,GASNAT,summer.night,0.0 +2040,1,CO2EMT,summer.night,0.0 +2040,1,GASPRD,summer.day,-0.0 +2040,1,GASNAT,summer.day,0.0 +2040,1,CO2EMT,summer.day,0.0 +2040,1,GASPRD,summer.peak,-0.0 +2040,1,GASNAT,summer.peak,0.0 +2040,1,CO2EMT,summer.peak,0.0 +2040,1,GASPRD,summer.evening,-28.10673815560485 +2040,1,GASNAT,summer.evening,26.768322052957 +2040,1,CO2EMT,summer.evening,68.43321532838458 +2040,1,GASPRD,autumn.night,-0.0 +2040,1,GASNAT,autumn.night,0.0 +2040,1,CO2EMT,autumn.night,0.0 +2040,1,GASPRD,autumn.day,-13.292701273257066 +2040,1,GASNAT,autumn.day,12.659715498340063 +2040,1,CO2EMT,autumn.day,32.36456267150637 +2040,1,GASPRD,autumn.peak,-124.10114062500001 +2040,1,GASNAT,autumn.peak,118.1915625 +2040,1,CO2EMT,autumn.peak,302.15672953125005 +2040,1,GASPRD,autumn.evening,-165.4681888237455 +2040,1,GASNAT,autumn.evening,157.58875126070998 +2040,1,CO2EMT,autumn.evening,402.87564259800513 +2040,6,GASNAT,winter.night,-29.838533129172095 +2040,6,RSHEAT,winter.night,25.94655054710617 +2040,6,CO2EMT,winter.night,1525.6441988945694 +2040,6,GASNAT,winter.day,-42.62647584035811 +2040,6,RSHEAT,winter.day,37.066500730746185 +2040,6,CO2EMT,winter.day,2179.4917097175103 +2040,6,GASNAT,winter.peak,-12.787942711186016 +2040,6,RSHEAT,winter.peak,11.119950183640015 +2040,6,CO2EMT,winter.peak,653.8475108229411 +2040,6,GASNAT,winter.evening,-17.050590417986076 +2040,6,RSHEAT,winter.evening,14.826600363466154 +2040,6,CO2EMT,winter.evening,871.7966880716282 +2040,6,GASNAT,peak.night,-29.838533129172095 +2040,6,RSHEAT,peak.night,25.94655054710617 +2040,6,CO2EMT,peak.night,1525.6441988945694 +2040,6,GASNAT,peak.day,-42.62647584035811 +2040,6,RSHEAT,peak.day,37.066500730746185 +2040,6,CO2EMT,peak.day,2179.4917097175103 +2040,6,GASNAT,peak.peak,-12.787942711186016 +2040,6,RSHEAT,peak.peak,11.119950183640015 +2040,6,CO2EMT,peak.peak,653.8475108229411 +2040,6,GASNAT,peak.evening,-17.050590417986076 +2040,6,RSHEAT,peak.evening,14.826600363466154 +2040,6,CO2EMT,peak.evening,871.7966880716282 +2040,6,GASNAT,summer.night,-0.0 +2040,6,RSHEAT,summer.night,0.0 +2040,6,CO2EMT,summer.night,0.0 +2040,6,GASNAT,summer.day,-0.0 +2040,6,RSHEAT,summer.day,0.0 +2040,6,CO2EMT,summer.day,0.0 +2040,6,GASNAT,summer.peak,-0.0 +2040,6,RSHEAT,summer.peak,0.0 +2040,6,CO2EMT,summer.peak,0.0 +2040,6,GASNAT,summer.evening,-0.0 +2040,6,RSHEAT,summer.evening,0.0 +2040,6,CO2EMT,summer.evening,0.0 +2040,6,GASNAT,autumn.night,-29.838533129172095 +2040,6,RSHEAT,autumn.night,25.94655054710617 +2040,6,CO2EMT,autumn.night,1525.6441988945694 +2040,6,GASNAT,autumn.day,-42.62647584035811 +2040,6,RSHEAT,autumn.day,37.066500730746185 +2040,6,CO2EMT,autumn.day,2179.4917097175103 +2040,6,GASNAT,autumn.peak,-12.787942711186016 +2040,6,RSHEAT,autumn.peak,11.119950183640015 +2040,6,CO2EMT,autumn.peak,653.8475108229411 +2040,6,GASNAT,autumn.evening,-17.050590417986076 +2040,6,RSHEAT,autumn.evening,14.826600363466154 +2040,6,CO2EMT,autumn.evening,871.7966880716282 +2040,8,GASNAT,winter.night,-55.07888519119691 +2040,8,RSHEAT,winter.night,47.894682774953836 +2040,8,CO2EMT,winter.night,2816.183399825898 +2040,8,GASNAT,winter.day,-250.5715919948809 +2040,8,RSHEAT,winter.day,217.88834086511383 +2040,8,CO2EMT,winter.day,12811.72549869826 +2040,8,GASNAT,winter.peak,-131.38099422 +2040,8,RSHEAT,winter.peak,114.2443428 +2040,8,CO2EMT,winter.peak,6717.5102344686 +2040,8,GASNAT,winter.evening,-82.37491934335293 +2040,8,RSHEAT,winter.evening,71.63036464639386 +2040,8,CO2EMT,winter.evening,4211.829626025636 +2040,8,GASNAT,peak.night,-29.648126735860906 +2040,8,RSHEAT,peak.night,25.780979770313834 +2040,8,CO2EMT,peak.night,1515.9087200045683 +2040,8,GASNAT,peak.day,-115.5291897320529 +2040,8,RSHEAT,peak.day,100.46016498439383 +2040,8,CO2EMT,peak.day,5907.007470999865 +2040,8,GASNAT,peak.peak,-75.54876689511998 +2040,8,RSHEAT,peak.peak,65.6945799088 +2040,8,CO2EMT,peak.peak,3862.8084513474855 +2040,8,GASNAT,peak.evening,-36.53908908163693 +2040,8,RSHEAT,peak.evening,31.773120940553852 +2040,8,CO2EMT,peak.evening,1868.2436247440962 +2040,8,GASNAT,summer.night,-7.3878574402420005 +2040,8,RSHEAT,summer.night,6.424223861080001 +2040,8,CO2EMT,summer.night,377.7411509195735 +2040,8,GASNAT,summer.day,-12.309069277277 +2040,8,RSHEAT,summer.day,10.70353850198 +2040,8,CO2EMT,summer.day,629.362712147173 +2040,8,GASNAT,summer.peak,-4.053351218157 +2040,8,RSHEAT,summer.peak,3.5246532331800005 +2040,8,CO2EMT,summer.peak,207.24784778436745 +2040,8,GASNAT,summer.evening,-3.0180441172809997 +2040,8,RSHEAT,summer.evening,2.62438618894 +2040,8,CO2EMT,summer.evening,154.31259571657753 +2040,8,GASNAT,autumn.night,-18.480316070073908 +2040,8,RSHEAT,autumn.night,16.069840060933835 +2040,8,CO2EMT,autumn.night,944.898560662879 +2040,8,GASNAT,autumn.day,-84.54202508133689 +2040,8,RSHEAT,autumn.day,73.51480441855382 +2040,8,CO2EMT,autumn.day,4322.633742408756 +2040,8,GASNAT,autumn.peak,-57.871336489916985 +2040,8,RSHEAT,autumn.peak,50.32290129557999 +2040,8,CO2EMT,autumn.peak,2958.9614347294555 +2040,8,GASNAT,autumn.evening,-25.24280951901992 +2040,8,RSHEAT,autumn.evening,21.950269146973845 +2040,8,CO2EMT,autumn.evening,1290.6648507074885 diff --git a/tests/data/simple_marginal_average/commodity_prices.csv b/tests/data/simple_marginal_average/commodity_prices.csv new file mode 100644 index 000000000..e8b54b5ae --- /dev/null +++ b/tests/data/simple_marginal_average/commodity_prices.csv @@ -0,0 +1,177 @@ +milestone_year,commodity_id,region_id,time_slice,price +2020,GASPRD,GBR,winter.night,2.20452 +2020,GASPRD,GBR,winter.day,2.20452 +2020,GASPRD,GBR,winter.peak,2.20452 +2020,GASPRD,GBR,winter.evening,2.20452 +2020,GASPRD,GBR,peak.night,2.20452 +2020,GASPRD,GBR,peak.day,2.20452 +2020,GASPRD,GBR,peak.peak,2.20452 +2020,GASPRD,GBR,peak.evening,2.20452 +2020,GASPRD,GBR,summer.night,2.20452 +2020,GASPRD,GBR,summer.day,2.20452 +2020,GASPRD,GBR,summer.peak,2.20452 +2020,GASPRD,GBR,summer.evening,2.20452 +2020,GASPRD,GBR,autumn.night,2.20452 +2020,GASPRD,GBR,autumn.day,2.20452 +2020,GASPRD,GBR,autumn.peak,2.20452 +2020,GASPRD,GBR,autumn.evening,2.20452 +2020,GASNAT,GBR,winter.night,2.917006 +2020,GASNAT,GBR,winter.day,2.917006 +2020,GASNAT,GBR,winter.peak,2.917006 +2020,GASNAT,GBR,winter.evening,2.917006 +2020,GASNAT,GBR,peak.night,2.917006 +2020,GASNAT,GBR,peak.day,2.917006 +2020,GASNAT,GBR,peak.peak,2.917006 +2020,GASNAT,GBR,peak.evening,2.917006 +2020,GASNAT,GBR,summer.night,2.917006 +2020,GASNAT,GBR,summer.day,2.917006 +2020,GASNAT,GBR,summer.peak,2.917006 +2020,GASNAT,GBR,summer.evening,2.917006 +2020,GASNAT,GBR,autumn.night,2.917006 +2020,GASNAT,GBR,autumn.day,2.917006 +2020,GASNAT,GBR,autumn.peak,2.917006 +2020,GASNAT,GBR,autumn.evening,2.917006 +2020,ELCTRI,GBR,winter.night,4.49392486284889 +2020,ELCTRI,GBR,winter.day,4.085658076947112 +2020,ELCTRI,GBR,winter.peak,4.364403190041015 +2020,ELCTRI,GBR,winter.evening,4.4454065115245385 +2020,ELCTRI,GBR,peak.night,5.428001796489709 +2020,ELCTRI,GBR,peak.day,4.474567691408357 +2020,ELCTRI,GBR,peak.peak,4.720817458382394 +2020,ELCTRI,GBR,peak.evening,5.330324771477526 +2020,ELCTRI,GBR,summer.night,0.8659930792701066 +2020,ELCTRI,GBR,summer.day,0.4 +2020,ELCTRI,GBR,summer.peak,0.4 +2020,ELCTRI,GBR,summer.evening,0.4 +2020,ELCTRI,GBR,autumn.night,5.22668845785066 +2020,ELCTRI,GBR,autumn.day,4.678616608131578 +2020,ELCTRI,GBR,autumn.peak,5.011288215572924 +2020,ELCTRI,GBR,autumn.evening,5.159984632386021 +2020,RSHEAT,GBR,winter.night,5.866536900000001 +2020,RSHEAT,GBR,winter.day,5.866536900000001 +2020,RSHEAT,GBR,winter.peak,5.866536900000001 +2020,RSHEAT,GBR,winter.evening,5.866536900000001 +2020,RSHEAT,GBR,peak.night,5.866536900000001 +2020,RSHEAT,GBR,peak.day,5.866536900000001 +2020,RSHEAT,GBR,peak.peak,5.866536900000001 +2020,RSHEAT,GBR,peak.evening,5.866536900000001 +2020,RSHEAT,GBR,summer.night,2.8077919700000002 +2020,RSHEAT,GBR,summer.day,0.30200000000000005 +2020,RSHEAT,GBR,summer.peak,0.30200000000000005 +2020,RSHEAT,GBR,summer.evening,0.30200000000000005 +2020,RSHEAT,GBR,autumn.night,5.866536900000001 +2020,RSHEAT,GBR,autumn.day,5.866536900000001 +2020,RSHEAT,GBR,autumn.peak,5.866536900000001 +2020,RSHEAT,GBR,autumn.evening,5.866536900000001 +2030,GASPRD,GBR,winter.night,2.20452 +2030,GASPRD,GBR,winter.day,2.20452 +2030,GASPRD,GBR,winter.peak,2.20452 +2030,GASPRD,GBR,winter.evening,2.20452 +2030,GASPRD,GBR,peak.night,2.20452 +2030,GASPRD,GBR,peak.day,2.20452 +2030,GASPRD,GBR,peak.peak,2.20452 +2030,GASPRD,GBR,peak.evening,2.20452 +2030,GASPRD,GBR,summer.night,2.20452 +2030,GASPRD,GBR,summer.day,2.20452 +2030,GASPRD,GBR,summer.peak,2.20452 +2030,GASPRD,GBR,summer.evening,2.20452 +2030,GASPRD,GBR,autumn.night,2.20452 +2030,GASPRD,GBR,autumn.day,2.20452 +2030,GASPRD,GBR,autumn.peak,2.20452 +2030,GASPRD,GBR,autumn.evening,2.20452 +2030,GASNAT,GBR,winter.night,2.917006 +2030,GASNAT,GBR,winter.day,2.917006 +2030,GASNAT,GBR,winter.peak,2.917006 +2030,GASNAT,GBR,winter.evening,2.917006 +2030,GASNAT,GBR,peak.night,2.917006 +2030,GASNAT,GBR,peak.day,2.917006 +2030,GASNAT,GBR,peak.peak,2.917006 +2030,GASNAT,GBR,peak.evening,2.917006 +2030,GASNAT,GBR,summer.night,2.917006 +2030,GASNAT,GBR,summer.day,2.917006 +2030,GASNAT,GBR,summer.peak,2.917006 +2030,GASNAT,GBR,summer.evening,2.917006 +2030,GASNAT,GBR,autumn.night,2.917006 +2030,GASNAT,GBR,autumn.day,2.917006 +2030,GASNAT,GBR,autumn.peak,2.917006 +2030,GASNAT,GBR,autumn.evening,2.917006 +2030,ELCTRI,GBR,winter.night,4.494008482053293 +2030,ELCTRI,GBR,winter.day,4.085751451978706 +2030,ELCTRI,GBR,winter.peak,4.364403190041015 +2030,ELCTRI,GBR,winter.evening,4.445321730914881 +2030,ELCTRI,GBR,peak.night,5.743714245783479 +2030,ELCTRI,GBR,peak.day,4.474483607641838 +2030,ELCTRI,GBR,peak.peak,4.720817458382394 +2030,ELCTRI,GBR,peak.evening,5.611792523011156 +2030,ELCTRI,GBR,summer.night,1.558979015805591 +2030,ELCTRI,GBR,summer.day,0.4 +2030,ELCTRI,GBR,summer.peak,0.4 +2030,ELCTRI,GBR,summer.evening,0.4 +2030,ELCTRI,GBR,autumn.night,5.465984170112597 +2030,ELCTRI,GBR,autumn.day,4.678616608131578 +2030,ELCTRI,GBR,autumn.peak,5.141965098471764 +2030,ELCTRI,GBR,autumn.evening,5.3687785182549606 +2030,RSHEAT,GBR,winter.night,5.866536900000001 +2030,RSHEAT,GBR,winter.day,5.866536900000001 +2030,RSHEAT,GBR,winter.peak,5.866536900000001 +2030,RSHEAT,GBR,winter.evening,5.866536900000001 +2030,RSHEAT,GBR,peak.night,5.866536900000001 +2030,RSHEAT,GBR,peak.day,5.866536900000001 +2030,RSHEAT,GBR,peak.peak,5.866536900000001 +2030,RSHEAT,GBR,peak.evening,5.866536900000001 +2030,RSHEAT,GBR,summer.night,2.8077919700000002 +2030,RSHEAT,GBR,summer.day,0.30200000000000005 +2030,RSHEAT,GBR,summer.peak,0.30200000000000005 +2030,RSHEAT,GBR,summer.evening,0.30200000000000005 +2030,RSHEAT,GBR,autumn.night,5.866536900000001 +2030,RSHEAT,GBR,autumn.day,5.866536900000001 +2030,RSHEAT,GBR,autumn.peak,5.866536900000001 +2030,RSHEAT,GBR,autumn.evening,5.866536900000001 +2040,GASPRD,GBR,winter.night,2.20452 +2040,GASPRD,GBR,winter.day,2.20452 +2040,GASPRD,GBR,winter.peak,2.20452 +2040,GASPRD,GBR,winter.evening,2.20452 +2040,GASPRD,GBR,peak.night,2.20452 +2040,GASPRD,GBR,peak.day,2.20452 +2040,GASPRD,GBR,peak.peak,2.20452 +2040,GASPRD,GBR,peak.evening,2.20452 +2040,GASPRD,GBR,summer.night,2.20452 +2040,GASPRD,GBR,summer.day,2.20452 +2040,GASPRD,GBR,summer.peak,2.20452 +2040,GASPRD,GBR,summer.evening,2.20452 +2040,GASPRD,GBR,autumn.night,2.20452 +2040,GASPRD,GBR,autumn.day,2.20452 +2040,GASPRD,GBR,autumn.peak,2.20452 +2040,GASPRD,GBR,autumn.evening,2.20452 +2040,GASNAT,GBR,winter.night,2.917006 +2040,GASNAT,GBR,winter.day,2.917006 +2040,GASNAT,GBR,winter.peak,2.917006 +2040,GASNAT,GBR,winter.evening,2.917006 +2040,GASNAT,GBR,peak.night,2.917006 +2040,GASNAT,GBR,peak.day,2.917006 +2040,GASNAT,GBR,peak.peak,2.917006 +2040,GASNAT,GBR,peak.evening,2.917006 +2040,GASNAT,GBR,summer.night,2.917006 +2040,GASNAT,GBR,summer.day,2.917006 +2040,GASNAT,GBR,summer.peak,2.917006 +2040,GASNAT,GBR,summer.evening,2.917006 +2040,GASNAT,GBR,autumn.night,2.917006 +2040,GASNAT,GBR,autumn.day,2.917006 +2040,GASNAT,GBR,autumn.peak,2.917006 +2040,GASNAT,GBR,autumn.evening,2.917006 +2040,RSHEAT,GBR,winter.night,5.866536900000001 +2040,RSHEAT,GBR,winter.day,5.866536900000001 +2040,RSHEAT,GBR,winter.peak,5.866536900000001 +2040,RSHEAT,GBR,winter.evening,5.866536900000001 +2040,RSHEAT,GBR,peak.night,5.866536900000001 +2040,RSHEAT,GBR,peak.day,5.866536900000001 +2040,RSHEAT,GBR,peak.peak,5.866536900000001 +2040,RSHEAT,GBR,peak.evening,5.866536900000001 +2040,RSHEAT,GBR,summer.night,5.866536900000001 +2040,RSHEAT,GBR,summer.day,5.866536900000001 +2040,RSHEAT,GBR,summer.peak,5.866536900000001 +2040,RSHEAT,GBR,summer.evening,5.866536900000001 +2040,RSHEAT,GBR,autumn.night,5.866536900000001 +2040,RSHEAT,GBR,autumn.day,5.866536900000001 +2040,RSHEAT,GBR,autumn.peak,5.866536900000001 +2040,RSHEAT,GBR,autumn.evening,5.866536900000001 diff --git a/tests/regression.rs b/tests/regression.rs index 88a164d91..8e40aef52 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -30,7 +30,9 @@ define_regression_test!(two_regions); define_regression_test_with_patches!(simple_divisible); define_regression_test_with_patches!(simple_npv); define_regression_test_with_patches!(simple_marginal); +define_regression_test_with_patches!(simple_marginal_average); define_regression_test_with_patches!(simple_full); +define_regression_test_with_patches!(simple_full_average); define_regression_test_with_patches!(simple_ironing_out); // ------ END: regression tests ------ From 37dd6101efa2355298a7d5cda2782a0504ff68bf Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Tue, 17 Mar 2026 13:31:04 +0000 Subject: [PATCH 03/12] Fix logic to weight by output rather than activity --- src/simulation/prices.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index 0c628a4e4..db1d5b458 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -664,8 +664,6 @@ where /// according to output rather than taking the max. /// /// Candidate assets are treated the same way (i.e. take the min across candidate assets). -/// -/// TODO: existing assets should weight by OUTPUT rather than activity fn calculate_marginal_cost_average_prices<'a, I, J>( activity_for_existing: I, activity_keys_for_candidates: J, @@ -704,6 +702,13 @@ where .time_slice_level .containing_selection(time_slice); + // Marginal costs will be weighted by output (activity * coefficient) + let output_coeff = asset + .get_flow(&commodity_id) + .expect("Commodity should be an output flow for this asset") + .coeff; + let output_weight = Dimensionless((activity * output_coeff).value()); + // Accumulate marginal cost for this group, weighted by output existing_accum .entry(( @@ -712,7 +717,7 @@ where time_slice_selection, )) .or_default() - .add(marginal_cost, Dimensionless(activity.value())); + .add(marginal_cost, output_weight); } } @@ -1043,8 +1048,6 @@ where /// according to output rather than taking the max. /// /// Candidate assets are treated the same way (i.e. take the min across candidate assets). -/// -/// TODO: existing assets should weight by OUTPUT rather than activity #[allow(clippy::too_many_arguments)] fn calculate_full_cost_average_prices<'a, I, J>( activity_for_existing: I, @@ -1095,19 +1098,23 @@ where .time_slice_level .containing_selection(time_slice); + // Full costs will be weighted by output (activity * coefficient) + let output_coeff = asset + .get_flow(&commodity_id) + .expect("Commodity should be an output flow for this asset") + .coeff; + let output_weight = Dimensionless((activity * output_coeff).value()); + // Get/calculate fixed costs per flow for this asset let annual_fixed_costs_per_flow = annual_fixed_costs .entry(asset.clone()) .or_insert_with(|| asset.get_annual_fixed_costs_per_flow(annual_activity)); - // Accumulate full costs (marginal cost and fixed cost per flow), weighted by activity + // Accumulate full costs (marginal cost + fixed cost per flow), weighted by activity existing_accum .entry((commodity_id.clone(), region_id.clone(), ts_selection)) .or_default() - .add( - marginal_cost + *annual_fixed_costs_per_flow, - Dimensionless(activity.value()), - ); + .add(marginal_cost + *annual_fixed_costs_per_flow, output_weight); } } From 69cd2209239f5a2a7d8d9d0f92e04c13673e63dd Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Tue, 17 Mar 2026 13:46:49 +0000 Subject: [PATCH 04/12] Small documentation improvements --- docs/model/investment.md | 4 ++-- src/simulation/prices.rs | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/model/investment.md b/docs/model/investment.md index cc753cdab..11af81a63 100644 --- a/docs/model/investment.md +++ b/docs/model/investment.md @@ -17,9 +17,9 @@ decisions as being based on recent, known economic conditions while responding t commodity demands. A core assumption is that all commodities, except specific user-identified SVD commodities, have reliable \\( \lambda\_{c,r,t} \\) values for these economic evaluations. -When `pricing_strategy` is `shadow_prices`, these are the shadow prices for each commodity +When `pricing_strategy` is `shadow`, these are the shadow prices for each commodity \\( c \\), in each region \\( r \\), for each time slice \\( t \\), taken from the final dispatch of -the preceding MSY. When the `pricing_strategy` option is set to `scarcity_adjusted`, these are the +the preceding MSY. When the `pricing_strategy` option is set to `scarcity`, these are the shadow prices for each commodity adjusted to remove the impact of binding capacity constraints. Note: there is an option to iterate over each year so that investment decisions are based on diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index db1d5b458..78742cc3b 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -663,7 +663,8 @@ where /// Similar to `calculate_marginal_cost_prices`, but takes a weighted average across assets /// according to output rather than taking the max. /// -/// Candidate assets are treated the same way (i.e. take the min across candidate assets). +/// Candidate assets are treated the same way as in `calculate_marginal_cost_prices` (i.e. take the +/// min across candidate assets). fn calculate_marginal_cost_average_prices<'a, I, J>( activity_for_existing: I, activity_keys_for_candidates: J, @@ -1047,7 +1048,8 @@ where /// Similar to `calculate_full_cost_prices`, but takes a weighted average across assets /// according to output rather than taking the max. /// -/// Candidate assets are treated the same way (i.e. take the min across candidate assets). +/// Candidate assets are treated the same way as in `calculate_full_cost_prices` (i.e. take the min +/// across candidate assets). #[allow(clippy::too_many_arguments)] fn calculate_full_cost_average_prices<'a, I, J>( activity_for_existing: I, From 2a377c180a6f3320fa6d43be1509063e87f1b8bc Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Tue, 17 Mar 2026 14:20:20 +0000 Subject: [PATCH 05/12] Use backup approach for new strategies --- src/simulation/prices.rs | 42 ++++++++++++++----- .../simple_full_average/commodity_prices.csv | 16 +++---- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index 78742cc3b..8b4733f9c 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -680,17 +680,23 @@ where { // Accumulator map to collect marginal costs from existing assets. Collects a weighted average // for each (commodity, region, ts selection), across all contributing assets, weighted - // according to output. The granularity of the selection depends on the time slice level of the - // commodity (i.e. individual, season, year). + // according to output (with a backup weight based on potential output if there is zero + // activity across the selection). The granularity of the selection depends on the time slice + // level of the commodity (i.e. individual, season, year). let mut existing_accum: IndexMap< (CommodityID, RegionID, TimeSliceSelection), - WeightedAverageAccumulator, + WeightedAverageBackupAccumulator, > = IndexMap::new(); // Iterate over existing assets and their activities for (asset, time_slice, activity) in activity_for_existing { let region_id = asset.region_id(); + // Get activity limits: used to calculate backup potential-output weights. + let activity_limit = *asset + .get_activity_limits_for_selection(&TimeSliceSelection::Single(time_slice.clone())) + .end(); + // Iterate over the marginal costs for commodities we need prices for for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( upstream_prices, @@ -709,8 +715,10 @@ where .expect("Commodity should be an output flow for this asset") .coeff; let output_weight = Dimensionless((activity * output_coeff).value()); + let backup_output_weight = Dimensionless((activity_limit * output_coeff).value()); - // Accumulate marginal cost for this group, weighted by output + // Accumulate marginal cost for this group, weighted by output with a backup + // potential-output weight. existing_accum .entry(( commodity_id.clone(), @@ -718,7 +726,7 @@ where time_slice_selection, )) .or_default() - .add(marginal_cost, output_weight); + .add(marginal_cost, output_weight, backup_output_weight); } } @@ -1050,7 +1058,7 @@ where /// /// Candidate assets are treated the same way as in `calculate_full_cost_prices` (i.e. take the min /// across candidate assets). -#[allow(clippy::too_many_arguments)] +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] fn calculate_full_cost_average_prices<'a, I, J>( activity_for_existing: I, activity_keys_for_candidates: J, @@ -1067,11 +1075,12 @@ where { // Accumulator map to collect full costs from existing assets. Collects a weighted average // for each (commodity, region, ts selection), across all contributing assets, weighted - // according to output. The granularity of the selection depends on the time slice level of the - // commodity (i.e. individual, season, year). + // according to output (with a backup weight based on potential output if there is zero + // activity across the selection). The granularity of the selection depends on the time slice + // level of the commodity (i.e. individual, season, year). let mut existing_accum: IndexMap< (CommodityID, RegionID, TimeSliceSelection), - WeightedAverageAccumulator, + WeightedAverageBackupAccumulator, > = IndexMap::new(); // Cache of annual fixed costs per flow for each asset, to avoid recalculating @@ -1088,6 +1097,11 @@ where continue; } + // Get activity limits: used to calculate backup potential-output weights. + let activity_limit = *asset + .get_activity_limits_for_selection(&TimeSliceSelection::Single(time_slice.clone())) + .end(); + // Iterate over the marginal costs for commodities we need prices for for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( upstream_prices, @@ -1106,17 +1120,23 @@ where .expect("Commodity should be an output flow for this asset") .coeff; let output_weight = Dimensionless((activity * output_coeff).value()); + let backup_output_weight = Dimensionless((activity_limit * output_coeff).value()); // Get/calculate fixed costs per flow for this asset let annual_fixed_costs_per_flow = annual_fixed_costs .entry(asset.clone()) .or_insert_with(|| asset.get_annual_fixed_costs_per_flow(annual_activity)); - // Accumulate full costs (marginal cost + fixed cost per flow), weighted by activity + // Accumulate full costs (marginal cost + fixed cost per flow), weighted by output + // with a backup potential-output weight. existing_accum .entry((commodity_id.clone(), region_id.clone(), ts_selection)) .or_default() - .add(marginal_cost + *annual_fixed_costs_per_flow, output_weight); + .add( + marginal_cost + *annual_fixed_costs_per_flow, + output_weight, + backup_output_weight, + ); } } diff --git a/tests/data/simple_full_average/commodity_prices.csv b/tests/data/simple_full_average/commodity_prices.csv index a6bc20412..823c7fc54 100644 --- a/tests/data/simple_full_average/commodity_prices.csv +++ b/tests/data/simple_full_average/commodity_prices.csv @@ -71,14 +71,14 @@ milestone_year,commodity_id,region_id,time_slice,price 2030,GASPRD,GBR,peak.day,8.687784851194666 2030,GASPRD,GBR,peak.peak,8.687784851194666 2030,GASPRD,GBR,peak.evening,8.687784851194666 +2030,GASPRD,GBR,summer.night,8.687784851194664 +2030,GASPRD,GBR,summer.day,8.687784851194664 +2030,GASPRD,GBR,summer.peak,8.687784851194664 +2030,GASPRD,GBR,summer.evening,8.687784851194664 2030,GASPRD,GBR,autumn.night,8.687784851194666 2030,GASPRD,GBR,autumn.day,8.687784851194666 2030,GASPRD,GBR,autumn.peak,8.687784851194666 2030,GASPRD,GBR,autumn.evening,8.687784851194666 -2030,GASPRD,GBR,summer.night,3.761943018103873 -2030,GASPRD,GBR,summer.day,3.761943018103873 -2030,GASPRD,GBR,summer.peak,3.761943018103873 -2030,GASPRD,GBR,summer.evening,3.761943018103873 2030,GASNAT,GBR,winter.night,14.227541264416862 2030,GASNAT,GBR,winter.day,14.227541264416862 2030,GASNAT,GBR,winter.peak,14.227541264416862 @@ -87,14 +87,14 @@ milestone_year,commodity_id,region_id,time_slice,price 2030,GASNAT,GBR,peak.day,14.22754126441686 2030,GASNAT,GBR,peak.peak,14.22754126441686 2030,GASNAT,GBR,peak.evening,14.22754126441686 +2030,GASNAT,GBR,summer.night,14.227541264416855 +2030,GASNAT,GBR,summer.day,14.227541264416855 +2030,GASNAT,GBR,summer.peak,14.227541264416855 +2030,GASNAT,GBR,summer.evening,14.227541264416855 2030,GASNAT,GBR,autumn.night,14.22754126441686 2030,GASNAT,GBR,autumn.day,14.22754126441686 2030,GASNAT,GBR,autumn.peak,14.22754126441686 2030,GASNAT,GBR,autumn.evening,14.22754126441686 -2030,GASNAT,GBR,summer.night,5.642496281681778 -2030,GASNAT,GBR,summer.day,5.642496281681778 -2030,GASNAT,GBR,summer.peak,5.642496281681778 -2030,GASNAT,GBR,summer.evening,5.642496281681778 2030,ELCTRI,GBR,winter.night,0.4 2030,ELCTRI,GBR,winter.day,0.4 2030,ELCTRI,GBR,winter.peak,0.4 From ce7c478f47c8354fea578ef8f77ad6b46019d510 Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Tue, 17 Mar 2026 14:23:12 +0000 Subject: [PATCH 06/12] Copilot suggestions --- src/simulation/prices.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index 8b4733f9c..c7b77b97c 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -119,8 +119,8 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result let investment_order = &model.investment_order[&year]; // Iterate over investment sets in reverse order. Markets within the same set can be priced - // simultaneously, since they are independent (apart from Cycle sets when using full/marginal - // cost pricing strategies, which get flagged at the validation stage). + // simultaneously, since they are independent (apart from Cycle sets when using cost-based + // pricing strategies, which get flagged at the validation stage). for investment_set in investment_order.iter().rev() { // Partition markets by pricing strategy into a map keyed by `PricingStrategy`. // For now, commodities use a single strategy for all regions, but this may change in the future. @@ -749,7 +749,7 @@ where let region_id = asset.region_id(); // Get activity limits: used to weight marginal costs for seasonal/annual commodities - let output_limit = *asset + let activity_limit = *asset .get_activity_limits_for_selection(&TimeSliceSelection::Single(time_slice.clone())) .end(); @@ -784,7 +784,7 @@ where .or_default() .entry(asset.clone()) .or_default() - .add(marginal_cost, Dimensionless(output_limit.value())); + .add(marginal_cost, Dimensionless(activity_limit.value())); } } From 239db5aa5a64801a77e146a187dba3482851d2cd Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Tue, 17 Mar 2026 14:34:20 +0000 Subject: [PATCH 07/12] Use WeightedAverageAccumulator in WeightedAverageBackupAccumulator --- src/simulation/prices.rs | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index c7b77b97c..684e4b234 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -44,49 +44,26 @@ impl WeightedAverageAccumulator { } /// Weighted average accumulator with a backup weighting path for `MoneyPerFlow` prices. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] struct WeightedAverageBackupAccumulator { - /// The numerator of the primary weighted average, i.e. the sum of value * weight across all entries. - primary_numerator: MoneyPerFlow, - /// The denominator of the primary weighted average, i.e. the sum of weights across all entries. - primary_denominator: Dimensionless, - /// The numerator of the backup weighted average, i.e. the sum of value * weight across all entries. - backup_numerator: MoneyPerFlow, - /// The denominator of the backup weighted average, i.e. the sum of weights across all entries. - backup_denominator: Dimensionless, -} - -impl Default for WeightedAverageBackupAccumulator { - fn default() -> Self { - Self { - primary_numerator: MoneyPerFlow(0.0), - primary_denominator: Dimensionless(0.0), - backup_numerator: MoneyPerFlow(0.0), - backup_denominator: Dimensionless(0.0), - } - } + /// Primary weighted average path. + primary: WeightedAverageAccumulator, + /// Backup weighted average path. + backup: WeightedAverageAccumulator, } impl WeightedAverageBackupAccumulator { /// Add a weighted value to the accumulator with a backup weight. fn add(&mut self, value: MoneyPerFlow, weight: Dimensionless, backup_weight: Dimensionless) { - self.primary_numerator += value * weight; - self.primary_denominator += weight; - self.backup_numerator += value * backup_weight; - self.backup_denominator += backup_weight; + self.primary.add(value, weight); + self.backup.add(value, backup_weight); } /// Solve the weighted average, falling back to backup weights if needed. /// /// Returns `None` if both denominators are zero (or close to zero). fn finalise(&self) -> Option { - if self.primary_denominator > Dimensionless::EPSILON { - Some(self.primary_numerator / self.primary_denominator) - } else if self.backup_denominator > Dimensionless::EPSILON { - Some(self.backup_numerator / self.backup_denominator) - } else { - None - } + self.primary.finalise().or_else(|| self.backup.finalise()) } } From cdec4386dee8b3444dba7892aa2e5e5b817e8672 Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Wed, 18 Mar 2026 13:25:52 +0000 Subject: [PATCH 08/12] Update new strategies according to parent branch --- src/simulation/prices.rs | 80 ++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index e0c790676..ffc571dc2 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -151,16 +151,15 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result // Add prices for marginal average commodities if let Some(marginal_avg_set) = pricing_sets.get(&PricingStrategy::MarginalCostAverage) { - let marginal_avg_prices = calculate_marginal_cost_average_prices( + add_marginal_cost_average_prices( solution.iter_activity_for_existing(), solution.iter_activity_keys_for_candidates(), - &result, + &mut result, year, marginal_avg_set, &model.commodities, &model.time_slice_info, ); - result.extend(marginal_avg_prices); } // Add prices for full cost commodities @@ -185,17 +184,16 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result let annual_activities = annual_activities.get_or_insert_with(|| { calculate_annual_activities(solution.iter_activity_for_existing()) }); - let full_avg_prices = calculate_full_cost_average_prices( + add_full_cost_average_prices( solution.iter_activity_for_existing(), solution.iter_activity_keys_for_candidates(), annual_activities, - &result, + &mut result, year, full_avg_set, &model.commodities, &model.time_slice_info, ); - result.extend(full_avg_prices); } } @@ -611,23 +609,22 @@ fn add_marginal_cost_prices<'a, I, J>( } /// Calculate marginal cost prices for a set of commodities using a load-weighted average across -/// assets. +/// assets and add to an existing prices map. /// /// Similar to `calculate_marginal_cost_prices`, but takes a weighted average across assets /// according to output rather than taking the max. /// /// Candidate assets are treated the same way as in `calculate_marginal_cost_prices` (i.e. take the /// min across candidate assets). -fn calculate_marginal_cost_average_prices<'a, I, J>( +fn add_marginal_cost_average_prices<'a, I, J>( activity_for_existing: I, activity_keys_for_candidates: J, - upstream_prices: &CommodityPrices, + existing_prices: &mut CommodityPrices, year: u32, markets_to_price: &HashSet<(CommodityID, RegionID)>, commodities: &CommodityMap, time_slice_info: &TimeSliceInfo, -) -> IndexMap<(CommodityID, RegionID, TimeSliceID), MoneyPerFlow> -where +) where I: Iterator, J: Iterator, { @@ -652,7 +649,7 @@ where // Iterate over the marginal costs for commodities we need prices for for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( - upstream_prices, + existing_prices, year, time_slice, |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), @@ -708,7 +705,7 @@ where // Iterate over the marginal costs for commodities we need prices for for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( - upstream_prices, + existing_prices, year, time_slice, |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), @@ -742,22 +739,20 @@ where } // For each group, finalise per-candidate weighted averages then reduce to the min across candidates - let cand_group_prices: IndexMap<_, MoneyPerFlow> = cand_accum - .into_iter() - .filter_map(|(key, per_candidate)| { - per_candidate - .into_values() - .filter_map(|inner| inner.finalise()) - .reduce(|current, value| current.min(value)) - .map(|v| (key, v)) - }) - .collect(); + let cand_group_prices = cand_accum.into_iter().filter_map(|(key, per_candidate)| { + per_candidate + .into_values() + .filter_map(WeightedAverageAccumulator::finalise) + .reduce(|current, value| current.min(value)) + .map(|v| (key, v)) + }); - // Merge existing and candidate group prices, then expand to individual time slices + // Merge existing and candidate group prices let mut all_group_prices = group_prices; all_group_prices.extend(cand_group_prices); - expand_selection_prices(&all_group_prices, time_slice_info) + // Expand selection-level prices to individual time slices and add to the main prices map + existing_prices.extend_selection_prices(&all_group_prices, time_slice_info); } /// Calculate annual activities for each asset by summing across all time slices @@ -998,7 +993,7 @@ fn add_full_cost_prices<'a, I, J>( } /// Calculate full cost prices for a set of commodities using a load-weighted average across -/// assets. +/// assets and add to an existing prices map. /// /// Similar to `calculate_full_cost_prices`, but takes a weighted average across assets /// according to output rather than taking the max. @@ -1006,17 +1001,16 @@ fn add_full_cost_prices<'a, I, J>( /// Candidate assets are treated the same way as in `calculate_full_cost_prices` (i.e. take the min /// across candidate assets). #[allow(clippy::too_many_arguments, clippy::too_many_lines)] -fn calculate_full_cost_average_prices<'a, I, J>( +fn add_full_cost_average_prices<'a, I, J>( activity_for_existing: I, activity_keys_for_candidates: J, annual_activities: &HashMap, - upstream_prices: &CommodityPrices, + existing_prices: &mut CommodityPrices, year: u32, markets_to_price: &HashSet<(CommodityID, RegionID)>, commodities: &CommodityMap, time_slice_info: &TimeSliceInfo, -) -> IndexMap<(CommodityID, RegionID, TimeSliceID), MoneyPerFlow> -where +) where I: Iterator, J: Iterator, { @@ -1051,7 +1045,7 @@ where // Iterate over the marginal costs for commodities we need prices for for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( - upstream_prices, + existing_prices, year, time_slice, |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), @@ -1112,7 +1106,7 @@ where // Iterate over the marginal costs for commodities we need prices for for (commodity_id, marginal_cost) in asset.iter_marginal_costs_with_filter( - upstream_prices, + existing_prices, year, time_slice, |cid: &CommodityID| markets_to_price.contains(&(cid.clone(), region_id.clone())), @@ -1155,22 +1149,20 @@ where } // For each group, finalise per-candidate weighted averages then reduce to the min across candidates - let cand_group_prices: IndexMap<_, MoneyPerFlow> = cand_accum - .into_iter() - .filter_map(|(key, per_candidate)| { - per_candidate - .into_values() - .filter_map(|inner| inner.finalise()) - .reduce(|current, value| current.min(value)) - .map(|v| (key, v)) - }) - .collect(); + let cand_group_prices = cand_accum.into_iter().filter_map(|(key, per_candidate)| { + per_candidate + .into_values() + .filter_map(WeightedAverageAccumulator::finalise) + .reduce(|current, value| current.min(value)) + .map(|v| (key, v)) + }); - // Merge existing and candidate group prices, then expand to individual time slices + // Merge existing and candidate group prices let mut all_group_prices = group_prices; all_group_prices.extend(cand_group_prices); - expand_selection_prices(&all_group_prices, time_slice_info) + // Expand selection-level prices to individual time slices and add to the main prices map + existing_prices.extend_selection_prices(&all_group_prices, time_slice_info); } #[cfg(test)] From 3a5b9775ebd657c95c6a8163b92fdbaa910db771 Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Thu, 19 Mar 2026 17:27:01 +0000 Subject: [PATCH 09/12] Update schemas/input/commodities.yaml Co-authored-by: Alex Dewar --- schemas/input/commodities.yaml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/schemas/input/commodities.yaml b/schemas/input/commodities.yaml index 76b31c5a7..c12530a3c 100644 --- a/schemas/input/commodities.yaml +++ b/schemas/input/commodities.yaml @@ -30,15 +30,13 @@ fields: - name: pricing_strategy type: string enum: - [ - shadow, - marginal, - marginal_average, - full, - full_average, - scarcity, - unpriced, - ] + - shadow + - marginal + - marginal_average + - full + - full_average + - scarcity + - unpriced description: The pricing strategy for this commodity notes: | Optional. If specified, must be one of `shadow` (priced using shadow prices from supply-demand From ccf812ed6d66ec626da81d9d4e075040817c3e4e Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Thu, 19 Mar 2026 17:27:09 +0000 Subject: [PATCH 10/12] Update schemas/input/commodities.yaml Co-authored-by: Alex Dewar --- schemas/input/commodities.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schemas/input/commodities.yaml b/schemas/input/commodities.yaml index c12530a3c..38391bb6a 100644 --- a/schemas/input/commodities.yaml +++ b/schemas/input/commodities.yaml @@ -47,8 +47,8 @@ fields: production across assets), `scarcity` (priced adjusted for scarcity), or `unpriced` (not priced at all). - For `svd` and `sed` commodities, this must be one of `shadow`, `marginal`, - `marginal_average`, `full` or `full_average`. For `oth` commodities, this must be + For `svd` and `sed` commodities, it must be one of `shadow`, `marginal`, + `marginal_average`, `full` or `full_average`. For `oth` commodities, it *must* be `unpriced`. If unspecified, the commodity will use the default pricing strategy according to the commodity From df1a77952bc74e877c8201e3ffa47b1881a3d63d Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Thu, 19 Mar 2026 17:28:17 +0000 Subject: [PATCH 11/12] Update src/simulation/prices.rs Co-authored-by: Alex Dewar --- src/simulation/prices.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index 134320b70..4d73668f5 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -1025,7 +1025,7 @@ fn add_full_cost_average_prices<'a, I, J>( > = IndexMap::new(); // Cache of annual fixed costs per flow for each asset, to avoid recalculating - let mut annual_fixed_costs: HashMap<_, _> = HashMap::new(); + let mut annual_fixed_costs = HashMap::new(); // Iterate over existing assets and their activities for (asset, time_slice, activity) in activity_for_existing { From 0432a6777cfe8c1d72e899a057a61386365414b7 Mon Sep 17 00:00:00 2001 From: Tom Bland Date: Fri, 20 Mar 2026 09:51:53 +0000 Subject: [PATCH 12/12] Fix references to renamed function --- src/simulation/prices.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/simulation/prices.rs b/src/simulation/prices.rs index 4d73668f5..3ba3491d3 100644 --- a/src/simulation/prices.rs +++ b/src/simulation/prices.rs @@ -611,10 +611,10 @@ fn add_marginal_cost_prices<'a, I, J>( /// Calculate marginal cost prices for a set of commodities using a load-weighted average across /// assets and add to an existing prices map. /// -/// Similar to `calculate_marginal_cost_prices`, but takes a weighted average across assets +/// Similar to `add_marginal_cost_prices`, but takes a weighted average across assets /// according to output rather than taking the max. /// -/// Candidate assets are treated the same way as in `calculate_marginal_cost_prices` (i.e. take the +/// Candidate assets are treated the same way as in `add_marginal_cost_prices` (i.e. take the /// min across candidate assets). fn add_marginal_cost_average_prices<'a, I, J>( activity_for_existing: I, @@ -995,10 +995,10 @@ fn add_full_cost_prices<'a, I, J>( /// Calculate full cost prices for a set of commodities using a load-weighted average across /// assets and add to an existing prices map. /// -/// Similar to `calculate_full_cost_prices`, but takes a weighted average across assets +/// Similar to `add_full_cost_prices`, but takes a weighted average across assets /// according to output rather than taking the max. /// -/// Candidate assets are treated the same way as in `calculate_full_cost_prices` (i.e. take the min +/// Candidate assets are treated the same way as in `add_full_cost_prices` (i.e. take the min /// across candidate assets). #[allow(clippy::too_many_arguments, clippy::too_many_lines)] fn add_full_cost_average_prices<'a, I, J>(