Skip to content

Commit 7a58d21

Browse files
committed
Precompute investment objective coefficients
1 parent b838db7 commit 7a58d21

4 files changed

Lines changed: 53 additions & 31 deletions

File tree

src/simulation/investment.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::collections::HashMap;
1818

1919
pub mod appraisal;
2020
use appraisal::appraise_investment;
21+
use appraisal::coefficients::calculate_coefficients_for_assets;
2122

2223
/// A map of demand across time slices for a specific commodity and region
2324
type DemandMap = IndexMap<TimeSliceID, Flow>;
@@ -364,7 +365,11 @@ fn select_best_assets(
364365
year: u32,
365366
writer: &mut DataWriter,
366367
) -> Result<Vec<AssetRef>> {
367-
let mut best_assets: Vec<AssetRef> = Vec::new();
368+
let objective_type = &agent.objectives[&year];
369+
370+
// Pre-compute coefficients for all asset options according to the agent's objective
371+
let coefficients =
372+
calculate_coefficients_for_assets(model, objective_type, &opt_assets, reduced_costs);
368373

369374
let mut remaining_candidate_capacity = HashMap::from_iter(
370375
opt_assets
@@ -374,7 +379,7 @@ fn select_best_assets(
374379
);
375380

376381
let mut round = 0;
377-
let objective_type = &agent.objectives[&year];
382+
let mut best_assets: Vec<AssetRef> = Vec::new();
378383
while is_any_remaining_demand(&demand) {
379384
ensure!(
380385
!opt_assets.is_empty(),
@@ -397,7 +402,7 @@ fn select_best_assets(
397402
max_capacity,
398403
commodity,
399404
objective_type,
400-
reduced_costs,
405+
&coefficients[asset],
401406
&demand,
402407
)?;
403408

src/simulation/investment/appraisal.rs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ use crate::asset::AssetRef;
55
use crate::commodity::Commodity;
66
use crate::finance::{lcox, profitability_index};
77
use crate::model::Model;
8-
use crate::simulation::prices::ReducedCosts;
98
use crate::units::Capacity;
109
use anyhow::Result;
1110

12-
mod coefficients;
11+
pub mod coefficients;
1312
mod constraints;
1413
mod costs;
1514
mod optimisation;
16-
use coefficients::{calculate_coefficients_for_lcox, calculate_coefficients_for_npv};
15+
use coefficients::ObjectiveCoefficients;
1716
use optimisation::perform_optimisation;
1817

1918
/// The output of investment appraisal required to compare potential investment decisions
@@ -37,17 +36,9 @@ fn calculate_lcox(
3736
asset: &AssetRef,
3837
max_capacity: Option<Capacity>,
3938
commodity: &Commodity,
40-
reduced_costs: &ReducedCosts,
39+
coefficients: &ObjectiveCoefficients,
4140
demand: &DemandMap,
4241
) -> Result<AppraisalOutput> {
43-
// Calculate coefficients
44-
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-
5142
// Perform optimisation to calculate capacity, activity and unmet demand
5243
let results = perform_optimisation(
5344
asset,
@@ -61,7 +52,7 @@ fn calculate_lcox(
6152

6253
// Calculate LCOX for the hypothetical investment
6354
let annual_fixed_cost = coefficients.capacity_coefficient;
64-
let activity_costs = coefficients.activity_coefficients;
55+
let activity_costs = &coefficients.activity_coefficients;
6556
let cost_index = lcox(
6657
results.capacity,
6758
annual_fixed_cost,
@@ -84,12 +75,9 @@ fn calculate_npv(
8475
asset: &AssetRef,
8576
max_capacity: Option<Capacity>,
8677
commodity: &Commodity,
87-
reduced_costs: &ReducedCosts,
78+
coefficients: &ObjectiveCoefficients,
8879
demand: &DemandMap,
8980
) -> Result<AppraisalOutput> {
90-
// Calculate coefficients
91-
let coefficients = calculate_coefficients_for_npv(asset, &model.time_slice_info, reduced_costs);
92-
9381
// Perform optimisation to calculate capacity, activity and unmet demand
9482
let results = perform_optimisation(
9583
asset,
@@ -103,7 +91,7 @@ fn calculate_npv(
10391

10492
// Calculate profitability index for the hypothetical investment
10593
let annual_fixed_cost = -coefficients.capacity_coefficient;
106-
let activity_surpluses = coefficients.activity_coefficients;
94+
let activity_surpluses = &coefficients.activity_coefficients;
10795
let profitability_index = profitability_index(
10896
results.capacity,
10997
annual_fixed_cost,
@@ -128,12 +116,12 @@ pub fn appraise_investment(
128116
max_capacity: Option<Capacity>,
129117
commodity: &Commodity,
130118
objective_type: &ObjectiveType,
131-
reduced_costs: &ReducedCosts,
119+
coefficients: &ObjectiveCoefficients,
132120
demand: &DemandMap,
133121
) -> Result<AppraisalOutput> {
134122
let appraisal_method = match objective_type {
135123
ObjectiveType::LevelisedCostOfX => calculate_lcox,
136124
ObjectiveType::NetPresentValue => calculate_npv,
137125
};
138-
appraisal_method(model, asset, max_capacity, commodity, reduced_costs, demand)
126+
appraisal_method(model, asset, max_capacity, commodity, coefficients, demand)
139127
}

src/simulation/investment/appraisal/coefficients.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//! Calculation of cost coefficients for investment tools.
22
use super::costs::{activity_cost, activity_surplus, annual_fixed_cost};
3+
use crate::agent::ObjectiveType;
34
use crate::asset::AssetRef;
5+
use crate::model::Model;
46
use crate::simulation::prices::ReducedCosts;
57
use crate::time_slice::{TimeSliceID, TimeSliceInfo};
68
use crate::units::{MoneyPerActivity, MoneyPerCapacity, MoneyPerFlow};
79
use indexmap::IndexMap;
10+
use std::collections::HashMap;
811

912
/// Map storing coefficients for each variable
10-
pub struct CoefficientsMap {
13+
#[derive(Clone)]
14+
pub struct ObjectiveCoefficients {
1115
/// Cost per unit of capacity
1216
pub capacity_coefficient: MoneyPerCapacity,
1317
/// Cost per unit of activity in each time slice
@@ -16,13 +20,38 @@ pub struct CoefficientsMap {
1620
pub unmet_demand_coefficient: MoneyPerFlow,
1721
}
1822

23+
pub fn calculate_coefficients_for_assets(
24+
model: &Model,
25+
objective_type: &ObjectiveType,
26+
assets: &[AssetRef],
27+
reduced_costs: &ReducedCosts,
28+
) -> HashMap<AssetRef, ObjectiveCoefficients> {
29+
assets
30+
.iter()
31+
.map(|asset| {
32+
let coefficient = match objective_type {
33+
ObjectiveType::LevelisedCostOfX => calculate_coefficients_for_lcox(
34+
asset,
35+
&model.time_slice_info,
36+
reduced_costs,
37+
model.parameters.value_of_lost_load,
38+
),
39+
ObjectiveType::NetPresentValue => {
40+
calculate_coefficients_for_npv(asset, &model.time_slice_info, reduced_costs)
41+
}
42+
};
43+
(asset.clone(), coefficient)
44+
})
45+
.collect()
46+
}
47+
1948
/// Calculates the cost coefficients for LCOX.
2049
pub fn calculate_coefficients_for_lcox(
2150
asset: &AssetRef,
2251
time_slice_info: &TimeSliceInfo,
2352
reduced_costs: &ReducedCosts,
2453
value_of_lost_load: MoneyPerFlow,
25-
) -> CoefficientsMap {
54+
) -> ObjectiveCoefficients {
2655
// Capacity coefficient
2756
let capacity_coefficient = annual_fixed_cost(asset);
2857

@@ -36,7 +65,7 @@ pub fn calculate_coefficients_for_lcox(
3665
// Unmet demand coefficient
3766
let unmet_demand_coefficient = value_of_lost_load;
3867

39-
CoefficientsMap {
68+
ObjectiveCoefficients {
4069
capacity_coefficient,
4170
activity_coefficients,
4271
unmet_demand_coefficient,
@@ -48,7 +77,7 @@ pub fn calculate_coefficients_for_npv(
4877
asset: &AssetRef,
4978
time_slice_info: &TimeSliceInfo,
5079
reduced_costs: &ReducedCosts,
51-
) -> CoefficientsMap {
80+
) -> ObjectiveCoefficients {
5281
// Capacity coefficient
5382
let capacity_coefficient = -annual_fixed_cost(asset);
5483

@@ -62,7 +91,7 @@ pub fn calculate_coefficients_for_npv(
6291
// Unmet demand coefficient (we don't apply a cost to unmet demand, so we set this to zero)
6392
let unmet_demand_coefficient = MoneyPerFlow(0.0);
6493

65-
CoefficientsMap {
94+
ObjectiveCoefficients {
6695
capacity_coefficient,
6796
activity_coefficients,
6897
unmet_demand_coefficient,

src/simulation/investment/appraisal/optimisation.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Optimisation problem for investment tools.
22
use super::DemandMap;
3-
use super::coefficients::CoefficientsMap;
3+
use super::coefficients::ObjectiveCoefficients;
44
use super::constraints::{
55
add_activity_constraints, add_capacity_constraint, add_demand_constraints,
66
};
@@ -37,7 +37,7 @@ pub struct ResultsMap {
3737
}
3838

3939
/// Add variables to the problem based on cost coefficients
40-
fn add_variables(problem: &mut Problem, cost_coefficients: &CoefficientsMap) -> VariableMap {
40+
fn add_variables(problem: &mut Problem, cost_coefficients: &ObjectiveCoefficients) -> VariableMap {
4141
// Create capacity variable
4242
let capacity_var = problem.add_column(cost_coefficients.capacity_coefficient.value(), 0.0..);
4343

@@ -98,7 +98,7 @@ pub fn perform_optimisation(
9898
asset: &AssetRef,
9999
max_capacity: Option<Capacity>,
100100
commodity: &Commodity,
101-
coefficients: &CoefficientsMap,
101+
coefficients: &ObjectiveCoefficients,
102102
demand: &DemandMap,
103103
time_slice_info: &TimeSliceInfo,
104104
sense: Sense,

0 commit comments

Comments
 (0)