Coverage for src/distopf/cim_converter/processors/switch_processor.py: 98%
46 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-09 17:44 -0700
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-09 17:44 -0700
1from distopf.cim_converter.processors.base_processor import BaseProcessor
2from distopf.cim_converter.utils import PhaseUtils
3import cimgraph.data_profile.cimhub_2023 as cim
6class SwitchProcessor(BaseProcessor):
7 """Processor for all switch types."""
9 def process(self, network) -> list[dict]:
10 """Process all switch subclasses."""
11 results = []
12 for cim_class in network.graph.keys():
13 if (
14 hasattr(cim_class, "__mro__")
15 and cim.Switch in cim_class.__mro__
16 and cim_class != cim.Switch
17 ):
18 for switch in network.list_by_class(cim_class):
19 results.append(self._process_switch(switch))
20 return results
22 def _process_switch(self, switch) -> dict:
23 """Process individual switch."""
24 data = self._create_base_branch_dict()
26 # Basic switch info
27 data["name"] = switch.name
28 data["type"] = switch.__class__.__name__.lower()
30 # Get terminal connections
32 terminals = switch.Terminals
33 from_bus = terminals[0].ConnectivityNode
34 to_bus = terminals[1].ConnectivityNode
35 data["from_name"] = from_bus.name
36 data["to_name"] = to_bus.name
38 # Get voltage base
39 v_ln_base = self._get_bus_voltage_base(from_bus)
40 z_base = v_ln_base**2 / self.s_base
41 data["v_ln_base"] = v_ln_base
42 data["z_base"] = z_base
44 # Determine actual phases using utility function
45 phases = PhaseUtils.get_equipment_phases(switch)
46 data["phases"] = phases
48 # Very small impedance for switches - apply only to active phases
49 r, x = self._get_switch_impedance_per_phase(z_base)
50 self._apply_switch_impedance(data, phases, r, x)
52 data["status"] = self._get_switch_status(switch)
54 return data
56 def _get_switch_impedance_per_phase(self, z_base: float) -> tuple[float, float]:
57 """Get per-phase impedance values for switches."""
58 # Very small impedance for switches
59 r = 1e-4 / z_base if z_base > 0 else 1e-4
60 x = 1e-4 / z_base if z_base > 0 else 1e-4
61 return r, x
63 def _apply_switch_impedance(self, data: dict, phases: str, r: float, x: float):
64 """Apply impedance only to active phases."""
65 # Initialize all impedances to zero
66 for phase_combo in ["aa", "bb", "cc", "ab", "ac", "bc"]:
67 data[f"r{phase_combo}"] = 0.0
68 data[f"x{phase_combo}"] = 0.0
70 # Apply impedance only to active phases
71 for phase in phases:
72 if phase in ["a", "b", "c"]:
73 data[f"r{phase}{phase}"] = r
74 data[f"x{phase}{phase}"] = x
76 def _get_switch_status(self, switch) -> str:
77 """Get switch status (open/closed)."""
78 if hasattr(switch, "open"):
79 return "open" if switch.open == "true" else "closed"
80 return "closed"