diff --git a/tutorial/README.rst b/tutorial/README.rst index 4d012bcdd..3c9ce7cba 100644 --- a/tutorial/README.rst +++ b/tutorial/README.rst @@ -67,6 +67,7 @@ uses it to illustrate a range of framework features. 3. `Limit emissions using a tax `_ instead of a bound. 4. `Represent both coal and wind electricity `_, using a “firm capacity” formulation: each generation technology can supply some firm capacity, but the variable, renewable technology (wind) supplies less than coal. 5. Represent coal and wind electricity using a different, `“flexibility requirement” formulation `_, wherein wind *requires* and coal *supplies* flexibility. +6. `Variablity in energy supply and demand `_, by adding two sub-annual time steps (winter and summer). Austrian energy system ---------------------- diff --git a/tutorial/westeros/westeros_addon_technologies.ipynb b/tutorial/westeros/westeros_addon_technologies.ipynb new file mode 100644 index 000000000..25fe7ea93 --- /dev/null +++ b/tutorial/westeros/westeros_addon_technologies.ipynb @@ -0,0 +1,607 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Westeros Tutorial\n", + "## Adding additional technology functionalities: Introducing `addon` technologies\n", + "\n", + "This tutorial is concerned with is looking at how to add additional features, known as `addon` technologies such as carbon-capture-and-storage (CCS) retrofits, passout-turbines (for optional heat cogeneration) or cooling technologies, to existing technologies.\n", + "\n", + "There are several ways in which to tackle this issue. Lets take for example our `coal_ppl`. All of the above mentioned additional features could be implemented by introducing different modes for the `coal_ppl`. For example, heat cogeneration could be implemented as a separate `mode` of the `coal_ppl`, where instead of just generating electricity, heat can also be produced at the cost of reducing the amount of electricity generated. Another approach would make use of the generic `relations`, therefore linking the a newly added technology representing the passout-turbine with the activity of the `coal_ppl`. Both of these approaches have some downsides. Using a separate `mode` will not permit explicitly modelling investment costs and lifetimes associated with the asset being added to the `coal_ppl`. Generic relations are very flexible, but if too many of these exist, then the modell quickly becomes very hard to understand. Therefore, MESSAGEix offers an explicit `addon` formulation.\n", + "\n", + "What this means, is that the additional technology options are explicitly modelled as separate technologies, classified as `addon` technologies, linked to the activity of the technology to which they serve as additional configuration options, the parent technology, through specifying a `addon_conversion` factor. The activity of the `addon` technology can further be restricted to a minimum or maximum share of the activity of the parent technology." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adding heat co-generation capacibility to the `coal_ppl`\n", + "This tutorial will extend the current reference-energy-system to include a demand for heat and the necessary technologies to meet this demand. Heat will be generated via a `passout-turbine` which will be linked to the `coal_ppl` using the `addon` formulation.\n", + "\n", + "# *Add modified RES*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import ixmp as ix\n", + "import message_ix\n", + "\n", + "from message_ix.utils import make_df\n", + "\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mp = ix.Platform(dbtype='HSQLDB')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load the existing scenario '*baseline*' and clone to a new scenario '*addon_technology*' which we will extended to include heat cogeneration using the `addon` feature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = 'Westeros Electrified'\n", + "base = message_ix.Scenario(mp, model=model, scenario='baseline')\n", + "scen = base.clone(model, 'addon_technology', 'illustration of addon formulation', keep_solution=False)\n", + "scen.check_out()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Retrieve parameters to perform subsequent addition of parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "year_df = scen.vintage_and_active_years()\n", + "vintage_years, act_years = year_df['year_vtg'], year_df['year_act']\n", + "model_horizon = scen.set('year')\n", + "country = 'Westeros'\n", + "gdp_profile = pd.Series([1., 1.5, 1.9], index=pd.Index([700, 710, 720], name='Time'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define helper dataframes used for subsequent operations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "base_input = {\n", + " 'node_loc': country,\n", + " 'year_vtg': vintage_years\n", + " ,\n", + " 'year_act': act_years,\n", + " 'mode': 'standard',\n", + " 'node_origin': country,\n", + " 'commodity': 'electricity',\n", + " 'time': 'year',\n", + " 'time_origin': 'year',\n", + "}\n", + "\n", + "base_output = {\n", + " 'node_loc': country,\n", + " 'year_vtg': vintage_years,\n", + " 'year_act': act_years,\n", + " 'mode': 'standard',\n", + " 'node_dest': country,\n", + " 'time': 'year',\n", + " 'time_dest': 'year', \n", + " 'unit': '%',\n", + "}\n", + "base_capacity_factor = {\n", + " 'node_loc': country,\n", + " 'year_vtg': vintage_years,\n", + " 'year_act': act_years,\n", + " 'time': 'year',\n", + " 'unit': '%',\n", + "}\n", + "base_technical_lifetime = {\n", + " 'node_loc': country,\n", + " 'year_vtg': model_horizon,\n", + " 'unit': 'y',\n", + "}\n", + "base_inv_cost = {\n", + " 'node_loc': country,\n", + " 'year_vtg': model_horizon,\n", + " 'unit': 'USD/GWa',\n", + "}\n", + "base_fix_cost = {\n", + " 'node_loc': country,\n", + " 'year_vtg': vintage_years,\n", + " 'year_act': act_years,\n", + " 'unit': 'USD/GWa',\n", + "}\n", + "base_var_cost = {\n", + " 'node_loc': country,\n", + " 'year_vtg': vintage_years,\n", + " 'year_act': act_years,\n", + " 'mode': 'standard',\n", + " 'time': 'year',\n", + " 'unit': 'USD/GWa',\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `addon` technology - interlinking technologies\n", + "In order to demonstrate the use of this feature, we will be adding a new demand for heat to this scenario. This demand will be met by the `coal_ppl` via a passout-turbine, the `addon` technology.\n", + "\n", + "We will therefore go through the following steps:\n", + "1. Define a new demand for heat.\n", + "2. Add new technologies: passout-turbine, district heat network, in-house district heat connection\n", + "3. Link the passout-turbine to the coal_ppl using the `addon` feature." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step1: Define a new demand\n", + "Note: Austria has an approximate space heating requirement of about 47000 GWh supplied via district heat, at approximately 9 Mio. inhabitants this equates to approximately 5000 kWh per capita." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define a new commodity `heat`\n", + "scen.add_set(\"commodity\", [\"heat\"])\n", + "\n", + "# Add heat demand at the useful level\n", + "heat_demand = pd.DataFrame({\n", + " 'node': country,\n", + " 'commodity': 'heat',\n", + " 'level': 'useful',\n", + " 'year': [700, 710, 720],\n", + " 'time': 'year',\n", + " 'value': (50 * gdp_profile).round(),\n", + " 'unit': 'GWa',\n", + " })\n", + "scen.add_par(\"demand\", heat_demand)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step2: Define new technologies inlcuding a passout-turbine along with a grid and an in-house heat-distribution system\n", + "\n", + "Passout-turbine (`po_turbine`) characteristics: The passout-turbine requires one unit of electricity to generate five units of heat. The lifetime is assumed to be 30 years, 10 years longer then that of the `coal_ppl`. Investment costs are 150\\$/kW compared to 500\\$/kW for the `coal_ppl`. A coal heatplant would have higher investment costs, approximately double that of the `po_turbine`.\n", + "\n", + "District heat (`dh_grid`) network characteristics: District heating networks have only very low losses as these cover only short distances (within city perimeters). We will assume the district heating network to have an efficiency of 97%." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As before we will work our way backwards, starting from the `heat` demand defined at the `useful` energy level and connecting this to the `final` energy level via a technology, `dh_house`, representing the in-house heat distribution system." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tec = 'dh_house'\n", + "scen.add_set('technology', tec)\n", + "\n", + "dh_house_out = make_df(base_output, technology=tec, commodity='heat', \n", + " level='useful', value=1.0)\n", + "scen.add_par('output', dh_house_out)\n", + "\n", + "dh_house_in = make_df(base_input, technology=tec, commodity='heat', \n", + " level='final', value=1.0, unit='%')\n", + "scen.add_par('input', dh_house_in)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will add the district heating network" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tec = 'dh_grid'\n", + "scen.add_set('technology', tec)\n", + "\n", + "dh_grid_out = make_df(base_output, technology='dh_grid', commodity='heat', \n", + " level='final', value=1.0)\n", + "scen.add_par('output', dh_grid_out)\n", + "\n", + "dh_grid_in = make_df(base_input, technology='dh_grid', commodity='heat', \n", + " level='secondary', value=1.03, unit='%')\n", + "scen.add_par('input', dh_grid_in)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last technology we will add is the `po_turbine` itself." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tec = 'po_turbine'\n", + "scen.add_set('technology', tec)\n", + "\n", + "po_out = make_df(base_output, technology=tec, commodity='heat', \n", + " level='secondary', value=1.0)\n", + "scen.add_par('output', po_out)\n", + "\n", + "po_in = make_df(base_input, technology=tec, commodity='electricity', \n", + " level='secondary', value=0.2, unit='%')\n", + "scen.add_par('input', po_in)\n", + "\n", + "po_tl = make_df(base_technical_lifetime, technology=tec, value=30)\n", + "scen.add_par('technical_lifetime', po_tl)\n", + "\n", + "po_inv = make_df(base_inv_cost, technology=tec, value=150)\n", + "scen.add_par('inv_cost', po_inv)\n", + "\n", + "po_fix = make_df(base_fix_cost, technology=tec, value=15)\n", + "scen.add_par('fix_cost', po_fix)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step3: Link the `po_turbine` with `coal_ppl`\n", + "The `po_turbine` could already operate as all required parameters are defined, yet without a link to the activity of the `coal_ppl`, the `po_turbine` has the possibility of using electricity generated from either the `coal_ppl` or the `wind_ppl`. But because the `po_turbine` is an addon component to the `coal_ppl` a distinct linkage needs to be established." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, the newly added technology `po_turbine` needs to be classified as an `addon` technology" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scen.add_set('addon', 'po_turbine')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we need a new `type_addon`, which we will name `cogeneration_heat`. We will classify the `po_turbine` via the *category* `addon` as one of the addon technologies as part of this specific `type_addon`. In some cases, for example when modelling cooling technologies, multiple technologies can be classfied within a single `type_addon`.\n", + "\n", + "Via the set `map_tec_addon` we map the electricity generation technology, `coal_ppl`, to the `addon` technology, `po_turbine`. Multiple technologies, for example further fossil powerplants, could also be added to the this `type_addon` so as to be able to produce heat via the `po_turbine`.\n", + "\n", + "Note: the `addon` technology as well as the parent technology must have the same `mode`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type_addon = 'cogeneration_heat'\n", + "addon = 'po_turbine'\n", + "tec = 'coal_ppl'\n", + "scen.add_cat('addon', type_addon, addon)\n", + "scen.add_set('map_tec_addon', pd.DataFrame({'technology': tec,\n", + " 'type_addon': [type_addon]}))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last step required in order to link the `coal_ppl` is to define the `addon_conversion` factor between the `coal_ppl` and the `type_addon`. This is important, because the `coal_ppl` generates electricty while the `po_turbine` generates heat. Therefore, we can use the inverse of the `input` coefficient from the `po_turbine`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Index for `addon_conversion` is ['node', 'technology', 'year_vtg',\n", + "# 'year_act', 'mode', 'time',\n", + "# 'type_addon', 'value', 'unit']\n", + "df = pd.DataFrame({'node': country,\n", + " 'technology': tec,\n", + " 'year_vtg': vintage_years,\n", + " 'year_act': act_years,\n", + " 'mode': 'standard',\n", + " 'time': 'year',\n", + " 'type_addon': type_addon,\n", + " 'value': 5,\n", + " 'unit': '%'})\n", + "scen.add_par('addon_conversion', df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Although not necessary for this specific example, it is also possible to limit the activity of the `po_turbine` to a specific share of the `coal_ppl` activity. In the example below, the `po_turbine` is limited to using 15% of the `coal_ppl` activity. Likewise, a constraint on the minimum amount of electricity used from the `po_turbine` can be applied by using the parameter `addon_lo`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Index for `addon_up` is ['node', 'technology', 'year_act',\n", + "# 'mode', 'time', 'type_addon',\n", + "# 'value', 'unit']\n", + "df = pd.DataFrame({'node': country,\n", + " 'technology': tec,\n", + " 'year_act': act_years,\n", + " 'mode': 'standard',\n", + " 'time': 'year',\n", + " 'type_addon': type_addon,\n", + " 'value': .15,\n", + " 'unit': '%'})\n", + "scen.add_par('addon_up', df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Commit and solve" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scen.commit(comment='define parameters for renewable implementation')\n", + "scen.set_as_default()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scen.solve()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scen.var('OBJ')['lvl']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plotting Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tools import Plots\n", + "p = Plots(scen, country, firstyear=700) # scenario: 'renewable_potential' (emission_bound scenario with firm capacity)\n", + "b = Plots(base, country, firstyear=700) # scenario: 'carbon_tax' (without renewable potentials)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Activity\n", + "***\n", + "In the new scenario ('*renewable_potential*'), the effects of the addon technology can be seen when comparing the activity to the baseline scenario ('*baseline*'). From 700 onwards, the activity of the `wind_ppl` has increased to compensate for the electricity required from the `coal_ppl` for use in the `po_turbine`. In 720, when the `wind_ppl` is phased out, then more electricity is required to be produced by the `coal_ppl`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scenario: '*carbon_tax*'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b.plot_activity(baseyear=True, subset=['coal_ppl', 'wind_ppl'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scenario: '*renewable_potential*'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p.plot_activity(baseyear=True, subset=['coal_ppl', 'wind_ppl'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Capacity\n", + "***\n", + "The behavior observed for the activity of the two electricity generation technologies is reflected in the capacity." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scenario: '*carbon_tax*'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b.plot_capacity(baseyear=True, subset=['coal_ppl', 'wind_ppl'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scenario: '*renewable_potential*'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p.plot_capacity(baseyear=True, subset=['coal_ppl', 'wind_ppl'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prices\n", + "***\n", + "The resulting impact on the electricity price though is negligable." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scenario: '*carbon_tax*'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b.plot_prices(subset=['light'], baseyear=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Scenario: '*renewable_potential*'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p.plot_prices(subset=['light'], baseyear=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mp.close_db()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorial/westeros/westeros_baseline.ipynb b/tutorial/westeros/westeros_baseline.ipynb index 2f077d7fc..fb17badaa 100644 --- a/tutorial/westeros/westeros_baseline.ipynb +++ b/tutorial/westeros/westeros_baseline.ipynb @@ -77,7 +77,7 @@ "source": [ "## MESSAGEix: the mathematical paradigm\n", "\n", - "At its core, *MESSAGEix* is an optimization model:\n", + "At its core, *MESSAGEix* is an optimization problem:\n", "\n", "> $\\min \\quad ~c^T \\cdot x$ \n", "> $~s.t. \\quad A \\cdot x \\leq b$\n", @@ -347,6 +347,8 @@ }, "source": [ "The `COMMODITY_BALANCE_GT` and `COMMODITY_BALANCE_LT` equations ensure that `demand` for each `commodity` is met at each `level` in the energy system.\n", + "The equation is copied below in this tutorial notebook, but every model equation is available for reference in\n", + "the [Mathematical formulation](https://message.iiasa.ac.at/en/stable/model/MESSAGE/model_core.html#) section of the MESSAGEix documentation.\n", "\n", "$\\sum_{\\substack{n^L,t,m \\\\ y^V \\leq y}} \\text{output}_{n^L,t,y^V,y,m,n,c,l} \\cdot \\text{ACT}_{n^L,t,y^V,y,m}$\n", "$- \\sum_{\\substack{n^L,t,m, \\\\ y^V \\leq y}} \\text{input}_{n^L,t,y^V,y,m,n,c,l} \\cdot \\text{ACT}_{n^L,t,m,y}$ \n", @@ -501,7 +503,9 @@ "- receives *input* in the form of the \"electricity\" *commodity* at the \"final [energy]\" *level*, and\n", "- *outputs* the commodity \"light\" at the \"useful [energy]\" level.\n", "\n", - "The `value` in the input and output parameter is used to represent the effiecieny of a technology (efficiency = output/input). For example, input of 1.0 and output of 1.0 for a technology shows that the efficiency of that technology is 100% in converting input commodity to output commodity." + "The `value` in the input and output parameter is used to represent the effiecieny of a technology (efficiency = output/input).\n", + "For example, input of 1.0 and output of 1.0 for a technology shows that the efficiency of that technology is 100% in converting\n", + "the input commodity to the output commodity." ] }, { @@ -1325,7 +1329,9 @@ "source": [ "### Electricity Price\n", "\n", - "And how much does the electricity cost? These prices are in fact **shadow prices** taken from the **dual variables** of the model solution. They reflect the marginal cost of electricity generation (i.e., the additional cost of the system for supplying one more unit of electricity), which is in fact the marginal cost of the most expensive generator. \n", + "And how much does the electricity cost? These prices are in fact **shadow prices** taken from the **dual variables** of the model solution.\n", + "They reflect the marginal cost of electricity generation (i.e., the additional cost of the system for supplying one more unit of\n", + "electricity), which is in fact the marginal cost of the most expensive operating generator. \n", "\n", "Note the price drop when the most expensive technology is no longer in the system." ] diff --git a/tutorial/westeros/westeros_emissions_taxes.ipynb b/tutorial/westeros/westeros_emissions_taxes.ipynb index c2a1fac03..38cab3358 100644 --- a/tutorial/westeros/westeros_emissions_taxes.ipynb +++ b/tutorial/westeros/westeros_emissions_taxes.ipynb @@ -85,7 +85,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "When setting a cumlulative bound, the undiscounted price of emission is the same in different model years (see the marginals of equation `EMISSION_CONSTRAINT`). However, considering the year-to-year discount factor, we observe an ascending trend in emission prices shown in `PRICE_EMISSION` above. This means the emission price in later years is higher as the value of money in the future is lower compared to today. " + "When setting a cumlulative bound, the undiscounted price of emission is the same in different model years (see the marginals of\n", + "equation `EMISSION_CONSTRAINT`). However, considering the year-to-year discount factor, we observe an ascending trend in\n", + "emission prices shown in `PRICE_EMISSION` above. This means the emission price in later years is higher as the value of money in\n", + "the future is lower compared to today. " ] }, { @@ -94,9 +97,11 @@ "source": [ "## Make a new scenario with emission bounds by year\n", "\n", - "In the previous example, we imposed a bound on emissions over the entire model horizon by using the `type_year` as 'cumulative' in the parameter `bound_emission`. Now, we will create a similar scenario, but the emission constraint will be defined per year.\n", + "In the previous example, we imposed a bound on emissions over the entire model horizon by using the `type_year` as 'cumulative'\n", + "in the parameter `bound_emission`. Now, we will create a similar scenario, but the emission constraint will be defined per year.\n", "\n", - "For the sake of comparison, the per-year emission values will be chosen exactly in line with the optimal emission trajectory obtained from the solution of the previous scenario." + "For the sake of comparison, the per-year emission values will be chosen exactly in line with the optimal emission trajectory\n", + "obtained from the solution of the previous scenario." ] }, { @@ -174,7 +179,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Comparing the emission prices between the two scenarios at this stage, we see that the values are not identical. The reason is that when we introduce emission bounds per year, the price of emission in each year reflects the cost occuring when reducing one more unit of emission in that year. However, in the scenario with a cumulative bound over the entire model horizon, the price of emission reflects the cost of the system in reducing one more unit of emission over the entire model horizon." + "Comparing the emission prices between the two scenarios at this stage, we see that the values are not identical.\n", + "The reason is that when we introduce emission bounds per year, the price of emission in each year reflects the cost occuring\n", + "when reducing one more unit of emission in that year.\n", + "However, in the scenario with a cumulative bound over the entire model horizon, the price of emission reflects the cost of the\n", + "system in reducing one more unit of emission over the entire model horizon." ] }, { @@ -234,10 +243,8 @@ }, { "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "scen_tax.commit(comment='setting taxes on emissions')" @@ -265,7 +272,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Question\n", + "# Exercises\n", "- How does these prices compare to the scenario with a cumulative emission bound (`scen_bd`)?\n", "- Try setting the emission tax again by using emission prices obtained from the scenario with yearly bounds on emissions (`scen_bd_by_year`). What is the difference in emissions (i.e., variable `EMISS`)?" ] diff --git a/tutorial/westeros/westeros_firm_capacity.ipynb b/tutorial/westeros/westeros_firm_capacity.ipynb index 889176df7..d9b482d81 100644 --- a/tutorial/westeros/westeros_firm_capacity.ipynb +++ b/tutorial/westeros/westeros_firm_capacity.ipynb @@ -67,7 +67,13 @@ "source": [ "## Improving the Representation of Electricity Sector\n", "### Peak load factor\n", - "The input demand for electricity (notice: not the demand for useful light but for electricity) in the model shows the average electricity demand in the given time (which in our example is a year). However, power systems need enough installed capacity not only to cover the average electricity demand but also to meet the peak demand, i.e., maximum load throughout that time. This feature can be specified in the model using parameter `peak_load_factor`. For example, if annual average load is 5 GWa peak load amy be 10GW, hence, the peak load factor is equal to 2." + "The input demand for electricity (notice: not the demand for useful light but for electricity) in the model shows the average\n", + "electricity demand in the given time (which in our example is a year).\n", + "However, power systems need enough installed capacity not only to cover the average electricity demand but also to meet the peak\n", + "demand, i.e., maximum load throughout that time.\n", + "This feature can be specified in the model using parameter `peak_load_factor`.\n", + "For example, if annual average load is 5 GW, the peak load may be twice as high, at 10 GW; hence, the peak load factor is equal\n", + "to 2." ] }, { @@ -94,7 +100,12 @@ "metadata": {}, "source": [ "## Reliability of Power System\n", - "In roder to meet demand reliably, the power system needs to maintain dispatchable or so called firm capacity at any time. Some technologies like coal power plants can fully contribute to the required firm capacity, i.e., 1 MW installed capacity of these technologies provide 1 MW firm capacity. However, some other technologies such as variable renewables such as wind are not fully reliable when the power system needs them. As such, the capacity value of wind power plant is considered to be lower than 1. In this example, we assume that 10% of electricity supply by wind is 80% reliable, while he remaining 90% installed capacity can only contribute by 5% to the required firm capcity. As such, we divide wind power into two parts with two different ratings (r1 and r2) and we define this through parameter `rating_bin`.\n" + "In order to meet demand reliably, the power system needs to maintain dispatchable or so-called firm capacity at any time.\n", + "Some technologies like coal power plants can fully contribute to the required firm capacity, i.e.,\n", + "1 MW installed capacity of these technologies provide 1 MW firm capacity.\n", + "However, some other technologies such as variable renewables like wind are not fully reliable when the power system needs them.\n", + "As such, the capacity value of wind power plant is considered to be lower than 1.\n", + "In this example, we assume that 10% of electricity supply by wind is 80% reliable, while the remaining 90% installed capacity can only contribute by 5% to the required firm capacity. As such, we divide wind power into two parts with two different ratings (r1 and r2) and we define this through parameter `rating_bin`.\n" ] }, { diff --git a/tutorial/westeros/westeros_flexible_generation.ipynb b/tutorial/westeros/westeros_flexible_generation.ipynb index fa69afe4b..128c46812 100644 --- a/tutorial/westeros/westeros_flexible_generation.ipynb +++ b/tutorial/westeros/westeros_flexible_generation.ipynb @@ -70,7 +70,7 @@ "source": [ "## Add a carbon tax\n", "\n", - "Then, we add a carbon tax to motivate the use of low-carbon technologies in the system. We do this similar to the process explained in the [tutorial for adding emission tax](https://github.com/iiasa/message_ix/blob/master/tutorial/westeros/westeros_emissions_taxes.ipynb)" + "Then, we add a carbon tax to motivate the use of low-carbon technologies in the system. We do this similar to the process explained in the tutorial for adding emissions taxes (`westeros_emission_taxes.ipynb`)." ] }, { @@ -101,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { "collapsed": true }, diff --git a/tutorial/westeros/westeros_seasonality.ipynb b/tutorial/westeros/westeros_seasonality.ipynb index be93f1acd..ecd72f59e 100644 --- a/tutorial/westeros/westeros_seasonality.ipynb +++ b/tutorial/westeros/westeros_seasonality.ipynb @@ -5,14 +5,19 @@ "metadata": {}, "source": [ "# \"*Winter is coming!*\": Modeling of variability in energy supply and demand\n", - "Time-dependent variations in demand and supply are common characteristics of an electricity system, and Westeros is not an exception. This tutorial helps to learn how to add sub-annual time steps to a MESSAGEix model and investigate the impact of the variability in supply and demand. This tutorial is structured as follows:\n", + "Time-dependent variations in demand and supply are common characteristics of an electricity system, and Westeros is not an\n", + "exception. This tutorial helps to learn how to add sub-annual time steps to a MESSAGEix model and investigate the impact of the\n", + "variability in supply and demand. It is structured as follows:\n", "1. A short note on seasonality\n", "2. Adding sub-annual time steps\n", "3. Analyzing the results\n", "\n", "### Requirements for running this tutorial\n", "- You have MESSAGEix framework installed and working\n", - "- You have run Westeros baseline scenario and solved it successfully" + "- You have run Westeros baseline scenario and solved it successfully\n", + "\n", + "This tutorial was developed by Behnam Zakeri ([@behnam-zakeri](https://github.com/behnam-zakeri)) for the course Energy\n", + "Economics and Modeling held at the International Summer School in Energy Technology, St. Petersburg Polytechnique University in August 2018." ] }, { @@ -189,7 +194,8 @@ "|-----------|----------| -----------|\n", "| duration_time\t| time\t|duration of sub-annual time slices (relative to 1)| \n", "\n", - "In our example, winter and summer are each half of the year." + "In our example, winter and summer are defined as each half of the year. However, the duration of time steps can be\n", + "different in a MESSAGEix model, e.g., winter 0.4 and summer 0.6. But the sum of the duration times must be equal to 1." ] }, { @@ -213,7 +219,10 @@ "In this stage, we introduce a function that helps us to modify the parameters after adding new time steps. This function called \"yearly_to_season\" does the following:\n", "- removing old values, where the \"time\" index was \"year\"\n", "- populating data for new \"time\" indexes\n", - "- using the ratios defined by the user to convert yearly values to seasonal ones" + "- using the ratios defined by the user to convert yearly values to seasonal ones\n", + "\n", + "This is a common code pattern when modelling using MESSAGEix:\n", + "writing re-usable code that helps modify existing parameter data to reflect some desired change in the reference energy system." ] }, { @@ -279,7 +288,7 @@ "metadata": {}, "source": [ "### Modifying \"input\" and \"output\"\n", - "However, not all parameters that have subannual time steps need to divide their values for each time step. For example, \"output\" parameter shows the output efficiency, commodities and level of a technology. Hence, as far as the efficiency of a technology remains unchanged in different seasons, the value of \"output\" remains fixed. As such, we only need to add the sub-annual time steps but with the same value as for the yearly one." + "However, not all parameters that have subannual time steps need to divide their annual values for each time step. For example, \"output\" parameter shows the output efficiency, commodities and the level of a technology. Hence, as far as the efficiency of a technology remains unchanged in different seasons, the value of \"output\" will be the same. As such, we only need to add the sub-annual time steps but with the same value as for the yearly one." ] }, { @@ -324,7 +333,7 @@ "metadata": {}, "source": [ "### Modifying capacity factor\n", - "We discussed about the variation in the capacity factor of wind power in each month. By averaging the values for the respective months, we reach a capacity factor of 0.46 for winter and 0.25 for summer in Westeros. For simplicity, the capacity factor of wind was 1 in the baseline scenario.\n" + "We discussed about the variation in the capacity factor of wind power in each month. By averaging the values for the respective months, we reach a capacity factor of 0.46 for winter and 0.25 for summer in Westeros.\n" ] }, { @@ -334,11 +343,11 @@ "outputs": [], "source": [ "# Modifying capacity factor\n", - "# Retrieving average yearly capacity factor of wind in the model\n", - "cf_wind = scen.par('capacity_factor', {'technology': 'wind_ppl'})['value'].mean()\n", + "# Let's check the yearly capacity factor of wind in the baseline scenario\n", + "scen.par('capacity_factor', {'technology': 'wind_ppl'})['value'].mean()\n", "\n", - "# Passing seasonal capacity factors as shares of the yearly value\n", - "cf_data = {'winter': 0.46/cf_wind, 'summer': 0.25/cf_wind} \n", + "# Passing seasonal capacity factors\n", + "cf_data = {'winter': 0.46, 'summer': 0.25} \n", "cf_filters = {'technology': 'wind_ppl'}\n", "yearly_to_season(scen, 'capacity_factor', cf_data, cf_filters)\n", "\n",