Skip to content

Commit

Permalink
Added the load folder and object since it uses the same defaults as w…
Browse files Browse the repository at this point in the history
…ind and solar
  • Loading branch information
Sigurd-Borge committed Sep 10, 2024
1 parent 621ce45 commit 68bf243
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/antares/model/area.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from antares.model.commons import FilterOption, sort_filter_values
from antares.model.hydro import HydroProperties, HydroMatrixName, Hydro
from antares.model.load import Load
from antares.model.misc_gen import MiscGen
from antares.model.renewable import RenewableCluster, RenewableClusterProperties
from antares.model.reserves import Reserves
Expand Down Expand Up @@ -242,6 +243,7 @@ def __init__( # type: ignore # TODO: Find a way to avoid circular imports
*,
renewables: Optional[Dict[str, RenewableCluster]] = None,
thermals: Optional[Dict[str, ThermalCluster]] = None,
load: Optional[Load] = None,
st_storages: Optional[Dict[str, STStorage]] = None,
hydro: Optional[Hydro] = None,
wind: Optional[Wind] = None,
Expand All @@ -259,6 +261,7 @@ def __init__( # type: ignore # TODO: Find a way to avoid circular imports
self._renewable_service = renewable_service
self._renewables = renewables or dict()
self._thermals = thermals or dict()
self._load = load
self._st_storages = st_storages or dict()
self._hydro = hydro
self._wind = wind
Expand Down Expand Up @@ -327,6 +330,11 @@ def create_renewable_cluster(
self._renewables[renewable.id] = renewable
return renewable

def create_load(self, series: Optional[pd.DataFrame]) -> Load:
load = self._area_service.create_load(self, series)
self._load = load
return load

def create_st_storage(self, st_storage_name: str, properties: Optional[STStorageProperties] = None) -> STStorage:
storage = self._area_service.create_st_storage(self.id, st_storage_name, properties)
self._st_storages[storage.id] = storage
Expand Down
40 changes: 40 additions & 0 deletions src/antares/model/load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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 pathlib import Path
from typing import Optional

import pandas as pd

from antares.tools.prepro_folder import PreproFolder
from antares.tools.time_series_tool import TimeSeries, TimeSeriesFile


class Load:
def __init__(
self,
time_series: pd.DataFrame = pd.DataFrame([]),
local_file: Optional[TimeSeriesFile] = None,
study_path: Optional[Path] = None,
area_id: Optional[str] = None,
) -> None:
self._time_series = TimeSeries(time_series, local_file)
self._prepro = (
PreproFolder(folder="load", study_path=study_path, area_id=area_id) if study_path and area_id else None
)

@property
def time_series(self) -> TimeSeries:
return self._time_series

@property
def prepro(self) -> Optional[PreproFolder]:
return self._prepro
1 change: 1 addition & 0 deletions src/antares/model/study.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def _correlation_defaults() -> dict[str, dict[str, str]]:
correlation_inis_to_create = [
("solar_correlation", IniFileTypes.SOLAR_CORRELATION_INI),
("wind_correlation", IniFileTypes.WIND_CORRELATION_INI),
("load_correlation", IniFileTypes.LOAD_CORRELATION_INI),
]
ini_files = {
correlation: IniFile(study_directory, file_type, ini_contents=_correlation_defaults())
Expand Down
4 changes: 4 additions & 0 deletions src/antares/service/api_services/area_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
)
from antares.model.area import AreaProperties, AreaUi, Area
from antares.model.hydro import HydroProperties, HydroMatrixName, Hydro
from antares.model.load import Load
from antares.model.misc_gen import MiscGen
from antares.model.renewable import RenewableClusterProperties, RenewableCluster
from antares.model.reserves import Reserves
Expand Down Expand Up @@ -323,6 +324,9 @@ def create_renewable_cluster(

return RenewableCluster(self.renewable_service, area_id, name, properties)

def create_load(self, area: Area, series: Optional[pd.DataFrame]) -> Load:
raise NotImplementedError

def create_st_storage(
self, area_id: str, st_storage_name: str, properties: Optional[STStorageProperties] = None
) -> STStorage:
Expand Down
11 changes: 11 additions & 0 deletions src/antares/service/base_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
)
from antares.model.hydro import HydroProperties, HydroMatrixName, Hydro
from antares.model.link import LinkProperties, LinkUi, Link
from antares.model.load import Load
from antares.model.misc_gen import MiscGen
from antares.model.renewable import RenewableClusterProperties, RenewableCluster
from antares.model.reserves import Reserves
Expand Down Expand Up @@ -120,6 +121,16 @@ def create_renewable_cluster(
"""
pass

@abstractmethod
def create_load(self, area: Area, series: Optional[pd.DataFrame]) -> Load:
"""
Args:
area: area to create load series matrices
series: load/series/load_{area_id}.txt
"""
pass

@abstractmethod
def create_st_storage(
self, area_id: str, st_storage_name: str, properties: Optional[STStorageProperties] = None
Expand Down
6 changes: 6 additions & 0 deletions src/antares/service/local_services/area_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from antares.exceptions.exceptions import CustomError
from antares.model.area import AreaProperties, AreaUi, Area, AreaPropertiesLocal, AreaUiLocal
from antares.model.hydro import HydroProperties, HydroMatrixName, Hydro, HydroPropertiesLocal
from antares.model.load import Load
from antares.model.misc_gen import MiscGen
from antares.model.renewable import RenewableClusterProperties, RenewableCluster, RenewableClusterPropertiesLocal
from antares.model.reserves import Reserves
Expand Down Expand Up @@ -116,6 +117,11 @@ def create_renewable_cluster(
self.renewable_service, area_id, renewable_name, local_properties.yield_renewable_cluster_properties()
)

def create_load(self, area: Area, series: Optional[pd.DataFrame]) -> Load:
series = series if series is not None else pd.DataFrame([])
local_file = TimeSeriesFile(TimeSeriesFileType.LOAD, self.config.study_path, area.id, series)
return Load(time_series=series, local_file=local_file, study_path=self.config.study_path, area_id=area.id)

def create_st_storage(
self, area_id: str, st_storage_name: str, properties: Optional[STStorageProperties] = None
) -> STStorage:
Expand Down
2 changes: 2 additions & 0 deletions src/antares/tools/ini_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class IniFileTypes(Enum):
AREA_ADEQUACY_PATCH_INI = "input/areas/{area_name}/adequacy_patch.ini"
HYDRO_INI = "input/hydro/hydro.ini"
LINK_PROPERTIES_INI = "input/links/{area_name}/properties.ini"
LOAD_CORRELATION_INI = "input/load/prepro/correlation.ini"
LOAD_SETTINGS_INI = "input/load/prepro/{area_name}/settings.ini"
RENEWABLES_LIST_INI = "input/renewables/clusters/{area_name}/list.ini"
SOLAR_CORRELATION_INI = "input/solar/prepro/correlation.ini"
SOLAR_SETTINGS_INI = "input/solar/prepro/{area_name}/settings.ini"
Expand Down
6 changes: 6 additions & 0 deletions src/antares/tools/prepro_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ def __init__(self, folder: str, study_path: Path, area_id: str) -> None:
data = TimeSeriesFileType.WIND_DATA
k = TimeSeriesFileType.WIND_K
translation = TimeSeriesFileType.WIND_TRANSLATION
elif folder == "load":
settings = IniFileTypes.LOAD_SETTINGS_INI
conversion = TimeSeriesFileType.LOAD_CONVERSION
data = TimeSeriesFileType.LOAD_DATA
k = TimeSeriesFileType.LOAD_K
translation = TimeSeriesFileType.LOAD_TRANSLATION

self._settings = IniFile(study_path, settings, area_id)
self._conversion = TimeSeries(
Expand Down
5 changes: 5 additions & 0 deletions src/antares/tools/time_series_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ class TimeSeriesFileType(Enum):
TimeSeriesFileType.SOLAR.value.format(area_id="test_area")
"""

LOAD = "input/load/series/load_{area_id}.txt"
LOAD_CONVERSION = "input/load/prepro/{area_id}/conversion.txt"
LOAD_DATA = "input/load/prepro/{area_id}/data.txt"
LOAD_K = "input/load/prepro/{area_id}/k.txt"
LOAD_TRANSLATION = "input/load/prepro/{area_id}/translation.txt"
MISC_GEN = "input/misc-gen/miscgen-{area_id}.txt"
RESERVES = "input/reserves/{area_id}.txt"
SOLAR = "input/solar/series/solar_{area_id}.txt"
Expand Down
6 changes: 6 additions & 0 deletions tests/antares/services/local_services/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from antares.config.local_configuration import LocalConfiguration
from antares.model.area import Area
from antares.model.hydro import HydroProperties
from antares.model.load import Load
from antares.model.renewable import RenewableClusterProperties, TimeSeriesInterpretation, RenewableClusterGroup
from antares.model.solar import Solar
from antares.model.st_storage import STStorageProperties, STStorageGroup
Expand Down Expand Up @@ -222,3 +223,8 @@ def fr_solar(area_fr) -> Solar:
@pytest.fixture
def fr_wind(area_fr) -> Wind:
return area_fr.create_wind(None)


@pytest.fixture
def fr_load(area_fr) -> Load:
return area_fr.create_load(None)
155 changes: 155 additions & 0 deletions tests/antares/services/local_services/test_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -1031,3 +1031,158 @@ def test_translation_txt_is_empty_by_default(self, area_fr, fr_solar):

# Then
assert actual_file_contents == expected_file_contents


class TestCreateLoad:
def test_can_create_load_ts_file(self, area_fr):
# Given
load_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.LOAD.value.format(
area_id=area_fr.id
)
expected_load_file_path = area_fr._area_service.config.study_path / "input/load/series/load_fr.txt"

# When
area_fr.create_load(None)

# Then
assert load_file_path == expected_load_file_path
assert load_file_path.exists()
assert load_file_path.is_file()

def test_can_create_load_ts_file_with_time_series(self, area_fr):
# Given
load_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.LOAD.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_load(pd.DataFrame(np.ones([2, 3])))
actual_time_series = pd.read_csv(load_file_path, sep="\t", header=None)
with load_file_path.open("r") as load_ts_file:
actual_time_series_string = load_ts_file.read()

# Then
assert actual_time_series.equals(expected_time_series)
assert actual_time_series_string == expected_time_series_string

def test_settings_ini_exists(self, area_fr, fr_load):
# Given
expected_ini_path = area_fr._area_service.config.study_path / "input/load/prepro/fr/settings.ini"

# Then
assert expected_ini_path.exists()
assert expected_ini_path.is_file()
assert expected_ini_path == fr_load.prepro.settings.ini_path

def test_conversion_txt_exists(self, area_fr, fr_load):
# Given
expected_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.LOAD_CONVERSION.value.format(
area_id=area_fr.id
)

# Then
assert expected_file_path.exists()
assert expected_file_path.is_file()
assert fr_load.prepro.conversion.local_file.file_path == expected_file_path

def test_conversion_txt_has_correct_default_values(self, area_fr, fr_load):
# Given
expected_file_contents = """-9999999980506447872\t0\t9999999980506447872
0\t0\t0
"""
# data has to be compared as strings as the first value in the first column is too small for python apparently
expected_file_data = pd.read_csv(StringIO(expected_file_contents), sep="\t", header=None).astype(str)

# When
with fr_load.prepro.conversion.local_file.file_path.open("r") as fr_load_file:
actual_file_contents = fr_load_file.read()
actual_file_data = fr_load.prepro.conversion.time_series.astype(str)

# Then
assert actual_file_data.equals(expected_file_data)
assert actual_file_contents == expected_file_contents

def test_data_txt_exists(self, area_fr, fr_load):
# Given
expected_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.LOAD_DATA.value.format(
area_id=area_fr.id
)

# Then
assert expected_file_path.exists()
assert expected_file_path.is_file()
assert fr_load.prepro.data.local_file.file_path == expected_file_path

def test_data_txt_has_correct_default_values(self, area_fr, fr_load):
# Given
expected_file_contents = """1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
1\t1\t0\t1\t1\t1
"""
expected_file_data = pd.read_csv(StringIO(expected_file_contents), sep="\t", header=None)

# When
with fr_load.prepro.data.local_file.file_path.open("r") as fr_load_file:
actual_file_contents = fr_load_file.read()
actual_file_data = fr_load.prepro.data.time_series

# Then
assert actual_file_data.equals(expected_file_data)
assert actual_file_contents == expected_file_contents

def test_k_txt_exists(self, area_fr, fr_load):
# Given
expected_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.LOAD_K.value.format(
area_id=area_fr.id
)

# Then
assert expected_file_path.exists()
assert expected_file_path.is_file()
assert fr_load.prepro.k.local_file.file_path == expected_file_path

def test_k_txt_is_empty_by_default(self, area_fr, fr_load):
# Given
expected_file_contents = """"""

# When
with fr_load.prepro.k.local_file.file_path.open("r") as fr_load_file:
actual_file_contents = fr_load_file.read()

# Then
assert actual_file_contents == expected_file_contents

def test_translation_txt_exists(self, area_fr, fr_load):
# Given
expected_file_path = area_fr._area_service.config.study_path / TimeSeriesFileType.LOAD_TRANSLATION.value.format(
area_id=area_fr.id
)

# Then
assert expected_file_path.exists()
assert expected_file_path.is_file()
assert fr_load.prepro.translation.local_file.file_path == expected_file_path

def test_translation_txt_is_empty_by_default(self, area_fr, fr_load):
# Given
expected_file_contents = """"""

# When
with fr_load.prepro.translation.local_file.file_path.open("r") as fr_load_file:
actual_file_contents = fr_load_file.read()

# Then
assert actual_file_contents == expected_file_contents
Loading

0 comments on commit 68bf243

Please sign in to comment.