muse2/simulation/investment/
appraisal.rs1use super::DemandMap;
3use crate::agent::ObjectiveType;
4use crate::asset::AssetRef;
5use crate::commodity::Commodity;
6use crate::finance::{lcox, profitability_index};
7use crate::model::Model;
8use crate::simulation::prices::ReducedCosts;
9use crate::units::Capacity;
10use anyhow::Result;
11
12mod coefficients;
13mod constraints;
14mod costs;
15mod optimisation;
16use coefficients::{calculate_coefficients_for_lcox, calculate_coefficients_for_npv};
17use optimisation::perform_optimisation;
18
19pub struct AppraisalOutput {
21 pub asset: AssetRef,
23 pub capacity: Capacity,
25 pub unmet_demand: DemandMap,
27 pub metric: f64,
29}
30
31fn calculate_lcox(
36 model: &Model,
37 asset: &AssetRef,
38 max_capacity: Option<Capacity>,
39 commodity: &Commodity,
40 reduced_costs: &ReducedCosts,
41 demand: &DemandMap,
42) -> Result<AppraisalOutput> {
43 let coefficients = calculate_coefficients_for_lcox(
45 asset,
46 &model.time_slice_info,
47 reduced_costs,
48 model.parameters.value_of_lost_load,
49 );
50
51 let results = perform_optimisation(
53 asset,
54 max_capacity,
55 commodity,
56 &coefficients,
57 demand,
58 &model.time_slice_info,
59 highs::Sense::Minimise,
60 )?;
61
62 let annual_fixed_cost = coefficients.capacity_coefficient;
64 let activity_costs = coefficients.activity_coefficients;
65 let cost_index = lcox(
66 results.capacity,
67 annual_fixed_cost,
68 &results.activity,
69 &activity_costs,
70 );
71
72 Ok(AppraisalOutput {
74 asset: asset.clone(),
75 capacity: results.capacity,
76 unmet_demand: results.unmet_demand,
77 metric: cost_index.value(),
78 })
79}
80
81fn calculate_npv(
83 model: &Model,
84 asset: &AssetRef,
85 max_capacity: Option<Capacity>,
86 commodity: &Commodity,
87 reduced_costs: &ReducedCosts,
88 demand: &DemandMap,
89) -> Result<AppraisalOutput> {
90 let coefficients = calculate_coefficients_for_npv(asset, &model.time_slice_info, reduced_costs);
92
93 let results = perform_optimisation(
95 asset,
96 max_capacity,
97 commodity,
98 &coefficients,
99 demand,
100 &model.time_slice_info,
101 highs::Sense::Maximise,
102 )?;
103
104 let annual_fixed_cost = -coefficients.capacity_coefficient;
106 let activity_surpluses = coefficients.activity_coefficients;
107 let profitability_index = profitability_index(
108 results.capacity,
109 annual_fixed_cost,
110 &results.activity,
111 &activity_surpluses,
112 );
113
114 Ok(AppraisalOutput {
117 asset: asset.clone(),
118 capacity: results.capacity,
119 unmet_demand: results.unmet_demand,
120 metric: -profitability_index.value(),
121 })
122}
123
124pub fn appraise_investment(
126 model: &Model,
127 asset: &AssetRef,
128 max_capacity: Option<Capacity>,
129 commodity: &Commodity,
130 objective_type: &ObjectiveType,
131 reduced_costs: &ReducedCosts,
132 demand: &DemandMap,
133) -> Result<AppraisalOutput> {
134 let appraisal_method = match objective_type {
135 ObjectiveType::LevelisedCostOfX => calculate_lcox,
136 ObjectiveType::NetPresentValue => calculate_npv,
137 };
138 appraisal_method(model, asset, max_capacity, commodity, reduced_costs, demand)
139}