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.
- zepben/examples/__init__.py +5 -0
- zepben/examples/building_network_hierarchy.py +46 -0
- zepben/examples/connecting_to_grpc_service.py +90 -0
- zepben/examples/current_state_manipulations.py +265 -0
- zepben/examples/examining_connectivity.py +87 -0
- zepben/examples/fetching_network_hierarchy.py +36 -0
- zepben/examples/fetching_network_model.py +88 -0
- zepben/examples/ieee_13_node_test_feeder.py +228 -0
- zepben/examples/list_ewb_network_models.py +61 -0
- zepben/examples/network_service_interactions.py +164 -0
- zepben/examples/request_power_factory_models.py +327 -0
- zepben/examples/studies/__init__.py +5 -0
- zepben/examples/studies/creating_and_uploading_study.py +101 -0
- zepben/examples/studies/suspect_end_of_line.py +245 -0
- zepben/examples/tracing.py +383 -0
- zepben/examples/tracing_conductor_type_by_lv_circuit.py +107 -0
- zepben/examples/tracing_example.py +90 -0
- zepben/examples/translating_to_pandapower_model.py +94 -0
- zepben.examples-0.2.0.dist-info/LICENSE +374 -0
- zepben.examples-0.2.0.dist-info/METADATA +33 -0
- zepben.examples-0.2.0.dist-info/RECORD +23 -0
- zepben.examples-0.2.0.dist-info/WHEEL +5 -0
- zepben.examples-0.2.0.dist-info/top_level.txt +1 -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())
|