1use crate::{ComponentGraph, Edge, Error, Node, component_category::CategoryPredicates};
7
8impl<N, E> ComponentGraph<N, E>
10where
11 N: Node,
12 E: Edge,
13{
14 pub fn is_pv_meter(&self, component_id: u64) -> Result<bool, Error> {
20 let mut has_successors = false;
21 Ok(self.component(component_id)?.is_meter()
22 && self.successors(component_id)?.all(|n| {
23 has_successors = true;
24 n.is_pv_inverter()
25 })
26 && has_successors)
27 }
28
29 pub fn is_battery_meter(&self, component_id: u64) -> Result<bool, Error> {
35 let mut has_successors = false;
36 Ok(self.component(component_id)?.is_meter()
37 && self.successors(component_id)?.all(|n| {
38 has_successors = true;
39 n.is_battery_inverter(&self.config)
40 })
41 && has_successors)
42 }
43
44 pub fn is_ev_charger_meter(&self, component_id: u64) -> Result<bool, Error> {
50 let mut has_successors = false;
51 Ok(self.component(component_id)?.is_meter()
52 && self.successors(component_id)?.all(|n| {
53 has_successors = true;
54 n.is_ev_charger()
55 })
56 && has_successors)
57 }
58
59 pub fn is_chp_meter(&self, component_id: u64) -> Result<bool, Error> {
65 let mut has_successors = false;
66 Ok(self.component(component_id)?.is_meter()
67 && self.successors(component_id)?.all(|n| {
68 has_successors = true;
69 n.is_chp()
70 })
71 && has_successors)
72 }
73
74 pub fn is_wind_turbine_meter(&self, component_id: u64) -> Result<bool, Error> {
80 let mut has_successors = false;
81 Ok(self.component(component_id)?.is_meter()
82 && self.successors(component_id)?.all(|n| {
83 has_successors = true;
84 n.is_wind_turbine()
85 })
86 && has_successors)
87 }
88
89 pub fn is_steam_boiler_meter(&self, component_id: u64) -> Result<bool, Error> {
95 let mut has_successors = false;
96 Ok(self.component(component_id)?.is_meter()
97 && self.successors(component_id)?.all(|n| {
98 has_successors = true;
99 n.is_steam_boiler()
100 })
101 && has_successors)
102 }
103
104 pub fn is_component_meter(&self, component_id: u64) -> Result<bool, Error> {
114 Ok(self.is_pv_meter(component_id)?
115 || self.is_battery_meter(component_id)?
116 || self.is_ev_charger_meter(component_id)?
117 || self.is_chp_meter(component_id)?
118 || self.is_wind_turbine_meter(component_id)?
119 || self.is_steam_boiler_meter(component_id)?)
120 }
121
122 pub fn is_battery_chain(&self, component_id: u64) -> Result<bool, Error> {
129 Ok(self.is_battery_meter(component_id)? || {
130 let component = self.component(component_id)?;
131 component.is_battery() || component.is_battery_inverter(&self.config)
132 })
133 }
134
135 pub fn is_pv_chain(&self, component_id: u64) -> Result<bool, Error> {
141 Ok(self.is_pv_meter(component_id)? || self.component(component_id)?.is_pv_inverter())
142 }
143
144 pub fn is_chp_chain(&self, component_id: u64) -> Result<bool, Error> {
150 Ok(self.is_chp_meter(component_id)? || self.component(component_id)?.is_chp())
151 }
152
153 pub fn is_ev_charger_chain(&self, component_id: u64) -> Result<bool, Error> {
159 Ok(
160 self.is_ev_charger_meter(component_id)?
161 || self.component(component_id)?.is_ev_charger(),
162 )
163 }
164
165 pub fn is_wind_turbine_chain(&self, component_id: u64) -> Result<bool, Error> {
171 Ok(self.is_wind_turbine_meter(component_id)?
172 || self.component(component_id)?.is_wind_turbine())
173 }
174
175 pub fn is_steam_boiler_chain(&self, component_id: u64) -> Result<bool, Error> {
181 Ok(self.is_steam_boiler_meter(component_id)?
182 || self.component(component_id)?.is_steam_boiler())
183 }
184
185 pub fn is_component_chain(&self, component_id: u64) -> Result<bool, Error> {
196 Ok(self.is_battery_chain(component_id)?
197 || self.is_pv_chain(component_id)?
198 || self.is_ev_charger_chain(component_id)?
199 || self.is_chp_chain(component_id)?
200 || self.is_wind_turbine_chain(component_id)?
201 || self.is_steam_boiler_chain(component_id)?)
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use crate::ComponentCategory;
209 use crate::ComponentGraphConfig;
210 use crate::InverterType;
211 use crate::component_category::BatteryType;
212 use crate::component_category::EvChargerType;
213 use crate::error::Error;
214 use crate::graph::test_utils::{TestComponent, TestConnection};
215
216 fn nodes_and_edges() -> (Vec<TestComponent>, Vec<TestConnection>) {
217 let components = vec![
218 TestComponent::new(1, ComponentCategory::GridConnectionPoint),
219 TestComponent::new(2, ComponentCategory::Meter),
220 TestComponent::new(3, ComponentCategory::Meter),
221 TestComponent::new(4, ComponentCategory::Inverter(InverterType::Battery)),
222 TestComponent::new(5, ComponentCategory::Battery(BatteryType::NaIon)),
223 TestComponent::new(6, ComponentCategory::Meter),
224 TestComponent::new(7, ComponentCategory::Inverter(InverterType::Battery)),
225 TestComponent::new(8, ComponentCategory::Battery(BatteryType::Unspecified)),
226 TestComponent::new(9, ComponentCategory::Meter),
227 TestComponent::new(10, ComponentCategory::Inverter(InverterType::Pv)),
228 TestComponent::new(11, ComponentCategory::Inverter(InverterType::Pv)),
229 TestComponent::new(12, ComponentCategory::Meter),
230 TestComponent::new(13, ComponentCategory::Chp),
231 TestComponent::new(14, ComponentCategory::Meter),
232 TestComponent::new(15, ComponentCategory::Chp),
233 TestComponent::new(16, ComponentCategory::Inverter(InverterType::Pv)),
234 TestComponent::new(17, ComponentCategory::Inverter(InverterType::Battery)),
235 TestComponent::new(18, ComponentCategory::Battery(BatteryType::LiIon)),
236 TestComponent::new(19, ComponentCategory::Meter),
237 TestComponent::new(20, ComponentCategory::SteamBoiler),
238 ];
239 let connections = vec![
240 TestConnection::new(1, 2),
242 TestConnection::new(2, 3),
244 TestConnection::new(3, 4),
245 TestConnection::new(4, 5),
246 TestConnection::new(2, 6),
248 TestConnection::new(6, 7),
249 TestConnection::new(7, 8),
250 TestConnection::new(2, 9),
252 TestConnection::new(9, 10),
253 TestConnection::new(9, 11),
254 TestConnection::new(2, 12),
256 TestConnection::new(12, 13),
257 TestConnection::new(2, 14),
259 TestConnection::new(14, 15),
260 TestConnection::new(14, 16),
261 TestConnection::new(14, 17),
262 TestConnection::new(17, 18),
263 TestConnection::new(2, 19),
265 TestConnection::new(19, 20),
266 ];
267
268 (components, connections)
269 }
270
271 fn with_multiple_grid_meters() -> (Vec<TestComponent>, Vec<TestConnection>) {
272 let (mut components, mut connections) = nodes_and_edges();
273
274 components.push(TestComponent::new(21, ComponentCategory::Meter));
276 connections.push(TestConnection::new(1, 21));
277
278 components.push(TestComponent::new(22, ComponentCategory::Meter));
281 connections.push(TestConnection::new(1, 22));
282
283 components.push(TestComponent::new(23, ComponentCategory::Meter));
285 components.push(TestComponent::new(
286 24,
287 ComponentCategory::Inverter(InverterType::Battery),
288 ));
289 components.push(TestComponent::new(
290 25,
291 ComponentCategory::Battery(BatteryType::Unspecified),
292 ));
293 connections.push(TestConnection::new(22, 23));
294 connections.push(TestConnection::new(23, 24));
295 connections.push(TestConnection::new(24, 25));
296
297 components.push(TestComponent::new(26, ComponentCategory::Meter));
299 components.push(TestComponent::new(
300 27,
301 ComponentCategory::Inverter(InverterType::Pv),
302 ));
303 connections.push(TestConnection::new(22, 26));
304 connections.push(TestConnection::new(26, 27));
305
306 components.push(TestComponent::new(28, ComponentCategory::Meter));
308 components.push(TestComponent::new(29, ComponentCategory::SteamBoiler));
309 connections.push(TestConnection::new(22, 28));
310 connections.push(TestConnection::new(28, 29));
311
312 (components, connections)
313 }
314
315 fn without_grid_meters() -> (Vec<TestComponent>, Vec<TestConnection>) {
316 let (mut components, mut connections) = nodes_and_edges();
317
318 components.push(TestComponent::new(21, ComponentCategory::Meter));
321 components.push(TestComponent::new(
322 22,
323 ComponentCategory::EvCharger(EvChargerType::Ac),
324 ));
325 connections.push(TestConnection::new(1, 21));
326 connections.push(TestConnection::new(21, 22));
327
328 (components, connections)
329 }
330
331 fn find_matching_components(
332 components: Vec<TestComponent>,
333 connections: Vec<TestConnection>,
334 filter: impl Fn(&ComponentGraph<TestComponent, TestConnection>, u64) -> Result<bool, Error>,
335 ) -> Result<Vec<u64>, Error> {
336 let config = ComponentGraphConfig::default();
337
338 let graph = ComponentGraph::try_new(components.clone(), connections.clone(), config)?;
339
340 let mut found_meters = vec![];
341 for comp in graph.components() {
342 if filter(&graph, comp.component_id())? {
343 found_meters.push(comp.component_id());
344 }
345 }
346
347 Ok(found_meters)
348 }
349
350 #[test]
351 fn test_is_pv_meter() -> Result<(), Error> {
352 let (components, connections) = nodes_and_edges();
353 assert_eq!(
354 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
355 vec![9],
356 );
357
358 let (components, connections) = with_multiple_grid_meters();
359 assert_eq!(
360 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
361 vec![9, 26],
362 );
363
364 let (components, connections) = without_grid_meters();
365 assert_eq!(
366 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
367 vec![9],
368 );
369
370 Ok(())
371 }
372
373 #[test]
374 fn test_is_battery_meter() -> Result<(), Error> {
375 let (components, connections) = nodes_and_edges();
376 assert_eq!(
377 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
378 vec![3, 6],
379 );
380
381 let (components, connections) = with_multiple_grid_meters();
382 assert_eq!(
383 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
384 vec![3, 6, 23],
385 );
386
387 let (components, connections) = without_grid_meters();
388 assert_eq!(
389 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
390 vec![3, 6],
391 );
392
393 Ok(())
394 }
395
396 #[test]
397 fn test_is_chp_meter() -> Result<(), Error> {
398 let (components, connections) = nodes_and_edges();
399 assert_eq!(
400 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
401 vec![12],
402 );
403
404 let (components, connections) = with_multiple_grid_meters();
405 assert_eq!(
406 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
407 vec![12],
408 );
409
410 let (components, connections) = without_grid_meters();
411 assert_eq!(
412 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
413 vec![12],
414 );
415
416 Ok(())
417 }
418
419 #[test]
420 fn test_is_ev_charger_meter() -> Result<(), Error> {
421 let (components, connections) = nodes_and_edges();
422 assert_eq!(
423 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
424 vec![],
425 );
426
427 let (components, connections) = with_multiple_grid_meters();
428 assert_eq!(
429 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
430 vec![],
431 );
432
433 let (components, connections) = without_grid_meters();
434 assert_eq!(
435 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
436 vec![21],
437 );
438
439 Ok(())
440 }
441
442 #[test]
443 fn test_is_steam_boiler_meter() -> Result<(), Error> {
444 let (components, connections) = nodes_and_edges();
445 assert_eq!(
446 find_matching_components(
447 components,
448 connections,
449 ComponentGraph::is_steam_boiler_meter
450 )?,
451 vec![19],
452 );
453
454 let (components, connections) = with_multiple_grid_meters();
455 assert_eq!(
456 find_matching_components(
457 components,
458 connections,
459 ComponentGraph::is_steam_boiler_meter
460 )?,
461 vec![19, 28],
462 );
463
464 let (components, connections) = without_grid_meters();
465 assert_eq!(
466 find_matching_components(
467 components,
468 connections,
469 ComponentGraph::is_steam_boiler_meter
470 )?,
471 vec![19],
472 );
473
474 Ok(())
475 }
476}