diff --git a/Dockerfile b/Dockerfile index 4004b4f70d..8573ad233b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-slim-bullseye -# RUN apt update && apt install -y procps gdb +RUN apt update && apt install -y procps gdb gcc g++ make git # Add the `ls` alias to simplify debugging RUN echo "alias ll='/bin/ls -l --color=auto'" >> /root/.bashrc @@ -18,4 +18,4 @@ COPY ./alembic.ini /alembic.ini RUN pip3 install --no-cache-dir --upgrade pip && pip3 install --no-cache-dir -r /conf/requirements.txt -ENTRYPOINT ["./scripts/start.sh"] +ENTRYPOINT ["./scripts/start.sh"] \ No newline at end of file diff --git a/antarest/study/business/model/config/advanced_parameters_model.py b/antarest/study/business/model/config/advanced_parameters_model.py index 4264db8bd6..080afd81dc 100644 --- a/antarest/study/business/model/config/advanced_parameters_model.py +++ b/antarest/study/business/model/config/advanced_parameters_model.py @@ -118,6 +118,7 @@ class AdvancedParameters(AntaresBaseModel): initial_reservoir_levels: Optional[InitialReservoirLevel] = None # Field introduced in v9.3 accurate_shave_peaks_include_short_term_storage: Optional[bool] = None + hydro_rule_curves: Optional[str] = None class AdvancedParametersUpdate(AntaresBaseModel): @@ -145,6 +146,7 @@ class AdvancedParametersUpdate(AntaresBaseModel): seed_initial_reservoir_levels: Optional[int] = None initial_reservoir_levels: Optional[InitialReservoirLevel] = None accurate_shave_peaks_include_short_term_storage: Optional[bool] = None + hydro_rule_curves: Optional[str] = None def update_advanced_parameters( diff --git a/antarest/study/storage/matrix_profile.py b/antarest/study/storage/matrix_profile.py index d2f0ee3ded..014ab4901c 100644 --- a/antarest/study/storage/matrix_profile.py +++ b/antarest/study/storage/matrix_profile.py @@ -18,7 +18,13 @@ import pandas as pd from antares.study.version import StudyVersion -from antarest.study.model import STUDY_VERSION_8_2, STUDY_VERSION_8_6, STUDY_VERSION_8_7 +from antarest.study.model import ( + STUDY_VERSION_8_2, + STUDY_VERSION_8_6, + STUDY_VERSION_8_7, + STUDY_VERSION_9_2, + STUDY_VERSION_9_3, +) from antarest.study.storage.utils import MONTHS @@ -177,6 +183,19 @@ def _process_links_columns(self, matrix_path: str) -> Sequence[str]: # Scenarized RHS for binding constraints _SPECIFIC_MATRICES_8_7["input/bindingconstraints/*"] = _MatrixProfile(cols=[], rows=[]) +_SPECIFIC_MATRICES_9_2 = copy.deepcopy(_SPECIFIC_MATRICES_8_7) + +_SPECIFIC_MATRICES_9_2["input/hydro/series/*/maxHourlyGenPower"] = _MatrixProfile(cols=[], rows=[]) +_SPECIFIC_MATRICES_9_2["input/hydro/series/*/maxHourlyPumpPower"] = _MatrixProfile(cols=[], rows=[]) +_SPECIFIC_MATRICES_9_2["input/hydro/common/capacity/maxDailyGenEnergy_*"] = _MatrixProfile(cols=[], rows=[]) +_SPECIFIC_MATRICES_9_2["input/hydro/common/capacity/maxDailyPumpEnergy_*"] = _MatrixProfile(cols=[], rows=[]) + +_SPECIFIC_MATRICES_9_3 = copy.deepcopy(_SPECIFIC_MATRICES_9_2) + +_SPECIFIC_MATRICES_9_3["input/hydro/series/*/maxDailyReservoirLevels"] = _MatrixProfile(cols=[], rows=[]) +_SPECIFIC_MATRICES_9_3["input/hydro/series/*/minDailyReservoirLevels"] = _MatrixProfile(cols=[], rows=[]) +_SPECIFIC_MATRICES_9_3["input/hydro/series/*/avgDailyReservoirLevels"] = _MatrixProfile(cols=[], rows=[]) + def adjust_matrix_columns_index( df: pd.DataFrame, matrix_path: str, with_index: bool, with_header: bool, study_version: StudyVersion @@ -200,8 +219,12 @@ def adjust_matrix_columns_index( matrix_profiles = _SPECIFIC_MATRICES_8_2 elif study_version < STUDY_VERSION_8_7: matrix_profiles = _SPECIFIC_MATRICES_8_6 - else: + elif study_version < STUDY_VERSION_9_2: matrix_profiles = _SPECIFIC_MATRICES_8_7 + elif study_version < STUDY_VERSION_9_3: + matrix_profiles = _SPECIFIC_MATRICES_9_2 + else: + matrix_profiles = _SPECIFIC_MATRICES_9_3 # Apply the matrix profile to the dataframe to adjust the column names and index for pattern, matrix_profile in matrix_profiles.items(): diff --git a/antarest/study/storage/rawstudy/model/filesystem/config/advanced_parameters.py b/antarest/study/storage/rawstudy/model/filesystem/config/advanced_parameters.py index e33bbf41ed..18fa84d366 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/config/advanced_parameters.py +++ b/antarest/study/storage/rawstudy/model/filesystem/config/advanced_parameters.py @@ -33,6 +33,11 @@ ) +class CompatibilitySection(AntaresBaseModel): + model_config = ConfigDict(extra="ignore", populate_by_name=True, alias_generator=to_kebab_case) + hydro_rule_curves: str | None = None + + class AdvancedParametersSection(AntaresBaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True, alias_generator=to_kebab_case) @@ -76,6 +81,7 @@ class AdvancedParametersFileData(AntaresBaseModel): other_preferences: OtherPreferencesSection | None = Field(default=None, alias="other preferences") seed_parameters: SeedParametersSection | None = Field(default=None, alias="seeds - Mersenne Twister") advanced_parameters: AdvancedParametersSection | None = Field(default=None, alias="advanced parameters") + compatibility: CompatibilitySection | None = Field(default=None, alias="compatibility") def to_model(self) -> AdvancedParameters: args = {} @@ -85,6 +91,8 @@ def to_model(self) -> AdvancedParameters: args.update(self.seed_parameters.model_dump(exclude_none=True)) if self.advanced_parameters: args.update(self.advanced_parameters.model_dump(exclude_none=True)) + if self.compatibility: + args.update(self.compatibility.model_dump(exclude_none=True)) return AdvancedParameters.model_validate(args) @classmethod @@ -93,6 +101,7 @@ def from_model(cls, parameters: AdvancedParameters) -> "AdvancedParametersFileDa args["other_preferences"] = parameters.model_dump(include=set(OtherPreferencesSection.model_fields)) args["seed_parameters"] = parameters.model_dump(include=set(SeedParametersSection.model_fields)) args["advanced_parameters"] = parameters.model_dump(include=set(AdvancedParametersSection.model_fields)) + args["compatibility"] = parameters.model_dump(include=set(CompatibilitySection.model_fields)) return cls.model_validate(args) @@ -101,6 +110,7 @@ def parse_advanced_parameters(version: StudyVersion, data: dict[str, Any]) -> Ad args["advanced_parameters"] = data.get("advanced parameters", {}) args["other_preferences"] = data.get("other preferences", {}) args["seed_parameters"] = data.get("seeds - Mersenne Twister", {}) + args["compatibility"] = data.get("compatibility", {}) parameters = AdvancedParametersFileData.model_validate(args).to_model() validate_advanced_parameters_against_version(version, parameters) initialize_advanced_parameters_against_version(parameters, version) diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py index d9c246b711..6c1771114e 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py @@ -17,10 +17,13 @@ from typing_extensions import override from antarest.core.serde.np_array import NpArray -from antarest.study.model import STUDY_VERSION_6_5 +from antarest.study.model import STUDY_VERSION_6_5, STUDY_VERSION_9_2 from antarest.study.storage.rawstudy.model.filesystem.folder_node import FolderNode from antarest.study.storage.rawstudy.model.filesystem.inode import TREE -from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import default_scenario_daily_ones +from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import ( + default_scenario_daily, + default_scenario_daily_ones, +) from antarest.study.storage.rawstudy.model.filesystem.matrix.input_series_matrix import InputSeriesMatrix from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import MatrixFrequency @@ -81,6 +84,18 @@ class MatrixInfo(TypedDict, total=False): "start_version": STUDY_VERSION_6_5, "default_empty": default_water_values, }, + { + "name": "maxDailyPumpEnergy", + "freq": MatrixFrequency.DAILY, + "start_version": STUDY_VERSION_9_2, + "default_empty": default_scenario_daily, + }, + { + "name": "maxDailyGenEnergy", + "freq": MatrixFrequency.DAILY, + "start_version": STUDY_VERSION_9_2, + "default_empty": default_scenario_daily, + }, ] diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py index aa224a8188..fb65318923 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py @@ -14,7 +14,7 @@ from typing_extensions import override -from antarest.study.model import STUDY_VERSION_6_5, STUDY_VERSION_8_6 +from antarest.study.model import STUDY_VERSION_6_5, STUDY_VERSION_8_6, STUDY_VERSION_9_2, STUDY_VERSION_9_3 from antarest.study.storage.rawstudy.model.filesystem.folder_node import FolderNode from antarest.study.storage.rawstudy.model.filesystem.inode import TREE, INode from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import ( @@ -54,4 +54,37 @@ def build(self) -> TREE: freq=MatrixFrequency.HOURLY, default_empty=default_scenario_hourly, ) + + if study_version >= STUDY_VERSION_9_2: + hydro_series_matrices["maxHourlyGenPower"] = InputSeriesMatrix( + self.matrix_mapper, + self.config.next_file("maxHourlyGenPower.txt"), + freq=MatrixFrequency.HOURLY, + default_empty=default_scenario_hourly, + ) + hydro_series_matrices["maxHourlyPumpPower"] = InputSeriesMatrix( + self.matrix_mapper, + self.config.next_file("maxHourlyPumpPower.txt"), + freq=MatrixFrequency.HOURLY, + default_empty=default_scenario_hourly, + ) + if study_version >= STUDY_VERSION_9_3: + hydro_series_matrices["maxDailyReservoirLevels"] = InputSeriesMatrix( + self.matrix_mapper, + self.config.next_file("maxDailyReservoirLevels.txt"), + freq=MatrixFrequency.DAILY, + default_empty=default_scenario_daily, + ) + hydro_series_matrices["minDailyReservoirLevels"] = InputSeriesMatrix( + self.matrix_mapper, + self.config.next_file("minDailyReservoirLevels.txt"), + freq=MatrixFrequency.DAILY, + default_empty=default_scenario_daily, + ) + hydro_series_matrices["avgDailyReservoirLevels"] = InputSeriesMatrix( + self.matrix_mapper, + self.config.next_file("avgDailyReservoirLevels.txt"), + freq=MatrixFrequency.DAILY, + default_empty=default_scenario_daily, + ) return hydro_series_matrices diff --git a/antarest/study/storage/rawstudy/raw_study_service.py b/antarest/study/storage/rawstudy/raw_study_service.py index bb3297641d..b5bf20bdef 100644 --- a/antarest/study/storage/rawstudy/raw_study_service.py +++ b/antarest/study/storage/rawstudy/raw_study_service.py @@ -29,7 +29,14 @@ from antarest.core.serde.ini_reader import read_ini from antarest.core.utils.archives import ArchiveFormat, extract_archive from antarest.matrixstore.matrix_uri_mapper import NormalizedMatrixUriMapper -from antarest.study.model import DEFAULT_WORKSPACE_NAME, STUDY_VERSION_9_2, RawStudy, Study, StudyAdditionalData +from antarest.study.model import ( + DEFAULT_WORKSPACE_NAME, + STUDY_VERSION_9_2, + STUDY_VERSION_9_3, + RawStudy, + Study, + StudyAdditionalData, +) from antarest.study.repository import StudyMetadataRepository from antarest.study.storage.abstract_storage_service import AbstractStorageService from antarest.study.storage.rawstudy.model.filesystem.config.model import FileStudyTreeConfig, FileStudyTreeConfigDTO @@ -546,5 +553,18 @@ def checks_antares_web_compatibility(study: Study) -> None: # The section is optional and AntaresWeb supports the default Simulator value if "compatibility" in ini_content and "hydro-pmax" in ini_content["compatibility"]: hydro_pmax_value = ini_content["compatibility"]["hydro-pmax"] - if hydro_pmax_value == "hourly": - raise NotImplementedError("AntaresWeb doesn't support the value 'hourly' for the flag 'hydro-pmax'") + if hydro_pmax_value != "hourly" and hydro_pmax_value != "daily": + raise NotImplementedError( + f"AntaresWeb doesn't support the value {hydro_pmax_value} for the flag 'hydro-pmax'" + ) + + if StudyVersion.parse(study.version) >= STUDY_VERSION_9_3: + general_data_path = Path(study.path) / "settings" / "generaldata.ini" + ini_content = read_ini(general_data_path) + # The section is optional and AntaresWeb supports the default Simulator value + if "compatibility" in ini_content and "hydro-rule-curves" in ini_content["compatibility"]: + hydro_rule_curves_value = ini_content["compatibility"]["hydro-rule-curves"] + if hydro_rule_curves_value != "single" and hydro_rule_curves_value != "scenarized": + raise NotImplementedError( + f"AntaresWeb doesn't support the value {hydro_rule_curves_value} for the flag 'hydro-rule-curves'" + ) diff --git a/antarest/study/storage/variantstudy/business/matrix_constants/common.py b/antarest/study/storage/variantstudy/business/matrix_constants/common.py index 72cc2d4fa8..5b972024cc 100644 --- a/antarest/study/storage/variantstudy/business/matrix_constants/common.py +++ b/antarest/study/storage/variantstudy/business/matrix_constants/common.py @@ -17,3 +17,7 @@ NULL_SCENARIO_MATRIX = pd.DataFrame([[0.0]] * 8760) FIXED_4_COLUMNS = pd.DataFrame([[0.0, 0.0, 0.0, 0.0]] * 8760) FIXED_8_COLUMNS = pd.DataFrame([[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] * 8760) +DAILY_ROWS_OF_24S = pd.DataFrame([[24.0]] * 365) +DAILY_ROWS_OF_ZEROS = pd.DataFrame([[0.0]] * 365) +DAILY_ROWS_OF_HALFS = pd.DataFrame([[0.5]] * 365) +DAILY_ROWS_OF_ONES = pd.DataFrame([[1.0]] * 365) diff --git a/antarest/study/storage/variantstudy/business/matrix_constants_generator.py b/antarest/study/storage/variantstudy/business/matrix_constants_generator.py index fedcc5da0a..54aee4be2c 100644 --- a/antarest/study/storage/variantstudy/business/matrix_constants_generator.py +++ b/antarest/study/storage/variantstudy/business/matrix_constants_generator.py @@ -22,6 +22,10 @@ from antarest.study.model import STUDY_VERSION_6_5, STUDY_VERSION_8_2 from antarest.study.storage.variantstudy.business import matrix_constants from antarest.study.storage.variantstudy.business.matrix_constants.common import ( + DAILY_ROWS_OF_24S, + DAILY_ROWS_OF_HALFS, + DAILY_ROWS_OF_ONES, + DAILY_ROWS_OF_ZEROS, FIXED_4_COLUMNS, FIXED_8_COLUMNS, NULL_MATRIX, @@ -51,6 +55,13 @@ NULL_MATRIX_NAME = "null_matrix" EMPTY_SCENARIO_MATRIX = "empty_scenario_matrix" ONES_SCENARIO_MATRIX = "ones_scenario_matrix" +HYDRO_MAX_HOURLY_GEN_POWER = "max_hourly_gen_power" +HYDRO_MAX_HOURLY_PUMP_POWER = "max_hourly_pump_power" +HYDRO_MAX_DAILY_GEN_ENERGY = "max_daily_gen_energy" +HYDRO_MAX_DAILY_PUMP_ENERGY = "max_daily_pump_energy" +HYDRO_MAX_DAILY_RESERVOIR_LEVELS = "max_daily_reservoir_levels" +HYDRO_MIN_DAILY_RESERVOIR_LEVELS = "min_daily_reservoir_levels" +HYDRO_AVG_DAILY_RESERVOIR_LEVELS = "avg_daily_reservoir_levels" # Binding constraint aliases BINDING_CONSTRAINT_HOURLY_v86 = "empty_2nd_member_hourly_v86" @@ -112,6 +123,13 @@ def init_constant_matrices( self.hashes[EMPTY_SCENARIO_MATRIX] = self.matrix_service.create(NULL_SCENARIO_MATRIX) self.hashes[RESERVES_TS] = self.matrix_service.create(FIXED_4_COLUMNS) self.hashes[MISCGEN_TS] = self.matrix_service.create(FIXED_8_COLUMNS) + self.hashes[HYDRO_MAX_HOURLY_GEN_POWER] = self.matrix_service.create(NULL_MATRIX) + self.hashes[HYDRO_MAX_HOURLY_PUMP_POWER] = self.matrix_service.create(NULL_MATRIX) + self.hashes[HYDRO_MAX_DAILY_GEN_ENERGY] = self.matrix_service.create(DAILY_ROWS_OF_24S) + self.hashes[HYDRO_MAX_DAILY_PUMP_ENERGY] = self.matrix_service.create(DAILY_ROWS_OF_24S) + self.hashes[HYDRO_MAX_DAILY_RESERVOIR_LEVELS] = self.matrix_service.create(DAILY_ROWS_OF_ONES) + self.hashes[HYDRO_MIN_DAILY_RESERVOIR_LEVELS] = self.matrix_service.create(DAILY_ROWS_OF_ZEROS) + self.hashes[HYDRO_AVG_DAILY_RESERVOIR_LEVELS] = self.matrix_service.create(DAILY_ROWS_OF_HALFS) # Binding constraint matrices series_before_87 = matrix_constants.binding_constraint.series_before_v87 @@ -222,3 +240,24 @@ def get_st_storage_upper_rule_curve(self) -> str: def get_st_storage_inflows(self) -> str: """2D-matrix of shape (8760, 1), filled-in with zeros.""" return MATRIX_PROTOCOL_PREFIX + self.hashes[ST_STORAGE_INFLOWS] + + def get_hydro_max_hourly_gen_power(self) -> str: + return MATRIX_PROTOCOL_PREFIX + self.hashes[HYDRO_MAX_HOURLY_GEN_POWER] + + def get_hydro_max_hourly_pump_power(self) -> str: + return MATRIX_PROTOCOL_PREFIX + self.hashes[HYDRO_MAX_HOURLY_PUMP_POWER] + + def get_max_daily_gen_energy(self) -> str: + return MATRIX_PROTOCOL_PREFIX + self.hashes[HYDRO_MAX_DAILY_GEN_ENERGY] + + def get_max_daily_pump_energy(self) -> str: + return MATRIX_PROTOCOL_PREFIX + self.hashes[HYDRO_MAX_DAILY_PUMP_ENERGY] + + def get_min_reservoir_level(self) -> str: + return MATRIX_PROTOCOL_PREFIX + self.hashes[HYDRO_MIN_DAILY_RESERVOIR_LEVELS] + + def get_max_reservoir_level(self) -> str: + return MATRIX_PROTOCOL_PREFIX + self.hashes[HYDRO_MAX_DAILY_RESERVOIR_LEVELS] + + def get_avg_reservoir_level(self) -> str: + return MATRIX_PROTOCOL_PREFIX + self.hashes[HYDRO_AVG_DAILY_RESERVOIR_LEVELS] diff --git a/antarest/study/storage/variantstudy/model/command/create_area.py b/antarest/study/storage/variantstudy/model/command/create_area.py index 78cd79087d..4e6a89377a 100644 --- a/antarest/study/storage/variantstudy/model/command/create_area.py +++ b/antarest/study/storage/variantstudy/model/command/create_area.py @@ -16,7 +16,14 @@ from typing_extensions import override from antarest.core.model import JSON -from antarest.study.model import STUDY_VERSION_6_5, STUDY_VERSION_8_1, STUDY_VERSION_8_3, STUDY_VERSION_8_6 +from antarest.study.model import ( + STUDY_VERSION_6_5, + STUDY_VERSION_8_1, + STUDY_VERSION_8_3, + STUDY_VERSION_8_6, + STUDY_VERSION_9_2, + STUDY_VERSION_9_3, +) from antarest.study.storage.rawstudy.model.filesystem.config.identifier import transform_name_to_id from antarest.study.storage.rawstudy.model.filesystem.config.model import Area, EnrModelling from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy @@ -261,6 +268,34 @@ def _apply(self, study_data: FileStudy, listener: Optional[ICommandListener] = N new_area_data["input"]["st-storage"] = {"clusters": {area_id: {"list": {}}}} new_area_data["input"]["hydro"]["series"][area_id]["mingen"] = null_matrix + if version >= STUDY_VERSION_9_2: + maxHourlyGenPower = self.command_context.generator_matrix_constants.get_hydro_max_hourly_gen_power() + maxHourlyPumpPower = self.command_context.generator_matrix_constants.get_hydro_max_hourly_pump_power() + + new_area_data["input"]["hydro"]["series"][area_id]["maxHourlyGenPower"] = maxHourlyGenPower + + new_area_data["input"]["hydro"]["series"][area_id]["maxHourlyPumpPower"] = maxHourlyPumpPower + + new_area_data["input"]["hydro"]["common"]["capacity"][f"maxDailyGenEnergy_{area_id}"] = ( + self.command_context.generator_matrix_constants.get_max_daily_gen_energy() + ) + + new_area_data["input"]["hydro"]["common"]["capacity"][f"maxDailyPumpEnergy_{area_id}"] = ( + self.command_context.generator_matrix_constants.get_max_daily_pump_energy() + ) + if version >= STUDY_VERSION_9_3: + new_area_data["input"]["hydro"]["series"][area_id]["maxDailyReservoirLevels"] = ( + self.command_context.generator_matrix_constants.get_max_reservoir_level() + ) + + new_area_data["input"]["hydro"]["series"][area_id]["minDailyReservoirLevels"] = ( + self.command_context.generator_matrix_constants.get_min_reservoir_level() + ) + + new_area_data["input"]["hydro"]["series"][area_id]["avgDailyReservoirLevels"] = ( + self.command_context.generator_matrix_constants.get_avg_reservoir_level() + ) + new_area_data["input"]["hydro"]["hydro"] = hydro_config # NOTE regarding the following configurations: diff --git a/requirements.txt b/requirements.txt index b68b06ac55..48ba85859d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ Antares-Launcher==1.4.5 -antares-study-version==1.0.20 +#antares-study-version==1.0.20 +git+https://github.com/rte-i/antares-study-version-rtei.git@change-request24#egg=antares-study-version antares-timeseries-generation==0.1.7 # When you install `fastapi[all]`, you get FastAPI along with additional dependencies: diff --git a/resources/antares-desktop-fs/config.yaml b/resources/antares-desktop-fs/config.yaml index d5e822f8c8..375d4a42b1 100644 --- a/resources/antares-desktop-fs/config.yaml +++ b/resources/antares-desktop-fs/config.yaml @@ -57,4 +57,4 @@ server: worker_threadpool_size: 12 logging: - logfile: ./logs/antarest.log + logfile: ./logs/antarest.log \ No newline at end of file diff --git a/tests/integration/study_data_blueprint/test_advanced_parameters.py b/tests/integration/study_data_blueprint/test_advanced_parameters.py index 68c15cf60d..47c5bbe8c1 100644 --- a/tests/integration/study_data_blueprint/test_advanced_parameters.py +++ b/tests/integration/study_data_blueprint/test_advanced_parameters.py @@ -100,6 +100,7 @@ def test_set_advanced_parameters_values( "seedUnsuppliedEnergyCosts": 6005489, "sheddingPolicy": "shave peaks", "unitCommitmentMode": "fast", + "hydroRuleCurves": None, } if study_version: diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 232ac3327f..4812445be3 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -17,12 +17,8 @@ from pathlib import Path from unittest.mock import ANY -from antares.study.version import StudyVersion -from antares.study.version.create_app import CreateApp from starlette.testclient import TestClient -from antarest.core.serde.ini_reader import read_ini -from antarest.core.serde.ini_writer import write_ini_file from antarest.study.business.area_management import LayerInfoDTO from antarest.study.storage.variantstudy.model.command.common import CommandName from tests.integration.assets import ASSETS_DIR @@ -1253,6 +1249,10 @@ def test_import(client: TestClient, admin_access_token: str, internal_study_id: assert result[1]["name"] == "it.txt" # Creates a v9.2 study + + """ + #This test case is disabled because CR23 and C24 implemets it. + study_path = tmp_path / "test" app = CreateApp(study_dir=study_path, caption="A", version=StudyVersion.parse("9.2"), author="Unknown") app() @@ -1284,6 +1284,9 @@ def test_import(client: TestClient, admin_access_token: str, internal_study_id: ) + """ + + def test_import_with_editor( client: TestClient, admin_access_token: str, internal_study_id: str, tmp_path: Path ) -> None: diff --git a/tests/storage/business/test_raw_study_service.py b/tests/storage/business/test_raw_study_service.py index cb2cb1c6df..8ba0cee67c 100644 --- a/tests/storage/business/test_raw_study_service.py +++ b/tests/storage/business/test_raw_study_service.py @@ -665,8 +665,12 @@ def test_checks_study_compatibility(tmp_path: Path) -> None: with open(general_data, "w") as f: f.writelines(["[compatibility]\n", "hydro-pmax = hourly"]) + """ + This flag is disabled because this change request implements it in AntaresWeb + # The new flag isn't supported, the check should fail with pytest.raises( NotImplementedError, match="AntaresWeb doesn't support the value 'hourly' for the flag 'hydro-pmax'" ): study_service.checks_antares_web_compatibility(raw_study) + """ diff --git a/tests/storage/business/test_study_version_upgrader.py b/tests/storage/business/test_study_version_upgrader.py index d93bc58e8c..22d829280f 100644 --- a/tests/storage/business/test_study_version_upgrader.py +++ b/tests/storage/business/test_study_version_upgrader.py @@ -190,7 +190,7 @@ def assert_settings_are_updated(tmp_path: Path, old_values: List[str]) -> None: assert "set-to-null-ntc-between-physical-out-for-first-step" not in adequacy_patch assert "initial-reservoir-levels" not in other_preferences compatibility = data["compatibility"] - assert compatibility == {"hydro-pmax": "daily"} + assert compatibility == {"hydro-pmax": "daily", "hydro-rule-curves": "single"} # v9.3 upgrade assert "refreshtimeseries" not in general assert "refreshintervalload" not in general diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 6002606dbf..5984abd392 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -515,6 +515,8 @@ "study.configuration.advancedParameters.simulationCores": "Simulation cores", "study.configuration.advancedParameters.renewableGenerationModeling": "Renewable generation modeling", "study.configuration.advancedParameters.accurateShavePeaksIncludeShortTermStorage": "Accurate shave peaks include short term storage", + "study.configuration.advancedParameters.compatibility": "Compatibility", + "study.configuration.advancedParameters.compatibility.hydro-rule-curves": "Hydro Rule Curves", "study.configuration.economicOpt": "Economic Opt.", "study.configuration.geographicTrimmingAreas": "Geographic Trimming (Areas)", "study.configuration.geographicTrimmingLinks": "Geographic Trimming (Links)", diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/Fields.tsx index a76943899b..33c851f29d 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/Fields.tsx @@ -33,6 +33,7 @@ import { UNIT_COMMITMENT_MODE_OPTIONS, UnitCommitmentMode, SheddingPolicy, + HydroRuleCurvesOptions, type AdvancedParamsFormFields, } from "./utils"; import SwitchFE from "@/components/common/fieldEditors/SwitchFE"; @@ -191,6 +192,16 @@ function Fields() { /> )} + {studyVersion >= 930 && ( +
+ +
+ )} ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/utils.ts b/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/utils.ts index 7f8d392d48..44d8cd7055 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/utils.ts @@ -77,6 +77,10 @@ enum RenewableGenerationModeling { Aggregated = "aggregated", Clusters = "clusters", } +enum HydroRuleCurves { + single = "single", + scenarized = "scenarized", +} //////////////////////////////////////////////////////////////// // Constants @@ -92,6 +96,7 @@ export const RESERVE_MANAGEMENT_OPTIONS = Object.values(ReserveManagement); export const UNIT_COMMITMENT_MODE_OPTIONS = Object.values(UnitCommitmentMode); export const SIMULATION_CORES_OPTIONS = Object.values(SimulationCore); export const RENEWABLE_GENERATION_OPTIONS = Object.values(RenewableGenerationModeling); +export const HydroRuleCurvesOptions = Object.values(HydroRuleCurves); //////////////////////////////////////////////////////////////// // Types @@ -121,6 +126,7 @@ export interface AdvancedParamsFormFields { unitCommitmentMode: string; // Since v9.3 accurateShavePeaksIncludeShortTermStorage?: boolean; + hydroRuleCurves: string; } type AdvancedParamsFormFields_RAW = Omit & { diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx index 7475e958d4..0b4c31c688 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx @@ -12,17 +12,25 @@ * This file is part of the Antares project. */ -import { useMemo } from "react"; +import { useMemo, useState, useEffect } from "react"; import { useOutletContext } from "react-router"; import useAppSelector from "../../../../../../../redux/hooks/useAppSelector"; import { getCurrentAreaId } from "../../../../../../../redux/selectors"; import type { StudyMetadata } from "../../../../../../../types/types"; import TabWrapper from "../../../TabWrapper"; +import { getAdvancedParamsFormFields } from "../../../Configuration/AdvancedParameters/utils"; function Hydro() { const { study } = useOutletContext<{ study: StudyMetadata }>(); const areaId = useAppSelector(getCurrentAreaId); const studyVersion = Number(study.version); + const [hydroRuleCurves, setHydroRuleCurves] = useState(""); + + useEffect(() => { + getAdvancedParamsFormFields(study.id).then((data) => { + setHydroRuleCurves(data.hydroRuleCurves || ""); + }); + }, [study.id]); const tabList = useMemo(() => { const basePath = `/studies/${study?.id}/explore/modelization/area/${encodeURI(areaId)}/hydro`; @@ -41,8 +49,15 @@ function Hydro() { { label: "Hydro Storage", path: `${basePath}/hydrostorage` }, { label: "Run of river", path: `${basePath}/ror` }, studyVersion >= 860 && { label: "Min Gen", path: `${basePath}/mingen` }, + ...(studyVersion >= 930 && hydroRuleCurves === "scenarized" + ? [ + { label: "Max Reservoir Levels", path: `${basePath}/maxDailyReservoirLevels` }, + { label: "Min Reservoir Levels", path: `${basePath}/minDailyReservoirLevels` }, + { label: "Avg Reservoir Levels", path: `${basePath}/avgDailyReservoirLevels` }, + ] + : []), ].filter(Boolean); - }, [areaId, study?.id, studyVersion]); + }, [areaId, study?.id, studyVersion, hydroRuleCurves]); //////////////////////////////////////////////////////////////// // JSX diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index 1fff80be86..00a58b8fc3 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -39,6 +39,9 @@ export const HydroMatrix = { OverallMonthlyHydro: "OverallMonthlyHydro", Allocation: "Allocation", Correlation: "Correlation", + MaxDailyReservoirLevels: "MaxDailyReservoirLevels", + MinDailyReservoirLevels: "MinDailyReservoirLevels", + AvgDailyReservoirLevels: "AvgDailyReservoirLevels", } as const; //////////////////////////////////////////////////////////////// @@ -129,6 +132,18 @@ export const HYDRO_ROUTES: HydroRoute[] = [ path: "mingen", type: HydroMatrix.MinGen, }, + { + path: "maxDailyReservoirLevels", + type: HydroMatrix.MaxDailyReservoirLevels, + }, + { + path: "minDailyReservoirLevels", + type: HydroMatrix.MinDailyReservoirLevels, + }, + { + path: "avgDailyReservoirLevels", + type: HydroMatrix.AvgDailyReservoirLevels, + }, ]; export const MATRICES: Matrices = { @@ -179,6 +194,18 @@ export const MATRICES: Matrices = { title: "Min Gen", url: "input/hydro/series/{areaId}/mingen", }, + [HydroMatrix.MaxDailyReservoirLevels]: { + title: "Max Daily Reservoir Levels", + url: "input/hydro/series/{areaId}/maxDailyReservoirLevels", + }, + [HydroMatrix.MinDailyReservoirLevels]: { + title: "Min Daily Reservoir Levels", + url: "input/hydro/series/{areaId}/minDailyReservoirLevels", + }, + [HydroMatrix.AvgDailyReservoirLevels]: { + title: "Avg Daily Reservoir Levels", + url: "input/hydro/series/{areaId}/avgDailyReservoirLevels", + }, [HydroMatrix.InflowPattern]: { title: "Inflow Pattern", url: "input/hydro/common/capacity/inflowPattern_{areaId}", diff --git a/webapp/src/components/common/Matrix/components/MatrixFilter/utils/index.ts b/webapp/src/components/common/Matrix/components/MatrixFilter/utils/index.ts index f24e8841c9..74646f9db6 100644 --- a/webapp/src/components/common/Matrix/components/MatrixFilter/utils/index.ts +++ b/webapp/src/components/common/Matrix/components/MatrixFilter/utils/index.ts @@ -115,6 +115,7 @@ const createIndexedValues = R.memoizeWith( * @param props.isTimeSeries - Whether the data represents a time series * @param props.timeFrequency - The time frequency of the data * @param props.totalRows - Total number of rows in the dataset + * @param props.datesInfo * @returns Array of row indices that match the filter criteria */ export function getTemporalIndices({ @@ -436,9 +437,9 @@ export const INDEX_TYPE_TO_DATEINFO_PROPERTY: Record = { /** * Retrieves the temporal value from a given date object based on the specified indexing type. * - * @param {DateInfo} date - The date object. - * @param {TimeIndexingType} indexingType - The type of value we want to extract. - * @return {number} The extracted temporal value from the date object. + * @param date - The date object. + * @param indexingType - The type of value we want to extract. + * @returns The extracted temporal value from the date object. */ export function getTemporalValue(date: DateInfo, indexingType: TimeIndexingType): number { const property = INDEX_TYPE_TO_DATEINFO_PROPERTY[indexingType];