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

Read GAMS CPLEX solver options for MESSAGE from user config #557

Merged
merged 12 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ jobs:
- uses: actions/setup-python@v2
# This should match the "Latest version testable on GitHub Actions"
# in pytest.yaml
with:
python-version: "3.9"
# with:
# python-version: "3.10"

- name: Cache Python packages
uses: actions/cache@v2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nightly.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ jobs:
- uses: actions/setup-python@v2
# This should match the "Latest version testable on GitHub Actions"
# in pytest.yaml
with:
python-version: "3.9"
# with:
# python-version: "3.10"

- name: Cache GAMS installer and Python packages
uses: actions/cache@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
# This should match the "Latest version testable on GitHub Actions"
# in pytest.yaml
# with:
# python-version: "3.9"
# python-version: "3.10"

- name: Cache Python packages
uses: actions/cache@v2
Expand Down
37 changes: 18 additions & 19 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,31 @@ jobs:
- ubuntu-latest
- windows-latest
python-version:
- "3.6" # Earliest version supported by message_ix
- "3.7" # Earliest version supported by message_ix
- "3.8"
- "3.9" # Latest release / latest supported by message_ix

# For development versions of Python, compiled binary wheels are not
# available for some dependencies, e.g. llvmlite, numba, numpy, and/or
# pandas. Compiling these on the job runner requires a more elaborate
# build environment, currently out of scope for the message_ix project.
- "3.9"
- "3.10" # Latest release / latest supported by message_ix

# For freshy released or development versions of Python, compiled
# binary wheels are not available for some dependencies, e.g. llvmlite,
# numba, numpy, and/or pandas. Compiling these on the job runner
# requires a more elaborate build environment, currently out of scope
# for the message_ix project.
# - "3.10.0-alpha.1" # Development version

exclude:
# JPype1 (for ixmp) binary wheels are not available for this combination
- os: windows-latest
python-version: "3.10"

fail-fast: false

runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}-py${{ matrix.python-version }}

steps:
- name: Cancel previous runs that have not completed
uses: styfle/cancel-workflow-action@0.7.0
uses: styfle/cancel-workflow-action@0.9.1
with:
access_token: ${{ github.token }}

Expand Down Expand Up @@ -73,17 +80,9 @@ jobs:
run: echo "RETICULATE_PYTHON=$pythonLocation" >> $GITHUB_ENV
shell: bash

- uses: r-lib/actions/setup-r@master
- uses: r-lib/actions/setup-r@v2
id: setup-r

- name: Use OpenJDK 14 (macOS only)
# Using the default OpenJDK 1.8 on the macos-latest runner produces
# "Abort trap: 6" when JPype1 starts the JVM
if: ${{ startsWith(matrix.os, 'macos') }}
uses: actions/setup-java@v1
with:
java-version: '14'

- name: Cache GAMS installer, Python packages, and R packages
uses: actions/cache@v2
with:
Expand Down Expand Up @@ -144,6 +143,6 @@ jobs:
run: make --directory=doc html

- name: Upload test coverage to Codecov.io
uses: codecov/codecov-action@v1.2.1
uses: codecov/codecov-action@v2
Copy link
Member Author

@khaeru khaeru Feb 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LauWien the discrepancy might also be due to this update to a newer version of the Codecov action.

Compare

The difference of 47 lines corresponds to .testing.nightly, so I think this might resolve itself at the next nightly test run.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, I'll then merge!

with:
root_dir: message_ix
directory: message_ix
10 changes: 6 additions & 4 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
.. Next release
.. ============
Next release
============

.. All changes
.. -----------
All changes
-----------

- Allow setting the “model_dir” and “solve_options” options for :class:`.GAMSModel` (and subclasses :class:`.MESSAGE`, :class:`.MACRO`, and :class:`.MESSAGE_MACRO`) through the user's ixmp configuration file; expand documentation (:pull:`557`).

.. _v3.4.0:

Expand Down
131 changes: 93 additions & 38 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,50 +93,112 @@ Model classes

These configure the GAMS CPLEX solver (or another solver, if selected); see `the solver documentation <https://www.gams.com/latest/docs/S_CPLEX.html>`_ for possible values.

.. autoclass:: GAMSModel
:members:
:exclude-members: defaults

The :class:`.MESSAGE`, :class:`MACRO`, and :class:`MESSAGE_MACRO` child classes encapsulate the GAMS code for the core MESSAGE (or MACRO) mathematical formulation.

The class receives `model_options` via :meth:`.Scenario.solve`. Some of these are passed on to the parent class :class:`ixmp.model.gams.GAMSModel` (see there for a list); others are handled as described below.

The “model_dir” option may be set in the user's :ref:`ixmp configuration file <ixmp:configuration>` using the key “message model dir”.
If not set, it defaults to “message_ix/model” below the directory where :mod:`message_ix` is installed.

The “solve_options” option may be set in the user's ixmp configuration file using the key “message solve options”.
If not set, it defaults to :data:`.DEFAULT_CPLEX_OPTIONS`.

For example, with the following configuration file:

.. code-block:: yaml

{
"platform": {
"default": "my-platform",
"my-platform": {"backend": "jdbc", "etc": "etc"},
},
"message model dir": "/path/to/custom/gams/source/files",
"message solve options": {"lpmethod": 4},
}

The following are equivalent:

.. code-block:: python

# Model options given explicitly
scen.solve(
model_dir="/path/to/custom/gams/source/files",
solve_options=dict(lpmethod=4),
)

# Model options are read from configuration file
scen.solve()

The following tables list all model options:

.. list-table:: Options in :class:`message_ix.models.GAMSModel` or overridden from :mod:`ixmp`
:widths: 20 40 40
:header-rows: 1

* - Option
- Usage
- Default value
* - **model_dir**
- Path to GAMS source files.
- See above.
* - **model_file**
- Path to GAMS source file.
- ``'{model_dir}/{model_name}_run.gms'``
* - **in_file**
- Path to write GDX input file.
- ``'{model_dir}/data/MsgData_{case}.gdx'``
* - **out_file**
- Path to read GDX output file.
- ``'{model_dir}/output/MsgOutput_{case}.gdx'``
* - **solve_args**
- Arguments passed directly to GAMS.
- .. code-block::

[
'--in="{in_file}"',
'--out="{out_file}"',
'--iter="{model_dir}/output/MsgIterationReport_{case}.gdx"'
]
* - **solve_options**
- Options for the GAMS LP solver.
- :data:`.DEFAULT_CPLEX_OPTIONS`

.. list-table:: Option defaults inherited from :class:`ixmp.model.gams.GAMSModel`
:widths: 20 80
:header-rows: 1

* - Option
- Default value
* - **case**
- ``'{scenario.model}_{scenario.scenario}'``
* - **gams_args**
- ``['LogOption=4']``
* - **check_solution**
- :obj:`True`
* - **comment**
- :obj:`None`
* - **equ_list**
- :obj:`None`
* - **var_list**
- :obj:`None`

.. autoclass:: MESSAGE
:members: initialize
:exclude-members: defaults
:show-inheritance:

The MESSAGE Python class encapsulates the GAMS code for the core MESSAGE mathematical formulation.
The *model_options* arguments are received from :meth:`.Scenario.solve`, and—except for *solve_options*—are passed on to the parent class :class:`~ixmp.model.gams.GAMSModel`; see there for a full list of options.

.. autoattribute:: name

.. autoattribute:: defaults
:annotation: = dict(...)

The paths to MESSAGE GAMS source files use the ``MODEL_PATH`` configuration setting.
``MODEL_PATH``, in turn, defaults to "message_ix/model" inside the directory where :mod:`message_ix` is installed.

================== ===
Key Value
================== ===
MESSAGE defaults
----------------------
**model_file** ``'{MODEL_PATH}/{model_name}_run.gms'``
**in_file** ``'{MODEL_PATH}/data/MsgData_{case}.gdx'``
**out_file** ``'{MODEL_PATH}/output/MsgOutput_{case}.gdx'``
**solve_args** ``['--in="{in_file}"', '--out="{out_file}"', '--iter="{MODEL_PATH}/output/MsgIterationReport_{case}.gdx"']``
------------------ ---
Inherited from :class:`~ixmp.model.gams.GAMSModel`
----------------------
**case** ``'{scenario.model}_{scenario.scenario}'``
**gams_args** ``['LogOption=4']``
**check_solution** :obj:`True`
**comment** :obj:`None`
**equ_list** :obj:`None`
**var_list** :obj:`None`
================== ===


.. autoclass:: MACRO
:members:
:show-inheritance:

.. autoattribute:: name


.. autoclass:: MESSAGE_MACRO
:members:
:show-inheritance:
Expand All @@ -158,11 +220,6 @@ Model classes

.. autoattribute:: name

.. autoclass:: GAMSModel
:members:
:exclude-members: defaults
:show-inheritance:

.. autodata:: MESSAGE_ITEMS
:annotation: = dict(…)

Expand All @@ -171,7 +228,6 @@ Model classes

.. seealso:: :meth:`.MESSAGE.initialize`, :data:`.MACRO_ITEMS`


.. currentmodule:: message_ix.macro

.. autodata:: MACRO_ITEMS
Expand All @@ -182,7 +238,6 @@ Model classes
.. seealso:: :data:`.MESSAGE_ITEMS`



.. _utils:

Utility methods
Expand Down
24 changes: 12 additions & 12 deletions doc/time.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,33 +58,33 @@ Duration of sub-annual time slices
The duration of each sub-annual time slice should be defined relative to the whole year, with a value between 0 and 1, using parameter ``duration_time``.
For example, in a model with four seasons with the same length, ``duration_time`` of each season will be 0.25.
Please note that the duration of time slices does not need to be equal to each other.
This information is needed to calculate capacity of a technology that is active in different time slices.
This information is needed to calculate capacity of a technology that is active in different time slices.
Time slices can be represented at different temporal levels, using sets ``lvl_temporal`` and ``map_temporal_hierarchy``.
This helps introducing a flexible temporal resolution, e.g., by representing some technologies at finer time resolution while others at ``year``.
When there are more than one temporal levels, e.g., "year", "season", "month", "day", etc., ``duration_time`` is defined for time slices at each **temporal level** separately.
The sum of ``duration_time`` of time slices at each temporal level must be equal to 1.
For example, in a model with 4 time slices as "season" and 10 time slices as "day" under each "season", ``duration_time`` of each "season" and "day" can be specified as 0.25 and 0.025, respectively.

By default, the unit of ``ACT`` is treated per year in the GAMS formulation for different time slices. This means values reported
in time slice "year" and "month" both have the same unit (e.g., GWa). However, the user can report the values across parameters
and variables with different units relative to the length of the full year. For example, the user can report ``ACT`` in units of
"GWa" and "GWh" for time slices of "year" and "hour", respectively, in the same model. To activate this feature, the parent time slice
for which the relative units are desired should be specified by set ``time_relative``. This will ensure that parameter ``duration_time_rel``
is effective. Otherwise, this parameter is filled by value of 1, meaning that the units will be treated uniformly across
different sub-annual time slices.
By default, the unit of ``ACT`` is treated per year in the GAMS formulation for different time slices.
This means values reported in time slice "year" and "month" both have the same unit (e.g., GWa).
However, the user can report the values across parameters and variables with different units relative to the length of the full year.
For example, the user can report ``ACT`` in units of "GWa" and "GWh" for time slices of "year" and "hour", respectively, in the same model.
To activate this feature, the parent time slice for which the relative units are desired should be specified by set ``time_relative``.
This will ensure that parameter ``duration_time_rel`` is effective.
Otherwise, this parameter is filled by value of 1, meaning that the units will be treated uniformly across different sub-annual time slices.

Discounting
===========

The ``interest_rate`` in |MESSAGEix| is defined for a period of one year, therefore, for periods of more than a year, the discounting is performed in a cumulative manner.
The ``interest_rate`` in |MESSAGEix| is defined for a period of one year, therefore, for periods of more than a year, the discounting is performed in a cumulative manner.

Example 5
Using the same setup as Example 2:

- Discounting for the element ``1010`` involves discounting for years ``1001``, ``1002``, ... , ``1010``.
- Using the standard PV formula, we have that, for the year ``1001`` the discount factor would be :math:`(1 + interest_rate)^(1000 - 1001)`, for the year ``1002`` the discount factor would be :math:`(1 + interest_rate)^(1000 - 1002)`, and so on.
- Therefore, the period discount factor for the element ``1010`` is :math:`df_1010 = (1 + interest_rate)^(1000 - 1001) + (1 + interest_rate)^(1000 - 1002) + ... + (1 + interest_rate)^(1000 - 1010)`
- Analogously, the period discount factor for the element ``1020`` is :math:`df_1020 = (1 + interest_rate)^(1000 - 1011) + (1 + interest_rate)^(1000 - 1012) + ... + (1 + interest_rate)^(1000 - 1020)`
- So, if we have a cost of ``K_1010`` for the element ``1010``, its discounted value would be ``df_1010 * K_1010``, which means, all the years in element ``1010`` have a representative cost of ``K_1010`` that is discounted up to the initial ``year`` of the setup, namely, the year ``1000``.
In practice, since the representative year of a period is always its final year, the actual calculation of the period discount factor within the model is performed backwards, i.e., starting from the final year of the period until the initial year.

In practice, since the representative year of a period is always its final year, the actual calculation of the period discount factor within the model is performed backwards, i.e., starting from the final year of the period until the initial year.
1 change: 1 addition & 0 deletions message_ix/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

# Register configuration keys with ixmp core and set default
config.register("message model dir", Path, Path(__file__).parent / "model")
config.register("message solve options", dict)

# Register models with ixmp core
MODELS["MACRO"] = MACRO
Expand Down
Loading