{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "#### Imports" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "tags": [ "exclude" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The autoreload extension is already loaded. To reload it, use:\n", " %reload_ext autoreload\n" ] } ], "source": [ "from datetime import timedelta\n", "import json\n", "import pprint\n", "from copy import deepcopy\n", "\n", "import cufflinks\n", "import numpy as np\n", "from numpy.polynomial import Polynomial\n", "import pandas as pd\n", "from tqdm.notebook import tqdm\n", "\n", "from pyrecoy.assets import Heatpump, Eboiler, GasBoiler\n", "from pyrecoy.colors import *\n", "from pyrecoy.converters import *\n", "from pyrecoy.financial import calculate_eb_ode, get_tax_tables, get_tax_rate, get_grid_tariffs_electricity\n", "from pyrecoy.framework import TimeFramework\n", "from pyrecoy.casestudy import CaseStudy\n", "from pyrecoy.plotting import ebitda_bar_chart, npv_bar_chart\n", "from pyrecoy.reports import CaseReport, ComparisonReport, BusinessCaseReport, SingleFigureComparison\n", "from pyrecoy.sensitivity import SensitivityAnalysis\n", "from pyrecoy.prices import get_tennet_data, get_afrr_capacity_fees_nl\n", "from pyrecoy.forecasts import Mipf\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", "\n", "--\n", "* Create strategy on imbalance POS (buy 100% day-ahead, and respond to high prices)\n", "* Graphs\n", " * EBITDA vs. baseline (earnings vs baseline)\n", "* Show COP curves in different cases, just for illustration\n", "* Energy report --> Check + add gas\n", "* Fix comparison reports\n", "* Model verification" ] }, { "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": [ "# ENCORE : Heatpump Optimisation Framework\n", "\n", "***\n", "© Mark Kremer \n", "July 2020\n", "##### Cases\n", "In this model, 3 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 turbine and heatpump run in hybrid mode, and are optimised on costs in real-time" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "#### Loading config" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "class Config():\n", " start = '2019-01-01'\n", " end = '2019-12-31'\n", " \n", " hp_vdg_e_power = 23.3 # MW\n", " hp_ndg_e_power = 7.7 # MW\n", " hp_min_load = 0.3\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 = 35 # MW\n", " gb_efficiency = 0.9\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": 97, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "class Store():\n", " pass" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "## Model set-up" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def load_demand_data(c, s):\n", " demand = pd.read_csv('data/smurfit_demand_preprocessed.csv', delimiter=';', decimal=',')\n", " dt_index = pd.date_range(\n", " start=s.time_fw.start,\n", " end=s.time_fw.start + timedelta(days=365), freq='1T',\n", " closed='left',\n", " tz='Europe/Amsterdam')\n", " demand.index = dt_index\n", " demand['Total demand'] = demand['MW (VDG)'] + demand['MW (NDG)']\n", " demand = demand[c.start:c.end]\n", " return demand\n" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [], "source": [ "path_str = r\"C:\\Users\\Shahla Huseynova\\Recoy\\Recoy - Documents\\03 - Libraries\\12 - Data Management\\Forecast Data\"\n", "\n", "from pathlib import Path\n", "path = Path(path_str)" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "WindowsPath('C:/Users/Shahla Huseynova/Recoy/Recoy - Documents/03 - Libraries/12 - Data Management/Forecast Data')" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "path" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [], "source": [ "from pyrecoy.forecasts import Mipf\n", "mipf = Mipf(\n", " start=c.start, \n", " end=c.end, \n", " folder_path=r\"C:\\Users\\Shahla Huseynova\\Recoy\\Recoy - Documents\\03 - Libraries\\12 - Data Management\\Forecast Data\"\n", ")" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [], "source": [ "from pyrecoy.forecasts import Mipf\n", "mipf = Mipf(\n", " start=c.start, \n", " end=c.end, \n", " folder_path=r\"C:\\Users\\Shahla Huseynova\\Recoy\\Recoy - Documents\\03 - Libraries\\12 - Data Management\\Forecast Data\"\n", ")" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [], "source": [ "s = Store()\n", "\n", "s.time_fw = TimeFramework(start=c.start, end=c.end)\n", "mipf = Mipf(\n", " start=s.time_fw.start, \n", " end=s.time_fw.end, \n", " tidy=True, \n", " include_nextQ=True, \n", " folder_path=r\"C:\\Users\\Shahla Huseynova\\Recoy\\Recoy - Documents\\03 - Libraries\\12 - Data Management\\Forecast Data\"\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def setup_model(c):\n", " s = Store()\n", " \n", " s.time_fw = TimeFramework(start=c.start, end=c.end)\n", " mipf = Mipf(\n", " start=s.time_fw.start, \n", " end=s.time_fw.end, \n", " tidy=True, \n", " include_nextQ=True, \n", " folder_path=r\"C:\\Users\\Shahla Huseynova\\Recoy\\Recoy - Documents\\03 - Libraries\\12 - Data Management\\Forecast Data\"\n", " ).data\n", " \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.afrr_case = CaseStudy(time_fw=s.time_fw, freq='1T', name='Optimisation + aFRR', data=mipf)\n", " \n", " s.cases = list(CaseStudy.instances.values())\n", " s.optcases = [s.hpcase, s.hpcase_sde, s.optcase1, s.afrr_case]\n", " s.sde_cases = [s.hpcase_sde, s.optcase1, s.afrr_case]\n", " \n", " s.demand = load_demand_data(c, s)\n", " return s\n", "\n", "s = setup_model(c)" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\SHAHLA~1\\AppData\\Local\\Temp/ipykernel_2612/4233922362.py:3: FutureWarning:\n", "\n", "Argument `closed` is deprecated in favor of `inclusive`.\n", "\n" ] } ], "source": [ "demand = load_demand_data(c, s)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "demand.resample('15T').mean()[['Tsource (VDG)', 'Tsink (VDG)']].iplot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "demand.describe()" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "## Load in data" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def add_afrr_prices(c, case):\n", " try:\n", " aFRR_signal = pd.read_csv(f'data/aFRR_{c.start}.csv', delimiter=';', decimal=',', index_col='datetime')\n", " aFRR_signal.index = case.data.index\n", " except:\n", " data = get_tennet_data('balansdelta2017', pd.to_datetime(c.start), pd.to_datetime(c.end))\n", " data.index = data[[\"datum\", \"tijd\"]].apply(lambda x: \" \".join(x), axis=1)\n", " data.index = pd.to_datetime(data.index, format=\"%d-%m-%Y %H:%M\").tz_localize(\n", " \"Europe/Amsterdam\", ambiguous=True\n", " )\n", " data = data[~data.index.duplicated(keep=\"first\")]\n", " date_ix = pd.date_range(\n", " data.index[0], data.index[-1], freq=\"1T\", tz=\"Europe/Amsterdam\"\n", " )\n", " data = data.reindex(date_ix)\n", " aFRR_signal = data[['Hoogste_prijs_opregelen', 'Mid_prijs_opregelen', 'Laagste_prijs_afregelen']]\n", " aFRR_signal.to_csv(f'data/aFRR_{c.start}.csv', sep=';', decimal=',', index_label='datetime')\n", "\n", " try:\n", " aFRR_prices = pd.read_csv(f'data/aFRR_prices_{c.start}.csv', delimiter=';', decimal=',', index_col='datetime')\n", " aFRR_prices.index = case.data.index\n", " except:\n", " data = get_aFRR_prices_nl(pd.to_datetime(c.start), pd.to_datetime(c.end))\n", " data.index = pd.date_range(\n", " start=c.start,\n", " end=pd.to_datetime(c.end) + timedelta(days=1),\n", " tz='Europe/Amsterdam', \n", " freq='15T', \n", " closed='left'\n", " )\n", " aFRR_prices = data.reindex(case.data.index, method='ffill')\n", " aFRR_prices.to_csv(f'data/aFRR_prices_{c.start}.csv', sep=';', decimal=',', index_label='datetime')\n", " \n", " case.data = pd.concat([case.data, aFRR_signal, aFRR_prices], axis=1)\n", " return case" ] }, { "cell_type": "code", "execution_count": null, "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": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def load_data(c, s):\n", " s.afrr_case = add_afrr_prices(c, s.afrr_case)\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", " for case in [s.afrr_case]:\n", " case.data['Hoogste_prijs_opregelen'] = multiply_by_factor(case.data['Hoogste_prijs_opregelen'], c.e_price_multiplier)\n", " case.data['Hoogste_prijs_opregelen'] = increase_volatility_by_factor(case.data['Hoogste_prijs_opregelen'], c.e_price_volatility_multiplier)\n", " case.data['aFRR_up'] = multiply_by_factor(case.data['aFRR_up'], c.e_price_multiplier)\n", " case.data['aFRR_up'] = increase_volatility_by_factor(case.data['aFRR_up'], c.e_price_volatility_multiplier)\n", " \n", " s.demand[['Tsource (VDG)', 'Tsource (NDG)']] += c.tsource_delta\n", " s.demand[['Tsink (VDG)', 'Tsink (NDG)']] += 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": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "## Assets" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "### COP curve" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "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": [ "cop_curve(140, 13)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cop_curve(140, 73)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "heatpump = Heatpump(\n", " name='Heatpump',\n", " max_th_power=1,\n", " min_th_power=0,\n", " cop_curve=cop_curve\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#heatpump.get_cop(heat_output=load, Tsink=140, Tsource=13)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#heatpump.get_cop(heat_output=load, Tsink=140, Tsource=99)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "import itertools\n", "\n", "source_Ts = np.arange(25, 75) + 273\n", "sink_Ts = np.arange(80, 170) + 273\n", "\n", "df = pd.DataFrame(columns=list(sink_Ts), index=list(source_Ts))\n", "for sourceT, sinkT in itertools.product(source_Ts, sink_Ts):\n", " df.loc[sourceT, sinkT] = heatpump.get_cop(heat_output=0.5, Tsink=sinkT, Tsource=sourceT)\n", " \n", "#df.to_csv('cops_at_50perc_load.csv', sep=';', decimal=',')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "sourceT = 63 + 273\n", "sinkT = 140 + 273\n", "loads = np.arange(0, 1, 0.01)\n", "\n", "cop_series = pd.Series(index=loads, dtype='float')\n", "load_series = pd.Series(index=loads, dtype='float')\n", "for load in loads:\n", " cop = heatpump.get_cop(heat_output=load, Tsink=sinkT, Tsource=sourceT)\n", " cop_series[load] = cop\n", " load_series[load] = load / cop " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "fig_cop_curve = cop_series.iplot(title=f'Heatpump COP curve at Tsource={sourceT} and Tsink={sinkT}', yTitle='COP', xTitle='Thermal load in %', colors=recoygreen, asFigure=True, dimensions=(600,400))\n", "fig_cop_curve" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig_load_curve = load_series.iplot(title=f'Heatpump Load curve at Tsource={sourceT} and Tsink={sinkT}', yTitle='E-load', xTitle='Thermal load in %', colors=recoygreen, asFigure=True, dimensions=(600,400))\n", "fig_load_curve" ] }, { "cell_type": "code", "execution_count": null, "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": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "sourceT = 63 + 273\n", "sinkT = 140 + 273\n", "cop_curve_new(sourceT, sinkT)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "heatpump = Heatpump(\n", " name='Heatpump',\n", " max_th_power=1,\n", " min_th_power=0,\n", " cop_curve=cop_curve_new\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "import itertools\n", "\n", "source_Ts = np.arange(25, 75) + 273\n", "sink_Ts = np.arange(80, 170) + 273\n", "\n", "df = pd.DataFrame(columns=list(sink_Ts), index=list(source_Ts))\n", "for sourceT, sinkT in itertools.product(source_Ts, sink_Ts):\n", " df.loc[sourceT, sinkT] = heatpump.get_cop(heat_output=0.5, Tsink=sinkT, Tsource=sourceT)\n", " \n", "#df.to_csv('cops_at_50perc_load.csv', sep=';', decimal=',')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "sourceT = 63 + 273\n", "sinkT = 140 + 273\n", "loads = np.arange(0, 1, 0.01)\n", "\n", "cop_series = pd.Series(index=loads, dtype='float')\n", "load_series = pd.Series(index=loads, dtype='float')\n", "for load in loads:\n", " cop = heatpump.get_cop(heat_output=load, Tsink=sinkT, Tsource=sourceT)\n", " cop_series[load] = cop\n", " load_series[load] = load / cop " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "fig_cop_curve = cop_series.iplot(title=f'Heatpump COP curve at Tsource={sourceT} and Tsink={sinkT}', yTitle='COP', xTitle='Thermal load in %', colors=recoygreen, asFigure=True, dimensions=(600,400))\n", "fig_cop_curve" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig_load_curve = load_series.iplot(title=f'Heatpump Load curve at Tsource={sourceT} and Tsink={sinkT}', yTitle='E-load', xTitle='Thermal load in %', colors=recoygreen, asFigure=True, dimensions=(600,400))\n", "fig_load_curve" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "### Create and assign assets" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def create_and_assign_assets(c, s):\n", " heatpump_vdg = Heatpump(\n", " name='Heatpump VDG',\n", " max_th_power=c.hp_vdg_e_power,\n", " min_th_power=c.hp_vdg_e_power * c.hp_min_load,\n", " cop_curve=cop_curve_new\n", " )\n", "\n", " heatpump_ndg = Heatpump(\n", " name='Heatpump NDG',\n", " max_th_power=c.hp_ndg_e_power,\n", " min_th_power=c.hp_ndg_e_power * c.hp_min_load,\n", " cop_curve=cop_curve_new\n", " )\n", "\n", " capex_vdg = c.hp_capex*(heatpump_vdg.max_th_power) \n", " capex_ndg = c.hp_capex*(heatpump_ndg.max_th_power)\n", " heatpump_vdg.set_financials(capex=capex_vdg, opex=c.hp_opex*capex_vdg, devex=c.hp_devex*capex_vdg, lifetime=25)\n", " heatpump_ndg.set_financials(capex=capex_ndg, opex=c.hp_opex*capex_ndg, devex=c.hp_devex*capex_ndg, 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", " s.baseline.add_asset(gasboiler)\n", " s.hpcase.add_asset(heatpump_vdg)\n", " s.hpcase.add_asset(heatpump_ndg)\n", " s.hpcase_sde.add_asset(heatpump_vdg)\n", " s.hpcase_sde.add_asset(heatpump_ndg)\n", " s.optcase1.add_asset(heatpump_vdg)\n", " s.optcase1.add_asset(heatpump_ndg)\n", " s.optcase1.add_asset(gasboiler)\n", " s.afrr_case.add_asset(heatpump_vdg)\n", " s.afrr_case.add_asset(heatpump_ndg)\n", " s.afrr_case.add_asset(gasboiler)\n", " return s\n", "\n", "s = create_and_assign_assets(c, s)" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "## Optimization" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "### Strategies" ] }, { "cell_type": "code", "execution_count": null, "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)'] + data['MW (NDG)']).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)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def hponly(case):\n", " hp_vdg = case.assets['Heatpump VDG']\n", " hp_ndg = case.assets['Heatpump NDG']\n", " demand_vdg = case.data['MW (VDG)'].to_list()\n", " demand_ndg = case.data['MW (NDG)'].to_list()\n", " Tsink_vdg = case.data['Tsink (VDG)'].to_list()\n", " Tsink_ndg = case.data['Tsink (NDG)'].to_list()\n", " Tsource_vdg = case.data['Tsource (VDG)'].to_list()\n", " Tsource_ndg = case.data['Tsource (NDG)'].to_list()\n", "\n", " hp_vdg_input = [0] * len(case.data)\n", " hp_ndg_input = [0] * len(case.data)\n", " hp_vdg_output = [0] * len(case.data)\n", " hp_ndg_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_vdg_input[m], hp_vdg_output[m] = hp_vdg.set_heat_output(\n", " heat_output=demand,\n", " Tsink=Tsink_vdg[m],\n", " Tsource=Tsource_vdg[m]\n", " )\n", "\n", " demand = demand_ndg[m]\n", " if demand != 0:\n", " hp_ndg_input[m], hp_ndg_output[m] = hp_ndg.set_heat_output(\n", " heat_output=demand_ndg[m],\n", " Tsink=Tsink_ndg[m],\n", " Tsource=Tsource_ndg[m]\n", " )\n", "\n", " case.data['hp_output_MW'] = np.array(hp_vdg_output) + np.array(hp_ndg_output)\n", " case.data['hp_input_MW'] = np.array(hp_vdg_input) + np.array(hp_ndg_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(3)" ] }, { "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", " hp_ndg = case.assets['Heatpump NDG']\n", " demand_vdg = case.data['MW (VDG)'].round(decimals).to_list()\n", " demand_ndg = case.data['MW (NDG)'].round(decimals).to_list()\n", " Tsink_vdg = case.data['Tsink (VDG)'].round(decimals).to_list()\n", " Tsink_ndg = case.data['Tsink (NDG)'].round(decimals).to_list()\n", " Tsource_vdg = case.data['Tsource (VDG)'].round(decimals).to_list()\n", " Tsource_ndg = case.data['Tsource (NDG)'].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", " dem_ngd = demand_ndg[m]\n", " if dem_ngd != 0:\n", " max_load = min(hp_ndg.max_th_power, dem_ngd)\n", " min_load = hp_ndg.min_th_power\n", " Tsink = Tsink_ndg[m]\n", " Tsource = Tsource_ndg[m]\n", " cop_max_load = hp_ndg.get_cop(heat_output=max_load, Tsink=Tsink, Tsource=Tsource)\n", " cop_min_load = hp_ndg.get_cop(heat_output=min_load, Tsink=Tsink, Tsource=Tsource)\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,\n", " alt_heat_price=gas_prices[m] + co2_prices[m] + eb_ode_g/case.assets['Gasboiler'].efficiency,\n", " demand=dem_ngd\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_ndg_input, hp_ndg_output = hp_ndg.set_heat_output(max_load, Tsink=Tsink, Tsource=Tsource)\n", " else:\n", " hp_ndg_input, hp_ndg_output = hp_ndg.set_heat_output(min_load, Tsink=Tsink, Tsource=Tsource)\n", " else:\n", " hp_ndg_input, hp_ndg_output = (0, 0)\n", "\n", " hp_out = hp_vdg_output + hp_ndg_output\n", " hp_output[m] = hp_out\n", " hp_input[m] = hp_vdg_input + hp_ndg_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)" ] }, { "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", " hp_ndg = case.assets['Heatpump NDG']\n", " \n", " capacity = 0\n", " for hp in [hp_vdg, hp_ndg]:\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": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def aFRR_optimisation(case, s):\n", " s.afrr_case.afrr_capacity = calc_afrr_capacity(s.afrr_case)\n", " gb = case.assets['Gasboiler']\n", " hp_vdg = case.assets['Heatpump VDG']\n", " hp_ndg = case.assets['Heatpump NDG']\n", " demand_vdg = case.data['MW (VDG)'].to_list()\n", " demand_ndg = case.data['MW (NDG)'].to_list()\n", " Tsink_vdg = case.data['Tsink (VDG)'].to_list()\n", " Tsink_ndg = case.data['Tsink (NDG)'].to_list()\n", " Tsource_vdg = case.data['Tsource (VDG)'].to_list()\n", " Tsource_ndg = case.data['Tsource (NDG)'].to_list()\n", " afrr_up = case.data['Hoogste_prijs_opregelen'].fillna(-999).to_list()\n", " gas_prices = case.data['Gas prices (€/MWh)'].to_list()\n", " co2_prices = case.data['CO2 prices (€/MWh)'].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=afrr_up[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=afrr_up[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", " dem_ngd = demand_ndg[m]\n", " if dem_ngd != 0:\n", " max_load = min(hp_ndg.max_th_power, dem_ngd)\n", " min_load = hp_ndg.min_th_power\n", " Tsink = Tsink_ndg[m]\n", " Tsource = Tsource_ndg[m]\n", " cop_max_load = hp_ndg.get_cop(heat_output=max_load, Tsink=Tsink, Tsource=Tsource)\n", " cop_min_load = hp_ndg.get_cop(heat_output=min_load, Tsink=Tsink, Tsource=Tsource)\n", " \n", " cost_full_load = cost_function(\n", " th_load=max_load,\n", " cop=cop_max_load,\n", " electricity_cost=afrr_up[m] + eb_ode_e,\n", " alt_heat_price=gas_prices[m] + co2_prices[m] + eb_ode_g/case.assets['Gasboiler'].efficiency,\n", " demand=dem_ngd\n", " )\n", " \n", " cost_min_load = cost_function(\n", " th_load=min_load,\n", " cop=cop_min_load,\n", " electricity_cost=afrr_up[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_ndg_input, hp_ndg_output = hp_ndg.set_heat_output(max_load, Tsink=Tsink, Tsource=Tsource)\n", " else:\n", " hp_ndg_input, hp_ndg_output = hp_ndg.set_heat_output(min_load, Tsink=Tsink, Tsource=Tsource)\n", " else:\n", " hp_ndg_input, hp_ndg_output = (0, 0)\n", "\n", " hp_out = hp_vdg_output + hp_ndg_output\n", " hp_output[m] = hp_out\n", " hp_input[m] = hp_vdg_input + hp_ndg_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)" ] }, { "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.assign_algorithm(baseline_sim)\n", " s.baseline.run()\n", " \n", " s.hpcase.assign_algorithm(hponly)\n", " s.hpcase.run()\n", " \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.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": "markdown", "metadata": { "tags": [ "exclude" ] }, "source": [ "## Financials" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "def calculate_sde_subsidy(c, s, case):\n", " hp_capacity = abs(case.assets['Heatpump VDG'].max_th_power) + abs(case.assets['Heatpump NDG'].max_th_power)\n", " case.full_load_hours = abs(case.data['hp_output_MWh'].sum() / hp_capacity)\n", "\n", " subsidized_hours = min(case.full_load_hours, 8000)\n", " subsidized_MWh = subsidized_hours * hp_capacity\n", "\n", " base_amount_per_MWh_th = round(c.sde_base_amount*0.173 + c.longterm_gas_price, 2)\n", " base_subsidy_amount = round(base_amount_per_MWh_th * subsidized_MWh, 2)\n", " long_term_gas_price_LHV = round(EURperHHV_to_EURperLHV(c.longterm_gas_price), 2)\n", " base_amount_gas = (2/3)*long_term_gas_price_LHV*0.9\n", " mean_ttf_price_LHV = round(EURperHHV_to_EURperLHV(case.data['Gas prices (€/MWh)'].mean()), 2)\n", " correction_amount_gas = max(mean_ttf_price_LHV, base_amount_gas)\n", " avoided_gas_consumption = subsidized_MWh/0.9\n", " correction_gas = round(correction_amount_gas * avoided_gas_consumption, 2)\n", "\n", " base_amount_co2 = (2/3)*c.longterm_co2_price\n", " avoided_co2_emission = avoided_gas_consumption * 0.226\n", " mean_ets_price = round(EURperHHV_to_EURperLHV(case.data['CO2 prices (€/ton)'].mean()), 2)\n", " correction_amount_co2 = max(base_amount_co2, mean_ets_price)\n", " correction_co2 = round(correction_amount_co2 * avoided_co2_emission, 2)\n", "\n", " sde_subsidy_corrected = max(base_subsidy_amount - (correction_gas + correction_co2), 0)\n", " sde_per_MWh_th = sde_subsidy_corrected / subsidized_MWh\n", " sde_per_MWh_e = sde_subsidy_corrected / case.data['hp_input_MWh'].abs().sum()\n", "\n", " case.sde_results = {\n", " 'base_amount': base_subsidy_amount,\n", " 'correction_gas': correction_gas,\n", " 'correction_co2': correction_co2,\n", " 'corrected_amount': sde_subsidy_corrected,\n", " 'sde_per_MWh_th': sde_per_MWh_th,\n", " 'sde_per_MWh_e': sde_per_MWh_e\n", " }\n", " return case" ] }, { "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.hpcase_sde.generate_electr_market_results(nom_col='hp_input_MWh', real_col='hp_input_MWh')\n", " \n", " s.optcase1.data['DA Nom'] = s.hpcase.data['hp_input_MWh'] * c.day_ahead_buying_perc\n", " s.optcase1.generate_electr_market_results(nom_col='DA Nom', real_col='hp_input_MWh')\n", " \n", " s.afrr_case.data['POS'] = s.afrr_case.data['aFRR_up'].fillna(value=s.afrr_case.data['POS'])\n", " s.afrr_case.data['DA Nom'] = s.hpcase.data['hp_input_MWh']\n", " s.afrr_case.generate_electr_market_results(nom_col='DA Nom', real_col='hp_input_MWh')\n", " s.afrr_case.add_cashflow('aFRR capacity fee (€)', c.afrr_capacity_fee * s.afrr_case.afrr_capacity)\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.hpcase_sde, s.optcase1, s.afrr_case]:\n", " case.add_eb_ode(commodity='electricity', tax_bracket=c.tax_bracket_e)\n", " case.add_grid_costs(\n", " power_MW_col='hp_input_MW',\n", " grid_operator=c.grid_operator,\n", " year=2020, \n", " connection_type=c.connection_type\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", " \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()\n", "\n", " for case in [s.hpcase, 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": [ "s.optcase1.cashflows" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s.optcase1.ebitda_calc" ] }, { "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)', 'Tsource (NDG)', 'Tsink (NDG)']].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": { "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=cop_curve\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "Tsrc_vdg, Tsnk_vdg, Tsrc_ndg, Tsnk_ndg = s.demand[['Tsource (VDG)', 'Tsink (VDG)', 'Tsource (NDG)', 'Tsink (NDG)']].mean().to_list()\n", "mean_gas_price = (s.optcase1.data['Gas prices (€/MWh)'].mean() \n", " + s.eb_ode_g \n", " + s.optcase1.data['CO2 prices (€/MWh)'].mean()\n", " )\n", "\n", "max_gas_price = (s.optcase1.data['Gas prices (€/MWh)'].max() \n", " + s.eb_ode_g \n", " + s.optcase1.data['CO2 prices (€/MWh)'].max()\n", " )\n", "\n", "min_gas_price = (s.optcase1.data['Gas prices (€/MWh)'].min() \n", " + s.eb_ode_g \n", " + s.optcase1.data['CO2 prices (€/MWh)'].min()\n", " )\n", "\n", "Tsrc_vdg_min, Tsnk_vdg_min, Tsrc_ndg_min, Tsnk_ndg_min = s.demand[['Tsource (VDG)', 'Tsink (VDG)', 'Tsource (NDG)', 'Tsink (NDG)']].min().to_list()\n", "Tsrc_vdg_max, Tsnk_vdg_max, Tsrc_ndg_max, Tsnk_ndg_max = s.demand[['Tsource (VDG)', 'Tsink (VDG)', 'Tsource (NDG)', 'Tsink (NDG)']].max().to_list()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "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": [ "import plotly.graph_objects as go\n", "import numpy as np\n", "\n", "fig = go.Figure()\n", "\n", "configs = {\n", " 'mean': [mean_gas_price, Tsnk_vdg, Tsrc_vdg],\n", " 'unfav_gas': [min_gas_price, Tsnk_vdg, Tsrc_vdg],\n", " 'fav_gas': [max_gas_price, Tsnk_vdg, Tsrc_vdg],\n", " 'unfav_all': [min_gas_price, Tsnk_vdg_max, Tsrc_vdg_min],\n", " 'fav_all': [max_gas_price, Tsnk_vdg_min, Tsrc_vdg_max],\n", "}\n", "\n", "for name, config in configs.items():\n", " trace = create_load_trace(*config, name)\n", " fig.add_trace(trace)\n", "\n", "fig.update_layout(title='Switch prices for different configurations')\n", "fig.show()" ] }, { "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": [ "fig_optcase = s.optcase1.data[['hp_output_MW', 'gb_output_MW', 'NEG']].loc[date, :].iplot(\n", " subplots=True, \n", " title=f'Hybrid case on {date}',\n", " subplot_titles=['Heatpump output (MW)', 'Steam boiler output (MW)', 'Imbalance price (€/MWh)'],\n", " legend=False,\n", " dimensions=(800, 600),\n", " colors=[recoydarkblue, recoygreen, recoyred],\n", " asFigure=True,\n", " shape=(3,1)\n", ")\n", "fig_optcase" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "date = '2019-09-04'\n", "\n", "fig_optcase2 = s.optcase1.data[['hp_output_MW', 'gb_output_MW', 'NEG']].loc[date, :].iplot(\n", " subplots=True, \n", " title=f'Hybrid case on {date}',\n", " subplot_titles=['Heatpump output (MW)', 'Steam boiler output (MW)', 'Imbalance price (€/MWh)'],\n", " legend=False,\n", " dimensions=(800, 600),\n", " colors=[recoydarkblue, recoygreen, recoyred],\n", " asFigure=True,\n", " shape=(3,1)\n", ")\n", "fig_optcase2" ] }, { "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.hpcase_sde, 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": { "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)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "exclude" ] }, "outputs": [], "source": [ "_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": { "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, _series.max() * 2]\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": [ "(SingleFigureComparison(s.optcases, 'mean_cop', label='COP').report).iplot(\n", " kind='bar',\n", " title='Mean COP by Casestudy',\n", " colors=recoygreen,\n", " dimensions=(600, 400),\n", " yTitle='Years'\n", ")" ] }, { "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 = 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": [ "%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": "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": "raw", "metadata": { "tags": [ "exclude" ] }, "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "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.9.5" }, "toc-autonumbering": false, "toc-showmarkdowntxt": false, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "008cc87f13974b39a6a284b44e25910c": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "196b71c110874552bb1411e1e9ca6272": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "3af31624ccf94328bf958e1ebe30110d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "446703e48d4849b791ca7843280088cd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "529b7771d1ae4fdbb067fcaf5b0608ed": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "54920e87dca64a94bb90c60e251da28d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "6968c912751e4b29bcff5971969531b7": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "79a2e99d1f754ef4a2ae9f21bc18880d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_008cc87f13974b39a6a284b44e25910c", "style": "IPY_MODEL_529b7771d1ae4fdbb067fcaf5b0608ed", "value": " 525600/525600 [00:15<00:00, 43942.23it/s]" } }, "7aa071e254c641b2ace0826feaa1c546": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_ddf1fe884d6e418987268d202dd03c97", "style": "IPY_MODEL_6968c912751e4b29bcff5971969531b7", "value": "100%" } }, "9cc8bbb7a6ec49b9abfea5376d5fd5d4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_fcbd6c499ec745ed8994ffdffd5b0870", "IPY_MODEL_a65da8c3e22d4ad3a1f2b6fa919125f0", "IPY_MODEL_d037e68bb6304420b37186172fe9e6cc" ], "layout": "IPY_MODEL_a35a411843ac46cebbe2205f8a10aa62" } }, "a35a411843ac46cebbe2205f8a10aa62": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "a65da8c3e22d4ad3a1f2b6fa919125f0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_c6a7e7cc96214fa0aa5c34b44740442c", "max": 525600, "style": "IPY_MODEL_b8d0859b631a414d89253e4516302294", "value": 525600 } }, "b8d0859b631a414d89253e4516302294": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "c33330a5a8414cbebea873c44d675a1b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_54920e87dca64a94bb90c60e251da28d", "max": 525600, "style": "IPY_MODEL_196b71c110874552bb1411e1e9ca6272", "value": 525600 } }, "c6a7e7cc96214fa0aa5c34b44740442c": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d037e68bb6304420b37186172fe9e6cc": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_fa5102d8881e49109ba066ba2fd2253a", "style": "IPY_MODEL_446703e48d4849b791ca7843280088cd", "value": " 525600/525600 [00:15<00:00, 79948.51it/s]" } }, "ddf1fe884d6e418987268d202dd03c97": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "e4f5fac3a78c4957b78a24651245f54d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "e6be80771d6440c2bddd59813eb8f51d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_7aa071e254c641b2ace0826feaa1c546", "IPY_MODEL_c33330a5a8414cbebea873c44d675a1b", "IPY_MODEL_79a2e99d1f754ef4a2ae9f21bc18880d" ], "layout": "IPY_MODEL_f02b4413c8104a4582bdc31d2b3de2c3" } }, "f02b4413c8104a4582bdc31d2b3de2c3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fa5102d8881e49109ba066ba2fd2253a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fcbd6c499ec745ed8994ffdffd5b0870": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_3af31624ccf94328bf958e1ebe30110d", "style": "IPY_MODEL_e4f5fac3a78c4957b78a24651245f54d", "value": "100%" } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }