Skip to content

Commit e9ec5cc

Browse files
committed
added schema tests
1 parent e8a8d5b commit e9ec5cc

9 files changed

+2605
-0
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
2+
import itertools
3+
import numpy as np
4+
import pandas as pd
5+
import pandera as pa
6+
import pytest
7+
8+
from pandapower import create_impedance
9+
from pandapower.create import create_empty_network, create_bus
10+
from pandapower.network_schema.tools.validation.network_validation import validate_network
11+
from pandapower.test.network_schema.elements.helper import (
12+
strings,
13+
all_allowed_floats,
14+
not_floats_list,
15+
not_strings_list,
16+
not_boolean_list,
17+
positiv_ints_plus_zero,
18+
negativ_ints,
19+
not_ints_list,
20+
positiv_floats,
21+
negativ_floats_plus_zero,
22+
positiv_floats_plus_zero,
23+
negativ_floats,
24+
bools,
25+
)
26+
27+
28+
class TestImpedanceRequiredFields:
29+
"""Tests for required Impedance fields"""
30+
31+
@pytest.mark.parametrize(
32+
"parameter,valid_value",
33+
list(
34+
itertools.chain(
35+
itertools.product(["from_bus"], positiv_ints_plus_zero),
36+
itertools.product(["to_bus"], positiv_ints_plus_zero),
37+
itertools.product(["rft_pu"], all_allowed_floats),
38+
itertools.product(["xft_pu"], all_allowed_floats),
39+
itertools.product(["rtf_pu"], all_allowed_floats),
40+
itertools.product(["xtf_pu"], all_allowed_floats),
41+
itertools.product(["gf_pu"], all_allowed_floats),
42+
itertools.product(["bf_pu"], all_allowed_floats),
43+
itertools.product(["gt_pu"], all_allowed_floats),
44+
itertools.product(["bt_pu"], all_allowed_floats),
45+
itertools.product(["sn_mva"], positiv_floats), # strictly > 0
46+
itertools.product(["in_service"], bools),
47+
)
48+
),
49+
)
50+
def test_valid_required_values(self, parameter, valid_value):
51+
net = create_empty_network()
52+
create_bus(net, 0.4, index=0)
53+
create_bus(net, 0.4, index=1)
54+
create_bus(net, 0.4, index=42)
55+
# Provide all required fields explicitly
56+
create_impedance(
57+
net,
58+
from_bus=0,
59+
to_bus=1,
60+
rft_pu=0.0,
61+
xft_pu=0.0,
62+
rtf_pu=0.0,
63+
xtf_pu=0.0,
64+
gf_pu=0.0,
65+
bf_pu=0.0,
66+
gt_pu=0.0,
67+
bt_pu=0.0,
68+
sn_mva=100.0,
69+
in_service=True,
70+
)
71+
net.impedance[parameter] = valid_value
72+
validate_network(net)
73+
74+
@pytest.mark.parametrize(
75+
"parameter,invalid_value",
76+
list(
77+
itertools.chain(
78+
itertools.product(["from_bus"], [*negativ_ints, *not_ints_list]),
79+
itertools.product(["to_bus"], [*negativ_ints, *not_ints_list]),
80+
itertools.product(["rft_pu"], not_floats_list),
81+
itertools.product(["xft_pu"], not_floats_list),
82+
itertools.product(["rtf_pu"], not_floats_list),
83+
itertools.product(["xtf_pu"], not_floats_list),
84+
itertools.product(["gf_pu"], not_floats_list),
85+
itertools.product(["bf_pu"], not_floats_list),
86+
itertools.product(["gt_pu"], not_floats_list),
87+
itertools.product(["bt_pu"], not_floats_list),
88+
itertools.product(["sn_mva"], [*negativ_floats_plus_zero, *not_floats_list]), # <= 0 invalid
89+
itertools.product(["in_service"], not_boolean_list),
90+
)
91+
),
92+
)
93+
def test_invalid_required_values(self, parameter, invalid_value):
94+
net = create_empty_network()
95+
create_bus(net, 0.4)
96+
create_bus(net, 0.4)
97+
create_impedance(
98+
net,
99+
from_bus=0,
100+
to_bus=1,
101+
rft_pu=0.0,
102+
xft_pu=0.0,
103+
rtf_pu=0.0,
104+
xtf_pu=0.0,
105+
gf_pu=0.0,
106+
bf_pu=0.0,
107+
gt_pu=0.0,
108+
bt_pu=0.0,
109+
sn_mva=100.0,
110+
in_service=True,
111+
)
112+
net.impedance[parameter] = invalid_value
113+
with pytest.raises(pa.errors.SchemaError):
114+
validate_network(net)
115+
116+
117+
class TestImpedanceOptionalFields:
118+
"""Tests for optional impedance fields"""
119+
120+
def test_full_optional_fields_validation(self):
121+
"""Impedance with all optional fields is valid"""
122+
net = create_empty_network()
123+
b0 = create_bus(net, 0.4)
124+
b1 = create_bus(net, 0.4)
125+
# Optional zero-sequence and name provided
126+
create_impedance(
127+
net,
128+
from_bus=b0,
129+
to_bus=b1,
130+
rft_pu=0.01,
131+
xft_pu=0.02,
132+
rtf_pu=0.03,
133+
xtf_pu=0.04,
134+
gf_pu=0.0,
135+
bf_pu=0.0,
136+
gt_pu=0.0,
137+
bt_pu=0.0,
138+
sn_mva=100.0,
139+
in_service=False,
140+
name="lorem ipsum",
141+
rft0_pu=0.1,
142+
xft0_pu=0.2,
143+
rtf0_pu=0.3,
144+
xtf0_pu=0.4,
145+
gf0_pu=0.0,
146+
bf0_pu=0.0,
147+
gt0_pu=0.0,
148+
bt0_pu=0.0,
149+
)
150+
validate_network(net)
151+
152+
def test_optional_fields_with_nulls(self):
153+
"""Optional fields can be missing or null"""
154+
net = create_empty_network()
155+
b0 = create_bus(net, 0.4)
156+
b1 = create_bus(net, 0.4)
157+
158+
# Only required fields
159+
create_impedance(
160+
net,
161+
from_bus=b0,
162+
to_bus=b1,
163+
rft_pu=0.0,
164+
xft_pu=0.0,
165+
rtf_pu=0.0,
166+
xtf_pu=0.0,
167+
gf_pu=0.0,
168+
bf_pu=0.0,
169+
gt_pu=0.0,
170+
bt_pu=0.0,
171+
sn_mva=50.0,
172+
in_service=True,
173+
)
174+
175+
# Set some optionals to NaN/None
176+
for col in ["name", "rft0_pu", "xft0_pu", "rtf0_pu", "xtf0_pu", "gf0_pu", "bf0_pu", "gt0_pu", "bt0_pu"]:
177+
# Ensure column exists and assign a null
178+
if col not in net.impedance.columns:
179+
net.impedance[col] = pd.Series([float(np.nan)], index=net.impedance.index)
180+
net.impedance[col].iat[0] = pd.NA
181+
182+
# Set name dtype properly
183+
net.impedance["name"] = net.impedance["name"].astype(pd.StringDtype())
184+
185+
validate_network(net)
186+
187+
@pytest.mark.parametrize(
188+
"parameter,valid_value",
189+
list(
190+
itertools.chain(
191+
itertools.product(["name"], [pd.NA, *strings]),
192+
itertools.product(["rft0_pu"], positiv_floats),
193+
itertools.product(["xft0_pu"], positiv_floats),
194+
itertools.product(["rtf0_pu"], positiv_floats),
195+
itertools.product(["xtf0_pu"], positiv_floats),
196+
itertools.product(["gf0_pu"], [float(np.nan), *positiv_floats_plus_zero, *negativ_floats_plus_zero]),
197+
itertools.product(["bf0_pu"], [float(np.nan), *positiv_floats_plus_zero, *negativ_floats_plus_zero]),
198+
itertools.product(["gt0_pu"], [float(np.nan), *positiv_floats_plus_zero, *negativ_floats_plus_zero]),
199+
itertools.product(["bt0_pu"], [float(np.nan), *positiv_floats_plus_zero, *negativ_floats_plus_zero]),
200+
)
201+
),
202+
)
203+
def test_valid_optional_values(self, parameter, valid_value):
204+
net = create_empty_network()
205+
b0 = create_bus(net, 0.4)
206+
b1 = create_bus(net, 0.4)
207+
create_impedance(
208+
net,
209+
from_bus=b0,
210+
to_bus=b1,
211+
rft_pu=0.0,
212+
xft_pu=0.0,
213+
rtf_pu=0.0,
214+
xtf_pu=0.0,
215+
gf_pu=0.0,
216+
bf_pu=0.0,
217+
gt_pu=0.0,
218+
bt_pu=0.0,
219+
sn_mva=10.0,
220+
in_service=False,
221+
)
222+
if parameter == "name":
223+
net.impedance[parameter] = pd.Series([valid_value], dtype=pd.StringDtype())
224+
else:
225+
net.impedance[parameter] = valid_value
226+
validate_network(net)
227+
228+
@pytest.mark.parametrize(
229+
"parameter,invalid_value",
230+
list(
231+
itertools.chain(
232+
itertools.product(["name"], not_strings_list),
233+
# zero-sequence impedance parts must be > 0 if provided
234+
itertools.product(["rft0_pu"], [*negativ_floats_plus_zero, *not_floats_list]),
235+
itertools.product(["xft0_pu"], [*negativ_floats_plus_zero, *not_floats_list]),
236+
itertools.product(["rtf0_pu"], [*negativ_floats_plus_zero, *not_floats_list]),
237+
itertools.product(["xtf0_pu"], [*negativ_floats_plus_zero, *not_floats_list]),
238+
# zero-sequence shunt parts are floats if provided (no >0 check)
239+
itertools.product(["gf0_pu"], not_floats_list),
240+
itertools.product(["bf0_pu"], not_floats_list),
241+
itertools.product(["gt0_pu"], not_floats_list),
242+
itertools.product(["bt0_pu"], not_floats_list),
243+
)
244+
),
245+
)
246+
def test_invalid_optional_values(self, parameter, invalid_value):
247+
net = create_empty_network()
248+
b0 = create_bus(net, 0.4)
249+
b1 = create_bus(net, 0.4)
250+
create_impedance(
251+
net,
252+
from_bus=b0,
253+
to_bus=b1,
254+
rft_pu=0.0,
255+
xft_pu=0.0,
256+
rtf_pu=0.0,
257+
xtf_pu=0.0,
258+
gf_pu=0.0,
259+
bf_pu=0.0,
260+
gt_pu=0.0,
261+
bt_pu=0.0,
262+
sn_mva=10.0,
263+
in_service=True,
264+
)
265+
net.impedance[parameter] = invalid_value
266+
with pytest.raises(pa.errors.SchemaError):
267+
validate_network(net)
268+
269+
270+
class TestImpedanceResults:
271+
"""Tests for impedance results after calculations"""
272+
273+
@pytest.mark.skip(reason="Not yet implemented")
274+
def test_impedance_power_results(self):
275+
"""Test: Power results are consistent"""
276+
pass
277+
278+
@pytest.mark.skip(reason="Not yet implemented")
279+
def test_impedance_currents_results(self):
280+
"""Test: Currents results are within valid range"""
281+
pass

0 commit comments

Comments
 (0)