Commit 27b4a8b7 authored by Fabian Neumann's avatar Fabian Neumann
Browse files

update for summer term 2019 session

parent 379abd90
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -85,18 +85,29 @@
%=====================================================================
\begin{document}
\begin{flushright}
\textbf{Energy System Modelling }\\
{\small Karlsruhe Institute of Technology}\\
{\small Institute for Automation and Applied Informatics}\\
{\small Summer Term 2019}\\
\end{flushright}
\vspace{-0.5em}
\hrulefill
\vspace{0.3em}
\begin{center}
\textbf{\Large Energy System Modelling }\\
{SS 2018, Karlsruhe Institute of Technology}\\
{Institute for Automation and Applied Informatics}\\ [1em]
\textbf{\textsc{\Large Solutions to Tutorial III}}\\
\small Will be worked on in the exercise session on Monday, 16 July 2018.\\[1.5em]
\textbf{\textsc{\Large Solutions III: Storage Optimisation}}\\
\small Will be worked on in the exercise session on Thursday, 13 June 2019.\\[1.5em]
\end{center}
\vspace{1em}
\vspace{-0.5em}
\hrulefill
\vspace{0.8em}
%=============== ======================================================
\paragraph{Solution III.1 \normalsize (storage adequacy).}~\\
\paragraph{Problem III.1 (analytical) -- storage optimisation without losses}~\\
%=====================================================================
\begin{wrapfigure}[11]{r}{0pt}
......@@ -118,30 +129,28 @@
\label{fig:variations}
\end{wrapfigure}
Imagine a two-node Germany. The South can install solar panels with a capacity factor $Cf_s$ to cover its load $L^S$, while the North uses wind turbines that have a capacity factor $Cf_w$
Imagine a two-node Germany. The South can install solar panels with a capacity factor $c_s$ to cover its load $L^S$, while the North uses wind turbines that have a capacity factor $c_w$
to feed their load $L^N$. Figure \ref{fig:variations} shows approximations to the daily and synoptic variations of per-unit wind and solar power generation \(G^{N}_{w}(t)\) and \(G^{S}_{s}(t)\) and a constant load \(L^{N/S}(t)\):
\vspace{-0.5em}
\begin{align*}
G_{w}^N(t) & = Cf_w(1+A_w \sin \omega_w t), \\
G_{s}^S(t) & = Cf_s(1+A_s \sin \omega_s t), \\
g_{w}^N(t) & = c_w(1+A_w \sin \omega_w t), \\
g_{s}^S(t) & = c_s(1+A_s \sin \omega_s t), \\
L^{N/S}(t) & = A_{l}^{N/S}.
\end{align*}
The capacity factors and constants are
\vspace{-0.25em}
\begin{align*}
A_{l}^{N} & = 20 \si{\giga\watt}, & A_{l}^{S} & = 30 \si{\giga\watt}, \\
Cf_w & = 0.3, & A_w & = 0.9, & \omega_w & = \frac{2\pi}{7 \text{d}}, \\
Cf_s & = 0.12, & A_s & = 1.0, & \omega_s & = \frac{2\pi}{1 \text{d}}. \\
c_w & = 0.3, & A_w & = 0.9, & \omega_w & = \frac{2\pi}{7 \text{d}}, \\
c_s & = 0.12, & A_s & = 1.0, & \omega_s & = \frac{2\pi}{1 \text{d}}.
\end{align*}
\vspace{-0.3em}
For now, assume the stores are lossless. Losses will be considered in Problem III.2.
For now, assume no power exchange between the regions and that the stores are lossless.
\begin{enumerate}[(a)]
% (a)
\begin{shaded}\item How much wind capacity $G^{N}_{w}$ must be installed in the North and solar capacity $G_s^S$ in the South?\end{shaded}
\begin{shaded}\item How much wind capacity $G^{N}_{w}$ must be installed in the North and solar capacity $G_s^S$ in the South so that on average generation matches demand?
?\end{shaded}
In the North:
......@@ -164,7 +173,7 @@ For now, assume the stores are lossless. Losses will be considered in Problem II
$$\Rightarrow \quad G^S_s = \frac{A^S_l}{Cf_s} = \frac{30\si{\giga\watt}}{0.12} = \rd{\res}\si{\giga\watt}$$
% (b)
\begin{shaded}\item What is the amount of store and dispatch power capacity $G_{st,store}=\max(-\Delta(t))$ and $G_{st,dispatch} = \max \Delta(t)$ the storage units must have in the North and in the South to account for the mismatch $\Delta(t)=L(t)-G_{w/s}\cdot G_{w/s}(t)$?\end{shaded}
\begin{shaded}\item For a system to work, generation must match demand not on average but at each location and each point in time. Storages are one way of ensuring this constraint with variable renewable generation. What is the amount of store and dispatch power capacity $G_{st,store}=\max(-\Delta(t))$ and $G_{st,dispatch} = \max \Delta(t)$ the storage units must have in the North and in the South to account for the mismatch $\Delta(t)=L(t)-G_{w/s}\cdot g_{w/s}(t)$?\end{shaded}
In the North:
......@@ -339,12 +348,6 @@ For now, assume the stores are lossless. Losses will be considered in Problem II
\end{align*}
the system cost could be reduced by approx.\ 7 \%.
%=============== ======================================================
\paragraph{Solution III.2 \normalsize (storage optimisation with PyPSA).}~\\
%=====================================================================
cf. Jupyter Notebook
\end{enumerate}
......
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Simple electricity market examples\n",
"\n",
"It gradually builds up more and more complicated energy-only electricity markets in PyPSA, starting from a single bidding zone, going up to multiple bidding zones connected with transmission (NTCs) along with variable renewables and storage.\n",
"\n",
"Available as a Jupyter notebook at http://www.pypsa.org/examples/simple-electricity-market-examples.ipynb."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Preliminaries\n",
"\n",
"Here libraries are imported and data is defined."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import pypsa, numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"#marginal costs in EUR/MWh\n",
"marginal_costs = {\"Wind\" : 0,\n",
" \"Hydro\" : 0,\n",
" \"Solar\" : 0,\n",
" \"Coal\" : 30,\n",
" \"Gas\" : 60,\n",
" \"Oil\" : 80}\n",
"\n",
"#capital costs in EUR/kW\n",
"capital_costs = {\"Wind\" : 10000,\n",
" \"Hydro\" : 3000,\n",
" \"Solar\" : 6000,\n",
" \"Coal\" : 35000,\n",
" \"Gas\" : 8000,\n",
" \"Oil\" : 20000}\n",
"\n",
"#power plant capacities (nominal powers in MW) in each country (not necessarily realistic)\n",
"power_plant_p_nom = {\"South Africa\" : {\"Coal\" : 35000,\n",
" \"Wind\" : 3000,\n",
" \"Gas\" : 8000,\n",
" \"Oil\" : 2000\n",
" },\n",
" \"Mozambique\" : {\"Hydro\" : 1200,\n",
" },\n",
" \"Swaziland\" : {\"Hydro\" : 600,\n",
" },\n",
" }\n",
"\n",
"#transmission capacities in MW (not necessarily realistic)\n",
"transmission = {\"South Africa\" : {\"Mozambique\" : 500,\n",
" \"Swaziland\" : 250},\n",
" \"Mozambique\" : {\"Swaziland\" : 100}}\n",
"\n",
"#country electrical loads in MW (not necessarily realistic)\n",
"loads = {\"South Africa\" : 42000,\n",
" \"Mozambique\" : 650,\n",
" \"Swaziland\" : 250}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Three bidding zones connected by transmission, one period\n",
"\n",
"In this example we have bidirectional transmission capacity between three bidding zones. The power transfer is treated as controllable (like an A/NTC (Available/Net Transfer Capacity) or HVDC line). Note that in the physical grid, power flows passively according to the network impedances."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"network = pypsa.Network()\n",
"\n",
"countries = [\"Swaziland\", \"Mozambique\", \"South Africa\"]\n",
"\n",
"for country in countries:\n",
" \n",
" network.add(\"Bus\",country)\n",
"\n",
" for tech in power_plant_p_nom[country]:\n",
" network.add(\"Generator\",\n",
" \"{} {}\".format(country,tech),\n",
" bus=country,\n",
" p_nom=power_plant_p_nom[country][tech],\n",
" marginal_cost=marginal_costs[tech])\n",
"\n",
"\n",
" network.add(\"Load\",\n",
" \"{} load\".format(country),\n",
" bus=country,\n",
" p_set=loads[country])\n",
" \n",
" #add transmission as controllable Link\n",
" if country not in transmission:\n",
" continue\n",
" \n",
" for other_country in countries:\n",
" if other_country not in transmission[country]:\n",
" continue\n",
" \n",
" #NB: Link is by default unidirectional, so have to set p_min_pu = -1\n",
" #to allow bidirectional (i.e. also negative) flow\n",
" network.add(\"Link\",\n",
" \"{} - {} link\".format(country, other_country),\n",
" bus0=country,\n",
" bus1=other_country,\n",
" p_nom=transmission[country][other_country],\n",
" p_min_pu=-1)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:pypsa.pf:Slack bus for sub-network 0 is Swaziland\n",
"INFO:pypsa.pf:Slack bus for sub-network 1 is Mozambique\n",
"INFO:pypsa.pf:Slack bus for sub-network 2 is South Africa\n",
"INFO:pypsa.opf:Performed preliminary steps\n",
"INFO:pypsa.opf:Building pyomo model using `angles` formulation\n",
"/home/ws/sp2668/software/anaconda3/lib/python3.6/site-packages/pypsa/components.py:758: FutureWarning:\n",
"\n",
"Sorting because non-concatenation axis is not aligned. A future version\n",
"of pandas will change to not sort by default.\n",
"\n",
"To accept the future behavior, pass 'sort=True'.\n",
"\n",
"To retain the current behavior and silence the warning, pass sort=False\n",
"\n",
"\n",
"INFO:pypsa.opf:Solving model using glpk\n",
"INFO:pypsa.opf:Optimization successful\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"# ==========================================================\n",
"# = Solver Results =\n",
"# ==========================================================\n",
"# ----------------------------------------------------------\n",
"# Problem Information\n",
"# ----------------------------------------------------------\n",
"Problem: \n",
"- Name: unknown\n",
" Lower bound: 1245000.0\n",
" Upper bound: 1245000.0\n",
" Number of objectives: 1\n",
" Number of constraints: 13\n",
" Number of variables: 13\n",
" Number of nonzeros: 22\n",
" Sense: minimize\n",
"# ----------------------------------------------------------\n",
"# Solver Information\n",
"# ----------------------------------------------------------\n",
"Solver: \n",
"- Status: ok\n",
" Termination condition: optimal\n",
" Statistics: \n",
" Branch and bound: \n",
" Number of bounded subproblems: 0\n",
" Number of created subproblems: 0\n",
" Error rc: 0\n",
" Time: 0.01071619987487793\n",
"# ----------------------------------------------------------\n",
"# Solution Information\n",
"# ----------------------------------------------------------\n",
"Solution: \n",
"- number of solutions: 0\n",
" number of solutions displayed: 0\n"
]
},
{
"data": {
"text/plain": [
"('ok', 'optimal')"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"network.lopf()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Swaziland load</th>\n",
" <th>Mozambique load</th>\n",
" <th>South Africa load</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>now</th>\n",
" <td>250.0</td>\n",
" <td>650.0</td>\n",
" <td>42000.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Swaziland load Mozambique load South Africa load\n",
"now 250.0 650.0 42000.0"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"network.loads_t.p"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Swaziland Hydro</th>\n",
" <th>Mozambique Hydro</th>\n",
" <th>South Africa Coal</th>\n",
" <th>South Africa Wind</th>\n",
" <th>South Africa Gas</th>\n",
" <th>South Africa Oil</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>now</th>\n",
" <td>600.0</td>\n",
" <td>1050.0</td>\n",
" <td>35000.0</td>\n",
" <td>3000.0</td>\n",
" <td>3250.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Swaziland Hydro Mozambique Hydro South Africa Coal South Africa Wind \\\n",
"now 600.0 1050.0 35000.0 3000.0 \n",
"\n",
" South Africa Gas South Africa Oil \n",
"now 3250.0 0.0 "
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"network.generators_t.p"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Mozambique - Swaziland link</th>\n",
" <th>South Africa - Swaziland link</th>\n",
" <th>South Africa - Mozambique link</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>now</th>\n",
" <td>-100.0</td>\n",
" <td>-250.0</td>\n",
" <td>-500.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Mozambique - Swaziland link South Africa - Swaziland link \\\n",
"now -100.0 -250.0 \n",
"\n",
" South Africa - Mozambique link \n",
"now -500.0 "
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"network.links_t.p0"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Swaziland</th>\n",
" <th>Mozambique</th>\n",
" <th>South Africa</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>now</th>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>60.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Swaziland Mozambique South Africa\n",
"now 0.0 0.0 60.0"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#print the clearing price (corresponding to hydro in S and M, and gas in SA)\n",
"network.buses_t.marginal_price"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Mozambique - Swaziland link</th>\n",
" <th>South Africa - Swaziland link</th>\n",
" <th>South Africa - Mozambique link</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>now</th>\n",
" <td>0.0</td>\n",
" <td>60.0</td>\n",
" <td>60.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Mozambique - Swaziland link South Africa - Swaziland link \\\n",
"now 0.0 60.0 \n",
"\n",
" South Africa - Mozambique link \n",
"now 60.0 "
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#link shadow prices\n",
"network.links_t.mu_lower"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [default]",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",