Skip to content

Commit ff47a80

Browse files
authored
Merge pull request #626 from EnergySystemsModellingLab/add-capacity-constraints
Add capacity constraints
2 parents 8fe9861 + e6abf78 commit ff47a80

4 files changed

Lines changed: 49 additions & 49 deletions

File tree

src/output.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ const ASSETS_FILE_NAME: &str = "assets.csv";
3232
/// The output file name for commodity balance duals
3333
const COMMODITY_BALANCE_DUALS_FILE_NAME: &str = "debug_commodity_balance_duals.csv";
3434

35-
/// The output file name for capacity duals
36-
const CAPACITY_DUALS_FILE_NAME: &str = "debug_capacity_duals.csv";
35+
/// The output file name for activity duals
36+
const ACTIVITY_DUALS_FILE_NAME: &str = "debug_activity_duals.csv";
3737

3838
/// Get the model name from the specified directory path
3939
pub fn get_output_dir(model_dir: &Path) -> Result<PathBuf> {
@@ -111,9 +111,9 @@ struct CommodityPriceRow {
111111
price: f64,
112112
}
113113

114-
/// Represents the capacity duals data in a row of the capacity duals CSV file
114+
/// Represents the activity duals data in a row of the activity duals CSV file
115115
#[derive(Serialize, Deserialize, Debug, PartialEq)]
116-
struct CapacityDualsRow {
116+
struct ActivityDualsRow {
117117
milestone_year: u32,
118118
asset_id: AssetID,
119119
time_slice: TimeSliceID,
@@ -133,7 +133,7 @@ struct CommodityBalanceDualsRow {
133133
/// For writing extra debug information about the model
134134
struct DebugDataWriter {
135135
commodity_balance_duals_writer: csv::Writer<File>,
136-
capacity_duals_writer: csv::Writer<File>,
136+
activity_duals_writer: csv::Writer<File>,
137137
}
138138

139139
impl DebugDataWriter {
@@ -150,33 +150,33 @@ impl DebugDataWriter {
150150

151151
Ok(Self {
152152
commodity_balance_duals_writer: new_writer(COMMODITY_BALANCE_DUALS_FILE_NAME)?,
153-
capacity_duals_writer: new_writer(CAPACITY_DUALS_FILE_NAME)?,
153+
activity_duals_writer: new_writer(ACTIVITY_DUALS_FILE_NAME)?,
154154
})
155155
}
156156

157157
/// Write all debug info to output files
158158
fn write_debug_info(&mut self, milestone_year: u32, solution: &Solution) -> Result<()> {
159-
self.write_capacity_duals(milestone_year, solution.iter_capacity_duals())?;
159+
self.write_activity_duals(milestone_year, solution.iter_activity_duals())?;
160160
self.write_commodity_balance_duals(
161161
milestone_year,
162162
solution.iter_commodity_balance_duals(),
163163
)?;
164164
Ok(())
165165
}
166166

167-
/// Write capacity duals to file
168-
fn write_capacity_duals<'a, I>(&mut self, milestone_year: u32, iter: I) -> Result<()>
167+
/// Write activity duals to file
168+
fn write_activity_duals<'a, I>(&mut self, milestone_year: u32, iter: I) -> Result<()>
169169
where
170170
I: Iterator<Item = (&'a AssetRef, &'a TimeSliceID, f64)>,
171171
{
172172
for (asset, time_slice, value) in iter {
173-
let row = CapacityDualsRow {
173+
let row = ActivityDualsRow {
174174
milestone_year,
175175
asset_id: asset.id.unwrap(),
176176
time_slice: time_slice.clone(),
177177
value,
178178
};
179-
self.capacity_duals_writer.serialize(row)?;
179+
self.activity_duals_writer.serialize(row)?;
180180
}
181181

182182
Ok(())
@@ -204,7 +204,7 @@ impl DebugDataWriter {
204204
/// Flush the underlying streams
205205
fn flush(&mut self) -> Result<()> {
206206
self.commodity_balance_duals_writer.flush()?;
207-
self.capacity_duals_writer.flush()?;
207+
self.activity_duals_writer.flush()?;
208208

209209
Ok(())
210210
}
@@ -457,30 +457,30 @@ mod tests {
457457
}
458458

459459
#[rstest]
460-
fn test_write_capacity_duals(assets: AssetPool, time_slice: TimeSliceID) {
460+
fn test_write_activity_duals(assets: AssetPool, time_slice: TimeSliceID) {
461461
let milestone_year = 2020;
462462
let value = 0.5;
463463
let dir = tempdir().unwrap();
464464
let asset = assets.iter().next().unwrap();
465465

466-
// Write capacity dual
466+
// Write activity dual
467467
{
468468
let mut writer = DebugDataWriter::create(dir.path()).unwrap();
469469
writer
470-
.write_capacity_duals(milestone_year, iter::once((asset, &time_slice, value)))
470+
.write_activity_duals(milestone_year, iter::once((asset, &time_slice, value)))
471471
.unwrap();
472472
writer.flush().unwrap();
473473
}
474474

475475
// Read back and compare
476-
let expected = CapacityDualsRow {
476+
let expected = ActivityDualsRow {
477477
milestone_year,
478478
asset_id: asset.id.unwrap(),
479479
time_slice,
480480
value,
481481
};
482-
let records: Vec<CapacityDualsRow> =
483-
csv::Reader::from_path(dir.path().join(CAPACITY_DUALS_FILE_NAME))
482+
let records: Vec<ActivityDualsRow> =
483+
csv::Reader::from_path(dir.path().join(ACTIVITY_DUALS_FILE_NAME))
484484
.unwrap()
485485
.into_deserialize()
486486
.try_collect()

src/simulation/optimisation.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ impl Solution<'_> {
9494
})
9595
}
9696

97-
/// Keys and dual values for capacity constraints.
98-
pub fn iter_capacity_duals(&self) -> impl Iterator<Item = (&AssetRef, &TimeSliceID, f64)> {
97+
/// Keys and dual values for activity constraints.
98+
pub fn iter_activity_duals(&self) -> impl Iterator<Item = (&AssetRef, &TimeSliceID, f64)> {
9999
self.constraint_keys
100-
.capacity_keys
100+
.activity_keys
101101
.zip_duals(self.solution.dual_rows())
102102
.map(|((asset, time_slice), dual)| (asset, time_slice, dual))
103103
}

src/simulation/optimisation/constraints.rs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,34 +28,33 @@ impl<T> KeysWithOffset<T> {
2828
/// Indicates the commodity ID and time slice selection covered by each commodity balance constraint
2929
pub type CommodityBalanceKeys = KeysWithOffset<(CommodityID, RegionID, TimeSliceSelection)>;
3030

31-
/// Indicates the asset ID and time slice covered by each capacity constraint
32-
pub type CapacityKeys = KeysWithOffset<(AssetRef, TimeSliceID)>;
31+
/// Indicates the asset ID and time slice covered by each activity constraint
32+
pub type ActivityKeys = KeysWithOffset<(AssetRef, TimeSliceID)>;
3333

3434
/// The keys for different constraints
3535
pub struct ConstraintKeys {
3636
/// Keys for commodity balance constraints
3737
pub commodity_balance_keys: CommodityBalanceKeys,
38-
/// Keys for capacity constraints
39-
pub capacity_keys: CapacityKeys,
38+
/// Keys for activity constraints
39+
pub activity_keys: ActivityKeys,
4040
}
4141

4242
/// Add asset-level constraints
4343
///
4444
/// Note: the ordering of constraints is important, as the dual values of the constraints must later
4545
/// be retrieved to calculate commodity prices.
4646
///
47-
/// # Arguments:
47+
/// # Arguments
4848
///
4949
/// * `problem` - The optimisation problem
5050
/// * `variables` - The variables in the problem
5151
/// * `model` - The model
5252
/// * `assets` - The asset pool
5353
/// * `year` - Current milestone year
5454
///
55-
/// # Returns:
55+
/// # Returns
5656
///
57-
/// * A vector of keys for commodity balance constraints
58-
/// * A vector of keys for capacity constraints
57+
/// Keys for the different constraints.
5958
pub fn add_asset_constraints(
6059
problem: &mut Problem,
6160
variables: &VariableMap,
@@ -67,12 +66,12 @@ pub fn add_asset_constraints(
6766
add_commodity_balance_constraints(problem, variables, model, assets, year);
6867

6968
let capacity_keys =
70-
add_asset_capacity_constraints(problem, variables, assets, &model.time_slice_info);
69+
add_activity_constraints(problem, variables, &model.time_slice_info, assets);
7170

7271
// Return constraint keys
7372
ConstraintKeys {
7473
commodity_balance_keys,
75-
capacity_keys,
74+
activity_keys: capacity_keys,
7675
}
7776
}
7877

@@ -140,28 +139,29 @@ fn add_commodity_balance_constraints(
140139
CommodityBalanceKeys { offset, keys }
141140
}
142141

143-
/// Add asset-level capacity and availability constraints.
142+
/// Add constraints on the activity of different assets.
144143
///
145-
/// For every asset at every time slice, the sum of the commodity flows for assets must not exceed
146-
/// the capacity limits, which are a product of the annual capacity, time slice length and process
147-
/// availability.
148-
///
149-
/// See description in [the dispatch optimisation documentation][1].
150-
///
151-
/// [1]: https://energysystemsmodellinglab.github.io/MUSE_2.0/dispatch_optimisation.html#asset-level-capacity-and-availability-constraints
152-
fn add_asset_capacity_constraints(
144+
/// This ensures that assets do not exceed their specified capacity and availability for each time
145+
/// slice.
146+
fn add_activity_constraints(
153147
problem: &mut Problem,
154-
_variables: &VariableMap,
155-
_assets: &AssetPool,
156-
_time_slice_info: &TimeSliceInfo,
157-
) -> CapacityKeys {
148+
variables: &VariableMap,
149+
time_slice_info: &TimeSliceInfo,
150+
assets: &AssetPool,
151+
) -> ActivityKeys {
158152
// Row offset in problem. This line **must** come before we add more constraints.
159153
let offset = problem.num_rows();
160154

161-
let keys = Vec::new();
155+
let mut keys = Vec::new();
156+
for asset in assets.iter() {
157+
for time_slice in time_slice_info.iter_ids() {
158+
let var = variables.get(asset, time_slice);
159+
let limits = asset.get_activity_limits(time_slice);
162160

163-
// **TODO:** Add capacity/availability constraints:
164-
// https://github.com/EnergySystemsModellingLab/MUSE_2.0/issues/579
161+
problem.add_row(limits, [(var, 1.0)]);
162+
keys.push((asset.clone(), time_slice.clone()))
163+
}
164+
}
165165

166-
CapacityKeys { offset, keys }
166+
ActivityKeys { offset, keys }
167167
}

src/simulation/prices.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl CommodityPrices {
3939
/// Add commodity prices for which there are values in the solution
4040
///
4141
/// Commodity prices are calculated as the sum of the commodity balance duals and the highest
42-
/// capacity dual for each commodity/timeslice.
42+
/// activity dual for each commodity/timeslice.
4343
///
4444
/// # Arguments
4545
///

0 commit comments

Comments
 (0)