@@ -9,52 +9,30 @@ use std::collections::BTreeSet;
99
1010use super :: expr:: Expr ;
1111
12- impl < N , E > ComponentGraph < N , E >
13- where
14- N : Node ,
15- E : Edge ,
16- {
17- /// Returns a formula expression with fallbacks where possible for the `sum`
18- /// of the given component ids.
19- pub ( super ) fn fallback_expr (
20- & self ,
21- component_ids : impl IntoIterator < Item = u64 > ,
22- prefer_meters : bool ,
23- ) -> Result < Expr , Error > {
24- FallbackExpr {
25- prefer_meters,
26- graph : self ,
27- }
28- . generate ( BTreeSet :: from_iter ( component_ids) )
29- }
30- }
31-
32- struct FallbackExpr < ' a , N , E >
33- where
34- N : Node ,
35- E : Edge ,
36- {
12+ pub ( crate ) struct FallbackExpr {
3713 pub ( crate ) prefer_meters : bool ,
38- pub ( crate ) graph : & ' a ComponentGraph < N , E > ,
14+ pub ( crate ) meter_fallback_for_meters : bool ,
3915}
4016
41- impl < N , E > FallbackExpr < ' _ , N , E >
42- where
43- N : Node ,
44- E : Edge ,
45- {
46- fn generate ( & self , mut component_ids : BTreeSet < u64 > ) -> Result < Expr , Error > {
17+ impl FallbackExpr {
18+ pub ( crate ) fn generate < N : Node , E : Edge > (
19+ & self ,
20+ graph : & ComponentGraph < N , E > ,
21+ mut component_ids : BTreeSet < u64 > ,
22+ ) -> Result < Expr , Error > {
4723 let mut formula = None :: < Expr > ;
48- if self . graph . config . disable_fallback_components {
24+ if graph. config . disable_fallback_components {
4925 while let Some ( component_id) = component_ids. pop_first ( ) {
5026 formula = Self :: add_to_option ( formula, Expr :: component ( component_id) ) ;
5127 }
5228 return formula. ok_or ( Error :: internal ( "No components to generate formula." ) ) ;
5329 }
5430 while let Some ( component_id) = component_ids. pop_first ( ) {
55- if let Some ( expr) = self . meter_fallback ( component_id) ? {
31+ if let Some ( expr) = self . meter_fallback ( graph , component_id) ? {
5632 formula = Self :: add_to_option ( formula, expr) ;
57- } else if let Some ( expr) = self . component_fallback ( & mut component_ids, component_id) ? {
33+ } else if let Some ( expr) =
34+ self . component_fallback ( graph, & mut component_ids, component_id) ?
35+ {
5836 formula = Self :: add_to_option ( formula, expr) ;
5937 } else {
6038 formula = Self :: add_to_option ( formula, Expr :: component ( component_id) ) ;
@@ -65,18 +43,26 @@ where
6543 }
6644
6745 /// Returns a fallback expression for a meter component.
68- fn meter_fallback ( & self , component_id : u64 ) -> Result < Option < Expr > , Error > {
69- let component = self . graph . component ( component_id) ?;
70- if !component. is_meter ( ) || self . graph . has_meter_successors ( component_id) ? {
46+ fn meter_fallback < N : Node , E : Edge > (
47+ & self ,
48+ graph : & ComponentGraph < N , E > ,
49+ component_id : u64 ,
50+ ) -> Result < Option < Expr > , Error > {
51+ let component = graph. component ( component_id) ?;
52+ if !component. is_meter ( ) {
7153 return Ok ( None ) ;
7254 }
55+ let has_successor_meters = graph. has_meter_successors ( component_id) ?;
7356
74- if !self . graph . has_successors ( component_id ) ? {
57+ if !self . meter_fallback_for_meters && has_successor_meters {
7558 return Ok ( Some ( Expr :: component ( component_id) ) ) ;
7659 }
7760
78- let ( sum_of_successors, sum_of_coalesced_successors) = self
79- . graph
61+ if !graph. has_successors ( component_id) ? {
62+ return Ok ( Some ( Expr :: component ( component_id) ) ) ;
63+ }
64+
65+ let ( sum_of_successors, sum_of_coalesced_successors) = graph
8066 . successors ( component_id) ?
8167 . map ( |node| {
8268 (
9177
9278 let has_multiple_successors = matches ! ( sum_of_successors, Expr :: Add { .. } ) ;
9379
80+ // If a meter has exactly one successor and it is a meter, we consider
81+ // it to be a fallback meter. If there are multiple meter successors,
82+ // we return the meter without fallback.
83+ if has_successor_meters && has_multiple_successors {
84+ return Ok ( Some ( Expr :: component ( component_id) ) ) ;
85+ }
86+
9487 let mut coalesced = Expr :: component ( component_id) ;
9588
9689 if !self . prefer_meters {
@@ -102,11 +95,13 @@ where
10295 coalesced = coalesced. coalesce ( sum_of_coalesced_successors) ;
10396 } else {
10497 coalesced = coalesced. coalesce ( sum_of_successors) ;
105- coalesced = coalesced. coalesce ( Expr :: number ( 0.0 ) ) ;
98+ if !has_successor_meters {
99+ coalesced = coalesced. coalesce ( Expr :: number ( 0.0 ) ) ;
100+ }
106101 }
107102 } else if has_multiple_successors {
108103 coalesced = coalesced. coalesce ( sum_of_coalesced_successors) ;
109- } else {
104+ } else if !has_successor_meters {
110105 coalesced = coalesced. coalesce ( Expr :: number ( 0.0 ) ) ;
111106 }
112107
@@ -119,13 +114,14 @@ where
119114 /// - Battery Inverter
120115 /// - PV Inverter
121116 /// - EV Charger
122- fn component_fallback (
117+ fn component_fallback < N : Node , E : Edge > (
123118 & self ,
119+ graph : & ComponentGraph < N , E > ,
124120 component_ids : & mut BTreeSet < u64 > ,
125121 component_id : u64 ,
126122 ) -> Result < Option < Expr > , Error > {
127- let component = self . graph . component ( component_id) ?;
128- if !component. is_battery_inverter ( & self . graph . config )
123+ let component = graph. component ( component_id) ?;
124+ if !component. is_battery_inverter ( & graph. config )
129125 && !component. is_chp ( )
130126 && !component. is_pv_inverter ( )
131127 && !component. is_ev_charger ( )
@@ -135,8 +131,7 @@ where
135131
136132 // If predecessors have other successors that are not in the list of
137133 // component ids, the predecessors can't be used as fallback.
138- let siblings = self
139- . graph
134+ let siblings = graph
140135 . siblings_from_predecessors ( component_id) ?
141136 . filter ( |sibling| sibling. component_id ( ) != component_id)
142137 . collect :: < Vec < _ > > ( ) ;
@@ -151,8 +146,7 @@ where
151146 }
152147
153148 // Collect predecessor meter ids.
154- let predecessor_ids: BTreeSet < u64 > = self
155- . graph
149+ let predecessor_ids: BTreeSet < u64 > = graph
156150 . predecessors ( component_id) ?
157151 . filter ( |x| x. is_meter ( ) )
158152 . map ( |x| x. component_id ( ) )
@@ -169,7 +163,7 @@ where
169163 component_ids. remove ( & sibling. component_id ( ) ) ;
170164 }
171165
172- Ok ( Some ( self . generate ( predecessor_ids) ?) )
166+ Ok ( Some ( self . generate ( graph , predecessor_ids) ?) )
173167 }
174168
175169 fn add_to_option ( expr : Option < Expr > , other : Expr ) -> Option < Expr > {
@@ -183,7 +177,12 @@ where
183177
184178#[ cfg( test) ]
185179mod tests {
186- use crate :: { graph:: test_utils:: ComponentGraphBuilder , ComponentGraphConfig , Error } ;
180+ use std:: collections:: BTreeSet ;
181+
182+ use crate :: {
183+ graph:: { formulas:: fallback:: FallbackExpr , test_utils:: ComponentGraphBuilder } ,
184+ ComponentGraphConfig , Error ,
185+ } ;
187186
188187 #[ test]
189188 fn test_meter_fallback ( ) -> Result < ( ) , Error > {
@@ -202,26 +201,76 @@ mod tests {
202201 assert_eq ! ( meter_bat_chain. component_id( ) , 2 ) ;
203202
204203 let graph = builder. build ( None ) ?;
205- let expr = graph. fallback_expr ( vec ! [ 1 , 2 ] , false ) ?;
204+ let expr = FallbackExpr {
205+ prefer_meters : true ,
206+ meter_fallback_for_meters : true ,
207+ }
208+ . generate ( & graph, BTreeSet :: from ( [ 1 ] ) ) ?;
209+ assert_eq ! ( expr. to_string( ) , "COALESCE(#1, #2)" ) ;
210+
211+ let expr = FallbackExpr {
212+ prefer_meters : true ,
213+ meter_fallback_for_meters : true ,
214+ }
215+ . generate ( & graph, BTreeSet :: from ( [ 1 , 2 ] ) ) ?;
216+ assert_eq ! ( expr. to_string( ) , "COALESCE(#1, #2) + COALESCE(#2, #3, 0.0)" ) ;
217+
218+ let expr = FallbackExpr {
219+ prefer_meters : false ,
220+ meter_fallback_for_meters : false ,
221+ }
222+ . generate ( & graph, BTreeSet :: from ( [ 1 , 2 ] ) ) ?;
206223 assert_eq ! ( expr. to_string( ) , "#1 + COALESCE(#3, #2, 0.0)" ) ;
207224
208- let expr = graph. fallback_expr ( vec ! [ 1 , 2 ] , true ) ?;
225+ let expr = FallbackExpr {
226+ prefer_meters : true ,
227+ meter_fallback_for_meters : false ,
228+ }
229+ . generate ( & graph, BTreeSet :: from ( [ 1 , 2 ] ) ) ?;
209230 assert_eq ! ( expr. to_string( ) , "#1 + COALESCE(#2, #3, 0.0)" ) ;
210231
211- let expr = graph. fallback_expr ( vec ! [ 3 ] , true ) ?;
232+ let expr = FallbackExpr {
233+ prefer_meters : true ,
234+ meter_fallback_for_meters : false ,
235+ }
236+ . generate ( & graph, BTreeSet :: from ( [ 3 ] ) ) ?;
237+ assert_eq ! ( expr. to_string( ) , "COALESCE(#2, #3, 0.0)" ) ;
238+ let expr = FallbackExpr {
239+ prefer_meters : true ,
240+ meter_fallback_for_meters : true ,
241+ }
242+ . generate ( & graph, BTreeSet :: from ( [ 3 ] ) ) ?;
243+ assert_eq ! ( expr. to_string( ) , "COALESCE(#2, #3, 0.0)" ) ;
244+ let expr = FallbackExpr {
245+ prefer_meters : true ,
246+ meter_fallback_for_meters : true ,
247+ }
248+ . generate ( & graph, BTreeSet :: from ( [ 2 ] ) ) ?;
212249 assert_eq ! ( expr. to_string( ) , "COALESCE(#2, #3, 0.0)" ) ;
213250
214251 let graph = builder. build ( Some ( ComponentGraphConfig {
215252 disable_fallback_components : true ,
216253 ..Default :: default ( )
217254 } ) ) ?;
218- let expr = graph. fallback_expr ( vec ! [ 1 , 2 ] , false ) ?;
255+ let expr = FallbackExpr {
256+ prefer_meters : false ,
257+ meter_fallback_for_meters : false ,
258+ }
259+ . generate ( & graph, BTreeSet :: from ( [ 1 , 2 ] ) ) ?;
219260 assert_eq ! ( expr. to_string( ) , "#1 + #2" ) ;
220261
221- let expr = graph. fallback_expr ( vec ! [ 1 , 2 ] , true ) ?;
262+ let expr = FallbackExpr {
263+ prefer_meters : true ,
264+ meter_fallback_for_meters : false ,
265+ }
266+ . generate ( & graph, BTreeSet :: from ( [ 1 , 2 ] ) ) ?;
222267 assert_eq ! ( expr. to_string( ) , "#1 + #2" ) ;
223268
224- let expr = graph. fallback_expr ( vec ! [ 3 ] , true ) ?;
269+ let expr = FallbackExpr {
270+ prefer_meters : true ,
271+ meter_fallback_for_meters : false ,
272+ }
273+ . generate ( & graph, BTreeSet :: from ( [ 3 ] ) ) ?;
225274 assert_eq ! ( expr. to_string( ) , "#3" ) ;
226275
227276 // Add a battery meter with three inverter and three batteries
@@ -231,7 +280,11 @@ mod tests {
231280 assert_eq ! ( meter_bat_chain. component_id( ) , 5 ) ;
232281
233282 let graph = builder. build ( None ) ?;
234- let expr = graph. fallback_expr ( vec ! [ 3 , 5 ] , false ) ?;
283+ let expr = FallbackExpr {
284+ prefer_meters : false ,
285+ meter_fallback_for_meters : false ,
286+ }
287+ . generate ( & graph, BTreeSet :: from ( [ 3 , 5 ] ) ) ?;
235288 assert_eq ! (
236289 expr. to_string( ) ,
237290 concat!(
@@ -244,7 +297,11 @@ mod tests {
244297 )
245298 ) ;
246299
247- let expr = graph. fallback_expr ( vec ! [ 2 , 5 ] , true ) ?;
300+ let expr = FallbackExpr {
301+ prefer_meters : true ,
302+ meter_fallback_for_meters : false ,
303+ }
304+ . generate ( & graph, BTreeSet :: from ( [ 2 , 5 ] ) ) ?;
248305 assert_eq ! (
249306 expr. to_string( ) ,
250307 concat!(
@@ -253,7 +310,11 @@ mod tests {
253310 )
254311 ) ;
255312
256- let expr = graph. fallback_expr ( vec ! [ 2 , 6 , 7 , 8 ] , true ) ?;
313+ let expr = FallbackExpr {
314+ prefer_meters : true ,
315+ meter_fallback_for_meters : false ,
316+ }
317+ . generate ( & graph, BTreeSet :: from ( [ 2 , 6 , 7 , 8 ] ) ) ?;
257318 assert_eq ! (
258319 expr. to_string( ) ,
259320 concat!(
@@ -262,7 +323,11 @@ mod tests {
262323 )
263324 ) ;
264325
265- let expr = graph. fallback_expr ( vec ! [ 2 , 7 , 8 ] , true ) ?;
326+ let expr = FallbackExpr {
327+ prefer_meters : true ,
328+ meter_fallback_for_meters : false ,
329+ }
330+ . generate ( & graph, BTreeSet :: from ( [ 2 , 7 , 8 ] ) ) ?;
266331 assert_eq ! (
267332 expr. to_string( ) ,
268333 "COALESCE(#2, #3, 0.0) + COALESCE(#7, 0.0) + COALESCE(#8, 0.0)"
@@ -272,16 +337,32 @@ mod tests {
272337 disable_fallback_components : true ,
273338 ..Default :: default ( )
274339 } ) ) ?;
275- let expr = graph. fallback_expr ( vec ! [ 3 , 5 ] , false ) ?;
340+ let expr = FallbackExpr {
341+ prefer_meters : false ,
342+ meter_fallback_for_meters : false ,
343+ }
344+ . generate ( & graph, BTreeSet :: from ( [ 3 , 5 ] ) ) ?;
276345 assert_eq ! ( expr. to_string( ) , "#3 + #5" ) ;
277346
278- let expr = graph. fallback_expr ( vec ! [ 2 , 5 ] , true ) ?;
347+ let expr = FallbackExpr {
348+ prefer_meters : true ,
349+ meter_fallback_for_meters : false ,
350+ }
351+ . generate ( & graph, BTreeSet :: from ( [ 2 , 5 ] ) ) ?;
279352 assert_eq ! ( expr. to_string( ) , "#2 + #5" ) ;
280353
281- let expr = graph. fallback_expr ( vec ! [ 2 , 6 , 7 , 8 ] , true ) ?;
354+ let expr = FallbackExpr {
355+ prefer_meters : true ,
356+ meter_fallback_for_meters : false ,
357+ }
358+ . generate ( & graph, BTreeSet :: from ( [ 2 , 6 , 7 , 8 ] ) ) ?;
282359 assert_eq ! ( expr. to_string( ) , "#2 + #6 + #7 + #8" ) ;
283360
284- let expr = graph. fallback_expr ( vec ! [ 2 , 7 , 8 ] , true ) ?;
361+ let expr = FallbackExpr {
362+ prefer_meters : true ,
363+ meter_fallback_for_meters : false ,
364+ }
365+ . generate ( & graph, BTreeSet :: from ( [ 2 , 7 , 8 ] ) ) ?;
285366 assert_eq ! ( expr. to_string( ) , "#2 + #7 + #8" ) ;
286367
287368 let meter = builder. meter ( ) ;
@@ -296,7 +377,11 @@ mod tests {
296377 assert_eq ! ( pv_inverter. component_id( ) , 14 ) ;
297378
298379 let graph = builder. build ( None ) ?;
299- let expr = graph. fallback_expr ( vec ! [ 5 , 12 ] , true ) ?;
380+ let expr = FallbackExpr {
381+ prefer_meters : true ,
382+ meter_fallback_for_meters : false ,
383+ }
384+ . generate ( & graph, BTreeSet :: from ( [ 5 , 12 ] ) ) ?;
300385 assert_eq ! (
301386 expr. to_string( ) ,
302387 concat!(
@@ -305,7 +390,11 @@ mod tests {
305390 )
306391 ) ;
307392
308- let expr = graph. fallback_expr ( vec ! [ 7 , 14 ] , false ) ?;
393+ let expr = FallbackExpr {
394+ prefer_meters : false ,
395+ meter_fallback_for_meters : false ,
396+ }
397+ . generate ( & graph, BTreeSet :: from ( [ 7 , 14 ] ) ) ?;
309398 assert_eq ! ( expr. to_string( ) , "COALESCE(#7, 0.0) + COALESCE(#14, 0.0)" ) ;
310399
311400 Ok ( ( ) )
0 commit comments