<jupyter_start><jupyter_text>Austrian energy system Tutorial Part 1: Building an Energy ModelFor information on how to install *MESSAGEix*, please refer to [Installation page](https://docs.messageix.org/en/stable/getting-started) and for getting *MESSAGEix* tutorials, please follow the steps mentioned in [Tutorials](https://docs.messageix.org/en/stable/tutorials.html).Please refer to the [user guidelines](https://github.com/iiasa/message_ix/blob/master/NOTICE.rst)for additional information on using *MESSAGEix*, including the recommended citation and how to name new models.**Pre-requisites**- You have the *MESSAGEix* framework installed and working**Structure of these tutorials.** After having run this baseline tutorial, you are able to start with any of the other tutorials, but we recommend to follow the order below for going through the information step-wise:1. Prepare the base model version (Python: ``austria.ipynb``, also available in R: ``austria_reticulate.ipynb``)2. Plot the results of the baseline runs (Python: ``austria_load_scenario.ipynb``, also available in R: ``austria_load_scenario_R.ipynb``).3. Run a single policy scenario (``austria_single_policy.ipynb``).4. Run multiple policy scenarios. This tutorial has two notebooks: an introduction with some exercises and completed code for the exercises (exercises: ``austria_multiple_policies.ipynb``, answers: ``austria_multiple_policies-answers.ipynb``).**Introduction**In this notebook, we will build a model of the Austrian energy system from scratch. The process will involve defining our model's time horizon and spatial extent, and then populating the model with data associated with model parameters. Once we have a baseline model, we will then move on to investigating policy scenarios.We will be populating different kinds of parameters including: Economic Parameters- `interestrate`- `demand` Technology Parameters Engineering Parameters- `input`- `output`- `technical_lifetime`- `capacity_factor` Technoeconomic Parameters- `inv_cost`- `fix_cost`- `var_cost` Dynamic Behavior Parameters- `bound_activity_up`- `bound_activity_lo`- `bound_new_capacity_up`- `initial_activity_up`- `growth_activity_up` Emissions- `emission_factor`A full list of parameters can be found in the [MESSAGEix documentation](http://messageix.iiasa.ac.at/model/MESSAGE/parameter_def.html). (If you have cloned the MESSAGEix [Github repository](https://github.com/iiasa/message_ix), the documentation can also be built offline; see `doc/README.md`.)  The Final ProductAt the completion of this exercise, we will have developed an energy model is comprised of the below Reference Energy System (RES):![title](austria.png)  Setup<jupyter_code># load required packages 
import itertools
import pandas as pd

import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')

import ixmp as ix
import message_ix

from message_ix.util import make_df
# launch the IX modeling platform using the local default database
mp = ix.Platform()
model = "Austrian energy model"
scen = "baseline"
annot = "developing a stylized energy system model for illustration and testing" 

scenario = message_ix.Scenario(mp, model, scen, version='new', annotation=annot)<jupyter_output><empty_output><jupyter_text>Time and Spatial DetailThe model includes the time periods 2010, 2020, 2030 and 2040.<jupyter_code>horizon = range(2010, 2041, 10)
scenario.add_horizon(year=horizon)
country = 'Austria'
scenario.add_spatial_sets({'country': country})<jupyter_output><empty_output><jupyter_text>Model Structure<jupyter_code>scenario.add_set("commodity", ["electricity", "light", "other_electricity"])
scenario.add_set("level", ["secondary", "final", "useful"])
scenario.add_set("mode", "standard")<jupyter_output><empty_output><jupyter_text>Economic Parameters Definition of the socio-economic discount rate:<jupyter_code>scenario.add_par("interestrate", horizon, value=0.05, unit='-')<jupyter_output><empty_output><jupyter_text>The fundamental premise of the model is to satisfy demand for energy (services). To first order, demands for services (e.g. electricity) track with economic productivity (GDP). Therefore, as a simple example, we define both a GDP profile and a correlation factor between GDP growth and demand, called beta. Beta will then be used to obtain a simplistic demand profile.<jupyter_code>gdp = pd.Series([1., 1.21631, 1.4108, 1.63746], index=horizon)
beta = 0.7
demand = gdp ** beta<jupyter_output><empty_output><jupyter_text>Technologies<jupyter_code>plants = [
    "coal_ppl", 
    "gas_ppl", 
    "oil_ppl", 
    "bio_ppl", 
    "hydro_ppl",
    "wind_ppl", 
    "solar_pv_ppl", # actually primary -> final
]
secondary_energy_techs = plants + ['import']

final_energy_techs = ['electricity_grid']

lights = [
    "bulb", 
    "cfl", 
]
useful_energy_techs = lights + ['appliances']
technologies = secondary_energy_techs + final_energy_techs + useful_energy_techs
scenario.add_set("technology", technologies)
demand_per_year = 55209. / 8760 # from IEA statistics
elec_demand = pd.DataFrame({
        'node': country,
        'commodity': 'other_electricity',
        'level': 'useful',
        'year': horizon,
        'time': 'year',
        'value': demand_per_year * demand,
        'unit': 'GWa',
    })
scenario.add_par("demand", elec_demand)

demand_per_year = 6134. / 8760 # from IEA statistics
light_demand = pd.DataFrame({
        'node': country,
        'commodity': 'light',
        'level': 'useful',
        'year': horizon,
        'time': 'year',
        'value': demand_per_year * demand,
        'unit': 'GWa',
    })
scenario.add_par("demand", light_demand)<jupyter_output><empty_output><jupyter_text>Engineering Parameters<jupyter_code>year_df = scenario.vintage_and_active_years()
vintage_years, act_years = year_df['year_vtg'], year_df['year_act']
base_input = {
    'node_loc': country,
    'year_vtg': vintage_years,
    'year_act': act_years,
    'mode': 'standard',
    'node_origin': country,
    'commodity': 'electricity',
    'time': 'year',
    'time_origin': 'year',
}

grid = pd.DataFrame(dict(
        technology = 'electricity_grid',
        level = 'secondary',
        value = 1.0,
        unit = '-',
        **base_input
        ))
scenario.add_par("input", grid)


bulb = pd.DataFrame(dict(
        technology = 'bulb',
        level = 'final',
        value = 1.0,
        unit = '-',
        **base_input
        ))
scenario.add_par("input", bulb)

cfl = pd.DataFrame(dict(
        technology = 'cfl',
        level = 'final',
        value = 0.3, # LED and CFL lighting equipment are more efficient than conventional light bulbs,
                     #so they need less input electricity to produce the same quantity of 'light'
                     #compared to conventional light bulbs (0.3 units vs 1.0, respectively) 
        unit = '-',
        **base_input
        ))
scenario.add_par("input", cfl)

app = pd.DataFrame(dict(
        technology = 'appliances',
        level = 'final',
        value = 1.0,
        unit = '-',
        **base_input
        ))
scenario.add_par("input", app)
make_df?
# to see what the funcion "df" does.
base_output = {
    'node_loc': country,
    'year_vtg': vintage_years,
    'year_act': act_years,
    'mode': 'standard',
    'node_dest': country,
    'time': 'year',
    'time_dest': 'year', 
    'unit': '-',
}

imports = make_df(base_output, technology='import', commodity='electricity', 
                  level='secondary', value=1.)
scenario.add_par('output', imports)

grid = make_df(base_output, technology='electricity_grid', commodity='electricity', 
               level='final', value=0.873)
scenario.add_par('output', grid)

bulb = make_df(base_output, technology='bulb', commodity='light', 
               level='useful', value=1.)
scenario.add_par('output', bulb)

cfl = make_df(base_output, technology='cfl', commodity='light', 
              level='useful', value=1.)
scenario.add_par('output', cfl)

app = make_df(base_output, technology='appliances', commodity='other_electricity', 
              level='useful', value=1.)
scenario.add_par('output', app)

coal = make_df(base_output, technology='coal_ppl', commodity='electricity', 
               level='secondary', value=1.)
scenario.add_par('output', coal)

gas = make_df(base_output, technology='gas_ppl', commodity='electricity', 
              level='secondary', value=1.)
scenario.add_par('output', gas)

oil = make_df(base_output, technology='oil_ppl', commodity='electricity', 
              level='secondary', value=1.)
scenario.add_par('output', oil)

bio = make_df(base_output, technology='bio_ppl', commodity='electricity', 
              level='secondary', value=1.)
scenario.add_par('output', bio)

hydro = make_df(base_output, technology='hydro_ppl', commodity='electricity', 
                level='secondary', value=1.)
scenario.add_par('output', hydro)

wind = make_df(base_output, technology='wind_ppl', commodity='electricity', 
               level='secondary', value=1.)
scenario.add_par('output', wind)

solar_pv = make_df(base_output, technology='solar_pv_ppl', commodity='electricity', 
                   level='final', value=1.)
scenario.add_par('output', solar_pv)
base_technical_lifetime = {
    'node_loc': country,
    'year_vtg': horizon,
    'unit': 'y',
}

lifetimes = {
    'coal_ppl': 40,
    'gas_ppl': 30,
    'oil_ppl': 30,
    'bio_ppl': 30,
    'hydro_ppl': 60,
    'wind_ppl': 20,
    'solar_pv_ppl': 20,
    'bulb': 1,
    'cfl': 10,
}

for tec, val in lifetimes.items():
    df = make_df(base_technical_lifetime, technology=tec, value=val)
    scenario.add_par('technical_lifetime', df)
base_capacity_factor = {
    'node_loc': country,
    'year_vtg': vintage_years,
    'year_act': act_years,
    'time': 'year',
    'unit': '-',
}

capacity_factor = {
    'coal_ppl': 0.85,
    'gas_ppl': 0.75,
    'oil_ppl': 0.75,
    'bio_ppl': 0.75,
    'hydro_ppl': 0.5,
    'wind_ppl': 0.2,
    'solar_pv_ppl': 0.15,
    'bulb': 0.1, 
    'cfl':  0.1, 
}

for tec, val in capacity_factor.items():
    df = make_df(base_capacity_factor, technology=tec, value=val)
    scenario.add_par('capacity_factor', df)<jupyter_output><empty_output><jupyter_text>Technoeconomic Parameters<jupyter_code>base_inv_cost = {
    'node_loc': country,
    'year_vtg': horizon,
    'unit': 'USD/kW',
}

# Adding a new unit to the library
mp.add_unit('USD/kW')    

# in $ / kW (specific investment cost)
costs = {
    'coal_ppl': 1500,
    'gas_ppl':  870,
    'oil_ppl':  950,
    'hydro_ppl': 3000,
    'bio_ppl':  1600,
    'wind_ppl': 1100,
    'solar_pv_ppl': 4000,
    'bulb': 5,
    'cfl':  900, 
}

for tec, val in costs.items():
    df = make_df(base_inv_cost, technology=tec, value=val)
    scenario.add_par('inv_cost', df)
base_fix_cost = {
    'node_loc': country,
    'year_vtg': vintage_years,
    'year_act': act_years,
    'unit': 'USD/kWa',
}

# Adding a new unit to the library
mp.add_unit('USD/kWa')  

# in $ / kW / year (every year a fixed quantity is destinated to cover part of the O&M costs
# based on the size of the plant, e.g. lightning, labor, scheduled maintenance, etc.)

costs = {
    'coal_ppl': 40,
    'gas_ppl':  25,
    'oil_ppl':  25,
    'hydro_ppl': 60,
    'bio_ppl':  30,
    'wind_ppl': 40,
    'solar_pv_ppl': 25,
}

for tec, val in costs.items():
    df = make_df(base_fix_cost, technology=tec, value=val)
    scenario.add_par('fix_cost', df)
base_var_cost = {
    'node_loc': country,
    'year_vtg': vintage_years,
    'year_act': act_years,
    'mode': 'standard',
    'time': 'year',
    'unit': 'USD/kWa',
}

# Variable O&M (costs associatied to the degradation of equipment when the plant is functioning
# per unit of energy produced)
# kWa = kW·year = 8760 kWh. Therefore this costs represents USD per 8760 kWh of energy.
# Do not confuse with fixed O&M units.


#var O&M in $ / MWh 
costs = {
    'coal_ppl': 24.4,
    'gas_ppl':  42.4,
    'oil_ppl':  77.8,
    'bio_ppl':  48.2,
    'electricity_grid': 47.8,
}

for tec, val in costs.items():
    df = make_df(base_var_cost, technology=tec, value=val * 8760. / 1e3) # to convert it into USD/kWa
    scenario.add_par('var_cost', df)<jupyter_output><empty_output><jupyter_text>Dynamic Behavior Parameters In this section the following parameters will be added to the different technologies:- `bound_activity_up`- `bound_activity_lo`- `bound_new_capacity_up`- `initial_activity_up`- `growth_activity_up`As stated in the **Introduction**, a full list of parameters can be found in the *MESSAGEix* documentation. Specifically for this list, please refer to the section [Bounds on capacity and activity](https://docs.messageix.org/en/stable/model/MESSAGE/parameter_def.htmlbounds-on-capacity-and-activity)<jupyter_code>base_growth = {
    'node_loc': country,
    'year_act': horizon[1:],
    'value': 0.05,
    'time': 'year',
    'unit': '%',
}

growth_technologies = [
    "coal_ppl", 
    "gas_ppl", 
    "oil_ppl", 
    "bio_ppl", 
    "hydro_ppl",
    "wind_ppl", 
    "solar_pv_ppl", 
    "cfl",
    "bulb",
]

for tec in growth_technologies:
    df = make_df(base_growth, technology=tec)
    scenario.add_par('growth_activity_up', df)
base_initial = {
    'node_loc': country,
    'year_act': horizon[1:],
    'time': 'year',
    'unit': '%',
}

for tec in lights:
    df = make_df(base_initial, technology=tec, value=0.01 * light_demand['value'].loc[horizon[1:]])
    scenario.add_par('initial_activity_up', df)
base_activity = {
    'node_loc': country,
    'year_act': [2010],
    'mode': 'standard',
    'time': 'year',
    'unit': 'GWa',
}

# in GWh - from IEA Electricity Output
activity = {
    'coal_ppl': 7184,
    'gas_ppl':  14346,
    'oil_ppl':  1275,
    'hydro_ppl': 38406,
    'bio_ppl':  4554,
    'wind_ppl': 2064,
    'solar_pv_ppl': 89,
    'import': 2340,
    'cfl': 0,
}

#MODEL CALIBRATION: by inserting an upper and lower bound to the same quantity we are ensuring
#that the model is calibrated at that value that year, so we are at the right starting point.
for tec, val in activity.items():
    df = make_df(base_activity, technology=tec, value=val / 8760.)
    scenario.add_par('bound_activity_up', df)
    scenario.add_par('bound_activity_lo', df)
base_capacity = {
    'node_loc': country,
    'year_vtg': [2010],
    'unit': 'GW',
}

cf = pd.Series(capacity_factor)
act = pd.Series(activity)
capacity = (act / 8760 / cf).dropna().to_dict()

for tec, val in capacity.items():
    df = make_df(base_capacity, technology=tec, value=val)
    scenario.add_par('bound_new_capacity_up', df)
base_activity = {
    'node_loc': country,
    'year_act': horizon[1:],
    'mode': 'standard',
    'time': 'year',
    'unit': 'GWa',
}

# in GWh - base value from IEA Electricity Output
keep_activity = {
    'hydro_ppl': 38406,
    'bio_ppl':  4554,
    'import': 2340,
}

for tec, val in keep_activity.items():
    df = make_df(base_activity, technology=tec, value=val / 8760.)
    scenario.add_par('bound_activity_up', df)<jupyter_output><empty_output><jupyter_text>Emissions<jupyter_code>scenario.add_set('emission', 'CO2')
scenario.add_cat('emission', 'GHGs', 'CO2')
base_emissions = {
    'node_loc': country,
    'year_vtg': vintage_years,
    'year_act': act_years,
    'mode': 'standard',
    'unit': 'tCO2/kWa',
}

# adding new units to the model library (needed only once)
mp.add_unit('tCO2/kWa')
mp.add_unit('MtCO2')

emissions = {
    'coal_ppl': ('CO2', 0.854), # units: tCO2/MWh
    'gas_ppl':  ('CO2', 0.339), # units: tCO2/MWh
    'oil_ppl':  ('CO2', 0.57),  # units: tCO2/MWh
}

for tec, (species, val) in emissions.items():
    df = make_df(base_emissions, technology=tec, emission=species, value=val * 8760. / 1000) #to convert tCO2/MWh into tCO2/kWa
    scenario.add_par('emission_factor', df)<jupyter_output><empty_output><jupyter_text>Commit the datastructure and solve the model<jupyter_code>comment = 'initial commit for Austria model'
scenario.commit(comment)
scenario.set_as_default()
scenario.solve()
scenario.var('OBJ')['lvl']<jupyter_output><empty_output><jupyter_text>Plotting Results<jupyter_code>from message_ix.reporting import Reporter
from message_ix.util.tutorial import prepare_plots

rep = Reporter.from_scenario(scenario)
prepare_plots(rep)
rep.set_filters(t=plants)
rep.get("plot new capacity")
rep.set_filters(t=lights)
rep.get("plot new capacity")
rep.set_filters(t=plants)
rep.get("plot capacity")
rep.set_filters(t=lights)
rep.get("plot capacity")
rep.get("plot demand")
rep.set_filters(t=plants)
rep.get("plot activity")
rep.set_filters(t=lights)
rep.get("plot activity")
rep.set_filters(c=["light", "other_electricity"])
rep.get("plot prices")
mp.close_db()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13572627037392138, 'fraction_numerical': 0.019115532118887824, 'mean_word_length': 4.2153125, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 50, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '37801', 'license': 'Apache-2.0', 'n_tokens_mistral': 6101, 'n_tokens_neox': 5648, 'n_words': 1411, 'path': 'tutorial/Austrian_energy_system/austria.ipynb', 'repo_name': 'gianlucaroccasalvo/westeros-bis', 'size': '16688'}