This article will walk you through the construction of an extremely simple dispatch engine programmed in GMPL. While my preferred language is Python, GLPK lets us focus exclusively on the equations behind the dispatch engine, enabling us to understand the basics of how dispatch works in the energy market.
So what is a dispatch engine? It is a program that allows us to figure out the optimial volume of power that different power stations should dispatch into the grid. As power is instantly produced and consumed, the total volume of power generated must equal demand at any time. Given that power stations sell different volumes of power at varying prices, the purpose of a dispatch engine is to identify how much each power station should generate to meet demand, while minimizing the total cost to supply that power.
Another important detail is that all power stations are ultimately paid the same price for the power they generate within a given interval – the most expensive power station called on to dispatch in order to satisfy demand effectively becomes the ‘price setter’.
Our dispatch engine is based on the technique of linear optimization – you can find out more background on the technique here if you’re interested.
Install GLPK (GNU Linear Programming Kit) – you can do this with the following command once you have Anaconda installed:
conda install -c conda-forge glpk
We’re going to start by setting out all the parameters for our optimization:
- We’re simulating a single region with a demand target of 150 MW
- We’ll have 3 power stations – 2 coal, and 1 gas
- Coal 1 sells power at $10/MWh and has a max output of 100 MW
- Coal 2 sells power at $20/MWh and has a max output of 100 MW
- Gas 1 sells power at $100/MWh and has a max output of 50 MW
param demand := 150; param coal1_price := 10; param coal2_price := 20; param gas1_price := 100; param coal1_max := 100; param coal2_max := 100; param gas1_max := 50;
Variables are the values we want the program to optimize for us – in the case of our dispatch engine, we want it to identify the optimial volume of power that each station should generate in order to minimise costs. Here we define a variable for each station and tell the program that a station cannot generate less than 0.
var coal1_dispatch >= 0; var coal2_dispatch >= 0; var gas1_dispatch >= 0;
The objective function is the equation we want the program to optimize – in this case we are telling it to minimize the total cost based on the price the generators are selling power at.
minimize obj: coal1_price * coal1_dispatch + coal2_price * coal2_dispatch + gas1_price * gas1_dispatch;
Constraints tell our program the limitations that it has to adhere to. The first three constraints below tell it that power stations cannot exceed their max output, while the forth constraint says that the combined volume dispatched by all three stations has to equal demand (which we set at 150 MW above).
s.t. c1: coal1_dispatch <= coal1_max; s.t. c2: coal2_dispatch <= coal2_max; s.t. c3: gas1_dispatch <= gas1_max; s.t. c4: coal1_dispatch + coal2_dispatch + gas1_dispatch = demand;
Here’s all the code we’ve written in a single block (there’s also a couple of additional instructions at the end telling the program to solve and display the values of the variables we were optimizing).
param demand := 150; param coal1_price := 10; param coal2_price := 20; param gas1_price := 100; param coal1_max := 100; param coal2_max := 100; param gas1_max := 50; var coal1_dispatch >= 0; var coal2_dispatch >= 0; var gas1_dispatch >= 0; minimize obj: coal1_price * coal1_dispatch + coal2_price * coal2_dispatch + gas1_price * gas1_dispatch; s.t. c1: coal1_dispatch <= coal1_max; s.t. c2: coal2_dispatch <= coal2_max; s.t. c3: gas1_dispatch <= gas1_max; s.t. c4: coal1_dispatch + coal2_dispatch + gas1_dispatch = demand; solve; display coal1_dispatch, coal2_dispatch, gas1_dispatch; end;
Save this code as ‘simple.mod’, and you can run it in the terminal (if you have GLPK installed) with the code below:
glpsol --math simple.mod
You should have the following outputs:
coal1_dispatch.val = 100 coal2_dispatch.val = 50 gas1_dispatch.val = 0
This output makes sense to me – Coal 1 was the cheapest station and was dispatched up to its max output of 100 MW. Then Coal 2 was called on to dispatch 50 MW and combined that met the demand of 150 MW. The gas station was not dispatched at all as it was the most expensive station and was not required to meet demand.
In this scenario, Coal 2 (at $20/MWh) is the price setter, so both Coal 1 and Coal 2 will receive this amount for the energy they generated.
You can now rerun the code with different parameters and see how that affects the results you get:
- What happens if you make the price of gas much cheaper?
- What happens if you make the first coal station much bigger?
- What happens if you set demand to be really low (e.g. 50 MW) or really high (350 MW)?
- Can you add in a forth generator?
Why do you think some settings have no feasible solution?
There’s still many more things we can add to this dispatch engine, if there’s interest, I may follow up with some articles running through a more detailed dispatch engine I’ve been developing in Python.