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;
11use std::rc::Rc;
12
13#[derive(Clone)]
19pub struct ObjectiveCoefficients {
20 pub capacity_coefficient: MoneyPerCapacity,
22 pub activity_coefficients: IndexMap<TimeSliceID, MoneyPerActivity>,
24 pub unmet_demand_coefficient: MoneyPerFlow,
26}
27
28pub fn calculate_coefficients_for_assets(
30 model: &Model,
31 objective_type: &ObjectiveType,
32 assets: &[AssetRef],
33 prices: &CommodityPrices,
34 year: u32,
35) -> HashMap<AssetRef, Rc<ObjectiveCoefficients>> {
36 assets
37 .iter()
38 .map(|asset| {
39 let coefficient = match objective_type {
40 ObjectiveType::LevelisedCostOfX => calculate_coefficients_for_lcox(
41 asset,
42 &model.time_slice_info,
43 prices,
44 model.parameters.value_of_lost_load,
45 year,
46 ),
47 ObjectiveType::NetPresentValue => {
48 calculate_coefficients_for_npv(asset, &model.time_slice_info, prices, year)
49 }
50 };
51 (asset.clone(), Rc::new(coefficient))
52 })
53 .collect()
54}
55
56pub fn calculate_coefficients_for_lcox(
62 asset: &AssetRef,
63 time_slice_info: &TimeSliceInfo,
64 prices: &CommodityPrices,
65 value_of_lost_load: MoneyPerFlow,
66 year: u32,
67) -> ObjectiveCoefficients {
68 let capacity_coefficient = annual_fixed_cost(asset);
70
71 let mut activity_coefficients = IndexMap::new();
73 for time_slice in time_slice_info.iter_ids() {
74 let coefficient = calculate_activity_coefficient_for_lcox(asset, time_slice, prices, year);
75 activity_coefficients.insert(time_slice.clone(), coefficient);
76 }
77
78 let unmet_demand_coefficient = value_of_lost_load;
80
81 ObjectiveCoefficients {
82 capacity_coefficient,
83 activity_coefficients,
84 unmet_demand_coefficient,
85 }
86}
87
88pub fn calculate_coefficients_for_npv(
95 asset: &AssetRef,
96 time_slice_info: &TimeSliceInfo,
97 prices: &CommodityPrices,
98 year: u32,
99) -> ObjectiveCoefficients {
100 const EPSILON_ACTIVITY_COEFFICIENT: MoneyPerActivity = MoneyPerActivity(f64::EPSILON * 100.0);
103
104 let mut activity_coefficients = IndexMap::new();
106 for time_slice in time_slice_info.iter_ids() {
107 let coefficient = calculate_activity_coefficient_for_npv(asset, time_slice, prices, year);
108 activity_coefficients.insert(
109 time_slice.clone(),
110 coefficient + EPSILON_ACTIVITY_COEFFICIENT,
111 );
112 }
113
114 let unmet_demand_coefficient = MoneyPerFlow(0.0);
116
117 ObjectiveCoefficients {
118 capacity_coefficient: MoneyPerCapacity(0.0),
119 activity_coefficients,
120 unmet_demand_coefficient,
121 }
122}
123
124fn calculate_activity_coefficient_for_lcox(
126 asset: &AssetRef,
127 time_slice: &TimeSliceID,
128 prices: &CommodityPrices,
129 year: u32,
130) -> MoneyPerActivity {
131 let operating_cost = asset.get_operating_cost(year, time_slice);
134
135 let revenue_from_flows = asset.get_revenue_from_flows_excluding_primary(prices, time_slice);
137
138 operating_cost - revenue_from_flows
140}
141
142fn calculate_activity_coefficient_for_npv(
144 asset: &AssetRef,
145 time_slice: &TimeSliceID,
146 prices: &CommodityPrices,
147 year: u32,
148) -> MoneyPerActivity {
149 let operating_cost = asset.get_operating_cost(year, time_slice);
152
153 let revenue_from_flows = asset.get_revenue_from_flows(prices, time_slice);
155
156 revenue_from_flows - operating_cost
158}