1+
2+ # test_pandera_b2b_vsc_elements.py
3+
4+ import itertools
5+ import pandas as pd
6+ import pandera as pa
7+ import pytest
8+
9+ from pandapower import create_b2b_vsc
10+ from pandapower .create import create_empty_network , create_bus , create_bus_dc
11+ from pandapower .network_schema .tools .validation .network_validation import validate_network
12+
13+ from pandapower .test .network_schema .elements .helper import (
14+ strings ,
15+ bools ,
16+ not_strings_list ,
17+ not_floats_list ,
18+ not_boolean_list ,
19+ positiv_ints_plus_zero ,
20+ not_ints_list ,
21+ negativ_ints ,
22+ all_allowed_floats ,
23+ )
24+
25+
26+ class TestB2BVSCRequiredFields :
27+ """Tests for required b2b_vsc fields"""
28+
29+ @pytest .mark .parametrize (
30+ "parameter,valid_value" ,
31+ list (
32+ itertools .chain (
33+ itertools .product (["bus" ], positiv_ints_plus_zero ),
34+ itertools .product (["bus_dc_plus" ], positiv_ints_plus_zero ),
35+ itertools .product (["bus_dc_minus" ], positiv_ints_plus_zero ),
36+ itertools .product (["r_ohm" ], all_allowed_floats ),
37+ itertools .product (["x_ohm" ], all_allowed_floats ),
38+ itertools .product (["r_dc_ohm" ], all_allowed_floats ),
39+ itertools .product (["pl_dc_mw" ], all_allowed_floats ),
40+ itertools .product (["control_mode_ac" ], ["vm_pu" , "q_mvar" , "slack" ]),
41+ itertools .product (["control_value_ac" ], all_allowed_floats ),
42+ itertools .product (["control_mode_dc" ], ["vm_pu" , "p_mw" ]),
43+ itertools .product (["control_value_dc" ], all_allowed_floats ),
44+ itertools .product (["controllable" ], bools ),
45+ itertools .product (["in_service" ], bools ),
46+ )
47+ ),
48+ )
49+ def test_valid_required_values (self , parameter , valid_value ):
50+ """Test: valid required values are accepted"""
51+ net = create_empty_network ()
52+ create_bus (net , vn_kv = 110.0 ) # index 0
53+ create_bus (net , vn_kv = 110.0 ) # index 1
54+ create_bus (net , vn_kv = 110.0 , index = 42 )
55+ create_bus_dc (net , vn_kv = 110.0 ) # index 0
56+ create_bus_dc (net , vn_kv = 110.0 ) # index 1
57+ create_bus_dc (net , vn_kv = 110.0 , index = 42 )
58+
59+ create_b2b_vsc (
60+ net ,
61+ bus = 0 ,
62+ bus_dc_plus = 0 ,
63+ bus_dc_minus = 1 ,
64+ r_ohm = 0.1 ,
65+ x_ohm = 0.2 ,
66+ r_dc_ohm = 0.05 ,
67+ pl_dc_mw = 0.0 ,
68+ control_mode_ac = "vm_pu" ,
69+ control_value_ac = 1.0 ,
70+ control_mode_dc = "p_mw" ,
71+ control_value_dc = 0.0 ,
72+ controllable = True ,
73+ in_service = True ,
74+ name = "test" ,
75+ )
76+
77+ net .b2b_vsc [parameter ] = valid_value
78+ validate_network (net )
79+
80+ @pytest .mark .parametrize (
81+ "parameter,invalid_value" ,
82+ list (
83+ itertools .chain (
84+ itertools .product (["bus" ], [* negativ_ints , * not_ints_list ]),
85+ itertools .product (["bus_dc_plus" ], [* negativ_ints , * not_ints_list ]),
86+ itertools .product (["bus_dc_minus" ], [* negativ_ints , * not_ints_list ]),
87+ itertools .product (["r_ohm" ], not_floats_list ),
88+ itertools .product (["x_ohm" ], not_floats_list ),
89+ itertools .product (["r_dc_ohm" ], not_floats_list ),
90+ itertools .product (["pl_dc_mw" ], not_floats_list ),
91+ itertools .product (["control_mode_ac" ], [* strings , * not_strings_list ]),
92+ itertools .product (["control_value_ac" ], not_floats_list ),
93+ itertools .product (["control_mode_dc" ], [* strings , * not_strings_list ]),
94+ itertools .product (["control_value_dc" ], not_floats_list ),
95+ itertools .product (["controllable" ], not_boolean_list ),
96+ itertools .product (["in_service" ], not_boolean_list ),
97+ )
98+ ),
99+ )
100+ def test_invalid_required_values (self , parameter , invalid_value ):
101+ """Test: invalid required values are rejected"""
102+ net = create_empty_network ()
103+ create_bus (net , 110.0 ) # index 0
104+ create_bus (net , 110.0 ) # index 1
105+ create_bus_dc (net , vn_kv = 110.0 ) # index 0
106+ create_bus_dc (net , vn_kv = 110.0 ) # index 1
107+ create_bus_dc (net , vn_kv = 110.0 , index = 42 )
108+ create_b2b_vsc (
109+ net ,
110+ bus = 0 ,
111+ bus_dc_plus = 0 ,
112+ bus_dc_minus = 1 ,
113+ r_ohm = 0.1 ,
114+ x_ohm = 0.2 ,
115+ r_dc_ohm = 0.05 ,
116+ pl_dc_mw = 0.0 ,
117+ control_mode_ac = "vm_pu" ,
118+ control_value_ac = 1.0 ,
119+ control_mode_dc = "p_mw" ,
120+ control_value_dc = 0.0 ,
121+ controllable = True ,
122+ in_service = True ,
123+ )
124+
125+ net .b2b_vsc [parameter ] = invalid_value
126+ with pytest .raises (pa .errors .SchemaError ):
127+ validate_network (net )
128+
129+
130+ class TestB2BVSCOptionalFields :
131+ """Tests for optional b2b_vsc fields"""
132+ @pytest .mark .parametrize (
133+ "parameter,valid_value" ,
134+ list (
135+ itertools .chain (
136+ itertools .product (["name" ], [pd .NA , * strings ]),
137+ )
138+ ),
139+ )
140+ def test_valid_optional_values (self , parameter , valid_value ):
141+ """Test: valid optional values are accepted"""
142+ net = create_empty_network ()
143+ b0 = create_bus (net , 110.0 )
144+ create_bus_dc (net , vn_kv = 110.0 ) # index 0
145+ create_bus_dc (net , vn_kv = 110.0 ) # index 1
146+ create_b2b_vsc (
147+ net ,
148+ bus = b0 ,
149+ bus_dc_plus = 0 ,
150+ bus_dc_minus = 1 ,
151+ r_ohm = 0.1 ,
152+ x_ohm = 0.2 ,
153+ r_dc_ohm = 0.05 ,
154+ pl_dc_mw = 0.0 ,
155+ control_mode_ac = "vm_pu" ,
156+ control_value_ac = 1.0 ,
157+ control_mode_dc = "p_mw" ,
158+ control_value_dc = 0.0 ,
159+ controllable = True ,
160+ in_service = True ,
161+ name = "initial" ,
162+ )
163+
164+ net .b2b_vsc [parameter ] = pd .Series ([valid_value ], dtype = pd .StringDtype ())
165+ validate_network (net )
166+
167+ @pytest .mark .parametrize (
168+ "parameter,invalid_value" ,
169+ list (
170+ itertools .chain (
171+ itertools .product (["name" ], not_strings_list ),
172+ )
173+ ),
174+ )
175+ def test_invalid_optional_values (self , parameter , invalid_value ):
176+ """Test: invalid optional values are rejected"""
177+ net = create_empty_network ()
178+ b0 = create_bus (net , 110.0 )
179+ create_bus_dc (net , vn_kv = 110.0 ) # index 0
180+ create_bus_dc (net , vn_kv = 110.0 ) # index 1
181+ create_b2b_vsc (
182+ net ,
183+ bus = b0 ,
184+ bus_dc_plus = 0 ,
185+ bus_dc_minus = 1 ,
186+ r_ohm = 0.1 ,
187+ x_ohm = 0.2 ,
188+ r_dc_ohm = 0.05 ,
189+ pl_dc_mw = 0.0 ,
190+ control_mode_ac = "vm_pu" ,
191+ control_value_ac = 1.0 ,
192+ control_mode_dc = "p_mw" ,
193+ control_value_dc = 0.0 ,
194+ controllable = True ,
195+ in_service = True ,
196+ )
197+
198+ net .b2b_vsc [parameter ] = invalid_value
199+ with pytest .raises (pa .errors .SchemaError ):
200+ validate_network (net )
201+
202+
203+ class TestB2BVSCSchemaForeignKey :
204+ """Tests for foreign key constraints"""
205+
206+ def test_invalid_bus_index (self ):
207+ """Test: bus FK must reference an existing bus index"""
208+ net = create_empty_network ()
209+ b0 = create_bus (net , 110.0 )
210+ create_bus_dc (net , vn_kv = 110.0 ) # index 0
211+ create_bus_dc (net , vn_kv = 110.0 ) # index 1
212+ create_b2b_vsc (
213+ net ,
214+ bus = b0 ,
215+ bus_dc_plus = 0 ,
216+ bus_dc_minus = 1 ,
217+ r_ohm = 0.1 ,
218+ x_ohm = 0.2 ,
219+ r_dc_ohm = 0.05 ,
220+ pl_dc_mw = 0.0 ,
221+ control_mode_ac = "vm_pu" ,
222+ control_value_ac = 1.0 ,
223+ control_mode_dc = "p_mw" ,
224+ control_value_dc = 0.0 ,
225+ controllable = True ,
226+ in_service = True ,
227+ )
228+
229+ net .b2b_vsc ["bus" ] = 9999
230+ with pytest .raises (pa .errors .SchemaError ):
231+ validate_network (net )
232+
233+ def test_invalid_bus_dc_plus_index (self ):
234+ """Test: bus_dc_plus FK must reference an existing dc bus index"""
235+ net = create_empty_network ()
236+ b0 = create_bus (net , 110.0 )
237+ create_bus_dc (net , vn_kv = 110.0 ) # index 0
238+ create_bus_dc (net , vn_kv = 110.0 ) # index 1
239+ create_b2b_vsc (
240+ net ,
241+ bus = b0 ,
242+ bus_dc_plus = 0 ,
243+ bus_dc_minus = 1 ,
244+ r_ohm = 0.1 ,
245+ x_ohm = 0.2 ,
246+ r_dc_ohm = 0.05 ,
247+ pl_dc_mw = 0.0 ,
248+ control_mode_ac = "vm_pu" ,
249+ control_value_ac = 1.0 ,
250+ control_mode_dc = "p_mw" ,
251+ control_value_dc = 0.0 ,
252+ controllable = True ,
253+ in_service = True ,
254+ )
255+
256+ net .b2b_vsc ["bus_dc_plus" ] = 9999
257+ with pytest .raises (pa .errors .SchemaError ):
258+ validate_network (net )
259+
260+ def test_invalid_bus_dc_minus_index (self ):
261+ """Test: bus_dc_minus FK must reference an existing dc bus index"""
262+ net = create_empty_network ()
263+ b0 = create_bus (net , 110.0 )
264+ create_bus_dc (net , vn_kv = 110.0 ) # index 0
265+ create_bus_dc (net , vn_kv = 110.0 ) # index 1
266+ create_b2b_vsc (
267+ net ,
268+ bus = b0 ,
269+ bus_dc_plus = 0 ,
270+ bus_dc_minus = 1 ,
271+ r_ohm = 0.1 ,
272+ x_ohm = 0.2 ,
273+ r_dc_ohm = 0.05 ,
274+ pl_dc_mw = 0.0 ,
275+ control_mode_ac = "vm_pu" ,
276+ control_value_ac = 1.0 ,
277+ control_mode_dc = "p_mw" ,
278+ control_value_dc = 0.0 ,
279+ controllable = True ,
280+ in_service = True ,
281+ )
282+
283+ net .b2b_vsc ["bus_dc_minus" ] = 9999
284+ with pytest .raises (pa .errors .SchemaError ):
285+ validate_network (net )
286+
287+
288+ class TestB2BVSCResults :
289+ """Tests for b2b_vsc results after calculations"""
290+
291+ @pytest .mark .skip (reason = "Not yet implemented" )
292+ def test_b2b_vsc_result_totals (self ):
293+ """Test: aggregated p_mw / q_mvar results are consistent"""
294+ pass
295+
296+ @pytest .mark .skip (reason = "Not yet implemented" )
297+ def test_b2b_vsc_internal_results (self ):
298+ """Test: internal vm/va and dc quantities are within expected ranges"""
299+ pass
0 commit comments