# Unit Commitment for Electrical Power Generation¶

Description: This notebook illustrates the power generation problem using AMPL. The original version featured the Gurobi solver. By default, this notebook uses the HiGHS and CBC solvers.

Major electric power companies around the world utilize mathematical optimization to manage the flow of energy across their electrical grids. In this example, you’ll discover the power of mathematical optimization in addressing a common energy industry problem: unit commitment for electrical power generation. We’ll show you how to figure out the optimal set of power stations to turn on in order to satisfy anticipated power demand over a 24-hour time horizon.

This model is example 15 from the fifth edition of Model Building in Mathematical Programming by H. Paul Williams on pages 270 – 271 and 325 – 326.

This example is at the intermediate level, where we assume that you know Python and the AMPL’s Python API and that you have some knowledge of building mathematical optimization models.

Tags: amplpy, energy, power-generation, unit-commitment

Notebook author: Gyorgy Matyasfalvi <gyorgy@ampl.com>

References:

1. Electrical power generation 1 notebook

2. H. Paul Williams, Model Building in Mathematical Programming, fifth edition.

# Install dependencies
%pip install -q amplpy pandas

# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook

ampl = ampl_notebook(
modules=["highs", "cbc"],  # modules to install
)  # instantiate AMPL object and register magics


## Problem Description¶

In this problem, power generation units are grouped into three distinct types, with different characteristics for each type (power output, cost per megawatt hour, startup cost, etc.). A unit can be on or off, with a startup cost associated with transitioning from off to on, and power output that can fall anywhere between a specified minimum and maximum value when the unit is on. A 24-hour time horizon is divided into 5 discrete time periods, each with an expected total power demand. The model decides which units to turn on, and when, in order to satisfy demand for each time period. The model also captures a reserve requirement, where the selected power plants must be capable of increasing their output, while still respecting their maximum output, in order to cope with the situation where actual demand exceeds predicted demand.

A set of generators is available to satisfy power demand for the following day. Anticipated demand is as follows:

Time Period

Demand (megawatts)

12 am to 6 am

15000

6 am to 9 am

30000

9 am to 3 pm

25000

3 pm to 6 pm

40000

6 pm to 12 am

27000

Generators are grouped into three types, with the following minimum and maximum output for each type (when they are on):

Type

Number available

Minimum output (MW)

Maximum output (MW)

wind

12

850

2000

gas

10

1250

1750

hydro

5

1500

4000

There are costs associated with using a generator: a cost per hour when the generator is on (and generating its minimum output), a cost per megawatt hour above its minimum, and a startup cost for turning a generator on:

Type

Cost per hour (when on)

Cost per MWh above minimum

Startup cost

wind

$$1000$$2.00$$$2000 gas$$2600$

$$1.30$$1000$hydro $$3000$$3.00$

### Unit Commitments¶

The following table shows the number of generators of each type that are active in each time period in the optimal solution using AMPL’s display command:

%%ampl_eval
option display_1col 0, display_transpose -10, display_width 100;
display ngen;

ngen [*,*]
:     '12 am to 6 am' '6 am to 9 am' '9 am to 3 pm' '3 pm to 6 pm' '6 pm to 12 am'    :=
gas          12              12             12             12             12
hydro         3               8              8              9              9
wind          0               0              0              2              0
;


The following shows the number of generators of each type that must be started in each time period to achieve this plan (recall that the model assumes that up to 5 generators of each type are available at the beginning of the time horizon). Here we use Pandas DataFrame and IPython’s display() function:

start_units = ampl.get_data("nstart").to_pandas()
# Capture the original order b/c unstack will mess-up the original ordering
original_order = start_units.index.get_level_values(1).unique()
start_units = start_units.unstack(level=1)
# Convert multi-index to simple index
start_units.columns = start_units.columns.get_level_values(1)
# Reindex using the original order
start_units = start_units.reindex(columns=original_order)
display(start_units)

12 am to 6 am 6 am to 9 am 9 am to 3 pm 3 pm to 6 pm 6 pm to 12 am
gas 7.0 0.0 0.0 0.0 0.0
hydro 0.0 5.0 0.0 1.0 0.0
wind 0.0 0.0 0.0 2.0 0.0