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

Record message_ix version in scenario data & GDX #765

Merged
merged 23 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8e7251c
Set GAMSModel.record_version_packages
khaeru Dec 11, 2023
9eeee8e
Pass ixmp_version through GAMS to output GDX
khaeru Dec 11, 2023
19e4e72
Address Sphinx nitpick
khaeru Dec 11, 2023
f9af772
Test ixmp_version is present in GDX I/O files
khaeru Dec 11, 2023
4929d13
Avoid leaking config state from test_copy_model()
khaeru Dec 11, 2023
523726c
Add "tutorial" job in "pytest" CI workflow
khaeru Dec 11, 2023
b27af9a
Quiet traitlets logging from test_tutorial()
khaeru Dec 12, 2023
64eb377
Set PYDEVD_DISABLE_FILE_VALIDATION=1 in tutorial tests
khaeru Dec 12, 2023
77378e3
Update deprecated genno.Key usage in .util.tutorial
khaeru Dec 12, 2023
0e4d6a0
Make some flaky marks cross-platform
khaeru Dec 12, 2023
015a7ca
Improve test_new_capacity_up()
khaeru Dec 12, 2023
d3fdbe6
Ensure unique scenario names in test_feature_bound_activity_shares
khaeru Dec 12, 2023
4ff7258
Isolate test_{integration,macro} tests from parallel runs
khaeru Dec 12, 2023
014dcfb
Support and run CI on Python 3.12
khaeru Jan 10, 2024
23d740e
Update copyright year to 2024
khaeru Jan 10, 2024
867d2f7
Support unique scenario name from make_westeros()
khaeru Jan 10, 2024
5bd90ee
Use distinct name in test_soft_constraint
khaeru Jan 10, 2024
f8a6952
Use distinct name in test_add_model_data()
khaeru Jan 10, 2024
33e95fd
Use distinct names in test_feature_bound_activity_shares
khaeru Jan 10, 2024
309424f
Use distinct name in dantzig_reporter fixture
khaeru Jan 10, 2024
4e10bef
Mark test_multi_db_run as flaky on all platforms
khaeru Jan 10, 2024
e677377
Don't re-use scenario name in .macro.calibrate()
khaeru Jan 10, 2024
134b1cb
Add #767 to release notes, docs
khaeru Jan 10, 2024
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
86 changes: 74 additions & 12 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ jobs:
- "3.8" # Earliest version supported by message_ix
- "3.9"
- "3.10"
- "3.11" # Latest version supported by message_ix
# - "3.12" # Pending JPype support; see iiasa/ixmp#501
- "3.11"
- "3.12" # Latest version supported by message_ix

# Below this comment are newly released or development versions of
# Python. For these versions, binary wheels are not available for some
Expand Down Expand Up @@ -62,6 +62,72 @@ jobs:
cache: pip
cache-dependency-path: "**/pyproject.toml"

- uses: ts-graphviz/[email protected]
with:
macos-skip-brew-update: true

- name: Cache GAMS installer and R packages
uses: actions/cache@v3
with:
path: |
gams
key: ${{ matrix.os }}-gams${{ env.GAMS_VERSION }}
restore-keys: |
${{ matrix.os }}-

- uses: iiasa/actions/setup-gams@main
with:
version: ${{ env.GAMS_VERSION }}
license: ${{ secrets.GAMS_LICENSE }}

- name: Install Python package and dependencies
# By default, the below installs ixmp from the main branch. To run against
# other code, e.g. other branches for open PRs), temporarily edit as
# appropriate. DO NOT merge such changes to `main`.
run: |
pip install --upgrade "ixmp @ git+https://github.com/iiasa/ixmp.git@main"
pip install .[tests]

- name: Run test suite using pytest
env:
# For test_cli.test_dl; see code in message_ix.cli.dl
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pytest message_ix \
-m "not nightly and not tutorial" \
-rA --verbose --color=yes --durations=20 \
--cov-report=xml \
--numprocesses=auto --dist=loadgroup
shell: bash

- name: Upload test coverage to Codecov.io
uses: codecov/codecov-action@v3

tutorials:
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
- windows-latest

fail-fast: false

runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} tutorials

steps:
- name: Check out message_ix
uses: actions/checkout@v3
with:
fetch-depth: ${{ env.depth }}

- uses: actions/setup-python@v4
with:
python-version: "3.11"
cache: pip
cache-dependency-path: "**/pyproject.toml"

- name: Set RETICULATE_PYTHON
# Use the environment variable set by the setup-python action, above.
run: echo "RETICULATE_PYTHON=$pythonLocation" >> $GITHUB_ENV
Expand All @@ -74,16 +140,16 @@ jobs:
- uses: r-lib/actions/setup-r@v2
id: setup-r

- name: Cache GAMS installer, Python packages, and R packages
- name: Cache GAMS installer and R packages
uses: actions/cache@v3
with:
path: |
gams
${{ env.R_LIBS_USER }}
key: ${{ matrix.os }}-gams${{ env.GAMS_VERSION }}-R${{ steps.setup-r.outputs.installed-r-version }}
key: tutorial-${{ matrix.os }}-gams${{ env.GAMS_VERSION }}-R${{ steps.setup-r.outputs.installed-r-version }}
restore-keys: |
${{ matrix.os }}-gams${{ env.GAMS_VERSION }}-
${{ matrix.os }}-
tutorial-${{ matrix.os }}-gams${{ env.GAMS_VERSION }}-
tutorial-${{ matrix.os }}-

- uses: iiasa/actions/setup-gams@main
with:
Expand All @@ -110,16 +176,12 @@ jobs:
shell: Rscript {0}

- name: Run test suite using pytest
env:
# For test_cli.test_dl; see code in message_ix.cli.dl
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Work around iiasa/ixmp#411
run: |
pytest message_ix \
-m "not nightly" \
-m "tutorial" \
-rA --verbose --color=yes --durations=20 \
--cov-report=xml \
--numprocesses=auto --dist=loadgroup || ( [ $? -eq 127 -a "${{ runner.os }}" = "Windows" ] && echo "Spurious exit code 127 from pytest" && exit 0)
--numprocesses=auto --dist=loadgroup
shell: bash

- name: Upload test coverage to Codecov.io
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Other forms of documentation:

## License

Copyright © 2018–2023 IIASA Energy, Climate, and Environment (ECE) Program
Copyright © 2018–2024 IIASA Energy, Climate, and Environment (ECE) Program

The MESSAGEix framework is licensed under the Apache License, Version 2.0 (the "License"); you may not use the files in this repository except in compliance with the License. You may obtain a copy of the License in [`LICENSE`](LICENSE) or at <http://www.apache.org/licenses/LICENSE-2.0>.

Expand Down
10 changes: 6 additions & 4 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ Migration notes
---------------
Update code that imports from the following modules:

- :py:`message_ix.reporting` → use :py:`message_ix.report`.
- :py:`message_ix.reporting.computations` → use :py:`message_ix.report.operator`.
- :py:`message_ix.reporting` → use :mod:`message_ix.report`.
- :py:`message_ix.reporting.computations` → use :mod:`message_ix.report.operator`.

Code that imports from the old locations will continue to work, but will raise :class:`DeprecationWarning`.

All changes
-----------

- Add the :doc:`LPdiag tool <tools/lpdiag>` to diagnose and analyze numerical issues in linear programming (LP) problems stored in MPS-format files (:pull:`704`).
- Drop support for Python 3.7, which `reached end-of-life on 2023-06-27 <https://peps.python.org/pep-0537/#lifespan>`__ (:pull:`738`).
- :mod:`message_ix` is tested and compatible with `Python 3.12 <https://www.python.org/downloads/release/python-3120/>`__ (:pull:`767`).
Support for Python 3.7, which `reached end-of-life on 2023-06-27 <https://peps.python.org/pep-0537/#lifespan>`__, is dropped (:pull:`738`).
:mod:`message_ix` now requires Python version 3.8 or greater.
- Rename :mod:`message_ix.report` (:pull:`761`).
- Add the :doc:`LPdiag tool <tools/lp_diag>` to diagnose and analyze numerical issues in linear programming (LP) problems stored in MPS-format files (:pull:`704`).
- GDX files generated by :class:`.GAMSModel` (thus :class:`.MESSAGE`, :class:`.MACRO`, and :class:`.MESSAGE_MACRO`) will contain an ``ixmp_version`` set with information on the versions of :mod:`ixmp` and :mod:`message_ix` that generated the file (:issue:`747`, :pull:`767`).
- New reporting operator :func:`.model_periods` and automatic keys ``y::model`` and ``y0`` (:pull:`738`).
- Improve readability of LaTeX equations in docs (:pull:`721`).
- Replace :py:`MESSAGE_ITEMS` and :py:`MACRO_ITEMS` with :attr:`.MESSAGE.items` and :attr:`.MACRO.items`, respectively (:pull:`761`).
Expand Down
18 changes: 12 additions & 6 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ Model classes
# Model options are read from configuration file
scen.solve()

GDX input and output files generated using this class will contain a 2-dimensional set named ``ixmp_version``, wherein the first element of each member is a package name from the :py:`record_version_packages` parameter, and the second is its version according to :func:`importlib.metadata.version`.
If the package is not installed, the string "(not installed)" is stored.

The following tables list all model options:

.. list-table:: Options in :class:`message_ix.models.GAMSModel` or overridden from :mod:`ixmp`
Expand All @@ -153,16 +156,16 @@ Model classes
- See above.
* - **model_file**
- Path to GAMS source file.
- ``'{model_dir}/{model_name}_run.gms'``
- :py:`"{model_dir}/{model_name}_run.gms"`
* - **in_file**
- Path to write GDX input file.
- ``'{model_dir}/data/MsgData_{case}.gdx'``
- :py:`"{model_dir}/data/MsgData_{case}.gdx"`
* - **out_file**
- Path to read GDX output file.
- ``'{model_dir}/output/MsgOutput_{case}.gdx'``
- :py:`"{model_dir}/output/MsgOutput_{case}.gdx"`
* - **solve_args**
- Arguments passed directly to GAMS.
- .. code-block::
- .. code-block:: python

[
'--in="{in_file}"',
Expand All @@ -172,6 +175,9 @@ Model classes
* - **solve_options**
- Options for the GAMS LP solver.
- :data:`.DEFAULT_CPLEX_OPTIONS`
* - **record_version_packages**
- Python package versions to record.
- :py:`["message_ix", "ixmp"]`

.. list-table:: Option defaults inherited from :class:`ixmp.model.gams.GAMSModel`
:widths: 20 80
Expand All @@ -180,9 +186,9 @@ Model classes
* - Option
- Default value
* - **case**
- ``'{scenario.model}_{scenario.scenario}'``
- :py:`"{scenario.model}_{scenario.scenario}"`
* - **gams_args**
- ``['LogOption=4']``
- :py:`["LogOption=4"]`
* - **check_solution**
- :obj:`True`
* - **comment**
Expand Down
3 changes: 2 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# -- Project information ---------------------------------------------------------------

project = "MESSAGEix"
copyright = "2018–2023, IIASA Energy, Climate, and Environment (ECE) Program"
copyright = "2018–2024, IIASA Energy, Climate, and Environment (ECE) Program"
author = "MESSAGEix Developers"

# The major project version, used as the replacement for |version|.
Expand Down Expand Up @@ -159,6 +159,7 @@ def local_inv(name: str, *parts: str) -> Optional[str]:
"message_doc": ("https://docs.messageix.org/projects/global/en/latest/", None),
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
"pint": ("https://pint.readthedocs.io/en/stable/", None),
"plotnine": ("https://plotnine.readthedocs.io/en/stable", None),
"pyam": ("https://pyam-iamc.readthedocs.io/en/stable/", None),
"python": ("https://docs.python.org/3/", None),
"sphinx": ("https://www.sphinx-doc.org/en/master/", None),
Expand Down
2 changes: 1 addition & 1 deletion message_ix/macro.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ def calibrate(s, check_convergence: bool = True, **kwargs):

# Test to make sure number of iterations is 1
if check_convergence:
test = s.clone(s.model, "test to confirm MACRO converges")
test = s.clone(scenario=f"{s.scenario} test to confirm MACRO converges")
kwargs.setdefault("gams_args", gams_args)
test.solve(model="MESSAGE-MACRO", var_list=["N_ITER"], **kwargs)
test.set_as_default()
Expand Down
6 changes: 6 additions & 0 deletions message_ix/model/MESSAGE/data_load.gms
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ $LOAD balance_equality, time_relative
$LOAD shares
$LOAD addon, type_addon, cat_addon, map_tec_addon
$LOAD storage_tec, level_storage, map_tec_storage

* Version information; conditional load to allow older GDX files
$ifthen gdxSetType ixmp_version
$load ixmp_version
$endif

$GDXIN

Execute_load '%in%'
Expand Down
2 changes: 2 additions & 0 deletions message_ix/model/MESSAGE/sets_maps_def.gms
Original file line number Diff line number Diff line change
Expand Up @@ -486,3 +486,5 @@ Sets
is_fixed_activity(node,tec,vintage,year_all,mode,time) flag whether activity variable is fixed
is_fixed_land(node,land_scenario,year_all) flag whether land level is fixed
;

Set ixmp_version(*,*) "Versions of Python packages used to generate this model";
4 changes: 3 additions & 1 deletion message_ix/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class Item:
name: str

#: Type of the item, for instance :attr:`ItemType.PAR <ixmp.backend.ItemType.PAR>`.
type: ItemType
type: "ixmp.backend.ItemType"

#: String expression for :attr:`coords` and :attr:`dims`. Split on spaces and parsed
#: using :data:`DIMS` so that, for instance, "nl yv" results in entries for
Expand Down Expand Up @@ -167,6 +167,8 @@ class GAMSModel(ixmp.model.gams.GAMSModel):
# Disable the feature to put input/output GDX files, list files, etc. in a
# temporary directory
"use_temp_dir": False,
# Record versions of message_ix and ixmp in GDX I/O files
"record_version_packages": ("message_ix", "ixmp"),
},
ixmp.model.gams.GAMSModel.defaults,
)
Expand Down
2 changes: 1 addition & 1 deletion message_ix/report/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def from_scenario(cls, scenario, **kwargs) -> "Reporter":

Returns
-------
message_ix.Reporter
.Reporter
A reporter for `scenario`.
"""
solved = scenario.has_solution()
Expand Down
24 changes: 21 additions & 3 deletions message_ix/testing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import io
from itertools import product
from typing import List, Union
from typing import TYPE_CHECKING, List, Optional, Union

import numpy as np
import pandas as pd
from ixmp import IAMC_IDX

from message_ix import Scenario, make_df

if TYPE_CHECKING:
import pytest


SCENARIO = {
"austria": dict(model="Austrian energy model", scenario="baseline"),
"dantzig": {"model": "Canning problem (MESSAGE scheme)", "scenario": "standard"},
Expand Down Expand Up @@ -426,7 +430,13 @@ def make_dantzig(mp, solve=False, multi_year=False, **solve_opts):


def make_westeros(
mp, emissions=False, solve=False, quiet=True, model_horizon=[700, 710, 720]
mp,
emissions=False,
solve=False,
quiet=True,
model_horizon=[700, 710, 720],
*,
request: Optional["pytest.FixtureRequest"] = None,
):
"""Return an :class:`message_ix.Scenario` for the Westeros model.

Expand All @@ -443,7 +453,15 @@ def make_westeros(
"""
mp.add_unit("USD/kW")
mp.add_unit("tCO2/kWa")
scen = Scenario(mp, version="new", **SCENARIO["westeros"])

# Scenario identifiers
args = SCENARIO["westeros"].copy()
args.setdefault("version", "new")
if request:
# Use a distinct scenario name for a particular test
args.update(scenario=request.node.name)

scen = Scenario(mp, **args)

# Sets
history = [690]
Expand Down
6 changes: 4 additions & 2 deletions message_ix/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ def ureg():


@pytest.fixture
def dantzig_reporter(message_test_mp, ureg):
scen = message_ix.Scenario(message_test_mp, **SCENARIO["dantzig"])
def dantzig_reporter(request, message_test_mp, ureg):
scen = message_ix.Scenario(message_test_mp, **SCENARIO["dantzig"]).clone(
scenario=request.node.name
)

if not scen.has_solution():
scen.solve(quiet=True)
Expand Down
16 changes: 8 additions & 8 deletions message_ix/tests/data/report-pyam-write.csv
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Model,Scenario,Region,Variable,Unit,1963
Canning problem (MESSAGE scheme),standard,san-diego,Activity|canning_plant|production,,600.0
Canning problem (MESSAGE scheme),standard,san-diego,Activity|transport_from_san-diego|to_chicago,,0.0
Canning problem (MESSAGE scheme),standard,san-diego,Activity|transport_from_san-diego|to_new-york,,325.0
Canning problem (MESSAGE scheme),standard,san-diego,Activity|transport_from_san-diego|to_topeka,,275.0
Canning problem (MESSAGE scheme),standard,seattle,Activity|canning_plant|production,,350.0
Canning problem (MESSAGE scheme),standard,seattle,Activity|transport_from_seattle|to_chicago,,300.0
Canning problem (MESSAGE scheme),standard,seattle,Activity|transport_from_seattle|to_new-york,,0.0
Canning problem (MESSAGE scheme),standard,seattle,Activity|transport_from_seattle|to_topeka,,0.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,san-diego,Activity|canning_plant|production,,600.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,san-diego,Activity|transport_from_san-diego|to_chicago,,0.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,san-diego,Activity|transport_from_san-diego|to_new-york,,325.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,san-diego,Activity|transport_from_san-diego|to_topeka,,275.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,seattle,Activity|canning_plant|production,,350.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,seattle,Activity|transport_from_seattle|to_chicago,,300.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,seattle,Activity|transport_from_seattle|to_new-york,,0.0
Canning problem (MESSAGE scheme),test_reporter_as_pyam,seattle,Activity|transport_from_seattle|to_topeka,,0.0
8 changes: 7 additions & 1 deletion message_ix/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
from message_ix import config


def test_copy_model(message_ix_cli, tmp_path, tmp_env):
def test_copy_model(monkeypatch, message_ix_cli, tmp_path, tmp_env):
# Use Pytest monkeypatch fixture; this ensures the original value is restored at the
# end of the test
monkeypatch.setattr(
config.values, "message_model_dir", config.get("message model dir")
)

r = message_ix_cli("copy-model", str(tmp_path))
assert r.exit_code == 0

Expand Down
Loading