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
12#[derive(Clone)]
18pub struct ObjectiveCoefficients {
19 pub capacity_coefficient: MoneyPerCapacity,
21 pub activity_coefficients: IndexMap<TimeSliceID, MoneyPerActivity>,
23 pub unmet_demand_coefficient: MoneyPerFlow,
25}
26
27pub fn calculate_coefficients_for_assets(
29 model: &Model,
30 objective_type: &ObjectiveType,
31 assets: &[AssetRef],
32 prices: &CommodityPrices,
33 year: u32,
34) -> HashMap<AssetRef, ObjectiveCoefficients> {
35 assets
36 .iter()
37 .map(|asset| {
38 let coefficient = match objective_type {
39 ObjectiveType::LevelisedCostOfX => calculate_coefficients_for_lcox(
40 asset,
41 &model.time_slice_info,
42 prices,
43 model.parameters.value_of_lost_load,
44 year,
45 ),
46 ObjectiveType::NetPresentValue => {
47 calculate_coefficients_for_npv(asset, &model.time_slice_info, prices, year)
48 }
49 };
50 (asset.clone(), coefficient)
51 })
52 .collect()
53}
54
55pub fn calculate_coefficients_for_lcox(
61 asset: &AssetRef,
62 time_slice_info: &TimeSliceInfo,
63 prices: &CommodityPrices,
64 value_of_lost_load: MoneyPerFlow,
65 year: u32,
66) -> ObjectiveCoefficients {
67 let capacity_coefficient = annual_fixed_cost(asset);
69
70 let mut activity_coefficients = IndexMap::new();
72 for time_slice in time_slice_info.iter_ids() {
73 let coefficient = calculate_activity_coefficient_for_lcox(asset, time_slice, prices, year);
74 activity_coefficients.insert(time_slice.clone(), coefficient);
75 }
76
77 let unmet_demand_coefficient = value_of_lost_load;
79
80 ObjectiveCoefficients {
81 capacity_coefficient,
82 activity_coefficients,
83 unmet_demand_coefficient,
84 }
85}
86
87pub fn calculate_coefficients_for_npv(
94 asset: &AssetRef,
95 time_slice_info: &TimeSliceInfo,
96 prices: &CommodityPrices,
97 year: u32,
98) -> ObjectiveCoefficients {
99 const EPSILON_ACTIVITY_COEFFICIENT: MoneyPerActivity = MoneyPerActivity(f64::EPSILON * 100.0);
102
103 let mut activity_coefficients = IndexMap::new();
105 for time_slice in time_slice_info.iter_ids() {
106 let coefficient = calculate_activity_coefficient_for_npv(asset, time_slice, prices, year);
107 activity_coefficients.insert(
108 time_slice.clone(),
109 coefficient + EPSILON_ACTIVITY_COEFFICIENT,
110 );
111 }
112
113 let unmet_demand_coefficient = MoneyPerFlow(0.0);
115
116 ObjectiveCoefficients {
117 capacity_coefficient: MoneyPerCapacity(0.0),
118 activity_coefficients,
119 unmet_demand_coefficient,
120 }
121}
122
123fn calculate_activity_coefficient_for_lcox(
125 asset: &AssetRef,
126 time_slice: &TimeSliceID,
127 prices: &CommodityPrices,
128 year: u32,
129) -> MoneyPerActivity {
130 let operating_cost = asset.get_operating_cost(year, time_slice);
133
134 let revenue_from_flows = asset.get_revenue_from_flows_excluding_primary(prices, time_slice);
136
137 operating_cost - revenue_from_flows
139}
140
141fn calculate_activity_coefficient_for_npv(
143 asset: &AssetRef,
144 time_slice: &TimeSliceID,
145 prices: &CommodityPrices,
146 year: u32,
147) -> MoneyPerActivity {
148 let operating_cost = asset.get_operating_cost(year, time_slice);
151
152 let revenue_from_flows = asset.get_revenue_from_flows(prices, time_slice);
154
155 revenue_from_flows - operating_cost
157}