Skip to content

Commit 6d09b4a

Browse files
committed
Fix: Reduce parent asset capacity for dispatch if only subset of children included
Fixes #1131.
1 parent f8f3c79 commit 6d09b4a

2 files changed

Lines changed: 119 additions & 104 deletions

File tree

src/simulation/optimisation.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ use crate::units::{
1717
use anyhow::{Result, bail, ensure};
1818
use highs::{HighsModelStatus, HighsStatus, RowProblem as Problem, Sense};
1919
use indexmap::{IndexMap, IndexSet};
20-
use itertools::{Itertools, chain, iproduct};
20+
use itertools::{chain, iproduct};
2121
use std::cell::Cell;
22-
use std::collections::{HashMap, HashSet};
22+
use std::collections::HashMap;
2323
use std::error::Error;
2424
use std::fmt;
2525
use std::ops::Range;
@@ -434,15 +434,30 @@ fn filter_input_prices(
434434
///
435435
/// Child assets are converted to their parents and non-divisible assets are returned as is. Each
436436
/// parent asset is returned only once.
437-
fn get_parent_or_self(assets: &[AssetRef]) -> impl Iterator<Item = AssetRef> {
438-
let mut parents = HashSet::new();
439-
assets
440-
.iter()
441-
.filter_map(move |asset| match asset.parent() {
442-
Some(parent) => parents.insert(parent.clone()).then_some(parent),
443-
None => Some(asset),
444-
})
445-
.cloned()
437+
///
438+
/// If only a subset of a parent's children are present in `assets`, a new parent asset representing
439+
/// a portion of the total capacity will be created. This will have the same hash as the original
440+
/// parent.
441+
fn get_parent_or_self(assets: &[AssetRef]) -> Vec<AssetRef> {
442+
let mut child_counts: IndexMap<&AssetRef, u32> = IndexMap::new();
443+
let mut out = Vec::new();
444+
445+
for asset in assets {
446+
if let Some(parent) = asset.parent() {
447+
// For child assets, keep count of number of children per parent
448+
*child_counts.entry(parent).or_default() += 1;
449+
} else {
450+
// Non-divisible assets can be returned as is
451+
out.push(asset.clone());
452+
}
453+
}
454+
455+
for (parent, child_count) in child_counts {
456+
// Convert to an object representing the appropriate portion of the parent's capacity
457+
out.push(parent.make_partial_parent(child_count));
458+
}
459+
460+
out
446461
}
447462

448463
/// Provides the interface for running the dispatch optimisation.
@@ -621,7 +636,7 @@ impl<'model, 'run> DispatchRun<'model, 'run> {
621636
allow_unmet_demand: bool,
622637
input_prices: Option<&CommodityPrices>,
623638
) -> Result<Solution<'model>, ModelError> {
624-
let parent_assets = get_parent_or_self(self.existing_assets).collect_vec();
639+
let parent_assets = get_parent_or_self(self.existing_assets);
625640

626641
// Set up problem
627642
let mut problem = Problem::default();

0 commit comments

Comments
 (0)