Two formulations of DCOPF#
As suggested in [1], the PTDF formulation is mainly used for ISO/RTO market clearing. Thus, DCOPF2 is developed using PTDF formulation for market clearing analysis.
In this demo, we will compare it with the DCOPF using B-theta formulation.
References:
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.
[1]:
import pandas as pd
import ams
[2]:
ams.config_logger(stream_level=20)
A small-size PJM 5-bus case with ESD1 is used in this example.
[3]:
sp = ams.load(ams.get_case('5bus/pjm5bus_demo.xlsx'),
setup=True,
no_output=True)
Parsing input file "/Users/jinningwang/work/ams/ams/cases/5bus/pjm5bus_demo.xlsx"...
Input file parsed in 0.0720 seconds.
Zero Line parameters detected, adjusted to default values: rate_b, rate_c.
All bus type are PQ, adjusted given load and generator connection status.
System set up in 0.0026 seconds.
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.
Next, we can solve the two routines.
For DCOPF2, it is a good practice to build the PTDF matrix first.
You can also load a calculated PTDF matrix from a file, but AMS do not check the consistency between the PTDF matrix and the network data.
[4]:
sp.mats.build_ptdf()
Building system matrices
[4]:
<7x5 sparse matrix of type '<class 'numpy.float64'>'
with 28 stored elements in List of Lists format>
[5]:
sp.DCOPF2.run(solver='CLARABEL')
Parsing OModel for <DCOPF2>
Evaluating OModel for <DCOPF2>
Finalizing OModel for <DCOPF2>
<DCOPF2> initialized in 0.0392 seconds.
<DCOPF2> solved as optimal in 0.0192 seconds, converged in 7 iterations with CLARABEL.
[5]:
True
The system energy price is calculated after solving the optimization, using pie, an ExpressionCalc object defined in the routine.
[36]:
sp.DCOPF2.pie.v
[36]:
0.15705242184924276
The congestion price is also calculated, using pic, another ExpressionCalc object defined in the routine.
[37]:
sp.DCOPF2.pic.v
[37]:
array([-0.07942907, -0.14705242, 0.14294758, 0. , -0.06534746])
Then, we can also run the DCOPF.
[6]:
sp.DCOPF.run(solver='CLARABEL')
Parsing OModel for <DCOPF>
Evaluating OModel for <DCOPF>
Finalizing OModel for <DCOPF>
<DCOPF> initialized in 0.0123 seconds.
<DCOPF> solved as optimal in 0.0206 seconds, converged in 8 iterations with CLARABEL.
[6]:
True
We can compare the Bus results and Line results as follows.
[57]:
resBus = pd.DataFrame()
resBus['idx'] = sp.Bus.idx.v
resBus['pi-2 [$/MW]'] = sp.DCOPF2.get(src='pi', idx=resBus['idx']) / sp.config.mva
resBus['pi [$/MW]'] = sp.DCOPF.get(src='pi', idx=resBus['idx']) / sp.config.mva
resBus['aBus-2 [rad]'] = sp.DCOPF2.get(src='aBus', idx=resBus['idx'])
resBus['aBus [rad]'] = sp.DCOPF.get(src='aBus', idx=resBus['idx'])
resBus.round(8)
[57]:
| idx | pi-2 [$/MW] | pi [$/MW] | aBus-2 [rad] | aBus [rad] | |
|---|---|---|---|---|---|
| 0 | 0 | 0.000776 | 0.000776 | 0.023989 | 0.023989 |
| 1 | 1 | 0.000100 | 0.000100 | 0.034668 | 0.034668 |
| 2 | 2 | 0.003000 | 0.003000 | 0.013068 | 0.013068 |
| 3 | 3 | 0.001571 | 0.001571 | 0.000000 | -0.000000 |
| 4 | 4 | 0.000917 | 0.000917 | 0.022896 | 0.022896 |
[61]:
resLine = pd.DataFrame()
resLine['idx'] = sp.Line.idx.v
resLine['ul'] = sp.DCOPF2.get(src='ul', idx=resLine['idx'])
resLine['rate_a [MW]'] = sp.DCOPF2.get(src='rate_a', idx=resLine['idx']) * sp.config.mva
resLine['plf-2 [MW]'] = sp.DCOPF2.get(src='plf', idx=resLine['idx']) * sp.config.mva
resLine['plf [MW]'] = sp.DCOPF.get(src='plf', idx=resLine['idx']) * sp.config.mva
resLine['mu1-2 [$/MW]'] = sp.DCOPF2.get(src='mu1', idx=resLine['idx']) / sp.config.mva
resLine['mu1 [$/MW]'] = sp.DCOPF.get(src='mu1', idx=resLine['idx']) / sp.config.mva
resLine['mu2-2 [$/MW]'] = sp.DCOPF2.get(src='mu2', idx=resLine['idx']) / sp.config.mva
resLine['mu2 [$/MW]'] = sp.DCOPF.get(src='mu2', idx=resLine['idx']) / sp.config.mva
resLine.round(8)
[61]:
| idx | ul | rate_a [MW] | plf-2 [MW] | plf [MW] | mu1-2 [$/MW] | mu1 [$/MW] | mu2-2 [$/MW] | mu2 [$/MW] | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | Line_1 | 1.0 | 400.0 | -38.000806 | -38.000806 | 0.0 | 0.0 | 0.00000 | 0.00000 |
| 1 | Line_2 | 1.0 | 200.0 | 78.912154 | 78.912153 | 0.0 | 0.0 | 0.00000 | 0.00000 |
| 2 | Line_3 | 1.0 | 200.0 | 17.089459 | 17.089459 | 0.0 | 0.0 | 0.00000 | 0.00000 |
| 3 | Line_4 | 1.0 | 200.0 | 200.000000 | 200.000000 | 0.0 | 0.0 | 0.00342 | 0.00342 |
| 4 | Line_5 | 1.0 | 200.0 | 43.998388 | 43.998388 | 0.0 | 0.0 | 0.00000 | 0.00000 |
| 5 | Line_6 | 1.0 | 240.0 | -77.089459 | -77.089459 | 0.0 | 0.0 | 0.00000 | 0.00000 |
| 6 | Line_7 | 1.0 | 400.0 | -38.000806 | -38.000806 | 0.0 | 0.0 | 0.00000 | 0.00000 |