Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A tutorial for adding an upper bound on investment #602

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Migration notes
All changes
-----------

- A tutorial for adding an upper bound on investment (:pull:`602`).
- Add additional oscillation detection mechanism for macro iterations (:pull:`645`, :pull:`676`)
- Adjust default `lpmethod` from "Dual Simplex" (2) to "Barrier" (4); do NOT remove `cplex.opt` file(s) after solving workflow completes (:pull:`657`).
- Adjust :meth:`.Scenario.add_macro` calculations for pandas 1.5.0 (:pull:`656`).
Expand Down
2 changes: 1 addition & 1 deletion message_ix/model/MESSAGE/data_load.gms
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ duration_period, duration_time, interestrate,
resource_volume, resource_cost, is_bound_extraction_up, bound_extraction_up, resource_remaining,
* technology technical-engineering parameters and economic costs
input, output, construction_time, technical_lifetime
capacity_factor, operation_factor, min_utilization_factor, inv_cost, fix_cost, var_cost,
capacity_factor, operation_factor, min_utilization_factor, inv_cost, fix_cost, var_cost, bound_investment_up
* upper and lower bounds on new capacity investment, total installed capacity and activity (including mapping sets)
is_bound_new_capacity_up, is_bound_new_capacity_lo, bound_new_capacity_up, bound_new_capacity_lo,
is_bound_total_capacity_up, is_bound_total_capacity_lo, bound_total_capacity_up, bound_total_capacity_lo,
Expand Down
59 changes: 51 additions & 8 deletions message_ix/model/MESSAGE/model_core.gms
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* :math:`COMMODITY\_BALANCE_{n,c,l,y,h} \in \mathbb{R}` Auxiliary variable for right-hand side of :ref:`commodity_balance`
* :math:`STORAGE_{n,t,m,l,c,y,h} \in \mathbb{R}` State of charge or content of storage at each sub-annual time slice
* :math:`STORAGE\_CHARGE_{n,t,m,l,c,y,h} \in \mathbb{R}` Charging of storage in each sub-annual time slice (negative for discharging)
* :math:`INVEST_{n,y} \in \mathbb{R}` Investment per region over time
* ======================================================== ====================================================================================
*
* The index :math:`y^V` is the year of construction (vintage) wherever it is necessary to
Expand Down Expand Up @@ -107,6 +108,8 @@ Positive Variables
LAND(node,land_scenario,year_all) relative share of land-use scenario
* content of storage
STORAGE(node,tec,mode,level,commodity,year_all,time) state of charge (SoC) of storage at each sub-annual time slice (positive)
* investment per region and year
INVEST(node,year_all) investment per model region and year
;

Variables
Expand Down Expand Up @@ -290,6 +293,8 @@ Equations
STORAGE_BALANCE balance of the state of charge of storage
STORAGE_BALANCE_INIT balance of the state of charge of storage at sub-annual time slices with initial storage content
STORAGE_INPUT connecting an input commodity to maintain the activity of storage container (not stored commodity)
INVESTMENT_EQUIVALENCE investment per node and year
INVESTMENT_CONSTRAINT upper bound on investment per node and year
;
*----------------------------------------------------------------------------------------------------------------------*
* equation statements *
Expand Down Expand Up @@ -337,10 +342,8 @@ OBJECTIVE..
*
* .. math::
* COST\_NODAL_{n,y} & = \sum_{c,g} \ resource\_cost_{n,c,g,y} \cdot EXT_{n,c,g,y} \\
* & + \sum_{t} \
* \bigg( inv\_cost_{n,t,y} \cdot construction\_time\_factor_{n,t,y} \\
* & \quad \quad \quad \cdot end\_of\_horizon\_factor_{n,t,y} \cdot CAP\_NEW_{n,t,y} \\[4 pt]
* & \quad \quad + \sum_{y^V \leq y} \ fix\_cost_{n,t,y^V,y} \cdot CAP_{n,t,y^V,y} \\
* & + INVEST_{n, y} + \sum_{t} \
* \bigg( \sum_{y^V \leq y} \ fix\_cost_{n,t,y^V,y} \cdot CAP_{n,t,y^V,y} \\
* & \quad \quad + \sum_{\substack{y^V \leq y \\ m,h}} \ var\_cost_{n,t,y^V,y,m,h} \cdot ACT_{n,t,y^V,y,m,h} \\
* & \quad \quad + \Big( abs\_cost\_new\_capacity\_soft\_up_{n,t,y} \\
* & \quad \quad \quad
Expand Down Expand Up @@ -371,10 +374,9 @@ COST_ACCOUNTING_NODAL(node, year)..
SUM((commodity,grade)$( map_resource(node,commodity,grade,year) ),
resource_cost(node,commodity,grade,year) * EXT(node,commodity,grade,year) )
* technology capacity investment, maintainance, operational cost
+ INVEST(node, year)
+ SUM((tec)$( map_tec(node,tec,year) ),
( inv_cost(node,tec,year) * construction_time_factor(node,tec,year)
* end_of_horizon_factor(node,tec,year) * CAP_NEW(node,tec,year)
+ SUM(vintage$( map_tec_lifetime(node,tec,vintage,year) ),
(SUM(vintage$( map_tec_lifetime(node,tec,vintage,year) ),
fix_cost(node,tec,vintage,year) * CAP(node,tec,vintage,year) ) )$( inv_tec(tec) )
+ SUM((vintage,mode,time)$( map_tec_lifetime(node,tec,vintage,year) AND map_tec_act(node,tec,year,mode,time) ),
var_cost(node,tec,vintage,year,mode,time) * ACT(node,tec,vintage,year,mode,time) )
Expand Down Expand Up @@ -447,13 +449,54 @@ COST_ACCOUNTING_NODAL(node, year)..
%SLACK_RELATION_BOUND_LO% + 1e6 * SLACK_RELATION_BOUND_LO(relation,node,year)$( is_relation_lower(relation,node,year) )
)
;

***
* Here, :math:`n^L \in N(n)` are all nodes :math:`n^L` that are sub-nodes of node :math:`n`.
* The subset of technologies :math:`t \in T(\widehat{t})` are all tecs that belong to category :math:`\widehat{t}`,
* and similar notation is used for emissions :math:`e \in E`.
***

***
* Regional investment cost accounting
* ----------------------------------------
*
* Accounting of regional investment costs over time
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* .. _equation_investment_equivalence:
*
* Equation INVESTMENT_EQUIVALENCE
* """"""""""""""""""""""""""""""
*
* This equation calculates the regional investment costs over time.
*
* .. math::
* INVEST_{n,y} & = \sum_{t} \
* \bigg( inv\_cost_{n,t,y} \cdot construction\_time\_factor_{n,t,y} \\
* & \quad \quad \quad \cdot end\_of\_horizon\_factor_{n,t,y} \cdot CAP\_NEW_{n,t,y}) \\
***

INVESTMENT_EQUIVALENCE(node, year) ..
INVEST(node, year) =E= SUM(tec$( map_tec(node,tec,year) ),
( inv_cost(node,tec,year) * construction_time_factor(node,tec,year)
* end_of_horizon_factor(node,tec,year) * CAP_NEW(node,tec,year) )$( inv_tec(tec) )
)
;

***
*
* .. _equation_investment_constraint:
*
* Equation INVESTMENT_CONSTRAINT
* """"""""""""""""""""""""""""""
*
* This equation puts an upper bound on the regional investment costs over time.
*
* .. math::
* INVEST_{n,y} \leq bound\_investment\_up_{n,y} \\
***
INVESTMENT_CONSTRAINT(node, year)$(bound_investment_up(node, year) ) ..
INVEST(node, year) =L= bound_investment_up(node, year)
;
*----------------------------------------------------------------------------------------------------------------------*
***
* .. _section_resource_commodity:
Expand Down
3 changes: 3 additions & 0 deletions message_ix/model/MESSAGE/parameter_def.gms
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,8 @@ Parameters
* - ``node`` | ``tec`` | ``year``
* * - beyond_horizon_factor
* - ``node`` | ``tec`` | ``year``
* * - bound_investment_up
* - ``node`` | ``year``
*
*
***
Expand All @@ -583,6 +585,7 @@ Parameters
end_of_horizon_factor(node,tec,year_all) multiplier for value of investment at end of model horizon
beyond_horizon_lifetime(node,tec,year_all) remaining technical lifetime at the end of model horizon
beyond_horizon_factor(node,tec,year_all) discount factor of remaining lifetime beyond model horizon
bound_investment_up(node, year_all) upper bound on investment per node and year
;

*----------------------------------------------------------------------------------------------------------------------*
Expand Down
3 changes: 3 additions & 0 deletions message_ix/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def item(ix_type, expr):
"bound_activity_up": item("par", "nl t ya m h"),
"bound_emission": item("par", "n type_emission type_tec type_year"),
"bound_extraction_up": item("par", "n c g y"),
"bound_investment_up": item("par", "n y"),
"bound_new_capacity_lo": item("par", "nl t yv"),
"bound_new_capacity_up": item("par", "nl t yv"),
"bound_total_capacity_lo": item("par", "nl t ya"),
Expand Down Expand Up @@ -269,6 +270,8 @@ def item(ix_type, expr):
#
# Variables
#
# # Investment
"INVEST": item("var", "n y"),
# # Activity
# "ACT": item("var", "nl t yv ya m h"),
# # Maintained capacity
Expand Down
6 changes: 3 additions & 3 deletions message_ix/tests/test_reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_reporter_from_scenario(message_test_mp):
rep = Reporter.from_scenario(scen)

# Number of quantities available in a rudimentary MESSAGEix Scenario
assert len(rep.graph["all"]) == 125
assert len(rep.graph["all"]) == 127

# Quantities have short dimension names
assert "demand:n-c-l-y-h" in rep
Expand All @@ -69,11 +69,11 @@ def test_reporter_from_scenario(message_test_mp):
assert_qty_equal(obs, demand, check_attrs=False)

# ixmp.Reporter pre-populated with only model quantities and aggregates
assert len(rep_ix.graph) == 5609
assert len(rep_ix.graph) == 5617

# message_ix.Reporter pre-populated with additional, derived quantities
# This is the same value as in test_tutorials.py
assert len(rep.graph) == 13074
assert len(rep.graph) == 13082

# Derived quantities have expected dimensions
vom_key = rep.full_key("vom")
Expand Down
3 changes: 2 additions & 1 deletion message_ix/tests/test_tutorials.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@
(("westeros", "westeros_soft_constraints"), [], {}),
(("westeros", "westeros_addon_technologies"), [], {}),
(("westeros", "westeros_historical_new_capacity"), [], {}),
(("westeros", "westeros_investment"), [], {}),
# NB this is the same value as in test_reporter()
(("westeros", "westeros_report"), [("len-rep-graph", 13074)], {}),
(("westeros", "westeros_report"), [("len-rep-graph", 13082)], {}),
((AT, "austria"), [("solve-objective-value", 206321.90625)], {}),
(
(AT, "austria_single_policy"),
Expand Down
6 changes: 6 additions & 0 deletions tutorial/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ framework, such as used in global research applications of |MESSAGEix|.
#. Import and combine data from multiple files to create a new scenario
(:tut:`westeros/westeros_baseline_using_xlsx_import_part2.ipynb`).

#. Extend the features of :mod:`message_ix` and :mod:`ixmp`:

#. Extend the GAMS formulation by adding new sets, parameters, variables,
and equations, and change the :mod:`message_ix` code accordingly
(:tut:`westeros/westeros_investment.ipynb`).

.. _austria-tutorials:

Austrian energy system
Expand Down
Loading