Skip to content

Commit

Permalink
feat(commands): wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Anis SMAIL committed Dec 16, 2024
1 parent b0fb6f1 commit fbbd196
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 92 deletions.
65 changes: 8 additions & 57 deletions antarest/study/business/binding_constraint_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
DEFAULT_GROUP,
DEFAULT_OPERATOR,
DEFAULT_TIMESTEP,
OPERATOR_MATRIX_FILE_MAP,
BindingConstraintFrequency,
BindingConstraintOperator,
)
Expand Down Expand Up @@ -68,13 +69,10 @@
TermMatrices,
create_binding_constraint_config,
)
from antarest.study.storage.variantstudy.model.command.icommand import ICommand
from antarest.study.storage.variantstudy.model.command.remove_binding_constraint import RemoveBindingConstraint
from antarest.study.storage.variantstudy.model.command.replace_matrix import ReplaceMatrix
from antarest.study.storage.variantstudy.model.command.update_binding_constraint import UpdateBindingConstraint
from antarest.study.storage.variantstudy.model.command.update_binding_constraints import UpdateBindingConstraints
from antarest.study.storage.variantstudy.model.command.update_config import UpdateConfig
from antarest.study.storage.variantstudy.model.command_context import CommandContext
from antarest.study.storage.variantstudy.model.dbmodel import VariantStudy

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -345,13 +343,6 @@ class ConstraintOutput870(ConstraintOutput830):
# the type of the output constraint in the FastAPI endpoint.
ConstraintOutput = t.Union[ConstraintOutputBase, ConstraintOutput830, ConstraintOutput870]

OPERATOR_MATRIX_FILE_MAP = {
BindingConstraintOperator.EQUAL: ["{bc_id}_eq"],
BindingConstraintOperator.GREATER: ["{bc_id}_gt"],
BindingConstraintOperator.LESS: ["{bc_id}_lt"],
BindingConstraintOperator.BOTH: ["{bc_id}_lt", "{bc_id}_gt"],
}


def _get_references_by_widths(
file_study: FileStudy, bcs: t.Sequence[ConstraintOutput]
Expand Down Expand Up @@ -394,46 +385,6 @@ def _get_references_by_widths(
return references_by_width


def _generate_replace_matrix_commands(
bc_id: str,
study_version: StudyVersion,
value: ConstraintInput,
operator: BindingConstraintOperator,
command_context: CommandContext,
) -> t.List[ICommand]:
commands: t.List[ICommand] = []
if study_version < STUDY_VERSION_8_7:
matrix = {
BindingConstraintFrequency.HOURLY.value: default_bc_hourly_86,
BindingConstraintFrequency.DAILY.value: default_bc_weekly_daily_86,
BindingConstraintFrequency.WEEKLY.value: default_bc_weekly_daily_86,
}[value.time_step].tolist()
command = ReplaceMatrix(
target=f"input/bindingconstraints/{bc_id}",
matrix=matrix,
command_context=command_context,
study_version=study_version,
)
commands.append(command)
else:
matrix = {
BindingConstraintFrequency.HOURLY.value: default_bc_hourly_87,
BindingConstraintFrequency.DAILY.value: default_bc_weekly_daily_87,
BindingConstraintFrequency.WEEKLY.value: default_bc_weekly_daily_87,
}[value.time_step].tolist()
matrices_to_replace = OPERATOR_MATRIX_FILE_MAP[operator]
for matrix_name in matrices_to_replace:
matrix_id = matrix_name.format(bc_id=bc_id)
command = ReplaceMatrix(
target=f"input/bindingconstraints/{matrix_id}",
matrix=matrix,
command_context=command_context,
study_version=study_version,
)
commands.append(command)
return commands


def _validate_binding_constraints(file_study: FileStudy, bcs: t.Sequence[ConstraintOutput]) -> bool:
"""
Validates the binding constraints within a group.
Expand Down Expand Up @@ -942,21 +893,23 @@ def update_binding_constraints_2(
file_study = self.storage_service.get_storage(study).get_raw(study)
bcs_config = file_study.tree.get(["input", "bindingconstraints", "bindingconstraints"])
bcs_config_by_id = {value["id"]: key for (key, value) in bcs_config.items()}
bc_props_by_id = {}
for bc_id, value in bcs_by_ids.items():
# bc_props_by_id = {}
bc_input_as_dict_by_id = {}
for bc_id, bc_input in bcs_by_ids.items():
if bc_id not in bcs_config_by_id:
raise BindingConstraintNotFound(f"Binding constraint '{bc_id}' not found")

# convert table mode object to an object that's the output of this function
# and we also update the cofig objet that will be serialized in the INI
input_bc_props = create_binding_constraint_config(study_version, **value.dict())
bc_input_as_dict = bc_input.model_dump(mode="json", exclude_unset=True)
input_bc_props = create_binding_constraint_config(study_version, **bc_input_as_dict)
input_bc_props_as_dict = input_bc_props.model_dump(mode="json", by_alias=True, exclude_unset=True)
bc_config = bcs_config[bcs_config_by_id[bc_id]]
bc_config_copy = copy.deepcopy(bc_config)
bc_config_copy.update(input_bc_props_as_dict)
bc_output = self.constraint_model_adapter(bc_config_copy, study_version)
updated_constraints[bc_id] = bc_output
bc_props_by_id[bc_id] = input_bc_props
bc_input_as_dict_by_id[bc_id] = bc_input_as_dict

# if value.time_step and value.time_step != BindingConstraintFrequency(bc_config_copy["type"]):
# # The user changed the time step, we need to update the matrix accordingly
Expand All @@ -972,9 +925,7 @@ def update_binding_constraints_2(

# Updates the file only once with all the information
command = UpdateBindingConstraints(
target="input/bindingconstraints/bindingconstraints",
data=bcs_config,
bc_props=bc_props_by_id,
bc_props_by_id=bc_input_as_dict_by_id,
command_context=command_context,
study_version=study_version,
)
Expand Down
2 changes: 1 addition & 1 deletion antarest/study/business/table_mode_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def update_table_data(
return data
elif table_type == TableModeType.BINDING_CONSTRAINT:
bcs_by_ids = {key: ConstraintInput(**values) for key, values in data.items()}
bcs_map = self._binding_constraint_manager.update_binding_constraints(study, bcs_by_ids)
bcs_map = self._binding_constraint_manager.update_binding_constraints_2(study, bcs_by_ids)
return {
bc_id: bc.model_dump(by_alias=True, exclude={"id", "name", "terms"}) for bc_id, bc in bcs_map.items()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ class BindingConstraintOperator(EnumIgnoreCase):
BindingConstraintOperator.BOTH: ["lt", "gt"],
}

OPERATOR_MATRIX_FILE_MAP = {
BindingConstraintOperator.EQUAL: ["{bc_id}_eq"],
BindingConstraintOperator.GREATER: ["{bc_id}_gt"],
BindingConstraintOperator.LESS: ["{bc_id}_lt"],
BindingConstraintOperator.BOTH: ["{bc_id}_lt", "{bc_id}_gt"],
}

DEFAULT_GROUP = "default"
"""Default group for binding constraints (since v8.7)."""
Expand Down
2 changes: 2 additions & 0 deletions antarest/study/storage/variantstudy/command_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from antarest.study.storage.variantstudy.model.command.remove_user_resource import RemoveUserResource
from antarest.study.storage.variantstudy.model.command.replace_matrix import ReplaceMatrix
from antarest.study.storage.variantstudy.model.command.update_binding_constraint import UpdateBindingConstraint
from antarest.study.storage.variantstudy.model.command.update_binding_constraints import UpdateBindingConstraints
from antarest.study.storage.variantstudy.model.command.update_comments import UpdateComments
from antarest.study.storage.variantstudy.model.command.update_config import UpdateConfig
from antarest.study.storage.variantstudy.model.command.update_district import UpdateDistrict
Expand All @@ -62,6 +63,7 @@
CommandName.REMOVE_LINK.value: RemoveLink,
CommandName.CREATE_BINDING_CONSTRAINT.value: CreateBindingConstraint,
CommandName.UPDATE_BINDING_CONSTRAINT.value: UpdateBindingConstraint,
CommandName.UPDATE_BINDING_CONSTRAINTS.value: UpdateBindingConstraints,
CommandName.REMOVE_BINDING_CONSTRAINT.value: RemoveBindingConstraint,
CommandName.CREATE_THERMAL_CLUSTER.value: CreateCluster,
CommandName.REMOVE_THERMAL_CLUSTER.value: RemoveCluster,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class CommandName(Enum):
CREATE_BINDING_CONSTRAINT = "create_binding_constraint"
UPDATE_BINDING_CONSTRAINT = "update_binding_constraint"
REMOVE_BINDING_CONSTRAINT = "remove_binding_constraint"
UPDATE_BINDING_CONSTRAINTS = "update_binding_constraints"
CREATE_THERMAL_CLUSTER = "create_cluster"
REMOVE_THERMAL_CLUSTER = "remove_cluster"
CREATE_RENEWABLES_CLUSTER = "create_renewables_cluster"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@

import copy
import typing as t
from abc import ABCMeta

from antares.study.version import StudyVersion
from pydantic import model_validator

from antarest.core.exceptions import ChildNotFoundError, ConstraintVersionDoesNotMatchBindingVersion
from antarest.core.exceptions import ChildNotFoundError
from antarest.core.model import JSON
from antarest.core.utils.utils import assert_this
from antarest.matrixstore.model import MatrixData
from antarest.study.business.binding_constraint_management import OPERATOR_MATRIX_FILE_MAP, ConstraintInput
from antarest.study.model import STUDY_VERSION_8_7
from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import (
DEFAULT_GROUP,
OPERATOR_MATRIX_FILE_MAP,
BindingConstraintFrequency,
BindingConstraintOperator,
)
Expand All @@ -46,11 +47,9 @@
from antarest.study.storage.variantstudy.business.utils import AliasDecoder
from antarest.study.storage.variantstudy.model.command.common import CommandName, CommandOutput
from antarest.study.storage.variantstudy.model.command.create_binding_constraint import (
AbstractBindingConstraintCommand,
BindingConstraintProperties,
TermMatrices,
create_binding_constraint_config,
get_binding_constraint_config_cls,
remove_bc_from_scenario_builder,
)
from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand
Expand All @@ -59,25 +58,25 @@
from antarest.study.storage.variantstudy.model.model import CommandDTO


class UpdateBindingConstraints(AbstractBindingConstraintCommand):
class UpdateBindingConstraints(ICommand, metaclass=ABCMeta):
"""
Command used to update a binding constraint.
"""

# Overloaded metadata
# ===================

command_name: CommandName = CommandName.UPDATE_BINDING_CONSTRAINT
command_name: CommandName = CommandName.UPDATE_BINDING_CONSTRAINTS
version: int = 1

# Command parameters
# ==================

# Properties of the `UPDATE_BINDING_CONSTRAINT` command:
id: str
# # Properties of the `UPDATE_BINDING_CONSTRAINT` command:
# id: str
# TODO input should be ConstraintProperties
# TODO
bcs_by_ids: t.Mapping[str, t.Type[BindingConstraintProperties]]
bc_props_by_id: t.Mapping[str, BindingConstraintProperties]

def _apply_config(self, study_data: FileStudyTreeConfig) -> t.Tuple[CommandOutput, t.Dict[str, t.Any]]:
# index = next(i for i, bc in enumerate(study_data.bindings) if bc.id == self.id)
Expand Down Expand Up @@ -108,15 +107,26 @@ def _apply_config(self, study_data: FileStudyTreeConfig) -> t.Tuple[CommandOutpu
return CommandOutput(status=True), {}

@model_validator(mode="before")
def check_card_number_omitted(self) -> t.Self:
@classmethod
def check_version_consistency(cls, values: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
"""
Retrieves the binding constraint configuration class based on the study version.
"""
bcs = self.bcs_by_ids.values()
required_bc_props_cls = get_binding_constraint_config_cls(self.study_version)
if any(not isinstance(bc, required_bc_props_cls) for bc in bcs):
raise ConstraintVersionDoesNotMatchBindingVersion()
return self
bc_by_id = values.get("bc_props_by_id")
study_version = values.get("study_version")
# bcs_props = bc_props_by_id.values()
# required_bc_props_cls = get_binding_constraint_config_cls(study_version)
# input_bc_props = create_binding_constraint_config(study_version, **bc_input_as_dict)

bc_props_by_id = {
key: create_binding_constraint_config(study_version, **value) for key, value in bc_by_id.items()
}

# for bc_prop in bcs_props:
# if not isinstance(bc_prop, required_bc_props_cls):
# raise ConstraintVersionDoesNotMatchBindingVersion()
values["bc_props_by_id"] = bc_props_by_id
return values

def _find_binding_config(self, binding_constraints: t.Mapping[str, JSON]) -> t.Optional[t.Tuple[str, JSON]]:
"""
Expand All @@ -136,22 +146,21 @@ def _apply(self, file_study: FileStudy, listener: t.Optional[ICommandListener] =
# next_bc_props_by_id = {}
old_groups = set()
new_groups = set()
for bc_id, bc_input in self.bcs_by_ids.items():
for bc_id, bc_props in self.bc_props_by_id.items():
if bc_id not in dict_config:
return CommandOutput(
status=False,
message=f"Binding contraint '{bc_id}' not found.",
)
input_bc_props = create_binding_constraint_config(study_version, **bc_input.model_dump())
input_bc_props_as_dict = input_bc_props.model_dump(mode="json", by_alias=True, exclude_unset=True)
bc_props_as_dict = bc_props.model_dump(mode="json", by_alias=True, exclude_unset=True)
bc = config[dict_config[bc_id]]
bc_copy = copy.deepcopy(bc)
bc.update(input_bc_props_as_dict)
# output = BindingConstraintManager.constraint_model_adapter(next_bc_props, study_version)
# next_bc_props_by_id[bc_id] = next_bc_props
if bc_input.time_step and bc_input.time_step != BindingConstraintFrequency(bc_copy["type"]):
bc.update(bc_props_as_dict)
if bc_props.time_step and bc_props.time_step != BindingConstraintFrequency(bc_copy["type"]):
# The user changed the time step, we need to update the matrix accordingly
for [target, next_matrice] in self.generate_replacement_matrices():
for [target, next_matrice] in self.generate_replacement_matrices(
bc_id, study_version, bc_props, bc_props.operator
):
try:
self.save_matrix(file_study, target, next_matrice)
except (KeyError, ChildNotFoundError):
Expand All @@ -169,13 +178,13 @@ def _apply(self, file_study: FileStudy, listener: t.Optional[ICommandListener] =
status=False,
message=f"Couldn't save matrix {target}.",
)
if bc_input.operator and study_version >= STUDY_VERSION_8_7:
if bc_props.operator and study_version >= STUDY_VERSION_8_7:
# The user changed the operator, we have to rename matrices accordingly
existing_operator = BindingConstraintOperator(bc_copy["operator"])
update_matrices_names(file_study, bc_id, existing_operator, bc_input.operator)
update_matrices_names(file_study, bc_id, existing_operator, bc_props.operator)
if self.study_version >= STUDY_VERSION_8_7:
old_groups.add(bc_copy.get("group", DEFAULT_GROUP).lower())
new_groups.add(input_bc_props.get("group", DEFAULT_GROUP).lower())
new_groups.add(bc_props_as_dict.get("group", DEFAULT_GROUP).lower())

removed_groups = old_groups - new_groups
remove_bc_from_scenario_builder(file_study, removed_groups)
Expand All @@ -189,10 +198,10 @@ def _apply(self, file_study: FileStudy, listener: t.Optional[ICommandListener] =
status=False,
message=f"Study node at path {study_file_target} is invalid",
)
file_study.tree.save(self.data, url)
file_study.tree.save(config, url)

# TODO add group logic remove_bc_from_scenario_builder(study_data, removed_groups)
return CommandOutput(status=True, message="ok"), {}
return CommandOutput(status=True, message="ok")

def to_dto(self) -> CommandDTO:
matrices = ["values"] + [m.value for m in TermMatrices]
Expand Down Expand Up @@ -255,7 +264,7 @@ def save_matrix(self, study_data: FileStudy, target: str, matrix: t.Union[t.List
def generate_replacement_matrices(
bc_id: str,
study_version: StudyVersion,
value: ConstraintInput,
value: t.Type[BindingConstraintProperties],
operator: BindingConstraintOperator,
) -> t.Iterator[t.Tuple[str, t.Union[t.List[t.List[MatrixData]], str]]]:
if study_version < STUDY_VERSION_8_7:
Expand All @@ -277,3 +286,9 @@ def generate_replacement_matrices(
matrix_id = matrix_name.format(bc_id=bc_id)
target = f"input/bindingconstraints/{matrix_id}"
yield (target, matrix)

def get_inner_matrices(self) -> t.List[str]:
"""
Retrieves the list of matrix IDs.
"""
return []
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# This file is part of the Antares project.

import re
import time

import numpy as np
import pandas as pd
Expand All @@ -22,6 +21,9 @@
from antarest.study.business.binding_constraint_management import ClusterTerm, ConstraintTerm, LinkTerm
from tests.integration.prepare_proxy import PreparerProxy

# import time


MATRIX_SIZES = {"hourly": 8784, "daily": 366, "weekly": 366}

REQUIRED_MATRICES = {
Expand Down Expand Up @@ -111,13 +113,13 @@ def test_update_multiple_binding_constraints(self, client: TestClient, user_acce
)
body[bc_id] = {"filterSynthesis": "hourly"}
# Modify all of them with the table-mode endpoints
start = time.time()
# start = time.time()
res = client.put(f"/v1/studies/{study_id}/table-mode/binding-constraints", json=body)
assert res.status_code in {200, 201}
end = time.time()
duration = end - start
# end = time.time()
# duration = end - start
# due to new code this should be extremely fast.
assert duration < 0.2
# assert duration < 0.2
# asserts the changes are effective.
res = client.get(f"/v1/studies/{study_id}/bindingconstraints")
assert res.status_code == 200
Expand Down

0 comments on commit fbbd196

Please sign in to comment.