Note
You can download this example as a Jupyter notebook or start it in interactive mode.
LOPF with coupling to heating sector
In this example three locations are optimised, each with an electric bus and a heating bus and corresponding loads. At each location the electric and heating buses are connected with heat pumps; heat can also be supplied to the heat bus with a boiler. The electric buses are connected with transmission lines and there are electrical generators at two of the nodes.
[1]:
import pypsa
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(rc={"figure.figsize": (9, 5)})
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In [1], line 1
----> 1 import pypsa
2 import numpy as np
3 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
[2]:
network = pypsa.Network()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [2], line 1
----> 1 network = pypsa.Network()
NameError: name 'pypsa' is not defined
Add three buses of AC and heat carrier each
[3]:
for i in range(3):
network.add("Bus", "electric bus {}".format(i), v_nom=20.0)
network.add("Bus", "heat bus {}".format(i), carrier="heat")
network.buses
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [3], line 2
1 for i in range(3):
----> 2 network.add("Bus", "electric bus {}".format(i), v_nom=20.0)
3 network.add("Bus", "heat bus {}".format(i), carrier="heat")
4 network.buses
NameError: name 'network' is not defined
[4]:
network.buses["carrier"].value_counts()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [4], line 1
----> 1 network.buses["carrier"].value_counts()
NameError: name 'network' is not defined
Add three lines in a ring
[5]:
for i in range(3):
network.add(
"Line",
"line {}".format(i),
bus0="electric bus {}".format(i),
bus1="electric bus {}".format((i + 1) % 3),
x=0.1,
s_nom=1000,
)
network.lines
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [5], line 2
1 for i in range(3):
----> 2 network.add(
3 "Line",
4 "line {}".format(i),
5 bus0="electric bus {}".format(i),
6 bus1="electric bus {}".format((i + 1) % 3),
7 x=0.1,
8 s_nom=1000,
9 )
10 network.lines
NameError: name 'network' is not defined
Connect the electric to the heat buses with heat pumps with COP 3
[6]:
for i in range(3):
network.add(
"Link",
"heat pump {}".format(i),
bus0="electric bus {}".format(i),
bus1="heat bus {}".format(i),
p_nom=100,
efficiency=3.0,
)
network.links
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [6], line 2
1 for i in range(3):
----> 2 network.add(
3 "Link",
4 "heat pump {}".format(i),
5 bus0="electric bus {}".format(i),
6 bus1="heat bus {}".format(i),
7 p_nom=100,
8 efficiency=3.0,
9 )
10 network.links
NameError: name 'network' is not defined
Add carriers
[7]:
network.add("Carrier", "gas", co2_emissions=0.27)
network.add("Carrier", "biomass", co2_emissions=0.0)
network.carriers
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [7], line 1
----> 1 network.add("Carrier", "gas", co2_emissions=0.27)
2 network.add("Carrier", "biomass", co2_emissions=0.0)
3 network.carriers
NameError: name 'network' is not defined
Add a gas generator at bus 0, a biomass generator at bus 1 and a boiler at all heat buses
[8]:
network.add(
"Generator",
"gas generator",
bus="electric bus 0",
p_nom=100,
marginal_cost=50,
carrier="gas",
efficiency=0.3,
)
network.add(
"Generator",
"biomass generator",
bus="electric bus 1",
p_nom=100,
marginal_cost=100,
efficiency=0.3,
carrier="biomass",
)
for i in range(3):
network.add(
"Generator",
"boiler {}".format(i),
bus="heat bus {}".format(i),
p_nom=1000,
efficiency=0.9,
marginal_cost=20.0,
carrier="gas",
)
network.generators
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [8], line 1
----> 1 network.add(
2 "Generator",
3 "gas generator",
4 bus="electric bus 0",
5 p_nom=100,
6 marginal_cost=50,
7 carrier="gas",
8 efficiency=0.3,
9 )
11 network.add(
12 "Generator",
13 "biomass generator",
(...)
18 carrier="biomass",
19 )
21 for i in range(3):
NameError: name 'network' is not defined
Add electric loads and heat loads.
[9]:
for i in range(3):
network.add(
"Load",
"electric load {}".format(i),
bus="electric bus {}".format(i),
p_set=i * 10,
)
for i in range(3):
network.add(
"Load",
"heat load {}".format(i),
bus="heat bus {}".format(i),
p_set=(3 - i) * 10,
)
network.loads
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [9], line 2
1 for i in range(3):
----> 2 network.add(
3 "Load",
4 "electric load {}".format(i),
5 bus="electric bus {}".format(i),
6 p_set=i * 10,
7 )
9 for i in range(3):
10 network.add(
11 "Load",
12 "heat load {}".format(i),
13 bus="heat bus {}".format(i),
14 p_set=(3 - i) * 10,
15 )
NameError: name 'network' is not defined
We define a function for the LOPF
[10]:
def run_lopf():
network.lopf()
df = pd.concat(
[
network.generators_t.p.loc["now"],
network.links_t.p0.loc["now"],
network.loads_t.p.loc["now"],
],
keys=["Generators", "Links", "Line"],
names=["Component", "index"],
).reset_index(name="Production")
sns.barplot(data=df, x="index", y="Production", hue="Component")
plt.title(f"Objective: {network.objective}")
plt.xticks(rotation=90)
plt.tight_layout()
[11]:
run_lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [11], line 1
----> 1 run_lopf()
Cell In [10], line 2, in run_lopf()
1 def run_lopf():
----> 2 network.lopf()
3 df = pd.concat(
4 [
5 network.generators_t.p.loc["now"],
(...)
10 names=["Component", "index"],
11 ).reset_index(name="Production")
13 sns.barplot(data=df, x="index", y="Production", hue="Component")
NameError: name 'network' is not defined
Now, rerun with marginal costs for the heat pump operation.
[12]:
network.links.marginal_cost = 10
run_lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [12], line 1
----> 1 network.links.marginal_cost = 10
2 run_lopf()
NameError: name 'network' is not defined
Finally, rerun with no CO2 emissions.
[13]:
network.add("GlobalConstraint", "co2_limit", sense="<=", constant=0.0)
run_lopf()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [13], line 1
----> 1 network.add("GlobalConstraint", "co2_limit", sense="<=", constant=0.0)
3 run_lopf()
NameError: name 'network' is not defined