Commit 03a19d2b authored by Eric Duminil's avatar Eric Duminil
Browse files

Adding solar results too

parent b886d1a3
......@@ -5,6 +5,7 @@ from simstadtpy.results import (
LoadProfileResults,
PhotovoltaicResults,
SimStadtResults,
SolarPotentialResults,
create_simstadt_results,
)
from simstadtpy.runner import run_workflow_with_citygml
......@@ -17,6 +18,7 @@ __all__ = [
"LoadProfileResults",
"PhotovoltaicResults",
"GreenWaterResults",
"SolarPotentialResults",
"create_simstadt_results",
"run_workflow_with_citygml",
"photovoltaic_simulation",
......
......@@ -77,6 +77,7 @@ class Providers(str, Enum):
)
GREEN_WATER = "de.hftstuttgart.simstadtworkflows.greenwater.GreenWaterWorkflowProvider"
LOAD_PROFILE = "de.hftstuttgart.simstadtworkflows.energy.LoadProfileProvider"
SOLAR_POTENTIAL = "de.hftstuttgart.simstadtworkflows.energy.SolarPotentialAnalysisWorkflowProvider"
def detect_decimal(csv_path: Path, line_number: int, check: str = "") -> str:
......@@ -459,6 +460,33 @@ class LoadProfileResults(SimStadtResults):
]
@dataclass(repr=False)
class SolarPotentialResults(SimStadtResults):
"""Results for SolarPotential workflows.
The output is a .prn weather file with 8760 hourly rows and three columns:
GHI (W/m²), DHI (W/m²), Ta (°C).
"""
supported_providers: ClassVar[list[str]] = [Providers.SOLAR_POTENTIAL]
csv_identifier = "_solar_potential" # no CSV output; csv_path will raise if called
def _parse_results(self) -> pd.DataFrame:
prn_paths = self.get_all_by_extension(".prn")
if len(prn_paths) != 1:
raise ValueError(f"Expected exactly one .prn file, found {len(prn_paths)}")
return pd.read_csv(prn_paths[0], sep=r"\s+", names=["GHI", "DHI", "Ta"], header=None)
@property
def kpis(self) -> list[KPI]:
df = self.dataframe
return [
KPI("Annual GHI", df["GHI"].sum() / 1000, "kWh/m²", precision=1),
KPI("Annual DHI", df["DHI"].sum() / 1000, "kWh/m²", precision=1),
KPI("Mean temperature", float(df["Ta"].mean()), "°C", precision=1),
]
@dataclass(repr=False)
class GreenWaterResults(SimStadtResults):
"""Results for GreenWater workflows (hourly time series with tree species breakdown)."""
......
<?xml version="1.0" encoding="UTF-8"?>
<java version="17.0.14" class="java.beans.XMLDecoder">
<object class="eu.simstadt.citygml.steps.ImportCityGml" id="ImportCityGml0">
<void class="de.hftstuttgart.hierarchicworkflow.WorkflowStep" method="getField">
<string>startStep</string>
<void method="set">
<object idref="ImportCityGml0"/>
<boolean>true</boolean>
</void>
</void>
<void class="de.hftstuttgart.hierarchicworkflow.WorkflowStep" method="getField">
<string>selectedNextStepPath</string>
<void method="set">
<object idref="ImportCityGml0"/>
<string>01_Preprocessing.step</string>
</void>
</void>
<void property="name">
<string>Base</string>
</void>
</object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java version="17.0.14" class="java.beans.XMLDecoder">
<object class="eu.simstadt.workflowsteps.GeometricPreprocessorStep" id="GeometricPreprocessorStep0">
<void class="de.hftstuttgart.hierarchicworkflow.WorkflowStep" method="getField">
<string>startStep</string>
<void method="set">
<object idref="GeometricPreprocessorStep0"/>
<boolean>true</boolean>
</void>
</void>
<void property="name">
<string>Base</string>
</void>
</object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java version="17.0.14" class="java.beans.XMLDecoder">
<object class="eu.simstadt.workflowsteps.Preprocessing" id="Preprocessing0">
<void class="de.hftstuttgart.hierarchicworkflow.WorkflowStep" method="getField">
<string>selectedNextStepPath</string>
<void method="set">
<object idref="Preprocessing0"/>
<string>02_WeatherProcessor.step</string>
</void>
</void>
<void property="name">
<string>Base</string>
</void>
</object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java version="17.0.14" class="java.beans.XMLDecoder">
<object class="eu.simstadt.workflowsteps.WeatherProcessorStep" id="WeatherProcessorStep0">
<void class="de.hftstuttgart.hierarchicworkflow.WorkflowStep" method="getField">
<string>selectedNextStepPath</string>
<void method="set">
<object idref="WeatherProcessorStep0"/>
<string>03_IrradianceProcessor.step</string>
</void>
</void>
<void property="inputFilename">
<string>Stuttgart-hour.csv</string>
</void>
<void property="name">
<string>Base</string>
</void>
<void property="weatherDataSource">
<object class="java.lang.Enum" method="valueOf">
<class>de.hft.stuttgart.simstadt2.weather.WeatherDataSourceType</class>
<string>INSEL_DATABASE</string>
</object>
</void>
</object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java version="17.0.14" class="java.beans.XMLDecoder">
<object class="eu.simstadt.workflowsteps.IrradianceProcessorStep" id="IrradianceProcessorStep0">
<void class="de.hftstuttgart.hierarchicworkflow.WorkflowStep" method="getField">
<string>selectedNextStepPath</string>
<void method="set">
<object idref="IrradianceProcessorStep0"/>
<string>04_Visualization.step</string>
</void>
</void>
<void property="name">
<string>Base</string>
</void>
</object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java version="21.0.9" class="java.beans.XMLDecoder">
<object class="eu.simstadt.workflowsteps.Visualization" id="Visualization0">
<void property="name">
<string>Base</string>
</void>
<void property="view">
<object class="java.lang.Enum" method="valueOf">
<class>eu.simstadt.workflowsteps.Visualization$View</class>
<string>Top_View</string>
</object>
</void>
<void id="Boolean0" method="isShowIrradianceOnLOD1"/>
<void property="withIrradianceGraph">
<object idref="Boolean0"/>
</void>
</object>
</java>
<java version="17.0.14" class="java.beans.XMLDecoder">
<object class="eu.simstadt.workflows.CityGmlWorkflow" id="CityGmlWorkflow0">
<void class="de.hftstuttgart.hierarchicworkflow.WorkflowStep" method="getField">
<string>startStep</string>
<void method="set">
<object idref="CityGmlWorkflow0" />
<boolean>true</boolean>
</void>
</void>
<void property="name">
<string>ImBuchwald_Solar</string>
</void>
<void property="shortName">
<string>Solar</string>
</void>
<void property="workflowProvider">
<object class="de.hftstuttgart.simstadtworkflows.energy.SolarPotentialAnalysisWorkflowProvider" />
</void>
<void property="cityGmlFileNames"><void method="add"><string>MiniBuchwald.gml</string></void></void></object>
</java>
\ No newline at end of file
......@@ -12,12 +12,14 @@ from simstadtpy import (
LoadProfileResults,
PhotovoltaicResults,
SimStadtResults,
SolarPotentialResults,
create_simstadt_results,
)
TEST_REPOSITORY = Path(__file__).parent / "data" / "TestRepo"
TEST_PROJECT_PATH = TEST_REPOSITORY / "CGSC.proj"
LOAD_PROFILE_FLOW = TEST_REPOSITORY / "SektorSim.proj" / "LoadProfile.flow"
SOLAR_FLOW = TEST_REPOSITORY / "SektorSim.proj" / "Solar.flow"
def kpis_by_name(results: SimStadtResults) -> dict[str, KPI]:
......@@ -492,6 +494,75 @@ def test_loadprofile_repr(loadprofile_results):
assert "Total load" in detailed
# ---------------------------------------------------------------------------
# Solar potential
# ---------------------------------------------------------------------------
@pytest.fixture(scope="module")
def solar_results() -> SimStadtResults:
output_files = [
SOLAR_FLOW / "02_WeatherProcessor.step/hourly_GHI_DHI_Ta_inseldb_Stuttgart.prn",
SOLAR_FLOW / "04_Visualization.step/MiniBuchwald_inseldb_Hay_irradiance_Top_View.png",
SOLAR_FLOW / "04_Visualization.step/irradiance_inseldb_Hay_Stuttgart.png",
]
return create_simstadt_results("Solar potential for MiniBuchwald", output_files)
def test_solar_result_type(solar_results):
assert isinstance(solar_results, SolarPotentialResults)
def test_solar_output_files_exist(solar_results):
assert all(f.exists() for f in solar_results.output_files)
def test_solar_dataframe(solar_results):
df = solar_results.dataframe
assert isinstance(df, pd.DataFrame)
assert df.shape == (8760, 3)
assert list(df.columns) == ["GHI", "DHI", "Ta"]
def test_solar_kpis(solar_results):
kpis = kpis_by_name(solar_results)
assert kpis["Annual GHI"].value == pytest.approx(1143.9, abs=0.1)
assert kpis["Annual GHI"].unit == "kWh/m²"
assert kpis["Annual DHI"].value == pytest.approx(685.1, abs=0.1)
assert kpis["Mean temperature"].value == pytest.approx(9.73, abs=0.05)
assert kpis["Mean temperature"].unit == "°C"
def test_solar_no_diagrams(solar_results):
assert solar_results.diagrams == {}
def test_solar_project(solar_results):
assert solar_results.project == "SektorSim"
def test_solar_params(solar_results):
assert solar_results.short_name == "Solar"
assert solar_results.name == "ImBuchwald_Solar"
assert solar_results.workflow_provider == (
"de.hftstuttgart.simstadtworkflows.energy.SolarPotentialAnalysisWorkflowProvider"
)
def test_solar_serialization(solar_results):
json_str = solar_results.to_json()
reconstructed = SimStadtResults.from_json(json_str)
assert isinstance(reconstructed, SolarPotentialResults)
assert reconstructed.description == solar_results.description
assert len(reconstructed.kpis) == 3
pd.testing.assert_frame_equal(reconstructed.dataframe, solar_results.dataframe)
def test_solar_repr(solar_results):
detailed = repr(solar_results)
assert "MiniBuchwald.gml" in detailed
assert "Annual GHI" in detailed
def test_broken_simulation():
output_files = [
TEST_REPOSITORY / "CGSC.proj/103_PV.flow/02_WeatherProcessor.step/notATable.log",
......
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