muse2/simulation/
prices.rs1use super::optimisation::Solution;
3use crate::asset::AssetPool;
4use crate::commodity::CommodityID;
5use crate::model::Model;
6use crate::region::RegionID;
7use crate::time_slice::{TimeSliceID, TimeSliceInfo};
8use indexmap::IndexMap;
9use log::warn;
10use std::collections::{HashMap, HashSet};
11
12#[derive(Default)]
14pub struct CommodityPrices(IndexMap<(CommodityID, RegionID, TimeSliceID), f64>);
15
16impl CommodityPrices {
17 pub fn from_model_and_solution(model: &Model, solution: &Solution, assets: &AssetPool) -> Self {
21 let mut prices = CommodityPrices::default();
22 let commodity_regions_updated = prices.add_from_solution(solution, assets);
23
24 let mut remaining_commodity_regions = HashSet::new();
26 for commodity_id in model.commodities.keys() {
27 for region_id in model.regions.keys() {
28 let key = (commodity_id.clone(), region_id.clone());
29 if !commodity_regions_updated.contains(&key) {
30 remaining_commodity_regions.insert(key);
31 }
32 }
33 }
34
35 prices.add_remaining(remaining_commodity_regions.iter(), &model.time_slice_info);
36
37 prices
38 }
39
40 fn add_from_solution(
54 &mut self,
55 solution: &Solution,
56 assets: &AssetPool,
57 ) -> HashSet<(CommodityID, RegionID)> {
58 let mut commodity_regions_updated = HashSet::new();
59
60 let mut highest_duals = HashMap::new();
62 for (asset_id, time_slice, dual) in solution.iter_capacity_duals() {
63 let asset = assets.get(asset_id).unwrap();
64 let region_id = asset.region_id.clone();
65
66 let process_pacs = asset.process.iter_pacs();
68 for pac in process_pacs {
69 let commodity = &pac.commodity;
70
71 if pac.flow > 0.0 {
73 highest_duals
75 .entry((commodity.id.clone(), region_id.clone(), time_slice.clone()))
76 .and_modify(|current_dual| {
77 if dual > *current_dual {
78 *current_dual = dual;
79 }
80 })
81 .or_insert(dual);
82 }
83 }
84 }
85
86 for (commodity_id, region_id, time_slice, dual) in solution.iter_commodity_balance_duals() {
88 let key = (commodity_id.clone(), region_id.clone(), time_slice.clone());
89 let price = dual + highest_duals.get(&key).unwrap_or(&0.0);
90 self.insert(commodity_id, region_id, time_slice, price);
91 commodity_regions_updated.insert((commodity_id.clone(), region_id.clone()));
92 }
93
94 commodity_regions_updated
95 }
96
97 fn add_remaining<'a, I>(&mut self, commodity_regions: I, time_slice_info: &TimeSliceInfo)
104 where
105 I: Iterator<Item = &'a (CommodityID, RegionID)>,
106 {
107 for (commodity_id, region_id) in commodity_regions {
108 warn!("No prices calculated for commodity {commodity_id} in region {region_id}; setting to NaN");
109 for time_slice in time_slice_info.iter_ids() {
110 self.insert(commodity_id, region_id, time_slice, f64::NAN);
111 }
112 }
113 }
114
115 pub fn insert(
117 &mut self,
118 commodity_id: &CommodityID,
119 region_id: &RegionID,
120 time_slice: &TimeSliceID,
121 price: f64,
122 ) {
123 let key = (commodity_id.clone(), region_id.clone(), time_slice.clone());
124 self.0.insert(key, price);
125 }
126
127 pub fn iter(&self) -> impl Iterator<Item = (&CommodityID, &RegionID, &TimeSliceID, f64)> {
133 self.0
134 .iter()
135 .map(|((commodity_id, region_id, ts), price)| (commodity_id, region_id, ts, *price))
136 }
137}