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

Include the GAMS version of the transport tutorial #108

Open
wants to merge 57 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
4229685
Make creation information a mixin
glatterf42 Apr 16, 2024
774b743
Make name column a mixin
glatterf42 Apr 18, 2024
554dbd8
Make optimization columns mixins
glatterf42 Apr 18, 2024
469ef7b
Inherit mixin requirements directly
glatterf42 Apr 18, 2024
3cea59c
Include optimization parameter basis (#79)
glatterf42 Jun 28, 2024
6dac5e9
Fix references to DB filters in docs
glatterf42 Jul 3, 2024
824e2d3
Streamline naming in tests
glatterf42 Jul 3, 2024
a60cda1
Introduce optimization.Variable
glatterf42 Jul 2, 2024
7d98251
Fix references to DB filters in docs
glatterf42 Jul 3, 2024
a9e6bc9
Streamline naming in tests
glatterf42 Jul 3, 2024
e8d350e
Make constrained_to_indexset optional for scalar Variables
glatterf42 Jul 5, 2024
cb3ac58
Enable import of typing.Never on Python 3.10
glatterf42 Jul 5, 2024
08e442b
Introduce optimization.Equation
glatterf42 Jul 3, 2024
26ff318
Fix references to DB filters in docs
glatterf42 Jul 3, 2024
8b8a84a
Streamline naming in tests
glatterf42 Jul 3, 2024
d347040
Validate var/equ data only for non-empty data
glatterf42 Jul 10, 2024
650b83e
TEMPORARY Add all missing db migrations
glatterf42 Jul 10, 2024
4ccd89b
Add transport tutorial
glatterf42 Jul 10, 2024
4832eea
Add required dependencies
glatterf42 Jul 10, 2024
1388079
Remove outdated comments
glatterf42 Jul 10, 2024
97d522b
Rename read_solution and other review suggestions
glatterf42 Jul 11, 2024
225a218
Rename model file
glatterf42 Jul 11, 2024
96b419a
Remove superfluous auxiliary function
glatterf42 Jul 11, 2024
213b20f
Refactor to only passing run for interface simplicity
glatterf42 Jul 11, 2024
154a101
Revert back to parameter.units and values as they are required
glatterf42 Jul 11, 2024
dd4c53c
Remove superfluous variable
glatterf42 Jul 11, 2024
6a507b1
Add tests for transport tutorial
glatterf42 Jul 11, 2024
566da24
Install tutorial dependencies for this PR
glatterf42 Jul 24, 2024
2ab1254
Allow error tolerance to pass test
glatterf42 Jul 24, 2024
c4b940c
Allow linopy version with numpy pin :(
glatterf42 Jul 24, 2024
8257274
Exclude untyped linopy from mypy
glatterf42 Aug 2, 2024
1d3ffaf
Rename transport file for consistency
glatterf42 Aug 2, 2024
56bb603
Shorten util function
glatterf42 Aug 12, 2024
a5f2db5
TEMPORARY Use pandas for updated add_data behaviour
glatterf42 Aug 27, 2024
eda2b11
Introduce equation.remove_data()
glatterf42 Aug 27, 2024
4007a21
Introduce variable.remove_data()
glatterf42 Aug 27, 2024
6969d90
Prefer None over Never for type hints
glatterf42 Aug 28, 2024
b2b02c9
Make run.get_by_id() available in all backends
glatterf42 Aug 28, 2024
93785f3
Introduce run.opt.remove_solution()
glatterf42 Aug 28, 2024
39ba3bc
Remove superfluous check for scalar.unit
glatterf42 Aug 29, 2024
641f30e
Introduce core.runs.clone()
glatterf42 Aug 29, 2024
1d547be
Remove superfluous session.add() for table, indexset
glatterf42 Aug 29, 2024
10be477
Remove superfluous session.add() for parameter
glatterf42 Aug 29, 2024
a35cc10
Remove superfluous session.add() for equation
glatterf42 Aug 29, 2024
d1a1726
Remove superfluous session.add() for variable
glatterf42 Aug 29, 2024
89c5e17
Allow clone of runs without iamc data
glatterf42 Aug 29, 2024
096b02c
Create helper create_default_dantzig_run()
glatterf42 Aug 29, 2024
1e77ad7
Improve linopy tutorial and helper functions
glatterf42 Aug 29, 2024
c86f7be
Add second part of transport tutorial
glatterf42 Aug 29, 2024
56cf60e
Add gamsapi depedency; exclude from mypy
glatterf42 Aug 2, 2024
2c685bf
Fix and test indexset list and tabulate for specific runs
glatterf42 Aug 2, 2024
3c6e9d9
Include gams transport tutorial
glatterf42 Aug 2, 2024
47b5d32
Exclude tutorial outputs from version control
glatterf42 Aug 2, 2024
c3fa962
Fix and test scalar list and tabulate for specific runs
glatterf42 Aug 2, 2024
856f0d7
Add tests for gams transport tutorial
glatterf42 Aug 6, 2024
c955e48
Install gams on GHA runs
glatterf42 Aug 27, 2024
3c3b60b
Add second part of transport tutorial for gams
glatterf42 Aug 29, 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
19 changes: 17 additions & 2 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,27 @@ jobs:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Load cached gams install
id: cached-gams
uses: actions/cache@v4
with:
path: gams
key: gams-${{ runner.os }}
restore-keys: gams-${{ runner.os }}

#------------------------------------------------
# install dependencies if cache does not exist
#------------------------------------------------
- name: Install gams
if: steps.cached-gams.outputs.cache-hit != 'true'
uses: iiasa/actions/setup-gams@main
with:
version: 45.7.0
license: ${{ secrets.GAMS_LICENSE }}

- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root --with dev,server
run: poetry install --no-interaction --no-root --with dev,server,tutorial

- name: Install PyArrow
if: ${{ matrix.with-pyarrow }}
Expand Down Expand Up @@ -165,7 +180,7 @@ jobs:
#------------------------------------------------
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root --with dev,server
run: poetry install --no-interaction --no-root --with dev,server,tutorial

#------------------------
# install root project
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,9 @@ requirements.txt
# misc
PR.md
.DS_STORE

# tutorial/gams
tutorial/transport/*.gdx
tutorial/transport/*.lst
tests/tutorial/transport/*.gdx
tests/tutorial/transport/*.lst
8 changes: 4 additions & 4 deletions ixmp4/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# flake8: noqa
import importlib.metadata

from ixmp4.core import IndexSet as IndexSet
from ixmp4.core import Model as Model
from ixmp4.core import Parameter as Parameter
from ixmp4.core import Platform as Platform
from ixmp4.core import Region as Region
from ixmp4.core import Run as Run
from ixmp4.core import Scalar as Scalar
from ixmp4.core import Scenario as Scenario
from ixmp4.core import Table as Table
from ixmp4.core import Unit as Unit
from ixmp4.core import Variable as Variable
from ixmp4.core import IndexSet as IndexSet
from ixmp4.core import Scalar as Scalar
from ixmp4.core import Table as Table
from ixmp4.core.exceptions import InconsistentIamcType as InconsistentIamcType
from ixmp4.core.exceptions import IxmpError as IxmpError
from ixmp4.core.exceptions import NotFound as NotFound
Expand Down
5 changes: 5 additions & 0 deletions ixmp4/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# flake8: noqa
from .iamc.variable import Variable as Variable
from .model import Model as Model
from .optimization.equation import Equation as Equation
from .optimization.indexset import IndexSet as IndexSet
from .optimization.parameter import Parameter as Parameter
from .optimization.scalar import Scalar as Scalar
from .optimization.table import Table as Table

# TODO Is this really the name we want to use?
from .optimization.variable import Variable as OptimizationVariable
from .platform import Platform as Platform
from .region import Region as Region
from .run import Run as Run
Expand Down
15 changes: 15 additions & 0 deletions ixmp4/core/optimization/data.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
from ixmp4.data.abstract import Run

from ..base import BaseFacade
from .equation import EquationRepository
from .indexset import IndexSetRepository
from .parameter import ParameterRepository
from .scalar import ScalarRepository
from .table import TableRepository
from .variable import VariableRepository


class OptimizationData(BaseFacade):
"""An optimization data instance, which provides access to optimization data such as
IndexSet, Table, Variable, etc."""

equations: EquationRepository
indexsets: IndexSetRepository
parameters: ParameterRepository
scalars: ScalarRepository
tables: TableRepository
variables: VariableRepository

def __init__(self, *args, run: Run, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.equations = EquationRepository(_backend=self.backend, _run=run)
self.indexsets = IndexSetRepository(_backend=self.backend, _run=run)
self.parameters = ParameterRepository(_backend=self.backend, _run=run)
self.scalars = ScalarRepository(_backend=self.backend, _run=run)
self.tables = TableRepository(_backend=self.backend, _run=run)
self.variables = VariableRepository(_backend=self.backend, _run=run)

def remove_solution(self) -> None:
for equation in self.equations.list():
equation.remove_data()
for variable in self.variables.list():
variable.remove_data()
139 changes: 139 additions & 0 deletions ixmp4/core/optimization/equation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
from datetime import datetime
from typing import Any, ClassVar, Iterable

import pandas as pd

from ixmp4.core.base import BaseFacade, BaseModelFacade
from ixmp4.data.abstract import Docs as DocsModel
from ixmp4.data.abstract import Equation as EquationModel
from ixmp4.data.abstract import Run
from ixmp4.data.abstract.optimization import Column


class Equation(BaseModelFacade):
_model: EquationModel
NotFound: ClassVar = EquationModel.NotFound
NotUnique: ClassVar = EquationModel.NotUnique

@property
def id(self) -> int:
return self._model.id

@property
def name(self) -> str:
return self._model.name

@property
def run_id(self) -> int:
return self._model.run__id

@property
def data(self) -> dict[str, Any]:
return self._model.data

def add(self, data: dict[str, Any] | pd.DataFrame) -> None:
# TODO change to "to the Equation"
"""Adds data to an existing Equation."""
self.backend.optimization.equations.add_data(
equation_id=self._model.id, data=data
)
self._model.data = self.backend.optimization.equations.get(
run_id=self._model.run__id, name=self._model.name
).data

def remove_data(self) -> None:
"""Removes data from an existing Equation."""
self.backend.optimization.equations.remove_data(equation_id=self._model.id)
self._model.data = self.backend.optimization.equations.get(
run_id=self._model.run__id, name=self._model.name
).data

@property
def levels(self) -> list:
return self._model.data.get("levels", [])

@property
def marginals(self) -> list:
return self._model.data.get("marginals", [])

@property
def constrained_to_indexsets(self) -> list[str]:
return [column.indexset.name for column in self._model.columns]

@property
def columns(self) -> list[Column]:
return self._model.columns

@property
def created_at(self) -> datetime | None:
return self._model.created_at

@property
def created_by(self) -> str | None:
return self._model.created_by

@property
def docs(self):
try:
return self.backend.optimization.equations.docs.get(self.id).description
except DocsModel.NotFound:
return None

@docs.setter
def docs(self, description):
if description is None:
self.backend.optimization.equations.docs.delete(self.id)
else:
self.backend.optimization.equations.docs.set(self.id, description)

@docs.deleter
def docs(self):
try:
self.backend.optimization.equations.docs.delete(self.id)
# TODO: silently failing
except DocsModel.NotFound:
return None

def __str__(self) -> str:
return f"<Equation {self.id} name={self.name}>"


class EquationRepository(BaseFacade):
_run: Run

def __init__(self, _run: Run, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._run = _run

def create(
self,
name: str,
constrained_to_indexsets: list[str],
column_names: list[str] | None = None,
) -> Equation:
model = self.backend.optimization.equations.create(
name=name,
run_id=self._run.id,
constrained_to_indexsets=constrained_to_indexsets,
column_names=column_names,
)
return Equation(_backend=self.backend, _model=model)

def get(self, name: str) -> Equation:
model = self.backend.optimization.equations.get(run_id=self._run.id, name=name)
return Equation(_backend=self.backend, _model=model)

def list(self, name: str | None = None) -> Iterable[Equation]:
equations = self.backend.optimization.equations.list(
run_id=self._run.id, name=name
)
return [
Equation(
_backend=self.backend,
_model=i,
)
for i in equations
]

def tabulate(self, name: str | None = None) -> pd.DataFrame:
return self.backend.optimization.equations.tabulate(name=name)
8 changes: 6 additions & 2 deletions ixmp4/core/optimization/indexset.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def get(self, name: str) -> IndexSet:
return IndexSet(_backend=self.backend, _model=indexset)

def list(self, name: str | None = None) -> list[IndexSet]:
indexsets = self.backend.optimization.indexsets.list(name=name)
indexsets = self.backend.optimization.indexsets.list(
run_id=self._run.id, name=name
)
return [
IndexSet(
_backend=self.backend,
Expand All @@ -104,4 +106,6 @@ def list(self, name: str | None = None) -> list[IndexSet]:
]

def tabulate(self, name: str | None = None) -> pd.DataFrame:
return self.backend.optimization.indexsets.tabulate(name=name)
return self.backend.optimization.indexsets.tabulate(
run_id=self._run.id, name=name
)
Loading