muse2/simulation/investment/appraisal/
coefficients.rs1use crate::agent::ObjectiveType;
3use crate::asset::AssetRef;
4use crate::model::Model;
5use crate::simulation::PriceMap;
6use crate::simulation::prices::Prices;
7use crate::time_slice::{TimeSliceID, TimeSliceInfo};
8use crate::units::{MoneyPerActivity, MoneyPerFlow};
9use indexmap::IndexMap;
10use std::collections::HashMap;
11use std::rc::Rc;
12
13#[derive(Clone)]
20pub struct ObjectiveCoefficients {
21 pub activity_coefficients: IndexMap<TimeSliceID, MoneyPerActivity>,
23 pub market_costs: IndexMap<TimeSliceID, MoneyPerActivity>,
25}
26
27pub fn calculate_coefficients_for_assets(
41 model: &Model,
42 objective_type: &ObjectiveType,
43 assets: &[AssetRef],
44 prices: &Prices,
45 year: u32,
46) -> HashMap<AssetRef, Rc<ObjectiveCoefficients>> {
47 assets
48 .iter()
49 .map(|asset| {
50 let coefficient = calculate_coefficients_for_asset(
51 asset,
52 objective_type,
53 &model.time_slice_info,
54 prices,
55 year,
56 );
57 (asset.clone(), Rc::new(coefficient))
58 })
59 .collect()
60}
61
62pub fn calculate_coefficients_for_asset(
64 asset: &AssetRef,
65 objective_type: &ObjectiveType,
66 time_slice_info: &TimeSliceInfo,
67 prices: &Prices,
68 year: u32,
69) -> ObjectiveCoefficients {
70 const EPSILON_ACTIVITY_COEFFICIENT: MoneyPerActivity = MoneyPerActivity(f64::EPSILON * 100.0);
73
74 let mut activity_coefficients = IndexMap::new();
76 let mut market_costs = IndexMap::new();
77 let primary_output_flow = asset.primary_output().unwrap();
78 let asset_region = asset.region_id();
79 for time_slice in time_slice_info.iter_ids() {
80 let operating_cost = asset.get_operating_cost(year, time_slice);
83 let net_operating_cost =
84 -calculate_asset_revenues(asset, operating_cost, time_slice, &prices.shadow);
85
86 let fallback_cost = prices
87 .fallback
88 .get(&primary_output_flow.commodity.id, asset_region, time_slice)
89 .unwrap_or(MoneyPerFlow(0.0))
90 * primary_output_flow.coeff;
91
92 activity_coefficients.insert(
93 time_slice.clone(),
94 fallback_cost - net_operating_cost + EPSILON_ACTIVITY_COEFFICIENT,
95 );
96
97 let market_cost = match objective_type {
98 ObjectiveType::LevelisedCostOfX => {
99 calculate_asset_costs_for_lcox(asset, operating_cost, time_slice, &prices.market)
100 }
101 ObjectiveType::NetPresentValue => {
102 -calculate_asset_revenues(asset, operating_cost, time_slice, &prices.market)
103 }
104 };
105 market_costs.insert(time_slice.clone(), market_cost);
106 }
107
108 ObjectiveCoefficients {
109 activity_coefficients,
110 market_costs,
111 }
112}
113
114fn calculate_asset_revenues(
116 asset: &AssetRef,
117 operating_cost: MoneyPerActivity,
118 time_slice: &TimeSliceID,
119 prices: &PriceMap,
120) -> MoneyPerActivity {
121 let revenue_from_flows = asset.get_revenue_from_flows(prices, time_slice);
123
124 revenue_from_flows - operating_cost
126}
127
128fn calculate_asset_costs_for_lcox(
132 asset: &AssetRef,
133 operating_cost: MoneyPerActivity,
134 time_slice: &TimeSliceID,
135 prices: &PriceMap,
136) -> MoneyPerActivity {
137 let revenue_from_flows = asset.get_revenue_from_flows_excluding_primary(prices, time_slice);
139
140 operating_cost - revenue_from_flows
142}