use super::costs::annual_fixed_cost;
use crate::agent::ObjectiveType;
use crate::asset::AssetRef;
use crate::model::Model;
use crate::simulation::CommodityPrices;
use crate::time_slice::{TimeSliceID, TimeSliceInfo};
use crate::units::{MoneyPerActivity, MoneyPerCapacity, MoneyPerFlow};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Clone)]
pub struct ObjectiveCoefficients {
pub capacity_coefficient: MoneyPerCapacity,
pub activity_coefficients: IndexMap<TimeSliceID, MoneyPerActivity>,
pub unmet_demand_coefficient: MoneyPerFlow,
}
pub fn calculate_coefficients_for_assets(
model: &Model,
objective_type: &ObjectiveType,
assets: &[AssetRef],
prices: &CommodityPrices,
year: u32,
) -> HashMap<AssetRef, Rc<ObjectiveCoefficients>> {
assets
.iter()
.map(|asset| {
let coefficient = match objective_type {
ObjectiveType::LevelisedCostOfX => calculate_coefficients_for_lcox(
asset,
&model.time_slice_info,
prices,
model.parameters.value_of_lost_load,
year,
),
ObjectiveType::NetPresentValue => {
calculate_coefficients_for_npv(asset, &model.time_slice_info, prices, year)
}
};
(asset.clone(), Rc::new(coefficient))
})
.collect()
}
pub fn calculate_coefficients_for_lcox(
asset: &AssetRef,
time_slice_info: &TimeSliceInfo,
prices: &CommodityPrices,
value_of_lost_load: MoneyPerFlow,
year: u32,
) -> ObjectiveCoefficients {
let capacity_coefficient = annual_fixed_cost(asset);
let mut activity_coefficients = IndexMap::new();
for time_slice in time_slice_info.iter_ids() {
let coefficient = calculate_activity_coefficient_for_lcox(asset, time_slice, prices, year);
activity_coefficients.insert(time_slice.clone(), coefficient);
}
let unmet_demand_coefficient = value_of_lost_load;
ObjectiveCoefficients {
capacity_coefficient,
activity_coefficients,
unmet_demand_coefficient,
}
}
pub fn calculate_coefficients_for_npv(
asset: &AssetRef,
time_slice_info: &TimeSliceInfo,
prices: &CommodityPrices,
year: u32,
) -> ObjectiveCoefficients {
const EPSILON_ACTIVITY_COEFFICIENT: MoneyPerActivity = MoneyPerActivity(f64::EPSILON * 100.0);
let mut activity_coefficients = IndexMap::new();
for time_slice in time_slice_info.iter_ids() {
let coefficient = calculate_activity_coefficient_for_npv(asset, time_slice, prices, year);
activity_coefficients.insert(
time_slice.clone(),
coefficient + EPSILON_ACTIVITY_COEFFICIENT,
);
}
let unmet_demand_coefficient = MoneyPerFlow(0.0);
ObjectiveCoefficients {
capacity_coefficient: MoneyPerCapacity(0.0),
activity_coefficients,
unmet_demand_coefficient,
}
}
fn calculate_activity_coefficient_for_lcox(
asset: &AssetRef,
time_slice: &TimeSliceID,
prices: &CommodityPrices,
year: u32,
) -> MoneyPerActivity {
let operating_cost = asset.get_operating_cost(year, time_slice);
let revenue_from_flows = asset.get_revenue_from_flows_excluding_primary(prices, time_slice);
operating_cost - revenue_from_flows
}
fn calculate_activity_coefficient_for_npv(
asset: &AssetRef,
time_slice: &TimeSliceID,
prices: &CommodityPrices,
year: u32,
) -> MoneyPerActivity {
let operating_cost = asset.get_operating_cost(year, time_slice);
let revenue_from_flows = asset.get_revenue_from_flows(prices, time_slice);
revenue_from_flows - operating_cost
}