Commit 86e653ad authored by jonas.hoersch's avatar jonas.hoersch
Browse files

Fix typos in exercise 3

Storages -> storage units
parent 650d0ada
%% Cell type:markdown id: tags:
# Energy System Modelling - Solutions to Tutorial III
%% Cell type:markdown id: tags:
**Settings**
%% Cell type:code id: tags:
``` python
import pypsa
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
```
%% Cell type:code id: tags:
``` python
%matplotlib inline
```
%% Cell type:code id: tags:
``` python
solver = "glpk"
```
%% Cell type:markdown id: tags:
***
**(a) Build a network in PyPSA with the two buses North and South and attach the load at each bus and attach the wind and solar generators with availability according to $G_{N,w}(t) = Cf_w(1+A_w\sin \omega_w t)$ and $G_{S,s}(t) = Cf_s(1+A_s\sin \omega_s t)$ for a year (you have to call `set_snapshots` for the year) and with `p_nom_extendable` set to `True`. As help you should have a look at the [minimal lopf example](https://www.pypsa.org/examples/minimal_example_lopf.html), understand what the [components documentation](https://pypsa.org/doc/components.html) of PyPSA gives you and that you can find the underlying objective function and constraints in the [LOPF documentation](https://pypsa.org/doc/optimal_power_flow.html#linear-optimal-power-flow).**
%% Cell type:markdown id: tags:
Initialize network
%% Cell type:code id: tags:
``` python
network = pypsa.Network()
```
%% Cell type:markdown id: tags:
Add North and South bus
%% Cell type:code id: tags:
``` python
network.add("Bus",
"North",
carrier="AC")
```
%% Cell type:code id: tags:
``` python
network.add("Bus",
"South",
carrier="AC")
```
%% Cell type:markdown id: tags:
Attach constant load
%% Cell type:code id: tags:
``` python
network.add("Load",
"North Load",
bus="North",
p_set=20e3)
```
%% Cell type:code id: tags:
``` python
network.add("Load",
"South Load",
bus="South",
p_set=30e3)
```
%% Cell type:markdown id: tags:
Attach renewable generators according to given parameters
%% Cell type:code id: tags:
``` python
network.set_snapshots(np.arange(0, 4*7*24))
```
%% Cell type:code id: tags:
``` python
Cfw = 0.3
Aw = 0.9
omegaw = 2*np.pi/(7*24)
Cfs = 0.12
As = 1.
omegas = 2*np.pi/24
GNwt = Cfw * (1+Aw*np.sin(omegaw*network.snapshots.to_series()))
GSst = Cfs * (1+As*np.sin(omegas*network.snapshots.to_series()))
```
%% Cell type:code id: tags:
``` python
pd.concat([GNwt, GSst], keys=['wind', 'solar'], axis=1).loc[:4*7*24].plot()
```
%% Output
<matplotlib.axes._subplots.AxesSubplot at 0x7f047996b3c8>
%% Cell type:code id: tags:
``` python
network.add("Generator",
"Wind",
bus="North",
p_nom_extendable=True,
capital_cost=1.2e6,
p_max_pu=GNwt)
```
%% Cell type:code id: tags:
``` python
network.add("Generator",
"Solar",
bus="South",
p_nom_extendable=True,
capital_cost=0.6e6,
p_max_pu=GSst)
```
%% Cell type:markdown id: tags:
***
**(b) Attach extendable storages at the North and the South! The storages have to be modelled as an `H2-bus` (a bus with `carrier='H2'`) linked to the `AC-bus` North with a `Link` where `p_nom_extendable=True` with the `capital_cost` of the power capacity and an also extendable `Store` with the `capital_cost` of the energy capacity, for instance. The losses can be set on the links as `efficiency`.**
**(b) Attach extendable storage units at the North and the South! The storages have to be modelled as an `H2-bus` (a bus with `carrier='H2'`) linked to the `AC-bus` North with a `Link` where `p_nom_extendable=True` with the `capital_cost` of the power capacity and an also extendable `Store` with the `capital_cost` of the energy capacity, for instance. The losses can be set on the links as `efficiency`.**
%% Cell type:code id: tags:
``` python
for bus in ["North", "South"]:
# H2 storage
network.add("Bus",
bus + " H2",
carrier="H2")
network.add("Store",
bus + " H2 St.",
bus=bus + " H2",
e_nom_extendable=True,
capital_cost=10e3)
network.add("Link",
bus + "->H2",
bus0=bus, bus1=bus + " H2",
p_nom_extendable=True,
capital_cost=0.3e6,
efficiency=0.75)
network.add("Link",
"H2->" + bus,
bus0=bus + " H2", bus1=bus,
p_nom_extendable=True,
capital_cost=0.45e6,
efficiency=0.58)
# Battery storage
network.add("Bus",
bus + " Battery",
carrier="Battery")
network.add("Store",
bus + " Battery St.",
bus=bus + " Battery",
e_nom_extendable=True,
capital_cost=0.2e6)
network.add("Link",
bus + "->Battery",
bus0=bus, bus1=bus + " Battery",
p_nom_extendable=True,
capital_cost=0.15e6,
efficiency=0.9)
network.add("Link",
"Battery->" + bus,
bus0=bus + " Battery", bus1=bus,
p_nom_extendable=True,
capital_cost=0.15e6,
efficiency=0.9)
```
%% Cell type:markdown id: tags:
***
**(c) Run an investment optimization by calling the `lopf` function.**
%% Cell type:code id: tags:
``` python
network.lopf(solver_name=solver)
```
%% Output
INFO:pypsa.pf:Slack bus for sub-network 0 is North
INFO:pypsa.pf:Slack bus for sub-network 1 is South
WARNING:pypsa.pf:No generators in sub-network 2, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 2 is North H2
WARNING:pypsa.pf:No generators in sub-network 3, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 3 is North Battery
WARNING:pypsa.pf:No generators in sub-network 4, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 4 is South H2
WARNING:pypsa.pf:No generators in sub-network 5, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 5 is South Battery
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pypsa/components.py:758: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=False'.
To retain the current behavior and silence the warning, pass 'sort=True'.
keys=self.passive_branch_components)
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
# ==========================================================
# = Solver Results =
# ==========================================================
# ----------------------------------------------------------
# Problem Information
# ----------------------------------------------------------
Problem:
- Name: unknown
Lower bound: 360476623771.945
Upper bound: 360476623771.945
Number of objectives: 1
Number of constraints: 29569
Number of variables: 16143
Number of nonzeros: 55073
Sense: minimize
# ----------------------------------------------------------
# Solver Information
# ----------------------------------------------------------
Solver:
- Status: ok
Termination condition: optimal
Statistics:
Branch and bound:
Number of bounded subproblems: 0
Number of created subproblems: 0
Error rc: 0
Time: 4.201282262802124
# ----------------------------------------------------------
# Solution Information
# ----------------------------------------------------------
Solution:
- number of solutions: 0
number of solutions displayed: 0
/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pypsa/opf.py:1207: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=False'.
To retain the current behavior and silence the warning, pass 'sort=True'.
for c in network.iterate_components(network.controllable_one_port_components)}) \
('ok', 'optimal')
%% Cell type:markdown id: tags:
***
**(d) How do your results `objective` and `{generators,stores,links}.p_nom_opt` compare with the results of III.1(d)?**
%% Cell type:code id: tags:
``` python
obj_v1 = network.objective / 1e9 # Mio. Euro
obj_v1
```
%% Output
360.476623771945
%% Cell type:code id: tags:
``` python
# (a) Capacities for wind and solar.
res_cap_v1 = network.generators.p_nom_opt / 1e3 # GW
res_cap_v1
```
%% Output
Wind 87.381871
Solar 267.961955
Name: p_nom_opt, dtype: float64
%% Cell type:code id: tags:
``` python
# (b) Store and dispatch power capacity.
sto_cap_v1 = network.links.p_nom_opt / 1e3 # GW
sto_cap_v1
```
%% Output
North->H2 2.899018e+01
H2->North 2.984926e+01
North->Battery 1.357335e-02
Battery->North 7.330123e-02
South->H2 -5.788267e-15
H2->South -5.293786e-14
South->Battery 3.431087e+01
Battery->South 3.333333e+01
Name: p_nom_opt, dtype: float64
%% Cell type:code id: tags:
``` python
# (c) Energy capacities.
sto_engy_v1 = network.stores.e_nom_opt / 1e6 # TWh
sto_engy_v1
```
%% Output
North H2 St. 1.350790e+00
North Battery St. 1.832403e-04
South H2 St. -5.803564e-16
South Battery St. 2.450389e-01
Name: e_nom_opt, dtype: float64
%% Cell type:code id: tags:
``` python
network.stores_t.e.plot()
```
%% Output
<matplotlib.axes._subplots.AxesSubplot at 0x7f0499d8c208>
%% Cell type:markdown id: tags:
***
**(e) Now we lift the restriction against transmission and allow North and South to bridge their 500 km
separation with a transmission line. How does the cost optimal technology mix change?**
%% Cell type:markdown id: tags:
Add extendable link between North and South:
%% Cell type:code id: tags:
``` python
network.add("Link",
"North<->South",
bus0="North", bus1="South",
p_min_pu=-1,
p_nom_extendable=True,
capital_cost=0.2e6)
```
%% Cell type:markdown id: tags:
Run LOPF:
%% Cell type:code id: tags:
``` python
network.lopf(solver_name=solver)
```
%% Output
/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pandas/core/frame.py:6211: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=False'.
To retain the current behavior and silence the warning, pass 'sort=True'.
sort=sort)
INFO:pypsa.pf:Slack bus for sub-network 0 is North
INFO:pypsa.pf:Slack bus for sub-network 1 is South
WARNING:pypsa.pf:No generators in sub-network 2, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 2 is North H2
WARNING:pypsa.pf:No generators in sub-network 3, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 3 is North Battery
WARNING:pypsa.pf:No generators in sub-network 4, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 4 is South H2
WARNING:pypsa.pf:No generators in sub-network 5, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 5 is South Battery
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pypsa/components.py:758: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=False'.
To retain the current behavior and silence the warning, pass 'sort=True'.
keys=self.passive_branch_components)
INFO:pypsa.opf:Solving model using glpk
INFO:pypsa.opf:Optimization successful
# ==========================================================
# = Solver Results =
# ==========================================================
# ----------------------------------------------------------
# Problem Information
# ----------------------------------------------------------
Problem:
- Name: unknown
Lower bound: 338496411380.838
Upper bound: 338496411380.838
Number of objectives: 1
Number of constraints: 30913
Number of variables: 16816
Number of nonzeros: 59105
Sense: minimize
# ----------------------------------------------------------
# Solver Information
# ----------------------------------------------------------
Solver:
- Status: ok
Termination condition: optimal
Statistics:
Branch and bound:
Number of bounded subproblems: 0
Number of created subproblems: 0
Error rc: 0
Time: 8.108036041259766
# ----------------------------------------------------------
# Solution Information
# ----------------------------------------------------------
Solution:
- number of solutions: 0
number of solutions displayed: 0
/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pypsa/opf.py:1207: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=False'.
To retain the current behavior and silence the warning, pass 'sort=True'.
for c in network.iterate_components(network.controllable_one_port_components)}) \
('ok', 'optimal')
%% Cell type:markdown id: tags:
Get the results `objective` and `{generators,stores,links}.p_nom_opt` with real availability:
%% Cell type:code id: tags:
``` python
obj_v2 = network.objective / 1e9 # Mio. Euro
obj_v2
```
%% Output
338.496411380838
%% Cell type:code id: tags:
``` python
# (a) Capacities for wind and solar.
res_cap_v2 = network.generators.p_nom_opt / 1e3 # GW
res_cap_v2
```
%% Output
Wind 130.539736
Solar 174.567156
Name: p_nom_opt, dtype: float64
%% Cell type:code id: tags:
``` python
# (b) Store and dispatch power capacity.
sto_cap_v2 = network.links.p_nom_opt / 1e3 # GW
sto_cap_v2
```
%% Output
North->H2 3.050408e+01
H2->North 4.681387e+01
North->Battery -2.328955e-13
Battery->North -6.652696e-14
South->H2 2.253023e+01
H2->South 1.654343e+01
South->Battery 4.278347e+00
Battery->South 6.495718e+00
North<->South 2.390357e+01
Name: p_nom_opt, dtype: float64
%% Cell type:code id: tags:
``` python
# (c) Energy capacities
sto_engy_v2 = network.stores.e_nom_opt / 1e6 # TWh
sto_engy_v2
```
%% Output
North H2 St. 1.400510e+00
North Battery St. -1.191108e-15
South H2 St. 7.131808e-01
South Battery St. 2.576810e-02
Name: e_nom_opt, dtype: float64
%% Cell type:code id: tags:
``` python
network.stores_t.e.plot()
```
%% Output
<matplotlib.axes._subplots.AxesSubplot at 0x7f0478a29198>
%% Cell type:markdown id: tags:
***
**(e) Replace the approximated availability time-series of the wind and the solar generators with the ones from `availability.csv` computed from reanalysis weather data available on the [course website](https://nworbmot.org/courses/complex_renewable_energy_networks/) and re-run the LOPF. Compare the results! Explain the differences by looking at the cumulative variations relative to the mean of the availability time-series!**
**(e) Replace the approximated availability time-series of the wind and the solar generators with the ones from `availability.csv` computed from reanalysis weather data and re-run the LOPF. Compare the results! Explain the differences by looking at the cumulative variations relative to the mean of the availability time-series!**
%% Cell type:markdown id: tags:
Adapt the network to new availabiltiy data:
%% Cell type:code id: tags:
``` python
network.remove("Generator", "Wind")
network.remove("Generator", "Solar")
```
%% Cell type:code id: tags:
``` python
availability = pd.read_csv("availability.csv", index_col=0, parse_dates=True)
availability.head()
```
%% Output
solar wind
name
2012-01-01 00:00:00 0.0 0.402412
2012-01-01 01:00:00 0.0 0.480648
2012-01-01 02:00:00 0.0 0.542354
2012-01-01 03:00:00 0.0 0.586046
2012-01-01 04:00:00 0.0 0.641201
%% Cell type:code id: tags:
``` python
availability.loc["2012-7"].plot()
```
%% Output
<matplotlib.axes._subplots.AxesSubplot at 0x7f0478c08240>
%% Cell type:code id: tags:
``` python
availability.loc["2012-7"].index
```
%% Output
DatetimeIndex(['2012-07-01 00:00:00', '2012-07-01 01:00:00',
'2012-07-01 02:00:00', '2012-07-01 03:00:00',
'2012-07-01 04:00:00', '2012-07-01 05:00:00',
'2012-07-01 06:00:00', '2012-07-01 07:00:00',
'2012-07-01 08:00:00', '2012-07-01 09:00:00',
...
'2012-07-31 14:00:00', '2012-07-31 15:00:00',
'2012-07-31 16:00:00', '2012-07-31 17:00:00',
'2012-07-31 18:00:00', '2012-07-31 19:00:00',
'2012-07-31 20:00:00', '2012-07-31 21:00:00',
'2012-07-31 22:00:00', '2012-07-31 23:00:00'],
dtype='datetime64[ns]', name='name', length=744, freq=None)
%% Cell type:code id: tags:
``` python
network.set_snapshots(availability.loc["2012-7"].index)
```
%% Cell type:code id: tags:
``` python
network.add("Generator",
"Wind",
bus="North",
p_nom_extendable=True,
capital_cost=1.2e6,
p_max_pu=availability["wind"])
```
%% Cell type:code id: tags:
``` python
network.add("Generator",
"Solar",
bus="South",
p_nom_extendable=True,
capital_cost=0.6e6,
p_max_pu=availability["solar"])
```
%% Cell type:markdown id: tags:
Run LOPF:
%% Cell type:code id: tags:
``` python
network.lopf(solver_name="glpk")
```
%% Output
/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pandas/core/frame.py:6211: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=False'.
To retain the current behavior and silence the warning, pass 'sort=True'.
sort=sort)
INFO:pypsa.pf:Slack bus for sub-network 0 is North
INFO:pypsa.pf:Slack bus for sub-network 1 is South
WARNING:pypsa.pf:No generators in sub-network 2, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 2 is North H2
WARNING:pypsa.pf:No generators in sub-network 3, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 3 is North Battery
WARNING:pypsa.pf:No generators in sub-network 4, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 4 is South H2
WARNING:pypsa.pf:No generators in sub-network 5, better hope power is already balanced
INFO:pypsa.pf:Slack bus for sub-network 5 is South Battery
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `angles` formulation
/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pypsa/components.py:758: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=False'.
To retain the current behavior and silence the warning, pass 'sort=True'.
keys=self.passive_branch_components)
INFO:pypsa.opf:Solving model using gurobi
ERROR:pyomo.opt:Solver (gurobi) returned non-zero return code (1)
ERROR:pyomo.opt:Solver log:
Traceback (most recent call last):
File "<stdin>", line 5, in <module>
File "/home/ws/sp2668/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pyomo/solvers/plugins/solvers/GUROBI_RUN.py", line 61, in gurobi_run
model = read(model_file)
File "gurobi.pxi", line 2652, in gurobipy.read (../../src/python/gurobipy.c:127968)
File "gurobi.pxi", line 65, in gurobipy.gurobi.read (../../src/python/gurobipy.c:125608)
File "gurobi.pxi", line 17, in gurobipy.gurobi._getdefaultenv (../../src/python/gurobipy.c:124705)
File "env.pxi", line 45, in gurobipy.Env.__init__ (../../src/python/gurobipy.c:7673)
gurobipy.GurobiError: No Gurobi license found (user sp2668, host iai-esm002, hostid 452b2088, cores 1)
ERROR: Solver (gurobi) returned non-zero return code (1)
ERROR: Solver log: Traceback (most recent call last):
File "<stdin>", line 5, in <module> File
"/home/ws/sp2668/software/anaconda3/envs/esm-
tutorials/lib/python3.6/site-
packages/pyomo/solvers/plugins/solvers/GUROBI_RUN.py", line 61, in
gurobi_run
model = read(model_file)
File "gurobi.pxi", line 2652, in gurobipy.read
(../../src/python/gurobipy.c:127968) File "gurobi.pxi", line 65, in
gurobipy.gurobi.read (../../src/python/gurobipy.c:125608) File
"gurobi.pxi", line 17, in gurobipy.gurobi._getdefaultenv
(../../src/python/gurobipy.c:124705) File "env.pxi", line 45, in
gurobipy.Env.__init__ (../../src/python/gurobipy.c:7673)
gurobipy.GurobiError: No Gurobi license found (user sp2668, host iai-
esm002, hostid 452b2088, cores 1)
---------------------------------------------------------------------------
ApplicationError Traceback (most recent call last)
<ipython-input-35-19c1296f410d> in <module>()
----> 1 network.lopf(solver_name="gurobi")
~/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, solver_options, keep_files, formulation, ptdf_tolerance, free_memory)
1546 return network_lopf_solve(network, snapshots, formulation=formulation,
1547 solver_options=solver_options,
-> 1548 keep_files=keep_files, free_memory=free_memory)
~/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, keep_files, free_memory)
1463 network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options)
1464 else:
-> 1465 network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options)
1466
1467 if logger.isEnabledFor(logging.INFO):
~/software/anaconda3/envs/esm-tutorials/lib/python3.6/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
624 logger.error("Solver log:\n" + str(_status.log))
625 raise pyutilib.common.ApplicationError(
--> 626 "Solver (%s) did not exit normally" % self.name)
627 solve_completion_time = time.time()
628 if self._report_timing:
ApplicationError: Solver (gurobi) did not exit normally
%% Cell type:markdown id: tags:
Get the results `objective` and `{generators,stores,links}.p_nom_opt` with real availability:
%% Cell type:code id: tags:
``` python
obj_v3 = network.objective / 1e9 # Mio. Euro
obj_v3
```
%% Cell type:code id: tags:
``` python
# (a) Capacities for wind and solar.
res_cap_v3 = network.generators.p_nom_opt / 1e3
res_cap_v3
```
%% Cell type:code id: tags:
``` python
# (b) Store and dispatch power capacity.
sto_cap_v3 = network.links.p_nom_opt / 1e3
sto_cap_v3
```
%% Cell type:code id: tags:
``` python
# (c) Energy capacities
sto_engy_v3 = network.stores.e_nom_opt / 1e6
sto_engy_v3
```
%% Cell type:code id: tags:
``` python
network.stores_t.e.plot()
```
%% Cell type:code id: tags:
``` python
np.cumsum(availability.loc["2012-7"] - availability.loc["2012-7"].mean()).plot()
```
%% Cell type:markdown id: tags:
***
**(f) Compare all results for all three scenarios in terms of total system cost, renewable generation capacity, storage power capacity and storage energy capacity!**
%% Cell type:code id: tags:
``` python
scens = ["without transmission",
"with transmission",
"with real data and transmission"]
attrs = ["storage power capacity",
"storage energy capacity",
"renewable capacity"]
```
%% Cell type:code id: tags:
``` python
sto_cap_v1.name = attrs[0] + " " + scens[0]
sto_cap_v2.name = attrs[0] + " " + scens[1]
sto_cap_v3.name = attrs[0] + " " + scens[2]
res_cap_v1.name = attrs[1] + " " + scens[0]
res_cap_v2.name = attrs[1] + " " + scens[1]
res_cap_v3.name = attrs[1] + " " + scens[2]
sto_engy_v1.name = attrs[2] + " " + scens[0]
sto_engy_v2.name = attrs[2] + " " + scens[1]
sto_engy_v3.name = attrs[2] + " " + scens[2]
```
%% Cell type:code id: tags:
``` python
values = [obj_v1, obj_v2, obj_v3]
plt.bar(scens,values)
plt.ylabel('Mio. Euro')
plt.show()
```
%% Cell type:code id: tags:
``` python
sto_caps = pd.concat([sto_cap_v1,sto_cap_v2,sto_cap_v3], axis=1, sort=False)
sto_caps.plot.bar()
```
%% Cell type:code id: tags:
``` python
res_caps = pd.concat([res_cap_v1,res_cap_v2,res_cap_v3], axis=1, sort=False)
res_caps.plot.bar()
```
%% Cell type:code id: tags:
``` python
sto_engy = pd.concat([sto_engy_v1,sto_engy_v2,sto_engy_v3], axis=1, sort=False)
sto_engy.plot.bar()
```
......
%% Cell type:markdown id: tags:
# Energy System Modelling - Tutorial III
%% Cell type:markdown id: tags:
**Settings**
%% Cell type:code id: tags:
``` python
import pypsa
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
```
%% Cell type:code id: tags:
``` python
%matplotlib inline
```
%% Cell type:code id: tags:
``` python
solver = "glpk"
```
%% Cell type:markdown id: tags:
***
**(a) Build a network in PyPSA with the two buses North and South and attach the load at each bus and attach the wind and solar generators with availability according to $G_{N,w}(t) = Cf_w(1+A_w\sin \omega_w t)$ and $G_{S,s}(t) = Cf_s(1+A_s\sin \omega_s t)$ for a year (you have to call `set_snapshots` for the year) and with `p_nom_extendable` set to `True`. As help you should have a look at the [minimal lopf example](https://www.pypsa.org/examples/minimal_example_lopf.html), understand what the [components documentation](https://pypsa.org/doc/components.html) of PyPSA gives you and that you can find the underlying objective function and constraints in the [LOPF documentation](https://pypsa.org/doc/optimal_power_flow.html#linear-optimal-power-flow).**
> **Remarks:** For time reasons, you do not have to build the network from scratch. However, to get you acquainted with PyPSA we have omitted a few elements or some of their parameters of the network marked by three question marks `???`. Either, you have to add an element similar to the one in the box above or add a few parameters.
%% Cell type:markdown id: tags:
Initialize network
%% Cell type:code id: tags:
``` python
network = pypsa.Network()
```
%% Cell type:markdown id: tags:
Add North and South bus
%% Cell type:code id: tags:
``` python
network.add("Bus",
"North",
carrier="AC")
```
%% Cell type:code id: tags:
``` python
???
```
%% Cell type:markdown id: tags:
Attach constant load
%% Cell type:code id: tags:
``` python
network.add("Load",
"North Load",
bus="North",
p_set=20e3)
```
%% Cell type:code id: tags:
``` python
???
```
%% Cell type:markdown id: tags:
Attach renewable generators according to given parameters
%% Cell type:code id: tags:
``` python
network.set_snapshots(np.arange(0, 4*7*24))
```
%% Cell type:code id: tags:
``` python
Cfw = 0.3
Aw = 0.9
omegaw = 2*np.pi/(7*24)
Cfs = 0.12
As = 1.
omegas = 2*np.pi/24
GNwt = Cfw * (1+Aw*np.sin(omegaw*network.snapshots.to_series()))
GSst = Cfs * (1+As*np.sin(omegas*network.snapshots.to_series()))
```
%% Cell type:code id: tags:
``` python
pd.concat([GNwt, GSst], keys=['wind', 'solar'], axis=1).loc[:4*7*24].plot()
```
%% Output
<matplotlib.axes._subplots.AxesSubplot at 0x7f38a43f4c88>
%% Cell type:code id: tags:
``` python
network.add("Generator",
"Wind",
bus="North",
p_nom_extendable=True,
capital_cost=1.2e6,
p_max_pu=GNwt)
```
%% Cell type:code id: tags:
``` python
???
```
%% Cell type:markdown id: tags:
***
**(b) Attach extendable storages at the North and the South! The storages have to be modelled as an `H2-bus` (a bus with `carrier='H2'`) linked to the `AC-bus` North with a `Link` where `p_nom_extendable=True` with the `capital_cost` of the power capacity and an also extendable `Store` with the `capital_cost` of the energy capacity, for instance. The losses can be set on the links as `efficiency`.**
**(b) Attach extendable storage units at the North and the South! The storages have to be modelled as an `H2-bus` (a bus with `carrier='H2'`) linked to the `AC-bus` North with a `Link` where `p_nom_extendable=True` with the `capital_cost` of the power capacity and an also extendable `Store` with the `capital_cost` of the energy capacity, for instance. The losses can be set on the links as `efficiency`.**
%% Cell type:code id: tags:
``` python
for bus in ["North", "South"]:
# H2 storage
network.add("Bus",
bus + " H2",
???
)
network.add("Store",
bus + " H2 St.",
bus=bus + " H2",
???
)
network.add("Link",
bus + "->H2",
bus0=bus, bus1=bus + " H2",
???
)
network.add("Link",
"H2->" + bus,
bus0=bus + " H2", bus1=bus,
???
)
# Battery storage
network.add("Bus",
bus + " Battery",
???
)
network.add("Store",
bus + " Battery St.",
bus=bus + " Battery",
???
)
network.add("Link",
bus + "<->Battery",
bus0=bus, bus1=bus + " Battery",
???
)
```
%% Cell type:markdown id: tags:
***
**(c) Run an investment optimization by calling the `lopf` function.**
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
***
**(d) How do your results `objective` and `{generators,stores,links}.p_nom_opt` compare with the results of III.1(d)?**
%% Cell type:markdown id: tags:
Objective value
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Capacities for wind and solar.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Store and dispatch power capacity.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Energy capacities.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Plot the storage energy states over time
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
***
**(e) Now we lift the restriction against transmission and allow North and South to bridge their 500 km
separation with a transmission line. How does the cost optimal technology mix change?**
%% Cell type:markdown id: tags:
Add extendable link between North and South:
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Run LOPF:
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Get the results `objective` and `{generators,stores,links}.p_nom_opt`:
%% Cell type:markdown id: tags:
Objective value
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Capacities for wind and solar.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Store and dispatch power capacity.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Energy capacities.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Plot the storage energy states over time
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
***
**(e) Replace the approximated availability time-series of the wind and the solar generators with the ones from `availability.csv` computed from reanalysis weather data available on the [course website](https://nworbmot.org/courses/complex_renewable_energy_networks/) and re-run the LOPF. Compare the results! Explain the differences by looking at the cumulative variations relative to the mean of the availability time-series!**
**(e) Replace the approximated availability time-series of the wind and the solar generators with the ones from `availability.csv` computed from reanalysis weather data and re-run the LOPF. Compare the results! Explain the differences by looking at the cumulative variations relative to the mean of the availability time-series!**
%% Cell type:markdown id: tags:
Adapt the network to new availabiltiy data:
%% Cell type:code id: tags:
``` python
network.remove("Generator", "Wind")
network.remove("Generator", "Solar")
```
%% Cell type:code id: tags:
``` python
availability = pd.read_csv("availability.csv", index_col=0, parse_dates=True)
availability.head()
```
%% Output
solar wind
name
2012-01-01 00:00:00 0.0 0.402412
2012-01-01 01:00:00 0.0 0.480648
2012-01-01 02:00:00 0.0 0.542354
2012-01-01 03:00:00 0.0 0.586046
2012-01-01 04:00:00 0.0 0.641201
%% Cell type:markdown id: tags:
Set snapshots of the network using the availability time series for July 2012.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Add wind generator with availability time series
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Add solar generator with availability time series
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Run LOPF:
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Get the results `objective` and `{generators,stores,links}.p_nom_opt` with real availability:
%% Cell type:markdown id: tags:
Objective value
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Capacities for wind and solar.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Store and dispatch power capacity.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Energy capacities.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Plot the storage energy states over time
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
Explain the differences by looking at the cumulative variations relative to the mean of the availability time-series.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
***
**(f) Compare all results for all three scenarios in terms of total system cost, renewable generation capacity, storage power capacity and storage energy capacity!**
> **Remark:** For example, you can use bar charts `plt.bar(...)` to visualize the differences.
%% Cell type:code id: tags:
``` python
```
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment