1+
2+ # test_asymmetric_sgen.py
3+
4+ import itertools
5+ import pandas as pd
6+ import pandera as pa
7+ import pytest
8+ import numpy as np
9+
10+ from pandapower import create_asymmetric_sgen
11+ from pandapower .create import create_empty_network , create_bus
12+ from pandapower .network_schema .tools .validation .network_validation import validate_network
13+
14+ from pandapower .test .network_schema .elements .helper import (
15+ strings ,
16+ bools ,
17+ not_strings_list ,
18+ not_floats_list ,
19+ not_boolean_list ,
20+ positiv_ints_plus_zero ,
21+ positiv_floats ,
22+ positiv_floats_plus_zero ,
23+ negativ_floats ,
24+ negativ_floats_plus_zero ,
25+ not_ints_list ,
26+ negativ_ints ,
27+ all_allowed_floats ,
28+ )
29+
30+
31+ class TestAsymmetricSgenRequiredFields :
32+ """Tests for required asymmetric_sgen fields"""
33+
34+ @pytest .mark .parametrize (
35+ "parameter,valid_value" ,
36+ list (
37+ itertools .chain (
38+ itertools .product (["bus" ], positiv_ints_plus_zero ),
39+ itertools .product (["p_a_mw" ], negativ_floats_plus_zero ),
40+ itertools .product (["p_b_mw" ], negativ_floats_plus_zero ),
41+ itertools .product (["p_c_mw" ], negativ_floats_plus_zero ),
42+ itertools .product (["q_a_mvar" ], all_allowed_floats ),
43+ itertools .product (["q_b_mvar" ], all_allowed_floats ),
44+ itertools .product (["q_c_mvar" ], all_allowed_floats ),
45+ itertools .product (["scaling" ], positiv_floats_plus_zero ),
46+ itertools .product (["in_service" ], bools ),
47+ itertools .product (["current_source" ], bools ),
48+ )
49+ ),
50+ )
51+ def test_valid_required_values (self , parameter , valid_value ):
52+ """Test: valid required values are accepted"""
53+ net = create_empty_network ()
54+ create_bus (net , 0.4 ) # index 0
55+ create_bus (net , 0.4 ) # index 1
56+ create_bus (net , 0.4 , index = 42 )
57+
58+ create_asymmetric_sgen (
59+ net ,
60+ bus = 0 ,
61+ p_a_mw = - 1.0 ,
62+ q_a_mvar = 0.5 ,
63+ p_b_mw = - 1.0 ,
64+ q_b_mvar = 0.5 ,
65+ p_c_mw = - 1.0 ,
66+ q_c_mvar = 0.5 ,
67+ scaling = 1.0 ,
68+ in_service = True ,
69+ current_source = False ,
70+ type = "PV" ,
71+ name = "test" ,
72+ sn_mva = 10.0 ,
73+ )
74+ net .asymmetric_sgen [parameter ] = valid_value
75+
76+ validate_network (net )
77+
78+ @pytest .mark .parametrize (
79+ "parameter,invalid_value" ,
80+ list (
81+ itertools .chain (
82+ itertools .product (["bus" ], [* negativ_ints , * not_ints_list ]),
83+ itertools .product (["p_a_mw" ], [* positiv_floats , * not_floats_list ]),
84+ itertools .product (["p_b_mw" ], [* positiv_floats , * not_floats_list ]),
85+ itertools .product (["p_c_mw" ], [* positiv_floats , * not_floats_list ]),
86+ itertools .product (["q_a_mvar" ], not_floats_list ),
87+ itertools .product (["q_b_mvar" ], not_floats_list ),
88+ itertools .product (["q_c_mvar" ], not_floats_list ),
89+ itertools .product (["scaling" ], [* negativ_floats , * not_floats_list ]),
90+ itertools .product (["in_service" ], not_boolean_list ),
91+ itertools .product (["current_source" ], not_boolean_list ),
92+ )
93+ ),
94+ )
95+ def test_invalid_required_values (self , parameter , invalid_value ):
96+ """Test: invalid required values are rejected"""
97+ net = create_empty_network ()
98+ create_bus (net , 0.4 ) # index 0
99+ create_bus (net , 0.4 ) # index 1
100+
101+ create_asymmetric_sgen (
102+ net ,
103+ bus = 0 ,
104+ p_a_mw = - 1.0 ,
105+ q_a_mvar = 0.5 ,
106+ p_b_mw = - 1.0 ,
107+ q_b_mvar = 0.5 ,
108+ p_c_mw = - 1.0 ,
109+ q_c_mvar = 0.5 ,
110+ scaling = 1.0 ,
111+ in_service = True ,
112+ current_source = False ,
113+ type = "PV" ,
114+ name = "test" ,
115+ sn_mva = 10.0 ,
116+ )
117+ net .asymmetric_sgen [parameter ] = invalid_value
118+
119+ with pytest .raises (pa .errors .SchemaError ):
120+ validate_network (net )
121+
122+
123+ class TestAsymmetricSgenOptionalFields :
124+ """Tests for optional asymmetric_sgen fields"""
125+
126+ def test_all_optional_fields_valid (self ):
127+ """Test: asymmetric_sgen with every optional field is valid"""
128+ net = create_empty_network ()
129+ b0 = create_bus (net , 0.4 )
130+
131+ create_asymmetric_sgen (
132+ net ,
133+ bus = b0 ,
134+ p_a_mw = - 10.0 ,
135+ q_a_mvar = 5.0 ,
136+ p_b_mw = - 12.0 ,
137+ q_b_mvar = 6.0 ,
138+ p_c_mw = - 11.0 ,
139+ q_c_mvar = 4.0 ,
140+ scaling = 1.0 ,
141+ in_service = True ,
142+ current_source = True ,
143+ type = "WP" ,
144+ name = "lorem ipsum" ,
145+ sn_mva = 25.0 ,
146+ )
147+ validate_network (net )
148+
149+ def test_optional_fields_with_nulls (self ):
150+ """Test: asymmetric_sgen with optional fields including nulls is valid"""
151+ net = create_empty_network ()
152+ b0 = create_bus (net , 0.4 )
153+
154+ create_asymmetric_sgen (
155+ net ,
156+ bus = b0 ,
157+ p_a_mw = - 10.0 ,
158+ q_a_mvar = 5.0 ,
159+ p_b_mw = - 10.0 ,
160+ q_b_mvar = 5.0 ,
161+ p_c_mw = - 10.0 ,
162+ q_c_mvar = 5.0 ,
163+ scaling = 1.0 ,
164+ in_service = True ,
165+ current_source = False ,
166+ name = "lorem ipsum" ,
167+ )
168+ create_asymmetric_sgen (
169+ net ,
170+ bus = b0 ,
171+ p_a_mw = - 8.0 ,
172+ q_a_mvar = 3.0 ,
173+ p_b_mw = - 9.0 ,
174+ q_b_mvar = 3.5 ,
175+ p_c_mw = - 7.5 ,
176+ q_c_mvar = 2.5 ,
177+ scaling = 1.0 ,
178+ in_service = False ,
179+ current_source = True ,
180+ type = "CHP" ,
181+ )
182+ create_asymmetric_sgen (
183+ net ,
184+ bus = b0 ,
185+ p_a_mw = - 8.0 ,
186+ q_a_mvar = 3.0 ,
187+ p_b_mw = - 9.0 ,
188+ q_b_mvar = 3.5 ,
189+ p_c_mw = - 7.5 ,
190+ q_c_mvar = 2.5 ,
191+ scaling = 1.0 ,
192+ in_service = False ,
193+ current_source = True ,
194+ sn_mva = 15.0 ,
195+ )
196+ net .asymmetric_sgen ['type' ].at [0 ] = None
197+ net .asymmetric_sgen ['type' ].at [2 ] = None
198+
199+ validate_network (net )
200+
201+ @pytest .mark .parametrize (
202+ "parameter,valid_value" ,
203+ list (
204+ itertools .chain (
205+ itertools .product (['name' ], strings ),
206+ #itertools.product(["type"], [pd.NA, "PV", "WP", "CHP"]),
207+ itertools .product (["sn_mva" ], positiv_floats ),
208+ )
209+ ),
210+ )
211+ def test_valid_optional_values (self , parameter , valid_value ):
212+ """Test: valid optional values are accepted"""
213+ net = create_empty_network ()
214+ b0 = create_bus (net , 0.4 )
215+
216+ create_asymmetric_sgen (
217+ net ,
218+ bus = b0 ,
219+ p_a_mw = - 10.0 ,
220+ q_a_mvar = 5.0 ,
221+ p_b_mw = - 10.0 ,
222+ q_b_mvar = 5.0 ,
223+ p_c_mw = - 10.0 ,
224+ q_c_mvar = 5.0 ,
225+ scaling = 1.0 ,
226+ in_service = True ,
227+ current_source = False ,
228+ ** {parameter : valid_value }
229+ )
230+ validate_network (net )
231+
232+ @pytest .mark .parametrize (
233+ "parameter,invalid_value" ,
234+ list (
235+ itertools .chain (
236+ itertools .product (["name" ], not_strings_list ),
237+ itertools .product (["type" ], [* strings , * not_strings_list ]),
238+ itertools .product (["sn_mva" ], [* negativ_floats_plus_zero , * not_floats_list ]),
239+ )
240+ ),
241+ )
242+ def test_invalid_optional_values (self , parameter , invalid_value ):
243+ """Test: invalid optional values are rejected"""
244+ net = create_empty_network ()
245+ b0 = create_bus (net , 0.4 )
246+
247+ create_asymmetric_sgen (
248+ net ,
249+ bus = b0 ,
250+ p_a_mw = - 1.0 ,
251+ q_a_mvar = 0.2 ,
252+ p_b_mw = - 1.0 ,
253+ q_b_mvar = 0.2 ,
254+ p_c_mw = - 1.0 ,
255+ q_c_mvar = 0.2 ,
256+ scaling = 1.0 ,
257+ in_service = True ,
258+ current_source = True ,
259+ )
260+ net .asymmetric_sgen [parameter ] = invalid_value
261+ with pytest .raises (pa .errors .SchemaError ):
262+ validate_network (net )
263+
264+
265+ class TestAsymmetricSgenForeignKey :
266+ """Tests for foreign key constraints"""
267+
268+ def test_invalid_bus_index (self ):
269+ """Test: bus FK must reference an existing bus index"""
270+ net = create_empty_network ()
271+ b0 = create_bus (net , 0.4 )
272+
273+ create_asymmetric_sgen (
274+ net ,
275+ bus = b0 ,
276+ p_a_mw = - 1.0 ,
277+ q_a_mvar = 0.5 ,
278+ p_b_mw = - 1.0 ,
279+ q_b_mvar = 0.5 ,
280+ p_c_mw = - 1.0 ,
281+ q_c_mvar = 0.5 ,
282+ scaling = 1.0 ,
283+ in_service = True ,
284+ current_source = False ,
285+ type = "PV" ,
286+ )
287+
288+ net .asymmetric_sgen ["bus" ] = 9999
289+
290+ with pytest .raises (pa .errors .SchemaError ):
291+ validate_network (net )
292+
293+
294+ class TestAsymmetricSgenResults :
295+ """Tests for asymmetric_sgen results after calculations"""
296+
297+ @pytest .mark .skip (reason = "Not yet implemented" )
298+ def test_asymmetric_sgen_result_totals (self ):
299+ """Test: aggregated p_mw / q_mvar results are consistent"""
300+ pass
301+
302+ @pytest .mark .skip (reason = "Not yet implemented" )
303+ def test_asymmetric_sgen_3ph_results (self ):
304+ """Test: 3-phase results contain valid values per phase"""
305+ pass
0 commit comments