Skip to content

Commit 1f47bc2

Browse files
chestm007kgreav
andauthored
[EWB-3084] Tracing API v2 (#180)
* Added ConductingEquipment.max_terminals and check to enforce. * Set BusbarSection.max_terminals = 1 * removed TracedPhases from Terminal Signed-off-by: Kurt Greaves <kurt.greaves@zepben.com> Signed-off-by: Max Chesterfield <max.chesterfield@zepben.com> Co-authored-by: Max Chesterfield <max.chesterfield@zepben.com> Co-authored-by: Kurt Greaves <kurt.greaves@zepben.com>
1 parent 16ce609 commit 1f47bc2

148 files changed

Lines changed: 5465 additions & 5432 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

changelog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Zepben Python SDK
22
## [0.48.0] - UNRELEASED
33
### Breaking Changes
4-
* None.
4+
* Updated to new Tracing API. All old traces will need to be re-written with the new API.
55

66
### New Features
77
* None.

src/zepben/evolve/__init__.py

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
# imported in a specific order to prevent unresolved dependency errors.
99
#
1010
# @formatter:off
11+
from __future__ import annotations
1112

1213
from zepben.evolve.util import *
1314

15+
1416
# We need to import SinglePhaseKind before anything uses PhaseCode to prevent cyclic dependencies.
1517
from zepben.evolve.model.cim.iec61970.base.wires.single_phase_kind import *
1618

@@ -147,12 +149,8 @@
147149
from zepben.evolve.model.phases import *
148150
from zepben.evolve.model.resistance_reactance import *
149151

150-
from zepben.evolve.services.network.tracing.traversals.tracker import *
151-
from zepben.evolve.services.network.tracing.traversals.basic_tracker import *
152-
from zepben.evolve.services.network.tracing.traversals.traversal import *
153-
from zepben.evolve.services.network.tracing.traversals.basic_traversal import *
154-
from zepben.evolve.services.network.tracing.traversals.queue import *
155-
from zepben.evolve.services.network.tracing.traversals.branch_recursive_tracing import *
152+
from zepben.evolve.services.network.tracing.traversal.traversal import *
153+
from zepben.evolve.services.network.tracing.traversal.queue import *
156154

157155
from zepben.evolve.services.network.tracing.feeder.feeder_direction import *
158156
from zepben.evolve.services.network.tracing.util import *
@@ -161,13 +159,8 @@
161159
from zepben.evolve.services.network.translator.network_cim2proto import *
162160
from zepben.evolve.services.network.network_service import *
163161

164-
from zepben.evolve.services.network.tracing.connectivity.conducting_equipment_step import *
165-
from zepben.evolve.services.network.tracing.connectivity.conducting_equipment_step_tracker import *
166-
from zepben.evolve.services.network.tracing.connectivity.connected_equipment_trace import *
167162
from zepben.evolve.services.network.tracing.connectivity.connectivity_result import *
168-
from zepben.evolve.services.network.tracing.connectivity.connectivity_tracker import *
169-
from zepben.evolve.services.network.tracing.connectivity.connectivity_trace import *
170-
from zepben.evolve.services.network.tracing.connectivity.limited_connected_equipment_trace import *
163+
from zepben.evolve.services.network.tracing.connectivity.nominal_phase_path import *
171164
from zepben.evolve.services.network.tracing.connectivity.phase_paths import *
172165
from zepben.evolve.services.network.tracing.connectivity.terminal_connectivity_connected import *
173166
from zepben.evolve.services.network.tracing.connectivity.terminal_connectivity_internal import *
@@ -177,24 +170,14 @@
177170
from zepben.evolve.services.network.tracing.feeder.direction_status import *
178171
from zepben.evolve.services.network.tracing.feeder.assign_to_feeders import *
179172
from zepben.evolve.services.network.tracing.feeder.assign_to_lv_feeders import *
180-
from zepben.evolve.services.network.tracing.feeder.associated_terminal_trace import *
181-
from zepben.evolve.services.network.tracing.feeder.associated_terminal_tracker import *
182-
from zepben.evolve.services.network.tracing.feeder.set_direction import *
183-
from zepben.evolve.services.network.tracing.feeder.remove_direction import *
184-
from zepben.evolve.services.network.tracing.phases.phase_step import *
185173
from zepben.evolve.services.network.tracing.phases.phase_status import *
186-
from zepben.evolve.services.network.tracing.phases.phase_step_tracker import *
187-
from zepben.evolve.services.network.tracing.phases.phase_trace import *
188-
from zepben.evolve.services.network.tracing.phases.set_phases import *
189174
from zepben.evolve.services.network.tracing.phases.phase_inferrer import *
190175
from zepben.evolve.services.network.tracing.phases.remove_phases import *
191-
from zepben.evolve.services.network.tracing.tree.downstream_tree import *
192-
from zepben.evolve.services.network.tracing.tree.tree_node import *
193-
from zepben.evolve.services.network.tracing.tree.tree_node_tracker import *
194-
from zepben.evolve.services.network.tracing.find import *
195176
from zepben.evolve.services.network.tracing.find_swer_equipment import *
196-
from zepben.evolve.services.network.tracing.tracing import *
197-
from zepben.evolve.services.network.tracing import tracing
177+
from zepben.evolve.services.network.tracing.traversal.queue_condition import *
178+
from zepben.evolve.services.network.tracing.traversal.context_value_computer import *
179+
from zepben.evolve.services.network.tracing.traversal.step_action import StepAction
180+
from zepben.evolve.services.network.tracing.feeder.set_direction import *
198181

199182
from zepben.evolve.services.common.meta.data_source import *
200183
from zepben.evolve.services.common.meta.metadata_collection import *
@@ -440,6 +423,8 @@
440423
from zepben.evolve.database.sqlite.network.network_database_reader import *
441424
from zepben.evolve.database.sqlite.network.network_service_reader import *
442425

426+
from zepben.evolve.services.network.tracing.phases.set_phases import *
427+
443428
from zepben.evolve.testing.test_network_builder import *
444429
from zepben.evolve.testing.test_traversal import *
445430

src/zepben/evolve/database/sqlite/network/network_database_reader.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
from zepben.evolve.services.network.tracing.feeder.assign_to_lv_feeders import AssignToLvFeeders
2323

2424
from zepben.evolve.services.network.tracing.feeder.set_direction import SetDirection
25+
from zepben.evolve.services.network.tracing.networktrace.tracing import Tracing
26+
from zepben.evolve.services.network.tracing.networktrace.operators.network_state_operators import NetworkStateOperators
2527
from zepben.evolve.services.network.tracing.phases.phase_inferrer import PhaseInferrer
2628
from zepben.evolve.services.network.tracing.phases.set_phases import SetPhases
2729

30+
from typing import List
31+
2832

2933
class NetworkDatabaseReader(BaseDatabaseReader):
3034
"""
@@ -44,26 +48,27 @@ def __init__(
4448
connection: Connection,
4549
service: NetworkService,
4650
database_description: str,
47-
tables: NetworkDatabaseTables = NetworkDatabaseTables(),
51+
infer_phases: bool = None,
4852
metadata_reader: MetadataCollectionReader = None,
4953
service_reader: NetworkServiceReader = None,
5054
table_version: TableVersion = TableVersion(),
51-
set_direction: SetDirection = SetDirection(),
52-
set_phases: SetPhases = SetPhases(),
53-
phase_inferrer: PhaseInferrer = PhaseInferrer(),
54-
assign_to_feeders: AssignToFeeders = AssignToFeeders(),
55-
assign_to_lv_feeders: AssignToLvFeeders = AssignToLvFeeders()
55+
set_feeder_direction: SetDirection = Tracing.set_direction(),
56+
set_phases: SetPhases = Tracing.set_phases(),
57+
phase_inferrer: PhaseInferrer = Tracing.phase_inferrer(),
58+
assign_to_feeders: AssignToFeeders = Tracing.assign_equipment_to_feeders(),
59+
assign_to_lv_feeders: AssignToLvFeeders = Tracing.assign_equipment_to_lv_feeders()
5660
):
5761
super().__init__(
5862
connection,
59-
metadata_reader if metadata_reader else MetadataCollectionReader(service, tables, connection),
60-
service_reader if service_reader else NetworkServiceReader(service, tables, connection),
63+
metadata_reader if metadata_reader else MetadataCollectionReader(service, NetworkDatabaseTables(), connection),
64+
service_reader if service_reader else NetworkServiceReader(service, NetworkDatabaseTables(), connection),
6165
service,
6266
database_description,
6367
table_version
6468
)
6569
self.service = service
66-
self.set_direction = set_direction
70+
self.infer_phases = infer_phases
71+
self.set_feeder_direction = set_feeder_direction
6772
self.set_phases = set_phases
6873
self.phase_inferrer = phase_inferrer
6974
self.assign_to_feeders = assign_to_feeders
@@ -73,20 +78,26 @@ async def _post_load(self) -> bool:
7378
status = await super()._post_load()
7479

7580
self._logger.info("Applying feeder direction to network...")
76-
await self.set_direction.run(self.service)
81+
await self.set_feeder_direction.run(self.service, NetworkStateOperators.NORMAL)
82+
await self.set_feeder_direction.run(self.service, NetworkStateOperators.CURRENT)
7783
self._logger.info("Feeder direction applied to network.")
7884

7985
self._logger.info("Applying phases to network...")
80-
await self.set_phases.run(self.service)
81-
await self.phase_inferrer.run(self.service)
86+
await self.set_phases.run(self.service, NetworkStateOperators.NORMAL)
87+
await self.set_phases.run(self.service, NetworkStateOperators.CURRENT)
88+
if self.infer_phases:
89+
await self.phase_inferrer.run(self.service, NetworkStateOperators.NORMAL)
90+
await self.phase_inferrer.run(self.service, NetworkStateOperators.CURRENT)
8291
self._logger.info("Phasing applied to network.")
8392

8493
self._logger.info("Assigning equipment to feeders...")
85-
await self.assign_to_feeders.run(self.service)
94+
await self.assign_to_feeders.run(self.service, NetworkStateOperators.NORMAL)
95+
await self.assign_to_feeders.run(self.service, NetworkStateOperators.CURRENT)
8696
self._logger.info("Equipment assigned to feeders.")
8797

8898
self._logger.info("Assigning equipment to LV feeders...")
89-
await self.assign_to_lv_feeders.run(self.service)
99+
await self.assign_to_lv_feeders.run(self.service, NetworkStateOperators.NORMAL)
100+
await self.assign_to_lv_feeders.run(self.service, NetworkStateOperators.CURRENT)
90101
self._logger.info("Equipment assigned to LV feeders.")
91102

92103
self._logger.info("Validating that each equipment is assigned to a container...")
@@ -99,6 +110,17 @@ async def _post_load(self) -> bool:
99110

100111
return status
101112

113+
def _log_inferred_phases(self, normal_inferred_phases: List, current_inferred_phases: List): # FIXME: set list contents classes, this'll likely explode until then
114+
# FIXME: im pretty sure this should be building a dict of lists, not just a simple KV store. if so, this logic is way too simple
115+
inferred_phases = {item.conducting_equipment: item for item in normal_inferred_phases}
116+
117+
for it in current_inferred_phases:
118+
ce = it.conducting_equipment
119+
inferred_phases[ce] = (inferred_phases[ce] if inferred_phases[ce].suspect else it)
120+
121+
for phase in inferred_phases:
122+
self._logger.warning(f"*** Action Required *** {phase.description()}")
123+
102124
def _validate_equipment_containers(self):
103125
missing_containers = [it for it in self.service.objects(Equipment) if not it.containers]
104126
count_by_class = Counter()

src/zepben/evolve/model/busbranch/bus_branch.py

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from functools import reduce
99
from typing import Set, Tuple, FrozenSet, Dict, Callable, Union, TypeVar, Any, List, Generic, Optional, Iterable
1010

11-
from zepben.evolve import BasicTraversal, Junction, BusbarSection, EquivalentBranch
11+
from zepben.evolve import Junction, BusbarSection, EquivalentBranch, Traversal, StepContext
1212
from zepben.evolve.model.cim.iec61970.base.core.conducting_equipment import ConductingEquipment
1313
from zepben.evolve.model.cim.iec61970.base.core.terminal import Terminal
1414
from zepben.evolve.model.cim.iec61970.base.wires.aclinesegment import AcLineSegment
@@ -18,6 +18,7 @@
1818
from zepben.evolve.model.cim.iec61970.base.wires.power_transformer import PowerTransformer, PowerTransformerEnd
1919
from zepben.evolve.model.cim.iec61970.base.wires.switch import Switch
2020
from zepben.evolve.services.network.network_service import NetworkService
21+
from zepben.evolve.services.network.tracing.busbranch_trace import BusBranchTrace, BusBranchTraceStep
2122

2223
__all__ = [
2324
"BusBranchNetworkCreationValidator",
@@ -27,6 +28,7 @@
2728
"TerminalGrouping"
2829
]
2930

31+
3032
BBN = TypeVar('BBN') # Bus-Branch Network
3133
TN = TypeVar('TN') # Topological Node
3234
TB = TypeVar('TB') # Topological Branch
@@ -36,6 +38,8 @@
3638
EC = TypeVar('EC') # Energy Consumer
3739
PEC = TypeVar('PEC') # Power Electronics Connection
3840

41+
D = TypeVar('D')
42+
3943

4044
class BusBranchNetworkCreationValidator(Generic[BBN, TN, TB, EB, PT, ES, EC, PEC], metaclass=abc.ABCMeta):
4145
"""
@@ -896,21 +900,26 @@ async def _group_negligible_impedance_terminals(
896900
has_negligible_impedance: Callable[[ConductingEquipment], bool]
897901
) -> TerminalGrouping[ConductingEquipment]:
898902
tg = TerminalGrouping[ConductingEquipment]()
899-
# noinspection PyArgumentList
900-
trace = BasicTraversal(
901-
start_item=terminal,
902-
queue_next=_queue_terminals_across_negligible_impedance(has_negligible_impedance),
903-
step_actions=[_process_terminal(tg, has_negligible_impedance)]
903+
904+
trace = (
905+
BusBranchTrace(
906+
queue_next=Traversal.QueueNext(_queue_terminals_across_negligible_impedance(has_negligible_impedance))
907+
).add_start_item(terminal)
908+
.add_step_action(_process_terminal(tg, has_negligible_impedance))
904909
)
910+
905911
await trace.run()
906912
return tg
907913

914+
def _create_traversal_step_object(next_item: Union[Terminal, AcLineSegment]) -> BusBranchTraceStep:
915+
return BusBranchTraceStep(next_item)
908916

909917
def _process_terminal(
910918
tg: TerminalGrouping[ConductingEquipment],
911919
has_negligible_impedance: Callable[[ConductingEquipment], bool]
912920
):
913-
async def add_to_group(t: Terminal, _):
921+
async def add_to_group(item: BusBranchTraceStep, _):
922+
t = item.identified_object
914923
if has_negligible_impedance(t.conducting_equipment):
915924
tg.conducting_equipment_group.add(t.conducting_equipment)
916925
tg.inner_terminals.add(t)
@@ -923,12 +932,17 @@ async def add_to_group(t: Terminal, _):
923932
def _queue_terminals_across_negligible_impedance(
924933
has_negligible_impedance: Callable[[ConductingEquipment], bool]
925934
):
926-
def queue_next(terminal: Terminal, traversal: BasicTraversal[Terminal]):
935+
def queue_next(item: BusBranchTraceStep, context: StepContext, _queue_next: Callable[[BusBranchTraceStep], bool]):
936+
terminal = item.identified_object
927937
if terminal.connectivity_node is not None:
928-
traversal.process_queue.extend(ot for ot in terminal.connectivity_node.terminals if ot != terminal)
938+
for ot in terminal.connectivity_node.terminals:
939+
if ot != terminal:
940+
_queue_next(_create_traversal_step_object(ot))
929941

930942
if has_negligible_impedance(terminal.conducting_equipment):
931-
traversal.process_queue.extend(ot for ot in terminal.conducting_equipment.terminals if ot != terminal)
943+
for ot in terminal.conducting_equipment.terminals:
944+
if ot != terminal:
945+
_queue_next(_create_traversal_step_object(ot))
932946

933947
return queue_next
934948

@@ -940,12 +954,13 @@ def has_common_impedance(line: AcLineSegment):
940954
common_acls: TerminalGrouping[AcLineSegment] = TerminalGrouping()
941955
connectivity_node_counter = Counter()
942956

943-
# noinspection PyArgumentList
944-
trace = BasicTraversal(
945-
start_item=acls,
946-
queue_next=_queue_common_impedance_lines(common_acls, has_common_impedance),
947-
step_actions=[_process_acls(common_acls, connectivity_node_counter)]
957+
trace = (
958+
BusBranchTrace(
959+
queue_next=Traversal.QueueNext(_queue_common_impedance_lines(common_acls, has_common_impedance))
960+
).add_start_item(acls)
961+
.add_step_action(_process_acls(common_acls, connectivity_node_counter))
948962
)
963+
949964
await trace.run()
950965

951966
for t in (t for line in common_acls.conducting_equipment_group for t in line.terminals):
@@ -966,7 +981,8 @@ def _process_acls(
966981
common_acls: TerminalGrouping[AcLineSegment],
967982
connectivity_node_counter: Counter
968983
):
969-
async def add_to_group(acls: AcLineSegment, _):
984+
async def add_to_group(item: BusBranchTraceStep, _):
985+
acls = item.identified_object
970986
if acls in common_acls.conducting_equipment_group:
971987
return
972988

@@ -981,8 +997,11 @@ def _queue_common_impedance_lines(
981997
common_acls: TerminalGrouping[AcLineSegment],
982998
has_common_impedance: Callable[[AcLineSegment], bool]
983999
):
984-
def queue_next(acls: AcLineSegment, traversal: BasicTraversal[AcLineSegment]):
985-
traversal.process_queue.extend(_next_common_acls(acls, has_common_impedance, common_acls))
1000+
def queue_next(item: BusBranchTraceStep, context: StepContext, _queue_next: Callable[[BusBranchTraceStep], bool]):
1001+
acls = item.identified_object
1002+
1003+
for it in _next_common_acls(acls, has_common_impedance, common_acls):
1004+
_queue_next(_create_traversal_step_object(it))
9861005

9871006
return queue_next
9881007

src/zepben/evolve/model/cim/iec61970/base/auxiliaryequipment/auxiliary_equipment.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
from __future__ import annotations
77

8-
from typing import Optional
8+
from typing import Optional, TYPE_CHECKING
99

1010
from zepben.evolve.model.cim.iec61970.base.core.equipment import Equipment
11-
from zepben.evolve.model.cim.iec61970.base.core.terminal import Terminal
11+
12+
if TYPE_CHECKING:
13+
from zepben.evolve.model.cim.iec61970.base.core.terminal import Terminal
1214

1315
__all__ = ["AuxiliaryEquipment", "FaultIndicator"]
1416

src/zepben/evolve/model/cim/iec61970/base/core/conducting_equipment.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from __future__ import annotations
77

8+
import sys
89
from typing import List, Optional, Generator, TYPE_CHECKING
910

1011
from zepben.evolve.model.cim.iec61970.base.core.base_voltage import BaseVoltage
@@ -35,6 +36,7 @@ class ConductingEquipment(Equipment):
3536
"""
3637

3738
_terminals: List[Terminal] = []
39+
max_terminals = int(sys.maxsize)
3840

3941
def __init__(self, terminals: List[Terminal] = None, **kwargs):
4042
super(ConductingEquipment, self).__init__(**kwargs)
@@ -112,10 +114,15 @@ def add_terminal(self, terminal: Terminal) -> ConductingEquipment:
112114
`terminal` The `Terminal` to associate with this `ConductingEquipment`.
113115
Returns A reference to this `ConductingEquipment` to allow fluent use.
114116
Raises `ValueError` if another `Terminal` with the same `mrid` already exists for this `ConductingEquipment`.
117+
Raises `ValueError` if `max_terminals` has already been reached.
115118
"""
116119
if self._validate_terminal(terminal):
117120
return self
118121

122+
require (self.num_terminals() < self.max_terminals,
123+
lambda: f"Unable to add {terminal} to {str(self)}. This conducting equipment already has the maximum number of terminals ({self.max_terminals}).")
124+
125+
119126
if terminal.sequence_number == 0:
120127
terminal.sequence_number = self.num_terminals() + 1
121128

0 commit comments

Comments
 (0)