Skip to content
Draft
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
1 change: 1 addition & 0 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Next release
All changes
-----------

- Add a tool for :doc:`tools/check` (:pull:`506`).
- Adjust test suite for pyam v1.1.0 compatibility (:pull:`499`).
- Add Westeros :doc:`tutorial <tutorials>` on historical parameters (:pull:`478`).
- Update reference for activity and capacity soft constraints (:pull:`474`).
Expand Down
2 changes: 0 additions & 2 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ Then, continue with the:
tutorials




.. _core:

Mathematical specification
Expand Down
27 changes: 27 additions & 0 deletions doc/tools/check.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Pre-solve checks
****************

.. currentmodule:: message_ix.tools

.. autofunction:: check

.. currentmodule:: message_ix.tools._check

.. _checks-summary:

.. automodule:: message_ix.tools._check
:members: gaps_input, gaps_tl, tl_integer

.. autoclass:: Result

Individual checks
=================

Gaps in certain parameters (:func:`gaps_*` check functions) can cause the GAMS implementation to treat that specific technology to be disabled in one period, between other periods where it *is* enabled.
This may prevent solution of the model.

.. autosummary::

gaps_input
gaps_tl
tl_integer
13 changes: 8 additions & 5 deletions message_ix/reporting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,14 @@ def from_scenario(cls, scenario, **kwargs):
solved = scenario.has_solution()

if not solved:
log.warning(
f'Scenario "{scenario.model}/{scenario.scenario}" has no solution'
)
log.warning("Some reporting may not function as expected")
fail_action = logging.DEBUG
if kwargs.pop("solved", True):
log.warning(
f'Scenario "{scenario.model}/{scenario.scenario}" has no solution'
)
log.warning("Some reporting may not function as expected")
fail_action = logging.DEBUG
else:
fail_action = logging.NOTSET
else:
fail_action = "raise"

Expand Down
3 changes: 2 additions & 1 deletion message_ix/tests/test_tutorials.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys
from shutil import copyfile
from typing import List, Tuple

import numpy as np
import pytest
Expand All @@ -17,7 +18,7 @@
# 3. Dictionary with extra keyword arguments to run_notebook().

# FIXME check objective function of the rest of tutorials.
tutorials = [
tutorials: List[Tuple] = [
# IPython kernel
(
("westeros", "westeros_baseline"),
Expand Down
96 changes: 96 additions & 0 deletions message_ix/tests/tools/test_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import pytest

from message_ix import Scenario, make_df
from message_ix.testing import make_dantzig, make_westeros
from message_ix.tools import check


def test_check_dantzig(test_mp):
scen = make_dantzig(test_mp)

# Checks all True
results = check(scen)
assert results[0]


@pytest.fixture
def westeros(test_mp):
yield make_westeros(test_mp)


# Minimal config to make Westeros reportable
WESTEROS_CONFIG = {"units": {"replace": {"-": ""}}}


def test_gaps_input(westeros):
# Delete one value
to_delete = make_df(
"input",
node_loc="Westeros",
technology="bulb",
year_vtg=690,
year_act=710,
mode="standard",
node_origin="Westeros",
commodity="electricity",
level="final",
time="year",
time_origin="year",
).dropna(axis=1)
with westeros.transact():
westeros.remove_par("input", to_delete)

# Checks fail
results = check(westeros, config=WESTEROS_CONFIG)
assert not results[0]


def test_check_tl_integer(westeros):
# Change one value
tl = "technical_lifetime"
with westeros.transact():
westeros.add_par(
tl,
make_df(
tl,
node_loc="Westeros",
technology="bulb",
year_vtg=700,
value=1.1,
unit="y",
),
)

# Checks fail
results = check(westeros, config=WESTEROS_CONFIG)
assert not results[0]

assert """FAIL Non-integer values for ``technical_lifetime``:
See https://github.com/iiasa/message_ix/issues/503.
- 1.1 at indices: nl=Westeros t=bulb yv=700""" in map(
str, results
)


@pytest.mark.parametrize(
"url, config",
[
# ("ixmp://platform/model/scenario#version", dict()),
],
)
def test_check_existing(url, config):
"""Check existing scenarios.

For local use only: extend the list of parameters, above, but do not commit
additions to ``main``.
"""
# import pint
# from iam_units import registry
#
# pint.set_application_registry(registry)

scen, mp = Scenario.from_url(url)
results = check(scen, config=config)

# Checks all pass
assert results[0], "\n".join(map(str, results))
5 changes: 5 additions & 0 deletions message_ix/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from ._check import check

__all__ = [
"check",
]
Loading