Note
You can download this example as a Jupyter notebook or start it in interactive mode.
Unit commitment
This tutorial runs through examples of unit commitment for generators at a single bus. Examples of minimum part-load, minimum up time, minimum down time, start up costs, shut down costs and ramp rate restrictions are shown.
To enable unit commitment on a generator, set its attribute committable = True.
[1]:
import pypsa
import pandas as pd
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In [1], line 1
----> 1 import pypsa
2 import pandas as pd
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/pypsa/__init__.py:10
1 # -*- coding: utf-8 -*-
4 """
5 Python for Power Systems Analysis (PyPSA)
6
7 Grid calculation library.
8 """
---> 10 from pypsa import (
11 components,
12 contingency,
13 descriptors,
14 examples,
15 geo,
16 io,
17 linopf,
18 linopt,
19 networkclustering,
20 opf,
21 opt,
22 optimization,
23 pf,
24 plot,
25 )
26 from pypsa.components import Network, SubNetwork
28 __version__ = "0.21.2"
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/pypsa/components.py:50
37 from pypsa.io import (
38 export_to_csv_folder,
39 export_to_hdf5,
(...)
47 import_series_from_dataframe,
48 )
49 from pypsa.opf import network_lopf, network_opf
---> 50 from pypsa.optimization.optimize import OptimizationAccessor
51 from pypsa.pf import (
52 calculate_B_H,
53 calculate_dependent_values,
(...)
62 sub_network_pf,
63 )
64 from pypsa.plot import iplot, plot
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/pypsa/optimization/__init__.py:7
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 """
4 Build optimisation problems from PyPSA networks with Linopy.
5 """
----> 7 from pypsa.optimization import abstract, constraints, optimize, variables
8 from pypsa.optimization.optimize import create_model
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/pypsa/optimization/constraints.py:9
6 import logging
8 import pandas as pd
----> 9 from linopy.expressions import LinearExpression, merge
10 from numpy import arange, cumsum, inf, nan, roll
11 from scipy import sparse
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/linopy/__init__.py:9
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 """
4 Created on Wed Mar 10 11:03:06 2021.
5
6 @author: fabulous
7 """
----> 9 from linopy import model, remote
10 from linopy.expressions import merge
11 from linopy.io import read_netcdf
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/linopy/model.py:22
20 from linopy import solvers
21 from linopy.common import best_int, replace_by_map
---> 22 from linopy.constraints import (
23 AnonymousConstraint,
24 AnonymousScalarConstraint,
25 Constraints,
26 )
27 from linopy.eval import Expr
28 from linopy.expressions import LinearExpression, ScalarLinearExpression
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/linopy/constraints.py:21
18 from scipy.sparse import coo_matrix
19 from xarray import DataArray, Dataset
---> 21 from linopy import expressions, variables
22 from linopy.common import (
23 _merge_inplace,
24 has_assigned_model,
(...)
27 replace_by_map,
28 )
31 class Constraint(DataArray):
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/linopy/expressions.py:23
20 from xarray.core.dataarray import DataArrayCoordinates
21 from xarray.core.groupby import _maybe_reorder, peek_at
---> 23 from linopy import constraints, variables
24 from linopy.common import as_dataarray
27 def exprwrap(method, *default_args, **new_default_kwargs):
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/site-packages/linopy/variables.py:398
393 roll = varwrap(DataArray.roll)
395 rolling = varwrap(DataArray.rolling)
--> 398 @dataclass(repr=False)
399 class Variables:
400 """
401 A variables container used for storing multiple variable arrays.
402 """
404 labels: Dataset = Dataset()
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/dataclasses.py:1211, in dataclass.<locals>.wrap(cls)
1210 def wrap(cls):
-> 1211 return _process_class(cls, init, repr, eq, order, unsafe_hash,
1212 frozen, match_args, kw_only, slots,
1213 weakref_slot)
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/dataclasses.py:959, in _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, slots, weakref_slot)
956 kw_only = True
957 else:
958 # Otherwise it's a field of some type.
--> 959 cls_fields.append(_get_field(cls, name, type, kw_only))
961 for f in cls_fields:
962 fields[f.name] = f
File ~/checkouts/readthedocs.org/user_builds/pypsa/conda/v0.21.2/lib/python3.11/dataclasses.py:816, in _get_field(cls, a_name, a_type, default_kw_only)
812 # For real fields, disallow mutable defaults. Use unhashable as a proxy
813 # indicator for mutability. Read the __hash__ attribute from the class,
814 # not the instance.
815 if f._field_type is _FIELD and f.default.__class__.__hash__ is None:
--> 816 raise ValueError(f'mutable default {type(f.default)} for field '
817 f'{f.name} is not allowed: use default_factory')
819 return f
ValueError: mutable default <class 'xarray.core.dataset.Dataset'> for field labels is not allowed: use default_factory
Minimum part load demonstration
In final hour load goes below part-load limit of coal gen (30%), forcing gas to commit.
[2]:
nu = pypsa.Network(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])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [2], line 1
----> 1 nu = pypsa.Network(snapshots=range(4))
3 nu.add("Bus", "bus")
5 nu.add(
6 "Generator",
7 "coal",
(...)
12 p_nom=10000,
13 )
NameError: name 'pypsa' is not defined
[3]:
nu.lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [3], line 1
----> 1 nu.lopf()
NameError: name 'nu' is not defined
[4]:
nu.generators_t.status
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [4], line 1
----> 1 nu.generators_t.status
NameError: name 'nu' is not defined
[5]:
nu.generators_t.p
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [5], line 1
----> 1 nu.generators_t.p
NameError: name 'nu' is not defined
Minimum up time demonstration
Gas has minimum up time, forcing it to be online longer
[6]:
nu = pypsa.Network(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,
up_time_before=0,
min_up_time=3,
p_nom=1000,
)
nu.add("Load", "load", bus="bus", p_set=[4000, 800, 5000, 3000])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [6], line 1
----> 1 nu = pypsa.Network(snapshots=range(4))
3 nu.add("Bus", "bus")
5 nu.add(
6 "Generator",
7 "coal",
(...)
12 p_nom=10000,
13 )
NameError: name 'pypsa' is not defined
[7]:
nu.lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [7], line 1
----> 1 nu.lopf()
NameError: name 'nu' is not defined
[8]:
nu.generators_t.status
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [8], line 1
----> 1 nu.generators_t.status
NameError: name 'nu' is not defined
[9]:
nu.generators_t.p
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [9], line 1
----> 1 nu.generators_t.p
NameError: name 'nu' is not defined
Minimum down time demonstration
Coal has a minimum down time, forcing it to go off longer.
[10]:
nu = pypsa.Network(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,
down_time_before=1,
p_nom=10000,
)
nu.add(
"Generator",
"gas",
bus="bus",
committable=True,
marginal_cost=70,
p_min_pu=0.1,
p_nom=4000,
)
nu.add("Load", "load", bus="bus", p_set=[3000, 800, 3000, 8000])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [10], line 1
----> 1 nu = pypsa.Network(snapshots=range(4))
3 nu.add("Bus", "bus")
5 nu.add(
6 "Generator",
7 "coal",
(...)
14 p_nom=10000,
15 )
NameError: name 'pypsa' is not defined
[11]:
nu.lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [11], line 1
----> 1 nu.lopf()
NameError: name 'nu' is not defined
[12]:
nu.objective
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [12], line 1
----> 1 nu.objective
NameError: name 'nu' is not defined
[13]:
nu.generators_t.status
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [13], line 1
----> 1 nu.generators_t.status
NameError: name 'nu' is not defined
[14]:
nu.generators_t.p
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [14], line 1
----> 1 nu.generators_t.p
NameError: name 'nu' is not defined
Start up and shut down costs
Now there are associated costs for shutting down, etc
[15]:
nu = pypsa.Network(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,
shut_down_cost=25,
p_nom=4000,
)
nu.add("Load", "load", bus="bus", p_set=[3000, 800, 3000, 8000])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [15], line 1
----> 1 nu = pypsa.Network(snapshots=range(4))
3 nu.add("Bus", "bus")
5 nu.add(
6 "Generator",
7 "coal",
(...)
14 p_nom=10000,
15 )
NameError: name 'pypsa' is not defined
[16]:
nu.lopf(nu.snapshots)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [16], line 1
----> 1 nu.lopf(nu.snapshots)
NameError: name 'nu' is not defined
[17]:
nu.objective
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [17], line 1
----> 1 nu.objective
NameError: name 'nu' is not defined
[18]:
nu.generators_t.status
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [18], line 1
----> 1 nu.generators_t.status
NameError: name 'nu' is not defined
[19]:
nu.generators_t.p
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [19], line 1
----> 1 nu.generators_t.p
NameError: name 'nu' is not defined
Ramp rate limits
[20]:
nu = pypsa.Network(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])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [20], line 1
----> 1 nu = pypsa.Network(snapshots=range(6))
3 nu.add("Bus", "bus")
5 nu.add(
6 "Generator",
7 "coal",
(...)
12 p_nom=10000,
13 )
NameError: name 'pypsa' is not defined
[21]:
nu.lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [21], line 1
----> 1 nu.lopf()
NameError: name 'nu' is not defined
[22]:
nu.generators_t.p
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [22], line 1
----> 1 nu.generators_t.p
NameError: name 'nu' is not defined
[23]:
nu = pypsa.Network(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])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [23], line 1
----> 1 nu = pypsa.Network(snapshots=range(6))
3 nu.add("Bus", "bus")
5 nu.add(
6 "Generator",
7 "coal",
(...)
13 capital_cost=1e2,
14 )
NameError: name 'pypsa' is not defined
[24]:
nu.lopf(nu.snapshots)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [24], line 1
----> 1 nu.lopf(nu.snapshots)
NameError: name 'nu' is not defined
[25]:
nu.generators.p_nom_opt
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [25], line 1
----> 1 nu.generators.p_nom_opt
NameError: name 'nu' is not defined
[26]:
nu.generators_t.p
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [26], line 1
----> 1 nu.generators_t.p
NameError: name 'nu' is not defined
[27]:
nu = pypsa.Network(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.0,
)
nu.add("Generator", "gas", bus="bus", marginal_cost=70, p_nom=10000)
nu.add("Load", "load", bus="bus", p_set=[0.0, 200.0, 7000, 7000, 7000, 2000, 0])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [27], line 1
----> 1 nu = pypsa.Network(snapshots=range(7))
3 nu.add("Bus", "bus")
5 # Can get bad interactions if SU > RU and p_min_pu; similarly if SD > RD
NameError: name 'pypsa' is not defined
[28]:
nu.lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [28], line 1
----> 1 nu.lopf()
NameError: name 'nu' is not defined
[29]:
nu.generators_t.p
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [29], line 1
----> 1 nu.generators_t.p
NameError: name 'nu' is not defined
[30]:
nu.generators_t.status
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [30], line 1
----> 1 nu.generators_t.status
NameError: name 'nu' is not defined
[31]:
nu.generators.loc["coal"]
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [31], line 1
----> 1 nu.generators.loc["coal"]
NameError: name 'nu' is not defined
Rolling horizon example
This example solves sequentially in batches
[32]:
sets_of_snapshots = 6
p_set = [4000, 5000, 700, 800, 4000]
nu = pypsa.Network(snapshots=range(len(p_set) * sets_of_snapshots))
nu.add("Bus", "bus")
nu.add(
"Generator",
"coal",
bus="bus",
committable=True,
p_min_pu=0.3,
marginal_cost=20,
min_down_time=2,
min_up_time=3,
up_time_before=1,
ramp_limit_up=1,
ramp_limit_down=1,
ramp_limit_start_up=1,
ramp_limit_shut_down=1,
shut_down_cost=150,
start_up_cost=200,
p_nom=10000,
)
nu.add(
"Generator",
"gas",
bus="bus",
committable=True,
marginal_cost=70,
p_min_pu=0.1,
up_time_before=2,
min_up_time=3,
shut_down_cost=20,
start_up_cost=50,
p_nom=1000,
)
nu.add("Load", "load", bus="bus", p_set=p_set * sets_of_snapshots)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [32], line 4
1 sets_of_snapshots = 6
2 p_set = [4000, 5000, 700, 800, 4000]
----> 4 nu = pypsa.Network(snapshots=range(len(p_set) * sets_of_snapshots))
6 nu.add("Bus", "bus")
8 nu.add(
9 "Generator",
10 "coal",
(...)
24 p_nom=10000,
25 )
NameError: name 'pypsa' is not defined
[33]:
overlap = 2
for i in range(sets_of_snapshots):
nu.lopf(nu.snapshots[i * len(p_set) : (i + 1) * len(p_set) + overlap], pyomo=False)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [33], line 3
1 overlap = 2
2 for i in range(sets_of_snapshots):
----> 3 nu.lopf(nu.snapshots[i * len(p_set) : (i + 1) * len(p_set) + overlap], pyomo=False)
NameError: name 'nu' is not defined
[34]:
pd.concat(
{"Active": nu.generators_t.status.astype(bool), "Output": nu.generators_t.p}, axis=1
)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [34], line 1
----> 1 pd.concat(
2 {"Active": nu.generators_t.status.astype(bool), "Output": nu.generators_t.p}, axis=1
3 )
NameError: name 'pd' is not defined