Skip to content

Commit

Permalink
Merge branch 'main' into refactor/bc
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinBelthle committed Feb 12, 2025
2 parents dfd4856 + 3864f14 commit 29e89ae
Show file tree
Hide file tree
Showing 15 changed files with 205 additions and 134 deletions.
56 changes: 14 additions & 42 deletions src/antares/craft/model/st_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@
# SPDX-License-Identifier: MPL-2.0
#
# This file is part of the Antares project.

from dataclasses import dataclass
from enum import Enum
from typing import Optional

import pandas as pd

from antares.craft.service.base_services import BaseShortTermStorageService
from antares.craft.tools.all_optional_meta import all_optional_model
from antares.craft.tools.contents_tool import transform_name_to_id
from pydantic import BaseModel
from pydantic.alias_generators import to_camel


class STStorageGroup(Enum):
Expand All @@ -43,53 +40,30 @@ class STStorageMatrixName(Enum):
INFLOWS = "inflows"


class DefaultSTStorageProperties(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel):
"""
Properties of a short-term storage system read from the configuration files.
@dataclass
class STStoragePropertiesUpdate:
group: Optional[STStorageGroup] = None
injection_nominal_capacity: Optional[float] = None
withdrawal_nominal_capacity: Optional[float] = None
reservoir_capacity: Optional[float] = None
efficiency: Optional[float] = None
initial_level: Optional[float] = None
initial_level_optim: Optional[bool] = None
enabled: Optional[bool] = None

All aliases match the name of the corresponding field in the INI files.
"""

@dataclass
class STStorageProperties:
group: STStorageGroup = STStorageGroup.OTHER1
injection_nominal_capacity: float = 0
withdrawal_nominal_capacity: float = 0
reservoir_capacity: float = 0
efficiency: float = 1
initial_level: float = 0.5
initial_level_optim: bool = False
# v880
enabled: bool = True


@all_optional_model
class STStorageProperties(DefaultSTStorageProperties):
pass


class STStoragePropertiesLocal(DefaultSTStorageProperties):
st_storage_name: str

@property
def list_ini_fields(self) -> dict[str, dict[str, str]]:
return {
f"{self.st_storage_name}": {
"name": self.st_storage_name,
"group": self.group.value,
"injectionnominalcapacity": f"{self.injection_nominal_capacity:.6f}",
"withdrawalnominalcapacity": f"{self.withdrawal_nominal_capacity:.6f}",
"reservoircapacity": f"{self.reservoir_capacity:.6f}",
"efficiency": f"{self.efficiency:.6f}",
"initiallevel": f"{self.initial_level:.6f}",
"initialleveloptim": f"{self.initial_level_optim}".lower(),
"enabled": f"{self.enabled}".lower(),
}
}

def yield_st_storage_properties(self) -> STStorageProperties:
excludes = {"st_storage_name", "list_ini_fields"}
return STStorageProperties.model_validate(self.model_dump(mode="json", exclude=excludes))


class STStorage:
def __init__(
self,
Expand All @@ -104,8 +78,6 @@ def __init__(
self._id: str = transform_name_to_id(name)
self._properties: STStorageProperties = properties or STStorageProperties()

# TODO: Add matrices.

@property
def area_id(self) -> str:
return self._area_id
Expand All @@ -122,7 +94,7 @@ def id(self) -> str:
def properties(self) -> STStorageProperties:
return self._properties

def update_properties(self, properties: STStorageProperties) -> None:
def update_properties(self, properties: STStoragePropertiesUpdate) -> None:
new_properties = self._storage_service.update_st_storage_properties(self, properties)
self._properties = new_properties

Expand Down
13 changes: 7 additions & 6 deletions src/antares/craft/service/api_services/area_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from antares.craft.model.st_storage import STStorage, STStorageProperties
from antares.craft.model.thermal import ThermalCluster, ThermalClusterProperties
from antares.craft.service.api_services.models.renewable import RenewableClusterPropertiesAPI
from antares.craft.service.api_services.models.st_storage import STStoragePropertiesAPI
from antares.craft.service.api_services.models.thermal import ThermalClusterPropertiesAPI
from antares.craft.service.api_services.utils import get_matrix, upload_series
from antares.craft.service.base_services import (
Expand Down Expand Up @@ -257,8 +258,7 @@ def create_renewable_cluster(
body = {**body, **camel_properties}
response = self._wrapper.post(url, json=body)
json_response = response.json()
name = json_response["name"]
del json_response["name"]
name = json_response.pop("name")
del json_response["id"]
api_properties = RenewableClusterPropertiesAPI.model_validate(json_response)
properties = api_properties.to_user_model()
Expand Down Expand Up @@ -294,14 +294,15 @@ def create_st_storage(
url = f"{self._base_url}/studies/{self.study_id}/areas/{area_id}/storages"
body = {"name": st_storage_name}
if properties:
camel_properties = properties.model_dump(mode="json", by_alias=True, exclude_none=True)
api_model = STStoragePropertiesAPI.from_user_model(properties)
camel_properties = api_model.model_dump(mode="json", by_alias=True, exclude_none=True)
body = {**body, **camel_properties}
response = self._wrapper.post(url, json=body)
json_response = response.json()
name = json_response["name"]
del json_response["name"]
name = json_response.pop("name")
del json_response["id"]
properties = STStorageProperties.model_validate(json_response)
api_properties = STStoragePropertiesAPI.model_validate(json_response)
properties = api_properties.to_user_model()

except APIError as e:
raise STStorageCreationError(st_storage_name, area_id, e.message) from e
Expand Down
48 changes: 48 additions & 0 deletions src/antares/craft/service/api_services/models/st_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 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 dataclasses import asdict
from typing import Union

from antares.craft.model.st_storage import STStorageGroup, STStorageProperties, STStoragePropertiesUpdate
from antares.craft.service.api_services.models.base_model import APIBaseModel
from antares.craft.tools.all_optional_meta import all_optional_model

STStoragePropertiesType = Union[STStorageProperties, STStoragePropertiesUpdate]


@all_optional_model
class STStoragePropertiesAPI(APIBaseModel):
group: STStorageGroup
injection_nominal_capacity: float
withdrawal_nominal_capacity: float
reservoir_capacity: float
efficiency: float
initial_level: float
initial_level_optim: bool
enabled: bool

@staticmethod
def from_user_model(user_class: STStoragePropertiesType) -> "STStoragePropertiesAPI":
user_dict = asdict(user_class)
return STStoragePropertiesAPI.model_validate(user_dict)

def to_user_model(self) -> STStorageProperties:
return STStorageProperties(
enabled=self.enabled,
group=self.group,
injection_nominal_capacity=self.injection_nominal_capacity,
withdrawal_nominal_capacity=self.withdrawal_nominal_capacity,
reservoir_capacity=self.reservoir_capacity,
efficiency=self.efficiency,
initial_level=self.initial_level,
initial_level_optim=self.initial_level_optim,
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@
STStorageMatrixUploadError,
STStoragePropertiesUpdateError,
)
from antares.craft.model.st_storage import STStorage, STStorageMatrixName, STStorageProperties
from antares.craft.model.st_storage import (
STStorage,
STStorageMatrixName,
STStorageProperties,
STStoragePropertiesUpdate,
)
from antares.craft.service.api_services.models.st_storage import STStoragePropertiesAPI
from antares.craft.service.base_services import BaseShortTermStorageService
from typing_extensions import override

Expand All @@ -36,19 +42,21 @@ def __init__(self, config: APIconf, study_id: str):

@override
def update_st_storage_properties(
self, st_storage: STStorage, properties: STStorageProperties
self, st_storage: STStorage, properties: STStoragePropertiesUpdate
) -> STStorageProperties:
url = f"{self._base_url}/studies/{self.study_id}/areas/{st_storage.area_id}/storages/{st_storage.id}"
try:
body = properties.model_dump(mode="json", by_alias=True, exclude_none=True)
api_model = STStoragePropertiesAPI.from_user_model(properties)
body = api_model.model_dump(mode="json", by_alias=True, exclude_none=True)
if not body:
return st_storage.properties

response = self._wrapper.patch(url, json=body)
json_response = response.json()
del json_response["id"]
del json_response["name"]
new_properties = STStorageProperties.model_validate(json_response)
new_api_properties = STStoragePropertiesAPI.model_validate(json_response)
new_properties = new_api_properties.to_user_model()

except APIError as e:
raise STStoragePropertiesUpdateError(st_storage.id, st_storage.area_id, e.message) from e
Expand Down Expand Up @@ -89,7 +97,8 @@ def read_st_storages(self, area_id: str) -> List[STStorage]:
storage_id = storage.pop("id")
storage_name = storage.pop("name")

storage_properties = STStorageProperties(**storage)
api_props = STStoragePropertiesAPI.model_validate(storage)
storage_properties = api_props.to_user_model()
st_storage = STStorage(self, storage_id, storage_name, storage_properties)
storages.append(st_storage)

Expand Down
9 changes: 7 additions & 2 deletions src/antares/craft/service/base_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@
RenewableClusterProperties,
RenewableClusterPropertiesUpdate,
)
from antares.craft.model.st_storage import STStorage, STStorageMatrixName, STStorageProperties
from antares.craft.model.st_storage import (
STStorage,
STStorageMatrixName,
STStorageProperties,
STStoragePropertiesUpdate,
)
from antares.craft.model.study import Study
from antares.craft.model.thermal import (
ThermalCluster,
Expand Down Expand Up @@ -700,7 +705,7 @@ def read_renewables(self, area_id: str) -> list["RenewableCluster"]:
class BaseShortTermStorageService(ABC):
@abstractmethod
def update_st_storage_properties(
self, st_storage: "STStorage", properties: "STStorageProperties"
self, st_storage: "STStorage", properties: "STStoragePropertiesUpdate"
) -> "STStorageProperties":
"""
Args:
Expand Down
11 changes: 6 additions & 5 deletions src/antares/craft/service/local_services/area_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
)
from antares.craft.model.hydro import Hydro, HydroProperties
from antares.craft.model.renewable import RenewableCluster, RenewableClusterProperties
from antares.craft.model.st_storage import STStorage, STStorageProperties, STStoragePropertiesLocal
from antares.craft.model.st_storage import STStorage, STStorageProperties
from antares.craft.model.thermal import ThermalCluster, ThermalClusterProperties
from antares.craft.service.base_services import (
BaseAreaService,
Expand All @@ -39,6 +39,7 @@
BaseThermalService,
)
from antares.craft.service.local_services.models.renewable import RenewableClusterPropertiesLocal
from antares.craft.service.local_services.models.st_storage import STStoragePropertiesLocal
from antares.craft.service.local_services.models.thermal import ThermalClusterPropertiesLocal
from antares.craft.service.local_services.services.hydro import edit_hydro_properties
from antares.craft.tools.contents_tool import transform_name_to_id
Expand Down Expand Up @@ -169,18 +170,18 @@ def create_st_storage(
self, area_id: str, st_storage_name: str, properties: Optional[STStorageProperties] = None
) -> STStorage:
properties = properties or STStorageProperties()
args = {"st_storage_name": st_storage_name, **properties.model_dump(mode="json", exclude_none=True)}
local_st_storage_properties = STStoragePropertiesLocal.model_validate(args)
local_properties = STStoragePropertiesLocal.from_user_model(properties)
new_section_content = {"name": st_storage_name, **local_properties.model_dump(mode="json", by_alias=True)}

list_ini = IniFile(self.config.study_path, InitializationFilesTypes.ST_STORAGE_LIST_INI, area_id=area_id)
list_ini.add_section(local_st_storage_properties.list_ini_fields)
list_ini.add_section({st_storage_name: new_section_content})
list_ini.write_ini_file(sort_sections=True)

return STStorage(
self.storage_service,
area_id,
st_storage_name,
local_st_storage_properties.yield_st_storage_properties(),
properties,
)

@override
Expand Down
52 changes: 52 additions & 0 deletions src/antares/craft/service/local_services/models/st_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 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 dataclasses import asdict
from typing import Union

from antares.craft.model.st_storage import STStorageGroup, STStorageProperties, STStoragePropertiesUpdate
from antares.craft.service.local_services.models.base_model import LocalBaseModel
from antares.craft.tools.all_optional_meta import all_optional_model

STStoragePropertiesType = Union[STStorageProperties, STStoragePropertiesUpdate]


def _sts_alias_generator(input: str) -> str:
return input.replace("_", "")


@all_optional_model
class STStoragePropertiesLocal(LocalBaseModel, alias_generator=_sts_alias_generator):
group: STStorageGroup = STStorageGroup.OTHER1
injection_nominal_capacity: float = 0
withdrawal_nominal_capacity: float = 0
reservoir_capacity: float = 0
efficiency: float = 1
initial_level: float = 0.5
initial_level_optim: bool = False
enabled: bool = True

@staticmethod
def from_user_model(user_class: STStoragePropertiesType) -> "STStoragePropertiesLocal":
user_dict = asdict(user_class)
return STStoragePropertiesLocal.model_validate(user_dict)

def to_user_model(self) -> STStorageProperties:
return STStorageProperties(
enabled=self.enabled,
group=self.group,
injection_nominal_capacity=self.injection_nominal_capacity,
withdrawal_nominal_capacity=self.withdrawal_nominal_capacity,
reservoir_capacity=self.reservoir_capacity,
efficiency=self.efficiency,
initial_level=self.initial_level,
initial_level_optim=self.initial_level_optim,
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
import pandas as pd

from antares.craft.config.local_configuration import LocalConfiguration
from antares.craft.model.st_storage import STStorage, STStorageMatrixName, STStorageProperties
from antares.craft.model.st_storage import (
STStorage,
STStorageMatrixName,
STStorageProperties,
STStoragePropertiesUpdate,
)
from antares.craft.service.base_services import BaseShortTermStorageService
from typing_extensions import override

Expand All @@ -28,7 +33,7 @@ def __init__(self, config: LocalConfiguration, study_name: str, **kwargs: Any) -

@override
def update_st_storage_properties(
self, st_storage: STStorage, properties: STStorageProperties
self, st_storage: STStorage, properties: STStoragePropertiesUpdate
) -> STStorageProperties:
raise NotImplementedError

Expand Down
4 changes: 2 additions & 2 deletions src/antares/craft/service/service_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
from antares.craft.service.api_services.services.renewable import RenewableApiService
from antares.craft.service.api_services.services.run import RunApiService
from antares.craft.service.api_services.services.settings import StudySettingsAPIService
from antares.craft.service.api_services.services.st_storage import ShortTermStorageApiService
from antares.craft.service.api_services.services.thermal import ThermalApiService
from antares.craft.service.api_services.st_storage_api import ShortTermStorageApiService
from antares.craft.service.api_services.study_api import StudyApiService
from antares.craft.service.base_services import (
BaseAreaService,
Expand All @@ -45,8 +45,8 @@
from antares.craft.service.local_services.services.renewable import RenewableLocalService
from antares.craft.service.local_services.services.run import RunLocalService
from antares.craft.service.local_services.services.settings import StudySettingsLocalService
from antares.craft.service.local_services.services.st_storage import ShortTermStorageLocalService
from antares.craft.service.local_services.services.thermal import ThermalLocalService
from antares.craft.service.local_services.st_storage_local import ShortTermStorageLocalService
from antares.craft.service.local_services.study_local import StudyLocalService

ERROR_MESSAGE = "Unsupported configuration type: "
Expand Down
Loading

0 comments on commit 29e89ae

Please sign in to comment.