Battery charging and discharging planning
Create and activate a Julia environment.
This project formulates battery charging and discharging as a mixed-integer linear programming (MILP) problem. Given an electricity market with known demand and known generation from non-battery sources over a planning horizon, a group of batteries is scheduled to charge when the market is in over-supply and to discharge when the market is in shortage. Other generation (such as renewable power generation) sources first meet the demand directly; any surplus is routed to the over-supply grid, where batteries may charge from it. When generation falls short, batteries discharge into the shortage grid to help serve the remaining demand. The objective is to minimize the total unserved electricity demand across the horizon.
%%{init: {'flowchart': {'curve': 'basis'}}}%%
flowchart LR
ES["Electricity supply<br/>(other sources)"]
OSM(Over-supply <br/>grid)
SM(Shortage <br/>grid)
ED[Electricity<br/>demand]
BAT[Batteries]
ES --> OSM
SM --> ED
OSM <-.->|"Supply and demand changes"| SM
OSM -.->|Charge| BAT
BAT -.->|Discharge| SM
We have
The electricity demand in each time slot (per hour) is known. Besides the batteries, other sources of power generation in each time slot are known as well. The demand minus all other sources' supply is the amount we need to serve. Denote this amount as
For each battery
- The maximum electricity capacity of each battery is
$C_{i}$ . - The charging efficiency is
$E_{i}$ , the ratio of the amount of electricity added into the battery over the amount of charged electricity. - The maximum charging power is
$I_{i}$ . - The maximum discharging power is
$O_{i}$ .
By default, all the variables are non-negative.
Let
Let
Let
Let
Let
Let
We penalize the total amount of unserved electricity.
The unserved electricity is lower bounded by the gap between discharging amount and demand. Over-generation is not penalized.
The charging deducts the discharging amount is equal to the difference of remained electricity in the battery between the current and the previous hour.
Specifically,
If the battery is not in the charging status, the amount of charging must be 0. If the battery is in the charging status, the amount can be as large as the maximum charging power.
A similar constraint is applied to discharging.
The battery cannot charge and discharge at the same time.
If the battery is fully charged, it cannot charge.
Similarly, if the battery is empty, it cannot discharge.
If the electricity produced by other sources cannot fulfill the demand, all batteries cannot charge.
The Australian Energy Market Operator (AEMO) runs the National Electricity Market (NEM), which links the eastern and southern states of Australia through a shared high-voltage transmission grid. AEMO dispatches generators every 5 minutes to match demand at the lowest offered price, and publishes the resulting data, e.g. regional demand, per-unit SCADA generation, and next-day actual generation (non-scheduled dispatchable units).
South Australia is a useful slice of this data, because the region has a high share of wind and rooftop solar, a small number of large grid-scale batteries (such as the Honesdale Power Reserve, Torrens Island BESS, and Lake Bonney BESS), and a handful of gas-fired stations as backup. Each battery appears in AEMO data as one or more dispatchable units (DUIDs) with registered charge or discharge capacity and a storage size in MWh, which map directly onto the model parameters
The scripts under get_data/ fetch these feeds and load them into a local SQLite database, so the MILP can be solved on a realistic, recent slice of the South Australian grid.
To collect the data, run the following command in terminal.
julia get_data/init_sa_battery.jl
julia get_data/fetch_aemo_dispatched_scada.jl
julia get_data/fetch_aemo_du_detail_summary.jl
julia get_data/fetch_aemo_hist_demand.jl
julia get_data/fetch_aemo_next_day_gen.jl
Check the data integrity by run the following command in terminal.
julia get_data/south_australia_check_integrity.jl
The terminal will output whether the data has missing value and what is the missed time range. Rerun the corresponding script or manually input data into the database to fill missing data. The data integrity check doesn't have a compulsory passing condition, because the program will fill missing values by statistical methods, instead of breaking down.
Note
At the moment, re-running fetch_aemo_dispatched_scada.jl will check existed data in database and fetch new data incrementally. Other scripts can only fetch all the data and update the whole table.
To create a dataset, run the following command in terminal.
julia get_data/south_australia.jl
In the web page:
- Config specification of the battery group. Save batteries if changed.
- Select start and end time of electricity load time series (supply and demand) to preview the line plot.
- Export the dataset containing battery and load to the given file path.
Let the saved dataset file path is $dataset.
Assume the file path to save the solution in format of JLD2 is $solution; the file path to save the visualization report in format of HTML is $report (all can be customized).
To solve the problem and generate the report, run the following command in terminal.
julia solve.jl --input_path $dataset --output_path $solution
julia visualization.jl --input_path $solution --output_path $report
solve.jl has additional arguments:
| Variable | Data type | Required? | Description |
|---|---|---|---|
--timeout |
Float64 | No | Maximum time for the solver to solve the optimization problem, in unit of seconds. |
After solving the model, an HTML report is saved at $report.
The report includes the following tabs.
Status and value of objective function and variables.
Charging and discharging history of each battery. Read segment means discharging; green segment means charging; gray segment means neither charging nor discharging.
The maximum value of Y-axis always the energy capacity of battery.
The X-axis value means the end time of each slot. (The same across all time series plots.)
The power of inputting or outputting electricity of each battery.
The positive part of Y-axis is output (discharging) power, and the negative part is input (charging) power.
The gray bar is the amount of electricity demand. The positive value means other supply resources cannot fulfill the demand of the electricity market; the negative value means redundant electricity is available in the electricity market and batteries owner can use these energy to charge batteries. The stacked bars of batteries are the amount of energy taken or served in each period.
The following table explains how to compare the height of bars.
| Stacked height of battery bars ______ the height (absolute value in either direction) of gray bar | Positive gray bar | Negative gray bar |
|---|---|---|
| equal to | All additional demand are served by batteries | All redundant supply are recycled by batteries |
| is shorter than | Outage | Grid waste redundant energy |
| is longer than | Batteries waste redundant energy | (Infeasible) |







