Commit a7f82d21 authored by Eric Duminil's avatar Eric Duminil
Browse files

Trying to add new tests

parent 15437ec7
......@@ -22,6 +22,9 @@ dev = ["pytest>=8.0"]
[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"live: tests that require a SimStadt installation and a live project path",
]
[build-system]
requires = ["uv_build>=0.10.9,<0.11.0"]
......
......@@ -169,6 +169,16 @@ def _compare_written_files(repo_path: Path, before: dict, after: dict) -> list[P
# Public execution API
# ---------------------------------------------------------------------------
def get_simstadt_output(path: Path) -> str:
"""Run SimStadt with the given path and return stdout regardless of exit code.
Useful for extracting version information from the startup banner.
"""
with chdir(get_simstadt_folder()):
result = subprocess.run([_simstadt_script(), str(path)], text=True, capture_output=True, check=False)
return result.stdout
def run_simstadt(workflow_path: Path, name: str) -> str:
"""Invoke the SimStadt CLI for the given workflow. Returns stdout."""
with chdir(get_simstadt_folder()):
......
import os
from pathlib import Path
import pytest
......@@ -14,3 +15,26 @@ def test_repository() -> Path:
@pytest.fixture(scope="session")
def test_project_path() -> Path:
return TEST_PROJECT_PATH
# ---------------------------------------------------------------------------
# Live fixtures (require SimStadt to be installed)
# ---------------------------------------------------------------------------
@pytest.fixture(scope="session")
def simstadt_folder():
"""Skip the test if SimStadt is not installed."""
from simstadtpy.runner import get_simstadt_folder
try:
return get_simstadt_folder()
except ValueError as e:
pytest.skip(str(e))
@pytest.fixture(scope="session")
def live_project_path(simstadt_folder):
"""Skip the test if SIMSTADTPY_TEST_PROJECT is not set."""
project = os.environ.get("SIMSTADTPY_TEST_PROJECT")
if not project:
pytest.skip("SIMSTADTPY_TEST_PROJECT env var not set")
return Path(project)
"""Live integration tests for SimStadt workflows. Skipped if SimStadt is not installed
or SIMSTADTPY_TEST_PROJECT is not set."""
import pytest
from simstadtpy.results import KPI
from simstadtpy.workflows import greenwater_simulation, heatdemand_simulation, photovoltaic_simulation
def to_dict(values: list[KPI]) -> dict[str, KPI]:
return {kpi.name: kpi for kpi in values}
@pytest.mark.live
def test_pv(live_project_path):
citygml = live_project_path / "Grombühl_mini.gml"
meteonorm = "Wuerzburg-hour.csv"
min_roof_insolation = 800
########################
# No shadow, no tree #
########################
results = photovoltaic_simulation(
citygml,
meteonorm_filename=meteonorm,
with_shadows=False,
min_roof_insolation=min_roof_insolation,
)
pv_kpis = to_dict(results.kpis)
assert "PV potential yield" in pv_kpis
assert "Roof area for PV" in pv_kpis
assert "PV specific yield" in pv_kpis
assert abs(pv_kpis["PV specific yield"].value - 1_100) <= 200
########################
# No shadow, more roofs #
########################
more_pv_kpis = to_dict(
photovoltaic_simulation(
citygml,
meteonorm_filename=meteonorm,
pv_share_flat=80,
pv_share_tilted=80,
with_shadows=False,
min_roof_insolation=min_roof_insolation,
).kpis
)
assert more_pv_kpis["PV potential nominal power"].value > pv_kpis["PV potential nominal power"].value * 1.5
assert more_pv_kpis["PV potential yield"].value > pv_kpis["PV potential yield"].value * 1.5
assert abs(more_pv_kpis["Irradiance in module plane"].value - pv_kpis["Irradiance in module plane"].value) <= 1
assert abs(more_pv_kpis["Roof area for PV"].value - pv_kpis["Roof area for PV"].value) <= 100
########################
# Shadow, no tree #
########################
shadow_results = photovoltaic_simulation(
citygml,
meteonorm_filename=meteonorm,
with_shadows=True,
min_roof_insolation=min_roof_insolation,
)
shadow_pv_kpis = to_dict(shadow_results.kpis)
assert pv_kpis["PV potential nominal power"].value == pytest.approx(
shadow_pv_kpis["PV potential nominal power"].value
)
assert shadow_pv_kpis["PV potential yield"].value < pv_kpis["PV potential yield"].value
assert shadow_pv_kpis["Irradiance in module plane"].value < pv_kpis["Irradiance in module plane"].value
########################
# Shadow, trees #
########################
tree_results = photovoltaic_simulation(
live_project_path / "Grombühl_mini_with_comically_large_trees.gml",
meteonorm_filename=meteonorm,
with_shadows=True,
min_roof_insolation=min_roof_insolation,
)
tree_pv_kpis = to_dict(tree_results.kpis)
assert tree_pv_kpis["PV potential nominal power"].value < shadow_pv_kpis["PV potential nominal power"].value * 0.8
assert tree_pv_kpis["PV potential yield"].value < shadow_pv_kpis["PV potential yield"].value * 0.8
assert tree_pv_kpis["Irradiance in module plane"].value < shadow_pv_kpis["Irradiance in module plane"].value
@pytest.mark.live
def test_heat_demand(live_project_path):
citygml = live_project_path / "Grombühl_mini.gml"
meteonorm = live_project_path / "Wuerzburg-hour.csv"
########################
# No shadow, no tree #
########################
results = heatdemand_simulation(
citygml,
meteonorm_filename=str(meteonorm),
with_shadows=False,
calculation_mode="HEATING_AND_COOLING",
)
kpis = to_dict(results.kpis)
assert "Heated area" in kpis
assert "Yearly Heating demand" in kpis
assert "Yearly Cooling demand" in kpis
assert kpis["Year of construction"].value > 1900
########################
# Shadow, no tree #
########################
shadow_results = heatdemand_simulation(
citygml,
meteonorm_filename=str(meteonorm),
with_shadows=True,
calculation_mode="HEATING_AND_COOLING",
)
shadow_kpis = to_dict(shadow_results.kpis)
assert shadow_kpis["Yearly Heating demand"].value > kpis["Yearly Heating demand"].value * 1.02
assert shadow_kpis["Yearly Cooling demand"].value < kpis["Yearly Cooling demand"].value * 0.95
########################
# Shadow, trees #
########################
tree_results = heatdemand_simulation(
live_project_path / "Grombühl_mini_with_trees.gml",
meteonorm_filename=str(meteonorm),
with_shadows=True,
calculation_mode="HEATING_AND_COOLING",
)
tree_kpis = to_dict(tree_results.kpis)
assert tree_kpis["Yearly Heating demand"].value > shadow_kpis["Yearly Heating demand"].value * 1.01
assert tree_kpis["Yearly Cooling demand"].value < shadow_kpis["Yearly Cooling demand"].value * 0.95
########################
# Refurbishment #
########################
refurb_results = heatdemand_simulation(
live_project_path / "Grombühl_mini_with_trees.gml",
meteonorm_filename=str(meteonorm),
with_shadows=True,
calculation_mode="HEATING_AND_COOLING",
refurbishment_ratio=1.0,
)
refurb_kpis = to_dict(refurb_results.kpis)
assert refurb_kpis["Yearly Heating demand"].value < tree_kpis["Yearly Heating demand"].value * 0.50
assert refurb_kpis["Mean Uvalue"].value < tree_kpis["Mean Uvalue"].value * 0.50
@pytest.mark.live
def test_greenwater(live_project_path):
citygml_with_trees = live_project_path / "Grombühl_mini_with_trees.gml"
meteonorm = "Wuerzburg-hour.csv"
results = greenwater_simulation(citygml_with_trees, meteonorm, irrigation_ratio=1.0)
kpis = to_dict(results.kpis)
assert "Rain" in kpis
assert "Evaporative cooling" in kpis
assert "Irrigation" in kpis
assert kpis["Rain"].rounded_value == pytest.approx(458)
more_results = greenwater_simulation(citygml_with_trees, meteonorm, irrigation_ratio=0.0)
more_kpis = to_dict(more_results.kpis)
assert "Rain" in more_kpis
assert "Evaporative cooling" in more_kpis
assert "Irrigation" in more_kpis
assert kpis["Irrigation"].value > 500
assert more_kpis["Irrigation"].rounded_value == pytest.approx(0)
"""Live test for SimStadt version. Skipped if SimStadt is not installed."""
import re
import pytest
from simstadtpy.runner import get_simstadt_output
SIMSTADT_MIN_VERSION = "0.14.0"
SIMSTADT_MIN_DATE = "20250701"
SIMSTADT_VERSION_FORMAT = re.compile(r"Launching SimStadt (\S+) \((\w+), rev. (\w+), (\d+)\)")
@pytest.mark.live
def test_version(simstadt_folder):
output = get_simstadt_output(simstadt_folder)
m = SIMSTADT_VERSION_FORMAT.search(output)
if not m:
pytest.fail(f"No SimStadt version string found in output:\n{output}")
version, _branch, _commit, date = m.groups()
assert version >= SIMSTADT_MIN_VERSION, (
f"SimStadt version {version} is older than required {SIMSTADT_MIN_VERSION}.\n"
"https://simstadt.hft-stuttgart.de/download/InstallFiles/SimStadt2_latest.zip"
)
assert date >= SIMSTADT_MIN_DATE, (
f"SimStadt build date {date} is older than required {SIMSTADT_MIN_DATE}.\n"
"https://simstadt.hft-stuttgart.de/download/InstallFiles/SimStadt2_latest.zip"
)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment