{
"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",
" idx | \n",
" pi-2 [$/MW] | \n",
" pi [$/MW] | \n",
" aBus-2 [rad] | \n",
" aBus [rad] | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" 0 | \n",
" 0.000776 | \n",
" 0.000776 | \n",
" 0.023989 | \n",
" 0.023989 | \n",
"
\n",
" \n",
" | 1 | \n",
" 1 | \n",
" 0.000100 | \n",
" 0.000100 | \n",
" 0.034668 | \n",
" 0.034668 | \n",
"
\n",
" \n",
" | 2 | \n",
" 2 | \n",
" 0.003000 | \n",
" 0.003000 | \n",
" 0.013068 | \n",
" 0.013068 | \n",
"
\n",
" \n",
" | 3 | \n",
" 3 | \n",
" 0.001571 | \n",
" 0.001571 | \n",
" 0.000000 | \n",
" -0.000000 | \n",
"
\n",
" \n",
" | 4 | \n",
" 4 | \n",
" 0.000917 | \n",
" 0.000917 | \n",
" 0.022896 | \n",
" 0.022896 | \n",
"
\n",
" \n",
"
\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",
" idx | \n",
" ul | \n",
" rate_a [MW] | \n",
" plf-2 [MW] | \n",
" plf [MW] | \n",
" mu1-2 [$/MW] | \n",
" mu1 [$/MW] | \n",
" mu2-2 [$/MW] | \n",
" mu2 [$/MW] | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" Line_1 | \n",
" 1.0 | \n",
" 400.0 | \n",
" -38.000806 | \n",
" -38.000806 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.00000 | \n",
" 0.00000 | \n",
"
\n",
" \n",
" | 1 | \n",
" Line_2 | \n",
" 1.0 | \n",
" 200.0 | \n",
" 78.912154 | \n",
" 78.912153 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.00000 | \n",
" 0.00000 | \n",
"
\n",
" \n",
" | 2 | \n",
" Line_3 | \n",
" 1.0 | \n",
" 200.0 | \n",
" 17.089459 | \n",
" 17.089459 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.00000 | \n",
" 0.00000 | \n",
"
\n",
" \n",
" | 3 | \n",
" Line_4 | \n",
" 1.0 | \n",
" 200.0 | \n",
" 200.000000 | \n",
" 200.000000 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.00342 | \n",
" 0.00342 | \n",
"
\n",
" \n",
" | 4 | \n",
" Line_5 | \n",
" 1.0 | \n",
" 200.0 | \n",
" 43.998388 | \n",
" 43.998388 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.00000 | \n",
" 0.00000 | \n",
"
\n",
" \n",
" | 5 | \n",
" Line_6 | \n",
" 1.0 | \n",
" 240.0 | \n",
" -77.089459 | \n",
" -77.089459 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.00000 | \n",
" 0.00000 | \n",
"
\n",
" \n",
" | 6 | \n",
" Line_7 | \n",
" 1.0 | \n",
" 400.0 | \n",
" -38.000806 | \n",
" -38.000806 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.00000 | \n",
" 0.00000 | \n",
"
\n",
" \n",
"
\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
}