diff --git a/teaching/03_Energy_system_expansion_planning.ipynb b/teaching/03_Sector_coupling.ipynb similarity index 80% rename from teaching/03_Energy_system_expansion_planning.ipynb rename to teaching/03_Sector_coupling.ipynb index 90bf6c8f..2eb813a3 100644 --- a/teaching/03_Energy_system_expansion_planning.ipynb +++ b/teaching/03_Sector_coupling.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Example 3: Energy system expansion planning" + "# Example 3: Sector coupling" ] }, { @@ -93,8 +93,8 @@ " mydata[\"sites\"] = [\"OldTown\", \"NewTown\", \"FutureTown\"]\n", " mydata[\"commodities\"] = [\"Elec\", \"Heat\", \"Gas\", \"SolarPV\", \"SolarThermal\", \"CO2\", \"AmbientTemp\"]\n", " mydata[\"com_type\"] = [\"Demand\", \"Stock\", \"Env\", \"SupIm\"] # (i.e. SupIm, Demand, Stock, Env)\n", - " mydata[\"process\"] = [\"Gas plant\", \"PV plant\", \n", - " \"Gas boiler\", \"Solar thermal\",\n", + " mydata[\"process\"] = [\"Gas CC\", \"PV plant\", \n", + " \"Gas heating plant\", \"Solar thermal\",\n", " \"Gas CHP\", # CHP: combined heat and power\n", " \"Heat pump\", \"Electric resistance\"] \n", " mydata[\"cost_type\"] = ['Invest', 'Fixed', 'Variable', 'Fuel', 'Environmental']\n", @@ -104,7 +104,7 @@ " mydata[\"com_max\"] = {}\n", " for stf in mydata[\"support_timeframes\"]:\n", " for sit in mydata[\"sites\"]:\n", - " mydata[\"com_prices\"].update({(stf, sit, \"Gas\", \"Stock\"): 24.8,\n", + " mydata[\"com_prices\"].update({(stf, sit, \"Gas\", \"Stock\"): 25.2,\n", " (stf, sit, \"SolarPV\", \"SupIm\"): 0,\n", " (stf, sit, \"SolarThermal\", \"SupIm\"): 0,\n", " (stf, sit, \"AmbientTemp\", \"SupIm\"): 0,\n", @@ -122,18 +122,18 @@ "\n", " # Dictionaries - processes\n", " mydata[\"pro_capup\"] = {\n", - " (2021, \"OldTown\", \"Gas boiler\"): np.inf, # for heat\n", + " (2021, \"OldTown\", \"Gas heating plant\"): np.inf, # for heat\n", " (2021, \"OldTown\", \"Solar thermal\"): np.inf, # for heat\n", - " (2021, \"OldTown\", \"Gas plant\"): np.inf, # for electricity\n", + " (2021, \"OldTown\", \"Gas CC\"): np.inf, # for electricity\n", " (2021, \"OldTown\", \"PV plant\"): np.inf, # for electricity\n", " \n", - " (2021, \"NewTown\", \"Gas boiler\"): np.inf, # for heat\n", + " (2021, \"NewTown\", \"Gas heating plant\"): np.inf, # for heat\n", " (2021, \"NewTown\", \"Solar thermal\"): np.inf, # for heat\n", - " (2021, \"NewTown\", \"Gas plant\"): np.inf, # for electricity\n", + " (2021, \"NewTown\", \"Gas CC\"): np.inf, # for electricity\n", " (2021, \"NewTown\", \"PV plant\"): np.inf, # for electricity\n", " (2021, \"NewTown\", \"Gas CHP\"): np.inf, # for electricity and heat\n", " \n", - " (2021, \"FutureTown\", \"Gas plant\"): np.inf, # for electricity\n", + " (2021, \"FutureTown\", \"Gas CC\"): np.inf, # for electricity\n", " (2021, \"FutureTown\", \"PV plant\"): np.inf, # for electricity\n", " (2021, \"FutureTown\", \"Heat pump\"): np.inf, # for heat from electricity\n", " (2021, \"FutureTown\", \"Electric resistance\"): np.inf, # for heat from electricity\n", @@ -153,22 +153,22 @@ " mydata[\"pro_depreciation\"] = mydata[\"pro_instcap\"].copy()\n", " \n", " for (stf, sit, pro) in mydata[\"pro_invcost\"].keys():\n", - " mydata[\"pro_wacc\"][(stf, sit, pro)] = 0.07 # weigthed average cost of capital (in % of capital cost)\n", - " if pro == \"Gas plant\":\n", - " mydata[\"pro_invcost\"][(stf, sit, pro)] = 850000\n", - " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 21250\n", - " mydata[\"pro_varcost\"][(stf, sit, pro)] = 2\n", + " mydata[\"pro_wacc\"][(stf, sit, pro)] = 0.05 # weigthed average cost of capital (in % of capital cost)\n", + " if pro == \"Gas CC\":\n", + " mydata[\"pro_invcost\"][(stf, sit, pro)] = 900000\n", + " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 22000\n", + " mydata[\"pro_varcost\"][(stf, sit, pro)] = 4\n", " mydata[\"pro_depreciation\"][(stf, sit, pro)] = 30\n", " if pro == \"PV plant\":\n", - " mydata[\"pro_invcost\"][(stf, sit, pro)] = 800000\n", - " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 13600\n", + " mydata[\"pro_invcost\"][(stf, sit, pro)] = 700000\n", + " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 22000\n", " mydata[\"pro_varcost\"][(stf, sit, pro)] = 0\n", " mydata[\"pro_depreciation\"][(stf, sit, pro)] = 25\n", - " if pro == \"Gas boiler\":\n", - " mydata[\"pro_invcost\"][(stf, sit, pro)] = 500000\n", - " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 25000\n", - " mydata[\"pro_varcost\"][(stf, sit, pro)] = 3\n", - " mydata[\"pro_depreciation\"][(stf, sit, pro)] = 15\n", + " if pro == \"Gas heating plant\":\n", + " mydata[\"pro_invcost\"][(stf, sit, pro)] = 400000\n", + " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 12000\n", + " mydata[\"pro_varcost\"][(stf, sit, pro)] = 6.7\n", + " mydata[\"pro_depreciation\"][(stf, sit, pro)] = 35\n", " if pro == \"Solar thermal\":\n", " mydata[\"pro_invcost\"][(stf, sit, pro)] = 800000\n", " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 13600\n", @@ -185,16 +185,16 @@ " mydata[\"pro_varcost\"][(stf, sit, pro)] = 0\n", " mydata[\"pro_depreciation\"][(stf, sit, pro)] = 15\n", " if pro == \"Electric resistance\":\n", - " mydata[\"pro_invcost\"][(stf, sit, pro)] = 100000\n", + " mydata[\"pro_invcost\"][(stf, sit, pro)] = 150000\n", " mydata[\"pro_fixcost\"][(stf, sit, pro)] = 5000\n", " mydata[\"pro_varcost\"][(stf, sit, pro)] = 0\n", " mydata[\"pro_depreciation\"][(stf, sit, pro)] = 10\n", "\n", " # Dictionaries - conversion ratios\n", " mydata[\"ratio_in\"] = {\n", - " (2021, \"Gas plant\", \"Gas\"): 1.67,\n", + " (2021, \"Gas CC\", \"Gas\"): 1.67,\n", " (2021, \"PV plant\", \"SolarPV\"): 1,\n", - " (2021, \"Gas boiler\", \"Gas\"): 1.09,\n", + " (2021, \"Gas heating plant\", \"Gas\"): 1.25,\n", " (2021, \"Solar thermal\", \"SolarThermal\"): 1,\n", " (2021, \"Gas CHP\", \"Gas\"): 2.22,\n", " (2021, \"Heat pump\", \"Elec\"): 1,\n", @@ -202,17 +202,17 @@ " (2021, \"Electric resistance\", \"Elec\"): 1,\n", " }\n", " mydata[\"ratio_out\"] = {\n", - " (2021, \"Gas plant\", \"Elec\"): 1,\n", - " (2021, \"Gas plant\", \"CO2\"): 0.3,\n", + " (2021, \"Gas CC\", \"Elec\"): 1,\n", + " (2021, \"Gas CC\", \"CO2\"): 0.3,\n", " (2021, \"PV plant\", \"Elec\"): 1,\n", - " (2021, \"Gas boiler\", \"Heat\"): 1,\n", - " (2021, \"Gas boiler\", \"CO2\"): 0.2,\n", + " (2021, \"Gas heating plant\", \"Heat\"): 1,\n", + " (2021, \"Gas heating plant\", \"CO2\"): 0.23,\n", " (2021, \"Solar thermal\", \"Heat\"): 1,\n", " (2021, \"Gas CHP\", \"Elec\"): 1,\n", " (2021, \"Gas CHP\", \"Heat\"): 0.88,\n", " (2021, \"Gas CHP\", \"CO2\"): 0.4,\n", " (2021, \"Heat pump\", \"Heat\"): 5,\n", - " (2021, \"Electric resistance\", \"Heat\"): 2.7,\n", + " (2021, \"Electric resistance\", \"Heat\"): 0.95,\n", " }\n", "\n", " # Dictionaries - time series\n", @@ -222,13 +222,14 @@ " ts_SolarPV = [0, 0, 0, 0, 0, 0.05, 0.1, 0.15, 0.22, 0.35, 0.4, 0.55, 0.5, 0.45, 0.39, 0.35, 0.3, 0.2, 0.05, 0, 0, 0, 0, 0]\n", " ts_SolarThermal = [0, 0, 0, 0, 0, 0.05, 0.1, 0.15, 0.22, 0.35, 0.4, 0.55, 0.5, 0.45, 0.39, 0.35, 0.3, 0.2, 0.05, 0, 0, 0, 0, 0]\n", "\n", - " # Scale the ambient temperature time series so that the efficiency is 1 if temperature <= 5°C,\n", - " # and decreases to 0.2 if >= 25°C\n", + " # Scale the ambient temperature time series so that the efficiency increases almost linearly with the temperature\n", + " # for the range that we are interested in.\n", + " # Approximation: https://www.researchgate.net/publication/273458507_A_new_two-degree-of-freedom_space_heating_model_for_demand_response/figures?lo=1\n", " ts_AmbientTemp = np.array(ts_AmbientTemp, dtype=float)\n", - " ts_AmbientTemp[np.array(ts_AmbientTemp)<=5] = 1\n", - " ts_AmbientTemp[ts_AmbientTemp>=25] = 0.2\n", - " ts_filter = ((ts_AmbientTemp>5) & (ts_AmbientTemp<25))\n", - " ts_AmbientTemp[ts_filter] = np.exp(np.log(5)/20*(5-ts_AmbientTemp[ts_filter]))\n", + " ts_AmbientTemp = 0.03 * (ts_AmbientTemp + 15) + 3.1\n", + " ts_AmbientTemp[ts_AmbientTemp<=1] = 1\n", + " # What we have here is a COP... let's scale it so that it is lower than 1\n", + " ts_AmbientTemp = ts_AmbientTemp / 5\n", "\n", " mydata[\"demand\"] = {}\n", " mydata[\"supim\"] = {}\n", @@ -264,8 +265,8 @@ "outputs": [], "source": [ "# Generate the data for the reference scenario\n", - "data_ref = generate_scenario()\n", - "%matplotlib inline" + "%matplotlib inline\n", + "data_ref = generate_scenario()" ] }, { @@ -324,7 +325,7 @@ "ax2.set_title(\"Temperature\")\n", "plt.plot(AmbientTemp, color=\"red\")\n", "ax2b = ax2.twinx()\n", - "ax2b.set_ylim(0, 6)\n", + "ax2b.set_ylim(3.5, 4.5)\n", "ax2b.set_ylabel(\"Heat pump COP [1]\")\n", "ax2b.plot(ts_AmbientTemp[1:], color=\"black\")" ] @@ -345,7 +346,31 @@ "metadata": {}, "outputs": [], "source": [ - "data_ref[\"pro_invcost\"]" + "# Retrieve the SolarPV and SolarThermal timeseries\n", + "ts_SolarPV = []\n", + "ts_SolarThermal = []\n", + "for (stf, sit, com, tm), v in data_ref[\"supim\"].items():\n", + " if ((sit==\"OldTown\") and (com==\"SolarPV\")):\n", + " ts_SolarPV.append(v)\n", + " if ((sit==\"OldTown\") and (com==\"SolarThermal\")):\n", + " ts_SolarThermal.append(v)\n", + "# Plot them side by side\n", + "fig1 = plt.figure(figsize=[12, 5])\n", + "ax1a = fig1.add_subplot(1,2,1)\n", + "ax1a.set_xlim(1, 24)\n", + "ax1a.set_xlabel(\"tm\")\n", + "ax1a.set_ylim(0, 1)\n", + "ax1a.set_ylabel(\"SE Elec [MWh]\")\n", + "ax1a.set_title(\"SolarPV\")\n", + "plt.plot(ts_SolarPV, color=\"blue\")\n", + "fig1.add_subplot(1,2,2)\n", + "ax1b = fig1.add_subplot(1,2,2)\n", + "ax1b.set_xlim(1, 24)\n", + "ax1b.set_xlabel(\"tm\")\n", + "ax1b.set_ylim(0, 1)\n", + "ax1b.set_ylabel(\"SE Heat [MWh]\")\n", + "ax1b.set_title(\"SolarThermal\")\n", + "plt.plot(ts_SolarThermal, color=\"orange\")" ] }, { @@ -412,23 +437,24 @@ "# You can access the variables, for example the output of the processes\n", "supply_data = {}\n", "for (tm, stf, sit, com, com_type), x in model_ref.e_pro_out.items():\n", - " supply_data[(tm, stf, sit, com, com_type)] = pyo.value(x)\n", + " supply_data[(tm, sit, com, com_type)] = pyo.value(x)\n", "\n", - "df_supply = pd.DataFrame.from_dict(supply_data, orient=\"index\", columns=[\"SE [MWh]\"])\n", - "df_supply.index = pd.MultiIndex.from_tuples(df_supply.index, names=('t', 'stf', 'Site', 'Technology', 'Commodity'))\n", - "df_supply.reorder_levels([2,4,3,0,1])" + "df_supply = pd.DataFrame.from_dict(supply_data, orient=\"index\", columns=[\"SE [MWh] or emissions [t_CO2]\"])\n", + "df_supply.index = pd.MultiIndex.from_tuples(df_supply.index, names=('t', 'Site', 'Technology', 'Commodity'))\n", + "df_supply = df_supply.reorder_levels([1,3,2,0])\n", + "df_supply.head()" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "***\n", - "### Task\n", - "1. Report the most important results (new capacities, costs, power mix over time) using the techniques that we learned on Day 01.\n", - "2. Experiment with different scenarios (i.e. vary the settings when generating the data and solve the different models).\n", - "3. Analyze the results and draw some conclusions.\n", - "***" + "df_supply_elec = df_supply[df_supply.index.get_level_values(1) == 'Elec']\n", + "df_supply_elec = df_supply_elec.droplevel(1)\n", + "df_supply_elec.rename({\"SE [MWh] or emissions [kgCO2]\": \"SE [MWh]\"}, axis=1, inplace=True)\n", + "df_supply_elec.head()" ] }, { @@ -437,11 +463,9 @@ "source": [ "***\n", "## Homework\n", - "1. Add another energy sector (transportation). The demand of transportation can be expressed in vehicle-kilometers. The processes that convert gasoline/gas or electricity to vehicle-kilometers are conventional cars or battery electric vehicles (BEVs). Beware that we have not modeled storage so far.\n", - "2. Perform a sensitivity analysis:
\n", - " a. Experiment with different investment cost assumptions for BEVs
\n", - " b. Experiment with different CO2 prices
\n", - "3. In which town is it cheaper to decarbonize the system?\n", + "1. Report the most important results (new capacities, costs, power mix over time) using the techniques that we learned on Day 01.\n", + "2. Experiment with different scenarios (i.e. vary the settings when generating the data and solve the different models).\n", + "3. Analyze the results and draw some conclusions.\n", "***" ] }