Dispatch with Energy Storage#
In this case, we will show the usage of energy storage included dispatch.
In AMS, ESD1 is a dispatch model for energy storage, which has a corresponding dynamic model ESD1 in ANDES.
[1]:
import pandas as pd
import ams
import datetime
[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.1181 seconds.
Zero line rates detacted in rate_b, rate_c, adjusted to 999.
System set up in 0.0028 seconds.
The model information can be inspected as follow.
[4]:
sp.ESD1.as_df()
[4]:
| idx | u | name | bus | gen | Sn | gammap | gammaq | SOCmin | SOCmax | SOCinit | En | EtaC | EtaD | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| uid | ||||||||||||||
| 0 | ESD1_1 | 1.0 | ESD1_1 | 1 | PV_2 | 100.0 | 1.0 | 1.0 | 0.0 | 1.0 | 0.8 | 100.0 | 1.0 | 1.0 |
RTEDES extends RTED to include energy storage.
Note that mixed integer linear programming (MILP) requires capable solvers such as Gurobi or CPLEX. They might require extra installation and have their own license.
The example here only aims to show the usage of RTEDES. More details can be found at CVXPY - Choosing a solver.
[5]:
sp.RTEDES.run()
Building system matrices
Parsing OModel for <RTEDES>
Evaluating OModel for <RTEDES>
Finalizing OModel for <RTEDES>
<RTEDES> initialized in 0.0280 seconds.
Set parameter Username
Set parameter LicenseID to value 2617183
Academic license - for non-commercial use only - expires 2026-02-02
<RTEDES> solved as optimal in 0.0398 seconds, converged in 6 iterations with GUROBI.
[5]:
True
Note that, in RTED, the time interval is 5/60 [H] by default, and the dispatch model has been adjusted accordingly.
[6]:
RTEDESres = pd.DataFrame()
items = [sp.RTEDES.uce, sp.RTEDES.ude,
sp.RTEDES.pce, sp.RTEDES.pde,
sp.RTEDES.SOC, sp.RTEDES.SOCinit]
RTEDESres['Var'] = [item.name for item in items]
RTEDESres['info'] = [item.info for item in items]
RTEDESres['Value'] = [item.v.round(4) for item in items]
RTEDESres
[6]:
| Var | info | Value | |
|---|---|---|---|
| 0 | uce | ESD1 charging decision | [0.0] |
| 1 | ude | ESD1 discharging decision | [1.0] |
| 2 | pce | ESD1 charging power | [0.0] |
| 3 | pde | ESD1 discharging power | [0.0] |
| 4 | SOC | ESD1 State of Charge | [0.8] |
| 5 | SOCinit | Initial SOC | [0.8] |
Similarly, multi-period dispatch EDES and UCES are also available. They have 1 [H] time interval by default.
[7]:
sp.EDES.config.t
[7]:
1
[8]:
sp.EDES.run()
Parsing OModel for <EDES>
Evaluating OModel for <EDES>
Finalizing OModel for <EDES>
<EDES> initialized in 0.0314 seconds.
<EDES> solved as optimal in 0.0443 seconds, converged in 8 iterations with GUROBI.
[8]:
True
[9]:
EDESres = pd.DataFrame()
items = [sp.EDES.uce, sp.EDES.ude,
sp.EDES.pce, sp.EDES.pde,
sp.EDES.SOC, sp.EDES.SOCinit]
EDESres['Var'] = [item.name for item in items]
EDESres['info'] = [item.info for item in items]
EDESres['Value'] = [item.v.round(4) for item in items]
EDESres
[9]:
| Var | info | Value | |
|---|---|---|---|
| 0 | uce | ESD1 charging decision | [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... |
| 1 | ude | ESD1 discharging decision | [[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,... |
| 2 | pce | ESD1 charging power | [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... |
| 3 | pde | ESD1 discharging power | [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... |
| 4 | SOC | ESD1 State of Charge | [[0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8,... |
| 5 | SOCinit | Initial SOC | [0.8] |
[10]:
sp.UCES.run()
All generators are online at initial, make initial guess for commitment.
As initial commitment guess, turn off StaticGen: PV_1
Parsing OModel for <UCES>
Evaluating OModel for <UCES>
Finalizing OModel for <UCES>
<UCES> initialized in 0.0354 seconds.
<UCES> solved as optimal in 0.0592 seconds, converged in 5 iterations with GUROBI.
[10]:
True
[11]:
UCESres = pd.DataFrame()
items = [sp.UCES.uce, sp.UCES.ude,
sp.UCES.pce, sp.UCES.pde,
sp.UCES.SOC, sp.UCES.SOCinit]
UCESres['Var'] = [item.name for item in items]
UCESres['info'] = [item.info for item in items]
UCESres['Value'] = [item.v.round(4) for item in items]
UCESres
[11]:
| Var | info | Value | |
|---|---|---|---|
| 0 | uce | ESD1 charging decision | [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... |
| 1 | ude | ESD1 discharging decision | [[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,... |
| 2 | pce | ESD1 charging power | [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... |
| 3 | pde | ESD1 discharging power | [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... |
| 4 | SOC | ESD1 State of Charge | [[0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8,... |
| 5 | SOCinit | Initial SOC | [0.8] |