@@ -165,7 +165,7 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result
165165 // Add prices for full cost commodities
166166 if let Some ( fullcost_set) = pricing_sets. get ( & PricingStrategy :: FullCost ) {
167167 let annual_activities = annual_activities. get_or_insert_with ( || {
168- calculate_annual_activities ( solution. iter_activity_for_existing ( ) )
168+ iter_annual_activities ( solution. iter_activity_for_existing ( ) )
169169 } ) ;
170170 add_full_cost_prices (
171171 solution. iter_activity_for_existing ( ) ,
@@ -182,7 +182,7 @@ pub fn calculate_prices(model: &Model, solution: &Solution, year: u32) -> Result
182182 // Add prices for full average commodities
183183 if let Some ( full_avg_set) = pricing_sets. get ( & PricingStrategy :: FullCostAverage ) {
184184 let annual_activities = annual_activities. get_or_insert_with ( || {
185- calculate_annual_activities ( solution. iter_activity_for_existing ( ) )
185+ iter_annual_activities ( solution. iter_activity_for_existing ( ) )
186186 } ) ;
187187 add_full_cost_average_prices (
188188 solution. iter_activity_for_existing ( ) ,
@@ -487,7 +487,7 @@ fn add_marginal_cost_prices<'a, I, J>(
487487 J : Iterator < Item = ( & ' a AssetRef , & ' a TimeSliceID ) > ,
488488{
489489 // Calculate marginal cost prices from existing assets
490- let mut group_prices: IndexMap < _ , _ > = calculate_existing_asset_prices (
490+ let mut group_prices: IndexMap < _ , _ > = iter_existing_asset_max_prices (
491491 activity_for_existing,
492492 markets_to_price,
493493 existing_prices,
@@ -501,7 +501,7 @@ fn add_marginal_cost_prices<'a, I, J>(
501501
502502 // Calculate marginal cost prices from candidate assets, skipping any groups already covered by
503503 // existing assets
504- let cand_group_prices = calculate_candidate_asset_prices (
504+ let cand_group_prices = iter_candidate_asset_min_prices (
505505 activity_keys_for_candidates,
506506 markets_to_price,
507507 existing_prices,
@@ -518,19 +518,29 @@ fn add_marginal_cost_prices<'a, I, J>(
518518 existing_prices. extend_selection_prices ( & group_prices, time_slice_info) ;
519519}
520520
521- /// Calculate prices using existing assets, taking a weighted average across time slices for
522- /// seasonal/annual commodities, and taking the max across assets for each commodity/region/selection.
521+ /// Calculate prices as the maximum cost across existing assets, using either a marginal cost or
522+ /// full cost strategy (depending on `pricing_strategy`). Prices are given for each commodity in
523+ /// the granularity of the commodity's time slice level. For seasonal/annual commodities, this
524+ /// involves taking a weighted average across time slices for each asset according to activity
525+ /// (with a backup weight based on potential activity if there is zero activity across the
526+ /// selection, and omitting prices in the extreme case of zero potential activity).
523527///
524528/// # Arguments
525529///
526530/// * `activity_for_existing` - Iterator over (asset, time slice, activity) tuples for existing assets
527- /// * `markets_to_price` - Set of (commodity, region) pairs to price
528- /// * `existing_prices` - Current commodity prices (used for marginal cost filtering )
531+ /// * `markets_to_price` - Set of (commodity, region) pairs to attempt to price
532+ /// * `existing_prices` - Current commodity prices (used to calculate marginal costs )
529533/// * `year` - Year for which prices are being calculated
530534/// * `commodities` - Commodity map
531- /// * `pricing_strategy` - Pricing strategy (determines whether to include fixed costs)
535+ /// * `pricing_strategy` - Pricing strategy, either `MarginalCost` or `FullCost`
532536/// * `annual_activities` - Optional annual activities (required for full cost pricing)
533- fn calculate_existing_asset_prices < ' a , I > (
537+ ///
538+ /// # Returns
539+ ///
540+ /// An iterator of ((commodity, region, time slice selection), price) tuples for the calculated
541+ /// prices. This will include all (commodity, region) combinations in `markets_to_price` for
542+ /// time slice selections where a price could be determined.
543+ fn iter_existing_asset_max_prices < ' a , I > (
534544 activity_for_existing : I ,
535545 markets_to_price : & HashSet < ( CommodityID , RegionID ) > ,
536546 existing_prices : & CommodityPrices ,
@@ -628,10 +638,32 @@ where
628638 } )
629639}
630640
631- /// Calculate candidate-asset prices (marginal or full), taking a weighted average across time
632- /// slices for seasonal/annual commodities, and taking the min across assets for each
633- /// commodity/region/selection. Only groups not already covered by existing assets are considered.
634- fn calculate_candidate_asset_prices < ' a , I > (
641+ /// Calculate prices as the minimum cost across candidate assets, using either a marginal cost or
642+ /// full cost strategy (depending on `pricing_strategy`). Prices are given for each commodity in
643+ /// the granularity of the commodity's time slice level. For seasonal/annual commodities, this
644+ /// involves taking a weighted average across time slices for each asset according to potential
645+ /// activity (i.e. the upper activity limit), omitting prices in the extreme case of zero potential
646+ /// activity (Note: this should NOT happen as validation should ensure there is at least one
647+ /// candidate that can provide a price in each timeslice for which a price could be required).
648+ /// Costs for candidates are calculated assuming full utilisation.
649+ ///
650+ /// # Arguments
651+ ///
652+ /// * `activity_keys_for_candidates` - Iterator over (asset, time slice) tuples for candidate assets
653+ /// * `markets_to_price` - Set of (commodity, region) pairs to attempt to price
654+ /// * `existing_prices` - Current commodity prices (used to calculate marginal costs)
655+ /// * `priced_groups` - Set of (commodity, region, time slice selection) groups that have already
656+ /// been prices using existing assets, so should be skipped when looking at candidates
657+ /// * `year` - Year for which prices are being calculated
658+ /// * `commodities` - Commodity map
659+ /// * `pricing_strategy` - Pricing strategy, either `MarginalCost` or `FullCost`
660+ ///
661+ /// # Returns
662+ ///
663+ /// An iterator of ((commodity, region, time slice selection), price) tuples for the calculated
664+ /// prices. This will include all (commodity, region) combinations in `markets_to_price` for
665+ /// time slice selections not covered by `priced_groups`, and for which a price could be determined
666+ fn iter_candidate_asset_min_prices < ' a , I > (
635667 activity_keys_for_candidates : I ,
636668 markets_to_price : & HashSet < ( CommodityID , RegionID ) > ,
637669 existing_prices : & CommodityPrices ,
@@ -747,7 +779,7 @@ fn add_marginal_cost_average_prices<'a, I, J>(
747779 J : Iterator < Item = ( & ' a AssetRef , & ' a TimeSliceID ) > ,
748780{
749781 // Calculate marginal cost prices from existing assets
750- let mut group_prices: IndexMap < _ , _ > = calculate_existing_asset_average_prices (
782+ let mut group_prices: IndexMap < _ , _ > = iter_existing_asset_average_prices (
751783 activity_for_existing,
752784 markets_to_price,
753785 existing_prices,
@@ -761,7 +793,7 @@ fn add_marginal_cost_average_prices<'a, I, J>(
761793
762794 // Calculate marginal cost prices from candidate assets, skipping any groups already covered by
763795 // existing assets
764- let cand_group_prices = calculate_candidate_asset_prices (
796+ let cand_group_prices = iter_candidate_asset_min_prices (
765797 activity_keys_for_candidates,
766798 markets_to_price,
767799 existing_prices,
@@ -778,12 +810,29 @@ fn add_marginal_cost_average_prices<'a, I, J>(
778810 existing_prices. extend_selection_prices ( & group_prices, time_slice_info) ;
779811}
780812
781- /// Calculate average prices for existing assets using a weighted average across time slices for
782- /// seasonal/annual commodities, and a weighted average across assets according to output (with a
783- /// backup weight based on potential output if there is zero activity across the selection).
813+ /// Calculate prices as the load-weighted average cost across existing assets, using either a
814+ /// marginal cost or full cost strategy (depending on `pricing_strategy`). Prices are given for each
815+ /// commodity in the granularity of the commodity's time slice level. For seasonal/annual
816+ /// commodities, this involves taking a weighted average across time slices for each asset according
817+ /// to activity (with a backup weight based on potential activity if there is zero activity across
818+ /// the selection, and omitting prices in the extreme case of zero potential activity).
819+ ///
820+ /// # Arguments
821+ ///
822+ /// * `activity_for_existing` - Iterator over (asset, time slice, activity) tuples for existing assets
823+ /// * `markets_to_price` - Set of (commodity, region) pairs to attempt to price
824+ /// * `existing_prices` - Current commodity prices (used to calculate marginal costs)
825+ /// * `year` - Year for which prices are being calculated
826+ /// * `commodities` - Commodity map
827+ /// * `pricing_strategy` - Pricing strategy, either `MarginalCost` or `FullCost`
828+ /// * `annual_activities` - Optional annual activities (required for full cost pricing)
829+ ///
830+ /// # Returns
784831///
785- /// `FullCost` adds annual fixed costs per flow and skips assets with zero annual activity.
786- fn calculate_existing_asset_average_prices < ' a , I > (
832+ /// An iterator of ((commodity, region, time slice selection), price) tuples for the calculated
833+ /// prices. This will include all (commodity, region) combinations in `markets_to_price` for
834+ /// time slice selections where a price could be determined.
835+ fn iter_existing_asset_average_prices < ' a , I > (
787836 activity_for_existing : I ,
788837 markets_to_price : & HashSet < ( CommodityID , RegionID ) > ,
789838 existing_prices : & CommodityPrices ,
@@ -883,8 +932,8 @@ where
883932 . filter_map ( |( key, accum) | accum. finalise ( ) . map ( |v| ( key, v) ) )
884933}
885934
886- /// Calculate annual activities for each asset by summing across all time slices
887- fn calculate_annual_activities < ' a , I > ( activities : I ) -> HashMap < AssetRef , Activity >
935+ /// Iterate over annual activities for each asset by summing across all time slices
936+ fn iter_annual_activities < ' a , I > ( activities : I ) -> HashMap < AssetRef , Activity >
888937where
889938 I : IntoIterator < Item = ( & ' a AssetRef , & ' a TimeSliceID , Activity ) > ,
890939{
@@ -971,7 +1020,7 @@ fn add_full_cost_prices<'a, I, J>(
9711020 J : Iterator < Item = ( & ' a AssetRef , & ' a TimeSliceID ) > ,
9721021{
9731022 // Calculate full cost prices from existing assets
974- let mut group_prices: IndexMap < _ , _ > = calculate_existing_asset_prices (
1023+ let mut group_prices: IndexMap < _ , _ > = iter_existing_asset_max_prices (
9751024 activity_for_existing,
9761025 markets_to_price,
9771026 existing_prices,
@@ -985,7 +1034,7 @@ fn add_full_cost_prices<'a, I, J>(
9851034
9861035 // Calculate full cost prices from candidate assets, skipping any groups already covered by
9871036 // existing assets
988- let cand_group_prices = calculate_candidate_asset_prices (
1037+ let cand_group_prices = iter_candidate_asset_min_prices (
9891038 activity_keys_for_candidates,
9901039 markets_to_price,
9911040 existing_prices,
@@ -1025,7 +1074,7 @@ fn add_full_cost_average_prices<'a, I, J>(
10251074 J : Iterator < Item = ( & ' a AssetRef , & ' a TimeSliceID ) > ,
10261075{
10271076 // Calculate full cost prices from existing assets
1028- let mut group_prices: IndexMap < _ , _ > = calculate_existing_asset_average_prices (
1077+ let mut group_prices: IndexMap < _ , _ > = iter_existing_asset_average_prices (
10291078 activity_for_existing,
10301079 markets_to_price,
10311080 existing_prices,
@@ -1038,7 +1087,7 @@ fn add_full_cost_average_prices<'a, I, J>(
10381087 let priced_groups: HashSet < _ > = group_prices. keys ( ) . cloned ( ) . collect ( ) ;
10391088
10401089 // Calculate full cost prices from candidate assets, skipping any groups already covered by existing assets
1041- let cand_group_prices = calculate_candidate_asset_prices (
1090+ let cand_group_prices = iter_candidate_asset_min_prices (
10421091 activity_keys_for_candidates,
10431092 markets_to_price,
10441093 existing_prices,
0 commit comments