{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Two formulations of DCOPF" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As suggested in [1], the PTDF formulation is mainly used for ISO/RTO market clearing.\n", "Thus, `DCOPF2` is developed using PTDF formulation for market clearing analysis.\n", "\n", "In this demo, we will compare it with the `DCOPF` using B-theta formulation.\n", "\n", "References:\n", "\n", "1. Y. Chen et al., \"Security-Constrained Unit Commitment for Electricity Market: Modeling, Solution Methods, and Future Challenges,\" in IEEE Transactions on Power Systems, vol. 38, no. 5, pp. 4668-4681, Sept. 2023, doi: 10.1109/TPWRS.2022.3213001." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "\n", "import ams" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "ams.config_logger(stream_level=20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A small-size PJM 5-bus case with ESD1 is used in this example." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Parsing input file \"/Users/jinningwang/work/ams/ams/cases/5bus/pjm5bus_demo.xlsx\"...\n", "Input file parsed in 0.0720 seconds.\n", "Zero Line parameters detected, adjusted to default values: rate_b, rate_c.\n", "All bus type are PQ, adjusted given load and generator connection status.\n", "System set up in 0.0026 seconds.\n" ] } ], "source": [ "sp = ams.load(ams.get_case('5bus/pjm5bus_demo.xlsx'),\n", " setup=True,\n", " no_output=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Whether the dual values are available depends on the solver/method. If you find the dual values are not fully computed, please try other solvers/methods.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we can solve the two routines." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For `DCOPF2`, it is a good practice to build the PTDF matrix first.\n", "\n", "You can also load a calculated PTDF matrix from a file, but AMS\n", "do not check the consistency between the PTDF matrix and the network data." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Building system matrices\n" ] }, { "data": { "text/plain": [ "<7x5 sparse matrix of type ''\n", "\twith 28 stored elements in List of Lists format>" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.mats.build_ptdf()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n", "Evaluating OModel for \n", "Finalizing OModel for \n", " initialized in 0.0392 seconds.\n", " solved as optimal in 0.0192 seconds, converged in 7 iterations with CLARABEL.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF2.run(solver='CLARABEL')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The system energy price is calculated after solving the optimization, using `pie`, an ``ExpressionCalc`` object defined in the routine." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.15705242184924276" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF2.pie.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The congestion price is also calculated, using `pic`, another ``ExpressionCalc`` object defined in the routine." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([-0.07942907, -0.14705242, 0.14294758, 0. , -0.06534746])" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF2.pic.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, we can also run the `DCOPF`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n", "Evaluating OModel for \n", "Finalizing OModel for \n", " initialized in 0.0123 seconds.\n", " solved as optimal in 0.0206 seconds, converged in 8 iterations with CLARABEL.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF.run(solver='CLARABEL')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can compare the Bus results and Line results as follows." ] }, { "cell_type": "code", "execution_count": 57, "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", "
idxpi-2 [$/MW]pi [$/MW]aBus-2 [rad]aBus [rad]
000.0007760.0007760.0239890.023989
110.0001000.0001000.0346680.034668
220.0030000.0030000.0130680.013068
330.0015710.0015710.000000-0.000000
440.0009170.0009170.0228960.022896
\n", "
" ], "text/plain": [ " idx pi-2 [$/MW] pi [$/MW] aBus-2 [rad] aBus [rad]\n", "0 0 0.000776 0.000776 0.023989 0.023989\n", "1 1 0.000100 0.000100 0.034668 0.034668\n", "2 2 0.003000 0.003000 0.013068 0.013068\n", "3 3 0.001571 0.001571 0.000000 -0.000000\n", "4 4 0.000917 0.000917 0.022896 0.022896" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resBus = pd.DataFrame()\n", "resBus['idx'] = sp.Bus.idx.v\n", "resBus['pi-2 [$/MW]'] = sp.DCOPF2.get(src='pi', idx=resBus['idx']) / sp.config.mva\n", "resBus['pi [$/MW]'] = sp.DCOPF.get(src='pi', idx=resBus['idx']) / sp.config.mva\n", "resBus['aBus-2 [rad]'] = sp.DCOPF2.get(src='aBus', idx=resBus['idx'])\n", "resBus['aBus [rad]'] = sp.DCOPF.get(src='aBus', idx=resBus['idx'])\n", "resBus.round(8)" ] }, { "cell_type": "code", "execution_count": 61, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idxulrate_a [MW]plf-2 [MW]plf [MW]mu1-2 [$/MW]mu1 [$/MW]mu2-2 [$/MW]mu2 [$/MW]
0Line_11.0400.0-38.000806-38.0008060.00.00.000000.00000
1Line_21.0200.078.91215478.9121530.00.00.000000.00000
2Line_31.0200.017.08945917.0894590.00.00.000000.00000
3Line_41.0200.0200.000000200.0000000.00.00.003420.00342
4Line_51.0200.043.99838843.9983880.00.00.000000.00000
5Line_61.0240.0-77.089459-77.0894590.00.00.000000.00000
6Line_71.0400.0-38.000806-38.0008060.00.00.000000.00000
\n", "
" ], "text/plain": [ " idx ul rate_a [MW] plf-2 [MW] plf [MW] mu1-2 [$/MW] mu1 [$/MW] \\\n", "0 Line_1 1.0 400.0 -38.000806 -38.000806 0.0 0.0 \n", "1 Line_2 1.0 200.0 78.912154 78.912153 0.0 0.0 \n", "2 Line_3 1.0 200.0 17.089459 17.089459 0.0 0.0 \n", "3 Line_4 1.0 200.0 200.000000 200.000000 0.0 0.0 \n", "4 Line_5 1.0 200.0 43.998388 43.998388 0.0 0.0 \n", "5 Line_6 1.0 240.0 -77.089459 -77.089459 0.0 0.0 \n", "6 Line_7 1.0 400.0 -38.000806 -38.000806 0.0 0.0 \n", "\n", " mu2-2 [$/MW] mu2 [$/MW] \n", "0 0.00000 0.00000 \n", "1 0.00000 0.00000 \n", "2 0.00000 0.00000 \n", "3 0.00342 0.00342 \n", "4 0.00000 0.00000 \n", "5 0.00000 0.00000 \n", "6 0.00000 0.00000 " ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resLine = pd.DataFrame()\n", "resLine['idx'] = sp.Line.idx.v\n", "resLine['ul'] = sp.DCOPF2.get(src='ul', idx=resLine['idx'])\n", "resLine['rate_a [MW]'] = sp.DCOPF2.get(src='rate_a', idx=resLine['idx']) * sp.config.mva\n", "resLine['plf-2 [MW]'] = sp.DCOPF2.get(src='plf', idx=resLine['idx']) * sp.config.mva\n", "resLine['plf [MW]'] = sp.DCOPF.get(src='plf', idx=resLine['idx']) * sp.config.mva\n", "resLine['mu1-2 [$/MW]'] = sp.DCOPF2.get(src='mu1', idx=resLine['idx']) / sp.config.mva\n", "resLine['mu1 [$/MW]'] = sp.DCOPF.get(src='mu1', idx=resLine['idx']) / sp.config.mva\n", "resLine['mu2-2 [$/MW]'] = sp.DCOPF2.get(src='mu2', idx=resLine['idx']) / sp.config.mva\n", "resLine['mu2 [$/MW]'] = sp.DCOPF.get(src='mu2', idx=resLine['idx']) / sp.config.mva\n", "resLine.round(8)" ] } ], "metadata": { "kernelspec": { "display_name": "ams", "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.12.0" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }