Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contrib: Add renewable conversion to yaml compnent #59

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions src/andromede/input_converter/src/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
from andromede.input_converter.src.utils import (
convert_area_to_component_list,
resolve_path,
convert_renewable_to_component_list,
)

from andromede.study.parsing import InputStudy


Expand All @@ -29,21 +31,25 @@ def __init__(self, study_path: Optional[Path]):
Initialize processor
"""
self.study_path = resolve_path(study_path) if study_path else None
self.study: Study = read_study_local(self.study_path) if self.study_path else None # type: ignore
self.study: Study = (
read_study_local(self.study_path) if self.study_path else None # type: ignore
)

def convert_study_to_input_study(self) -> InputStudy:
areas = self.study.read_areas()
area_components = convert_area_to_component_list(areas)
return InputStudy(nodes=area_components)

def validate_with_pydantic(
self, data: dict, model_class: type[BaseModel]
) -> BaseModel:
return model_class(**data)

def transform_to_yaml(self, data: dict, output_path: str) -> None:
with open(output_path, "w") as yaml_file:
yaml.dump(data, yaml_file)
root_path = self.study.service.config.study_path # type: ignore
renewable_components = convert_renewable_to_component_list(areas, root_path)
return InputStudy(nodes=area_components, components=renewable_components)

@staticmethod
def transform_to_yaml(model: BaseModel, output_path: str) -> None:
with open(output_path, "w", encoding="utf-8") as yaml_file:
yaml.dump(
{"study": model.model_dump(by_alias=True, exclude_unset=True)},
yaml_file,
allow_unicode=True,
)

def process_all(self) -> None:
raise NotImplementedError
46 changes: 44 additions & 2 deletions src/andromede/input_converter/src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,50 @@ def convert_area_to_component_list(areas: list[Area]) -> list[InputComponent]:
return [InputComponent(id=area.id, model="area") for area in areas]


def convert_renewable_to_component_list(areas: list[Area]) -> list[InputComponent]:
raise NotImplementedError
def convert_renewable_to_component_list(
areas: list[Area], root_path: Path
) -> list[InputComponent]:
components = []
for area in areas:
renewables = area.read_renewables()
for renewable in renewables:
series_path = (
root_path
/ "input"
/ "renewables"
/ "series"
/ Path(area.id)
/ Path(renewable.id)
/ "series.txt"
)
components.extend(
[
InputComponent(
id=renewable.id,
model="renewable",
parameters=[
InputComponentParameter(
name="unit_count",
type="constant",
value=renewable.properties.unit_count,
),
InputComponentParameter(
name="nominal_capacity",
type="constant",
value=renewable.properties.nominal_capacity,
),
InputComponentParameter(
name=renewable.id,
type="timeseries",
timeseries=str(series_path),
),
],
)
for renewable in renewables
]
)

return components


def convert_hydro_to_component_list(area: Area) -> list[InputComponent]:
Expand Down
16 changes: 13 additions & 3 deletions tests/input_converter/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# This file is part of the Antares project.
import pandas as pd
import pytest
from antares.craft.model.area import Area
from antares.craft.model.area import Area, AreaPropertiesLocal
from antares.craft.model.binding_constraint import (
BindingConstraint,
BindingConstraintFrequency,
Expand Down Expand Up @@ -47,7 +47,10 @@ def local_study(tmp_path) -> Study:
def local_study_w_areas(tmp_path, local_study) -> Study:
areas_to_create = ["fr", "it"]
for area in areas_to_create:
local_study.create_area(area)
area_properties = AreaPropertiesLocal(
energy_cost_spilled="1.0", energy_cost_unsupplied="1.5"
)
local_study.create_area(area, properties=area_properties)
return local_study


Expand Down Expand Up @@ -138,8 +141,15 @@ def actual_adequacy_patch_ini(local_study_w_areas) -> IniFile:
@pytest.fixture
def local_study_with_renewable(local_study_w_thermal) -> Study:
renewable_cluster_name = "renewable cluster"
time_serie = pd.DataFrame(
[
[-9999999980506447872, 0, 9999999980506447872],
[0, "fr", 0],
],
dtype="object",
)
local_study_w_thermal.get_areas()["fr"].create_renewable_cluster(
renewable_cluster_name, RenewableClusterProperties(), None
renewable_cluster_name, RenewableClusterProperties(), series=time_serie
)
return local_study_w_thermal

Expand Down
83 changes: 78 additions & 5 deletions tests/input_converter/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@
#
# This file is part of the Antares project.


from andromede.input_converter.src.converter import AntaresStudyConverter
from andromede.study.parsing import InputComponent, InputStudy
from andromede.input_converter.src.utils import (
convert_area_to_component_list,
convert_renewable_to_component_list,
)
from andromede.study.parsing import InputComponent, InputStudy, parse_yaml_components


class TestConverter:
def test_convert_area_to_input_study(self, local_study_w_areas):
converter = AntaresStudyConverter(study_path=None)
converter.study = local_study_w_areas
area_components = converter.convert_study_to_input_study()
areas = converter.study.read_areas()
area_components = convert_area_to_component_list(areas)
input_study = InputStudy(nodes=area_components)
expected_area_components = InputStudy(
nodes=[
InputComponent(id="fr", model="area", parameters=None),
Expand All @@ -31,7 +36,75 @@ def test_convert_area_to_input_study(self, local_study_w_areas):
# To ensure that the comparison between the actual and expected results is not affected by the order of the nodes,
# both the area_components.nodes and expected_area_components.nodes lists are sorted by the id attribute of each node.
# This sorting step ensures that the test checks only the presence and validity of the nodes, not their order.
area_components.nodes.sort(key=lambda x: x.id)
input_study.nodes.sort(key=lambda x: x.id)
expected_area_components.nodes.sort(key=lambda x: x.id)

assert input_study == expected_area_components

def test_convert_area_to_yaml(self, local_study_w_areas):
converter = AntaresStudyConverter(study_path=None)

converter.study = local_study_w_areas
areas = converter.study.read_areas()
area_components = convert_area_to_component_list(areas)
input_study = InputStudy(nodes=area_components)

# Dump model into yaml file
yaml_path = local_study_w_areas.service.config.study_path / "study_path.yaml"
converter.transform_to_yaml(model=input_study, output_path=yaml_path)

# Open yaml file to validate
with open(yaml_path, "r") as yaml_file:
validated_data = parse_yaml_components(yaml_file)

assert isinstance(
validated_data, InputStudy
), "Validated datas don't match InputStudy model"
assert len(validated_data.nodes) == 2, "Yaml file must contain 2 nodes"

def test_convert_renewables_to_input_study(self, local_study_with_renewable):
converter = AntaresStudyConverter(study_path=None)

converter.study = local_study_with_renewable
areas = converter.study.read_areas()
area_components = convert_area_to_component_list(areas)
study_path = local_study_with_renewable.service.config.study_path

renewables_components = convert_renewable_to_component_list(areas, study_path)

input_study = InputStudy(
nodes=area_components, components=renewables_components
)
expected_area_components = InputStudy(
nodes=[
InputComponent(id="fr", model="area", parameters=None),
InputComponent(id="it", model="area", parameters=None),
InputComponent(id="at", model="area", parameters=None),
],
components=[],
connections=[],
)
timeserie_path = str(
study_path
/ "input"
/ "renewables"
/ "series"
/ "fr"
/ "renewable cluster"
/ "series.txt"
)

assert renewables_components[0].id == "renewable cluster"
assert renewables_components[0].parameters[0].name == "unit_count"
assert renewables_components[0].parameters[1].name == "nominal_capacity"
assert renewables_components[0].parameters[1].type == "constant"
assert renewables_components[0].parameters[2].name == "renewable cluster"
assert renewables_components[0].parameters[2].type == "timeseries"
assert renewables_components[0].parameters[2].timeseries == timeserie_path
# To ensure that the comparison between the actual and expected results is not affected by the order of the nodes,
# both the area_components.nodes and expected_area_components.nodes lists are sorted by the id attribute of each node.
# This sorting step ensures that the test checks only the presence and validity of the nodes, not their order.
input_study.nodes.sort(key=lambda x: x.id)
expected_area_components.nodes.sort(key=lambda x: x.id)

assert area_components == expected_area_components
assert input_study.nodes == expected_area_components.nodes
Loading