@@ -17,9 +17,9 @@ use crate::units::{
1717use anyhow:: { Result , bail, ensure} ;
1818use highs:: { HighsModelStatus , HighsStatus , RowProblem as Problem , Sense } ;
1919use indexmap:: { IndexMap , IndexSet } ;
20- use itertools:: { Itertools , chain, iproduct} ;
20+ use itertools:: { chain, iproduct} ;
2121use std:: cell:: Cell ;
22- use std:: collections:: { HashMap , HashSet } ;
22+ use std:: collections:: HashMap ;
2323use std:: error:: Error ;
2424use std:: fmt;
2525use 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