Coverage for tests/cim_converter/unit/test_reg_transformer_small.py: 98%

88 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-11-13 17:34 -0800

1# tests/unit/test_reg_transformer_small.py 

2import math 

3import pytest 

4from cimgraph.data_profile import cimhub_2023 as cim 

5from distopf.cim_importer.processors.regulator_processor import RegulatorProcessor 

6from distopf.cim_importer.processors.transformer_processor import TransformerProcessor 

7from distopf.cim_importer.utils.phase_utils import PhaseUtils 

8 

9 

10def make_terminal(name: str, mrid: str | None = None): 

11 node = cim.ConnectivityNode(mRID=(mrid or f"mrid_{name}"), name=name) 

12 terminal = cim.Terminal(ConnectivityNode=node) 

13 # Add terminal backref into node.Terminals list 

14 node.Terminals = [terminal] 

15 return terminal 

16 

17 

18def test_regulator_is_regulator_and_extract_tap(monkeypatch): 

19 # Build a TransformerTankEnd with a RatioTapChanger and attach to TransformerTank 

20 t_from = make_terminal("frombus") 

21 t_to = make_terminal("tobus") 

22 

23 # Create a RatioTapChanger in the profile if available; otherwise fall back to simple object 

24 try: 

25 rtc = cim.RatioTapChanger(step=10.0, stepVoltageIncrement=2.0) 

26 except AttributeError: 

27 # Fallback if RatioTapChanger dataclass isn't present in the profile 

28 rtc = type("RTC", (), {"step": 10.0, "stepVoltageIncrement": 2.0})() 

29 

30 tank_end = cim.TransformerTankEnd( 

31 orderedPhases=None, Terminal=t_from, RatioTapChanger=rtc 

32 ) 

33 tank = cim.TransformerTank(name="tank1", TransformerTankEnds=[tank_end]) 

34 xfmr = cim.PowerTransformer( 

35 name="Reg1", 

36 TransformerTanks=[tank], 

37 PowerTransformerEnd=[], 

38 Terminals=[t_from, t_to], 

39 ) 

40 

41 # Make PhaseUtils deterministic for orderedPhases handling 

42 monkeypatch.setattr(PhaseUtils, "get_phase_str", staticmethod(lambda v: "a")) 

43 reg_proc = RegulatorProcessor(1e6) 

44 

45 assert reg_proc.is_regulator(xfmr) is True 

46 

47 reg_data = reg_proc._extract_regulator_data(xfmr) 

48 # Expected: tap_a set to step (10.0) and ratio_a computed as 1 + step * (stepVoltageIncrement/100) 

49 assert "tap_a" in reg_data 

50 assert pytest.approx(reg_data["tap_a"], rel=1e-12) == 10.0 

51 # stepVoltageIncrement is given in percent in the implementation; divide by 100 

52 expected_ratio = 1.0 + (reg_data["tap_a"] * (rtc.stepVoltageIncrement / 100.0)) 

53 assert pytest.approx(reg_data["ratio_a"], rel=1e-12) == expected_ratio 

54 assert reg_data["phases"] == "a" 

55 assert reg_data["from_name"] == t_from.ConnectivityNode.name 

56 assert reg_data["to_name"] == t_to.ConnectivityNode.name 

57 

58 

59def _make_power_transformer_end( 

60 end_number: int, ratedU: float = 480.0, mesh_r=None, mesh_x=None, to_end_number=None 

61): 

62 """ 

63 Create a cim.PowerTransformerEnd with optional FromMeshImpedance entries. 

64 """ 

65 pte = cim.PowerTransformerEnd(endNumber=end_number, ratedU=float(ratedU)) 

66 if mesh_r is not None or mesh_x is not None: 

67 # Create a TransformerMeshImpedance instance 

68 mesh_imp = cim.TransformerMeshImpedance( 

69 r=float(mesh_r) if mesh_r is not None else None, 

70 x=float(mesh_x) if mesh_x is not None else None, 

71 ) 

72 if to_end_number is not None: 

73 # create a TransformerEnd placeholder with endNumber to serve as ToTransformerEnd 

74 to_end = cim.TransformerEnd(endNumber=int(to_end_number)) 

75 mesh_imp.ToTransformerEnd = [to_end] 

76 pte.FromMeshImpedance = [mesh_imp] 

77 else: 

78 pte.FromMeshImpedance = [] 

79 return pte 

80 

81 

82def test_transformer_2winding_from_mesh_impedance(): 

83 t0 = make_terminal("busA") 

84 t1 = make_terminal("busB") 

85 primary_end = _make_power_transformer_end(1, ratedU=480.0, mesh_r=0.1, mesh_x=0.2) 

86 xfmr = cim.PowerTransformer( 

87 name="Xfmr2W", 

88 PowerTransformerEnd=[primary_end], 

89 TransformerTanks=[], 

90 Terminals=[t0, t1], 

91 ) 

92 tp = TransformerProcessor(1e6) 

93 data = tp._process_2winding_transformer(xfmr, ["busA", "busB"]) 

94 v_ln_expected = float(primary_end.ratedU) / math.sqrt(3) 

95 assert pytest.approx(data["v_ln_base"], rel=1e-12) == v_ln_expected 

96 z_base = v_ln_expected**2 / tp.s_base 

97 expected_r = float(primary_end.FromMeshImpedance[0].r) / z_base 

98 expected_x = float(primary_end.FromMeshImpedance[0].x) / z_base 

99 assert pytest.approx(data["raa"], rel=1e-9) == expected_r 

100 assert pytest.approx(data["xaa"], rel=1e-9) == expected_x 

101 assert data["type"] == "transformer" 

102 assert data["from_name"] == "busA" 

103 assert data["to_name"] == "busB" 

104 

105 

106def test_transformer_3winding_mesh_impedance_pairing(): 

107 t0 = make_terminal("bus1") 

108 t1 = make_terminal("bus2") 

109 t2 = make_terminal("bus3") 

110 # Primary end with mesh impedance that references transformer end 2 

111 primary_end = _make_power_transformer_end( 

112 1, ratedU=480.0, mesh_r=0.3, mesh_x=0.4, to_end_number=2 

113 ) 

114 end2 = _make_power_transformer_end(2, ratedU=480.0) 

115 end3 = _make_power_transformer_end(3, ratedU=480.0) 

116 xfmr = cim.PowerTransformer( 

117 name="Xfmr3W", 

118 PowerTransformerEnd=[primary_end, end2, end3], 

119 TransformerTanks=[], 

120 Terminals=[t0, t1, t2], 

121 ) 

122 tp = TransformerProcessor(1e6) 

123 results = tp._process_3winding_transformer(xfmr, ["bus1", "bus2", "bus3"]) 

124 assert isinstance(results, list) and len(results) == 2 

125 data12 = results[0] 

126 v_ln_expected = float(primary_end.ratedU) / math.sqrt(3) 

127 z_base = v_ln_expected**2 / tp.s_base 

128 expected_r = float(primary_end.FromMeshImpedance[0].r) / z_base 

129 expected_x = float(primary_end.FromMeshImpedance[0].x) / z_base 

130 assert pytest.approx(data12["raa"], rel=1e-9) == expected_r 

131 assert pytest.approx(data12["xaa"], rel=1e-9) == expected_x 

132 assert data12["from_name"] == "bus1" 

133 assert data12["to_name"] == "bus2" 

134 

135 

136def test_transformer_default_impedance_when_none_found(): 

137 t0 = make_terminal("b1") 

138 t1 = make_terminal("b2") 

139 primary_end = cim.PowerTransformerEnd( 

140 endNumber=1, ratedU=float(480.0), FromMeshImpedance=[] 

141 ) 

142 xfmr = cim.PowerTransformer( 

143 name="Xfmr_default", 

144 PowerTransformerEnd=[primary_end], 

145 TransformerTanks=[], 

146 Terminals=[t0, t1], 

147 ) 

148 tp = TransformerProcessor(1e6) 

149 data = tp._process_2winding_transformer(xfmr, ["b1", "b2"]) 

150 # default values expected 

151 assert pytest.approx(data["raa"], rel=1e-12) == 0.01 

152 assert pytest.approx(data["xaa"], rel=1e-12) == 0.05