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:

    1. 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.

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