Skip to content

Commit 5f9d803

Browse files
authored
Merge pull request #633 from EnergySystemsModellingLab/add-commodity-prices
Calculate commodity prices using dual values
2 parents 4473c5d + 9b8dec7 commit 5f9d803

2 files changed

Lines changed: 42 additions & 21 deletions

File tree

src/simulation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub fn run(
5252
// Dispatch optimisation
5353
let solution = perform_dispatch_optimisation(&model, &assets, year)?;
5454
let flow_map = solution.create_flow_map();
55-
let prices = CommodityPrices::from_model_and_solution(&model, &solution, &assets);
55+
let prices = CommodityPrices::from_model_and_solution(&model, &solution);
5656

5757
// Write result of dispatch optimisation to file
5858
writer.write_debug_info(year, &solution)?;

src/simulation/prices.rs

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
//! Code for updating the simulation state.
22
use super::optimisation::Solution;
3-
use crate::asset::AssetPool;
43
use crate::commodity::CommodityID;
54
use crate::model::Model;
65
use crate::region::RegionID;
76
use crate::time_slice::{TimeSliceID, TimeSliceInfo};
87
use log::warn;
9-
use std::collections::{BTreeMap, HashSet};
8+
use std::collections::{BTreeMap, HashMap, HashSet};
109

1110
/// A map relating commodity ID + region + time slice to current price (endogenous)
1211
#[derive(Default)]
1312
pub struct CommodityPrices(BTreeMap<(CommodityID, RegionID, TimeSliceID), f64>);
1413

1514
impl CommodityPrices {
16-
/// Calculate commodity prices based on the result of the dispatch optimisation.
17-
///
18-
/// Missing prices will be calculated directly from the input data
19-
pub fn from_model_and_solution(model: &Model, solution: &Solution, assets: &AssetPool) -> Self {
15+
/// Calculate commodity prices based on the result of the dispatch optimisation
16+
pub fn from_model_and_solution(model: &Model, solution: &Solution) -> Self {
2017
let mut prices = CommodityPrices::default();
21-
let commodity_regions_updated = prices.add_from_solution(solution, assets);
18+
let commodity_regions_updated = prices.add_from_solution(solution);
2219

2320
// Find commodity/region combinations not updated in last step
2421
let mut remaining_commodity_regions = HashSet::new();
@@ -44,27 +41,51 @@ impl CommodityPrices {
4441
/// # Arguments
4542
///
4643
/// * `solution` - The solution to the dispatch optimisation
47-
/// * `assets` - The asset pool
4844
///
4945
/// # Returns
5046
///
51-
/// The set of commodities for which prices were added.
52-
fn add_from_solution(
53-
&mut self,
54-
_solution: &Solution,
55-
_assets: &AssetPool,
56-
) -> HashSet<(CommodityID, RegionID)> {
57-
// **TODO:** Calculate commodity prices here:
58-
// https://github.com/EnergySystemsModellingLab/MUSE_2.0/issues/589
47+
/// The set of commodity/region pairs for which prices were added.
48+
fn add_from_solution(&mut self, solution: &Solution) -> HashSet<(CommodityID, RegionID)> {
49+
let mut commodity_regions_updated = HashSet::new();
50+
51+
// Calculate highest activity dual for each commodity/region/timeslice
52+
let mut highest_duals = HashMap::new();
53+
for (asset, time_slice, dual) in solution.iter_activity_duals() {
54+
// Iterate over all output flows
55+
for flow in asset.iter_flows().filter(|flow| flow.coeff > 0.0) {
56+
// Update the highest dual for this commodity/timeslice
57+
highest_duals
58+
.entry((
59+
flow.commodity.id.clone(),
60+
asset.region_id.clone(),
61+
time_slice.clone(),
62+
))
63+
.and_modify(|current_dual| {
64+
if dual > *current_dual {
65+
*current_dual = dual;
66+
}
67+
})
68+
.or_insert(dual);
69+
}
70+
}
71+
72+
// Add the highest activity dual for each commodity/region/timeslice to each commodity
73+
// balance dual
74+
for (commodity_id, region_id, time_slice, dual) in solution.iter_commodity_balance_duals() {
75+
let key = (commodity_id.clone(), region_id.clone(), time_slice.clone());
76+
let price = dual + highest_duals.get(&key).unwrap_or(&0.0);
77+
self.insert(commodity_id, region_id, time_slice, price);
78+
commodity_regions_updated.insert((commodity_id.clone(), region_id.clone()));
79+
}
5980

60-
HashSet::new()
81+
commodity_regions_updated
6182
}
6283

6384
/// Add prices for any commodity not updated by the dispatch step.
6485
///
6586
/// # Arguments
6687
///
67-
/// * `commodity_ids` - IDs of commodities to update
88+
/// * `commodity_regions` - Commodity/region pairs to update
6889
/// * `time_slice_info` - Information about time slices
6990
fn add_remaining<'a, I>(&mut self, commodity_regions: I, time_slice_info: &TimeSliceInfo)
7091
where
@@ -78,7 +99,7 @@ impl CommodityPrices {
7899
}
79100
}
80101

81-
/// Insert a price for the given commodity, time slice and region
102+
/// Insert a price for the given commodity, region and time slice
82103
pub fn insert(
83104
&mut self,
84105
commodity_id: &CommodityID,
@@ -94,7 +115,7 @@ impl CommodityPrices {
94115
///
95116
/// # Returns
96117
///
97-
/// An iterator of tuples containing commodity ID, time slice and price.
118+
/// An iterator of tuples containing commodity ID, region ID, time slice and price.
98119
pub fn iter(&self) -> impl Iterator<Item = (&CommodityID, &RegionID, &TimeSliceID, f64)> {
99120
self.0
100121
.iter()

0 commit comments

Comments
 (0)