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_component_meter(&self, component_id: u64) -> Result<bool, Error> {
98 Ok(self.is_pv_meter(component_id)?
99 || self.is_battery_meter(component_id)?
100 || self.is_ev_charger_meter(component_id)?
101 || self.is_chp_meter(component_id)?
102 || self.is_wind_turbine_meter(component_id)?)
103 }
104
105 pub fn is_battery_chain(&self, component_id: u64) -> Result<bool, Error> {
112 Ok(self.is_battery_meter(component_id)? || {
113 let component = self.component(component_id)?;
114 component.is_battery() || component.is_battery_inverter(&self.config)
115 })
116 }
117
118 pub fn is_pv_chain(&self, component_id: u64) -> Result<bool, Error> {
124 Ok(self.is_pv_meter(component_id)? || self.component(component_id)?.is_pv_inverter())
125 }
126
127 pub fn is_chp_chain(&self, component_id: u64) -> Result<bool, Error> {
133 Ok(self.is_chp_meter(component_id)? || self.component(component_id)?.is_chp())
134 }
135
136 pub fn is_ev_charger_chain(&self, component_id: u64) -> Result<bool, Error> {
142 Ok(
143 self.is_ev_charger_meter(component_id)?
144 || self.component(component_id)?.is_ev_charger(),
145 )
146 }
147
148 pub fn is_wind_turbine_chain(&self, component_id: u64) -> Result<bool, Error> {
154 Ok(self.is_wind_turbine_meter(component_id)?
155 || self.component(component_id)?.is_wind_turbine())
156 }
157
158 pub fn is_component_chain(&self, component_id: u64) -> Result<bool, Error> {
168 Ok(self.is_battery_chain(component_id)?
169 || self.is_pv_chain(component_id)?
170 || self.is_ev_charger_chain(component_id)?
171 || self.is_chp_chain(component_id)?
172 || self.is_wind_turbine_chain(component_id)?)
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use crate::ComponentCategory;
180 use crate::ComponentGraphConfig;
181 use crate::InverterType;
182 use crate::component_category::BatteryType;
183 use crate::component_category::EvChargerType;
184 use crate::error::Error;
185 use crate::graph::test_utils::{TestComponent, TestConnection};
186
187 fn nodes_and_edges() -> (Vec<TestComponent>, Vec<TestConnection>) {
188 let components = vec![
189 TestComponent::new(1, ComponentCategory::GridConnectionPoint),
190 TestComponent::new(2, ComponentCategory::Meter),
191 TestComponent::new(3, ComponentCategory::Meter),
192 TestComponent::new(4, ComponentCategory::Inverter(InverterType::Battery)),
193 TestComponent::new(5, ComponentCategory::Battery(BatteryType::NaIon)),
194 TestComponent::new(6, ComponentCategory::Meter),
195 TestComponent::new(7, ComponentCategory::Inverter(InverterType::Battery)),
196 TestComponent::new(8, ComponentCategory::Battery(BatteryType::Unspecified)),
197 TestComponent::new(9, ComponentCategory::Meter),
198 TestComponent::new(10, ComponentCategory::Inverter(InverterType::Pv)),
199 TestComponent::new(11, ComponentCategory::Inverter(InverterType::Pv)),
200 TestComponent::new(12, ComponentCategory::Meter),
201 TestComponent::new(13, ComponentCategory::Chp),
202 TestComponent::new(14, ComponentCategory::Meter),
203 TestComponent::new(15, ComponentCategory::Chp),
204 TestComponent::new(16, ComponentCategory::Inverter(InverterType::Pv)),
205 TestComponent::new(17, ComponentCategory::Inverter(InverterType::Battery)),
206 TestComponent::new(18, ComponentCategory::Battery(BatteryType::LiIon)),
207 ];
208 let connections = vec![
209 TestConnection::new(1, 2),
211 TestConnection::new(2, 3),
213 TestConnection::new(3, 4),
214 TestConnection::new(4, 5),
215 TestConnection::new(2, 6),
217 TestConnection::new(6, 7),
218 TestConnection::new(7, 8),
219 TestConnection::new(2, 9),
221 TestConnection::new(9, 10),
222 TestConnection::new(9, 11),
223 TestConnection::new(2, 12),
225 TestConnection::new(12, 13),
226 TestConnection::new(2, 14),
228 TestConnection::new(14, 15),
229 TestConnection::new(14, 16),
230 TestConnection::new(14, 17),
231 TestConnection::new(17, 18),
232 ];
233
234 (components, connections)
235 }
236
237 fn with_multiple_grid_meters() -> (Vec<TestComponent>, Vec<TestConnection>) {
238 let (mut components, mut connections) = nodes_and_edges();
239
240 components.push(TestComponent::new(19, ComponentCategory::Meter));
242 connections.push(TestConnection::new(1, 19));
243
244 components.push(TestComponent::new(20, ComponentCategory::Meter));
247 connections.push(TestConnection::new(1, 20));
248
249 components.push(TestComponent::new(21, ComponentCategory::Meter));
251 components.push(TestComponent::new(
252 22,
253 ComponentCategory::Inverter(InverterType::Battery),
254 ));
255 components.push(TestComponent::new(
256 23,
257 ComponentCategory::Battery(BatteryType::Unspecified),
258 ));
259 connections.push(TestConnection::new(20, 21));
260 connections.push(TestConnection::new(21, 22));
261 connections.push(TestConnection::new(22, 23));
262
263 components.push(TestComponent::new(24, ComponentCategory::Meter));
265 components.push(TestComponent::new(
266 25,
267 ComponentCategory::Inverter(InverterType::Pv),
268 ));
269 connections.push(TestConnection::new(20, 24));
270 connections.push(TestConnection::new(24, 25));
271
272 (components, connections)
273 }
274
275 fn without_grid_meters() -> (Vec<TestComponent>, Vec<TestConnection>) {
276 let (mut components, mut connections) = nodes_and_edges();
277
278 components.push(TestComponent::new(20, ComponentCategory::Meter));
281 components.push(TestComponent::new(
282 21,
283 ComponentCategory::EvCharger(EvChargerType::Ac),
284 ));
285 connections.push(TestConnection::new(1, 20));
286 connections.push(TestConnection::new(20, 21));
287
288 (components, connections)
289 }
290
291 fn find_matching_components(
292 components: Vec<TestComponent>,
293 connections: Vec<TestConnection>,
294 filter: impl Fn(&ComponentGraph<TestComponent, TestConnection>, u64) -> Result<bool, Error>,
295 ) -> Result<Vec<u64>, Error> {
296 let config = ComponentGraphConfig::default();
297
298 let graph = ComponentGraph::try_new(components.clone(), connections.clone(), config)?;
299
300 let mut found_meters = vec![];
301 for comp in graph.components() {
302 if filter(&graph, comp.component_id())? {
303 found_meters.push(comp.component_id());
304 }
305 }
306
307 Ok(found_meters)
308 }
309
310 #[test]
311 fn test_is_pv_meter() -> Result<(), Error> {
312 let (components, connections) = nodes_and_edges();
313 assert_eq!(
314 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
315 vec![9],
316 );
317
318 let (components, connections) = with_multiple_grid_meters();
319 assert_eq!(
320 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
321 vec![9, 24],
322 );
323
324 let (components, connections) = without_grid_meters();
325 assert_eq!(
326 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
327 vec![9],
328 );
329
330 Ok(())
331 }
332
333 #[test]
334 fn test_is_battery_meter() -> Result<(), Error> {
335 let (components, connections) = nodes_and_edges();
336 assert_eq!(
337 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
338 vec![3, 6],
339 );
340
341 let (components, connections) = with_multiple_grid_meters();
342 assert_eq!(
343 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
344 vec![3, 6, 21],
345 );
346
347 let (components, connections) = without_grid_meters();
348 assert_eq!(
349 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
350 vec![3, 6],
351 );
352
353 Ok(())
354 }
355
356 #[test]
357 fn test_is_chp_meter() -> Result<(), Error> {
358 let (components, connections) = nodes_and_edges();
359 assert_eq!(
360 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
361 vec![12],
362 );
363
364 let (components, connections) = with_multiple_grid_meters();
365 assert_eq!(
366 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
367 vec![12],
368 );
369
370 let (components, connections) = without_grid_meters();
371 assert_eq!(
372 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
373 vec![12],
374 );
375
376 Ok(())
377 }
378
379 #[test]
380 fn test_is_ev_charger_meter() -> Result<(), Error> {
381 let (components, connections) = nodes_and_edges();
382 assert_eq!(
383 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
384 vec![],
385 );
386
387 let (components, connections) = with_multiple_grid_meters();
388 assert_eq!(
389 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
390 vec![],
391 );
392
393 let (components, connections) = without_grid_meters();
394 assert_eq!(
395 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
396 vec![20],
397 );
398
399 Ok(())
400 }
401}