@@ -38,7 +38,7 @@ impl WeightedAverageAccumulator {
3838 /// Solve the weighted average.
3939 ///
4040 /// Returns `None` if the denominator is zero (or close to zero)
41- fn finalise ( & self ) -> Option < MoneyPerFlow > {
41+ fn finalise ( self ) -> Option < MoneyPerFlow > {
4242 ( self . denominator > Dimensionless :: EPSILON ) . then ( || self . numerator / self . denominator )
4343 }
4444}
@@ -62,7 +62,7 @@ impl WeightedAverageBackupAccumulator {
6262 /// Solve the weighted average, falling back to backup weights if needed.
6363 ///
6464 /// Returns `None` if both denominators are zero (or close to zero).
65- fn finalise ( & self ) -> Option < MoneyPerFlow > {
65+ fn finalise ( self ) -> Option < MoneyPerFlow > {
6666 self . primary . finalise ( ) . or_else ( || self . backup . finalise ( ) )
6767 }
6868}
@@ -388,15 +388,13 @@ where
388388 scarcity_prices
389389}
390390
391- /// Expand a map of prices for commodity/region/time slice selections to a map of prices for
392- /// commodity/region/time slices by applying the same price to all time slices within each
393- /// selection.
394- fn expand_selection_prices (
391+ /// Extend an existing commodity/region/time- slice price map by applying each
392+ /// selection-level price to all time slices within that selection.
393+ fn extend_selection_prices (
394+ prices : & mut IndexMap < ( CommodityID , RegionID , TimeSliceID ) , MoneyPerFlow > ,
395395 group_prices : & IndexMap < ( CommodityID , RegionID , TimeSliceSelection ) , MoneyPerFlow > ,
396396 time_slice_info : & TimeSliceInfo ,
397- ) -> IndexMap < ( CommodityID , RegionID , TimeSliceID ) , MoneyPerFlow > {
398- let mut prices = IndexMap :: new ( ) ;
399-
397+ ) {
400398 for ( ( commodity_id, region_id, selection) , & selection_price) in group_prices {
401399 for ( time_slice_id, _) in selection. iter ( time_slice_info) {
402400 let key = (
@@ -408,8 +406,6 @@ fn expand_selection_prices(
408406 assert ! ( !existing, "Key {key:?} already exists in the map" ) ;
409407 }
410408 }
411-
412- prices
413409}
414410
415411/// Calculate marginal cost prices for a set of commodities.
@@ -530,7 +526,7 @@ where
530526 . filter_map ( |( key, per_asset) | {
531527 per_asset
532528 . into_values ( )
533- . filter_map ( |inner| inner . finalise ( ) )
529+ . filter_map ( WeightedAverageBackupAccumulator :: finalise)
534530 . reduce ( |current, value| current. max ( value) )
535531 . map ( |v| ( key, v) )
536532 } )
@@ -584,22 +580,21 @@ where
584580 }
585581
586582 // For each group, finalise per-candidate weighted averages then take the min across candidates
587- let cand_group_prices: IndexMap < _ , MoneyPerFlow > = cand_accum
588- . into_iter ( )
589- . filter_map ( |( key, per_candidate) | {
590- per_candidate
591- . into_values ( )
592- . filter_map ( |inner| inner. finalise ( ) )
593- . reduce ( |current, value| current. min ( value) )
594- . map ( |v| ( key, v) )
595- } )
596- . collect ( ) ;
583+ let cand_group_prices = cand_accum. into_iter ( ) . filter_map ( |( key, per_candidate) | {
584+ per_candidate
585+ . into_values ( )
586+ . filter_map ( WeightedAverageAccumulator :: finalise)
587+ . reduce ( |current, value| current. min ( value) )
588+ . map ( |v| ( key, v) )
589+ } ) ;
597590
598591 // Merge existing and candidate group prices, then expand to individual time slices
599592 let mut all_group_prices = group_prices;
600593 all_group_prices. extend ( cand_group_prices) ;
601594
602- expand_selection_prices ( & all_group_prices, time_slice_info)
595+ let mut prices = IndexMap :: new ( ) ;
596+ extend_selection_prices ( & mut prices, & all_group_prices, time_slice_info) ;
597+ prices
603598}
604599
605600/// Calculate annual activities for each asset by summing across all time slices
@@ -760,7 +755,7 @@ where
760755 . filter_map ( |( key, per_asset) | {
761756 per_asset
762757 . into_values ( )
763- . filter_map ( |inner| inner . finalise ( ) )
758+ . filter_map ( WeightedAverageBackupAccumulator :: finalise)
764759 . reduce ( |current, value| current. max ( value) )
765760 . map ( |v| ( key, v) )
766761 } )
@@ -827,22 +822,21 @@ where
827822 }
828823
829824 // For each group, finalise per-candidate weighted averages then reduce to the min across candidates
830- let cand_group_prices: IndexMap < _ , MoneyPerFlow > = cand_accum
831- . into_iter ( )
832- . filter_map ( |( key, per_candidate) | {
833- per_candidate
834- . into_values ( )
835- . filter_map ( |inner| inner. finalise ( ) )
836- . reduce ( |current, value| current. min ( value) )
837- . map ( |v| ( key, v) )
838- } )
839- . collect ( ) ;
825+ let cand_group_prices = cand_accum. into_iter ( ) . filter_map ( |( key, per_candidate) | {
826+ per_candidate
827+ . into_values ( )
828+ . filter_map ( WeightedAverageAccumulator :: finalise)
829+ . reduce ( |current, value| current. min ( value) )
830+ . map ( |v| ( key, v) )
831+ } ) ;
840832
841833 // Merge existing and candidate group prices, then expand to individual time slices
842834 let mut all_group_prices = group_prices;
843835 all_group_prices. extend ( cand_group_prices) ;
844836
845- expand_selection_prices ( & all_group_prices, time_slice_info)
837+ let mut prices = IndexMap :: new ( ) ;
838+ extend_selection_prices ( & mut prices, & all_group_prices, time_slice_info) ;
839+ prices
846840}
847841
848842#[ cfg( test) ]
@@ -862,6 +856,7 @@ mod tests {
862856 Activity , Capacity , Dimensionless , FlowPerActivity , MoneyPerActivity , MoneyPerCapacity ,
863857 MoneyPerCapacityPerYear , MoneyPerFlow ,
864858 } ;
859+ use float_cmp:: assert_approx_eq;
865860 use indexmap:: { IndexMap , IndexSet } ;
866861 use rstest:: rstest;
867862 use std:: collections:: { HashMap , HashSet } ;
@@ -1157,7 +1152,7 @@ mod tests {
11571152 accum. add ( MoneyPerFlow ( 200.0 ) , Dimensionless ( 2.0 ) ) ;
11581153 // (100*1 + 200*2) / (1+2) = 500/3 ≈ 166.667
11591154 let result = accum. finalise ( ) . unwrap ( ) ;
1160- assert ! ( ( result - MoneyPerFlow ( 500.0 / 3.0 ) ) . abs ( ) < MoneyPerFlow :: EPSILON ) ;
1155+ assert_approx_eq ! ( MoneyPerFlow , result, MoneyPerFlow ( 500.0 / 3.0 ) ) ;
11611156 }
11621157
11631158 #[ test]
0 commit comments