Skip to content

Commit

Permalink
Added bindingconstraints.ini file is managed from the Study and has t…
Browse files Browse the repository at this point in the history
…he correct contents
  • Loading branch information
Sigurd-Borge committed Sep 13, 2024
1 parent 46214bd commit 39b07af
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 18 deletions.
73 changes: 72 additions & 1 deletion src/antares/model/binding_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
from typing import Optional, Union, List, Any, Dict

import pandas as pd
from pydantic import BaseModel, Field, model_validator
from pydantic import BaseModel, Field, model_validator, computed_field
from pydantic.alias_generators import to_camel

from antares.tools.contents_tool import EnumIgnoreCase, transform_name_to_id
from antares.tools.ini_tool import check_if_none

DEFAULT_GROUP = "default"

Expand Down Expand Up @@ -94,6 +95,65 @@ class BindingConstraintProperties(BaseModel, extra="forbid", populate_by_name=Tr
group: Optional[str] = None


class BindingConstraintPropertiesLocal(BaseModel):
"""
Used to create the entries for the bindingconstraints.ini file
Args:
constraint_name: The constraint name
constraint_id: The constraint id
properties (BindingConstraintProperties): The BindingConstraintProperties to set
"""

def __init__(
self,
constraint_name: str,
constraint_id: str,
properties: Optional[BindingConstraintProperties] = None,
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
properties = properties if properties is not None else BindingConstraintProperties()
self._constraint_name = constraint_name
self._constraint_id = constraint_id
self._enabled = check_if_none(properties.enabled, True)
self._time_step = check_if_none(properties.time_step, BindingConstraintFrequency.HOURLY)
self._operator = check_if_none(properties.operator, BindingConstraintOperator.LESS)
self._comments = properties.comments
self._filter_year_by_year = check_if_none(properties.filter_year_by_year, "hourly")
self._filter_synthesis = check_if_none(properties.filter_synthesis, "hourly")
self._group = properties.group

@computed_field # type: ignore[misc]
@property
def list_ini_fields(self) -> dict[str, str]:
ini_dict = {
"name": self._constraint_name,
"id": self._constraint_id,
"enabled": f"{self._enabled}".lower(),
"type": self._time_step.value,
"operator": self._operator.value,
"comments": self._comments,
"filter-year-by-year": self._filter_year_by_year,
"filter-synthesis": self._filter_synthesis,
"group": self._group,
}
return {key: value for key, value in ini_dict.items() if value is not None}

@computed_field # type: ignore[misc]
@property
def yield_binding_constraint_properties(self) -> BindingConstraintProperties:
return BindingConstraintProperties(
enabled=self._enabled,
time_step=self._time_step,
operator=self._operator,
comments=self._comments,
filter_year_by_year=self._filter_year_by_year,
filter_synthesis=self._filter_synthesis,
group=self._group,
)


class BindingConstraint:
def __init__( # type: ignore # TODO: Find a way to avoid circular imports
self,
Expand All @@ -107,6 +167,9 @@ def __init__( # type: ignore # TODO: Find a way to avoid circular imports
self._id = transform_name_to_id(name)
self._properties = properties or BindingConstraintProperties()
self._terms = {term.id: term for term in terms} if terms else {}
self._local_properties = BindingConstraintPropertiesLocal(
constraint_name=self._name, constraint_id=self._id, properties=properties
)

@property
def name(self) -> str:
Expand All @@ -120,6 +183,14 @@ def id(self) -> str:
def properties(self) -> BindingConstraintProperties:
return self._properties

@properties.setter
def properties(self, new_properties: BindingConstraintProperties) -> None:
self._properties = new_properties

@property
def local_properties(self) -> BindingConstraintPropertiesLocal:
return self._local_properties

def get_terms(self) -> Dict[str, ConstraintTerm]:
return self._terms

Expand Down
9 changes: 8 additions & 1 deletion src/antares/model/study.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,17 @@ def create_binding_constraint(
constraint = self._binding_constraints_service.create_binding_constraint(
name, properties, terms, less_term_matrix, equal_term_matrix, greater_term_matrix
)
self._binding_constraints[constraint.id] = constraint
if isinstance(self.service, StudyLocalService):
constraint.properties = constraint.local_properties.yield_binding_constraint_properties
binding_constraints_ini_content = {
idx: idx_constraint.local_properties.list_ini_fields
for idx, idx_constraint in enumerate((self._binding_constraints | {constraint.id: constraint}).values())
}

binding_constraints_ini = IniFile(self.service.config.study_path, IniFileTypes.BINDING_CONSTRAINTS_INI)
binding_constraints_ini.ini_dict = binding_constraints_ini_content
binding_constraints_ini.write_ini_file()
self._binding_constraints[constraint.id] = constraint
return constraint

def update_settings(self, settings: StudySettings) -> None:
Expand Down
15 changes: 0 additions & 15 deletions src/antares/service/local_services/binding_constraint_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
ConstraintTerm,
BindingConstraint,
ConstraintMatrixName,
BindingConstraintFrequency,
BindingConstraintOperator,
)
from antares.service.base_services import BaseBindingConstraintService

Expand All @@ -29,19 +27,6 @@ def create_binding_constraint(
equal_term_matrix: Optional[pd.DataFrame] = None,
greater_term_matrix: Optional[pd.DataFrame] = None,
) -> BindingConstraint:
properties = (
properties
if properties is not None
else BindingConstraintProperties(
enabled=True,
time_step=BindingConstraintFrequency.HOURLY,
operator=BindingConstraintOperator.LESS,
comments=None,
filter_year_by_year="hourly",
filter_synthesis="hourly",
group=None,
)
)
return BindingConstraint(
name=name,
binding_constraint_service=self,
Expand Down
80 changes: 79 additions & 1 deletion tests/antares/services/local_services/test_study.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
from antares.config.local_configuration import LocalConfiguration
from antares.exceptions.exceptions import CustomError, LinkCreationError
from antares.model.area import AreaProperties, AreaUi, AreaUiLocal, AreaPropertiesLocal, Area
from antares.model.binding_constraint import BindingConstraint
from antares.model.binding_constraint import (
BindingConstraint,
BindingConstraintProperties,
BindingConstraintFrequency,
BindingConstraintOperator,
)
from antares.model.commons import FilterOption
from antares.model.hydro import Hydro
from antares.model.link import (
Expand All @@ -28,6 +33,7 @@
from antares.service.local_services.renewable_local import RenewableLocalService
from antares.service.local_services.st_storage_local import ShortTermStorageLocalService
from antares.service.local_services.thermal_local import ThermalLocalService
from antares.tools.ini_tool import IniFileTypes


class TestCreateStudy:
Expand Down Expand Up @@ -1089,3 +1095,75 @@ def test_creating_constraints_creates_ini(self, local_study_with_constraint):
# Then
assert expected_ini_file_path.exists()
assert expected_ini_file_path.is_file()

def test_constraints_ini_have_correct_default_content(
self, local_study_with_constraint, test_constraint, default_constraint_properties
):
# Given
expected_ini_contents = """[0]
name = test constraint
id = test constraint
enabled = true
type = hourly
operator = less
filter-year-by-year = hourly
filter-synthesis = hourly
"""

# When
actual_ini_path = (
local_study_with_constraint.service.config.study_path / IniFileTypes.BINDING_CONSTRAINTS_INI.value
)
with actual_ini_path.open("r") as file:
actual_ini_content = file.read()

# Then
assert default_constraint_properties == test_constraint.properties
assert actual_ini_content == expected_ini_contents

def test_constraints_and_ini_have_custom_properties(self, local_study_with_constraint):
# Given
custom_constraint_properties = BindingConstraintProperties(
enabled=False,
time_step=BindingConstraintFrequency.WEEKLY,
operator=BindingConstraintOperator.BOTH,
comments="test comment",
filter_year_by_year="yearly",
filter_synthesis="monthly",
group="test group",
)
expected_ini_content = """[0]
name = test constraint
id = test constraint
enabled = true
type = hourly
operator = less
filter-year-by-year = hourly
filter-synthesis = hourly
[1]
name = test constraint two
id = test constraint two
enabled = false
type = weekly
operator = both
comments = test comment
filter-year-by-year = yearly
filter-synthesis = monthly
group = test group
"""

# When
local_study_with_constraint.create_binding_constraint(
name="test constraint two", properties=custom_constraint_properties
)
actual_file_path = (
local_study_with_constraint.service.config.study_path / IniFileTypes.BINDING_CONSTRAINTS_INI.value
)
with actual_file_path.open("r") as file:
actual_ini_content = file.read()

# Then
assert actual_ini_content == expected_ini_content

0 comments on commit 39b07af

Please sign in to comment.