PandaPower conversion
This example illustrates conversion from PandaPower to power-grid-model input data. We can then calculate power-flow with it or convert to a different formats like PGM JSON.
NOTE: To run this example, the optional examples dependencies are required:
pip install .[examples]
1. Load the PandaPower Data
For this example we will construct a minimal pandapower network.
(ext_grid #1) shunt - [104] - trafo_3w - [105] - (sym_gen + asym_gen + asym_load + ward + motor)
| |
[101] ---trafo- [102] ------------- [103]
| |
-/- (load #31)
|
[106]
import warnings
import pandapower as pp
warnings.filterwarnings("ignore", module="pandapower", category=FutureWarning) # Hide warnings related to pandas
def pandapower_simple_grid():
net = pp.create_empty_network(f_hz=50)
pp.create_bus(net, index=101, vn_kv=110)
pp.create_bus(net, index=102, vn_kv=20)
pp.create_bus(net, index=103, vn_kv=20)
pp.create_bus(net, index=104, vn_kv=30.1)
pp.create_bus(net, index=105, vn_kv=60)
pp.create_bus(net, index=106, vn_kv=110)
pp.create_ext_grid(net, index=1, in_service=True, bus=101, vm_pu=1, s_sc_max_mva=1e10, rx_max=0, va_degree=0)
pp.create_transformer_from_parameters(
net,
index=101,
hv_bus=101,
lv_bus=102,
i0_percent=3.0,
pfe_kw=11.6,
vkr_percent=10.22,
sn_mva=40,
vn_lv_kv=20.0,
vn_hv_kv=110.0,
vk_percent=17.8,
vector_group="Dyn",
shift_degree=30,
tap_side="hv",
tap_pos=2,
tap_min=-1,
tap_max=3,
tap_step_percent=2,
tap_neutral=1,
parallel=2,
)
pp.create_line(
net, index=101, from_bus=103, to_bus=102, length_km=1.23, parallel=2, df=0.2, std_type="NAYY 4x150 SE"
)
pp.create_load(
net, index=101, bus=103, p_mw=2.5, q_mvar=0.24, const_i_percent=26.0, const_z_percent=51.0, cos_phi=2
)
pp.create_switch(net, index=101, et="l", bus=103, element=101, closed=True)
pp.create_switch(net, index=3021, et="b", bus=101, element=106, closed=True)
pp.create_switch(net, index=321, et="t", bus=101, element=101, closed=True)
pp.create_shunt(net, index=1201, in_service=True, bus=104, p_mw=0.1, q_mvar=0.55, step=3)
pp.create_sgen(net, index=31, bus=105, p_mw=1.21, q_mvar=0.81)
pp.create_asymmetric_sgen(
net, index=32, bus=105, p_a_mw=0.1, p_b_mw=0.2, p_c_mw=3, q_a_mvar=0.01, q_b_mvar=0.01, q_c_mvar=0.01
)
pp.create_asymmetric_load(
net, index=33, bus=105, p_a_mw=0.1, p_b_mw=0.2, p_c_mw=3, q_a_mvar=0.01, q_b_mvar=0.01, q_c_mvar=0.01
)
pp.create_ward(net, index=34, bus=105, ps_mw=0.1, qs_mvar=0.1, pz_mw=0.1, qz_mvar=0.1)
pp.create_motor(
net, bus=105, index=12, pn_mech_mw=0.1, cos_phi=0.9, loading_percent=80, efficiency_percent=90, scaling=0.8
)
pp.create_transformer3w_from_parameters(
net,
index=102,
hv_bus=103,
mv_bus=105,
lv_bus=104,
in_service=True,
vn_hv_kv=20.0,
vn_mv_kv=60.0,
vn_lv_kv=30.1,
sn_hv_mva=40,
sn_mv_mva=100,
sn_lv_mva=50,
vk_hv_percent=10,
vk_mv_percent=11,
vk_lv_percent=12,
vkr_hv_percent=1,
vkr_mv_percent=2,
vkr_lv_percent=4,
i0_percent=0.1,
pfe_kw=10,
vector_group="Dyny",
shift_mv_degree=30,
shift_lv_degree=30,
tap_side="lv",
tap_pos=2,
tap_min=1,
tap_max=3,
tap_step_percent=3,
tap_neutral=2,
)
return net
Instantiate the converter. The converter assumes that all the parameters (eg. r_ohm_per_km) are already present in the respective component dataframes. If they are not present but a std_type is mentioned, then it is recommended that the user refers pandapower.add_zero_impedance_parameters() or pandapower.load_std_type() to include those parameters to the pandapower net.
Then use load_input_data() to load the data and convert it to power-grid-model data.
The additional information that is not used in the powerflow calculation but may be useful to link the results to the source data is stored in extra_info.
%%capture cap --no-stderr
from power_grid_model_io.converters import PandaPowerConverter
pp_net = pandapower_simple_grid()
converter = PandaPowerConverter()
input_data, extra_info = converter.load_input_data(pp_net)
Let’s investigate the data we have converted, for one of the components: lines
import pandas as pd
from power_grid_model import AttributeType, ComponentType
pd.options.future.no_silent_downcasting = True # enable behaviour of pandas 3.x
# The node data is stored as a numpy structured array in input_data[ComponentType.line]
display(input_data[ComponentType.line])
# We can use pandas to display the data in a convenient tabular format
# display(pd.DataFrame(input_data[ComponentType.line]))
# The original indices are stored in the extra_data dictionary
display({i: extra_info[i] for i in input_data[ComponentType.line][AttributeType.id]})
array([(6, 2, 1, 1, 1, 0.12792, 0.0492, 6.4206e-07, 0., nan, nan, 0., 0., 108.)],
dtype={'names': [id, from_node, to_node, from_status, to_status, r1, x1, c1, tan1, r0, x0, c0, tan0, i_n], 'formats': ['<i4', '<i4', '<i4', 'i1', 'i1', '<f8', '<f8', '<f8', '<f8', '<f8', '<f8', '<f8', '<f8', '<f8'], 'offsets': [0, 4, 8, 12, 13, 16, 24, 32, 40, 48, 56, 64, 72, 80], 'itemsize': 88, 'aligned': True})
{np.int32(6): {'id_reference': {'table': 'line', 'index': 101},
'pgm_input': {from_node: np.int32(2),
to_node: np.int32(1),
i_n: np.float64(108.0)}}}
2. Validate the data
Before we run a power flow calculation, it is wise validate the data. The most basic method is to use assert_valid_input_data(), which will raise a ValueError when the data is invalid. For more details on data validation, please consult the validation Example.
from power_grid_model import CalculationType
from power_grid_model.validation import assert_valid_input_data
assert_valid_input_data(input_data, calculation_type=CalculationType.power_flow, symmetric=True)
3. Run the calculation
Run powerflow calculation with the input_data and show the results for nodes.
from power_grid_model import PowerGridModel
pgm = PowerGridModel(input_data=input_data)
output_data = pgm.calculate_power_flow()
display(pd.DataFrame(output_data[ComponentType.node]))
| id | energized | u_pu | u | u_angle | p | q | |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 1 | 1.000000 | 109999.999962 | -1.798666e-10 | 1.798666e+06 | 3.476629e+06 |
| 1 | 1 | 1 | 0.973746 | 19474.919873 | -5.239008e-01 | 5.817753e-07 | -1.649725e-08 |
| 2 | 2 | 1 | 0.973014 | 19460.275746 | -5.237224e-01 | -2.414573e+06 | -2.317990e+05 |
| 3 | 3 | 1 | 0.969550 | 29183.446690 | -1.045187e+00 | 2.765216e-08 | -4.767335e-08 |
| 4 | 4 | 1 | 0.971998 | 58319.874965 | -1.044829e+00 | 9.444109e+05 | 5.810813e+05 |
| 5 | 5 | 1 | 1.000000 | 109999.999962 | -1.798666e-10 | 0.000000e+00 | -0.000000e+00 |
Cross referencing objects
The converter has generated unique numerical IDs for all the components in the pandapower net, in fact for some special components like loads , multiple PGM components have been created, each with their own numerical ID. To find out which component belongs to which id, some helper functions have been defined:
print("PGM object #4:", converter.lookup_id(4))
print("Trafo with index=101:", converter.get_id("trafo", 101))
PGM object #4: {'table': 'bus', 'index': np.int64(105)}
Trafo with index=101: 12
Saving the data as a JSON file
The data can be stored in a json file using the PgmJsonConverter. The file will be saved in the destination_file path supplied in the constructor.
from power_grid_model_io.converters import PgmJsonConverter
input_file = "data/pandapower/example_simple_input.json"
output_file = "data/pandapower/example_simple_output.json"
PgmJsonConverter(destination_file=input_file).save(data=input_data, extra_info=extra_info)
PgmJsonConverter(destination_file=output_file).save(data=output_data, extra_info=extra_info)
4. Converting output data
Before we convert the output data, lets run the powerflow in pandapower so we can compare results for demostration purpose
pp.runpp(pp_net, trafo_model="pi", trafo_loading="power", calculate_voltage_angles=True, numba=False)
display(pp_net.res_bus)
| vm_pu | va_degree | p_mw | q_mvar | |
|---|---|---|---|---|
| 101 | 1.000000 | 0.000000 | -1.877113 | -3.640600 |
| 102 | 0.993171 | -30.016181 | 0.000000 | 0.000000 |
| 103 | 0.992419 | -30.005563 | 2.475814 | 0.237678 |
| 104 | 0.988794 | -59.887701 | 0.293314 | 1.613227 |
| 105 | 0.991372 | -59.864589 | -0.940607 | -0.577278 |
| 106 | 1.000000 | 0.000000 | 0.000000 | 0.000000 |
To get the results of powerflow in the pandapower net, convert the result from power-grid-model powerflow i.e., output_data from previous section to the pandapower res_* dataframes.
converted_output_data = converter.convert(output_data)
display(converted_output_data["res_bus"])
| vm_pu | va_degree | p_mw | q_mvar | |
|---|---|---|---|---|
| 101 | 1.000000 | -1.030560e-08 | -1.798666e+00 | -3.476629e+00 |
| 102 | 0.973746 | -3.001731e+01 | -2.926681e-13 | -5.341135e-13 |
| 103 | 0.973014 | -3.000708e+01 | 2.414573e+00 | 2.317990e-01 |
| 104 | 0.969550 | -5.988481e+01 | 2.820080e-01 | 1.551044e+00 |
| 105 | 0.971998 | -5.986427e+01 | -9.444109e-01 | -5.810813e-01 |
| 106 | 1.000000 | -1.030560e-08 | 0.000000e+00 | 0.000000e+00 |
Thus we can see that the results of powerflow match. We can then replace the dataframes of results in the pandapower net.
for table in converted_output_data:
pp_net[table] = converted_output_data[table]
5. Asymmetrical Calculations
For simulating the asymmetric calculation, we shall use the same grid as used in unbalanced_minimal.ipynb tutorial of pandapower.
def pandapower_simple_asym_grid():
net = pp.create_empty_network()
b1 = pp.create_bus(net, 20.0)
b2 = pp.create_bus(net, 0.4)
b3 = pp.create_bus(net, 0.4)
pp.create_ext_grid(net, b1, s_sc_max_mva=1000, rx_max=0.1, x0x_max=1.0, r0x0_max=0.1)
pp.create_transformer_from_parameters(
net,
b1,
b2,
sn_mva=0.63,
vn_hv_kv=20.0,
vn_lv_kv=0.4,
vkr_percent=0.1,
vk_percent=6,
vk0_percent=6,
vkr0_percent=0.78125,
mag0_percent=100,
mag0_rx=0.0,
pfe_kw=0.1,
i0_percent=0.1,
vector_group="Dyn",
shift_degree=150,
si0_hv_partial=0.9,
)
pp.create_line_from_parameters(
net,
b2,
b3,
length_km=0.1,
r0_ohm_per_km=0.0848,
x0_ohm_per_km=0.4649556,
c0_nf_per_km=230.6,
max_i_ka=0.963,
r_ohm_per_km=0.0212,
x_ohm_per_km=0.1162389,
c_nf_per_km=230,
)
pp.create_asymmetric_load(net, b3, p_a_mw=0.25, p_b_mw=0.18, p_c_mw=0.20, type="wye")
return net
Convert to get input data. Run asymmetric powerflow calculation similarly. Then convert the asymmetric PGM output data:
pp_net_3ph = pandapower_simple_asym_grid()
asym_input_data, asym_extra_info = converter.load_input_data(pp_net_3ph)
asym_pgm = PowerGridModel(input_data=asym_input_data)
asym_output_data = asym_pgm.calculate_power_flow(symmetric=False)
converted_asym_output_data = converter.convert(asym_output_data)
2026-03-27 16:55:28 [warning ] Zero sequence parameters given in trafo shall be ignored: vkr0_percent, si0_hv_partial
Add the keys to pandapower net if required
for table in converted_asym_output_data:
pp_net_3ph[table] = converted_asym_output_data[table]
Summary
%%capture cap --no-stderr
from power_grid_model import CalculationType, PowerGridModel
from power_grid_model.validation import assert_valid_input_data
from power_grid_model_io.converters import PandaPowerConverter
output_file = "data/pandapower/example_simple_output.json"
pp_net = pandapower_simple_grid()
converter = PandaPowerConverter()
input_data, extra_info = converter.load_input_data(pp_net)
assert_valid_input_data(input_data, calculation_type=CalculationType.power_flow, symmetric=True)
pgm = PowerGridModel(input_data=input_data)
output_data = pgm.calculate_power_flow()
json_converter = PgmJsonConverter(destination_file=output_file)
json_converter.save(data=output_data, extra_info=extra_info)
converted_output_data = converter.convert(output_data)
for table in converted_output_data:
pp_net[table] = converted_output_data[table]