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_component_meter(&self, component_id: u64) -> Result<bool, Error> {
82 Ok(self.is_pv_meter(component_id)?
83 || self.is_battery_meter(component_id)?
84 || self.is_ev_charger_meter(component_id)?
85 || self.is_chp_meter(component_id)?)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::ComponentCategory;
93 use crate::ComponentGraphConfig;
94 use crate::InverterType;
95 use crate::component_category::BatteryType;
96 use crate::component_category::EvChargerType;
97 use crate::error::Error;
98 use crate::graph::test_utils::{TestComponent, TestConnection};
99
100 fn nodes_and_edges() -> (Vec<TestComponent>, Vec<TestConnection>) {
101 let components = vec![
102 TestComponent::new(1, ComponentCategory::GridConnectionPoint),
103 TestComponent::new(2, ComponentCategory::Meter),
104 TestComponent::new(3, ComponentCategory::Meter),
105 TestComponent::new(4, ComponentCategory::Inverter(InverterType::Battery)),
106 TestComponent::new(5, ComponentCategory::Battery(BatteryType::NaIon)),
107 TestComponent::new(6, ComponentCategory::Meter),
108 TestComponent::new(7, ComponentCategory::Inverter(InverterType::Battery)),
109 TestComponent::new(8, ComponentCategory::Battery(BatteryType::Unspecified)),
110 TestComponent::new(9, ComponentCategory::Meter),
111 TestComponent::new(10, ComponentCategory::Inverter(InverterType::Pv)),
112 TestComponent::new(11, ComponentCategory::Inverter(InverterType::Pv)),
113 TestComponent::new(12, ComponentCategory::Meter),
114 TestComponent::new(13, ComponentCategory::Chp),
115 TestComponent::new(14, ComponentCategory::Meter),
116 TestComponent::new(15, ComponentCategory::Chp),
117 TestComponent::new(16, ComponentCategory::Inverter(InverterType::Pv)),
118 TestComponent::new(17, ComponentCategory::Inverter(InverterType::Battery)),
119 TestComponent::new(18, ComponentCategory::Battery(BatteryType::LiIon)),
120 ];
121 let connections = vec![
122 TestConnection::new(1, 2),
124 TestConnection::new(2, 3),
126 TestConnection::new(3, 4),
127 TestConnection::new(4, 5),
128 TestConnection::new(2, 6),
130 TestConnection::new(6, 7),
131 TestConnection::new(7, 8),
132 TestConnection::new(2, 9),
134 TestConnection::new(9, 10),
135 TestConnection::new(9, 11),
136 TestConnection::new(2, 12),
138 TestConnection::new(12, 13),
139 TestConnection::new(2, 14),
141 TestConnection::new(14, 15),
142 TestConnection::new(14, 16),
143 TestConnection::new(14, 17),
144 TestConnection::new(17, 18),
145 ];
146
147 (components, connections)
148 }
149
150 fn with_multiple_grid_meters() -> (Vec<TestComponent>, Vec<TestConnection>) {
151 let (mut components, mut connections) = nodes_and_edges();
152
153 components.push(TestComponent::new(19, ComponentCategory::Meter));
155 connections.push(TestConnection::new(1, 19));
156
157 components.push(TestComponent::new(20, ComponentCategory::Meter));
160 connections.push(TestConnection::new(1, 20));
161
162 components.push(TestComponent::new(21, ComponentCategory::Meter));
164 components.push(TestComponent::new(
165 22,
166 ComponentCategory::Inverter(InverterType::Battery),
167 ));
168 components.push(TestComponent::new(
169 23,
170 ComponentCategory::Battery(BatteryType::Unspecified),
171 ));
172 connections.push(TestConnection::new(20, 21));
173 connections.push(TestConnection::new(21, 22));
174 connections.push(TestConnection::new(22, 23));
175
176 components.push(TestComponent::new(24, ComponentCategory::Meter));
178 components.push(TestComponent::new(
179 25,
180 ComponentCategory::Inverter(InverterType::Pv),
181 ));
182 connections.push(TestConnection::new(20, 24));
183 connections.push(TestConnection::new(24, 25));
184
185 (components, connections)
186 }
187
188 fn without_grid_meters() -> (Vec<TestComponent>, Vec<TestConnection>) {
189 let (mut components, mut connections) = nodes_and_edges();
190
191 components.push(TestComponent::new(20, ComponentCategory::Meter));
194 components.push(TestComponent::new(
195 21,
196 ComponentCategory::EvCharger(EvChargerType::Ac),
197 ));
198 connections.push(TestConnection::new(1, 20));
199 connections.push(TestConnection::new(20, 21));
200
201 (components, connections)
202 }
203
204 fn find_matching_components(
205 components: Vec<TestComponent>,
206 connections: Vec<TestConnection>,
207 filter: impl Fn(&ComponentGraph<TestComponent, TestConnection>, u64) -> Result<bool, Error>,
208 ) -> Result<Vec<u64>, Error> {
209 let config = ComponentGraphConfig::default();
210
211 let graph = ComponentGraph::try_new(components.clone(), connections.clone(), config)?;
212
213 let mut found_meters = vec![];
214 for comp in graph.components() {
215 if filter(&graph, comp.component_id())? {
216 found_meters.push(comp.component_id());
217 }
218 }
219
220 Ok(found_meters)
221 }
222
223 #[test]
224 fn test_is_pv_meter() -> Result<(), Error> {
225 let (components, connections) = nodes_and_edges();
226 assert_eq!(
227 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
228 vec![9],
229 );
230
231 let (components, connections) = with_multiple_grid_meters();
232 assert_eq!(
233 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
234 vec![9, 24],
235 );
236
237 let (components, connections) = without_grid_meters();
238 assert_eq!(
239 find_matching_components(components, connections, ComponentGraph::is_pv_meter)?,
240 vec![9],
241 );
242
243 Ok(())
244 }
245
246 #[test]
247 fn test_is_battery_meter() -> Result<(), Error> {
248 let (components, connections) = nodes_and_edges();
249 assert_eq!(
250 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
251 vec![3, 6],
252 );
253
254 let (components, connections) = with_multiple_grid_meters();
255 assert_eq!(
256 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
257 vec![3, 6, 21],
258 );
259
260 let (components, connections) = without_grid_meters();
261 assert_eq!(
262 find_matching_components(components, connections, ComponentGraph::is_battery_meter)?,
263 vec![3, 6],
264 );
265
266 Ok(())
267 }
268
269 #[test]
270 fn test_is_chp_meter() -> Result<(), Error> {
271 let (components, connections) = nodes_and_edges();
272 assert_eq!(
273 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
274 vec![12],
275 );
276
277 let (components, connections) = with_multiple_grid_meters();
278 assert_eq!(
279 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
280 vec![12],
281 );
282
283 let (components, connections) = without_grid_meters();
284 assert_eq!(
285 find_matching_components(components, connections, ComponentGraph::is_chp_meter)?,
286 vec![12],
287 );
288
289 Ok(())
290 }
291
292 #[test]
293 fn test_is_ev_charger_meter() -> Result<(), Error> {
294 let (components, connections) = nodes_and_edges();
295 assert_eq!(
296 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
297 vec![],
298 );
299
300 let (components, connections) = with_multiple_grid_meters();
301 assert_eq!(
302 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
303 vec![],
304 );
305
306 let (components, connections) = without_grid_meters();
307 assert_eq!(
308 find_matching_components(components, connections, ComponentGraph::is_ev_charger_meter)?,
309 vec![20],
310 );
311
312 Ok(())
313 }
314}