{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# PandaPower conversion\n", "\n", "This example illustrates conversion from PandaPower to power-grid-model input data. \n", "We can then calculate power-flow with it or convert to a different formats like PGM JSON.\n", "\n", "**NOTE:** To run this example, the optional `examples` dependencies are required:\n", "\n", "```sh\n", "pip install .[examples]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Load the PandaPower Data\n", "\n", "For this example we will construct a minimal pandapower network.\n", "\n", "\n", " \n", " (ext_grid #1) shunt - [104] - trafo_3w - [105] - (sym_gen + asym_gen + asym_load + ward + motor)\n", " | |\n", " [101] ---trafo- [102] ------------- [103]\n", " | |\n", " -/- (load #31)\n", " |\n", " [106]" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import warnings\n", "\n", "import pandapower as pp\n", "\n", "warnings.filterwarnings(\"ignore\", module=\"pandapower\", category=FutureWarning) # Hide warnings related to pandas\n", "\n", "\n", "def pandapower_simple_grid():\n", " net = pp.create_empty_network(f_hz=50)\n", " pp.create_bus(net, index=101, vn_kv=110)\n", " pp.create_bus(net, index=102, vn_kv=20)\n", " pp.create_bus(net, index=103, vn_kv=20)\n", " pp.create_bus(net, index=104, vn_kv=30.1)\n", " pp.create_bus(net, index=105, vn_kv=60)\n", " pp.create_bus(net, index=106, vn_kv=110)\n", " 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)\n", " pp.create_transformer_from_parameters(\n", " net,\n", " index=101,\n", " hv_bus=101,\n", " lv_bus=102,\n", " i0_percent=3.0,\n", " pfe_kw=11.6,\n", " vkr_percent=10.22,\n", " sn_mva=40,\n", " vn_lv_kv=20.0,\n", " vn_hv_kv=110.0,\n", " vk_percent=17.8,\n", " vector_group=\"Dyn\",\n", " shift_degree=30,\n", " tap_side=\"hv\",\n", " tap_pos=2,\n", " tap_min=-1,\n", " tap_max=3,\n", " tap_step_percent=2,\n", " tap_neutral=1,\n", " parallel=2,\n", " )\n", " pp.create_line(\n", " net, index=101, from_bus=103, to_bus=102, length_km=1.23, parallel=2, df=0.2, std_type=\"NAYY 4x150 SE\"\n", " )\n", " pp.create_load(\n", " 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\n", " )\n", " pp.create_switch(net, index=101, et=\"l\", bus=103, element=101, closed=True)\n", " pp.create_switch(net, index=3021, et=\"b\", bus=101, element=106, closed=True)\n", " pp.create_switch(net, index=321, et=\"t\", bus=101, element=101, closed=True)\n", " pp.create_shunt(net, index=1201, in_service=True, bus=104, p_mw=0.1, q_mvar=0.55, step=3)\n", " pp.create_sgen(net, index=31, bus=105, p_mw=1.21, q_mvar=0.81)\n", " pp.create_asymmetric_sgen(\n", " 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\n", " )\n", " pp.create_asymmetric_load(\n", " 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\n", " )\n", " pp.create_ward(net, index=34, bus=105, ps_mw=0.1, qs_mvar=0.1, pz_mw=0.1, qz_mvar=0.1)\n", " pp.create_motor(\n", " net, bus=105, index=12, pn_mech_mw=0.1, cos_phi=0.9, loading_percent=80, efficiency_percent=90, scaling=0.8\n", " )\n", " pp.create_transformer3w_from_parameters(\n", " net,\n", " index=102,\n", " hv_bus=103,\n", " mv_bus=105,\n", " lv_bus=104,\n", " in_service=True,\n", " vn_hv_kv=20.0,\n", " vn_mv_kv=60.0,\n", " vn_lv_kv=30.1,\n", " sn_hv_mva=40,\n", " sn_mv_mva=100,\n", " sn_lv_mva=50,\n", " vk_hv_percent=10,\n", " vk_mv_percent=11,\n", " vk_lv_percent=12,\n", " vkr_hv_percent=1,\n", " vkr_mv_percent=2,\n", " vkr_lv_percent=4,\n", " i0_percent=0.1,\n", " pfe_kw=10,\n", " vector_group=\"Dyny\",\n", " shift_mv_degree=30,\n", " shift_lv_degree=30,\n", " tap_side=\"lv\",\n", " tap_pos=2,\n", " tap_min=1,\n", " tap_max=3,\n", " tap_step_percent=3,\n", " tap_neutral=2,\n", " )\n", " return net" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "Then use `load_input_data()` to load the data and convert it to power-grid-model data.\n", "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`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%%capture cap --no-stderr\n", "from power_grid_model_io.converters import PandaPowerConverter\n", "\n", "pp_net = pandapower_simple_grid()\n", "converter = PandaPowerConverter()\n", "input_data, extra_info = converter.load_input_data(pp_net)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's investigate the data we have converted, for one of the components: `lines`" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([(6, 2, 1, 1, 1, 0.12792, 0.0492, 6.4206e-07, 0., nan, nan, 0., 0., 108.)],\n", " dtype={'names': [id, from_node, to_node, from_status, to_status, r1, x1, c1, tan1, r0, x0, c0, tan0, i_n], 'formats': ['\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idenergizedu_puuu_anglepq
0011.000000109999.999962-1.798666e-101.798666e+063.476629e+06
1110.97374619474.919873-5.239008e-015.817753e-07-1.649725e-08
2210.97301419460.275746-5.237224e-01-2.414573e+06-2.317990e+05
3310.96955029183.446690-1.045187e+002.765216e-08-4.767335e-08
4410.97199858319.874965-1.044829e+009.444109e+055.810813e+05
5511.000000109999.999962-1.798666e-100.000000e+00-0.000000e+00
\n", "" ], "text/plain": [ " id energized u_pu u u_angle p \\\n", "0 0 1 1.000000 109999.999962 -1.798666e-10 1.798666e+06 \n", "1 1 1 0.973746 19474.919873 -5.239008e-01 5.817753e-07 \n", "2 2 1 0.973014 19460.275746 -5.237224e-01 -2.414573e+06 \n", "3 3 1 0.969550 29183.446690 -1.045187e+00 2.765216e-08 \n", "4 4 1 0.971998 58319.874965 -1.044829e+00 9.444109e+05 \n", "5 5 1 1.000000 109999.999962 -1.798666e-10 0.000000e+00 \n", "\n", " q \n", "0 3.476629e+06 \n", "1 -1.649725e-08 \n", "2 -2.317990e+05 \n", "3 -4.767335e-08 \n", "4 5.810813e+05 \n", "5 -0.000000e+00 " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from power_grid_model import PowerGridModel\n", "\n", "pgm = PowerGridModel(input_data=input_data)\n", "output_data = pgm.calculate_power_flow()\n", "\n", "display(pd.DataFrame(output_data[ComponentType.node]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Cross referencing objects\n", "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:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PGM object #4: {'table': 'bus', 'index': np.int64(105)}\n", "Trafo with index=101: 12\n" ] } ], "source": [ "print(\"PGM object #4:\", converter.lookup_id(4))\n", "\n", "print(\"Trafo with index=101:\", converter.get_id(\"trafo\", 101))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Saving the data as a JSON file\n", "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." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from power_grid_model_io.converters import PgmJsonConverter\n", "\n", "input_file = \"data/pandapower/example_simple_input.json\"\n", "output_file = \"data/pandapower/example_simple_output.json\"\n", "\n", "PgmJsonConverter(destination_file=input_file).save(data=input_data, extra_info=extra_info)\n", "PgmJsonConverter(destination_file=output_file).save(data=output_data, extra_info=extra_info)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Converting output data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we convert the output data, lets run the powerflow in pandapower so we can compare results for demostration purpose" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
vm_puva_degreep_mwq_mvar
1011.0000000.000000-1.877113-3.640600
1020.993171-30.0161810.0000000.000000
1030.992419-30.0055632.4758140.237678
1040.988794-59.8877010.2933141.613227
1050.991372-59.864589-0.940607-0.577278
1061.0000000.0000000.0000000.000000
\n", "
" ], "text/plain": [ " vm_pu va_degree p_mw q_mvar\n", "101 1.000000 0.000000 -1.877113 -3.640600\n", "102 0.993171 -30.016181 0.000000 0.000000\n", "103 0.992419 -30.005563 2.475814 0.237678\n", "104 0.988794 -59.887701 0.293314 1.613227\n", "105 0.991372 -59.864589 -0.940607 -0.577278\n", "106 1.000000 0.000000 0.000000 0.000000" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pp.runpp(pp_net, trafo_model=\"pi\", trafo_loading=\"power\", calculate_voltage_angles=True, numba=False)\n", "display(pp_net.res_bus)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
vm_puva_degreep_mwq_mvar
1011.000000-1.030560e-08-1.798666e+00-3.476629e+00
1020.973746-3.001731e+01-2.926681e-13-5.341135e-13
1030.973014-3.000708e+012.414573e+002.317990e-01
1040.969550-5.988481e+012.820080e-011.551044e+00
1050.971998-5.986427e+01-9.444109e-01-5.810813e-01
1061.000000-1.030560e-080.000000e+000.000000e+00
\n", "
" ], "text/plain": [ " vm_pu va_degree p_mw q_mvar\n", "101 1.000000 -1.030560e-08 -1.798666e+00 -3.476629e+00\n", "102 0.973746 -3.001731e+01 -2.926681e-13 -5.341135e-13\n", "103 0.973014 -3.000708e+01 2.414573e+00 2.317990e-01\n", "104 0.969550 -5.988481e+01 2.820080e-01 1.551044e+00\n", "105 0.971998 -5.986427e+01 -9.444109e-01 -5.810813e-01\n", "106 1.000000 -1.030560e-08 0.000000e+00 0.000000e+00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "converted_output_data = converter.convert(output_data)\n", "\n", "display(converted_output_data[\"res_bus\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thus we can see that the results of powerflow match. We can then replace the dataframes of results in the pandapower net." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "for table in converted_output_data:\n", " pp_net[table] = converted_output_data[table]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Asymmetrical Calculations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For simulating the asymmetric calculation, we shall use the same grid as used in `unbalanced_minimal.ipynb` tutorial of pandapower." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def pandapower_simple_asym_grid():\n", " net = pp.create_empty_network()\n", " b1 = pp.create_bus(net, 20.0)\n", " b2 = pp.create_bus(net, 0.4)\n", " b3 = pp.create_bus(net, 0.4)\n", " pp.create_ext_grid(net, b1, s_sc_max_mva=1000, rx_max=0.1, x0x_max=1.0, r0x0_max=0.1)\n", " pp.create_transformer_from_parameters(\n", " net,\n", " b1,\n", " b2,\n", " sn_mva=0.63,\n", " vn_hv_kv=20.0,\n", " vn_lv_kv=0.4,\n", " vkr_percent=0.1,\n", " vk_percent=6,\n", " vk0_percent=6,\n", " vkr0_percent=0.78125,\n", " mag0_percent=100,\n", " mag0_rx=0.0,\n", " pfe_kw=0.1,\n", " i0_percent=0.1,\n", " vector_group=\"Dyn\",\n", " shift_degree=150,\n", " si0_hv_partial=0.9,\n", " )\n", " pp.create_line_from_parameters(\n", " net,\n", " b2,\n", " b3,\n", " length_km=0.1,\n", " r0_ohm_per_km=0.0848,\n", " x0_ohm_per_km=0.4649556,\n", " c0_nf_per_km=230.6,\n", " max_i_ka=0.963,\n", " r_ohm_per_km=0.0212,\n", " x_ohm_per_km=0.1162389,\n", " c_nf_per_km=230,\n", " )\n", " pp.create_asymmetric_load(net, b3, p_a_mw=0.25, p_b_mw=0.18, p_c_mw=0.20, type=\"wye\")\n", " return net" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convert to get input data. Run asymmetric powerflow calculation similarly. Then convert the asymmetric PGM output data:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2026-03-27 16:55:28 [warning ] Zero sequence parameters given in trafo shall be ignored: vkr0_percent, si0_hv_partial\n" ] } ], "source": [ "pp_net_3ph = pandapower_simple_asym_grid()\n", "asym_input_data, asym_extra_info = converter.load_input_data(pp_net_3ph)\n", "asym_pgm = PowerGridModel(input_data=asym_input_data)\n", "asym_output_data = asym_pgm.calculate_power_flow(symmetric=False)\n", "converted_asym_output_data = converter.convert(asym_output_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Add the keys to pandapower net if required" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "for table in converted_asym_output_data:\n", " pp_net_3ph[table] = converted_asym_output_data[table]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "%%capture cap --no-stderr\n", "\n", "from power_grid_model import CalculationType, PowerGridModel\n", "from power_grid_model.validation import assert_valid_input_data\n", "\n", "from power_grid_model_io.converters import PandaPowerConverter\n", "\n", "output_file = \"data/pandapower/example_simple_output.json\"\n", "\n", "pp_net = pandapower_simple_grid()\n", "converter = PandaPowerConverter()\n", "input_data, extra_info = converter.load_input_data(pp_net)\n", "assert_valid_input_data(input_data, calculation_type=CalculationType.power_flow, symmetric=True)\n", "pgm = PowerGridModel(input_data=input_data)\n", "output_data = pgm.calculate_power_flow()\n", "json_converter = PgmJsonConverter(destination_file=output_file)\n", "json_converter.save(data=output_data, extra_info=extra_info)\n", "converted_output_data = converter.convert(output_data)\n", "for table in converted_output_data:\n", " pp_net[table] = converted_output_data[table]" ] } ], "metadata": { "kernelspec": { "display_name": "power-grid-model-io", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.2" } }, "nbformat": 4, "nbformat_minor": 2 }