muse2/simulation/investment/appraisal/
coefficients.rs1use super::costs::annual_fixed_cost;
3use crate::agent::ObjectiveType;
4use crate::asset::AssetRef;
5use crate::model::Model;
6use crate::simulation::CommodityPrices;
7use crate::time_slice::{TimeSliceID, TimeSliceInfo};
8use crate::units::{MoneyPerActivity, MoneyPerCapacity, MoneyPerFlow};
9use indexmap::IndexMap;
10use std::collections::HashMap;
11
12pub struct ObjectiveCoefficients {
17 pub capacity_coefficient: MoneyPerCapacity,
19 pub activity_coefficients: IndexMap<TimeSliceID, MoneyPerActivity>,
21 pub unmet_demand_coefficient: MoneyPerFlow,
23}
24
25pub fn calculate_coefficients_for_assets(
27 model: &Model,
28 objective_type: &ObjectiveType,
29 assets: &[AssetRef],
30 prices: &CommodityPrices,
31 year: u32,
32) -> HashMap<AssetRef, ObjectiveCoefficients> {
33 assets
34 .iter()
35 .map(|asset| {
36 let coefficient = match objective_type {
37 ObjectiveType::LevelisedCostOfX => calculate_coefficients_for_lcox(
38 asset,
39 &model.time_slice_info,
40 prices,
41 model.parameters.value_of_lost_load,
42 year,
43 ),
44 ObjectiveType::NetPresentValue => {
45 calculate_coefficients_for_npv(asset, &model.time_slice_info, prices, year)
46 }
47 };
48 (asset.clone(), coefficient)
49 })
50 .collect()
51}
52
53pub fn calculate_coefficients_for_lcox(
55 asset: &AssetRef,
56 time_slice_info: &TimeSliceInfo,
57 prices: &CommodityPrices,
58 value_of_lost_load: MoneyPerFlow,
59 year: u32,
60) -> ObjectiveCoefficients {
61 let capacity_coefficient = annual_fixed_cost(asset);
63
64 let mut activity_coefficients = IndexMap::new();
66 for time_slice in time_slice_info.iter_ids() {
67 let coefficient = calculate_activity_coefficient_for_lcox(asset, time_slice, prices, year);
68 activity_coefficients.insert(time_slice.clone(), coefficient);
69 }
70
71 let unmet_demand_coefficient = value_of_lost_load;
73
74 ObjectiveCoefficients {
75 capacity_coefficient,
76 activity_coefficients,
77 unmet_demand_coefficient,
78 }
79}
80
81pub fn calculate_coefficients_for_npv(
83 asset: &AssetRef,
84 time_slice_info: &TimeSliceInfo,
85 prices: &CommodityPrices,
86 year: u32,
87) -> ObjectiveCoefficients {
88 let capacity_coefficient = -annual_fixed_cost(asset);
90
91 let mut activity_coefficients = IndexMap::new();
93 for time_slice in time_slice_info.iter_ids() {
94 let coefficient = calculate_activity_coefficient_for_npv(asset, time_slice, prices, year);
95 activity_coefficients.insert(time_slice.clone(), coefficient);
96 }
97
98 let unmet_demand_coefficient = MoneyPerFlow(0.0);
100
101 ObjectiveCoefficients {
102 capacity_coefficient,
103 activity_coefficients,
104 unmet_demand_coefficient,
105 }
106}
107
108fn calculate_activity_coefficient_for_lcox(
110 asset: &AssetRef,
111 time_slice: &TimeSliceID,
112 prices: &CommodityPrices,
113 year: u32,
114) -> MoneyPerActivity {
115 let operating_cost = asset.get_operating_cost(year, time_slice);
118
119 let revenue_from_flows = asset.get_revenue_from_flows_excluding_primary(prices, time_slice);
121
122 operating_cost - revenue_from_flows
124}
125
126fn calculate_activity_coefficient_for_npv(
128 asset: &AssetRef,
129 time_slice: &TimeSliceID,
130 prices: &CommodityPrices,
131 year: u32,
132) -> MoneyPerActivity {
133 let operating_cost = asset.get_operating_cost(year, time_slice);
136
137 let revenue_from_flows = asset.get_revenue_from_flows(prices, time_slice);
139
140 revenue_from_flows - operating_cost
142}