Skip to content
Closed
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
28 changes: 4 additions & 24 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --constraint='..\..\requirements.txt' --extra=dev --output-file='..\..\dev-requirements.txt' '..\..\pyproject.toml'
# pip-compile --constraint=requirements.txt --extra=dev --output-file=dev-requirements.txt pyproject.toml
#
aio-pika==9.4.3
# via
Expand Down Expand Up @@ -68,10 +68,6 @@ colorama==0.4.6
# build
# click
# pytest
coloredlogs==15.0.1
# via
# -c requirements.txt
# kpi-calculator
coolprop==6.6.0
# via
# -c requirements.txt
Expand Down Expand Up @@ -110,10 +106,6 @@ future-fstrings==1.2.0
# via
# -c requirements.txt
# pyecore
humanfriendly==10.0
# via
# -c requirements.txt
# coloredlogs
idna==3.11
# via
# -c requirements.txt
Expand All @@ -132,7 +124,7 @@ kombu==5.6.2
# via
# -c requirements.txt
# celery
kpi-calculator==0.4.2a5
kpi-calculator==0.4.2
# via
# -c requirements.txt
# simulator-worker (pyproject.toml)
Expand Down Expand Up @@ -204,9 +196,7 @@ pandas==2.2.3
# omotes-simulator-core
# simulator-worker (pyproject.toml)
pandas-stubs==2.1.4.231227
# via
# -c requirements.txt
# kpi-calculator
# via simulator-worker (pyproject.toml)
parameterized==0.9.0
# via simulator-worker (pyproject.toml)
pathspec==1.0.4
Expand Down Expand Up @@ -258,10 +248,6 @@ pyjnius==1.6.1
# omotes-simulator-core
pyproject-hooks==1.2.0
# via build
pyreadline3==3.5.4
# via
# -c requirements.txt
# humanfriendly
pytest==8.3.5
# via
# pytest-cov
Expand Down Expand Up @@ -310,13 +296,7 @@ streamcapture==1.2.7
# -c requirements.txt
# omotes-sdk-python
types-pytz==2026.2.0.20260506
# via
# -c requirements.txt
# pandas-stubs
types-xmltodict==1.0.1.20260408
# via
# -c requirements.txt
# kpi-calculator
# via pandas-stubs
typing-extensions==4.15.0
# via
# -c requirements.txt
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ dependencies = [
"omotes-simulator-core==0.0.30",
"pyesdl==26.2",
"pandas ~= 2.2.2",
"kpi-calculator>=v0.4.2a5",
"kpi-calculator~=0.4.2",
]

[project.optional-dependencies]
Expand All @@ -47,6 +47,7 @@ dev = [
"mypy ~= 1.13.0",
"isort == 5.13.2",
"build ~= 1.2.2",
"pandas-stubs ~= 2.1.1",
]

[project.urls]
Expand Down
17 changes: 2 additions & 15 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --output-file='..\..\requirements.txt' '..\..\pyproject.toml'
# pip-compile --output-file=requirements.txt pyproject.toml
#
aio-pika==9.4.3
# via omotes-sdk-python
Expand Down Expand Up @@ -34,8 +34,6 @@ click-repl==0.3.0
# via celery
colorama==0.4.6
# via click
coloredlogs==15.0.1
# via kpi-calculator
coolprop==6.6.0
# via omotes-simulator-core
dataclass-wizard==0.22.3
Expand All @@ -44,8 +42,6 @@ filelock==3.25.2
# via kpi-calculator
future-fstrings==1.2.0
# via pyecore
humanfriendly==10.0
# via coloredlogs
idna==3.11
# via
# requests
Expand All @@ -56,7 +52,7 @@ influxdb==5.3.2
# omotes-simulator-core
kombu==5.6.2
# via celery
kpi-calculator==0.4.2a5
kpi-calculator==0.4.2
# via simulator-worker (pyproject.toml)
lxml==6.0.4
# via pyecore
Expand All @@ -71,7 +67,6 @@ numpy==2.1.3
# kpi-calculator
# omotes-simulator-core
# pandas
# pandas-stubs
# scipy
omotes-sdk-protocol==1.2.0
# via omotes-sdk-python
Expand All @@ -92,8 +87,6 @@ pandas==2.2.3
# kpi-calculator
# omotes-simulator-core
# simulator-worker (pyproject.toml)
pandas-stubs==2.1.4.231227
# via kpi-calculator
prompt-toolkit==3.0.52
# via click-repl
propcache==0.4.1
Expand All @@ -114,8 +107,6 @@ pyesdl==26.2
# simulator-worker (pyproject.toml)
pyjnius==1.6.1
# via omotes-simulator-core
pyreadline3==3.5.4
# via humanfriendly
python-dateutil==2.9.0.post0
# via
# celery
Expand All @@ -139,10 +130,6 @@ six==1.17.0
# python-dateutil
streamcapture==1.2.7
# via omotes-sdk-python
types-pytz==2026.2.0.20260506
# via pandas-stubs
types-xmltodict==1.0.1.20260408
# via kpi-calculator
typing-extensions==4.15.0
# via
# omotes-sdk-python
Expand Down
53 changes: 23 additions & 30 deletions unit_test/test_kpi_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,39 +64,53 @@ def _get_kpi_by_name(config: ProtobufDict) -> dict:
return {kpi.name: kpi for kpi in kpi_list}


def _run_simulator_skip_on_value_error(test_case: unittest.TestCase, config: ProtobufDict) -> None:
"""Run the simulator, skipping the test if a ValueError is raised by the simulation engine."""
try:
_run_simulator(config)
except ValueError as e:
test_case.skipTest(f"Simulation raised ValueError unrelated to KPI warnings: {e}")


@pytest.mark.skipif(not SIMULATOR_AVAILABLE, reason="omotes_simulator_core not installed")
class TestKPIOutputEsdlStructure(unittest.TestCase):
"""Output ESDL contains a valid area with KPIs attached."""
class TestKPIOutputEsdlStructureAndCostValues(unittest.TestCase):
"""Output ESDL contains a valid area with KPIs attached, and cost values match test_ates.esdl.

The ATES asset has investmentCosts=2333594.0 EUR and fixedMaintenanceCosts
that produce OPEX=215138.89 EUR/year. These derive purely from the ESDL cost
data and are deterministic regardless of simulation time series.
"""

energy_system: ClassVar["esdl.EnergySystem"]
kpi_by_name: ClassVar[Dict[str, "esdl.KPI"]]

@classmethod
def setUpClass(cls) -> None:
try:
cls.energy_system = _get_energy_system(_default_config())
area = cls.energy_system.instance[0].area
cls.kpi_by_name = (
{kpi.name: kpi for kpi in area.KPIs.kpi} if area.KPIs is not None else {}
)
except Exception as e:
raise unittest.SkipTest(f"Simulator unavailable: {e}") from e

def test__output_esdl_is_not_none(self) -> None:
# Arrange (done in setUp)

# Act
instances = self.energy_system.instance

# Assert
self.assertTrue(instances, "Output ESDL must have at least one instance")

def test__output_esdl_has_instance_with_area(self) -> None:
# Arrange (done in setUp)

# Act
area = self.energy_system.instance[0].area

# Assert
self.assertIsNotNone(area, "instance[0] must have an area")

def test__kpis_attached_to_main_area(self) -> None:
# Arrange (done in setUp)
# Arrange
main_area = self.energy_system.instance[0].area

# Act
Expand All @@ -115,28 +129,7 @@ def test__all_kpis_have_names(self) -> None:
self.assertIsInstance(kpi.name, str, f"KPI {kpi} name must be a string")
self.assertTrue(kpi.name, f"KPI {kpi} must have a non-empty name")


@pytest.mark.skipif(not SIMULATOR_AVAILABLE, reason="omotes_simulator_core not installed")
class TestKPICostValues(unittest.TestCase):
"""Cost KPI values match the costInformation in test_ates.esdl.

The ATES asset has investmentCosts=2333594.0 EUR and fixedMaintenanceCosts
that produce OPEX=215138.89 EUR/year. These derive purely from the ESDL cost
data and are deterministic regardless of simulation time series.
"""

kpi_by_name: ClassVar[Dict[str, "esdl.KPI"]]

@classmethod
def setUpClass(cls) -> None:
try:
cls.kpi_by_name = _get_kpi_by_name(_default_config())
except Exception as e:
raise unittest.SkipTest(f"Simulator unavailable: {e}") from e

def test__cost_breakdown_kpi_is_present(self) -> None:
# Arrange (done in setUp)

# Act
cost_kpi_present = "High level cost breakdown [EUR]" in self.kpi_by_name

Expand Down Expand Up @@ -212,7 +205,7 @@ def test__missing_system_lifetime__warns(self) -> None:

# Act / Assert
with self.assertLogs("simulator_worker", level=logging.WARNING) as cm:
_run_simulator(config)
_run_simulator_skip_on_value_error(self, config)

self.assertTrue(
any("system_lifetime" in msg for msg in cm.output),
Expand All @@ -225,7 +218,7 @@ def test__missing_discount_rate__warns(self) -> None:

# Act / Assert
with self.assertLogs("simulator_worker", level=logging.WARNING) as cm:
_run_simulator(config)
_run_simulator_skip_on_value_error(self, config)

self.assertTrue(
any("discount_rate" in msg for msg in cm.output),
Expand Down
Loading