Skip to content

Commit

Permalink
Merge pull request #450 from iiasa/issue/449
Browse files Browse the repository at this point in the history
Adjust typing for mypy 0.960 & other minor fixes
  • Loading branch information
khaeru committed May 26, 2022
2 parents 8f8c74b + de3df62 commit e795853
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 27 deletions.
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
-----------

- Add :meth:`.enforce` to the :class:`~.base.Model` API for enforcing structure/data consistency before :meth:`.Model.solve` (:pull:`450`).

.. _v3.5.0:

Expand Down
6 changes: 3 additions & 3 deletions ixmp/core/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,14 @@ def add_set(

# Combine iterators to tuples. If the lengths are mismatched, the sentinel
# value 'False' is filled in
to_add = list(zip_longest(keys, comments, fillvalue=False))
to_add = list(zip_longest(keys, comments, fillvalue=(False,)))

# Check processed arguments
for e, c in to_add:
# Check for sentinel values
if e is False:
if e == (False,):
raise ValueError(f"Comment {repr(c)} without matching key")
elif c is False:
elif c == (False,):
raise ValueError(f"Key {repr(e)} without matching comment")
elif len(idx_names) and len(idx_names) != len(e):
raise ValueError(
Expand Down
55 changes: 39 additions & 16 deletions ixmp/model/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ def clean_path(cls, value: str) -> str:
chars = r'<>"/\|?*' + (":" if os.name == "nt" else "")
return re.sub("[{}]+".format(re.escape(chars)), "_", value)

@staticmethod
def enforce(scenario):
"""Enforce data consistency in `scenario`.
Optional. Implementations of :meth:`enforce`:
- **should** modify the contents of sets and parameters so that `scenario`
contains structure and data that is consistent with the underlying model.
- **must not** add or remove sets or parameters; for that, use
:meth:`initiatize`.
:meth:`enforce` is always called by :meth:`run` before the model is run or
solved; it **may** be called manually at other times.
Parameters
----------
scenario : .Scenario
Object on which to enforce data consistency.
"""

@classmethod
def initialize(cls, scenario):
"""Set up *scenario* with required items.
Expand All @@ -42,8 +62,8 @@ def initialize(cls, scenario):
- **may** add sets, set elements, and/or parameter values.
- **may** accept any number of keyword arguments to control behaviour.
- **must not** modify existing parameter data in *scenario*, either by
deleting or overwriting values.
- **must not** modify existing parameter data in *scenario*, either by deleting
or overwriting values; for that, use :meth:`enforce`.
Parameters
----------
Expand All @@ -60,28 +80,28 @@ def initialize(cls, scenario):
def initialize_items(cls, scenario, items):
"""Helper for :meth:`initialize`.
All of the `items` are added to `scenario`. Existing items are not
modified. Errors are logged if the description in `items` conflicts
with the index set(s) and/or index name(s) of existing items.
All of the `items` are added to `scenario`. Existing items are not modified.
Errors are logged if the description in `items` conflicts with the index set(s)
and/or index name(s) of existing items.
initialize_items may perform one commit. `scenario` is in the same
state (checked in, or checked out) after initialize_items is complete.
initialize_items may perform one commit. `scenario` is in the same state
(checked in, or checked out) after initialize_items is complete.
Parameters
----------
scenario : .Scenario
Object to initialize.
items : dict of (str -> dict)
Each key is the name of an ixmp item (set, parameter, equation, or
variable) to initialize. Each dict **must** have the key 'ix_type';
one of 'set', 'par', 'equ', or 'var'; any other entries are keyword
arguments to the methods :meth:`.init_set` etc.
variable) to initialize. Each dict **must** have the key 'ix_type'; one of
'set', 'par', 'equ', or 'var'; any other entries are keyword arguments to
the methods :meth:`.init_set` etc.
Raises
------
ValueError
if `scenario` has a solution, i.e. :meth:`~.Scenario.has_solution`
is :obj:`True`.
if `scenario` has a solution, i.e. :meth:`~.Scenario.has_solution` is
:obj:`True`.
See also
--------
Expand Down Expand Up @@ -135,10 +155,9 @@ def initialize_items(cls, scenario, items):
try:
checkout = maybe_check_out(scenario, checkout)
except ValueError as exc: # pragma: no cover
# The Scenario has a solution. This indicates an inconsistent
# situation: the Scenario lacks the item *name*, but somehow it
# was successfully solved without it, and the solution stored.
# Can't proceed further.
# The Scenario has a solution. This indicates an inconsistent situation:
# the Scenario lacks the item *name*, but somehow it was successfully
# solved without it, and the solution stored. Can't proceed further.
log.error(str(exc))
return

Expand All @@ -161,6 +180,10 @@ def initialize_items(cls, scenario, items):
def run(self, scenario):
"""Execute the model.
Implementations of :meth:`run`:
- **must** call :meth:`enforce`.
Parameters
----------
scenario : .Scenario
Expand Down
17 changes: 13 additions & 4 deletions ixmp/reporting/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@
from genno.core.computer import Computer, Key

from ixmp.core.scenario import Scenario
from ixmp.reporting import computations
from ixmp.reporting.util import RENAME_DIMS, keys_for_quantity


class Reporter(Computer):
"""Class for describing and executing computations."""

# Append ixmp.reporting.computations to the modules in which the Computer will look
# up computations names.
modules = list(Computer.modules) + [computations]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Append ixmp.reporting.computations to the modules in which the Computer will
# look up computations names.
# genno <= 1.11
from ixmp.reporting import computations

if computations not in self.modules:
self.modules.append(computations)

# TODO use this once genno >= 1.12.0 is released
# self.require_compat("ixmp.reporting.computations")

@classmethod
def from_scenario(cls, scenario: Scenario, **kwargs) -> "Reporter":
Expand Down

0 comments on commit e795853

Please sign in to comment.