# Commodity Constraints¶

**Commodity Balance** The function commodity balance calculates the in- and
outflows into all processes, storages and transmission of a commodity \(c\)
in a site \(v\) in support timeframe \(y\) at a timestep \(t\). The
value of the function \(\mathrm{CB}\) being greater than zero
\(\mathrm{CB} > 0\) means that the presence of the commodity \(c\) in
the site \(v\) in support timeframe \(y\) at the timestep \(t\) is
getting by the interaction with the technologies given above. Correspondingly,
the value of the function being less than zero means that the presence of the
commodity in the site at the timestep is getting more than before by the
technologies given above. The mathematical explanation of this rule for general
problems is explained in Energy Storage.

In script `modelhelper.py`

the value of the commodity balance function
\(\mathrm{CB}(y,v,c,t)\) is calculated by the following code fragment:

```
def commodity_balance(m, tm, stf, sit, com):
"""Calculate commodity balance at given timestep.
For a given commodity co and timestep tm, calculate the balance of
consumed (to process/storage/transmission, counts positive) and provided
(from process/storage/transmission, counts negative) commodity flow. Used
as helper function in create_model for constraints on demand and stock
commodities.
Args:
m: the model object
tm: the timestep
site: the site
com: the commodity
Returns
balance: net value of consumed (positive) or provided (negative) power
"""
balance = (sum(m.e_pro_in[(tm, stframe, site, process, com)]
# usage as input for process increases balance
for stframe, site, process in m.pro_tuples
if site == sit and stframe == stf and
(stframe, process, com) in m.r_in_dict) -
sum(m.e_pro_out[(tm, stframe, site, process, com)]
# output from processes decreases balance
for stframe, site, process in m.pro_tuples
if site == sit and stframe == stf and
(stframe, process, com) in m.r_out_dict))
if m.mode['tra']:
balance += transmission_balance(m, tm, stf, sit, com)
if m.mode['sto']:
balance += storage_balance(m, tm, stf, sit, com)
return balance
```

where the two functions introducing the partly balances for transmissions and storages, respectively, are given by:

```
def transmission_balance(m, tm, stf, sit, com):
"""called in commodity balance
For a given commodity co and timestep tm, calculate the balance of
import and export """
return (sum(m.e_tra_in[(tm, stframe, site_in, site_out,
transmission, com)]
# exports increase balance
for stframe, site_in, site_out, transmission, commodity
in m.tra_tuples
if (site_in == sit and stframe == stf and
commodity == com)) -
sum(m.e_tra_out[(tm, stframe, site_in, site_out,
transmission, com)]
# imports decrease balance
for stframe, site_in, site_out, transmission, commodity
in m.tra_tuples
if (site_out == sit and stframe == stf and
commodity == com)))
```

```
def storage_balance(m, tm, stf, sit, com):
"""called in commodity balance
For a given commodity co and timestep tm, calculate the balance of
storage input and output """
return sum(m.e_sto_in[(tm, stframe, site, storage, com)] -
m.e_sto_out[(tm, stframe, site, storage, com)]
# usage as input for storage increases consumption
# output from storage decreases consumption
for stframe, site, storage, commodity in m.sto_tuples
if site == sit and stframe == stf and commodity == com)
```

**Vertex Rule**: The vertex rule is the main constraint that has to be
satisfied for every commodity. It represents a version of
“Kirchhoff’s current law” or local energy conservation. This constraint is
defined differently for each commodity type. The inequality requires, that any
imbalance (CB>0, CB<0) of a commodity \(c\) in a site \(v\) and support
timeframe \(y\) at a timestep \(t\) to be balanced by a corresponding
source term or demand. The rule is not defined for environmental or SupIm
commodities. The mathematical explanation of this rule is given in
Minimal optimization model.

In script `model.py`

the constraint vertex rule is defined and calculated by
the following code fragments:

```
m.res_vertex = pyomo.Constraint(
m.tm, m.com_tuples,
rule=res_vertex_rule,
doc='storage + transmission + process + source + buy - sell == demand')
```

```
def res_vertex_rule(m, tm, stf, sit, com, com_type):
# environmental or supim commodities don't have this constraint (yet)
if com in m.com_env:
return pyomo.Constraint.Skip
if com in m.com_supim:
return pyomo.Constraint.Skip
# helper function commodity_balance calculates balance from input to
# and output from processes, storage and transmission.
# if power_surplus > 0: production/storage/imports create net positive
# amount of commodity com
# if power_surplus < 0: production/storage/exports consume a net
# amount of the commodity com
power_surplus = - commodity_balance(m, tm, stf, sit, com)
# if com is a stock commodity, the commodity source term e_co_stock
# can supply a possibly negative power_surplus
if com in m.com_stock:
power_surplus += m.e_co_stock[tm, stf, sit, com, com_type]
# if Buy and sell prices are enabled
if m.mode['bsp']:
power_surplus += bsp_surplus(m, tm, stf, sit, com, com_type)
# if com is a demand commodity, the power_surplus is reduced by the
# demand value; no scaling by m.dt or m.weight is needed here, as this
# constraint is about power (MW), not energy (MWh)
if com in m.com_demand:
try:
power_surplus -= m.demand_dict[(sit, com)][(stf, tm)]
except KeyError:
pass
if m.mode['dsm']:
power_surplus += dsm_surplus(m, tm, stf, sit, com)
return power_surplus == 0
```

where the two functions introducing the effects of Buy/Sell or DSM events, respectively, are given by:

```
def bsp_surplus(m, tm, stf, sit, com, com_type):
power_surplus = 0
# if com is a sell commodity, the commodity source term e_co_sell
# can supply a possibly positive power_surplus
if com in m.com_sell:
power_surplus -= m.e_co_sell[tm, stf, sit, com, com_type]
# if com is a buy commodity, the commodity source term e_co_buy
# can supply a possibly negative power_surplus
if com in m.com_buy:
power_surplus += m.e_co_buy[tm, stf, sit, com, com_type]
return power_surplus
```

```
def dsm_surplus(m, tm, stf, sit, com):
""" called in vertex rule
calculate dsm surplus"""
if (stf, sit, com) in m.dsm_site_tuples:
return (- m.dsm_up[tm, stf, sit, com] +
sum(m.dsm_down[t, tm, stf, sit, com]
for t in dsm_time_tuples(
tm, m.timesteps[1:],
max(int(1 / m.dt *
m.dsm_dict['delay'][(stf, sit, com)]), 1))))
else:
return 0
```

**Stock Per Step Rule**: The constraint stock per step rule applies only for
commodities of type “Stock” (\(c \in C_\text{st}\)). This constraint limits
the amount of stock commodity \(c \in C_\text{st}\), that can be used by
the energy system in the site \(v\) in support timeframe \(y\) at the
timestep \(t\). This amount is limited by the product of the parameter
maximum stock supply limit per hour \(\overline{l}_{yvc}\) and the timestep
length \(\Delta t\). The mathematical explanation of this rule is given in
Minimal optimization model.

In script `model.py`

the constraint stock per step rule is defined and
calculated by the following code fragment:

```
m.res_stock_step = pyomo.Constraint(
m.tm, m.com_tuples,
rule=res_stock_step_rule,
doc='stock commodity input per step <= commodity.maxperstep')
```

```
def res_stock_step_rule(m, tm, stf, sit, com, com_type):
if com not in m.com_stock:
return pyomo.Constraint.Skip
else:
return (m.e_co_stock[tm, stf, sit, com, com_type] <=
m.dt * m.commodity_dict['maxperhour']
[(stf, sit, com, com_type)])
```

**Total Stock Rule**: The constraint total stock rule applies only for
commodities of type “Stock” (\(c \in C_\text{st}\)). This constraint limits
the amount of stock commodity \(c \in C_\text{st}\), that can be used
annually by the energy system in the site \(v\) and support timeframe
\(y\). This amount is limited by the parameter maximum annual stock supply
limit per vertex \(\overline{L}_{yvc}\). The annual usage of stock
commodity is calculated by the sum of the products of the parameter weight
\(w\) and the parameter stock commodity source term \(\rho_{yvct}\),
summed over all timesteps \(t \in T_m\). The mathematical explanation of
this rule is given in Minimal optimization model.

In script `model.py`

the constraint total stock rule is defined and
calculated by the following code fragment:

```
m.res_stock_total = pyomo.Constraint(
m.com_tuples,
rule=res_stock_total_rule,
doc='total stock commodity input <= commodity.max')
```

```
def res_stock_total_rule(m, stf, sit, com, com_type):
if com not in m.com_stock:
return pyomo.Constraint.Skip
else:
# calculate total consumption of commodity com
total_consumption = 0
for tm in m.tm:
total_consumption += (
m.e_co_stock[tm, stf, sit, com, com_type])
total_consumption *= m.weight
return (total_consumption <=
m.commodity_dict['max'][(stf, sit, com, com_type)])
```

**Sell Per Step Rule**: The constraint sell per step rule applies only for
commodities of type “Sell” ( \(c \in C_\text{sell}\)). This constraint
limits the amount of sell commodity \(c \in C_\text{sell}\), that can be
sold by the energy system in the site \(v\) in support timeframe \(y\)
at the timestep \(t\). The limit is defined by the parameter maximum sell
supply limit per hour \(\overline{g}_{yvc}\). To satisfy this constraint,
the value of the variable sell commodity source term \(\varrho_{yvct}\)
must be less than or equal to the value of the parameter maximum sell supply
limit per hour \(\overline{g}_{vc}\) multiplied with the length of the
time steps \(\Delta t\). The mathematical explanation of this rule is given
in Trading with an external market.

In script `BuySellPrice.py`

the constraint sell per step rule is defined and
calculated by the following code fragment:

```
m.res_sell_step = pyomo.Constraint(
m.tm, m.com_tuples,
rule=res_sell_step_rule,
doc='sell commodity output per step <= commodity.maxperstep')
```

```
def res_sell_step_rule(m, tm, stf, sit, com, com_type):
if com not in m.com_sell:
return pyomo.Constraint.Skip
else:
return (m.e_co_sell[tm, stf, sit, com, com_type] <=
m.dt * m.commodity_dict['maxperhour']
[(stf, sit, com, com_type)])
```

**Total Sell Rule**: The constraint total sell rule applies only for
commodities of type “Sell” ( \(c \in C_\text{sell}\)). This constraint
limits the amount of sell commodity \(c \in C_\text{sell}\), that can be
sold annually by the energy system in the site \(v\) and support timeframe
\(y\). The limit is defined by the parameter maximum annual sell supply
limit per vertex \(\overline{G}_{yvc}\). The annual usage of sell commodity
is calculated by the sum of the products of the parameter weight \(w\) and
the parameter sell commodity source term \(\varrho_{yvct}\), summed over
all timesteps \(t \in T_m\). The mathematical explanation of this rule is
given in Trading with an external market.

In script `BuySellPrice.py`

the constraint total sell rule is defined and
calculated by the following code fragment:

```
m.res_sell_total = pyomo.Constraint(
m.com_tuples,
rule=res_sell_total_rule,
doc='total sell commodity output <= commodity.max')
```

```
def res_sell_total_rule(m, stf, sit, com, com_type):
if com not in m.com_sell:
return pyomo.Constraint.Skip
else:
# calculate total sale of commodity com
total_consumption = 0
for tm in m.tm:
total_consumption += (
m.e_co_sell[tm, stf, sit, com, com_type])
total_consumption *= m.weight
return (total_consumption <=
m.commodity_dict['max'][(stf, sit, com, com_type)])
```

**Buy Per Step Rule**: The constraint buy per step rule applies only for
commodities of type “Buy” ( \(c \in C_\text{buy}\)). This constraint limits
the amount of buy commodity \(c \in C_\text{buy}\), that can be bought by
the energy system in the site \(v\) in support timeframe \(y\) at the
timestep \(t\). The limit is defined by the parameter maximum buy
supply limit per time step \(\overline{b}_{yvc}\). To satisfy this
constraint, the value of the variable buy commodity source term
\(\psi_{yvct}\) must be less than or equal to the value of the parameter
maximum buy supply limit per time step \(\overline{b}_{vc}\), multiplied by
the length of the time steps \(\Delta t\). The mathematical explanation of
this rule is given in Trading with an external market.

In script `BuySellPrice.py`

the constraint buy per step rule is defined and
calculated by the following code fragment:

```
m.res_buy_step = pyomo.Constraint(
m.tm, m.com_tuples,
rule=res_buy_step_rule,
doc='buy commodity output per step <= commodity.maxperstep')
```

```
def res_buy_step_rule(m, tm, stf, sit, com, com_type):
if com not in m.com_buy:
return pyomo.Constraint.Skip
else:
return (m.e_co_buy[tm, stf, sit, com, com_type] <=
m.dt * m.commodity_dict['maxperhour']
[(stf, sit, com, com_type)])
```

**Total Buy Rule**: The constraint total buy rule applies only for commodities
of type “Buy” ( \(c \in C_\text{buy}\)). This constraint limits the amount
of buy commodity \(c \in C_\text{buy}\), that can be bought annually by the
energy system in the site \(v\) in support timeframe \(y\). The limit
is defined by the parameter maximum annual buy supply limit per vertex
\(\overline{B}_{yvc}\). To satisfy this constraint, the annual usage of buy
commodity must be less than or equal to the value of the parameter buy supply
limit per vertex \(\overline{B}_{vc}\). The annual usage of buy commodity
is calculated by the sum of the products of the parameter weight \(w\) and
the parameter buy commodity source term \(\psi_{yvct}\), summed over all
modeled timesteps \(t \in T_m\). The mathematical explanation of this rule
is given in Trading with an external market.

In script `BuySellPrice.py`

the constraint total buy rule is defined and
calculated by the following code fragment:

```
m.res_buy_total = pyomo.Constraint(
m.com_tuples,
rule=res_buy_total_rule,
doc='total buy commodity output <= commodity.max')
```

```
def res_buy_total_rule(m, stf, sit, com, com_type):
if com not in m.com_buy:
return pyomo.Constraint.Skip
else:
# calculate total sale of commodity com
total_consumption = 0
for tm in m.tm:
total_consumption += (
m.e_co_buy[tm, stf, sit, com, com_type])
total_consumption *= m.weight
return (total_consumption <=
m.commodity_dict['max'][(stf, sit, com, com_type)])
```

**Environmental Output Per Step Rule**: The constraint environmental output per
step rule applies only for commodities of type “Env”
(\(c \in C_\text{env}\)). This constraint limits the amount of
environmental commodity \(c \in C_\text{env}\), that can be released to
environment by the energy system in the site \(v\) in support timeframe
\(y\) at the timestep \(t\). The limit is defined by the parameter
maximum environmental output per time step \(\overline{m}_{yvc}\). To
satisfy this constraint, the negative value of the commodity balance for the
given environmental commodity \(c \in C_\text{env}\) must be less than or
equal to the value of the parameter maximum environmental output per time step
\(\overline{m}_{vc}\), multiplied by the length of the time steps
\(\Delta t\). The mathematical explanation of this rule is given
in Minimal optimization model.

In script `model.py`

the constraint environmental output per step rule is
defined and calculated by the following code fragment:

```
m.res_env_step = pyomo.Constraint(
m.tm, m.com_tuples,
rule=res_env_step_rule,
doc='environmental output per step <= commodity.maxperstep')
```

```
def res_env_step_rule(m, tm, stf, sit, com, com_type):
if com not in m.com_env:
return pyomo.Constraint.Skip
else:
environmental_output = - commodity_balance(m, tm, stf, sit, com)
return (environmental_output <=
m.dt * m.commodity_dict['maxperhour']
[(stf, sit, com, com_type)])
```

**Total Environmental Output Rule**: The constraint total environmental output
rule applies only for commodities of type “Env” ( \(c \in C_\text{env}\)).
This constraint limits the amount of environmental commodity
\(c \in C_\text{env}\), that can be released to environment annually by the
energy system in the site \(v\) in support timeframe \(y\). The limit
is defined by the parameter maximum annual environmental output limit per
vertex \(\overline{M}_{yvc}\). To satisfy this constraint, the annual
release of environmental commodity must be less than or equal to the value of
the parameter maximum annual environmental output \(\overline{M}_{vc}\).
The annual release of environmental commodity is calculated by the sum of the
products of the parameter weight \(w\) and the negative value of commodity
balance function, summed over all modeled time steps \(t \in T_m\). The
mathematical explanation of this rule is given in Minimal optimization model.

In script `model.py`

the constraint total environmental output rule is
defined and calculated by the following code fragment:

```
m.res_env_total = pyomo.Constraint(
m.com_tuples,
rule=res_env_total_rule,
doc='total environmental commodity output <= commodity.max')
```

```
def res_env_total_rule(m, stf, sit, com, com_type):
if com not in m.com_env:
return pyomo.Constraint.Skip
else:
# calculate total creation of environmental commodity com
env_output_sum = 0
for tm in m.tm:
env_output_sum += (- commodity_balance(m, tm, stf, sit, com))
env_output_sum *= m.weight
return (env_output_sum <=
m.commodity_dict['max'][(stf, sit, com, com_type)])
```

## Demand Side Management Constraints¶

The DSM equations are taken from the Paper of Zerrahn and Schill “On the representation of demand-side management in power system models”, DOI: 10.1016/j.energy.2015.03.037.

**DSM Variables Rule**: The DSM variables rule defines the relation between the
up- and downshifted DSM commodities. An upshift \(\delta_{yvct}^\text{up}\)
in site \(v\) and support timeframe \(y\) of demand commodity \(c\)
in time step \(t\) can be compensated during a certain time step interval
\([t-y_{yvc}/{\Delta t}, t+y_{yvc}/{\Delta t}]\) by multiple downshifts
\(\delta_{t,tt,yvc}^\text{down}\). Here, \(y_{yvc}\) represents the
allowable delay time of downshifts in hours, which is scaled into time steps by
dividing by the timestep length \({\Delta t}\). Depending on the DSM
efficiency \(e_{yvc}\), an upshift in a DSM commodity may correspond to
multiple downshifts which sum to less than the original upshift. The
mathematical explanation of this rule is given in Demand side management.

In script `dsm.py`

the constraint DSM variables rule is defined by the
following code fragment:

```
m.def_dsm_variables = pyomo.Constraint(
m.tm, m.dsm_site_tuples,
rule=def_dsm_variables_rule,
doc='DSMup * efficiency factor n == DSMdo (summed)')
```

```
def def_dsm_variables_rule(m, tm, stf, sit, com):
dsm_down_sum = 0
for tt in dsm_time_tuples(tm,
m.timesteps[1:],
max(int(1 / m.dt *
m.dsm_dict['delay'][(stf, sit, com)]), 1)):
dsm_down_sum += m.dsm_down[tm, tt, stf, sit, com]
return dsm_down_sum == (m.dsm_up[tm, stf, sit, com] *
m.dsm_dict['eff'][(stf, sit, com)])
```

**DSM Upward Rule**: The DSM upshift \(\delta_{yvct}^\text{up}\) in site
\(v\) and support timeframe \(y\) of demand commodity \(c\) in time
step \(t\) is limited by the DSM maximal upshift per hour
\(\overline{K}_{yvc}^\text{up}\), multiplied by the length of the time
steps \(\Delta t\). The mathematical explanation of this rule is given in
Demand side management.

In script `dsm.py`

the constraint DSM upward rule is defined by the
following code fragment:

```
m.res_dsm_upward = pyomo.Constraint(
m.tm, m.dsm_site_tuples,
rule=res_dsm_upward_rule,
doc='DSMup <= Cup (threshold capacity of DSMup)')
```

```
def res_dsm_upward_rule(m, tm, stf, sit, com):
return m.dsm_up[tm, stf, sit, com] <= (m.dt *
m.dsm_dict['cap-max-up']
[(stf, sit, com)])
```

**DSM Downward Rule**: The total DSM downshift
\(\delta_{t,tt,yvc}^\text{down}\) in site \(v\) and support timeframe
\(y\) of demand commodity \(c\) in time step \(t\) is limited by
the DSM maximal downshift per hour \(\overline{K}_{yvc}^\text{down}\),
multiplied by the length of the time steps \(\Delta t\). The mathematical
explanation of this rule is given in Demand side management.

In script `dsm.py`

the constraint DSM downward rule is defined by the
following code fragment:

```
m.res_dsm_downward = pyomo.Constraint(
m.tm, m.dsm_site_tuples,
rule=res_dsm_downward_rule,
doc='DSMdo (summed) <= Cdo (threshold capacity of DSMdo)')
```

```
def res_dsm_downward_rule(m, tm, stf, sit, com):
dsm_down_sum = 0
for t in dsm_time_tuples(tm,
m.timesteps[1:],
max(int(1 / m.dt *
m.dsm_dict['delay'][(stf, sit, com)]), 1)):
dsm_down_sum += m.dsm_down[t, tm, stf, sit, com]
return dsm_down_sum <= (m.dt * m.dsm_dict['cap-max-do'][(stf, sit, com)])
```

**DSM Maximum Rule**: The DSM maximum rule limits the shift of one DSM unit in
site \(v\) in support timeframe \(y\) of demand commodity \(c\) in
time step \(t\). The mathematical explanation of this rule is given in
Demand side management.

In script `dsm.py`

the constraint DSM maximum rule is defined by the
following code fragment:

```
m.res_dsm_maximum = pyomo.Constraint(
m.tm, m.dsm_site_tuples,
rule=res_dsm_maximum_rule,
doc='DSMup + DSMdo (summed) <= max(Cup,Cdo)')
```

```
def res_dsm_maximum_rule(m, tm, stf, sit, com):
dsm_down_sum = 0
for t in dsm_time_tuples(tm,
m.timesteps[1:],
max(int(1 / m.dt *
m.dsm_dict['delay'][(stf, sit, com)]), 1)):
dsm_down_sum += m.dsm_down[t, tm, stf, sit, com]
max_dsm_limit = m.dt * max(m.dsm_dict['cap-max-up'][(stf, sit, com)],
m.dsm_dict['cap-max-do'][(stf, sit, com)])
return m.dsm_up[tm, stf, sit, com] + dsm_down_sum <= max_dsm_limit
```

**DSM Recovery Rule**: The DSM recovery rule limits the upshift in site
\(v\) and support timeframe \(y\) of demand commodity \(c\) during
a set recovery period \(o_{yvc}\). Since the recovery period
\(o_{yvc}\) is input as hours, it is scaled into time steps by dividing it
by the length of the time steps \(\Delta t\). The mathematical explanation
of this rule is given in Demand side management.

In script `dsm.py`

the constraint DSM Recovery rule is defined by the
following code fragment:

```
m.res_dsm_recovery = pyomo.Constraint(
m.tm, m.dsm_site_tuples,
rule=res_dsm_recovery_rule,
doc='DSMup(t, t + recovery time R) <= Cup * delay time L')
```

```
def res_dsm_recovery_rule(m, tm, stf, sit, com):
dsm_up_sum = 0
for t in dsm_recovery(tm,
m.timesteps[1:],
max(int(1 / m.dt *
m.dsm_dict['recov'][(stf, sit, com)]), 1)):
dsm_up_sum += m.dsm_up[t, stf, sit, com]
return dsm_up_sum <= (m.dsm_dict['cap-max-up'][(stf, sit, com)] *
m.dsm_dict['delay'][(stf, sit, com)])
```

## Global Environmental Constraint¶

**Global CO2 Limit Rule**: The constraint global CO2 limit rule applies to the
whole energy system in one support timeframe \(y\), that is to say it
applies to every site and timestep. This constraints restricts the total amount
of CO2 to environment. The constraint states that the sum of released CO2
across all sites \(v\in V\) and timesteps \(t \in t_m\) must be less
than or equal to the parameter maximum global annual CO2 emission limit
\(\overline{L}_{CO_{2},y}\), where the amount of released CO2 in a single
site \(v\) at a single timestep \(t\) is calculated by the product of
commodity balance of environmental commodities \(\mathrm{CB}(y,v,CO_{2},t)\)
and the parameter weight \(w\). This constraint is skipped if the value of
the parameter \(\overline{L}_{CO_{2}}\) is set to `inf`

. The mathematical
explanation of this rule is given in Minimal optimization model.

In script `model.py`

the constraint annual global CO2 limit rule is defined
and calculated by the following code fragment:

```
def res_global_co2_limit_rule(m, stf):
if math.isinf(m.global_prop_dict['value'][stf, 'CO2 limit']):
return pyomo.Constraint.Skip
elif m.global_prop_dict['value'][stf, 'CO2 limit'] >= 0:
co2_output_sum = 0
for tm in m.tm:
for sit in m.sit:
# minus because negative commodity_balance represents creation
# of that commodity.
co2_output_sum += (- commodity_balance(m, tm,
stf, sit, 'CO2'))
# scaling to annual output (cf. definition of m.weight)
co2_output_sum *= m.weight
return (co2_output_sum <= m.global_prop_dict['value']
[stf, 'CO2 limit'])
else:
return pyomo.Constraint.Skip
```

**Global CO2 Budget Rule**: The constraint global CO2 budget rule applies to
the whole energy system over the entire modeling horizon, that is to say it
applies to every support timeframe, site and timestep. This constraints
restricts the total amount of CO2 to environment. The constraint states that
the sum of released CO2 across all support timeframe \(y\in Y\), sites
\(v\in V\) and timesteps \(t \in t_m\) must be less than or equal to
the parameter maximum global CO2 emission budget
\(\overline{\overline{L}}_{CO_{2},y}\), where the amount of released CO2 in
a single support timeframe \(y\) in a single site \(v\) and at a single
timestep \(t\) is calculated by the product of the commodity balance of
environmental commodities \(\mathrm{CB}(y,v,CO_{2},t)\) and the parameter
weight \(w\). This constraint is skipped if the value of the parameter
\(\overline{\overline{L}}_{CO_{2}}\) is set to `inf`

. The mathematical
explanation of this rule is given in Intertemporal optimization model.

In script `model.py`

the constraint global CO2 budget is defined and
calculated by the following code fragment:

```
def res_global_co2_budget_rule(m):
if math.isinf(m.global_prop_dict['value'][min(m.stf_list), 'CO2 budget']):
return pyomo.Constraint.Skip
elif (m.global_prop_dict['value'][min(m.stf_list), 'CO2 budget']) >= 0:
co2_output_sum = 0
for stf in m.stf:
for tm in m.tm:
for sit in m.sit:
# minus because negative commodity_balance represents
# creation of that commodity.
co2_output_sum += (- commodity_balance
(m, tm, stf, sit, 'CO2') *
m.weight *
stf_dist(stf, m))
return (co2_output_sum <=
m.global_prop_dict['value'][min(m.stf), 'CO2 budget'])
else:
return pyomo.Constraint.Skip
```