diff --git a/src/antares/model/area.py b/src/antares/model/area.py index a805aa75..f56905d6 100644 --- a/src/antares/model/area.py +++ b/src/antares/model/area.py @@ -28,6 +28,7 @@ from antares.model.misc_gen import MiscGen from antares.model.renewable import RenewableCluster, RenewableClusterProperties from antares.model.reserves import Reserves +from antares.model.solar import Solar from antares.model.st_storage import STStorage, STStorageProperties from antares.model.thermal import ThermalCluster, ThermalClusterProperties from antares.model.wind import Wind @@ -245,6 +246,7 @@ def __init__( # type: ignore # TODO: Find a way to avoid circular imports hydro: Optional[Hydro] = None, wind: Optional[Wind] = None, reserves: Optional[Reserves] = None, + solar: Optional[Solar] = None, misc_gen: Optional[MiscGen] = None, properties: Optional[AreaProperties] = None, ui: Optional[AreaUi] = None, @@ -261,6 +263,7 @@ def __init__( # type: ignore # TODO: Find a way to avoid circular imports self._hydro = hydro self._wind = wind self._reserves = reserves + self._solar = solar self._misc_gen = misc_gen self._properties = properties or AreaProperties() self._ui = ui or AreaUi() @@ -378,8 +381,10 @@ def create_reserves(self, series: Optional[pd.DataFrame]) -> Reserves: self._reserves = reserves return reserves - def create_solar(self, series: Optional[pd.DataFrame]) -> None: - self._area_service.create_solar(self, series) + def create_solar(self, series: Optional[pd.DataFrame]) -> Solar: + solar = self._area_service.create_solar(self, series) + self._solar = solar + return solar def create_misc_gen(self, series: Optional[pd.DataFrame]) -> MiscGen: misc_gen = self._area_service.create_misc_gen(self, series) diff --git a/src/antares/model/solar.py b/src/antares/model/solar.py new file mode 100644 index 00000000..57aca133 --- /dev/null +++ b/src/antares/model/solar.py @@ -0,0 +1,18 @@ +# Copyright (c) 2024, RTE (https://www.rte-france.com) +# +# See AUTHORS.txt +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 +# +# This file is part of the Antares project. + + +from antares.tools.time_series_tool import TimeSeries + + +class Solar(TimeSeries): + pass diff --git a/src/antares/service/api_services/area_api.py b/src/antares/service/api_services/area_api.py index ba28b595..5a846ca0 100644 --- a/src/antares/service/api_services/area_api.py +++ b/src/antares/service/api_services/area_api.py @@ -40,6 +40,7 @@ from antares.model.misc_gen import MiscGen from antares.model.renewable import RenewableClusterProperties, RenewableCluster from antares.model.reserves import Reserves +from antares.model.solar import Solar from antares.model.st_storage import STStorageProperties, STStorage from antares.model.thermal import ThermalClusterProperties, ThermalCluster from antares.model.wind import Wind @@ -377,9 +378,11 @@ def create_reserves(self, area: Area, series: Optional[pd.DataFrame]) -> Reserve self._upload_series(area, series, series_path) return Reserves(series) - def create_solar(self, area: Area, series: Optional[pd.DataFrame]) -> None: + def create_solar(self, area: Area, series: Optional[pd.DataFrame]) -> Solar: + series = series if series is not None else pd.DataFrame([]) series_path = f"input/solar/series/solar_{area.id}" self._upload_series(area, series, series_path) + return Solar(series) def create_misc_gen(self, area: Area, series: Optional[pd.DataFrame]) -> MiscGen: series = series if series is not None else pd.DataFrame([]) diff --git a/src/antares/service/base_services.py b/src/antares/service/base_services.py index 8187c022..abdd04d4 100644 --- a/src/antares/service/base_services.py +++ b/src/antares/service/base_services.py @@ -30,6 +30,7 @@ from antares.model.renewable import RenewableClusterProperties, RenewableCluster from antares.model.reserves import Reserves from antares.model.settings import StudySettings +from antares.model.solar import Solar from antares.model.st_storage import STStorageProperties, STStorage from antares.model.thermal import ThermalClusterProperties, ThermalCluster, ThermalClusterMatrixName from antares.model.wind import Wind @@ -158,11 +159,11 @@ def create_reserves(self, area: Area, series: Optional[pd.DataFrame]) -> Reserve pass @abstractmethod - def create_solar(self, area: Area, series: Optional[pd.DataFrame]) -> None: + def create_solar(self, area: Area, series: Optional[pd.DataFrame]) -> Solar: """ Args: area: area to create reserves series matrices - series: solar/series/series.txt + series: solar/series/solar_{area_id}.txt """ pass diff --git a/src/antares/service/local_services/area_local.py b/src/antares/service/local_services/area_local.py index 4170d042..a43d743c 100644 --- a/src/antares/service/local_services/area_local.py +++ b/src/antares/service/local_services/area_local.py @@ -24,6 +24,7 @@ from antares.model.misc_gen import MiscGen from antares.model.renewable import RenewableClusterProperties, RenewableCluster, RenewableClusterPropertiesLocal from antares.model.reserves import Reserves +from antares.model.solar import Solar from antares.model.st_storage import STStorageProperties, STStorage, STStoragePropertiesLocal from antares.model.thermal import ThermalClusterProperties, ThermalCluster, ThermalClusterPropertiesLocal from antares.model.wind import Wind @@ -141,8 +142,10 @@ def create_reserves(self, area: Area, series: Optional[pd.DataFrame]) -> Reserve local_file = TimeSeriesFile(TimeSeriesFileType.RESERVES, self.config.study_path, area.id, series) return Reserves(series, local_file) - def create_solar(self, area: Area, series: Optional[pd.DataFrame]) -> None: - raise NotImplementedError + def create_solar(self, area: Area, series: Optional[pd.DataFrame]) -> Solar: + series = series if series is not None else pd.DataFrame([]) + local_file = TimeSeriesFile(TimeSeriesFileType.SOLAR, self.config.study_path, area.id, series) + return Solar(series, local_file) def create_misc_gen(self, area: Area, series: Optional[pd.DataFrame]) -> MiscGen: series = series if series is not None else pd.DataFrame([]) diff --git a/src/antares/tools/time_series_tool.py b/src/antares/tools/time_series_tool.py index bb1ed6d1..ea2ebb9c 100644 --- a/src/antares/tools/time_series_tool.py +++ b/src/antares/tools/time_series_tool.py @@ -28,6 +28,7 @@ class TimeSeriesFileType(Enum): MISC_GEN = "input/misc-gen/miscgen-{area_id}.txt" RESERVES = "input/reserves/{area_id}.txt" + SOLAR = "input/solar/series/solar_{area_id}.txt" WIND = "input/wind/series/wind_{area_id}.txt" diff --git a/tests/antares/services/local_services/test_area.py b/tests/antares/services/local_services/test_area.py index 8b92dc13..a65485bd 100644 --- a/tests/antares/services/local_services/test_area.py +++ b/tests/antares/services/local_services/test_area.py @@ -757,3 +757,40 @@ def test_can_create_wind_ts_file_with_time_series(self, area_fr): # Then assert actual_time_series.equals(expected_time_series) assert actual_time_series_string == expected_time_series_string + + +class TestCreateSolar: + def test_can_create_solar_ts_file(self, area_fr): + # Given + solar_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.SOLAR.value.format( + area_id=area_fr.id + ) + expected_solar_file_path = area_fr._area_service.config.study_path / "input/solar/series/solar_fr.txt" + + # When + area_fr.create_solar(None) + + # Then + assert solar_file_path == expected_solar_file_path + assert solar_file_path.exists() + assert solar_file_path.is_file() + + def test_can_create_solar_ts_file_with_time_series(self, area_fr): + # Given + solar_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.SOLAR.value.format( + area_id=area_fr.id + ) + expected_time_series_string = """1.0\t1.0\t1.0 +1.0\t1.0\t1.0 +""" + expected_time_series = pd.read_csv(StringIO(expected_time_series_string), sep="\t", header=None) + + # When + area_fr.create_solar(pd.DataFrame(np.ones([2, 3]))) + actual_time_series = pd.read_csv(solar_file_path, sep="\t", header=None) + with solar_file_path.open("r") as solar_ts_file: + actual_time_series_string = solar_ts_file.read() + + # Then + assert actual_time_series.equals(expected_time_series) + assert actual_time_series_string == expected_time_series_string