You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Mooi-Kickstart/Kickstart/V2/Flex-heat-pump5.ipynb

4456 lines
120 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"#### Imports"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [
{
"data": {
"text/html": [
" <script type=\"text/javascript\">\n",
" window.PlotlyConfig = {MathJaxConfig: 'local'};\n",
" if (window.MathJax && window.MathJax.Hub && window.MathJax.Hub.Config) {window.MathJax.Hub.Config({SVG: {font: \"STIX-Web\"}});}\n",
" if (typeof require !== 'undefined') {\n",
" require.undef(\"plotly\");\n",
" requirejs.config({\n",
" paths: {\n",
" 'plotly': ['https://cdn.plot.ly/plotly-2.32.0.min']\n",
" }\n",
" });\n",
" require(['plotly'], function(Plotly) {\n",
" window._Plotly = Plotly;\n",
" });\n",
" }\n",
" </script>\n",
" "
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\__init__.py\n"
]
}
],
"source": [
"from datetime import timedelta, datetime\n",
"import json\n",
"import pprint\n",
"from copy import deepcopy\n",
"import pytz\n",
"\n",
"import cufflinks\n",
"cufflinks.go_offline()\n",
"import numpy as np\n",
"from numpy.polynomial import Polynomial\n",
"import pandas as pd\n",
"from tqdm.notebook import tqdm\n",
"\n",
"import sys\n",
"\n",
"import os\n",
" \n",
"# Path to the folder containing the alternate version of PyRecoy\n",
"\n",
"alternate_pyrecoy_path = 'C:\\\\Users\\\\z004yn8c\\\\OneDrive - Siemens AG\\\\Documents\\\\GitHub\\\\Mooi-Kickstart\\\\pyrecoy\\\\pyrecoy'\n",
" \n",
"# Add the path to sys.path\n",
"\n",
"if alternate_pyrecoy_path not in sys.path:\n",
"\n",
" sys.path.insert(0, alternate_pyrecoy_path)\n",
" \n",
"# Now import PyRecoy\n",
"\n",
"import pyrecoy2\n",
"\n",
"# Check the version or path to confirm\n",
"\n",
"print(pyrecoy2.__file__)\n",
"\n",
"\n",
"from pyrecoy2.assets_kickstart import Heatpump, Eboiler, GasBoiler, HotWaterStorage\n",
"from pyrecoy2.colors import *\n",
"from pyrecoy2.converters import *\n",
"from pyrecoy2.financial import calculate_eb_ode, get_tax_tables, get_tax_rate, get_grid_tariffs_electricity\n",
"from pyrecoy2.framework import TimeFramework\n",
"from pyrecoy2.casestudy import CaseStudy\n",
"from pyrecoy2.plotting import ebitda_bar_chart, npv_bar_chart\n",
"from pyrecoy2.reports import CaseReport, ComparisonReport, BusinessCaseReport, SingleFigureComparison\n",
"from pyrecoy2.sensitivity import SensitivityAnalysis\n",
"from pyrecoy2.prices import get_tennet_data, get_afrr_capacity_fees_nl\n",
"from pyrecoy2.forecasts import Mipf, Forecast\n",
"\n",
"%load_ext autoreload\n",
"%autoreload 2"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"#### Development backlog"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"* aFRR (can improve the optimisation case)\n",
"* Report\n",
"* COP seems to have fix values, rather than COP curve"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"#### Meeting Notes"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"##### Meeting 25-11-2020"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"* aFRR can help optimisation case\n",
"* SDE++ should be included\n",
"* Tsource sensitivity really gives interesting insights\n",
"* Sensitivities should be verified (especially CO2, Tsink, Tsource, time period)\n",
"* AP TNO: Update/verify COP curve\n",
"* AP TNO: Update CAPEX\n",
"* AP Mark: \n",
" * Create graphs on COP curve with different Tsource, Tsink\n",
" * Generate table and output in .csv (send it to Andrew)\n",
"* Investigate opportunity to lower the COP and negative electricity prices\n",
" * Technically feasible, but not really needed/possible to do it in this project\n",
"* Could be interesting to run this model on a usecase with higher delta T\n",
"* Conclusion: Finalize this model, but not add too much extra complexity, next steps is to go towards industrial partners with the results"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"# Kickstart: Heatpump Optimisation Framework\n",
"\n",
"***\n",
"© Shahla Huseynova \n",
"July 2024\n",
"##### Cases\n",
"In this model, 4 cases are compared:\n",
"* **Baseline** : All heat is supplied by steam turbine\n",
"* **Heatpump case** : All heat is supplied by heatpump\n",
"* **Hybrid case** : Steam boiler and heatpump run in hybrid mode, and are optimised on costs in real-time\n",
"* **Heat pump & storage cases**: Heat pump only case is compared with different setup of storage +heat pump cases"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"#### Loading config"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"class Config():\n",
" start = '2023-01-01'\n",
" end = '2023-12-31'\n",
" # year = '2019'\n",
" # startdate = year + '-01-01'\n",
" # enddate = str(year) + '-12-31'\n",
" # start = datetime.strptime(startdate, \"%Y-%m-%d\").astimezone(pytz.timezone('Europe/Amsterdam'))\n",
" # end = datetime.strptime(enddate, \"%Y-%m-%d\").astimezone(pytz.timezone('Europe/Amsterdam'))\n",
" # start = start.astimezone(pytz.UTC)\n",
" # end = end.astimezone(pytz.UTC)\n",
" \n",
" hp_e_power = 1.5 # MW\n",
" # hp_ndg_e_power = 7.7 # MW\n",
" hp_min_load = 0\n",
" hp_lifetime = 25\n",
" hp_capex = 200_000 # EUR/MWth\n",
" hp_opex = 0.01 # in % of CAPEX\n",
" hp_devex = 0.005 # in % of CAPEX\n",
" \n",
" gb_power = 5 # MW\n",
" gb_efficiency = 0.9\n",
" \n",
" storage_power = 1\n",
" # storage_volume = 1000 *3/50\n",
" # storage_cap_per_volume = 50 * 1e-3\n",
" rated_capacity = 3\n",
" storage_lifetime = 25\n",
" storage_temperature = 144\n",
" # storage_min_level = storage_volume * storage_cap_per_volume * 0.05\n",
" storage_min_level = rated_capacity * 0.00\n",
" # storage_min_level = rated_capacity\n",
" storage_capex_per_MW = 60_000\n",
" storage_capex_per_MWh = 0\n",
" storage_opex_perc_of_capex = 0.02\n",
" # storage_initial_level = 5\n",
" threshold = 0\n",
" \n",
" tax_bracket_g = 4 \n",
" tax_bracket_e = 4\n",
" \n",
" include_transport_costs = False\n",
" grid_operator = 'Liander'\n",
" connection_type = 'TS/MS'\n",
" \n",
" discount_rate = 0.1\n",
" project_duration = 12\n",
"\n",
" forecast = 'ForeNeg'\n",
" gas_price_multiplier = 1\n",
" e_price_multiplier = 1\n",
" e_price_volatility_multiplier = 1\n",
" co2_price_multiplier = 1\n",
" tsource_delta = 0\n",
" tsink_delta = 0\n",
" energy_tax_multiplier = 1\n",
" \n",
" # Review the SDE implementation\n",
" sde_base_amount = 81\n",
" longterm_gas_price = 24.00\n",
" longterm_co2_price = 37.90\n",
" sde_switch_price_correction = 40\n",
" \n",
" day_ahead_buying_perc = 0.3\n",
" \n",
" afrr_capacity_fee = 25_000\n",
"\n",
"c = Config()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"class Store():\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Model set-up"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"# Define the file paths\n",
"excel_file_path = r'C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\Kickstart\\V2\\Demand_Data_Smurfit_Preprocessed3.xlsx'\n",
"output_directory = r'C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\Kickstart\\V2\\data'\n",
"output_file_name = 'smurfit_demand_preprocessed3.csv'\n",
"csv_file_path = os.path.join(output_directory, output_file_name)\n",
"\n",
"df = pd.read_excel(excel_file_path)\n",
"# df = df.fillna(0) \n",
"\n",
"# # Save the DataFrame to CSV\n",
"df.to_csv(csv_file_path, index=False)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def load_demand_data(c, s):\n",
" demand = pd.read_csv('data/smurfit_demand_preprocessed3.csv', delimiter=',', decimal='.')\n",
" dt_index = pd.date_range(\n",
" start=s.time_fw.start,\n",
" end=s.time_fw.start + timedelta(days=365), \n",
" freq='1T',\n",
" tz='Europe/Amsterdam')\n",
" dt_index = dt_index[:len(demand)]\n",
" demand.index = dt_index\n",
" demand['Total demand'] = demand['MW (VDG)']\n",
" \n",
" demand = demand[c.start:c.end]\n",
" return demand"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"s = Store()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"testetss\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\framework.py:41: UserWarning:\n",
"\n",
"The chosen timeperiod spans 365.99930555555557 days, which is not a full year. Beware that certain functions that use yearly rates might return incorrect values.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\forecasts.py:62: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\forecasts.py:115: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\framework.py:54: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\framework.py:54: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\framework.py:54: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\AppData\\Local\\Temp\\ipykernel_7920\\2484316097.py:3: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n"
]
}
],
"source": [
"def setup_model(c):\n",
" s = Store()\n",
" \n",
" s.time_fw = TimeFramework(start=c.start, end=c.end)\n",
" # s.minute_ix = s.time_fw.dt_index(freq='1T')\n",
" mipf = Mipf(None, start=s.time_fw.start, end=s.time_fw.end, from_database=True, add_days_to_start_end=False).data\n",
" s.mipf = mipf\n",
" s.baseline = CaseStudy(time_fw=s.time_fw, freq='1T', name='Baseline')\n",
" s.hpcase = CaseStudy(time_fw=s.time_fw, freq='1T', name='Heatpump only', data=mipf)\n",
" #s.hpcase_sde = CaseStudy(time_fw=s.time_fw, freq='1T', name='Heatpump + SDE', data=mipf)\n",
" #s.optcase1 = CaseStudy(time_fw=s.time_fw, freq='1T', name='Optimisation', data=mipf)\n",
" s.storage_case_PCM = CaseStudy(time_fw=s.time_fw, freq='1T', name='Heatpump + Storage_PCM', data=mipf)\n",
" # s.storage_case_TCM = CaseStudy(time_fw=s.time_fw, freq='1T', name='Heatpump + Storage_TCM', data=mipf)\n",
" # s.storage_case_battery = CaseStudy(time_fw=s.time_fw, freq='1T', name='Heatpump + Storage_battery', data=mipf)\n",
" s.cases = list(CaseStudy.instances.values())\n",
" s.optcases = [s.hpcase, s.storage_case_PCM]\n",
" s.sde_cases = []\n",
" \n",
" s.demand = load_demand_data(c, s)\n",
" return s\n",
"\n",
"s = setup_model(c)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"# s.demand.resample('15T').mean()[['Tsource (VDG)', 'Tsink (VDG)']].iplot()"
]
},
{
"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>Tsource (VDG)</th>\n",
" <th>Tsink (VDG)</th>\n",
" <th>MW (VDG)</th>\n",
" <th>Total demand</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2023-01-01 00:00:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:01:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:02:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:03:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:04:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:55:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:56:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:57:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:58:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:59:00+01:00</th>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>525600 rows × 4 columns</p>\n",
"</div>"
],
"text/plain": [
" Tsource (VDG) Tsink (VDG) MW (VDG) Total demand\n",
"2023-01-01 00:00:00+01:00 70 144 1 1\n",
"2023-01-01 00:01:00+01:00 70 144 1 1\n",
"2023-01-01 00:02:00+01:00 70 144 1 1\n",
"2023-01-01 00:03:00+01:00 70 144 1 1\n",
"2023-01-01 00:04:00+01:00 70 144 1 1\n",
"... ... ... ... ...\n",
"2023-12-31 23:55:00+01:00 70 144 1 1\n",
"2023-12-31 23:56:00+01:00 70 144 1 1\n",
"2023-12-31 23:57:00+01:00 70 144 1 1\n",
"2023-12-31 23:58:00+01:00 70 144 1 1\n",
"2023-12-31 23:59:00+01:00 70 144 1 1\n",
"\n",
"[525600 rows x 4 columns]"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s.demand"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Load in data"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def increase_volatility_by_factor(col, factor):\n",
" mean = col.mean()\n",
" diff_to_mean = col - mean\n",
" new_diff = diff_to_mean * factor\n",
" return mean + new_diff\n",
"\n",
"def multiply_by_factor(col, factor):\n",
" mean = col.mean()\n",
" diff_to_mean = col - mean\n",
" \n",
" cond = diff_to_mean > 0\n",
" diff_to_mean[cond] *= factor\n",
" diff_to_mean[~cond] /= factor\n",
" return mean + diff_to_mean"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\prices.py:531: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\prices.py:443: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\prices.py:531: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\prices.py:443: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\prices.py:531: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n",
"C:\\Users\\z004yn8c\\OneDrive - Siemens AG\\Documents\\GitHub\\Mooi-Kickstart\\pyrecoy\\pyrecoy\\pyrecoy2\\prices.py:443: FutureWarning:\n",
"\n",
"'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
"\n"
]
}
],
"source": [
"def load_data(c, s):\n",
" \n",
" for case in s.cases:\n",
" case.add_gasprices()\n",
" case.add_co2prices(perMWh=True)\n",
" \n",
" case.data['Gas prices (€/MWh)'] *= c.gas_price_multiplier\n",
" case.data['CO2 prices (€/MWh)'] *= c.co2_price_multiplier\n",
" case.data['CO2 prices (€/ton)'] *= c.co2_price_multiplier\n",
" \n",
" for case in s.optcases:\n",
" case.data['NEG'] = multiply_by_factor(case.data['NEG'], c.e_price_multiplier)\n",
" case.data['ForeNeg'] = multiply_by_factor(case.data['ForeNeg'], c.e_price_multiplier)\n",
" case.data['DAM'] = multiply_by_factor(case.data['DAM'], c.e_price_multiplier)\n",
" \n",
" case.data['NEG'] = increase_volatility_by_factor(case.data['NEG'], c.e_price_volatility_multiplier)\n",
" case.data['ForeNeg'] = increase_volatility_by_factor(case.data['ForeNeg'], c.e_price_volatility_multiplier)\n",
"\n",
"\n",
" s.demand['Tsource (VDG)'] += c.tsource_delta\n",
" s.demand['Tsink (VDG)'] += c.tsink_delta\n",
" for case in s.cases:\n",
" case.data = pd.concat([case.data, s.demand], axis=1) \n",
"\n",
" s.eb_ode_g = get_tax_rate('gas', 2020, 4)['EB+ODE'] * c.energy_tax_multiplier\n",
" s.eb_ode_e = get_tax_rate('electricity', 2020, 4)['EB+ODE'] * c.energy_tax_multiplier\n",
" s.grid_fees = get_grid_tariffs_electricity(c.grid_operator, 2020, c.connection_type)\n",
" s.grid_fee_per_MWh = s.grid_fees['kWh tarief'] * 1000\n",
" return s\n",
"\n",
"s = load_data(c, s)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"75.08224016788466"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s.hpcase.data['CO2 prices (€/ton)'].mean()"
]
},
{
"cell_type": "code",
"execution_count": 23,
"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>DAM</th>\n",
" <th>POS</th>\n",
" <th>NEG</th>\n",
" <th>ForeNeg</th>\n",
" <th>ForePos</th>\n",
" <th>Gas prices (€/MWh)</th>\n",
" <th>CO2 prices (€/ton)</th>\n",
" <th>CO2 prices (€/MWh)</th>\n",
" <th>Tsource (VDG)</th>\n",
" <th>Tsink (VDG)</th>\n",
" <th>MW (VDG)</th>\n",
" <th>Total demand</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2023-01-01 00:00:00+01:00</th>\n",
" <td>-3.61</td>\n",
" <td>-209.40</td>\n",
" <td>-209.40</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>63.998</td>\n",
" <td>5.23</td>\n",
" <td>1.01</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:01:00+01:00</th>\n",
" <td>-3.61</td>\n",
" <td>-209.40</td>\n",
" <td>-209.40</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>63.998</td>\n",
" <td>5.23</td>\n",
" <td>1.01</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:02:00+01:00</th>\n",
" <td>-3.61</td>\n",
" <td>-209.40</td>\n",
" <td>-209.40</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>63.998</td>\n",
" <td>5.23</td>\n",
" <td>1.01</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:03:00+01:00</th>\n",
" <td>-3.61</td>\n",
" <td>-209.40</td>\n",
" <td>-209.40</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>63.998</td>\n",
" <td>5.23</td>\n",
" <td>1.01</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-01-01 00:04:00+01:00</th>\n",
" <td>-3.61</td>\n",
" <td>-209.40</td>\n",
" <td>-209.40</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>63.998</td>\n",
" <td>5.23</td>\n",
" <td>1.01</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:55:00+01:00</th>\n",
" <td>3.17</td>\n",
" <td>-6.81</td>\n",
" <td>-6.81</td>\n",
" <td>-7.10</td>\n",
" <td>-11.16</td>\n",
" <td>29.927</td>\n",
" <td>77.25</td>\n",
" <td>14.90</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:56:00+01:00</th>\n",
" <td>3.17</td>\n",
" <td>-6.81</td>\n",
" <td>-6.81</td>\n",
" <td>-5.21</td>\n",
" <td>-8.12</td>\n",
" <td>29.927</td>\n",
" <td>77.25</td>\n",
" <td>14.90</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:57:00+01:00</th>\n",
" <td>3.17</td>\n",
" <td>-6.81</td>\n",
" <td>-6.81</td>\n",
" <td>-5.61</td>\n",
" <td>-7.59</td>\n",
" <td>29.927</td>\n",
" <td>77.25</td>\n",
" <td>14.90</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:58:00+01:00</th>\n",
" <td>3.17</td>\n",
" <td>-6.81</td>\n",
" <td>-6.81</td>\n",
" <td>-5.74</td>\n",
" <td>-7.87</td>\n",
" <td>29.927</td>\n",
" <td>77.25</td>\n",
" <td>14.90</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2023-12-31 23:59:00+01:00</th>\n",
" <td>3.17</td>\n",
" <td>-6.81</td>\n",
" <td>-6.81</td>\n",
" <td>-5.70</td>\n",
" <td>-7.27</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>70</td>\n",
" <td>144</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>525600 rows × 12 columns</p>\n",
"</div>"
],
"text/plain": [
" DAM POS NEG ForeNeg ForePos \\\n",
"2023-01-01 00:00:00+01:00 -3.61 -209.40 -209.40 NaN NaN \n",
"2023-01-01 00:01:00+01:00 -3.61 -209.40 -209.40 NaN NaN \n",
"2023-01-01 00:02:00+01:00 -3.61 -209.40 -209.40 NaN NaN \n",
"2023-01-01 00:03:00+01:00 -3.61 -209.40 -209.40 NaN NaN \n",
"2023-01-01 00:04:00+01:00 -3.61 -209.40 -209.40 NaN NaN \n",
"... ... ... ... ... ... \n",
"2023-12-31 23:55:00+01:00 3.17 -6.81 -6.81 -7.10 -11.16 \n",
"2023-12-31 23:56:00+01:00 3.17 -6.81 -6.81 -5.21 -8.12 \n",
"2023-12-31 23:57:00+01:00 3.17 -6.81 -6.81 -5.61 -7.59 \n",
"2023-12-31 23:58:00+01:00 3.17 -6.81 -6.81 -5.74 -7.87 \n",
"2023-12-31 23:59:00+01:00 3.17 -6.81 -6.81 -5.70 -7.27 \n",
"\n",
" Gas prices (€/MWh) CO2 prices (€/ton) \\\n",
"2023-01-01 00:00:00+01:00 63.998 5.23 \n",
"2023-01-01 00:01:00+01:00 63.998 5.23 \n",
"2023-01-01 00:02:00+01:00 63.998 5.23 \n",
"2023-01-01 00:03:00+01:00 63.998 5.23 \n",
"2023-01-01 00:04:00+01:00 63.998 5.23 \n",
"... ... ... \n",
"2023-12-31 23:55:00+01:00 29.927 77.25 \n",
"2023-12-31 23:56:00+01:00 29.927 77.25 \n",
"2023-12-31 23:57:00+01:00 29.927 77.25 \n",
"2023-12-31 23:58:00+01:00 29.927 77.25 \n",
"2023-12-31 23:59:00+01:00 NaN NaN \n",
"\n",
" CO2 prices (€/MWh) Tsource (VDG) Tsink (VDG) \\\n",
"2023-01-01 00:00:00+01:00 1.01 70 144 \n",
"2023-01-01 00:01:00+01:00 1.01 70 144 \n",
"2023-01-01 00:02:00+01:00 1.01 70 144 \n",
"2023-01-01 00:03:00+01:00 1.01 70 144 \n",
"2023-01-01 00:04:00+01:00 1.01 70 144 \n",
"... ... ... ... \n",
"2023-12-31 23:55:00+01:00 14.90 70 144 \n",
"2023-12-31 23:56:00+01:00 14.90 70 144 \n",
"2023-12-31 23:57:00+01:00 14.90 70 144 \n",
"2023-12-31 23:58:00+01:00 14.90 70 144 \n",
"2023-12-31 23:59:00+01:00 NaN 70 144 \n",
"\n",
" MW (VDG) Total demand \n",
"2023-01-01 00:00:00+01:00 1 1 \n",
"2023-01-01 00:01:00+01:00 1 1 \n",
"2023-01-01 00:02:00+01:00 1 1 \n",
"2023-01-01 00:03:00+01:00 1 1 \n",
"2023-01-01 00:04:00+01:00 1 1 \n",
"... ... ... \n",
"2023-12-31 23:55:00+01:00 1 1 \n",
"2023-12-31 23:56:00+01:00 1 1 \n",
"2023-12-31 23:57:00+01:00 1 1 \n",
"2023-12-31 23:58:00+01:00 1 1 \n",
"2023-12-31 23:59:00+01:00 1 1 \n",
"\n",
"[525600 rows x 12 columns]"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s.storage_case_PCM.data"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Assets"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### COP curve"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def cop_curve_new(Tsink, Tsource):\n",
" Tsink += 273\n",
" Tsource += 273\n",
" Tlift = Tsink - Tsource\n",
"\n",
" c0 = 0.0005426*Tlift**2 - 0.1178*Tlift + 6.962\n",
" c1 = 6.7058 \n",
" c2 = -1.79\n",
" \n",
" return Polynomial([c0, c1, c2])"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [
{
"data": {
"text/latex": [
"$x \\mapsto \\text{19.2496754} + \\text{6.7058}\\,x - \\text{1.79}\\,x^{2}$"
],
"text/plain": [
"Polynomial([19.2496754, 6.7058 , -1.79 ], domain=[-1, 1], window=[-1, 1], symbol='x')"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sourceT = 63 + 273\n",
"sinkT = 140 + 273\n",
"cop_curve_new(sourceT, sinkT)"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Create and assign assets"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def create_and_assign_assets(c, s):\n",
" heatpump = Heatpump(\n",
" name='Heatpump',\n",
" max_th_power=c.hp_e_power,\n",
" min_th_power=c.hp_e_power * c.hp_min_load,\n",
" cop_curve=2.5\n",
" )\n",
"\n",
"\n",
" capex = c.hp_capex*(heatpump.max_th_power) \n",
" heatpump.set_financials(capex=capex, opex=c.hp_opex*capex, devex=c.hp_devex*capex, lifetime=25)\n",
"\n",
" gasboiler = GasBoiler(\n",
" name='Gasboiler',\n",
" max_th_output=c.gb_power,\n",
" efficiency=c.gb_efficiency\n",
" )\n",
" gasboiler.set_financials(capex=0, opex=0, devex=0, lifetime=25)\n",
" \n",
" waterstorage = HotWaterStorage(\n",
" name='HotWaterStorage',\n",
" rated_power=c.storage_power, \n",
" # capacity_per_volume=None,\n",
" # volume=None, \n",
" temperature=c.storage_temperature,\n",
" min_storagelevel=c.storage_min_level,\n",
" # initial_storagelevel=c.storage_initial_level\n",
" )\n",
" capex_ws = c.storage_capex_per_MW * 1 + c.storage_capex_per_MWh * 3\n",
" opex_ws = c.storage_opex_perc_of_capex * capex_ws\n",
" waterstorage.set_financials(capex=capex_ws, opex=opex_ws, devex=0, lifetime=c.storage_lifetime)\n",
" \n",
" s.baseline.add_asset(gasboiler)\n",
" s.hpcase.add_asset(heatpump)\n",
" s.storage_case_PCM.add_asset(heatpump)\n",
" s.storage_case_PCM.add_asset(waterstorage)\n",
" return s\n",
"\n",
"s = create_and_assign_assets(c, s)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"60000"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"capex_ws = c.storage_capex_per_MW * 1 + c.storage_capex_per_MWh * 3\n",
"capex_ws"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"HotWaterStorage(name=HotWaterStorage, rated_power=1, capacity=3, temperature=144, min_storagelevel=0.0)"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s.storage_case_PCM.assets['HotWaterStorage']"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Optimization"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Strategies"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def baseline_sim(case):\n",
" gasboiler = list(case.assets.values())[0]\n",
" data = case.data\n",
" demand = (data['MW (VDG)']).to_list()\n",
"\n",
" minutes = iter(range(len(case.data)))\n",
" th_output = [0] * len(case.data)\n",
" gas_input = [0] * len(case.data)\n",
"\n",
" for m in minutes:\n",
" th_output[m], gas_input[m] = gasboiler.set_heat_output(demand[m])\n",
"\n",
" data['output_MW_th'] = np.array(th_output)\n",
" data['output_MWh_th'] = np.array(data['output_MW_th']/60)\n",
" data['gb_input_MW'] = np.array(gas_input)\n",
" data['gb_input_MWh'] = np.array(data['gb_input_MW']/60)\n",
" case.data = data.round(5)\n",
" return case"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def hponly(case):\n",
" hp = case.assets['Heatpump']\n",
" demand_vdg = case.data['MW (VDG)'].to_list()\n",
" Tsink_vdg = case.data['Tsink (VDG)'].to_list()\n",
" Tsource_vdg = case.data['Tsource (VDG)'].to_list()\n",
"\n",
" hp_input = [0] * len(case.data)\n",
" hp_output = [0] * len(case.data)\n",
"\n",
" minutes = iter(range(len(case.data)))\n",
" for m in minutes:\n",
" demand = demand_vdg[m]\n",
" if demand != 0:\n",
" hp_input[m], hp_output[m] = hp.set_heat_output(\n",
" heat_output=demand - 0.66,\n",
" Tsink=Tsink_vdg[m],\n",
" Tsource=Tsource_vdg[m]\n",
" )\n",
"\n",
" case.data['hp_output_MW'] = np.array(hp_output)\n",
" case.data['hp_input_MW'] = np.array(hp_input)\n",
" case.data['cop'] = case.data['hp_output_MW'] / -case.data['hp_input_MW']\n",
" \n",
" for col in case.data.columns:\n",
" if col.endswith('MW'):\n",
" case.data[col + 'h'] = case.data[col] / 60\n",
"\n",
" case.data = case.data.round(3)\n",
" return case"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
"def hp_storage_opt_enginge(c, ws, hp, pos, neg, dam, demand, tsource_vdg, tsink_vdg):\n",
" \n",
" from_storage_MW = 0\n",
" to_storage_MW = 0 \n",
" \n",
" if neg < dam - c.threshold:\n",
" # overproduce\n",
" if demand_vdg != 0:\n",
" storage_charge_power = ws.charging_power_limit\n",
" if ws.chargelevel == ws.max_chargelevel:\n",
" storage_charge_power = 0\n",
" else:\n",
" # Adjust charge power if charge level is not at max\n",
" storage_charge_power = min(ws.charging_power_limit, (ws.max_chargelevel - ws.chargelevel) * 60)\n",
"\n",
" desired_hp_load = min(demand_vdg + storage_charge_power - 0.66, hp.max_th_power)\n",
" e_load, th_load = hp.set_heat_output(desired_hp_load, tsink_vdg, tsource_vdg)\n",
"\n",
" to_storage_MW = desired_hp_load - demand_vdg + 0.66\n",
" to_storage_MW = -ws.charge(round(to_storage_MW, 3))\n",
" # #Added lately\n",
" # extra_charging_power_constraint = ws.max_power - to_storage_vdg_MW\n",
" # desired_hp_load_new = min(demand_vdg + min(storage_charge_power, extra_charging_power_constraint) - 0.66, hp_vdg.max_th_power)\n",
" # e_load_vdg, th_load_vdg = hp_vdg.set_heat_output(desired_hp_load_new, tsink_vdg, tsource_vdg)\n",
"\n",
" else:\n",
" e_load_, th_load = (0,0)\n",
" \n",
" \n",
"\n",
" elif pos > dam + c.threshold:\n",
" # take from storage\n",
" if demand_vdg != 0:\n",
" from_storage_MW = demand_vdg - 0.66\n",
" from_storage_MW = ws.discharge(round(from_storage_MW, 3))\n",
" # from_storage_vdg_MW = min(ws.max_power - from_storage_vdg_MW, ws.discharging_power_limit)\n",
" desired_hp_load = min(demand_vdg - from_storage_MW - 0.66, hp.max_th_power)\n",
" e_load, th_load = hp.set_heat_output(desired_hp_load, tsink_vdg, tsource_vdg)\n",
" else:\n",
" e_load, th_load = (0,0)\n",
" \n",
" #print({f'from_storage_vdg_MW='})\n",
" else:\n",
" e_load, th_load = hp.set_heat_output(\n",
" heat_output=demand_vdg - 0.66,\n",
" Tsink=tsink_vdg,\n",
" Tsource=tsource_vdg\n",
" )\n",
"\n",
" return e_load, th_load, tsource, to_storage_MW, from_storage_MW, ws, hp\n",
"# Neg-take price, Pos-feed price"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def hponly_with_storage(case):\n",
" hp = case.assets['Heatpump']\n",
" ws = case.assets['HotWaterStorage']\n",
" demands_vdg = case.data['MW (VDG)'].to_list()\n",
" Tsink_vdg = case.data['Tsink (VDG)'].to_list()\n",
" Tsource_vdg = case.data['Tsource (VDG)'].to_list()\n",
" dam_prices = case.data['DAM'].to_list()\n",
" pos_prices = case.data['POS'].to_list()\n",
" neg_prices = case.data['NEG'].to_list()\n",
"\n",
" hp_input = [0] * len(case.data)\n",
" hp_output = [0] * len(case.data)\n",
"\n",
" minutes = len(case.data)\n",
" storage_levels = [None] * minutes\n",
" to_storage_list = [0] * minutes\n",
" from_storage_list = [0] * minutes\n",
" to_storage_list = [0] * minutes\n",
"\n",
" new_tsources_vdg = case.data['Tsource (VDG)'].to_list()\n",
"\n",
" \n",
" ws.set_chargelevel(ws.min_chargelevel)\n",
" \n",
" for m in range(minutes):\n",
" tsource_vdg = Tsource_vdg[m]\n",
" tsink_vdg = Tsink_vdg[m]\n",
" demand_vdg = demands_vdg[m]\n",
" \n",
" e_load, th_load, tsource_vdg, to_storage_MW, from_storage_MW, ws, hp = hp_storage_opt_enginge(\n",
" c, ws, hp, pos_prices[m], neg_prices[m], dam_prices[m], demand_vdg, tsource_vdg, tsink_vdg\n",
" )\n",
" \n",
" hp_input[m] = e_load\n",
" hp_output[m] = th_load\n",
" \n",
" storage_levels[m] = ws.chargelevel\n",
" new_tsources_vdg[m] = tsource_vdg\n",
" to_storage_list[m] = to_storage_MW\n",
" from_storage_list[m] = from_storage_MW\n",
" \n",
" case.data['hp_output_MW'] = np.array(hp_output)\n",
" case.data['hp_input_MW'] = np.array(hp_input)\n",
" case.data['cop'] = case.data['hp_output_MW'] / -case.data['hp_input_MW']\n",
" case.data['tsource_vdg'] = new_tsources_vdg\n",
" case.data['to_storage_MW'] = to_storage_list\n",
" case.data['from_storage_MW'] = from_storage_list\n",
" case.data['to_storage_MW'] = case.data['to_storage_MW']\n",
" case.data['from_storage_MW'] = case.data['from_storage_MW']\n",
" case.data['storage_level_MWh'] = storage_levels\n",
" \n",
" for col in case.data.columns:\n",
" if col.endswith('MW'):\n",
" case.data[col + 'h'] = case.data[col] / 60\n",
"\n",
" case.data = case.data.round(3)\n",
" return case\n"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [],
"source": [
"# charge_times = print(sum(s.storage_case_PCM.data['to_storage_MW']!= 0))\n",
"# discharge_times = print(sum(s.storage_case_PCM.data['from_storage_MW']!= 0))\n",
"# # 154476 times out of 525600, the storage is used to charge/dischage\n",
"# # from 39 000 to 110 000 discharge time increases\n"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"ename": "KeyError",
"evalue": "'Heatpump VDG'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[39], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m s\u001b[38;5;241m.\u001b[39mstorage_case_PCM \u001b[38;5;241m=\u001b[39m \u001b[43mhponly_with_storage\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstorage_case_PCM\u001b[49m\u001b[43m)\u001b[49m\n",
"Cell \u001b[1;32mIn[37], line 2\u001b[0m, in \u001b[0;36mhponly_with_storage\u001b[1;34m(case)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mhponly_with_storage\u001b[39m(case):\n\u001b[1;32m----> 2\u001b[0m hp_vdg \u001b[38;5;241m=\u001b[39m \u001b[43mcase\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43massets\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mHeatpump VDG\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[0;32m 3\u001b[0m ws \u001b[38;5;241m=\u001b[39m case\u001b[38;5;241m.\u001b[39massets[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mHotWaterStorage\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[0;32m 4\u001b[0m demands_vdg \u001b[38;5;241m=\u001b[39m case\u001b[38;5;241m.\u001b[39mdata[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mMW (VDG)\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39mto_list()\n",
"\u001b[1;31mKeyError\u001b[0m: 'Heatpump VDG'"
]
}
],
"source": [
"s.storage_case_PCM = hponly_with_storage(s.storage_case_PCM)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data.sample(2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data['cop'].fillna(2.5, inplace = True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# s.storage_case_PCM.data.to_csv('storage_data.csv')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data[['DAM', 'POS', 'NEG', 'hp_output_MW', 'to_storage_MW', 'Total demand', 'cop']].describe()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data[['DAM', 'POS', 'NEG', 'hp_output_MW', 'to_storage_MW', 'from_storage_MW','Total demand']].sample(20)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.hpcase.data.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# hp_selection = s.hpcase.data[['DAM', 'POS', 'NEG', 'hp_input_MW', 'hp_output_MW', 'cop', 'Total demand']]\n",
"hp_selection = s.hpcase.data[['DAM', 'POS', 'NEG', 'Total demand']]\n",
"storage_selection = s.storage_case_PCM.data[['hp_input_MW', 'hp_output_MW', 'cop', 'to_storage_MW', 'from_storage_MW']]\n",
"\n",
"selection = pd.concat([hp_selection, storage_selection], axis=1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"selection.sample(20)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"none_count = selection['cop'].isnull().sum()\n",
"none_count"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"selection.mean()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"########################\n",
"\n",
"# This is where we left\n",
"\n",
"########################"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def cost_function(th_load, cop, electricity_cost, alt_heat_price, demand):\n",
" return (\n",
" th_load / cop * electricity_cost\n",
" + (demand - th_load) * alt_heat_price\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def hybrid_imb_optimisation(case, decimals, s):\n",
" gb = case.assets['Gasboiler']\n",
" hp_vdg = case.assets['Heatpump VDG']\n",
" demand_vdg = case.data['MW (VDG)'].round(decimals).to_list()\n",
" Tsink_vdg = case.data['Tsink (VDG)'].round(decimals).to_list()\n",
" Tsource_vdg = case.data['Tsource (VDG)'].round(decimals).to_list()\n",
" fore_neg = case.data[c.forecast].fillna(999).round(decimals).to_list()\n",
" gas_prices = case.data['Gas prices (€/MWh)'].round(decimals).to_list()\n",
" co2_prices = case.data['CO2 prices (€/MWh)'].round(decimals).to_list()\n",
" eb_ode_g = s.eb_ode_g\n",
" eb_ode_e = s.eb_ode_e\n",
" \n",
" gb_input = [0] * len(case.data)\n",
" gb_output = [0] * len(case.data)\n",
"\n",
" minutes = range(len(case.data))\n",
" hp_output = [0] * len(case.data)\n",
" hp_input = [0] * len(case.data)\n",
"\n",
" for m in tqdm(minutes):\n",
" dem_vgd = demand_vdg[m]\n",
" if dem_vgd != 0:\n",
" max_load = min(hp_vdg.max_th_power, dem_vgd)\n",
" min_load = hp_vdg.min_th_power\n",
" Tsink = Tsink_vdg[m]\n",
" Tsource = Tsource_vdg[m]\n",
" cop_max_load = hp_vdg.get_cop(heat_output=max_load, Tsink=Tsink, Tsource=Tsource)\n",
" cop_min_load = hp_vdg.get_cop(heat_output=min_load, Tsink=Tsink_vdg[m], Tsource=Tsource_vdg[m])\n",
" \n",
" cost_full_load = cost_function(\n",
" th_load=max_load,\n",
" cop=cop_max_load,\n",
" electricity_cost=fore_neg[m] + eb_ode_e - c.sde_switch_price_correction,\n",
" alt_heat_price=gas_prices[m] + co2_prices[m] + eb_ode_g/case.assets['Gasboiler'].efficiency,\n",
" demand=dem_vgd\n",
" )\n",
" \n",
" cost_min_load = cost_function(\n",
" th_load=min_load,\n",
" cop=cop_min_load,\n",
" electricity_cost=fore_neg[m] + eb_ode_e,\n",
" alt_heat_price=gas_prices[m] + co2_prices[m] + eb_ode_g/case.assets['Gasboiler'].efficiency,\n",
" demand=dem_vgd\n",
" )\n",
" \n",
" if cost_full_load < cost_min_load:\n",
" hp_vdg_input, hp_vdg_output = hp_vdg.set_heat_output(max_load, Tsink, Tsource)\n",
" else:\n",
" hp_vdg_input, hp_vdg_output = hp_vdg.set_heat_output(min_load, Tsink, Tsource)\n",
" else:\n",
" hp_vdg_input, hp_vdg_output = (0, 0)\n",
"\n",
"\n",
" hp_out = hp_vdg_output\n",
" hp_output[m] = hp_out\n",
" hp_input[m] = hp_vdg_input\n",
" remaining_demand = max(dem_vgd+dem_ngd-hp_out, 0)\n",
" gb_output[m], gb_input[m] = gb.set_heat_output(remaining_demand)\n",
"\n",
" case.data['hp_output_MW'] = np.array(hp_output)\n",
" case.data['hp_input_MW'] = np.array(hp_input)\n",
" case.data['gb_output_MW'] = np.array(gb_output)\n",
" case.data['gb_input_MW'] = np.array(gb_input)\n",
"\n",
" for col in case.data.columns:\n",
" if col.endswith('MW'):\n",
" case.data[col + 'h'] = case.data[col] / 60\n",
"\n",
" case.data = case.data.round(5)\n",
" return case"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"aFRR\n",
"* Bid in a volume (X MW) --> Strategy is to only bid in on aFRR down\n",
"* Remaining demand is filed in by gasboiler\n",
"* Bid price at switch price?\n",
"* Assume direct response to 0% for now?\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def calc_afrr_capacity(case):\n",
" hp_vdg = case.assets['Heatpump VDG']\n",
" \n",
" capacity = 0\n",
" for hp in hp_vdg:\n",
" max_th_output = hp.max_th_power\n",
" cop = hp.get_cop(max_th_output, Tsink=135, Tsource=60)\n",
" e_power = max_th_output / cop\n",
" capacity += e_power\n",
" return capacity"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Run optimisation"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def run_optimisation(c, s):\n",
" s.baseline = baseline_sim(s.baseline)\n",
" s.hpcase = hponly(s.hpcase)\n",
" s.storage_case_PCM = hponly_with_storage(s.storage_case_PCM)\n",
"# s.hpcase_sde.assign_algorithm(hponly)\n",
"# s.hpcase_sde.run()\n",
" \n",
"# s.optcase1.assign_algorithm(hybrid_imb_optimisation)\n",
"# s.optcase1.run(decimals=2, s=s)\n",
" \n",
"# s.afrr_case.assign_algorithm(aFRR_optimisation)\n",
"# s.afrr_case.run(s=s)\n",
" \n",
" for case in [s.hpcase, s.storage_case_PCM]: # [s.hpcase, s.hpcase_sde, s.optcase1, s.afrr_case]:\n",
" case.mean_cop = case.data['hp_output_MW'].sum() / case.data['hp_input_MW'].abs().sum()\n",
" \n",
" return s\n",
"\n",
"s = run_optimisation(c, s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.hpcase.data['hp_output_MW'].sum() /s.hpcase.data['hp_input_MW'].abs().sum() "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.hpcase.data['hp_input_MW'].sum()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.hpcase.data['hp_input_MW'].min()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data['hp_input_MW'].sum()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data['hp_input_MW'].min()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data['hp_output_MW'].sum() /s.storage_case_PCM.data['hp_input_MW'].abs().sum() "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.hpcase.data.head()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data['cop'].isnull().sum()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vis_data = s.storage_case_PCM.data[[\n",
" 'storage_level_MWh', \n",
" #'to_storage_ndg_MW', \n",
" #'to_storage_vdg_MW', \n",
" 'to_storage_MW', \n",
" #'from_storage_ndg_MW', \n",
" #'from_storage_vdg_MW',\n",
" 'from_storage_MW',\n",
" 'NEG'\n",
"]]\n",
"\n",
"cops = pd.concat([s.hpcase.data['cop'], s.storage_case_PCM.data['cop']], axis=1)\n",
"vis_data = pd.concat([vis_data, cops], axis=1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vis_data.iplot(subplots=True, shape=(6,1), dimensions=(1000, 800))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vis_data.min()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"total_to_storage = s.storage_case_PCM.data['to_storage_MWh'].sum()\n",
"total_from_storage = s.storage_case_PCM.data['from_storage_MWh'].sum()\n",
"total_to_storage, total_from_storage"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hp_output = s.storage_case_PCM.data['hp_output_MW'].round(2)\n",
"demand_and_to_storage = (s.storage_case_PCM.data['Total demand'] + s.storage_case_PCM.data['to_storage_MW']).round(2)\n",
"hp_output.equals(demand_and_to_storage)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.concat([hp_output, demand_and_to_storage], axis=1).resample('H').sum().iplot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Financials"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def collect_cashflows(c, s):\n",
" s.hpcase.generate_electr_market_results(nom_col='hp_input_MWh', real_col='hp_input_MWh')\n",
" s.storage_case_PCM.data['nomination_MWh'] = s.hpcase.data['hp_input_MWh']\n",
" s.storage_case_PCM.generate_electr_market_results(nom_col='nomination_MWh', real_col='hp_input_MWh')\n",
"\n",
" \n",
" for case in [s.baseline]: #, s.optcase1, s.afrr_case]:\n",
" case.add_gas_costs(gasvolumes_col='gb_input_MWh')\n",
" case.add_co2_costs(volume_cols='gb_input_MWh', fuel='gas')\n",
" case.add_eb_ode(commodity='gas', tax_bracket=c.tax_bracket_g)\n",
" \n",
" for case in [s.hpcase, s.storage_case_PCM]: #, s.hpcase_sde, s.optcase1, s.afrr_case]:\n",
" \n",
" case.add_eb_ode(commodity='electricity', tax_bracket=c.tax_bracket_e, cons_col='hp_input_MWh')\n",
" case.add_grid_costs(\n",
" power_MW_col='hp_input_MW',\n",
" grid_operator=c.grid_operator,\n",
" year=2023, \n",
" connection_type=c.connection_type\n",
" )\n",
" \n",
" for case in s.sde_cases:\n",
" case = calculate_sde_subsidy(c=c, s=s, case=case)\n",
" case.add_cashflow('SDE++ subsidy (€)', case.sde_results['corrected_amount'])\n",
" return s\n",
"\n",
"s = collect_cashflows(c, s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def calculate_financials(c, s):\n",
" for case in s.cases:\n",
" case.calculate_ebitda(c.project_duration)\n",
"\n",
" for case in [s.hpcase, s.storage_case_PCM]: #, s.hpcase_sde, s.optcase1, s.afrr_case]:\n",
" case.calculate_business_case(\n",
" project_duration=c.project_duration, \n",
" discount_rate=c.discount_rate, \n",
" baseline=s.baseline\n",
" )\n",
" \n",
" return s\n",
"\n",
"s = calculate_financials(c, s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"charge_times = print(sum(s.storage_case_PCM.data['to_storage_MW']!= 0))\n",
"discharge_times = print(sum(s.storage_case_PCM.data['from_storage_MW']!= 0))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"charge_times_pcm = print(sum(s.storage_case_PCM.data['hp_input_MW']))\n",
"charge_times_hp = print(sum(s.hpcase.data['hp_input_MW']))"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Visualisations"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"fig_demands_over_time = s.demand['Total demand'].resample('H').mean().iplot(\n",
" title='Smurfit Kappa: Heat demand by Hour in MW', \n",
" yTitle='MW', \n",
" colors=recoygreen,\n",
" asFigure=True,\n",
" dimensions=(800, 400)\n",
")\n",
"fig_demands_over_time"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"demands_fig = s.demand[['Tsource (VDG)', 'Tsink (VDG)']].resample('H').mean().iplot(\n",
" kind='box',\n",
" title='Smurfit Kappa: Source and Sink temperatures',\n",
" color=recoygreen,\n",
" yTitle='Temperature in degrees C',\n",
" legend=False,\n",
" asFigure=True,\n",
" dimensions=(800, 400)\n",
")\n",
"demands_fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# temp_comparison = pd.concat([s.storage_case_PCM.data[['tsource_ndg', 'tsource_vdg']], s.storage_case_PCM.data[['Tsource (NDG)', 'Tsource (VDG)']]], axis=1)\n",
"# temp_comparison.columns = ['Tsource (NDG) -after', 'Tsource (VDG) - after', 'Tsource (NDG) - before', 'Tsource (VDG) - before']\n",
"# temp_comparison.resample('15T').mean().iplot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s.storage_case_PCM.data['tsource_vdg'].idxmin()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# s.storage_case_PCM.data.loc['2019-01-04 07:45:00']"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"case = s.storage_case_PCM\n",
"hp_vdg = case.assets['Heatpump VDG']\n",
"ws = case.assets['HotWaterStorage']\n",
"ws.storage_level = 47.083 + 25\n",
"pos = 35.650\n",
"neg = 43.850\n",
"dam = 68.400\n",
"demand_vdg = 0\n",
"tsource_vdg = 29.635\n",
"tsink_vdg = 134.840\n",
"\n",
"hp_storage_opt_enginge(c, ws, hp_vdg, pos, neg, dam, demand_vdg, tsource_vdg, tsink_vdg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#e_load_vdg, th_load_vdg, e_load_ndg, th_load_ndg, tsource_vdg, tsource_ndg, to_storage_vdg_MW, from_storage_vdg_MW, to_storage_ndg_MW, from_storage_ndg_MW, ws, hp_vdg, hp_ndg"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Tsource_VDG_before = s.hpcase.data['Tsource (VDG)'].round(2)\n",
"Tsource_VDG_after = s.storage_case_PCM.data['Tsource (VDG)'].round(2)\n",
"Tsource_VDG_before.equals(Tsource_VDG_after)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"test_hp = Heatpump(\n",
" name='Heatpump for Testing',\n",
" max_th_power=1,\n",
" min_th_power=0.3,\n",
" cop_curve=2.5\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"import plotly.graph_objs as go\n",
"def create_load_trace(gas_price, Tsnk, Tsrc, name):\n",
" loads = []\n",
" eprices = list(range(1000))\n",
" for eprice in eprices:\n",
" _, load = test_hp.set_opt_load(\n",
" electricity_cost=eprice + s.eb_ode_e,\n",
" alt_heat_price=gas_price / 0.9,\n",
" demand=1, \n",
" Tsink=Tsnk,\n",
" Tsource=Tsrc\n",
" )\n",
" loads.append(load)\n",
" trace = go.Scatter(x=eprices, y=loads, name=name)\n",
" return trace"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"date = s.baseline.data.index[0].strftime('%Y-%m-%d')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"fig_steamboiler = s.baseline.data[['Total demand', 'output_MW_th']].abs().loc[date, :].iplot(\n",
" subplots=True, \n",
" title=f'Steamboiler only case on {date}',\n",
" subplot_titles=['Total demand', 'Steam boiler output (MW)'],\n",
" legend=False,\n",
" dimensions=(800, 400),\n",
" colors=[recoydarkblue, recoygreen], \n",
" asFigure=True,\n",
" shape = (2, 1)\n",
")\n",
"fig_steamboiler"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"fig_heatpump = s.hpcase.data[['Total demand', 'hp_output_MW']].abs().loc[date, :].iplot(\n",
" subplots=True, \n",
" title=f'Heatpump only case on {date}', \n",
" subplot_titles=['Total demand', 'Heatpump output (MW)'],\n",
" legend=False,\n",
" dimensions=(800, 400),\n",
" colors=[recoydarkblue, recoygreen], \n",
" asFigure=True,\n",
" shape = (2, 1)\n",
")\n",
"fig_heatpump"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"report = ComparisonReport(\n",
" cases=s.optcases, \n",
" kind='electr_market_results',\n",
")\n",
"report.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"casereport = ComparisonReport(cases = s.cases, kind='ebitda_calc', baseline=s.baseline, comparison='relative')\n",
"casereport.show(presentation_format=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"BusinessCaseReport(s.hpcase).show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"BusinessCaseReport(s.storage_case_PCM).show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"#price chart\n",
"from copy import deepcopy\n",
"\n",
"data = deepcopy(s.baseline.data)\n",
"data = data[data.columns[:2]]\n",
"data[data.columns[1]] = MWh_gas_to_tonnes_CO2(data[data.columns[1]])\n",
"data = data.rename(columns={\"CO2 prices (€/ton)\": \"CO2 prices (€/MWh)\"})\n",
"\n",
"data.resample('D').mean().iplot(dimensions=(800, 300), title='Gasprices vs. CO2 prices', colors=recoycolors)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"# #price chart\n",
"# from copy import deepcopy\n",
"\n",
"# s.optcase1.data['DAM'].resample('D').mean().iplot(dimensions=(800, 300), title='Electricity Prices', colors=recoycolors)\n",
"\n",
"# _source_output = s.optcase1.data[['hp_output_MWh', 'gb_output_MWh']].resample('M').sum()\n",
"# _total_output = _source_output.sum(axis=1)\n",
"# _data = _source_output.divide(_total_output, axis=0).rename(\n",
"# columns={'hp_output_MWh':'Heat pump', 'gb_output_MWh':'Gasboiler'}\n",
"# ) * 100\n",
"\n",
"# production_fig = _data.iplot(\n",
"# kind='bar',\n",
"# barmode='stack',\n",
"# colors=[recoydarkblue, recoygreen],\n",
"# title='Hybrid case: Heat production per Month by Source in % share',\n",
"# yTitle='Share of production in %',\n",
"# dimensions=(600, 400),\n",
"# asFigure=True\n",
"# )\n",
"\n",
"# production_fig = production_fig.update_layout(legend_traceorder=\"reversed\")\n",
"# production_fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"report = ComparisonReport(s.cases, kind='capex').report\n",
"report = report[report.index.str.contains('CAPEX')].T\n",
"capex_fig = report.iplot(\n",
" kind='bar', \n",
" barmode='relative',\n",
" colors=recoycolors,\n",
" title='CAPEX by Casestudy',\n",
" yTitle='CAPEX in €',\n",
" dimensions=(600, 400),\n",
" asFigure=True,\n",
")\n",
"\n",
"capex_fig = capex_fig.update_layout(legend_traceorder=\"reversed\")\n",
"capex_fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"cashflow_report = ComparisonReport(cases = s.cases, kind='cashflows')\n",
"\n",
"fig = cashflow_report.report.T.iplot(\n",
" kind='bar',\n",
" barmode='relative',\n",
" colors=recoycolors,\n",
" title='OPEX breakdown',\n",
" asFigure=True,\n",
" yTitle='€',\n",
" dimensions=(800, 600)\n",
")\n",
"\n",
"ebitda_report = ComparisonReport(cases=s.cases, kind='ebitda_calc')\n",
"scat = go.Scatter(\n",
" mode='markers',\n",
" y=ebitda_report.report.loc['EBITDA (€)', :].values, \n",
" x=ebitda_report.report.columns, \n",
" line=go.scatter.Line(color=recoydarkgrey),\n",
" marker=dict(\n",
" color=recoydarkgrey,\n",
" size=20,\n",
" line=dict(\n",
" color=recoydarkgrey,\n",
" width=3\n",
" ),\n",
" symbol='line-ew'\n",
" ),\n",
" name='EBITDA (€)'\n",
")\n",
"fig.add_trace(scat)\n",
"fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ebitda_report.show(comparison='relative')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_series = SingleFigureComparison(s.cases, 'ebitda', label='EBITDA').report\n",
"ebitda_graph = _series.iplot(\n",
" kind='bar',\n",
" title='Yearly EBITDA by Casestudy in €',\n",
" colors=recoygreen,\n",
" dimensions=(600, 400), \n",
" yTitle='EBITDA in €',\n",
" asFigure=True,\n",
" yrange=[_series.min() * 1.2, max(_series.max() * 2, 0)]\n",
")\n",
"\n",
"ebitda_graph.update_traces(\n",
" text=_series.values/1000_000, \n",
" textposition='outside', \n",
" texttemplate=\"%{text:.1f}M €\", \n",
")\n",
"\n",
"ebitda_graph"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_series = SingleFigureComparison(s.optcases, 'npv', label='NPV').report\n",
"\n",
"npv_graph = _series.iplot(\n",
" kind='bar',\n",
" title='NPV by Casestudy in €',\n",
" colors=recoygreen,\n",
" dimensions=(600, 400), \n",
" yTitle='NPV in €',\n",
" asFigure=True,\n",
" yrange=[0, _series.max() * 1.1]\n",
")\n",
"\n",
"npv_graph.update_traces(\n",
" text=_series.values/1000_000, \n",
" textposition='outside', \n",
" texttemplate=\"%{text:.1f}M €\", \n",
")\n",
"\n",
"npv_graph"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"irr_report = (SingleFigureComparison(s.optcases, 'irr', label='IRR').report * 100)\n",
"irr_fig = irr_report.iplot(\n",
" kind='bar',\n",
" title='IRR by Casestudy in %',\n",
" colors=recoygreen,\n",
" dimensions=(600, 400),\n",
" yTitle='IRR in %',\n",
" asFigure=True,\n",
" yrange=[0, irr_report.max() * 1.2]\n",
")\n",
"\n",
"irr_fig.update_traces(\n",
" text=irr_report.values, \n",
" textposition='outside', \n",
" texttemplate=\"%{text:.0f} %\", \n",
")\n",
"\n",
"irr_fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_series = (SingleFigureComparison(s.optcases, 'spp', label='Simple Payback Time').report)\n",
"spt_fig = _series.iplot(\n",
" kind='bar',\n",
" title='Simple Payback Time by Casestudy in Years',\n",
" colors=recoygreen,\n",
" dimensions=(600, 400),\n",
" yTitle='Years',\n",
" asFigure=True,\n",
" yrange=[0, _series.max() * 1.2]\n",
")\n",
"\n",
"spt_fig.update_traces(\n",
" text=_series.values, \n",
" textposition='outside', \n",
" texttemplate=\"%{text:.1f} years\", \n",
")\n",
"\n",
"spt_fig "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"mean_cops = (SingleFigureComparison(s.optcases, 'mean_cop', label='COP').report)\n",
"cop_fig = mean_cops.iplot(\n",
" kind='bar',\n",
" title='Mean COP by Casestudy',\n",
" colors=recoygreen,\n",
" dimensions=(600, 400),\n",
" yTitle='COP',\n",
" asFigure=True,\n",
" yrange=[0, mean_cops.max() * 1.2]\n",
")\n",
"\n",
"cop_fig.update_traces(\n",
" text=mean_cops.values, \n",
" textposition='outside', \n",
" texttemplate=\"%{text:.1f}\", \n",
")\n",
"\n",
"cop_fig "
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Sensitivity analysis"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"We have agreed that for the sensitivity analysis we will vary the following key assumptions in the flex-model:\n",
"1. Market prices of gas and electricity. We will use the actual prices for 2019, the actual prices for 2020.\n",
"2. aFRR prices +/- 30%\n",
"3. CAPEX\n",
"4. CO2 price\n",
"5. Tsource +/- 10 degrees Celsius\n",
"6. Tsink\n",
" * Roam off the peak / lower pressure\n",
" * Stabalize / running average per hour/ 2 hours\n",
"7. __A scenario with a constraint on grid capacity and a scenario without grid capacity as a constraint__\n",
"8. Energy Tax and ODE +/- 30%"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def setup():\n",
" _c = Config()\n",
" s = setup_model(c=_c)\n",
" return s\n",
"\n",
"def routine(c, s):\n",
" s = load_data(c=c, s=s)\n",
" s = create_and_assign_assets(c=c, s=s)\n",
" #s = preprocessing(c=c, s=s)\n",
" s = run_optimisation(c=c, s=s)\n",
" #s = postprocessing(c=c, s=s)\n",
" s = collect_cashflows(c=c, s=s)\n",
" s = calculate_financials(c=c, s=s)\n",
" return s"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"%time _s = setup()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"%time result = routine(c, _s)\n",
"npv = result.hpcase.npv\n",
"npv"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# sensitivity:Storage temperature\n",
"values = range(80, 120, 10)\n",
"param = 'storage_temperature'\n",
"kpis = ['npv','irr','spp', 'mean_cop']\n",
"\n",
"sens = SensitivityAnalysis(c, _s, routine, param, values, kpis)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='volume(m3)',\n",
" yTitle='NPV in €',\n",
" yrange=[output.min().min()*1.1, 0],\n",
" title='Sensitivity: Storage volume',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sensitivity: Water storage volume"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"(_s.demand['MW (VDG)'] + _s.demand['MW (NDG)']).to_list()[:3]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%time _s = setup()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"values = range(100, 500, 100)\n",
"param = 'storage_volume'\n",
"kpis = ['npv','irr','spp']\n",
"\n",
"sens = SensitivityAnalysis(c, _s, routine, param, values, kpis)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"[case.name for case in s.cases][1:]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='volume(m3)',\n",
" yTitle='NPV in €',\n",
" yrange=[output.min().min()*1.1, 0],\n",
" title='Sensitivity: Storage volume',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('irr', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='volume(m3)',\n",
" yTitle='irr in %',\n",
" yrange=[output.min().min()*1.1, 0],\n",
" title='Sensitivity: IRR',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='volume(m3)',\n",
" yTitle='years',\n",
" yrange=[output.min().min()*1.2, 0],\n",
" title='Sensitivity: payback time',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('mean_cop', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='volume(m3)',\n",
" yTitle='COP',\n",
" yrange=[output.min().min()*1.1, 0],\n",
" title='Sensitivity: COP',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sensitivity: CAPEX per MW storage"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"values = range(4_000, 10_000, 2_000)\n",
"param = 'storage_capex_per_MW'\n",
"kpis = ['npv', 'irr', 'spp', 'mean_cop']\n",
"\n",
"sens = SensitivityAnalysis(c, _s, routine, param, values, kpis)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='CAPEX',\n",
" yTitle='NPV in Euro',\n",
" yrange=[output.min().min()*1.1, 0],\n",
" title='Sensitivity: NPV in euro',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='CAPEX/MW',\n",
" yTitle='years',\n",
" yrange=[output.min().min()*1.2, 0],\n",
" title='Sensitivity: payback time',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sensitivity: CAPEX per MWh storage"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"values = range(1_000, 5_000, 1000)\n",
"param = 'storage_capex_per_MWh'\n",
"kpis = ['npv', 'spp', 'mean_cop']\n",
"\n",
"sens = SensitivityAnalysis(c, _s, routine, param, values, kpis)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='CAPEX/MWh',\n",
" yTitle='NPV',\n",
" yrange=[output.min().min()*1.2, 0],\n",
" title='Sensitivity: NPV in Euro',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='CAPEX/MWh',\n",
" yTitle='years',\n",
" yrange=[output.min().min()*1.2, 0],\n",
" title='Sensitivity: Payback time',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sensitivity: threshold price"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"values = range(10, 60, 10)\n",
"param = 'threshold'\n",
"kpis = ['npv', 'spp', 'mean_cop']\n",
"\n",
"sens = SensitivityAnalysis(c, _s, routine, param, values, kpis)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='threshold',\n",
" yTitle='NPV',\n",
" yrange=[output.min().min()*1.2, 0],\n",
" title='Sensitivity: NPV in Euro',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='threshold',\n",
" yTitle='years',\n",
" yrange=[output.min().min()*1.2, 0],\n",
" title='Sensitivity: SPP in years',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('mean_cop', case_names=[case.name for case in s.cases][1:])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='threshold',\n",
" yTitle='COP',\n",
" yrange=[output.min().min()*1.2, 0],\n",
" title='Sensitivity: SOP',\n",
" colors=recoycolors,\n",
" dimensions=(600, 400),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Heat Pump CAPEX"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [0.7, 1, 1.3, 2]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.hp_capex *= value\n",
" configs[value*100] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"%%time\n",
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"\n",
"sens_capex = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='CAPEX factor (%)',\n",
" yTitle='Payback Period in years',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Heat Pump CAPEX (€)',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_capex"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: CO2 prices"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"mean = 24.86\n",
"co2_prices = [10, 25, 50, 100]\n",
"\n",
"for price in co2_prices:\n",
" _c = Config()\n",
" multiplier = price / mean\n",
" _c.co2_price_multiplier = multiplier\n",
" configs[price] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"%%time\n",
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('ebitda', case_names=['Baseline', 'Heatpump + SDE', 'Heatpump only', 'Optimisation', 'Optimisation + aFRR'])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Mean CO2 price in €',\n",
" yTitle='EBITDA in €',\n",
" yrange=[output.min().min() * 1.1, 0],\n",
" title='Sensitivity: CO2 prices > EBITDA',\n",
" colors=[recoypurple, recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"sens_co2_spp = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Mean CO2 price in €',\n",
" yTitle='Payback Period in Years',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: CO2 prices',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_co2_spp"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Mean CO2 price in €',\n",
" yTitle='NPV in €',\n",
" yrange=[output.min().min() * 10, output.max().max()*1.1],\n",
" title='Sensitivity: CO2 prices > NPV',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Gas prices"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [0.7, 1, 1.3]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.gas_price_multiplier = value\n",
" configs[value * 100] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"%%time\n",
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Gas price factor (%)',\n",
" yTitle='NPV in €',\n",
" yrange=[output.min().min()*1.5, output.max().max()*1.1],\n",
" title='Sensitivity: Gas prices',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Day Ahead buying amount"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [0, 0.5, 1]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.day_ahead_buying_perc = value\n",
" configs[value * 100] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Volume in %',\n",
" yTitle='NPV in €',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Volume bought on Day-Ahead market in %',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Electricity prices"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [0.7, 1, 1.3]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.e_price_multiplier = value\n",
" configs[value * 100] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"sens_eprices = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Electricity price factor in %',\n",
" yTitle='Payback Period in years',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Electricity prices',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_eprices"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Electricity price volatility"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [0.7, 1, 1.3]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.e_price_volatility_multiplier = value\n",
" configs[value] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"sens_evol = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Volatility factor',\n",
" yTitle='Payback period in years',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: E-price volatility',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_evol"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: aFRR capacity fee"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [10_000, 25_000, 50_000]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.afrr_capacity_fee = value\n",
" configs[value] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"sens_affr_fee = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='aFRR capacity fee in €/MW',\n",
" yTitle='Payback Period in years',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: aFRR capacity fee',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_affr_fee"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Energy tax"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [0.7, 1, 1.3]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.energy_tax_multiplier = value\n",
" configs[value] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('npv', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Factor',\n",
" yTitle='NPV in €',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Energy taxes',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Tsource"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [-25, -10, 0, 10, 25]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.tsource_delta = value\n",
" configs[value] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"output.loc[-25, 'Heatpump only'] = np.nan\n",
"sens_tsource = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Tsource delta',\n",
" yTitle='Payback Period in years',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Tsource',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_tsource"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('mean_cop', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Tsource delta',\n",
" yTitle='COP',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Tsource > Mean COP',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Tsink"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"_s = setup()\n",
"configs = {}\n",
"values = [-25, -10, 0, 10, 25]\n",
"for value in values:\n",
" _c = Config()\n",
" _c.tsink_delta = value\n",
" configs[value] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"sens = SensitivityAnalysis(_s, routine, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('spp', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"sens_tsink = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Tsink delta',\n",
" yTitle='Payback Period in Years',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Tsink',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_tsink"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"output = sens.single_kpi_overview('mean_cop', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"sens_tsink_cop = output.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Tsink delta',\n",
" yTitle='COP',\n",
" yrange=[0, output.max().max()*1.1],\n",
" title='Sensitivity: Tsink > Mean COP',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400),\n",
" asFigure=True\n",
")\n",
"sens_tsink_cop"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"### Sensitivity: Time period"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"def routine2(c, s):\n",
" s = setup_model(c=c)\n",
" s = load_data(c=c, s=s)\n",
" s = create_and_assign_assets(c=c, s=s)\n",
" s = run_optimisation(c=c, s=s)\n",
" s = collect_cashflows(c=c, s=s)\n",
" s = calculate_financials(c=c, s=s)\n",
" return s"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"configs = {}\n",
"start_values = ['2018-01-01', '2019-01-01', '2019-11-01']\n",
"\n",
"for value in start_values:\n",
" _c = Config()\n",
" _c.start = value\n",
" _c.end = (pd.to_datetime(value) + timedelta(days=364)).strftime('%Y-%m-%d')\n",
" configs[value] = _c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"%%time\n",
"sens = SensitivityAnalysis(_s, routine2, configs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"result = sens.single_kpi_overview('npv', case_names=['Heatpump only', 'Heatpump + SDE', 'Optimisation', 'Optimisation + aFRR'])\n",
"result.iplot(\n",
" mode='lines+markers',\n",
" symbol='circle-dot',\n",
" size=10,\n",
" xTitle='Start date',\n",
" yTitle='NPV in €',\n",
" yrange=[0, result.max().max()*1.1],\n",
" title='Sensitivity: Modelled time period',\n",
" colors=[recoygreen, recoyyellow, recoydarkblue, recoyred],\n",
" dimensions=(600, 400)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exclude"
]
},
"source": [
"## Report"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"exclude"
]
},
"outputs": [],
"source": [
"renderer = 'svg'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# __Presentation Flexible Heatpumps__\n",
"\n",
"ENCORE meeting: 17-12-2020\n",
"Mark Kremer\n",
"\n",
" \n",
" \n",
" \n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## __Central question:__\n",
"#### *Can Smurfit Kappi shorten the Payback Period of an investment in a Heatpump by operating it in a flexible manner?*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Flexible operations on imbalance market__\n",
"\n",
"Benefiting from fluctuations in electricity market prices by ramping the asset up- and down (increasing and decreasing the electricity consumption)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_example_1 = s.hpcase.data[['DAM']].iloc[:60*24].iplot(\n",
" title='Day-Ahead prices on arbitrary day in €/MWh',\n",
" yrange=[-100, 200],\n",
" colors=recoygreen, \n",
" yTitle='Price in €/MWh',\n",
" xTitle='Time of Day',\n",
" dimensions=(800, 400),\n",
" asFigure=True\n",
")\n",
"\n",
"fig_example_1.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Imbalance prices are very volatile, with prices below -100 and above 200 €/MWh on a daily basis."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_example_2 = s.hpcase.data[['DAM', 'POS']].rename(columns={'POS':'IMB'}).iloc[:60*24].iplot(\n",
" title='Imbalance Prices on arbitrary day in €/MWh',\n",
" colors=[recoygreen, recoydarkblue], \n",
" yTitle='Price in €/MWh',\n",
" xTitle='Time of Day',\n",
" dimensions=(800, 400),\n",
" asFigure=True\n",
")\n",
"\n",
"fig_example_2.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It is possible to benefit from these fluctiations, if you have __flexibility__\n",
"* Storage options\n",
"* Hybrid installations (e.g. with gas-powered assets)\n",
"\n",
"In this case we are looking at a __hybrid set-up of a Steamboiler and a Heatpump__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Simulations & mathematical modelling__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* To answer the central question, we have build a simulation model\n",
"* The model simulates the operations of a hybrid set-up of a Heatpump and a Steamboiler over the timespan of 1 years (on a 1 minute basis)\n",
"* The goal of the model is to minimize the operating costs, in order to reach the shortest Payback Period\n",
"* We are taking into account all major investment and operating costs, including:\n",
" * Asset CAPEX\n",
" * Commodity costs for gas and electricity\n",
" * Energy taxes\n",
" * Grid transport costs\n",
" * SDE++ subsidies\n",
" * Maintenance costs\n",
" * CO2 allowances\n",
"* The output of the model is a Payback Period for an investment in an heatpump, in different scenario's"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Casestudies__\n",
"5 main scenario's"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"1. Steamboiler only (baseline, baseload)\n",
"2. Heatpump only (stand-alone, baseload, without SDE++)\n",
"3. Heatpump + SDE\n",
"4. Heatpump + SDE + Steam boiler (hybrid set-up) on Imbalance market\n",
"5. Heatpump + SDE + Steam boiler (hybrid set-up) on aFRR (secondary reserve market)\n",
"\n",
"Besides that, we modelled 11 sensitivities:\n",
"* Heatpump CAPEX\n",
"* Gas & CO2 prices\n",
"* Electricity prices & volatility\n",
"* Energy taxes\n",
"* Bidding strategies\n",
"* Source temperatures (affecting COP)\n",
"* Sink temperatures (affecting COP)\n",
"* Time period (2018, 2019, 2020)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Smurfit Kappa case__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The model is based on the context of Smurfit Kappa (paper factory)\n",
"* Currently a Steamboiler is providing the 20-30 MW of average heat demand for drying processes\n",
"* We add a 31 MW heatpump (to make sure it can cover entire demand)\n",
"* The steam demand must be fulfilled at all times, by either the heatpump or the gasboiler\n",
"* The heatpump and steam boiler can both respond very quickly (within minutes) within a flexible range (30%-100% for heatpump)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_demands_over_time.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Source temperatures of around 65 degrees C\n",
"* Sink temperatures of 125-170 degrees C \n",
"* Average Temperature lift of about 85 degrees C"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"demands_fig.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Heat pump__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"COP roughly between 4 and 1.5, depending on load, Tsource and Tsink"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def cop_curve(Tsink, Tsource):\n",
" Tsink += 273\n",
" Tsource += 273\n",
"\n",
" c1 = 0.267 * Tsink / (Tsink - Tsource)\n",
" c2 = 0.333 * Tsink / (Tsink - Tsource)\n",
" \n",
" return Polynomial([c2, c1])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sourceT = 63\n",
"sinkT = 140\n",
"cop_curve(sourceT, sinkT)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_cop_curve.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Optimisation__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* At each moment in time, we calculate the cheapest option to produce the required heat. \n",
"* Taking into account the COP fluctuations, due to changing Tsource and Tsink\n",
"* Taking into account fluctuating market prices (electricity, gas, CO2)\n",
"* We are predicting real-time electricity prices using our forecasting models"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"__Some example days:__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Steamboiler only is following demand pattern"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_steamboiler.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similar pattern for heatpump only case"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_heatpump.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Hybrid set-up is responding to price fluctuactions, steam boiler taking over at high prices"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_optcase.show(renderer=renderer)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig_optcase2.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Business case__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"CAPEX of around 6 M€ (200.000 €/MW), which need to be earned back by savings in operating costs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"capex_fig.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Savings in EBITDA compared to the baseline are about 1.5 mln € without subsidy, and up to 4.5 mln € including subsidy\n",
"* The optimisation on aFRR allows for a 30-40% improvement in EBITDA"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ebitda_graph.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Resulting in a Payback Period of 5.4 years without subsidy, and 1.8 years with subsidy"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"spt_fig.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The added value of the optimisation is limited (in absolute terms), which is explained by the high COP of the heatpump"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"production_fig.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* The heatpump is filling in 95-98 % of the demand. \n",
"* Because of its high COP, it is almost always cheaper to run than the steam boiler\n",
"* Switch price is on average around 90€/MWh (excluding subsidies)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Sensitivities__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If CAPEX is 200%, subsidy is needed to keep a good Payback Time"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sens_capex.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Subsidies are protecting the business case againsts low CO2 prices"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sens_co2_spp.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* The businesscase is quite sensitive to Tsource and Tsink differences, because they directly impact the COP\n",
"* The Smurtfit Kappa case, with a temperature lifte of about 85 degrees C on average, looks favorable. \n",
"* When the temperature lift is higher, the COP will decrease and the business case will degrade"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sens_tsource.show(renderer=renderer)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sens_tsink.show(renderer=renderer)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sens_tsink_cop.show(renderer=renderer)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sens_eprices.show(renderer=renderer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### __Conclusions__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* The business case for a Heat Pump seems favourable\n",
"* Flexible operation, using aFRR, can improve the operational results by 30-40%\n",
"* However, this only results in a marginal improvement of the business case\n",
"* SDE++ has a very favourable effect on the business case, but is not critical\n",
"* The business case is notably sensitive to the temperature lift required, and is therefore strongly dependent on the specific use case."
]
}
],
"metadata": {
"interpreter": {
"hash": "fea6e7ab4c0ed1d184e153838ac4b2d24a0985a5f17e99f4f437a114b66796b8"
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
},
"toc-autonumbering": false,
"toc-showmarkdowntxt": false,
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}