Demonstrate PyPSA unit commitment with a one-bus two-generator example

To enable unit commitment on a generator, set its attribute committable = True.

Available as a Jupyter notebook at http://www.pypsa.org/examples/unit-commitment.ipynb.

In [1]:
import pypsa

Minimum part load demonstration

In final hour load goes below part-load limit of coal gen (30%), forcing gas to commit.

In [5]:
nu = pypsa.Network()

nu.set_snapshots(range(4))

nu.add("Bus","bus")


nu.add("Generator","coal",bus="bus",
       committable=True,
       p_min_pu=0.3,
       marginal_cost=20,
       p_nom=10000)

nu.add("Generator","gas",bus="bus",
       committable=True,
       marginal_cost=70,
       p_min_pu=0.1,
       p_nom=1000)

nu.add("Load","load",bus="bus",p_set=[4000,6000,5000,800])
WARNING:pypsa.components:Attribute network.now is not in newly-defined snapshots. (network.now is only relevant if you call e.g. network.pf() without specifying snapshots.)
In [6]:
nu.lopf(nu.snapshots)
INFO:pypsa.pf:Slack bus for sub-network 0 is bus
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
Out[6]:
('ok', 'optimal')
In [7]:
nu.generators_t.status
Out[7]:
coal gas
0 1.0 0.0
1 1.0 0.0
2 1.0 0.0
3 0.0 1.0
In [8]:
nu.generators_t.p
Out[8]:
coal gas
0 4000.0 -0.0
1 6000.0 -0.0
2 5000.0 -0.0
3 -0.0 800.0

Minimum up time demonstration

Gas has minimum up time, forcing it to be online longer

In [9]:
nu = pypsa.Network()

nu.set_snapshots(range(4))

nu.add("Bus","bus")


nu.add("Generator","coal",bus="bus",
       committable=True,
       p_min_pu=0.3,
       marginal_cost=20,
       p_nom=10000)

nu.add("Generator","gas",bus="bus",
       committable=True,
       marginal_cost=70,
       p_min_pu=0.1,
       initial_status=0,
       min_up_time=3,
       p_nom=1000)

nu.add("Load","load",bus="bus",p_set=[4000,800,5000,3000])
WARNING:pypsa.components:Attribute network.now is not in newly-defined snapshots. (network.now is only relevant if you call e.g. network.pf() without specifying snapshots.)
In [10]:
nu.lopf(nu.snapshots)
INFO:pypsa.pf:Slack bus for sub-network 0 is bus
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
Out[10]:
('ok', 'optimal')
In [11]:
nu.generators_t.status
Out[11]:
coal gas
0 1.0 1.0
1 0.0 1.0
2 1.0 1.0
3 1.0 0.0
In [12]:
nu.generators_t.p
Out[12]:
coal gas
0 3900.0 100.0
1 -0.0 800.0
2 4900.0 100.0
3 3000.0 -0.0

Minimum down time demonstration

Coal has a minimum down time, forcing it to go off longer.

In [13]:
nu = pypsa.Network()

nu.set_snapshots(range(4))

nu.add("Bus","bus")


nu.add("Generator","coal",bus="bus",
       committable=True,
       p_min_pu=0.3,
       marginal_cost=20,
       min_down_time=2,
       p_nom=10000)

nu.add("Generator","gas",bus="bus",
       committable=True,
       marginal_cost=70,
       p_min_pu=0.1,
       initial_status=0,
       p_nom=4000)

nu.add("Load","load",bus="bus",p_set=[3000,800,3000,8000])
WARNING:pypsa.components:Attribute network.now is not in newly-defined snapshots. (network.now is only relevant if you call e.g. network.pf() without specifying snapshots.)
In [14]:
nu.lopf(nu.snapshots)
INFO:pypsa.pf:Slack bus for sub-network 0 is bus
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
Out[14]:
('ok', 'optimal')
In [15]:
nu.objective
Out[15]:
486000.0
In [16]:
nu.generators_t.status
Out[16]:
coal gas
0 1.0 0.0
1 0.0 1.0
2 0.0 1.0
3 1.0 0.0
In [17]:
nu.generators_t.p
Out[17]:
coal gas
0 3000.0 -0.0
1 -0.0 800.0
2 0.0 3000.0
3 8000.0 -0.0

Start up and shut down costs

Now there are associated costs for shutting down, etc

In [18]:
nu = pypsa.Network()

nu.set_snapshots(range(4))

nu.add("Bus","bus")


nu.add("Generator","coal",bus="bus",
       committable=True,
       p_min_pu=0.3,
       marginal_cost=20,
       min_down_time=2,
       start_up_cost=5000,
       p_nom=10000)

nu.add("Generator","gas",bus="bus",
       committable=True,
       marginal_cost=70,
       p_min_pu=0.1,
       initial_status=0,
       shut_down_cost=25,
       p_nom=4000)

nu.add("Load","load",bus="bus",p_set=[3000,800,3000,8000])
WARNING:pypsa.components:Attribute network.now is not in newly-defined snapshots. (network.now is only relevant if you call e.g. network.pf() without specifying snapshots.)
In [19]:
nu.lopf(nu.snapshots)
INFO:pypsa.pf:Slack bus for sub-network 0 is bus
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
Out[19]:
('ok', 'optimal')
In [20]:
nu.objective
Out[20]:
491025.0
In [21]:
nu.generators_t.status
Out[21]:
coal gas
0 0.0 1.0
1 0.0 1.0
2 1.0 0.0
3 1.0 0.0
In [22]:
nu.generators_t.p
Out[22]:
coal gas
0 0.0 3000.0
1 -0.0 800.0
2 3000.0 -0.0
3 8000.0 -0.0

Ramp rate limits

In [23]:
import pypsa

nu = pypsa.Network()

nu.set_snapshots(range(6))

nu.add("Bus","bus")


nu.add("Generator","coal",bus="bus",
       marginal_cost=20,
       ramp_limit_up=0.1,
       ramp_limit_down=0.2,
       p_nom=10000)

nu.add("Generator","gas",bus="bus",
       marginal_cost=70,
       p_nom=4000)

nu.add("Load","load",bus="bus",p_set=[4000,7000,7000,7000,7000,3000])
WARNING:pypsa.components:Attribute network.now is not in newly-defined snapshots. (network.now is only relevant if you call e.g. network.pf() without specifying snapshots.)
In [24]:
nu.lopf(nu.snapshots)
INFO:pypsa.pf:Slack bus for sub-network 0 is bus
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
Out[24]:
('ok', 'optimal')
In [25]:
nu.generators_t.p
Out[25]:
coal gas
0 4000.0 0.0
1 5000.0 2000.0
2 6000.0 1000.0
3 7000.0 0.0
4 5000.0 2000.0
5 3000.0 0.0
In [26]:
import pypsa

nu = pypsa.Network()

nu.set_snapshots(range(6))

nu.add("Bus","bus")


nu.add("Generator","coal",bus="bus",
       marginal_cost=20,
       ramp_limit_up=0.1,
       ramp_limit_down=0.2,
       p_nom_extendable=True,
       capital_cost=1e2)

nu.add("Generator","gas",bus="bus",
       marginal_cost=70,
       p_nom=4000)

nu.add("Load","load",bus="bus",p_set=[4000,7000,7000,7000,7000,3000])
WARNING:pypsa.components:Attribute network.now is not in newly-defined snapshots. (network.now is only relevant if you call e.g. network.pf() without specifying snapshots.)
In [27]:
nu.lopf(nu.snapshots)
INFO:pypsa.pf:Slack bus for sub-network 0 is bus
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
Out[27]:
('ok', 'optimal')
In [28]:
nu.generators.p_nom_opt
Out[28]:
coal    5000.0
gas     4000.0
Name: p_nom_opt, dtype: float64
In [29]:
nu.generators_t.p
Out[29]:
coal gas
0 4000.0 0.0
1 4500.0 2500.0
2 5000.0 2000.0
3 5000.0 2000.0
4 4000.0 3000.0
5 3000.0 0.0
In [30]:
import pypsa

nu = pypsa.Network()

nu.set_snapshots(range(7))

nu.add("Bus","bus")


#Can get bad interactions if SU > RU and p_min_pu; similarly if SD > RD


nu.add("Generator","coal",bus="bus",
       marginal_cost=20,
       committable=True,
       p_min_pu=0.05,
       initial_status=0,
       ramp_limit_start_up=0.1,
       ramp_limit_up=0.2,
       ramp_limit_down=0.25,
       ramp_limit_shut_down=0.15,
       p_nom=10000.)

nu.add("Generator","gas",bus="bus",
       marginal_cost=70,
       p_nom=10000)

nu.add("Load","load",bus="bus",p_set=[0.,200.,7000,7000,7000,2000,0])
WARNING:pypsa.components:Attribute network.now is not in newly-defined snapshots. (network.now is only relevant if you call e.g. network.pf() without specifying snapshots.)
In [31]:
nu.lopf(nu.snapshots)
INFO:pypsa.pf:Slack bus for sub-network 0 is bus
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
Out[31]:
('ok', 'optimal')
In [32]:
nu.generators_t.p
Out[32]:
coal gas
0 -0.0 0.0
1 -0.0 200.0
2 1000.0 6000.0
3 3000.0 4000.0
4 4000.0 3000.0
5 1500.0 500.0
6 -0.0 0.0
In [33]:
nu.generators_t.status
Out[33]:
coal gas
0 0.0 1.0
1 0.0 1.0
2 1.0 1.0
3 1.0 1.0
4 1.0 1.0
5 1.0 1.0
6 0.0 1.0
In [34]:
nu.generators.initial_status
Out[34]:
coal    0
gas     1
Name: initial_status, dtype: int64
In [35]:
nu.generators.loc["coal"]
Out[35]:
attribute
bus                       bus
control                 Slack
type                         
p_nom                   10000
p_nom_extendable        False
p_nom_min                   0
p_nom_max                 inf
p_min_pu                 0.05
p_max_pu                    1
p_set                       0
q_set                       0
sign                        1
carrier                      
marginal_cost              20
capital_cost                0
efficiency                  1
committable              True
start_up_cost               0
shut_down_cost              0
min_up_time                 0
min_down_time               0
initial_status              0
ramp_limit_up             0.2
ramp_limit_down          0.25
ramp_limit_start_up       0.1
ramp_limit_shut_down     0.15
p_nom_opt               10000
Name: coal, dtype: object