zepben.examples 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ # Copyright 2022 Zeppelin Bend Pty Ltd
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
@@ -0,0 +1,46 @@
1
+ # Copyright 2022 Zeppelin Bend Pty Ltd
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+
7
+ from zepben.evolve import NetworkHierarchy, GeographicalRegion, SubGeographicalRegion, Feeder, Substation, Loop, Circuit
8
+
9
+ # A network hierarchy describes the high-level hierarchy of the network.
10
+
11
+ fdr1 = Feeder(name="Sydney feeder 1")
12
+ fdr2 = Feeder(name="Sydney feeder 2")
13
+ fdr3 = Feeder(name="Sydney feeder 3")
14
+ fdr4 = Feeder(name="Newcastle feeder 1")
15
+ fdr5 = Feeder(name="Newcastle feeder 2")
16
+ fdr6 = Feeder(name="Newcastle feeder 3")
17
+
18
+ sub1 = Substation(name="Sydney substation 1")
19
+ sub2 = Substation(name="Sydney substation 2", normal_energized_feeders=[fdr1, fdr2, fdr3])
20
+ sub3 = Substation(name="Newcastle substation", normal_energized_feeders=[fdr4, fdr5, fdr6])
21
+
22
+ circuit_sydney = Circuit(end_substations=[sub1, sub2])
23
+ loop_sydney = Loop(circuits=[circuit_sydney], substations=[sub1], energizing_substations=[sub2])
24
+ sgr_sydney = SubGeographicalRegion(name="Sydney", substations=[sub1, sub2])
25
+ sgr_newcastle = SubGeographicalRegion(name="Newcastle", substations=[sub3])
26
+
27
+ gr_nsw = GeographicalRegion(name="New South Wales", sub_geographical_regions=[sgr_sydney, sgr_newcastle])
28
+
29
+ network_hierarchy = NetworkHierarchy(
30
+ geographical_regions={gr_nsw.mrid: gr_nsw},
31
+ sub_geographical_regions={sgr.mrid: sgr for sgr in (sgr_sydney, sgr_newcastle)},
32
+ substations={sub.mrid for sub in (sub1, sub2, sub3)},
33
+ feeders={fdr.mrid: fdr for fdr in (fdr1, fdr2, fdr3, fdr4, fdr5, fdr6)},
34
+ circuits={circuit_sydney.mrid: circuit_sydney},
35
+ loops={loop_sydney.mrid: loop_sydney}
36
+ )
37
+
38
+ print("Network hierarchy:")
39
+ for gr in network_hierarchy.geographical_regions.values():
40
+ print(f"- {gr.name}")
41
+ for sgr in gr.sub_geographical_regions:
42
+ print(f" - {sgr.name}")
43
+ for sub in sgr.substations:
44
+ print(f" - {sub.name}")
45
+ for fdr in sub.feeders:
46
+ print(f" - {fdr.name}")
@@ -0,0 +1,90 @@
1
+ # Copyright 2022 Zeppelin Bend Pty Ltd
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+ import asyncio
7
+ import json
8
+
9
+ from zepben.auth import AuthMethod
10
+ from zepben.evolve import connect_insecure, NetworkConsumerClient, connect_tls, connect_with_password, connect_with_secret, SyncNetworkConsumerClient, \
11
+ connect_with_token
12
+
13
+
14
+ with open("config.json") as f:
15
+ c = json.loads(f.read())
16
+
17
+
18
+ async def plaintext_connection():
19
+ """ Connects to an RPC server without TLS or authentication. This method should only be used in development and for demos. """
20
+ async with connect_insecure("hostname", 1234) as insecure_channel:
21
+ client = NetworkConsumerClient(insecure_channel)
22
+ grpc_result = await client.get_network_hierarchy()
23
+ print(grpc_result.result)
24
+
25
+
26
+ async def secure_connection():
27
+ """ Connects to an RPC server over TLS. No user/client credentials are used. """
28
+ async with connect_tls("hostname", 1234) as secure_channel:
29
+ client = NetworkConsumerClient(secure_channel)
30
+ grpc_result = await client.get_network_hierarchy()
31
+ print(grpc_result.result)
32
+
33
+
34
+ async def secure_connection_with_user_credentials():
35
+ """
36
+ Connects to an RPC server over TLS with user credentials. The authentication config will be fetched from
37
+ https://hostname/auth or https://hostname/ewb/auth by default, which includes the domain of the OAuth token provider.
38
+ """
39
+ async with connect_with_password("client ID", "username", "password", "hostname", 1234) as secure_channel:
40
+ client = NetworkConsumerClient(secure_channel)
41
+ grpc_result = await client.get_network_hierarchy()
42
+ print(grpc_result.result)
43
+
44
+ # Specify authentication config explicitly
45
+ async with connect_with_password("client ID", "username", "password", "hostname", 1234,
46
+ audience="https://fake_audience/", issuer_domain="fake.issuer.domain", auth_method=AuthMethod.AUTH0) as secure_channel:
47
+ client = NetworkConsumerClient(secure_channel)
48
+ grpc_result = await client.get_network_hierarchy()
49
+ print(grpc_result.result)
50
+
51
+
52
+ async def secure_connection_with_client_credentials():
53
+ """
54
+ Connects to an RPC server over TLS with client credentials. The authentication config will be fetched from
55
+ https://hostname/auth or https://hostname/ewb/auth by default, which includes the domain of the OAuth token provider.
56
+ """
57
+ async with connect_with_secret("client ID", "client secret", "hostname", 1234) as secure_channel:
58
+ client = NetworkConsumerClient(secure_channel)
59
+ grpc_result = await client.get_network_hierarchy()
60
+ print(grpc_result.result)
61
+
62
+ # Specify authentication config explicitly
63
+ async with connect_with_secret("client ID", "client secret", "hostname", 1234,
64
+ audience="https://fake_audience/", issuer_domain="fake.issuer.domain", auth_method=AuthMethod.AUTH0) as secure_channel:
65
+ client = NetworkConsumerClient(secure_channel)
66
+ grpc_result = await client.get_network_hierarchy()
67
+ print(grpc_result.result)
68
+
69
+
70
+ # You may use `SyncNetworkConsumerClient` if you prefer not to use asyncio.
71
+ # The API calls are the same between `SyncNetworkConsumerClient` and `NetworkConsumerClient`.
72
+ def connect_sync():
73
+ channel = connect_insecure("hostname", 1234)
74
+ client = SyncNetworkConsumerClient(channel)
75
+ grpc_result = client.get_network_hierarchy()
76
+ print(grpc_result.result)
77
+
78
+
79
+ async def connect_using_token():
80
+ print("Connecting to EWB..")
81
+ channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"])
82
+ client = NetworkConsumerClient(channel)
83
+ print("Connection established..")
84
+ print("Printing network hierarchy..")
85
+ grpc_result = await client.get_network_hierarchy()
86
+ print(grpc_result.result)
87
+
88
+
89
+ if __name__ == "__main__":
90
+ asyncio.run(connect_using_token())
@@ -0,0 +1,265 @@
1
+ # Copyright 2023 Zeppelin Bend Pty Ltd
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+
7
+ import asyncio
8
+ import sys
9
+ from typing import List, Set
10
+
11
+ from zepben.evolve import Feeder, PowerTransformer, Switch, assign_equipment_to_feeders, set_phases, NetworkConsumerClient, \
12
+ connect_with_password, BusbarSection, tracing, ConductingEquipment, ConductingEquipmentStep, Terminal, Breaker, EquipmentContainer
13
+
14
+ """
15
+ Primary question to answer/example for:
16
+ 1. How to access the CIM model? Show examples of how the static/design and dynamic/current states of the network model is typically accessed by software
17
+ developers?
18
+ a. Can Zepben model be updated with dynamic ADMS state information
19
+ b. if we have current state of network (dynamically updated from ADMS), can we query the model to find all current connected HV feeders in a voltage bus?
20
+ (VVC Example)
21
+ c. How fast can we retrieve a model (dynamic sate) from CIM for near real time applications? (VVC Example)
22
+ 2. Show how the static and dynamic states of the network model is used by applications.
23
+ """
24
+
25
+
26
+ async def run_simple(client: NetworkConsumerClient):
27
+ print()
28
+ print()
29
+ print("######################")
30
+ print("# FETCH ZONE FEEDERS #")
31
+ print("######################")
32
+ print()
33
+ print()
34
+ await fetch_zone_feeders(client)
35
+ print()
36
+ print()
37
+ print("##################")
38
+ print("# SPUR ISOLATION #")
39
+ print("##################")
40
+ print()
41
+ await isolate_spur_current(client)
42
+ print()
43
+ print()
44
+ print("##################")
45
+ print("# ZONE BUS TRACE #")
46
+ print("##################")
47
+ print()
48
+ await zone_bus_trace(client)
49
+ print()
50
+ print()
51
+
52
+
53
+ async def fetch_zone_feeders(client: NetworkConsumerClient):
54
+ print("fetching network hierarchy...")
55
+ hierarchy = (await client.get_network_hierarchy()).throw_on_error().result
56
+ print("hierarchy fetched, fetching BAS feeders...")
57
+ substation = hierarchy.substations["BAS"]
58
+ for feeder in substation.feeders:
59
+ print(f" {feeder.mrid}...")
60
+ await client.get_equipment_container(feeder.mrid, Feeder, include_energizing_containers=True, include_energized_containers=True)
61
+ print("CPM feeders fetched.")
62
+
63
+
64
+ async def isolate_spur_current(client: NetworkConsumerClient):
65
+ feeder: Feeder = client.service.get("BAS022", Feeder)
66
+ switch: Switch = client.service.get("171143844", Switch)
67
+ tx: PowerTransformer = client.service.get("171143830", PowerTransformer)
68
+
69
+ log_spur("original:", switch, tx)
70
+
71
+ clear_feeders({feeder})
72
+
73
+ print(f"setting switch open...")
74
+
75
+ log_spur("cleaned:", switch, tx)
76
+
77
+ # Change the open state of the switch.
78
+ switch.set_open(True)
79
+
80
+ await recalculate_feeders({feeder})
81
+
82
+ log_spur("currently open:", switch, tx)
83
+
84
+
85
+ def log_spur(desc: str, switch: Switch, tx: PowerTransformer):
86
+ print("==========================")
87
+ print(desc)
88
+ print(
89
+ f" {str(switch)}: is_normally_open={switch.is_normally_open()}, "
90
+ f"is_open={switch.is_open()}, "
91
+ f"normal_feeders={[it.mrid for it in switch.normal_feeders]}, "
92
+ f"current_feeders={[it.mrid for it in switch.current_feeders]}"
93
+ )
94
+ print(
95
+ f" {str(tx)}: nominal_phases={[it.phases.name for it in tx.terminals]}, "
96
+ f"normal_phases={[it.normal_phases.as_phase_code().name for it in tx.terminals]}, "
97
+ f"current_phases={[it.current_phases.as_phase_code().name for it in tx.terminals]}, "
98
+ f"normal_feeders={[it.mrid for it in tx.normal_feeders]}, "
99
+ f"current_feeders={[it.mrid for it in tx.current_feeders]}"
100
+ )
101
+ print("==========================")
102
+
103
+
104
+ async def zone_bus_trace(client: NetworkConsumerClient):
105
+ feeder_head_terminals = [it.normal_head_terminal for it in client.service.objects(Feeder) if
106
+ it.normal_head_terminal is not None and it.normal_head_terminal.conducting_equipment is not None]
107
+ feeder_heads = [it.conducting_equipment for it in feeder_head_terminals]
108
+ feeder_head_other_terms = [ot for it in feeder_head_terminals for ot in it.other_terminals()]
109
+
110
+ print(f"creating bus for {[feeder.mrid for it in feeder_heads for feeder in it.normal_feeders]}...")
111
+ # There is no subtrans in the model we pulled down so create a zone bus for all the feeders.
112
+ bus = BusbarSection()
113
+ bus_terminal = Terminal()
114
+ bus.add_terminal(bus_terminal)
115
+
116
+ for it in feeder_head_other_terms:
117
+ print(f" connecting {[feeder.mrid for feeder in it.conducting_equipment.normal_feeders]} to bus...")
118
+ # disconnect the terminal from its energy source and move it to the bus
119
+ client.service.disconnect(it)
120
+ client.service.connect_terminals(bus_terminal, it)
121
+
122
+ print("bus created")
123
+ await log_bus("original:", bus, feeder_heads)
124
+
125
+ print("Setting BAS022 and BAS023 to currently open")
126
+ client.service.get("15416104", Breaker).set_open(True) # BAS022 Breaker
127
+ client.service.get("15416128", Breaker).set_open(True) # BAS023 Breaker
128
+
129
+ print("Setting BAS032 and BAS033 to normally open")
130
+ client.service.get("177750988", Breaker).set_normally_open(True) # BAS032 Breaker
131
+ client.service.get("177750866", Breaker).set_normally_open(True) # BAS033 Breaker
132
+
133
+ await log_bus("opened:", bus, feeder_heads)
134
+
135
+
136
+ async def log_bus(desc: str, bus: ConductingEquipment, feeder_heads: List[ConductingEquipment]):
137
+ print("==========================")
138
+ print(desc)
139
+
140
+ # we run a trace on the assumption that the real model may have more equipment between the bus and the feeder heads.
141
+ # e.g. other minor busbars or ac line segments
142
+ trace = tracing.connected_equipment_trace()
143
+ open_heads: List[ConductingEquipment] = []
144
+ closed_heads: List[ConductingEquipment] = []
145
+ normally_open_heads: List[ConductingEquipment] = []
146
+ normally_closed_heads: List[ConductingEquipment] = []
147
+
148
+ # stop at all feeder heads
149
+ async def stop_on_feeder_heads(step: ConductingEquipmentStep) -> bool:
150
+ return isinstance(step.conducting_equipment, Switch) and step.conducting_equipment in feeder_heads
151
+
152
+ # stop at transformers to prevent tracing out of this zone into others
153
+ async def stop_on_transformers(step: ConductingEquipmentStep) -> bool:
154
+ return isinstance(step.conducting_equipment, PowerTransformer)
155
+
156
+ # sort feeder heads based on state
157
+ async def sort_feeder_heads(step: ConductingEquipmentStep):
158
+ if isinstance(step.conducting_equipment, Switch):
159
+ if step.conducting_equipment.is_open():
160
+ open_heads.append(step.conducting_equipment)
161
+ else:
162
+ closed_heads.append(step.conducting_equipment)
163
+ if step.conducting_equipment.is_normally_open():
164
+ normally_open_heads.append(step.conducting_equipment)
165
+ else:
166
+ normally_closed_heads.append(step.conducting_equipment)
167
+
168
+ trace.add_stop_condition(stop_on_feeder_heads)
169
+ trace.add_stop_condition(stop_on_transformers)
170
+ trace.if_stopping(sort_feeder_heads)
171
+
172
+ await trace.run_from(bus)
173
+ print(f" disconnected feeders: {[feeder.mrid for it in open_heads for feeder in it.normal_feeders]}")
174
+ print(f" connected feeders: {[feeder.mrid for it in closed_heads for feeder in it.normal_feeders]}")
175
+ print(f" normally disconnected feeders: {[feeder.mrid for it in normally_open_heads for feeder in it.normal_feeders]}")
176
+ print(f" normally connected feeders: {[feeder.mrid for it in normally_closed_heads for feeder in it.normal_feeders]}")
177
+ print("==========================")
178
+
179
+
180
+ async def run_swap_feeder(client: NetworkConsumerClient):
181
+ open_point_id = "13953031"
182
+ isolation_point_id = "13952991"
183
+
184
+ # noinspection PyTypeChecker
185
+ open_point: Switch = (await client.get_identified_object(open_point_id)).throw_on_error().value
186
+ # noinspection PyTypeChecker
187
+ isolation_point: Switch = (await client.get_identified_object(isolation_point_id)).throw_on_error().value
188
+
189
+ feeder_ids = {it.to_mrid for it in client.service.get_unresolved_references_from(open_point.mrid) if it.resolver.to_class == EquipmentContainer}
190
+
191
+ # get the feeders on both sides of the open point.
192
+ print(f"fetching feeders...")
193
+ for feeder in feeder_ids:
194
+ print(f" {feeder}...")
195
+ (await client.get_equipment_container(feeder)).throw_on_error()
196
+ print(f"done.")
197
+
198
+ feeders = set(open_point.normal_feeders)
199
+ log_txs(f"original:", feeders)
200
+
201
+ clear_feeders(feeders)
202
+
203
+ print(f"setting switches to move feeders...")
204
+
205
+ open_point.set_open(False)
206
+ isolation_point.set_open(True)
207
+
208
+ print(f"switches updated.")
209
+
210
+ await recalculate_feeders(feeders)
211
+
212
+ log_txs(f"swapped:", feeders)
213
+
214
+
215
+ def clear_feeders(feeders: Set[Feeder]):
216
+ # remove the phases and feeders to show the difference in open/normal state
217
+ for feeder in feeders:
218
+ print(f"removing phases from {feeder.mrid}...")
219
+ # should use `await remove_phases().run(feeder.normal_head_terminal)`, but it is not working (or just really slow) for some reason...
220
+ for equip in feeder.equipment:
221
+ if isinstance(equip, ConductingEquipment):
222
+ for term in equip.terminals:
223
+ term.normal_phases.phase_status = 0
224
+ term.current_phases.phase_status = 0
225
+ print(f"phases removed, removing equipment...")
226
+ for equip in feeder.equipment:
227
+ equip.clear_containers()
228
+ equip.clear_current_containers()
229
+ feeder.clear_equipment()
230
+ feeder.clear_current_equipment()
231
+ print(f"equipment removed.")
232
+
233
+
234
+ async def recalculate_feeders(feeders: Set[Feeder]):
235
+ # recalculate the phases and feeders with the new switch state.
236
+ for feeder in feeders:
237
+ print(f"assigning phases to {feeder.mrid}...")
238
+ await set_phases().run_with_terminal(feeder.normal_head_terminal)
239
+ print(f"phases assigned, assigning equipment...")
240
+ await assign_equipment_to_feeders().run_feeder(feeder)
241
+ print(f"equipment assigned.")
242
+
243
+
244
+ def log_txs(desc: str, feeders: Set[Feeder]):
245
+ print()
246
+ print(desc)
247
+
248
+ for feeder in feeders:
249
+ print(f" {feeder.mrid} txs: {sorted([tx.name for tx in feeder.current_equipment if isinstance(tx, PowerTransformer)])}")
250
+
251
+ print()
252
+
253
+
254
+ async def main():
255
+ if len(sys.argv) != 6:
256
+ raise TypeError("you must provided the CLIENT_ID, username, password, host and port to connect")
257
+
258
+ # noinspection PyTypeChecker
259
+ async with connect_with_password(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]) as secure_channel:
260
+ await run_simple(NetworkConsumerClient(secure_channel))
261
+ await run_swap_feeder(NetworkConsumerClient(secure_channel))
262
+
263
+
264
+ if __name__ == "__main__":
265
+ asyncio.run(main())
@@ -0,0 +1,87 @@
1
+ # Copyright 2022 Zeppelin Bend Pty Ltd
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+ from zepben.evolve import EnergySource, AcLineSegment, Fuse, PowerTransformer, Breaker, EnergyConsumer, NetworkService, Terminal, connected_equipment, \
7
+ ConductingEquipment, PhaseCode, connected_terminals, ConnectivityResult
8
+
9
+ # This example explores how to examine the immediate connectivity of equipment.
10
+ # We will build a simple, linear network to examine:
11
+
12
+ # source consumer
13
+ # | |
14
+ # line line
15
+ # | |
16
+ # fuse breaker
17
+ # | |
18
+ # transformer fuse
19
+ # | |
20
+ # +-----------+
21
+
22
+ es_t = Terminal(mrid="es-t")
23
+ es = EnergySource(mrid="es", terminals=[es_t])
24
+
25
+ hv_line_t1, hv_line_t2 = Terminal(mrid="hv_line_t1"), Terminal(mrid="hv_line_t2")
26
+ hv_line = AcLineSegment(mrid="hv_line", terminals=[hv_line_t1, hv_line_t2])
27
+
28
+ hv_fuse_t1, hv_fuse_t2 = Terminal(mrid="hv_fuse_t1"), Terminal(mrid="hv_fuse_t2")
29
+ hv_fuse = Fuse(mrid="hv_fuse", terminals=[hv_fuse_t1, hv_fuse_t2])
30
+
31
+ tx_t1, tx_t2 = Terminal(mrid="tx_t1"), Terminal(mrid="tx_t2", phases=PhaseCode.ABCN)
32
+ tx = PowerTransformer(mrid="tx", terminals=[tx_t1, tx_t2])
33
+
34
+ lv_fuse_t1, lv_fuse_t2 = Terminal(mrid="lv_fuse_t1", phases=PhaseCode.ABCN), Terminal(mrid="lv_fuse_t2", phases=PhaseCode.ABCN)
35
+ lv_fuse = Fuse(mrid="lv_fuse", terminals=[lv_fuse_t1, lv_fuse_t2])
36
+
37
+ breaker_t1, breaker_t2 = Terminal(mrid="breaker_t1", phases=PhaseCode.ABCN), Terminal(mrid="breaker_t2", phases=PhaseCode.BN)
38
+ breaker = Breaker(mrid="breaker", terminals=[breaker_t1, breaker_t2])
39
+
40
+ lv_line_t1, lv_line_t2 = Terminal(mrid="lv_line_t1", phases=PhaseCode.BN), Terminal(mrid="lv_line_t2", phases=PhaseCode.BN)
41
+ lv_line = AcLineSegment(mrid="lv_line", terminals=[lv_line_t1, lv_line_t2])
42
+
43
+ ec_t = Terminal(mrid="ec_t", phases=PhaseCode.BN)
44
+ ec = EnergyConsumer(mrid="ec", terminals=[ec_t])
45
+
46
+ network = NetworkService()
47
+ for io in [es_t, es, hv_line_t1, hv_line_t2, hv_line, hv_fuse_t1, hv_fuse_t2, hv_fuse, tx_t1, tx_t2, tx, lv_fuse_t1, lv_fuse_t2, lv_fuse, breaker_t1,
48
+ breaker_t2, breaker, lv_line_t1, lv_line_t2, lv_line, ec_t, ec]:
49
+ network.add(io)
50
+
51
+ network.connect_terminals(es_t, hv_line_t1)
52
+ network.connect_terminals(hv_line_t2, hv_fuse_t1)
53
+ network.connect_terminals(hv_fuse_t2, tx_t1)
54
+ network.connect_terminals(tx_t2, lv_fuse_t1)
55
+ network.connect_terminals(lv_fuse_t2, breaker_t1)
56
+ network.connect_terminals(breaker_t2, lv_line_t1)
57
+ network.connect_terminals(lv_line_t2, ec_t)
58
+
59
+
60
+ def fancy_print_connectivity_result(connectivity_result: ConnectivityResult):
61
+ print(f"\t{connectivity_result.from_terminal} to {connectivity_result.to_terminal}")
62
+
63
+ terminal_str_len = len(str(connectivity_result.from_terminal))
64
+ for core_path in connectivity_result.nominal_phase_paths:
65
+ print(f"\t{core_path.from_phase.name:>{terminal_str_len}}----{core_path.to_phase.name}")
66
+
67
+
68
+ def fancy_print_connected_equipment(equipment: ConductingEquipment, phases=None):
69
+ if phases:
70
+ print(f"Connectivity results for {equipment} on phases {phases}:")
71
+ else:
72
+ print(f"Connectivity results for {equipment}:")
73
+ for connectivity_result in connected_equipment(equipment, phases):
74
+ fancy_print_connectivity_result(connectivity_result)
75
+ print()
76
+
77
+
78
+ # connected_equipment(equipment, phases) will get all connections between equipment cores matching one of the requested phases.
79
+ # The connected equipment does not need to connect via all specified phases to appear in the list of connectivity results.
80
+ fancy_print_connected_equipment(tx)
81
+ fancy_print_connected_equipment(tx, phases=PhaseCode.N)
82
+ fancy_print_connected_equipment(breaker, phases=PhaseCode.BC)
83
+
84
+ # connected_terminals is essentially connected_equipment where only one terminal is considered.
85
+ print(f"Connectivity results for terminal {lv_fuse_t2} on phases {PhaseCode.ACN}:")
86
+ for connectivity_result in connected_terminals(lv_fuse_t2, PhaseCode.ACN):
87
+ fancy_print_connectivity_result(connectivity_result)
@@ -0,0 +1,36 @@
1
+ # Copyright 2023 Zeppelin Bend Pty Ltd
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+ import asyncio
7
+ import json
8
+
9
+ from zepben.evolve import connect_with_token, NetworkConsumerClient
10
+
11
+ with open("config.json") as f:
12
+ c = json.loads(f.read())
13
+
14
+
15
+ async def main():
16
+ # See connecting_to_grpc_service.py for examples of each connect function
17
+ print("Connecting to EWB..")
18
+ channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"])
19
+ client = NetworkConsumerClient(channel)
20
+ print("Connection established..")
21
+ # Fetch network hierarchy
22
+ network_hierarchy = await client.get_network_hierarchy()
23
+
24
+ print("Network hierarchy:")
25
+ for gr in network_hierarchy.result.geographical_regions.values():
26
+ print(f"- {gr.name}")
27
+ for sgr in gr.sub_geographical_regions:
28
+ print(f" - {sgr.name}")
29
+ for sub in sgr.substations:
30
+ print(f" - {sub.name}")
31
+ for fdr in sub.feeders:
32
+ print(f" - {fdr.name}")
33
+
34
+
35
+ if __name__ == "__main__":
36
+ asyncio.run(main())
@@ -0,0 +1,88 @@
1
+ # Copyright 2022 Zeppelin Bend Pty Ltd
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+ import asyncio
7
+ import json
8
+
9
+ from zepben.evolve import Conductor, PowerTransformer, ConductingEquipment, EnergyConsumer, Switch, \
10
+ connect_with_token, NetworkConsumerClient
11
+ from zepben.protobuf.nc.nc_requests_pb2 import INCLUDE_ENERGIZED_LV_FEEDERS, INCLUDE_ENERGIZED_FEEDERS, INCLUDE_ENERGIZING_SUBSTATIONS, \
12
+ INCLUDE_ENERGIZING_FEEDERS
13
+
14
+ with open("config.json") as f:
15
+ c = json.loads(f.read())
16
+
17
+
18
+ async def main():
19
+ # See connecting_to_grpc_service.py for examples of each connect function
20
+ print("Connecting to EWB..")
21
+ channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"])
22
+ feeder_mrid = "WD24"
23
+ print(f"Fetching {feeder_mrid}")
24
+ # Note you should create a new client for each Feeder you retrieve
25
+ # There is also a NetworkConsumerClient that is asyncio compatible, with the same API.
26
+ client = NetworkConsumerClient(channel=channel)
27
+ network = client.service
28
+
29
+ # Fetch feeder and all its LvFeeders
30
+ await client.get_equipment_container(feeder_mrid, include_energized_containers=INCLUDE_ENERGIZED_LV_FEEDERS)
31
+
32
+ print()
33
+ print(f"Total Number of objects: {client.service.len_of()}")
34
+ types = set(type(x) for x in network.objects(ConductingEquipment))
35
+ for t in types:
36
+ print(f"Number of {t.__name__}'s = {len(list(network.objects(t)))}")
37
+
38
+ total_length = 0
39
+ for conductor in network.objects(Conductor):
40
+ if conductor.length is not None:
41
+ total_length += conductor.length
42
+
43
+ print()
44
+ print(f"Total conductor length in {feeder_mrid}: {total_length:.3f}m")
45
+
46
+ print()
47
+ feeder = network.get(feeder_mrid)
48
+ print(f"{feeder.mrid} Transformers:")
49
+ for eq in feeder.equipment:
50
+ if isinstance(eq, PowerTransformer):
51
+ print(f" {eq} - Vector Group: {eq.vector_group.short_name}, Function: {eq.function.short_name}")
52
+ print()
53
+
54
+ print()
55
+ print(f"{feeder_mrid} Energy Consumers:")
56
+ for ec in network.objects(EnergyConsumer):
57
+ print(f" {ec} - Real power draw: {ec.q}W, Reactive power draw: {ec.p}VAr")
58
+ print()
59
+
60
+ print(f"{feeder_mrid} Switches:")
61
+ for switch in network.objects(Switch):
62
+ print(f" {switch} - Open status: {switch.get_state():04b}")
63
+
64
+ # === Some other examples of fetching containers ===
65
+
66
+ # Fetch substation equipment and include equipment from HV/MV feeders powered by it
67
+ await client.get_equipment_container("substation ID", include_energized_containers=INCLUDE_ENERGIZED_FEEDERS)
68
+
69
+ # Same as above, but also fetch equipment from LV feeders powered by the HV/MV feeders
70
+ await client.get_equipment_container("substation ID", include_energized_containers=INCLUDE_ENERGIZED_LV_FEEDERS)
71
+
72
+ # Fetch feeder equipment without fetching any additional equipment from powering/powered containers
73
+ await client.get_equipment_container("feeder ID")
74
+
75
+ # Fetch HV/MV feeder equipment, the equipment from the substation powering it, and the equipment from the LV feeders it powers
76
+ await client.get_equipment_container("feeder ID",
77
+ include_energizing_containers=INCLUDE_ENERGIZING_SUBSTATIONS,
78
+ include_energized_containers=INCLUDE_ENERGIZED_LV_FEEDERS)
79
+
80
+ # Fetch LV feeder equipment and include equipment from HV/MV feeders powering it
81
+ await client.get_equipment_container("LV feeder ID", include_energizing_containers=INCLUDE_ENERGIZING_FEEDERS)
82
+
83
+ # Same as above, but also fetch equipment from the substations powering the HV/MV feeders
84
+ await client.get_equipment_container("LV feeder ID", include_energizing_containers=INCLUDE_ENERGIZING_SUBSTATIONS)
85
+
86
+
87
+ if __name__ == "__main__":
88
+ asyncio.run(main())