Skip to content

Commit 3cea59c

Browse files
committed
Include optimization parameter basis (#79)
* Make Column generic enough for multiple parents * Introduce optimization.Parameter * Add tests for add_data * Enable remaining parameter tests (#86) * Enable remaining parameter tests * Include optimization parameter api layer (#89) * Bump several dependency versions * Let api/column handle both tables and parameters * Make api-layer tests pass * Include optimization parameter core layer (#90) * Enable parameter core layer and test it * Fix things after rebase * Ensure all intended changes survive the rebase * Adapt data validation function for parameters * Allow tests to pass again
1 parent 469ef7b commit 3cea59c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2011
-258
lines changed

ixmp4/__init__.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
# flake8: noqa
21
import importlib.metadata
32

3+
from ixmp4.core import IndexSet as IndexSet
44
from ixmp4.core import Model as Model
5+
from ixmp4.core import Parameter as Parameter
56
from ixmp4.core import Platform as Platform
67
from ixmp4.core import Region as Region
78
from ixmp4.core import Run as Run
9+
from ixmp4.core import Scalar as Scalar
810
from ixmp4.core import Scenario as Scenario
11+
from ixmp4.core import Table as Table
912
from ixmp4.core import Unit as Unit
1013
from ixmp4.core import Variable as Variable
11-
from ixmp4.core import IndexSet as IndexSet
12-
from ixmp4.core import Scalar as Scalar
13-
from ixmp4.core import Table as Table
1414
from ixmp4.core.exceptions import InconsistentIamcType as InconsistentIamcType
1515
from ixmp4.core.exceptions import IxmpError as IxmpError
1616
from ixmp4.core.exceptions import NotFound as NotFound

ixmp4/core/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .optimization.indexset import IndexSet as IndexSet
55
from .optimization.scalar import Scalar as Scalar
66
from .optimization.table import Table as Table
7+
from .optimization.parameter import Parameter as Parameter
78
from .platform import Platform as Platform
89
from .region import Region as Region
910
from .run import Run as Run

ixmp4/core/optimization/data.py

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from ..base import BaseFacade
44
from .indexset import IndexSetRepository
5+
from .parameter import ParameterRepository
56
from .scalar import ScalarRepository
67
from .table import TableRepository
78

@@ -11,11 +12,13 @@ class OptimizationData(BaseFacade):
1112
IndexSet, Table, Variable, etc."""
1213

1314
indexsets: IndexSetRepository
15+
parameters: ParameterRepository
1416
scalars: ScalarRepository
1517
tables: TableRepository
1618

1719
def __init__(self, *args, run: Run, **kwargs) -> None:
1820
super().__init__(*args, **kwargs)
1921
self.indexsets = IndexSetRepository(_backend=self.backend, _run=run)
22+
self.parameters = ParameterRepository(_backend=self.backend, _run=run)
2023
self.scalars = ScalarRepository(_backend=self.backend, _run=run)
2124
self.tables = TableRepository(_backend=self.backend, _run=run)

ixmp4/core/optimization/parameter.py

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
from datetime import datetime
2+
from typing import Any, ClassVar, Iterable
3+
4+
import pandas as pd
5+
6+
from ixmp4.core.base import BaseFacade, BaseModelFacade
7+
from ixmp4.data.abstract import Docs as DocsModel
8+
from ixmp4.data.abstract import Parameter as ParameterModel
9+
from ixmp4.data.abstract import Run
10+
from ixmp4.data.abstract.optimization import Column
11+
12+
13+
class Parameter(BaseModelFacade):
14+
_model: ParameterModel
15+
NotFound: ClassVar = ParameterModel.NotFound
16+
NotUnique: ClassVar = ParameterModel.NotUnique
17+
18+
@property
19+
def id(self) -> int:
20+
return self._model.id
21+
22+
@property
23+
def name(self) -> str:
24+
return self._model.name
25+
26+
@property
27+
def run_id(self) -> int:
28+
return self._model.run__id
29+
30+
@property
31+
def data(self) -> dict[str, Any]:
32+
return self._model.data
33+
34+
def add(self, data: dict[str, Any] | pd.DataFrame) -> None:
35+
"""Adds data to an existing Parameter."""
36+
self.backend.optimization.parameters.add_data(
37+
parameter_id=self._model.id, data=data
38+
)
39+
self._model.data = self.backend.optimization.parameters.get(
40+
run_id=self._model.run__id, name=self._model.name
41+
).data
42+
43+
@property
44+
def values(self) -> list:
45+
return self._model.data.get("values", [])
46+
47+
@property
48+
def units(self) -> list:
49+
return self._model.data.get("units", [])
50+
51+
@property
52+
def constrained_to_indexsets(self) -> list[str]:
53+
return [column.indexset.name for column in self._model.columns]
54+
55+
@property
56+
def columns(self) -> list[Column]:
57+
return self._model.columns
58+
59+
@property
60+
def created_at(self) -> datetime | None:
61+
return self._model.created_at
62+
63+
@property
64+
def created_by(self) -> str | None:
65+
return self._model.created_by
66+
67+
@property
68+
def docs(self):
69+
try:
70+
return self.backend.optimization.parameters.docs.get(self.id).description
71+
except DocsModel.NotFound:
72+
return None
73+
74+
@docs.setter
75+
def docs(self, description):
76+
if description is None:
77+
self.backend.optimization.parameters.docs.delete(self.id)
78+
else:
79+
self.backend.optimization.parameters.docs.set(self.id, description)
80+
81+
@docs.deleter
82+
def docs(self):
83+
try:
84+
self.backend.optimization.parameters.docs.delete(self.id)
85+
# TODO: silently failing
86+
except DocsModel.NotFound:
87+
return None
88+
89+
def __str__(self) -> str:
90+
return f"<Parameter {self.id} name={self.name}>"
91+
92+
93+
class ParameterRepository(BaseFacade):
94+
_run: Run
95+
96+
def __init__(self, _run: Run, *args, **kwargs) -> None:
97+
super().__init__(*args, **kwargs)
98+
self._run = _run
99+
100+
def create(
101+
self,
102+
name: str,
103+
constrained_to_indexsets: list[str],
104+
column_names: list[str] | None = None,
105+
) -> Parameter:
106+
model = self.backend.optimization.parameters.create(
107+
name=name,
108+
run_id=self._run.id,
109+
constrained_to_indexsets=constrained_to_indexsets,
110+
column_names=column_names,
111+
)
112+
return Parameter(_backend=self.backend, _model=model)
113+
114+
def get(self, name: str) -> Parameter:
115+
model = self.backend.optimization.parameters.get(run_id=self._run.id, name=name)
116+
return Parameter(_backend=self.backend, _model=model)
117+
118+
def list(self, name: str | None = None) -> Iterable[Parameter]:
119+
parameters = self.backend.optimization.parameters.list(
120+
run_id=self._run.id, name=name
121+
)
122+
return [
123+
Parameter(
124+
_backend=self.backend,
125+
_model=i,
126+
)
127+
for i in parameters
128+
]
129+
130+
def tabulate(self, name: str | None = None) -> pd.DataFrame:
131+
return self.backend.optimization.parameters.tabulate(name=name)

ixmp4/data/abstract/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
This module holds a shared datastructure and interface for normalization
33
between the database and api data models and repositories.
44
"""
5-
# flake8: noqa
65

76
from .base import (
87
BaseMeta,
@@ -32,6 +31,8 @@
3231
from .optimization import (
3332
IndexSet,
3433
IndexSetRepository,
34+
Parameter,
35+
ParameterRepository,
3536
Scalar,
3637
ScalarRepository,
3738
Table,
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .column import Column
22
from .indexset import IndexSet, IndexSetRepository
3+
from .parameter import Parameter, ParameterRepository
34
from .scalar import Scalar, ScalarRepository
45
from .table import Table, TableRepository

ixmp4/data/abstract/optimization/column.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ class Column(base.BaseModel, Protocol):
1414
"""Unique name of the Column."""
1515
dtype: types.String
1616
"""Type of the Column's data."""
17-
table__id: types.Integer
17+
table__id: types.Mapped[int | None]
1818
"""Foreign unique integer id of a Table."""
19+
parameter__id: types.Mapped[int | None]
20+
"""Foreign unique integer id of a Parameter."""
1921
indexset: types.Mapped[IndexSet]
2022
"""Associated IndexSet."""
2123
constrained_to_indexset: types.Integer

0 commit comments

Comments
 (0)