11//! Module for creating and analysing commodity graphs
2- use crate :: commodity:: { CommodityID , CommodityMap , CommodityType , InvestmentSet } ;
2+ use crate :: commodity:: { CommodityID , CommodityMap , CommodityType } ;
33use crate :: process:: { ProcessID , ProcessMap } ;
44use crate :: region:: RegionID ;
5+ use crate :: simulation:: investment:: InvestmentSet ;
56use crate :: time_slice:: { TimeSliceInfo , TimeSliceLevel , TimeSliceSelection } ;
67use crate :: units:: { Dimensionless , Flow } ;
78use anyhow:: { Context , Result , ensure} ;
@@ -338,7 +339,7 @@ fn solve_investment_order(
338339 . iter ( )
339340 . rev ( )
340341 . filter_map ( |node_idx| {
341- // Get set of commodity IDs for the node
342+ // Get set of commodity ID(s) for the node, referring back to `condensed_graph`
342343 let commodities: Vec < CommodityID > = condensed_graph
343344 . node_weight ( * node_idx)
344345 . unwrap ( )
@@ -495,7 +496,10 @@ mod tests {
495496 use std:: rc:: Rc ;
496497
497498 #[ rstest]
498- fn test_topo_sort_linear_graph ( sed_commodity : Commodity , svd_commodity : Commodity ) {
499+ fn test_solve_investment_order_linear_graph (
500+ sed_commodity : Commodity ,
501+ svd_commodity : Commodity ,
502+ ) {
499503 // Create a simple linear graph: A -> B -> C
500504 let mut graph = Graph :: new ( ) ;
501505
@@ -513,17 +517,18 @@ mod tests {
513517 commodities. insert ( "B" . into ( ) , Rc :: new ( sed_commodity) ) ;
514518 commodities. insert ( "C" . into ( ) , Rc :: new ( svd_commodity) ) ;
515519
516- let result = solve_investment_order ( & graph, & commodities) . unwrap ( ) ;
520+ let result = solve_investment_order ( & graph, & commodities) ;
517521
518522 // Expected order: C, B, A (leaf nodes first)
523+ // No cycles, so all investment sets should be `Single`
519524 assert_eq ! ( result. len( ) , 3 ) ;
520- assert_eq ! ( result[ 0 ] , "C" . into( ) ) ;
521- assert_eq ! ( result[ 1 ] , "B" . into( ) ) ;
522- assert_eq ! ( result[ 2 ] , "A" . into( ) ) ;
525+ assert_eq ! ( result[ 0 ] , InvestmentSet :: Single ( "C" . into( ) ) ) ;
526+ assert_eq ! ( result[ 1 ] , InvestmentSet :: Single ( "B" . into( ) ) ) ;
527+ assert_eq ! ( result[ 2 ] , InvestmentSet :: Single ( "A" . into( ) ) ) ;
523528 }
524529
525530 #[ rstest]
526- fn test_topo_sort_cyclic_graph ( sed_commodity : Commodity ) {
531+ fn test_solve_investment_order_cyclic_graph ( sed_commodity : Commodity ) {
527532 // Create a simple cyclic graph: A -> B -> A
528533 let mut graph = Graph :: new ( ) ;
529534
@@ -539,11 +544,14 @@ mod tests {
539544 commodities. insert ( "A" . into ( ) , Rc :: new ( sed_commodity. clone ( ) ) ) ;
540545 commodities. insert ( "B" . into ( ) , Rc :: new ( sed_commodity) ) ;
541546
542- // This should return an error due to the cycle
543- // The error message should flag commodity B
544- // Note: A is also involved in the cycle, but B is flagged as it is encountered first
545547 let result = solve_investment_order ( & graph, & commodities) ;
546- assert_error ! ( result, "Cycle detected in commodity graph for commodity B" ) ;
548+
549+ // Should be a single `Cycle` investment set containing both commodities
550+ assert_eq ! ( result. len( ) , 1 ) ;
551+ assert_eq ! (
552+ result[ 0 ] ,
553+ InvestmentSet :: Cycle ( vec![ "A" . into( ) , "B" . into( ) ] )
554+ ) ;
547555 }
548556
549557 #[ rstest]
@@ -570,8 +578,7 @@ mod tests {
570578 graph. add_edge ( node_c, node_d, GraphEdge :: Demand ) ;
571579
572580 // Validate the graph at DayNight level
573- let result = validate_commodities_graph ( & graph, & commodities, TimeSliceLevel :: Annual ) ;
574- assert ! ( result. is_ok( ) ) ;
581+ assert ! ( validate_commodities_graph( & graph, & commodities, TimeSliceLevel :: Annual ) . is_ok( ) ) ;
575582 }
576583
577584 #[ rstest]
@@ -596,8 +603,10 @@ mod tests {
596603 graph. add_edge ( node_a, node_b, GraphEdge :: Primary ( "process2" . into ( ) ) ) ;
597604
598605 // Validate the graph at DayNight level
599- let result = validate_commodities_graph ( & graph, & commodities, TimeSliceLevel :: DayNight ) ;
600- assert_error ! ( result, "SVD commodity A cannot be an input to a process" ) ;
606+ assert_error ! (
607+ validate_commodities_graph( & graph, & commodities, TimeSliceLevel :: DayNight ) ,
608+ "SVD commodity A cannot be an input to a process"
609+ ) ;
601610 }
602611
603612 #[ rstest]
@@ -614,8 +623,10 @@ mod tests {
614623 graph. add_edge ( node_a, node_b, GraphEdge :: Demand ) ;
615624
616625 // Validate the graph at DayNight level
617- let result = validate_commodities_graph ( & graph, & commodities, TimeSliceLevel :: DayNight ) ;
618- assert_error ! ( result, "SVD commodity A is demanded but has no producers" ) ;
626+ assert_error ! (
627+ validate_commodities_graph( & graph, & commodities, TimeSliceLevel :: DayNight ) ,
628+ "SVD commodity A is demanded but has no producers"
629+ ) ;
619630 }
620631
621632 #[ rstest]
@@ -633,9 +644,8 @@ mod tests {
633644 graph. add_edge ( node_b, node_a, GraphEdge :: Primary ( "process1" . into ( ) ) ) ;
634645
635646 // Validate the graph at DayNight level
636- let result = validate_commodities_graph ( & graph, & commodities, TimeSliceLevel :: DayNight ) ;
637647 assert_error ! (
638- result ,
648+ validate_commodities_graph ( & graph , & commodities , TimeSliceLevel :: DayNight ) ,
639649 "SED commodity B may be consumed but has no producers"
640650 ) ;
641651 }
@@ -661,9 +671,8 @@ mod tests {
661671 graph. add_edge ( node_a, node_c, GraphEdge :: Primary ( "process2" . into ( ) ) ) ;
662672
663673 // Validate the graph at DayNight level
664- let result = validate_commodities_graph ( & graph, & commodities, TimeSliceLevel :: DayNight ) ;
665674 assert_error ! (
666- result ,
675+ validate_commodities_graph ( & graph , & commodities , TimeSliceLevel :: DayNight ) ,
667676 "OTH commodity A cannot have both producers and consumers"
668677 ) ;
669678 }
0 commit comments