# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python [conda env:anaconda]
#     language: python
#     name: conda-env-anaconda-py
# ---

# ## Westeros Tutorial Part III - Introducing emission taxes
#
# In the second part, we showed how to introduce emissions into a stylized energy systems model, and what happens if you put a constraint on total CO2 emissions.
#
# Now, we will tackle the complementary policy to emissions constraints, namely emissions taxes.

# +
import pandas as pd
import ixmp as ix
import message_ix

from message_ix.utils import make_df

# %matplotlib inline
# -

mp = ix.Platform(dbtype='HSQLDB')

model = 'Westeros Electrified'
base = message_ix.Scenario(mp, model=model, scenario='baseline')

# ## Load the scenario with an emission bound and look at the result in more detail

scen_bd = message_ix.Scenario(mp, model=model, scenario='emission_bound')

emissions = scen_bd.var('EMISS', {'node': 'Westeros'})
emissions

emission_prices = scen_bd.var('PRICE_EMISSION')
emission_prices

# When setting a cumlulative bound, the optimization model choses an emission trajectory that pushes the cost towards the  end of the model horizon. As a consequence, the shadow price or dual variable of the constraint increase exponentially at the discount rate.

# ## Make a new scenario with emission bounds by year
#
# In the previous example, we imposed a bound on emissions over the entire model horizon by using the `type_year 'cumulative'`. Now, we will create a similar scenario, but the constraint will be defined per year.
#
# For the sake of comparison, the per-year emission values will be chosen exactly in line with the optimal emission trajectory from the previous scenario.

scen_bd_by_year = base.clone(model, 'carbon_bound_by_year','introducing a carbon tax', keep_sol=False)
scen_bd_by_year.check_out()

scen_bd_by_year.add_set('emission', 'CO2')
scen_bd_by_year.add_cat('emission', 'GHG', 'CO2')
scen_bd_by_year.add_par('emission_factor', scen_bd.par('emission_factor'))

# +
base_bd_emission = {
    'node': 'Westeros',
    'type_year': [700,710,720],
    'type_tec': 'all',
    'unit': 'tCO2',
    'type_emission': 'GHG',
    'value': emissions.lvl
}

bd_emission = make_df(base_bd_emission)
scen_bd_by_year.add_par('bound_emission', bd_emission)
# -

scen_bd_by_year.commit(comment='emission bound by year')

scen_bd_by_year.solve()

emission_prices_by_year = scen_bd_by_year.var('PRICE_EMISSION')
emission_prices_by_year

# Comparing the emission prices between the two scenarios, we see that the values are identical in the year 710 and close in the year 720. However, the bound in the year 700 is not binding, so the shadow price is 0 (and is not shown here).

# ## Setting an emissions tax instead of a bound
#
# Again, we choose the emissions prices from the first example (with a cumulative bound) as the tax level over time.

scen_tax = base.clone(model, 'carbon_tax','introducing a carbon tax', keep_sol=False)
scen_tax.check_out()

scen_tax.add_set('emission', 'CO2')
scen_tax.add_cat('emission', 'GHG', 'CO2')
scen_tax.add_par('emission_factor', scen_bd.par('emission_factor'))

# +
scen_tax.add_set('type_year', [700,710,720])

base_tax_emission = {
    'node': 'Westeros',
    'type_year': [700,710,720],
    'type_tec': 'all',
    'unit': 'tCO2',
    'type_emission': 'GHG',
    'value': emission_prices.lvl
}

tax_emission = make_df(base_tax_emission)
scen_tax.add_par('tax_emission', tax_emission)
# -

scen_tax.commit(comment='setting taxes on emissions')

scen_tax.solve()

scen_tax.var('EMISS', {'node': 'Westeros'})

# Comparing the emissions trajectory in the tax scenario to the outcome in the cumulative budget constraint scenario, we notice that the values in the years 700 and 720 are identical, but the value in 710 is different.
#
# This is the flip side of having an identical shadow price on the constraint in the two previous examples - at that price, the costs between wind and coal (with the tax) are exactly equal, hence the optimal solution is not unique.
#
# This is usually only an issue in small, stylized problems...

mp.close_db()



StarcoderdataJupyterScriptsDedupFiltered