{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# pandapower Optimal Power Flow\n", "This is an introduction into the usage of the pandapower optimal power flow. It shows how to set the constraints and the cost factors into the pandapower element tables.\n", "\n", "## Example Network\n", "\n", "We use the following four bus example network for this tutorial:\n", "\n", "\n", "\n", "We first create this network in pandapower:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pandapower as pp\n", "import numpy as np\n", "net = pp.create_empty_network()\n", "\n", "#create buses\n", "bus1 = pp.create_bus(net, vn_kv=220.)\n", "bus2 = pp.create_bus(net, vn_kv=110.)\n", "bus3 = pp.create_bus(net, vn_kv=110.)\n", "bus4 = pp.create_bus(net, vn_kv=110.)\n", "\n", "#create 220/110 kV transformer\n", "pp.create_transformer(net, bus1, bus2, std_type=\"100 MVA 220/110 kV\")\n", "\n", "#create 110 kV lines\n", "pp.create_line(net, bus2, bus3, length_km=70., std_type='149-AL1/24-ST1A 110.0')\n", "pp.create_line(net, bus3, bus4, length_km=50., std_type='149-AL1/24-ST1A 110.0')\n", "pp.create_line(net, bus4, bus2, length_km=40., std_type='149-AL1/24-ST1A 110.0')\n", "\n", "#create loads\n", "pp.create_load(net, bus2, p_mw=60, controllable=False)\n", "pp.create_load(net, bus3, p_mw=70, controllable=False)\n", "pp.create_load(net, bus4, p_mw=10, controllable=False)\n", "\n", "#create generators\n", "eg = pp.create_ext_grid(net, bus1, min_p_mw=-1000, max_p_mw=1000)\n", "g0 = pp.create_gen(net, bus3, p_mw=80, min_p_mw=0, max_p_mw=80, vm_pu=1.01, controllable=True)\n", "g1 = pp.create_gen(net, bus4, p_mw=100, min_p_mw=0, max_p_mw=100, vm_pu=1.01, controllable=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loss Minimization\n", "\n", "We specify the same costs for the power at the external grid and all generators to minimize the overall power feed in. This equals an overall loss minimization:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "costeg = pp.create_poly_cost(net, 0, 'ext_grid', cp1_eur_per_mw=10)\n", "costgen1 = pp.create_poly_cost(net, 0, 'gen', cp1_eur_per_mw=10)\n", "costgen2 = pp.create_poly_cost(net, 1, 'gen', cp1_eur_per_mw=10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We run an OPF:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']\n", "hp.pandapower.run - INFO: 'min_vm_pu' is missing in bus table. In OPF these limits are considered as 0.0 pu.\n", "hp.pandapower.run - INFO: 'max_vm_pu' is missing in bus table. In OPF these limits are considered as 2.0 pu.\n" ] } ], "source": [ "pp.runopp(net, delta=1e-16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function runs an Optimal Power Flow using the PYPOWER OPF. To make sure that the PYPOWER OPF converges, we decrease the power tolerance `delta` (the default value is `delta=1e-10`). The power tolerance `delta` is a measure of the extent to which exceeding of minimum and maximum power limits is tolerated. That is, in above case, the limits considered by the OPF for the generators are `min_p_mw - delta` and `max_p_mw + delta` as lower and upper bound respectively on the active power. \n", "\n", "Let's check the results:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
p_mwq_mvar
056.5305841.974564
\n", "
" ], "text/plain": [ " p_mw q_mvar\n", "0 56.530584 1.974564" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_ext_grid" ] }, { "cell_type": "code", "execution_count": 5, "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", "
p_mwq_mvarva_degreevm_pu
071.309255-1.969681-3.7130311.000008
112.303443-1.451180-3.7127351.000010
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 71.309255 -1.969681 -3.713031 1.000008\n", "1 12.303443 -1.451180 -3.712735 1.000010" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since all costs were specified the same, the OPF minimizes overall power generation, which is equal to a loss minimization in the network. The loads at buses 3 and 4 are supplied by generators at the same bus, the load at Bus 2 is provided by a combination of the other generators so that the power transmission leads to minimal losses." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Individual Generator Costs\n", "\n", "Let's now assign individual costs to each generator.\n", "\n", "We assign a cost of 10 ct/kW for the external grid, 15 ct/kw for the generator g0 and 12 ct/kw for generator g1:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "net.poly_cost.cp1_eur_per_mw.at[costeg] = 10\n", "net.poly_cost.cp1_eur_per_mw.at[costgen1] = 15\n", "net.poly_cost.cp1_eur_per_mw.at[costgen2] = 12" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now run an OPF:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']\n", "hp.pandapower.run - INFO: 'min_vm_pu' is missing in bus table. In OPF these limits are considered as 0.0 pu.\n", "hp.pandapower.run - INFO: 'max_vm_pu' is missing in bus table. In OPF these limits are considered as 2.0 pu.\n" ] } ], "source": [ "pp.runopp(net, delta=1e-16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that all active power is provided by the external grid: " ] }, { "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", "
p_mwq_mvar
0144.5591669.193021
\n", "
" ], "text/plain": [ " p_mw q_mvar\n", "0 144.559166 9.193021" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_ext_grid" ] }, { "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", "
p_mwq_mvarva_degreevm_pu
00.0000798.601766-16.4268350.967619
10.00022510.594623-13.4810070.989756
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 0.000079 8.601766 -16.426835 0.967619\n", "1 0.000225 10.594623 -13.481007 0.989756" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This makes sense, because the external grid has the lowest cost of all generators and we did not define any constraints.\n", "\n", "The dispatch costs are given in net.res_cost:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1445.5955448594823" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_cost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Transformer Constraint\n", "\n", "Since all active power comes from the external grid and subsequently flows through the transformer, the transformer is overloaded with a loading of about 145%:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 144.851179\n", "Name: loading_percent, dtype: float64" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_trafo.loading_percent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now limit the transformer loading to 50%:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "net.trafo[\"max_loading_percent\"] = 50" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(the max_loading_percent parameter can also be specified directly when creating the transformer)\n", "and run the OPF:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']\n", "hp.pandapower.run - INFO: 'min_vm_pu' is missing in bus table. In OPF these limits are considered as 0.0 pu.\n", "hp.pandapower.run - INFO: 'max_vm_pu' is missing in bus table. In OPF these limits are considered as 2.0 pu.\n" ] } ], "source": [ "pp.runopp(net, delta=1e-16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that the transformer complies with the maximum loading:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 49.999136\n", "Name: loading_percent, dtype: float64" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_trafo.loading_percent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And power generation is now split between the external grid and generator 1 (which is the second cheapest generation unit):" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
p_mwq_mvar
049.953012-2.147126
\n", "
" ], "text/plain": [ " p_mw q_mvar\n", "0 49.953012 -2.147126" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_ext_grid" ] }, { "cell_type": "code", "execution_count": 16, "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", "
p_mwq_mvarva_degreevm_pu
00.0005982.992989-6.2327100.985230
193.3043173.453173-1.2377841.025709
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 0.000598 2.992989 -6.232710 0.985230\n", "1 93.304317 3.453173 -1.237784 1.025709" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This comes of course with an increase in dispatch costs:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1619.1908981410575" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_cost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Line Loading Constraints" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wen now look at the line loadings:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 22.192863\n", "1 57.476322\n", "2 33.473473\n", "Name: loading_percent, dtype: float64" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_line.loading_percent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and run the OPF with a 50% loading constraint:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']\n", "hp.pandapower.run - INFO: 'min_vm_pu' is missing in bus table. In OPF these limits are considered as 0.0 pu.\n", "hp.pandapower.run - INFO: 'max_vm_pu' is missing in bus table. In OPF these limits are considered as 2.0 pu.\n" ] } ], "source": [ "net.line[\"max_loading_percent\"] = 50\n", "pp.runopp(net, delta=1e-16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the line loading constraint is complied with:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "0 18.905589\n", "1 49.999986\n", "2 30.435895\n", "Name: loading_percent, dtype: float64" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_line.loading_percent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And all generators are involved in supplying the loads:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
p_mwq_mvar
049.787584-4.603451
\n", "
" ], "text/plain": [ " p_mw q_mvar\n", "0 49.787584 -4.603451" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_ext_grid" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "scrolled": true }, "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", "
p_mwq_mvarva_degreevm_pu
09.1364462.430957-5.8154400.993015
183.5926144.853496-1.5113261.028887
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 9.136446 2.430957 -5.815440 0.993015\n", "1 83.592614 4.853496 -1.511326 1.028887" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This of course comes with a once again rising dispatch cost:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1638.0339026783781" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_cost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Voltage Constraints\n", "\n", "Finally, we have a look at the bus voltage:" ] }, { "cell_type": "code", "execution_count": 24, "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", "
vm_puva_degreep_mwq_mvarlam_plam_q
01.0000000.000000-49.7875844.60345110.000000-1.410210e-21
11.006024-3.40883260.0000000.00000013.095255-5.409162e-02
20.993015-5.81544060.863554-2.43095714.9999854.490949e-22
31.028887-1.511326-73.592614-4.85349612.0000071.781411e-21
\n", "
" ], "text/plain": [ " vm_pu va_degree p_mw q_mvar lam_p lam_q\n", "0 1.000000 0.000000 -49.787584 4.603451 10.000000 -1.410210e-21\n", "1 1.006024 -3.408832 60.000000 0.000000 13.095255 -5.409162e-02\n", "2 0.993015 -5.815440 60.863554 -2.430957 14.999985 4.490949e-22\n", "3 1.028887 -1.511326 -73.592614 -4.853496 12.000007 1.781411e-21" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_bus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and constrain it:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']\n" ] } ], "source": [ "net.bus[\"min_vm_pu\"] = 1.0\n", "net.bus[\"max_vm_pu\"] = 1.02\n", "pp.runopp(net, delta=1e-16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that all voltages are within the voltage band:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "scrolled": true }, "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", "
vm_puva_degreep_mwq_mvarlam_plam_q
01.0000000.000000-49.9068393.05061010.000000-2.360898e-22
11.004168-3.42101460.0000000.00000013.126862-2.133418e-02
21.000000-5.97609359.278168-14.85901114.9999971.148963e-21
31.020000-1.366891-71.8634629.17278112.000002-7.089262e-22
\n", "
" ], "text/plain": [ " vm_pu va_degree p_mw q_mvar lam_p lam_q\n", "0 1.000000 0.000000 -49.906839 3.050610 10.000000 -2.360898e-22\n", "1 1.004168 -3.421014 60.000000 0.000000 13.126862 -2.133418e-02\n", "2 1.000000 -5.976093 59.278168 -14.859011 14.999997 1.148963e-21\n", "3 1.020000 -1.366891 -71.863462 9.172781 12.000002 -7.089262e-22" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_bus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And all generators are once again involved in supplying the loads:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
p_mwq_mvar
049.906839-3.05061
\n", "
" ], "text/plain": [ " p_mw q_mvar\n", "0 49.906839 -3.05061" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_ext_grid" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "scrolled": true }, "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", "
p_mwq_mvarva_degreevm_pu
010.72183214.859011-5.9760931.00
181.863462-9.172781-1.3668911.02
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 10.721832 14.859011 -5.976093 1.00\n", "1 81.863462 -9.172781 -1.366891 1.02" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This of course comes once again with rising dispatch costs:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1642.2574101559053" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_cost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## DC OPF\n", "\n", "pandapower also provides the possibility of running a DC Optimal Power Flow:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "pp.rundcopp(net, delta=1e-16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since voltage magnitudes are not included in the DC power flow formulation, voltage constraints cannot be considered in the DC OPF:" ] }, { "cell_type": "code", "execution_count": 31, "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", "
vm_puva_degreep_mwq_mvarlam_plam_q
01.0000008.508637e-24-50.0000003.05061010.0000000.0
11.004168-3.436967e+0060.0000000.00000013.0909090.0
21.000000-5.708566e+0061.488746-14.85901115.0000000.0
31.020000-1.362340e+00-71.4887479.17278112.0000000.0
\n", "
" ], "text/plain": [ " vm_pu va_degree p_mw q_mvar lam_p lam_q\n", "0 1.000000 8.508637e-24 -50.000000 3.050610 10.000000 0.0\n", "1 1.004168 -3.436967e+00 60.000000 0.000000 13.090909 0.0\n", "2 1.000000 -5.708566e+00 61.488746 -14.859011 15.000000 0.0\n", "3 1.020000 -1.362340e+00 -71.488747 9.172781 12.000000 0.0" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_bus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Line and transformer loading limits are however complied with:" ] }, { "cell_type": "code", "execution_count": 32, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
p_from_mwq_from_mvarp_to_mwq_to_mvarpl_mwql_mvari_from_kai_to_kai_kavm_from_puva_from_degreevm_to_puva_to_degreeloading_percent
016.7152330.0-16.7152330.00.00.00.0877320.0877320.0877321.0-3.4369671.0-5.70856618.666430
1-44.7735130.044.7735130.00.00.00.2350000.2350000.2350001.0-5.7085661.0-1.36234050.000000
226.7152330.0-26.7152330.00.00.00.1402190.1402190.1402191.0-1.3623401.0-3.43696729.833747
\n", "
" ], "text/plain": [ " p_from_mw q_from_mvar p_to_mw q_to_mvar pl_mw ql_mvar i_from_ka \\\n", "0 16.715233 0.0 -16.715233 0.0 0.0 0.0 0.087732 \n", "1 -44.773513 0.0 44.773513 0.0 0.0 0.0 0.235000 \n", "2 26.715233 0.0 -26.715233 0.0 0.0 0.0 0.140219 \n", "\n", " i_to_ka i_ka vm_from_pu va_from_degree vm_to_pu va_to_degree \\\n", "0 0.087732 0.087732 1.0 -3.436967 1.0 -5.708566 \n", "1 0.235000 0.235000 1.0 -5.708566 1.0 -1.362340 \n", "2 0.140219 0.140219 1.0 -1.362340 1.0 -3.436967 \n", "\n", " loading_percent \n", "0 18.666430 \n", "1 50.000000 \n", "2 29.833747 " ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_line" ] }, { "cell_type": "code", "execution_count": 33, "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", "
p_hv_mwq_hv_mvarp_lv_mwq_lv_mvarpl_mwql_mvari_hv_kai_lv_kavm_hv_puva_hv_degreevm_lv_puva_lv_degreeloading_percent
050.00.0-50.00.00.00.00.1312160.2624321.08.508637e-241.0-3.43696750.0
\n", "
" ], "text/plain": [ " p_hv_mw q_hv_mvar p_lv_mw q_lv_mvar pl_mw ql_mvar i_hv_ka i_lv_ka \\\n", "0 50.0 0.0 -50.0 0.0 0.0 0.0 0.131216 0.262432 \n", "\n", " vm_hv_pu va_hv_degree vm_lv_pu va_lv_degree loading_percent \n", "0 1.0 8.508637e-24 1.0 -3.436967 50.0 " ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_trafo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As are generator limits:" ] }, { "cell_type": "code", "execution_count": 34, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namebusp_mwvm_pusn_mvamin_q_mvarmax_q_mvarscalingslackin_serviceslack_weighttypecontrollablemin_p_mwmax_p_mwpower_station_trafo
0None280.01.01NaNNaNNaN1.0FalseTrue0.0NoneTrue0.080.0NaN
1None3100.01.01NaNNaNNaN1.0FalseTrue0.0NoneTrue0.0100.0NaN
\n", "
" ], "text/plain": [ " name bus p_mw vm_pu sn_mva min_q_mvar max_q_mvar scaling slack \\\n", "0 None 2 80.0 1.01 NaN NaN NaN 1.0 False \n", "1 None 3 100.0 1.01 NaN NaN NaN 1.0 False \n", "\n", " in_service slack_weight type controllable min_p_mw max_p_mw \\\n", "0 True 0.0 None True 0.0 80.0 \n", "1 True 0.0 None True 0.0 100.0 \n", "\n", " power_station_trafo \n", "0 NaN \n", "1 NaN " ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.gen" ] }, { "cell_type": "code", "execution_count": 35, "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", "
p_mwq_mvarva_degreevm_pu
08.51125414.859011-5.7085661.0
181.488747-9.172781-1.3623401.0
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 8.511254 14.859011 -5.708566 1.0\n", "1 81.488747 -9.172781 -1.362340 1.0" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The cost function is the same for the linearized OPF as for the non-linear one:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1605.5337611101422" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_cost" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Piecewise linear cost functions\n", "\n", "The OPF also offers piecewise linear cost functions. Let us first check the actual cost function setup:" ] }, { "cell_type": "code", "execution_count": 37, "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", "
elementetcp0_eurcp1_eur_per_mwcp2_eur_per_mw2cq0_eurcq1_eur_per_mvarcq2_eur_per_mvar2
00ext_grid0.010.00.00.00.00.0
10gen0.015.00.00.00.00.0
21gen0.012.00.00.00.00.0
\n", "
" ], "text/plain": [ " element et cp0_eur cp1_eur_per_mw cp2_eur_per_mw2 cq0_eur \\\n", "0 0 ext_grid 0.0 10.0 0.0 0.0 \n", "1 0 gen 0.0 15.0 0.0 0.0 \n", "2 1 gen 0.0 12.0 0.0 0.0 \n", "\n", " cq1_eur_per_mvar cq2_eur_per_mvar2 \n", "0 0.0 0.0 \n", "1 0.0 0.0 \n", "2 0.0 0.0 " ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.poly_cost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An element can either have polynomial costs or piecewise linear costs at the same time. So let us first delete the polynomial costs in order to avoid confusion and errors:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "net.poly_cost.drop(net.poly_cost.index.values, inplace=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The results above have been produced with linear polynomial cost functions. Let's try to reproduce the results using piecewise linear cost functions. Costs have to be defined for the whole range of generators and external grids:" ] }, { "cell_type": "code", "execution_count": 39, "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", "
min_p_mwmax_p_mw
00.080.0
10.0100.0
\n", "
" ], "text/plain": [ " min_p_mw max_p_mw\n", "0 0.0 80.0\n", "1 0.0 100.0" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.gen[[\"min_p_mw\", \"max_p_mw\"]]" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
min_p_mwmax_p_mw
0-1000.01000.0
\n", "
" ], "text/plain": [ " min_p_mw max_p_mw\n", "0 -1000.0 1000.0" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.ext_grid[[\"min_p_mw\", \"max_p_mw\"]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define the piecewise linear cost as constant over the whole range to reproduce the polyomial costs defined above:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pp.create_pwl_cost(net, 0, \"gen\", [[0, 80, 15]])\n", "pp.create_pwl_cost(net, 1, \"gen\", [[0, 100, 12]])\n", "pp.create_pwl_cost(net, 0, \"ext_grid\", [[-1000, 1000, 10]])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us check the results from the previous OPF again!" ] }, { "cell_type": "code", "execution_count": 42, "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", "
p_mwq_mvarva_degreevm_pu
08.51125414.859011-5.7085661.0
181.488747-9.172781-1.3623401.0
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 8.511254 14.859011 -5.708566 1.0\n", "1 81.488747 -9.172781 -1.362340 1.0" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1605.5337611101422" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_cost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We run the same OPF now with different cost function setup. We should get the exact same results:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "pp.rundcopp(net, delta=1e-16)" ] }, { "cell_type": "code", "execution_count": 45, "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", "
p_mwq_mvarva_degreevm_pu
08.51125414.859011-5.7085661.0
181.488747-9.172781-1.3623401.0
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 8.511254 14.859011 -5.708566 1.0\n", "1 81.488747 -9.172781 -1.362340 1.0" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1605.5337611101422" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_cost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now lets define real piecewise linear costs for generator 1. We define the costs as 12€/MW up to 70MW and 20€/MW from 70MW to 100MW:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "net.pwl_cost.points.loc[1] = [[0, 70, 12], [70, 100, 20]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And run another OPF:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "pp.rundcopp(net, delta=1e-16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can see that generator 1 only dispatches 70MW, above which generator 0 becomes less expensive and is therefore dispatched:" ] }, { "cell_type": "code", "execution_count": 49, "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", "
p_mwq_mvarva_degreevm_pu
020.00000714.859011-5.2206521.0
169.999997-9.172781-1.6411461.0
\n", "
" ], "text/plain": [ " p_mw q_mvar va_degree vm_pu\n", "0 20.000007 14.859011 -5.220652 1.0\n", "1 69.999997 -9.172781 -1.641146 1.0" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.res_gen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Debugging\n", "\n", "For more information on the status of the OPF solver, set verbose=True:" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "PYPOWER Version 5.1.4, 27-June-2018 -- AC Optimal Power Flow\n", "Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011\n", "Converged!\n", "\n", "Converged in 1.85 seconds\n", "Objective Function Value = 1666.07 $/hr\n", "================================================================================\n", "| PyPower (ppci) System Summary - these are not valid for pandapower DataFrames|\n", "================================================================================\n", "\n", "How many? How much? P (MW) Q (MVAr)\n", "--------------------- ------------------- ------------- -----------------\n", "Buses 4 Total Gen Capacity 1180.0 -3000000000.0 to 3000000000.0\n", "Generators 3 On-line Capacity 1180.0 -3000000000.0 to 3000000000.0\n", "Committed Gens 3 Generation (actual) 141.7 1.0\n", "Loads 3 Load 140.0 0.0\n", " Fixed 3 Fixed 140.0 0.0\n", " Dispatchable 0 Dispatchable 0.0 of 0.0 0.0\n", "Shunts 0 Shunt (inj) 0.0 0.0\n", "Branches 4 Losses (I^2 * Z) 1.65 6.34\n", "Transformers 4 Branch Charging (inj) - 5.4\n", "Inter-ties 0 Total Inter-tie Flow 0.0 0.0\n", "Areas 1\n", "\n", " Minimum Maximum\n", " ------------------------- --------------------------------\n", "Voltage Magnitude 1.000 p.u. @ bus 0 1.020 p.u. @ bus 3 \n", "Voltage Angle -5.39 deg @ bus 2 0.00 deg @ bus 0 \n", "P Losses (I^2*R) - 1.05 MW @ line 2-3\n", "Q Losses (I^2*X) - 3.00 MVAr @ line 0-1\n", "Lambda P 10.00 $/MWh @ bus 0 15.00 $/MWh @ bus 2 \n", "Lambda Q -0.08 $/MWh @ bus 1 0.00 $/MWh @ bus 2 \n", "\n", "================================================================================\n", "| Area Summary |\n", "================================================================================\n", "Area # of # of Gens # of Loads # of # of # of # of\n", " Num Buses Total Online Total Fixed Disp Shunt Brchs Xfmrs Ties\n", "---- ----- ----- ------ ----- ----- ----- ----- ----- ----- -----\n", " 1 4 3 3 3 3 0 0 4 4 0\n", "---- ----- ----- ------ ----- ----- ----- ----- ----- ----- -----\n", "Tot: 4 3 3 3 3 0 0 4 4 0\n", "\n", "Area Total Gen Capacity On-line Gen Capacity Generation\n", " Num MW MVAr MW MVAr MW MVAr\n", "---- ------ ------------------ ------ ------------------ ------ ------\n", " 1 1180.0 -3000000000.0 to 3000000000.0 1180.0 -3000000000.0 to 3000000000.0 141.7 1.0\n", "---- ------ ------------------ ------ ------------------ ------ ------\n", "\n", "Area Disp Load Cap Disp Load Fixed Load Total Load\n", " Num MW MVAr MW MVAr MW MVAr MW MVAr\n", "---- ------ ------ ------ ------ ------ ------ ------ ------\n", " 1 0.0 0.0 0.0 0.0 140.0 0.0 140.0 0.0\n", "---- ------ ------ ------ ------ ------ ------ ------ ------\n", "Tot: 0.0 0.0 0.0 0.0 140.0 0.0 140.0 0.0\n", "\n", "Area Shunt Inj Branch Series Losses Net Export\n", " Num MW MVAr Charging MW MVAr MW MVAr\n", "---- ------ ------ -------- ------ ------ ------ ------\n", " 1 0.0 0.0 5.4 1.65 6.34 0.0 0.0\n", "---- ------ ------ -------- ------ ------ ------ ------\n", "Tot: 0.0 0.0 5.4 1.65 6.34 - -\n", "\n", "================================================================================\n", "| Generator Data |\n", "================================================================================\n", " Gen Bus Status Pg Qg Lambda ($/MVA-hr)\n", " # # (MW) (MVAr) P Q \n", "---- ----- ------ -------- -------- -------- --------\n", " 0 0 1 49.90 -3.17 10.00 -0.00\n", " 1 2 1 21.81 8.70 15.00 0.00\n", " 2 3 1 70.00 -4.56 14.11 -0.00\n", " -------- --------\n", " Total: 141.70 0.97\n", "\n", "================================================================================\n", "| Bus Data |\n", "================================================================================\n", " Bus Voltage Generation Load Lambda($/MVA-hr)\n", " # Mag(pu) Ang(deg) P (MW) Q (MVAr) P (MW) Q (MVAr) P Q \n", "----- ------- -------- -------- -------- -------- -------- ------- -------\n", " 0 1.000 0.000* 49.90 -3.17 - - 10.000 -\n", " 1 1.004 -3.420 - - 60.00 0.00 14.482 -0.082\n", " 2 1.000 -5.387 21.81 8.70 70.00 0.00 15.000 -\n", " 3 1.020 -1.699 70.00 -4.56 10.00 0.00 14.114 -\n", " -------- -------- -------- --------\n", " Total: 141.70 0.97 140.00 0.00\n", "\n", "================================================================================\n", "| Branch Data |\n", "================================================================================\n", "Brnch From To From Bus Injection To Bus Injection Loss (I^2 * Z) \n", " # Bus Bus P (MW) Q (MVAr) P (MW) Q (MVAr) P (MW) Q (MVAr)\n", "----- ----- ----- -------- -------- -------- -------- -------- --------\n", " 0 1 2 12.68 -5.10 -12.48 3.17 0.196 0.41\n", " 1 2 3 -35.71 5.53 36.77 -5.00 1.055 2.23\n", " 2 3 1 23.23 0.44 -22.90 -1.09 0.333 0.70\n", " 3 0 1 49.90 -3.17 -49.78 6.19 0.065 3.00\n", " -------- --------\n", " Total: 1.649 6.34\n", "\n", "================================================================================\n", "| Voltage Constraints |\n", "================================================================================\n", "Bus # Vmin mu Vmin |V| Vmax Vmax mu\n", "----- -------- ----- ----- ----- --------\n", " 0 0.000 1.000 1.000 1.000 392.325\n", " 1 - 1.000 1.004 1.020 - \n", " 2 144.754 1.000 1.000 1.020 - \n", " 3 - 1.000 1.020 1.020 20.418\n", "\n", "================================================================================\n", "| Generation Constraints |\n", "================================================================================\n", " Gen Bus Active Power Limits\n", " # # Pmin mu Pmin Pg Pmax Pmax mu\n", "---- ----- ------- -------- -------- -------- -------\n", " 0 0 - -1000.00 49.90 1000.00 - \n", " 1 2 - -0.00 21.81 80.00 - \n", " 2 3 - -0.00 70.00 100.00 - \n", "\n", "Gen Bus Reactive Power Limits\n", " # # Qmin mu Qmin Qg Qmax Qmax mu\n", "--- --- ------- -------- -------- -------- -------\n", " 0 0 - -1000000000.00 -3.171000000000.00 - \n", " 1 2 - -1000000000.00 8.701000000000.00 - \n", " 2 3 - -1000000000.00 -4.561000000000.00 - \n", "\n", "================================================================================\n", "| Dispatchable Load Constraints |\n", "================================================================================\n", "Gen Bus Active Power Limits\n", " # # Pmin mu Pmin Pg Pmax Pmax mu\n", "--- --- ------- -------- -------- -------- -------\n", "\n", "Gen Bus Reactive Power Limits\n", " # # Qmin mu Qmin Qg Qmax Qmax mu\n", "--- --- ------- -------- -------- -------- -------\n", "\n", "================================================================================\n", "| Branch Flow Constraints |\n", "================================================================================\n", "Brnch From \"From\" End Limit \"To\" End To\n", " # Bus |If| mu |If| |Imax| |It| |It| mu Bus\n", "----- ----- ------- -------- -------- -------- ------- -----\n", " 0 1 - 13.60 44.77 12.88 - 2\n", " 1 2 - 36.14 44.77 36.38 - 3\n", " 2 3 - 22.78 44.77 22.83 - 1\n", " 3 0 4.470 50.00 50.00 49.95 - 1\n" ] } ], "source": [ "pp.runopp(net, verbose=True, delta=1e-16)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "celltoolbar": "Raw Cell Format", "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.13" } }, "nbformat": 4, "nbformat_minor": 1 }