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.
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 pandapower as pp
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
# The node data is stored as a numpy structured array in input_data["line"]
display(input_data["line"])
# We can use pandas to display the data in a convenient tabular format
# display(pd.DataFrame(input_data["line"]))
# The original indices are stored in the extra_data dictionary
display({i: extra_info[i] for i in input_data["line"]["id"]})
array([(6, 2, 1, 1, 1, 0.12792, 0.0492, 6.4206e-07, 0., nan, nan, nan, nan, 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})
{6: {'id_reference': {'table': 'line', 'index': 101},
'from_node': 2,
'to_node': 1}}
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["node"]))
id | energized | u_pu | u | u_angle | |
---|---|---|---|---|---|
0 | 0 | 1 | 1.000000 | 109999.999962 | -1.798666e-10 |
1 | 1 | 1 | 0.973746 | 19474.919873 | -5.239008e-01 |
2 | 2 | 1 | 0.973014 | 19460.275746 | -5.237224e-01 |
3 | 3 | 1 | 0.969550 | 29183.446690 | -1.045187e+00 |
4 | 4 | 1 | 0.971998 | 58319.874965 | -1.044829e+00 |
5 | 5 | 1 | 1.000000 | 109999.999962 | -1.798666e-10 |
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': 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.798661 | -3.476701 |
102 | 0.973746 | -30.017299 | 0.000000 | 0.000000 |
103 | 0.973014 | -30.007075 | 2.414572 | 0.231799 |
104 | 0.969519 | -59.886045 | 0.281990 | 1.550945 |
105 | 0.972063 | -59.860441 | -0.944398 | -0.581069 |
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 ie. 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 | 3.026798e-14 | 3.019813e-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.keys():
pp_net[table] = converted_output_data[table]
Summary
%%capture cap --no-stderr
from power_grid_model import PowerGridModel, CalculationType
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.keys():
pp_net[table] = converted_output_data[table]