From 8830197ca27690ef808b640cfda9bb548534a225 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 16:07:42 +0100 Subject: [PATCH 01/69] start work --- src/antares/craft/model/settings/general.py | 126 ++++++++++++-------- 1 file changed, 75 insertions(+), 51 deletions(-) diff --git a/src/antares/craft/model/settings/general.py b/src/antares/craft/model/settings/general.py index 7871c45e..feb05638 100644 --- a/src/antares/craft/model/settings/general.py +++ b/src/antares/craft/model/settings/general.py @@ -9,6 +9,8 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import dataclass +from typing import Optional from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase @@ -69,69 +71,91 @@ class OutputFormat(EnumIgnoreCase): ZIP = "zip-files" -class DefaultGeneralParameters(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): +@dataclass +class GeneralParameters: + mode: Optional[Mode] = None + horizon: Optional[str] = None + nb_years: Optional[int] = None + simulation_start: Optional[int] = None + simulation_end: Optional[int] = None + january_first: Optional[WeekDay] = None + first_month_in_year: Optional[Month] = None + first_week_day: Optional[WeekDay] = None + leap_year: Optional[bool] = None + year_by_year: Optional[bool] = None + simulation_synthesis: Optional[bool] = None + building_mode: Optional[BuildingMode] = None + user_playlist: Optional[bool] = None + thematic_trimming: Optional[bool] = None + geographic_trimming: Optional[bool] = None + nb_timeseries_thermal: Optional[int] = None + + +@all_optional_model +class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): model_config = ConfigDict(use_enum_values=True) mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) horizon: str = "" - # Calendar parameters nb_years: int = 1 first_day: int = 1 last_day: int = 365 - first_january: WeekDay = Field(default=WeekDay.MONDAY, validate_default=True) - first_month: Month = Field(default=Month.JANUARY, validate_default=True) - first_week_day: WeekDay = Field(default=WeekDay.MONDAY, validate_default=True) + first_january: WeekDay = WeekDay.MONDAY + first_month: Month = Month.JANUARY + first_week_day: WeekDay = WeekDay.MONDAY leap_year: bool = False - # Additional parameters year_by_year: bool = False - building_mode: BuildingMode = Field( - default=BuildingMode.AUTOMATIC, validate_default=True - ) # ? derated and custom-scenario - selection_mode: bool = False # ? user-playlist + building_mode: BuildingMode = BuildingMode.AUTOMATIC + selection_mode: bool = False thematic_trimming: bool = False geographic_trimming: bool = False - active_rules_scenario: str = "default ruleset" # only one option available currently + active_rules_scenario: str = "default ruleset" read_only: bool = False - # Output parameters - simulation_synthesis: bool = True # ? output/synthesis - mc_scenario: bool = False # ? output/storenewset + simulation_synthesis: bool = True + mc_scenario: bool = False result_format: OutputFormat = Field(default=OutputFormat.TXT, exclude=True) -@all_optional_model -class GeneralParameters(DefaultGeneralParameters): - pass - - -class GeneralParametersLocal(DefaultGeneralParameters): - @property - def ini_fields(self) -> dict: - return { - "general": { - "mode": str(self.mode).title(), - "horizon": self.horizon, - "nbyears": str(self.nb_years), - "simulation.start": str(self.first_day), - "simulation.end": str(self.last_day), - "january.1st": str(self.first_january).title(), - "first-month-in-year": str(self.first_month).title(), - "first.weekday": str(self.first_week_day).title(), - "leapyear": str(self.leap_year).lower(), - "year-by-year": str(self.year_by_year).lower(), - "derated": str(self.building_mode == BuildingMode.DERATED).lower(), - "custom-scenario": str(self.building_mode == BuildingMode.CUSTOM).lower(), - "user-playlist": str(self.selection_mode).lower(), - "thematic-trimming": str(self.thematic_trimming).lower(), - "geographic-trimming": str(self.geographic_trimming).lower(), - "readonly": str(self.read_only).lower(), - }, - "input": {}, - "output": { - "synthesis": str(self.simulation_synthesis).lower(), - "storenewset": str(self.mc_scenario).lower(), - "result-format": self.result_format.value, - }, - } - - def yield_properties(self) -> GeneralParameters: - return GeneralParameters.model_validate(self.model_dump(exclude_none=True)) +class GeneralSectionLocal(BaseModel): + mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) + horizon: str = "" + nb_years: int = Field(default=1, alias="nb.years") + simulation_start: int = Field(default=1, alias="simulation.start") + simulation_end: int = Field(default=365, alias="simulation.end") + first_january: WeekDay = Field(default=WeekDay.MONDAY, alias="january.1st") + first_month: Month = Field(default=Month.JANUARY, alias="first-month-in-year") + first_week_day: WeekDay = Field(default=WeekDay.MONDAY, alias="first.weekday") + leap_year: bool = Field(default=False, alias="leapyear") + year_by_year: bool = Field(default=False, alias="year-by-year") + derated: bool = False + custom_scenario: bool = Field(default=False, alias="custom-scenario") + user_playlist: bool = Field(default=False, alias="user-playlist") + thematic_trimming: bool = Field(default=False, alias="thematic-trimming") + geographic_trimming: bool = Field(default=False, alias="geographic-trimming") + generate: bool = False + nb_timeseries_load: int = Field(default=1, alias="nbtimeseriesload") + nb_timeseries_hydro: int = Field(default=1, alias="nbtimeserieshydro") + nb_timeseries_wind: int = Field(default=1, alias="nbtimeserieswind") + nb_timeseries_thermal: int = Field(default=1, alias="nbtimeseriesthermal") + nb_timeseries_solar: int = Field(default=1, alias="nbtimeseriessolar") + refresh_timeseries: bool = Field(default=False, alias="refreshtimeseries") + intra_model: bool = Field(default=False, alias="intra-model") + inter_model: bool = Field(default=False, alias="inter-model") + refresh_interval_load: int = Field(default=100, alias="refreshintervalload") + refresh_interval_hydro: int = Field(default=100, alias="refreshintervalhydro") + refresh_interval_wind: int = Field(default=100, alias="refreshintervalwind") + refresh_interval_thermal: int = Field(default=100, alias="refreshintervalthermal") + refresh_interval_solar: int = Field(default=100, alias="refreshintervalsolar") + read_only: bool = Field(default=False, alias="readonly") + + +class OutputSectionLocal(BaseModel): + synthesis: bool = True + store_new_set: bool = Field(default=True, alias="storenewset") + archives: OutputFormat = Field(default=OutputFormat.TXT, exclude=True) + + +class GeneralParametersLocal(BaseModel): + general: GeneralSectionLocal + input: dict = {"input": ""} + output: OutputSectionLocal From cb5f31022cab56286a1df6d52b3368b28afef7bc Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 16:12:05 +0100 Subject: [PATCH 02/69] remove ts inside settings --- .../craft/model/settings/study_settings.py | 14 +-- .../craft/model/settings/time_series.py | 104 ------------------ src/antares/craft/model/study.py | 9 +- .../craft/service/api_services/study_api.py | 2 - 4 files changed, 4 insertions(+), 125 deletions(-) delete mode 100644 src/antares/craft/model/settings/time_series.py diff --git a/src/antares/craft/model/settings/study_settings.py b/src/antares/craft/model/settings/study_settings.py index d243f942..17c2c50f 100644 --- a/src/antares/craft/model/settings/study_settings.py +++ b/src/antares/craft/model/settings/study_settings.py @@ -13,27 +13,20 @@ from antares.craft.model.settings.adequacy_patch import AdequacyPatchParametersLocal, DefaultAdequacyPatchParameters from antares.craft.model.settings.advanced_parameters import AdvancedParametersLocal, DefaultAdvancedParameters -from antares.craft.model.settings.general import DefaultGeneralParameters, GeneralParametersLocal +from antares.craft.model.settings.general import GeneralParameters from antares.craft.model.settings.optimization import DefaultOptimizationParameters, OptimizationParametersLocal from antares.craft.model.settings.playlist_parameters import PlaylistParameters from antares.craft.model.settings.thematic_trimming import ( DefaultThematicTrimmingParameters, ThematicTrimmingParametersLocal, ) -from antares.craft.model.settings.time_series import DefaultTimeSeriesParameters, TimeSeriesParametersLocal from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.ini_tool import get_ini_fields_for_ini from pydantic import BaseModel, model_serializer class DefaultStudySettings(BaseModel): - general_parameters: DefaultGeneralParameters = DefaultGeneralParameters() - # These parameters are listed under the [variables selection] section in the .ini file. - # They are required if thematic-trimming is set to true. - # https://antares-simulator.readthedocs.io/en/latest/user-guide/solver/04-parameters/#variables-selection-parameters - time_series_parameters: DefaultTimeSeriesParameters = DefaultTimeSeriesParameters() - # These parameters are listed under the [general] section in the .ini file. - # https://antares-simulator.readthedocs.io/en/latest/user-guide/ts-generator/04-parameters/ + general_parameters: GeneralParameters = GeneralParameters() optimization_parameters: DefaultOptimizationParameters = DefaultOptimizationParameters() adequacy_patch_parameters: DefaultAdequacyPatchParameters = DefaultAdequacyPatchParameters() advanced_parameters: DefaultAdvancedParameters = DefaultAdvancedParameters() @@ -47,8 +40,7 @@ class StudySettings(DefaultStudySettings): class StudySettingsLocal(DefaultStudySettings): - general_parameters: GeneralParametersLocal = GeneralParametersLocal() - time_series_parameters: TimeSeriesParametersLocal = TimeSeriesParametersLocal() + general_parameters: GeneralParameters = GeneralParameters() optimization_parameters: OptimizationParametersLocal = OptimizationParametersLocal() adequacy_patch_parameters: AdequacyPatchParametersLocal = AdequacyPatchParametersLocal() advanced_parameters: AdvancedParametersLocal = AdvancedParametersLocal() diff --git a/src/antares/craft/model/settings/time_series.py b/src/antares/craft/model/settings/time_series.py deleted file mode 100644 index 19381518..00000000 --- a/src/antares/craft/model/settings/time_series.py +++ /dev/null @@ -1,104 +0,0 @@ -# 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 enum import Enum -from typing import Optional - -from antares.craft.tools.all_optional_meta import all_optional_model -from antares.craft.tools.model_tools import filter_out_empty_model_fields -from pydantic import BaseModel, Field -from pydantic.alias_generators import to_camel - - -class SeasonCorrelation(Enum): - MONTHLY = "monthly" - ANNUAL = "annual" - - -class _DefaultParameters(BaseModel, alias_generator=to_camel): - stochastic_ts_status: bool = False - number: int = 1 - refresh: bool = False - refresh_interval: int = 100 - season_correlation: SeasonCorrelation = SeasonCorrelation.ANNUAL - store_in_input: bool = False - store_in_output: bool = False - intra_modal: bool = False - inter_modal: bool = False - - -@all_optional_model -class _Parameters(_DefaultParameters): - pass - - -class _ParametersLocal(_DefaultParameters, populate_by_name=True): - field_name: str = Field(exclude=True) - - -class DefaultTimeSeriesParameters(BaseModel, alias_generator=to_camel): - load: _DefaultParameters = _DefaultParameters() - hydro: _DefaultParameters = _DefaultParameters() - thermal: _DefaultParameters = _DefaultParameters() - wind: _DefaultParameters = _DefaultParameters() - solar: _DefaultParameters = _DefaultParameters() - renewables: Optional[_DefaultParameters] = None - ntc: Optional[_DefaultParameters] = None - - -@all_optional_model -class TimeSeriesParameters(DefaultTimeSeriesParameters): - pass - - -class TimeSeriesParametersLocal(DefaultTimeSeriesParameters): - load: _ParametersLocal = _ParametersLocal(field_name="load") - hydro: _ParametersLocal = _ParametersLocal(field_name="hydro") - thermal: _ParametersLocal = _ParametersLocal(field_name="thermal") - wind: _ParametersLocal = _ParametersLocal(field_name="wind") - solar: _ParametersLocal = _ParametersLocal(field_name="solar") - renewables: Optional[_ParametersLocal] = None - ntc: Optional[_ParametersLocal] = None - - @property - def ini_fields(self) -> dict: - fields_to_check = filter_out_empty_model_fields(self) - general_dict = {} - general_dict["generate"] = ", ".join(self._make_list_from_fields("stochastic_ts_status")) - general_dict |= { - "nbtimeseries" + field_to_add: str(getattr(self, field_to_add).number) for field_to_add in fields_to_check - } - general_dict["refreshtimeseries"] = ", ".join(self._make_list_from_fields("refresh")) - general_dict["intra-modal"] = ", ".join(self._make_list_from_fields("intra_modal")) - general_dict["inter-modal"] = ", ".join(self._make_list_from_fields("inter_modal")) - general_dict |= { - "refreshinterval" + field_to_add: str(getattr(self, field_to_add).refresh_interval) - for field_to_add in fields_to_check - } - input_dict = {"import": ", ".join(self._make_list_from_fields("store_in_input"))} - output_dict = {"archives": ", ".join(self._make_list_from_fields("store_in_output"))} - return {"general": general_dict, "input": input_dict, "output": output_dict} - - def _make_list_from_fields(self, field_to_check: str) -> list: - fields_to_check = filter_out_empty_model_fields(self) - return [ - field_to_add for field_to_add in fields_to_check if getattr(getattr(self, field_to_add), field_to_check) - ] - - -def correlation_defaults(season_correlation: SeasonCorrelation) -> dict[str, dict[str, str]]: - general_section = {"general": {"mode": season_correlation.value}} - annual_section: dict[str, dict] = {"annual": {}} if season_correlation.value == "annual" else {} - extra_sections: dict[str, dict] = ( - {f"{num}": {} for num in range(12)} if season_correlation.value == "annual" else {} - ) - return general_section | annual_section | extra_sections diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 43ad1140..85c33d9f 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -33,7 +33,6 @@ from antares.craft.model.link import Link, LinkProperties, LinkUi from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import DefaultStudySettings, StudySettings, StudySettingsLocal -from antares.craft.model.settings.time_series import correlation_defaults from antares.craft.model.simulation import AntaresSimulationParameters, Job from antares.craft.service.api_services.study_api import _returns_study_settings from antares.craft.service.base_services import BaseStudyService @@ -487,11 +486,5 @@ def _create_correlation_ini_files(local_settings: StudySettingsLocal, study_dire ] for correlation, file_type, field in correlation_inis_to_create: - ini_file = IniFile( - study_directory, - file_type, - ini_contents=correlation_defaults( - season_correlation=getattr(local_settings.time_series_parameters, field).season_correlation, - ), - ) + ini_file = IniFile(study_directory, file_type, ini_contents=None) ini_file.write_ini_file() diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 2a29ef43..2f3d2772 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -38,7 +38,6 @@ from antares.craft.model.settings.playlist_parameters import PlaylistData, PlaylistParameters from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters -from antares.craft.model.settings.time_series import TimeSeriesParameters from antares.craft.service.api_services.utils import wait_task_completion from antares.craft.service.base_services import BaseOutputService, BaseStudyService @@ -53,7 +52,6 @@ def _returns_study_settings( mapping = { "general_parameters": ("general", GeneralParameters), "thematic_trimming_parameters": ("thematictrimming", ThematicTrimmingParameters), - "time_series_parameters": ("timeseries", TimeSeriesParameters), "adequacy_patch_parameters": ("adequacypatch", AdequacyPatchParameters), "advanced_parameters": ("advancedparameters", AdvancedParameters), "optimization_parameters": ("optimization", OptimizationParameters), From 5a51bd405dbdb364b54991faae8eba66d49d8815 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 16:47:57 +0100 Subject: [PATCH 03/69] continue work --- src/antares/craft/model/settings/general.py | 47 ++++---- .../craft/model/settings/optimization.py | 109 +++++++++--------- .../craft/model/settings/study_settings.py | 6 +- 3 files changed, 82 insertions(+), 80 deletions(-) diff --git a/src/antares/craft/model/settings/general.py b/src/antares/craft/model/settings/general.py index feb05638..9d19af13 100644 --- a/src/antares/craft/model/settings/general.py +++ b/src/antares/craft/model/settings/general.py @@ -14,7 +14,7 @@ from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, Field from pydantic.alias_generators import to_camel @@ -93,27 +93,25 @@ class GeneralParameters: @all_optional_model class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): - model_config = ConfigDict(use_enum_values=True) - mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) - horizon: str = "" - nb_years: int = 1 - first_day: int = 1 - last_day: int = 365 - first_january: WeekDay = WeekDay.MONDAY - first_month: Month = Month.JANUARY - first_week_day: WeekDay = WeekDay.MONDAY - leap_year: bool = False - year_by_year: bool = False - building_mode: BuildingMode = BuildingMode.AUTOMATIC - selection_mode: bool = False - thematic_trimming: bool = False - geographic_trimming: bool = False - active_rules_scenario: str = "default ruleset" - read_only: bool = False - simulation_synthesis: bool = True - mc_scenario: bool = False - result_format: OutputFormat = Field(default=OutputFormat.TXT, exclude=True) + horizon: str + nb_years: int + first_day: int + last_day: int + first_january: WeekDay + first_month: Month + first_week_day: WeekDay + leap_year: bool + year_by_year: bool + building_mode: BuildingMode + selection_mode: bool + thematic_trimming: bool + geographic_trimming: bool + active_rules_scenario: str + read_only: bool + simulation_synthesis: bool + mc_scenario: bool + result_format: OutputFormat class GeneralSectionLocal(BaseModel): @@ -155,7 +153,12 @@ class OutputSectionLocal(BaseModel): archives: OutputFormat = Field(default=OutputFormat.TXT, exclude=True) -class GeneralParametersLocal(BaseModel): +class GeneralParametersLocalCreation(BaseModel): general: GeneralSectionLocal input: dict = {"input": ""} output: OutputSectionLocal + + +@all_optional_model +class GeneralParametersLocalEdition(GeneralParametersLocalCreation): + pass diff --git a/src/antares/craft/model/settings/optimization.py b/src/antares/craft/model/settings/optimization.py index 8154786f..cd0f2d62 100644 --- a/src/antares/craft/model/settings/optimization.py +++ b/src/antares/craft/model/settings/optimization.py @@ -9,19 +9,16 @@ # 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 Union +from typing import Optional +from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel from pydantic.alias_generators import to_camel -class LegacyTransmissionCapacities(Enum): - INFINITE = "infinite" - - class OptimizationTransmissionCapacities(Enum): LOCAL_VALUES = "local-values" NULL_FOR_ALL_LINKS = "null-for-all-links" @@ -43,60 +40,62 @@ class SimplexOptimizationRange(Enum): class ExportMPS(Enum): - FALSE = False NONE = "none" OPTIM1 = "optim1" OPTIM2 = "optim2" BOTH_OPTIMS = "both-optims" - TRUE = True - - -class DefaultOptimizationParameters(BaseModel, alias_generator=to_camel): - model_config = ConfigDict(use_enum_values=True) - - simplex_optimization_range: SimplexOptimizationRange = Field( - default=SimplexOptimizationRange.WEEK, validate_default=True - ) - transmission_capacities: Union[bool, Union[LegacyTransmissionCapacities, OptimizationTransmissionCapacities]] = ( - Field(default=OptimizationTransmissionCapacities.LOCAL_VALUES, validate_default=True) - ) - binding_constraints: bool = True - hurdle_costs: bool = True - thermal_clusters_min_stable_power: bool = True - thermal_clusters_min_ud_time: bool = True - day_ahead_reserve: bool = True - strategic_reserve: bool = True - spinning_reserve: bool = True - primary_reserve: bool = True - export_mps: ExportMPS = Field(default=ExportMPS.NONE, validate_default=True) - include_exportstructure: bool = False - unfeasible_problem_behavior: UnfeasibleProblemBehavior = Field( - default=UnfeasibleProblemBehavior.ERROR_VERBOSE, validate_default=True - ) + + +@dataclass +class OptimizationParameters: + simplex_range: Optional[SimplexOptimizationRange] = None + transmission_capacities: Optional[OptimizationTransmissionCapacities] = None + include_constraints: Optional[bool] = None + include_hurdle_costs: Optional[bool] = None + include_thermal_cluster_min_stable_power: Optional[bool] = None + include_thermal_cluster_min_ud_time: Optional[bool] = None + include_day_ahead: Optional[bool] = None + include_strategic_reserve: Optional[bool] = None + include_spinning_reserve: Optional[bool] = None + include_primary_reserve: Optional[bool] = None + include_export_mps: Optional[ExportMPS] = None + include_export_structure: Optional[bool] = None + include_unfeasible_problem_behavior: Optional[UnfeasibleProblemBehavior] = None @all_optional_model -class OptimizationParameters(DefaultOptimizationParameters): - pass +class OptimizationParametersAPI(BaseModel, alias_generator=to_camel): + simplex_optimization_range: SimplexOptimizationRange + transmission_capacities: OptimizationTransmissionCapacities + binding_constraints: bool + hurdle_costs: bool + thermal_clusters_min_stable_power: bool + thermal_clusters_min_ud_time: bool + day_ahead_reserve: bool + strategic_reserve: bool + spinning_reserve: bool + primary_reserve: bool + export_mps: ExportMPS + include_exportstructure: bool + unfeasible_problem_behavior: UnfeasibleProblemBehavior + + +class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): + simplex_range: SimplexOptimizationRange = SimplexOptimizationRange.WEEK + transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES + include_constraints: bool = True + include_hurdle_costs: bool = True + include_thermal_cluster_min_stable_power: bool = True + include_thermal_cluster_min_ud_time: bool = True + include_day_ahead: bool = True + include_strategic_reserve: bool = True + include_spinning_reserve: bool = True + include_primary_reserve: bool = True + include_export_mps: bool = False + include_export_structure: bool = False + include_unfeasible_problem_behavior: UnfeasibleProblemBehavior = UnfeasibleProblemBehavior.ERROR_VERBOSE -class OptimizationParametersLocal(DefaultOptimizationParameters): - @property - def ini_fields(self) -> dict: - return { - "optimization": { - "simplex-range": self.simplex_optimization_range, - "transmission-capacities": self.transmission_capacities, - "include-constraints": str(self.binding_constraints).lower(), - "include-hurdlecosts": str(self.hurdle_costs).lower(), - "include-tc-minstablepower": str(self.thermal_clusters_min_stable_power).lower(), - "include-tc-min-ud-time": str(self.thermal_clusters_min_ud_time).lower(), - "include-dayahead": str(self.day_ahead_reserve).lower(), - "include-strategicreserve": str(self.primary_reserve).lower(), - "include-spinningreserve": str(self.spinning_reserve).lower(), - "include-primaryreserve": str(self.primary_reserve).lower(), - "include-exportmps": self.export_mps, - "include-exportstructure": str(self.include_exportstructure).lower(), - "include-unfeasible-problem-behavior": self.unfeasible_problem_behavior, - } - } +@all_optional_model +class OptimizationSettingsLocalEdition(OptimizationParametersLocalCreation): + pass diff --git a/src/antares/craft/model/settings/study_settings.py b/src/antares/craft/model/settings/study_settings.py index 17c2c50f..97e93e8d 100644 --- a/src/antares/craft/model/settings/study_settings.py +++ b/src/antares/craft/model/settings/study_settings.py @@ -14,7 +14,7 @@ from antares.craft.model.settings.adequacy_patch import AdequacyPatchParametersLocal, DefaultAdequacyPatchParameters from antares.craft.model.settings.advanced_parameters import AdvancedParametersLocal, DefaultAdvancedParameters from antares.craft.model.settings.general import GeneralParameters -from antares.craft.model.settings.optimization import DefaultOptimizationParameters, OptimizationParametersLocal +from antares.craft.model.settings.optimization import OptimizationParameters from antares.craft.model.settings.playlist_parameters import PlaylistParameters from antares.craft.model.settings.thematic_trimming import ( DefaultThematicTrimmingParameters, @@ -27,7 +27,7 @@ class DefaultStudySettings(BaseModel): general_parameters: GeneralParameters = GeneralParameters() - optimization_parameters: DefaultOptimizationParameters = DefaultOptimizationParameters() + optimization_parameters: OptimizationParameters = OptimizationParameters() adequacy_patch_parameters: DefaultAdequacyPatchParameters = DefaultAdequacyPatchParameters() advanced_parameters: DefaultAdvancedParameters = DefaultAdvancedParameters() playlist_parameters: Optional[PlaylistParameters] = None @@ -41,7 +41,7 @@ class StudySettings(DefaultStudySettings): class StudySettingsLocal(DefaultStudySettings): general_parameters: GeneralParameters = GeneralParameters() - optimization_parameters: OptimizationParametersLocal = OptimizationParametersLocal() + optimization_parameters: OptimizationParameters = OptimizationParameters() adequacy_patch_parameters: AdequacyPatchParametersLocal = AdequacyPatchParametersLocal() advanced_parameters: AdvancedParametersLocal = AdvancedParametersLocal() thematic_trimming_parameters: Optional[ThematicTrimmingParametersLocal] = None From 2b41478506c88f6a1b27cffecf38e66b4d4eaefd Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 16:52:17 +0100 Subject: [PATCH 04/69] fix typo --- src/antares/craft/model/settings/optimization.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/antares/craft/model/settings/optimization.py b/src/antares/craft/model/settings/optimization.py index cd0f2d62..85099db1 100644 --- a/src/antares/craft/model/settings/optimization.py +++ b/src/antares/craft/model/settings/optimization.py @@ -85,14 +85,14 @@ class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES include_constraints: bool = True include_hurdle_costs: bool = True - include_thermal_cluster_min_stable_power: bool = True - include_thermal_cluster_min_ud_time: bool = True - include_day_ahead: bool = True - include_strategic_reserve: bool = True - include_spinning_reserve: bool = True - include_primary_reserve: bool = True - include_export_mps: bool = False - include_export_structure: bool = False + include_tc_min_stable_power: bool = True + include_tc_min_ud_time: bool = True + include_dayahead: bool = True + include_strategicreserve: bool = True + include_spinningreserve: bool = True + include_primaryreserve: bool = True + include_exportmps: bool = False + include_exportstructure: bool = False include_unfeasible_problem_behavior: UnfeasibleProblemBehavior = UnfeasibleProblemBehavior.ERROR_VERBOSE From 48a197172f53a45343eb37ed469e231220f9d9c4 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 17:27:14 +0100 Subject: [PATCH 05/69] continue --- .../model/settings/advanced_parameters.py | 163 ++++++++++-------- src/antares/craft/model/settings/general.py | 2 +- .../craft/model/settings/study_settings.py | 8 +- 3 files changed, 98 insertions(+), 75 deletions(-) diff --git a/src/antares/craft/model/settings/advanced_parameters.py b/src/antares/craft/model/settings/advanced_parameters.py index b145ef1f..24fe9a16 100644 --- a/src/antares/craft/model/settings/advanced_parameters.py +++ b/src/antares/craft/model/settings/advanced_parameters.py @@ -9,16 +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 Any, Optional from antares.craft.model.settings.general import OutputChoices from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model -from pydantic import BaseModel, ConfigDict, Field, model_validator -from pydantic.alias_generators import to_camel -from typing_extensions import Self +from pydantic import BaseModel, Field class InitialReservoirLevel(Enum): @@ -47,10 +45,6 @@ class SheddingPolicy(Enum): MINIMIZE_DURATION = "minimize duration" -class ReserveManagement(Enum): - GLOBAL = "global" - - class UnitCommitmentMode(Enum): FAST = "fast" ACCURATE = "accurate" @@ -70,27 +64,83 @@ class RenewableGenerationModeling(Enum): CLUSTERS = "clusters" -class DefaultAdvancedParameters(BaseModel, alias_generator=to_camel): - model_config = ConfigDict(use_enum_values=True) - - # Advanced parameters +@dataclass +class AdvancedParameters: + initial_reservoir_levels: Optional[InitialReservoirLevel] = None + hydro_heuristic_policy: Optional[HydroHeuristicPolicy] = None + hydro_pricing_mode: Optional[HydroPricingMode] = None + power_fluctuations: Optional[PowerFluctuation] = None + shedding_policy: Optional[SheddingPolicy] = None + unit_commitment_mode: Optional[UnitCommitmentMode] = None + number_of_cores_mode: Optional[SimulationCore] = None + renewable_generation_modelling: Optional[RenewableGenerationModeling] = None accuracy_on_correlation: Optional[set[OutputChoices]] = None - # Other preferences - initial_reservoir_levels: InitialReservoirLevel = Field( - default=InitialReservoirLevel.COLD_START, validate_default=True - ) - hydro_heuristic_policy: HydroHeuristicPolicy = Field( - default=HydroHeuristicPolicy.ACCOMMODATE_RULES_CURVES, validate_default=True - ) - hydro_pricing_mode: HydroPricingMode = Field(default=HydroPricingMode.FAST, validate_default=True) - power_fluctuations: PowerFluctuation = Field(default=PowerFluctuation.FREE_MODULATIONS, validate_default=True) - shedding_policy: SheddingPolicy = Field(default=SheddingPolicy.SHAVE_PEAKS, validate_default=True) - unit_commitment_mode: UnitCommitmentMode = Field(default=UnitCommitmentMode.FAST, validate_default=True) - number_of_cores_mode: SimulationCore = Field(default=SimulationCore.MEDIUM, validate_default=True) - renewable_generation_modelling: RenewableGenerationModeling = Field( - default=RenewableGenerationModeling.AGGREGATED, validate_default=True - ) - # Seeds + + +@dataclass +class SeedParameters: + seed_ts_gen_thermal: Optional[int] = None + seed_ts_numbers: Optional[int] = None + seed_unsupplied_energy_costs: Optional[int] = None + seed_spilled_energy_costs: Optional[int] = None + seed_thermal_costs: Optional[int] = None + seed_hydro_costs: Optional[int] = None + seed_initial_reservoir_levels: Optional[int] = None + + +@all_optional_model +class AdvancedAndSeedParametersAPI(BaseModel): + accuracy_on_correlation: set[OutputChoices] + initial_reservoir_levels: InitialReservoirLevel + hydro_heuristic_policy: HydroHeuristicPolicy + hydro_pricing_mode: HydroPricingMode + power_fluctuations: PowerFluctuation + shedding_policy: SheddingPolicy + unit_commitment_mode: UnitCommitmentMode + number_of_cores_mode: SimulationCore + renewable_generation_modelling: RenewableGenerationModeling + seed_tsgen_wind: int + seed_tsgen_load: int + seed_tsgen_hydro: int + seed_tsgen_thermal: int + seed_tsgen_solar: int + seed_tsnumbers: int + seed_unsupplied_energy_costs: int + seed_spilled_energy_costs: int + seed_thermal_costs: int + seed_hydro_costs: int + seed_initial_reservoir_levels: int + + +class OtherPreferencesLocalCreation(BaseModel, alias_generator=to_kebab): + initial_reservoir_levels: InitialReservoirLevel + hydro_heuristic_policy: HydroHeuristicPolicy + hydro_pricing_mode: HydroPricingMode + power_fluctuations: PowerFluctuation + shedding_policy: SheddingPolicy + shedding_strategy: Any + unit_commitment_mode: UnitCommitmentMode + number_of_cores_mode: SimulationCore + renewable_generation_modelling: RenewableGenerationModeling + day_ahead_reserve_management: Any + + +@all_optional_model +class OtherPreferencesLocalEdition(OtherPreferencesLocalCreation): + pass + + +class AdvancedParametersLocalCreation(BaseModel, alias_generator=to_kebab): + accuracy_on_correlation: set[OutputChoices] = set() + adequacy_block_size: int = 100 + + +@all_optional_model +class AdvancedParametersLocalEdition(AdvancedParametersLocalCreation): + pass + + +class SeedParametersLocalCreation(BaseModel, alias_generator=to_kebab): seed_tsgen_wind: int = 5489 seed_tsgen_load: int = 1005489 seed_tsgen_hydro: int = 2005489 @@ -105,46 +155,17 @@ class DefaultAdvancedParameters(BaseModel, alias_generator=to_camel): @all_optional_model -class AdvancedParameters(DefaultAdvancedParameters): - @model_validator(mode="before") - def change_accuracy_on_correlation(cls, data: Any) -> Self: - if "accuracyOnCorrelation" in data.keys(): - data["accuracyOnCorrelation"] = ( - {OutputChoices(list_item) for list_item in data["accuracyOnCorrelation"].replace(" ", "").split(",")} - if data["accuracyOnCorrelation"] - else None - ) - return data - - -class AdvancedParametersLocal(DefaultAdvancedParameters, alias_generator=to_kebab): - @property - def ini_fields(self) -> dict: - return { - "other preferences": { - "initial-reservoir-levels": self.initial_reservoir_levels, - "hydro-heuristic-policy": self.hydro_heuristic_policy, - "hydro-pricing-mode": self.hydro_pricing_mode, - "power-fluctuations": self.power_fluctuations, - "shedding-policy": self.shedding_policy, - "unit-commitment-mode": self.unit_commitment_mode, - "number-of-cores-mode": self.number_of_cores_mode, - "renewable-generation-modelling": self.renewable_generation_modelling, - }, - "advanced parameters": { - "accuracy-on-correlation": self.accuracy_on_correlation if self.accuracy_on_correlation else "", - }, - "seeds - Mersenne Twister": { - "seed-tsgen-wind": str(self.seed_tsgen_wind), - "seed-tsgen-load": str(self.seed_tsgen_load), - "seed-tsgen-hydro": str(self.seed_tsgen_hydro), - "seed-tsgen-thermal": str(self.seed_tsgen_thermal), - "seed-tsgen-solar": str(self.seed_tsgen_solar), - "seed-tsnumbers": str(self.seed_tsnumbers), - "seed-unsupplied-energy-costs": str(self.seed_unsupplied_energy_costs), - "seed-spilled-energy-costs": str(self.seed_spilled_energy_costs), - "seed-thermal-costs": str(self.seed_thermal_costs), - "seed-hydro-costs": str(self.seed_hydro_costs), - "seed-initial-reservoir-levels": str(self.seed_initial_reservoir_levels), - }, - } +class SeedParametersLocalEdition(SeedParametersLocalCreation): + pass + + +class AdvancedAndSeedParametersLocalCreation(BaseModel): + other_preferences: OtherPreferencesLocalCreation = Field(alias="other preferences") + advanced_parameters: AdvancedParametersLocalCreation = Field(alias="advanced parameters") + seeds: SeedParametersLocalCreation = Field(alias="seeds - Mersenne Twister") + + +class AdvancedAndSeedParametersLocalEdition(BaseModel): + other_preferences: OtherPreferencesLocalEdition = Field(default=None, alias="other preferences") + advanced_parameters: AdvancedParametersLocalEdition = Field(default_factory=None, alias="advanced parameters") + seeds: SeedParametersLocalEdition = Field(default_factory=None, alias="seeds - Mersenne Twister") diff --git a/src/antares/craft/model/settings/general.py b/src/antares/craft/model/settings/general.py index 9d19af13..c7291d34 100644 --- a/src/antares/craft/model/settings/general.py +++ b/src/antares/craft/model/settings/general.py @@ -150,7 +150,7 @@ class GeneralSectionLocal(BaseModel): class OutputSectionLocal(BaseModel): synthesis: bool = True store_new_set: bool = Field(default=True, alias="storenewset") - archives: OutputFormat = Field(default=OutputFormat.TXT, exclude=True) + archives: set[OutputChoices] = set() class GeneralParametersLocalCreation(BaseModel): diff --git a/src/antares/craft/model/settings/study_settings.py b/src/antares/craft/model/settings/study_settings.py index 97e93e8d..8e1eb7d7 100644 --- a/src/antares/craft/model/settings/study_settings.py +++ b/src/antares/craft/model/settings/study_settings.py @@ -12,7 +12,7 @@ from typing import Optional from antares.craft.model.settings.adequacy_patch import AdequacyPatchParametersLocal, DefaultAdequacyPatchParameters -from antares.craft.model.settings.advanced_parameters import AdvancedParametersLocal, DefaultAdvancedParameters +from antares.craft.model.settings.advanced_parameters import AdvancedParameters, SeedParameters from antares.craft.model.settings.general import GeneralParameters from antares.craft.model.settings.optimization import OptimizationParameters from antares.craft.model.settings.playlist_parameters import PlaylistParameters @@ -28,8 +28,9 @@ class DefaultStudySettings(BaseModel): general_parameters: GeneralParameters = GeneralParameters() optimization_parameters: OptimizationParameters = OptimizationParameters() + advanced_parameters: AdvancedParameters = AdvancedParameters() + seed_parameters: SeedParameters = SeedParameters() adequacy_patch_parameters: DefaultAdequacyPatchParameters = DefaultAdequacyPatchParameters() - advanced_parameters: DefaultAdvancedParameters = DefaultAdvancedParameters() playlist_parameters: Optional[PlaylistParameters] = None thematic_trimming_parameters: Optional[DefaultThematicTrimmingParameters] = None @@ -43,7 +44,8 @@ class StudySettingsLocal(DefaultStudySettings): general_parameters: GeneralParameters = GeneralParameters() optimization_parameters: OptimizationParameters = OptimizationParameters() adequacy_patch_parameters: AdequacyPatchParametersLocal = AdequacyPatchParametersLocal() - advanced_parameters: AdvancedParametersLocal = AdvancedParametersLocal() + advanced_parameters: AdvancedParameters = AdvancedParameters() + seed_parameters: SeedParameters = SeedParameters() thematic_trimming_parameters: Optional[ThematicTrimmingParametersLocal] = None @model_serializer From 972f07ae043a1104eae0f5e89a26469ef427d6d5 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 17:39:59 +0100 Subject: [PATCH 06/69] continue work --- .../craft/model/settings/adequacy_patch.py | 64 ++++++++++--------- .../model/settings/advanced_parameters.py | 3 +- .../craft/model/settings/study_settings.py | 6 +- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/antares/craft/model/settings/adequacy_patch.py b/src/antares/craft/model/settings/adequacy_patch.py index 6159c1d1..63ba1b3e 100644 --- a/src/antares/craft/model/settings/adequacy_patch.py +++ b/src/antares/craft/model/settings/adequacy_patch.py @@ -9,11 +9,13 @@ # 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 +from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, Field from pydantic.alias_generators import to_camel @@ -22,14 +24,24 @@ class PriceTakingOrder(Enum): LOAD = "Load" -class DefaultAdequacyPatchParameters(BaseModel, populate_by_name=True, alias_generator=to_camel): - model_config = ConfigDict(use_enum_values=True) +@dataclass +class AdequacyPatchParameters: + include_adq_patch: Optional[bool] = None + set_to_null_ntc_from_physical_out_to_physical_in_for_first_step: Optional[bool] = None + set_to_null_ntc_between_physical_out_for_first_step: Optional[bool] = None + price_taking_order: Optional[PriceTakingOrder] = None + include_hurdle_cost_csr: Optional[bool] = None + check_csr_cost_function: Optional[bool] = None + threshold_initiate_curtailment_sharing_rule: Optional[int] = None + threshold_display_local_matching_rule_violations: Optional[int] = None + threshold_csr_variable_bounds_relaxation: Optional[int] = None + - # version 830 - enable_adequacy_patch: bool = False +@all_optional_model +class AdequacyPatchParametersAPI(BaseModel, alias_generator=to_camel): + enable_adequacy_patch: Optional[bool] = None ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch: bool = True ntc_between_physical_areas_out_adequacy_patch: bool = True - # version 850 price_taking_order: PriceTakingOrder = Field(default=PriceTakingOrder.DENS, validate_default=True) include_hurdle_cost_csr: bool = False check_csr_cost_function: bool = False @@ -39,29 +51,19 @@ class DefaultAdequacyPatchParameters(BaseModel, populate_by_name=True, alias_gen threshold_csr_variable_bounds_relaxation: int = 3 -@all_optional_model -class AdequacyPatchParameters(DefaultAdequacyPatchParameters): - pass +class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): + include_adq_patch: bool = False + set_to_null_ntc_from_physical_out_to_physical_in_for_first_step: bool = True + set_to_null_ntc_between_physical_out_for_first_step: bool = True + price_taking_order: PriceTakingOrder = PriceTakingOrder.DENS + include_hurdle_cost_csr: bool = False + check_csr_cost_function: bool = False + threshold_initiate_curtailment_sharing_rule: int = 0 + threshold_display_local_matching_rule_violations: int = 0 + threshold_csr_variable_bounds_relaxation: int = 3 + enable_first_step: bool = False -class AdequacyPatchParametersLocal(DefaultAdequacyPatchParameters): - @property - def ini_fields(self) -> dict: - return { - "adequacy patch": { - "include-adq-patch": str(self.enable_adequacy_patch).lower(), - "set-to-null-ntc-from-physical-out-to-physical-in-for-first-step": str( - self.ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch - ).lower(), - "set-to-null-ntc-between-physical-out-for-first-step": str( - self.ntc_between_physical_areas_out_adequacy_patch - ).lower(), - "enable-first-step": str(self.enable_first_step).lower(), - "price-taking-order": self.price_taking_order, - "include-hurdle-cost-csr": str(self.include_hurdle_cost_csr).lower(), - "check-csr-cost-function": str(self.check_csr_cost_function).lower(), - "threshold-initiate-curtailment-sharing-rule": f"{self.threshold_initiate_curtailment_sharing_rule:.6f}", - "threshold-display-local-matching-rule-violations": f"{self.threshold_display_local_matching_rule_violations:.6f}", - "threshold-csr-variable-bounds-relaxation": f"{self.threshold_csr_variable_bounds_relaxation}", - } - } +@all_optional_model +class AdequacyPatchParametersLocalEdition(AdequacyPatchParametersLocalCreation): + pass diff --git a/src/antares/craft/model/settings/advanced_parameters.py b/src/antares/craft/model/settings/advanced_parameters.py index 24fe9a16..81fc0d5e 100644 --- a/src/antares/craft/model/settings/advanced_parameters.py +++ b/src/antares/craft/model/settings/advanced_parameters.py @@ -17,6 +17,7 @@ from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel, Field +from pydantic.alias_generators import to_camel class InitialReservoirLevel(Enum): @@ -89,7 +90,7 @@ class SeedParameters: @all_optional_model -class AdvancedAndSeedParametersAPI(BaseModel): +class AdvancedAndSeedParametersAPI(BaseModel, alias_generator=to_camel): accuracy_on_correlation: set[OutputChoices] initial_reservoir_levels: InitialReservoirLevel hydro_heuristic_policy: HydroHeuristicPolicy diff --git a/src/antares/craft/model/settings/study_settings.py b/src/antares/craft/model/settings/study_settings.py index 8e1eb7d7..9233480b 100644 --- a/src/antares/craft/model/settings/study_settings.py +++ b/src/antares/craft/model/settings/study_settings.py @@ -11,7 +11,7 @@ # This file is part of the Antares project. from typing import Optional -from antares.craft.model.settings.adequacy_patch import AdequacyPatchParametersLocal, DefaultAdequacyPatchParameters +from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters from antares.craft.model.settings.advanced_parameters import AdvancedParameters, SeedParameters from antares.craft.model.settings.general import GeneralParameters from antares.craft.model.settings.optimization import OptimizationParameters @@ -30,7 +30,7 @@ class DefaultStudySettings(BaseModel): optimization_parameters: OptimizationParameters = OptimizationParameters() advanced_parameters: AdvancedParameters = AdvancedParameters() seed_parameters: SeedParameters = SeedParameters() - adequacy_patch_parameters: DefaultAdequacyPatchParameters = DefaultAdequacyPatchParameters() + adequacy_patch_parameters: AdequacyPatchParameters = AdequacyPatchParameters() playlist_parameters: Optional[PlaylistParameters] = None thematic_trimming_parameters: Optional[DefaultThematicTrimmingParameters] = None @@ -43,7 +43,7 @@ class StudySettings(DefaultStudySettings): class StudySettingsLocal(DefaultStudySettings): general_parameters: GeneralParameters = GeneralParameters() optimization_parameters: OptimizationParameters = OptimizationParameters() - adequacy_patch_parameters: AdequacyPatchParametersLocal = AdequacyPatchParametersLocal() + adequacy_patch_parameters: AdequacyPatchParameters = AdequacyPatchParameters() advanced_parameters: AdvancedParameters = AdvancedParameters() seed_parameters: SeedParameters = SeedParameters() thematic_trimming_parameters: Optional[ThematicTrimmingParametersLocal] = None From 6d1f0e89b5de966aa07c0b75851e4f4775db6fdd Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 17:50:00 +0100 Subject: [PATCH 07/69] add all new classes --- .../craft/model/settings/study_settings.py | 55 +-- .../craft/model/settings/thematic_trimming.py | 324 +++++++++++------- src/antares/craft/model/study.py | 14 +- 3 files changed, 211 insertions(+), 182 deletions(-) diff --git a/src/antares/craft/model/settings/study_settings.py b/src/antares/craft/model/settings/study_settings.py index 9233480b..0c3ff6b2 100644 --- a/src/antares/craft/model/settings/study_settings.py +++ b/src/antares/craft/model/settings/study_settings.py @@ -9,6 +9,7 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import dataclass from typing import Optional from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters @@ -16,51 +17,15 @@ from antares.craft.model.settings.general import GeneralParameters from antares.craft.model.settings.optimization import OptimizationParameters from antares.craft.model.settings.playlist_parameters import PlaylistParameters -from antares.craft.model.settings.thematic_trimming import ( - DefaultThematicTrimmingParameters, - ThematicTrimmingParametersLocal, -) -from antares.craft.tools.all_optional_meta import all_optional_model -from antares.craft.tools.ini_tool import get_ini_fields_for_ini -from pydantic import BaseModel, model_serializer +from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters -class DefaultStudySettings(BaseModel): - general_parameters: GeneralParameters = GeneralParameters() - optimization_parameters: OptimizationParameters = OptimizationParameters() - advanced_parameters: AdvancedParameters = AdvancedParameters() - seed_parameters: SeedParameters = SeedParameters() - adequacy_patch_parameters: AdequacyPatchParameters = AdequacyPatchParameters() +@dataclass +class StudySettings: + general_parameters: Optional[GeneralParameters] = None + optimization_parameters: Optional[OptimizationParameters] = None + advanced_parameters: Optional[AdvancedParameters] = None + seed_parameters: Optional[SeedParameters] = None + adequacy_patch_parameters: Optional[AdequacyPatchParameters] = None playlist_parameters: Optional[PlaylistParameters] = None - thematic_trimming_parameters: Optional[DefaultThematicTrimmingParameters] = None - - -@all_optional_model -class StudySettings(DefaultStudySettings): - pass - - -class StudySettingsLocal(DefaultStudySettings): - general_parameters: GeneralParameters = GeneralParameters() - optimization_parameters: OptimizationParameters = OptimizationParameters() - adequacy_patch_parameters: AdequacyPatchParameters = AdequacyPatchParameters() - advanced_parameters: AdvancedParameters = AdvancedParameters() - seed_parameters: SeedParameters = SeedParameters() - thematic_trimming_parameters: Optional[ThematicTrimmingParametersLocal] = None - - @model_serializer - def serialize(self) -> dict: - output_dict = get_ini_fields_for_ini(self) - return self._sort_fields_last(output_dict) - - @staticmethod - def _sort_fields_last(output_dict: dict) -> dict: - new_general = {key: value for key, value in output_dict["general"].items() if key != "readonly"} | { - "readonly": output_dict["general"]["readonly"] - } - new_output = {key: value for key, value in output_dict["output"].items() if key != "result-format"} | { - "result-format": output_dict["output"]["result-format"] - } - output_dict["general"] = new_general - output_dict["output"] = new_output - return output_dict + thematic_trimming_parameters: Optional[ThematicTrimmingParameters] = None diff --git a/src/antares/craft/model/settings/thematic_trimming.py b/src/antares/craft/model/settings/thematic_trimming.py index 8d1460fd..86ff37cd 100644 --- a/src/antares/craft/model/settings/thematic_trimming.py +++ b/src/antares/craft/model/settings/thematic_trimming.py @@ -9,148 +9,212 @@ # 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 from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel from pydantic.alias_generators import to_camel -class DefaultThematicTrimmingParameters(BaseModel, alias_generator=to_camel): - """ - This class manages the configuration of result filtering in a simulation. - - This table allows the user to enable or disable specific variables before running a simulation. - """ - - ov_cost: bool = True - op_cost: bool = True - mrg_price: bool = True - co2_emis: bool = True - dtg_by_plant: bool = True - balance: bool = True - row_bal: bool = True - psp: bool = True - misc_ndg: bool = True - load: bool = True - h_ror: bool = True - wind: bool = True - solar: bool = True - nuclear: bool = True - lignite: bool = True - coal: bool = True - gas: bool = True - oil: bool = True - mix_fuel: bool = True - misc_dtg: bool = True - h_stor: bool = True - h_pump: bool = True - h_lev: bool = True - h_infl: bool = True - h_ovfl: bool = True - h_val: bool = True - h_cost: bool = True - unsp_enrg: bool = True - spil_enrg: bool = True - lold: bool = True - lolp: bool = True - avl_dtg: bool = True - dtg_mrg: bool = True - max_mrg: bool = True - np_cost: bool = True - np_cost_by_plant: bool = True - nodu: bool = True - nodu_by_plant: bool = True - flow_lin: bool = True - ucap_lin: bool = True - loop_flow: bool = True - flow_quad: bool = True - cong_fee_alg: bool = True - cong_fee_abs: bool = True - marg_cost: bool = True - cong_prob_plus: bool = True - cong_prob_minus: bool = True - hurdle_cost: bool = True - # since v8.1 - res_generation_by_plant: bool = True - misc_dtg_2: bool = True - misc_dtg_3: bool = True - misc_dtg_4: bool = True - wind_offshore: bool = True - wind_onshore: bool = True - solar_concrt: bool = True - solar_pv: bool = True - solar_rooft: bool = True - renw_1: bool = True - renw_2: bool = True - renw_3: bool = True - renw_4: bool = True - # since v8.3 - dens: bool = True - profit_by_plant: bool = True - # since v8.6 - sts_inj_by_plant: bool = True - sts_withdrawal_by_plant: bool = True - sts_lvl_by_plant: bool = True - psp_open_injection: bool = True - psp_open_withdrawal: bool = True - psp_open_level: bool = True - psp_closed_injection: bool = True - psp_closed_withdrawal: bool = True - psp_closed_level: bool = True - pondage_injection: bool = True - pondage_withdrawal: bool = True - pondage_level: bool = True - battery_injection: bool = True - battery_withdrawal: bool = True - battery_level: bool = True - other1_injection: bool = True - other1_withdrawal: bool = True - other1_level: bool = True - other2_injection: bool = True - other2_withdrawal: bool = True - other2_level: bool = True - other3_injection: bool = True - other3_withdrawal: bool = True - other3_level: bool = True - other4_injection: bool = True - other4_withdrawal: bool = True - other4_level: bool = True - other5_injection: bool = True - other5_withdrawal: bool = True - other5_level: bool = True - # since v8.8 - sts_cashflow_by_cluster: bool = True - - @property - def selected_vars_reset(self) -> bool: - return sum([getattr(self, field) for field in self.model_fields]) > (len(self.model_fields) / 2) +@dataclass +class ThematicTrimmingParameters: + ov_cost: Optional[bool] = None + op_cost: Optional[bool] = None + mrg_price: Optional[bool] = None + co2_emis: Optional[bool] = None + dtg_by_plant: Optional[bool] = None + balance: Optional[bool] = None + row_bal: Optional[bool] = None + psp: Optional[bool] = None + misc_ndg: Optional[bool] = None + load: Optional[bool] = None + h_ror: Optional[bool] = None + wind: Optional[bool] = None + solar: Optional[bool] = None + nuclear: Optional[bool] = None + lignite: Optional[bool] = None + coal: Optional[bool] = None + gas: Optional[bool] = None + oil: Optional[bool] = None + mix_fuel: Optional[bool] = None + misc_dtg: Optional[bool] = None + h_stor: Optional[bool] = None + h_pump: Optional[bool] = None + h_lev: Optional[bool] = None + h_infl: Optional[bool] = None + h_ovfl: Optional[bool] = None + h_val: Optional[bool] = None + h_cost: Optional[bool] = None + unsp_enrg: Optional[bool] = None + spil_enrg: Optional[bool] = None + lold: Optional[bool] = None + lolp: Optional[bool] = None + avl_dtg: Optional[bool] = None + dtg_mrg: Optional[bool] = None + max_mrg: Optional[bool] = None + np_cost: Optional[bool] = None + np_cost_by_plant: Optional[bool] = None + nodu: Optional[bool] = None + nodu_by_plant: Optional[bool] = None + flow_lin: Optional[bool] = None + ucap_lin: Optional[bool] = None + loop_flow: Optional[bool] = None + flow_quad: Optional[bool] = None + cong_fee_alg: Optional[bool] = None + cong_fee_abs: Optional[bool] = None + marg_cost: Optional[bool] = None + cong_prob_plus: Optional[bool] = None + cong_prob_minus: Optional[bool] = None + hurdle_cost: Optional[bool] = None + res_generation_by_plant: Optional[bool] = None + misc_dtg_2: Optional[bool] = None + misc_dtg_3: Optional[bool] = None + misc_dtg_4: Optional[bool] = None + wind_offshore: Optional[bool] = None + wind_onshore: Optional[bool] = None + solar_concrt: Optional[bool] = None + solar_pv: Optional[bool] = None + solar_rooft: Optional[bool] = None + renw_1: Optional[bool] = None + renw_2: Optional[bool] = None + renw_3: Optional[bool] = None + renw_4: Optional[bool] = None + dens: Optional[bool] = None + profit_by_plant: Optional[bool] = None + sts_inj_by_plant: Optional[bool] = None + sts_withdrawal_by_plant: Optional[bool] = None + sts_lvl_by_plant: Optional[bool] = None + psp_open_injection: Optional[bool] = None + psp_open_withdrawal: Optional[bool] = None + psp_open_level: Optional[bool] = None + psp_closed_injection: Optional[bool] = None + psp_closed_withdrawal: Optional[bool] = None + psp_closed_level: Optional[bool] = None + pondage_injection: Optional[bool] = None + pondage_withdrawal: Optional[bool] = None + pondage_level: Optional[bool] = None + battery_injection: Optional[bool] = None + battery_withdrawal: Optional[bool] = None + battery_level: Optional[bool] = None + other1_injection: Optional[bool] = None + other1_withdrawal: Optional[bool] = None + other1_level: Optional[bool] = None + other2_injection: Optional[bool] = None + other2_withdrawal: Optional[bool] = None + other2_level: Optional[bool] = None + other3_injection: Optional[bool] = None + other3_withdrawal: Optional[bool] = None + other3_level: Optional[bool] = None + other4_injection: Optional[bool] = None + other4_withdrawal: Optional[bool] = None + other4_level: Optional[bool] = None + other5_injection: Optional[bool] = None + other5_withdrawal: Optional[bool] = None + other5_level: Optional[bool] = None + sts_cashflow_by_cluster: Optional[bool] = None @all_optional_model -class ThematicTrimmingParameters(DefaultThematicTrimmingParameters): - pass - - -class ThematicTrimmingParametersLocal(DefaultThematicTrimmingParameters, populate_by_name=True): - @property - def ini_fields(self) -> dict: - variable_list = repr( - [ - getattr(ThematicVars, to_camel(variable)).value - for variable in self.model_fields - if getattr(self, variable) ^ self.selected_vars_reset - ] - ) - thematic_trimming_dict = {"select_var " + ("-" if self.selected_vars_reset else "+"): variable_list} - - return { - "variables selection": {"selected_vars_reset": str(self.selected_vars_reset).lower()} - | thematic_trimming_dict - } +class ThematicTrimmingParametersAPI(BaseModel, alias_generator=to_camel): + ov_cost: bool + op_cost: bool + mrg_price: bool + co2_emis: bool + dtg_by_plant: bool + balance: bool + row_bal: bool + psp: bool + misc_ndg: bool + load: bool + h_ror: bool + wind: bool + solar: bool + nuclear: bool + lignite: bool + coal: bool + gas: bool + oil: bool + mix_fuel: bool + misc_dtg: bool + h_stor: bool + h_pump: bool + h_lev: bool + h_infl: bool + h_ovfl: bool + h_val: bool + h_cost: bool + unsp_enrg: bool + spil_enrg: bool + lold: bool + lolp: bool + avl_dtg: bool + dtg_mrg: bool + max_mrg: bool + np_cost: bool + np_cost_by_plant: bool + nodu: bool + nodu_by_plant: bool + flow_lin: bool + ucap_lin: bool + loop_flow: bool + flow_quad: bool + cong_fee_alg: bool + cong_fee_abs: bool + marg_cost: bool + cong_prob_plus: bool + cong_prob_minus: bool + hurdle_cost: bool + res_generation_by_plant: bool + misc_dtg_2: bool + misc_dtg_3: bool + misc_dtg_4: bool + wind_offshore: bool + wind_onshore: bool + solar_concrt: bool + solar_pv: bool + solar_rooft: bool + renw_1: bool + renw_2: bool + renw_3: bool + renw_4: bool + dens: bool + profit_by_plant: bool + sts_inj_by_plant: bool + sts_withdrawal_by_plant: bool + sts_lvl_by_plant: bool + psp_open_injection: bool + psp_open_withdrawal: bool + psp_open_level: bool + psp_closed_injection: bool + psp_closed_withdrawal: bool + psp_closed_level: bool + pondage_injection: bool + pondage_withdrawal: bool + pondage_level: bool + battery_injection: bool + battery_withdrawal: bool + battery_level: bool + other1_injection: bool + other1_withdrawal: bool + other1_level: bool + other2_injection: bool + other2_withdrawal: bool + other2_level: bool + other3_injection: bool + other3_withdrawal: bool + other3_level: bool + other4_injection: bool + other4_withdrawal: bool + other4_level: bool + other5_injection: bool + other5_withdrawal: bool + other5_level: bool + sts_cashflow_by_cluster: bool -class ThematicVars(Enum): +class ThematicVarsLocal(Enum): balance = "BALANCE" dens = "DENS" load = "LOAD" diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 85c33d9f..3aabb6d7 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -32,7 +32,7 @@ ) from antares.craft.model.link import Link, LinkProperties, LinkUi from antares.craft.model.output import Output -from antares.craft.model.settings.study_settings import DefaultStudySettings, StudySettings, StudySettingsLocal +from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.simulation import AntaresSimulationParameters, Job from antares.craft.service.api_services.study_api import _returns_study_settings from antares.craft.service.base_services import BaseStudyService @@ -92,7 +92,7 @@ def create_study_local( study_name: str, version: str, parent_directory: str, - settings: StudySettingsLocal = StudySettingsLocal(), + settings: StudySettings = StudySettings(), ) -> "Study": """ Create a directory structure for the study with empty files. @@ -138,7 +138,7 @@ def create_study_local( with open(desktop_ini_path, "w") as desktop_ini_file: desktop_ini_file.write(desktop_ini_content) - local_settings = StudySettingsLocal.model_validate(settings) + local_settings = StudySettings.model_validate(settings) local_settings_file = IniFile(study_directory, InitializationFilesTypes.GENERAL) local_settings_file.ini_dict = local_settings.model_dump(exclude_none=True, by_alias=True) local_settings_file.write_ini_file() @@ -231,7 +231,7 @@ def __init__( name: str, version: str, service_factory: ServiceFactory, - settings: Union[StudySettings, StudySettingsLocal, None] = None, + settings: Union[StudySettings, None] = None, path: PurePath = PurePath("."), ): self.name = name @@ -242,7 +242,7 @@ def __init__( self._link_service = service_factory.create_link_service() self._run_service = service_factory.create_run_service() self._binding_constraints_service = service_factory.create_binding_constraints_service() - self._settings = DefaultStudySettings.model_validate(settings if settings is not None else StudySettings()) + self._settings = settings or StudySettings() self._areas: dict[str, Area] = dict() self._links: dict[str, Link] = dict() self._binding_constraints: dict[str, BindingConstraint] = dict() @@ -272,7 +272,7 @@ def get_areas(self) -> MappingProxyType[str, Area]: def get_links(self) -> MappingProxyType[str, Link]: return MappingProxyType(self._links) - def get_settings(self) -> DefaultStudySettings: + def get_settings(self) -> StudySettings: return self._settings def get_binding_constraints(self) -> MappingProxyType[str, BindingConstraint]: @@ -474,7 +474,7 @@ def _create_directory_structure(study_path: Path) -> None: (study_path / subdirectory).mkdir(parents=True, exist_ok=True) -def _create_correlation_ini_files(local_settings: StudySettingsLocal, study_directory: Path) -> None: +def _create_correlation_ini_files(local_settings: StudySettings, study_directory: Path) -> None: fields_to_check = ["hydro", "load", "solar", "wind"] correlation_inis_to_create = [ ( From 8493952e85160709ba8f7c0fb51847ffe05f871a Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 17:55:01 +0100 Subject: [PATCH 08/69] move api related classes --- src/antares/__init__.py | 0 src/antares/craft/model/area.py | 5 +- src/antares/craft/model/binding_constraint.py | 5 +- src/antares/craft/model/hydro.py | 3 +- src/antares/craft/model/link.py | 3 +- .../craft/model/settings/adequacy_patch.py | 18 +- .../model/settings/advanced_parameters.py | 28 +-- src/antares/craft/model/settings/general.py | 27 +-- .../craft/model/settings/optimization.py | 21 +- .../craft/model/settings/thematic_trimming.py | 102 --------- src/antares/craft/model/st_storage.py | 5 +- .../api_services/study_settings_api.py | 211 ++++++++++++++++++ .../binding_constraint_local.py | 3 +- src/antares/craft/tools/contents_tool.py | 3 +- src/antares/craft/tools/ini_tool.py | 3 +- 15 files changed, 238 insertions(+), 199 deletions(-) create mode 100644 src/antares/__init__.py create mode 100644 src/antares/craft/service/api_services/study_settings_api.py diff --git a/src/antares/__init__.py b/src/antares/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/antares/craft/model/area.py b/src/antares/craft/model/area.py index 3742895e..ce05fbfd 100644 --- a/src/antares/craft/model/area.py +++ b/src/antares/craft/model/area.py @@ -21,6 +21,9 @@ import pandas as pd +from pydantic import BaseModel, computed_field +from pydantic.alias_generators import to_camel + from antares.craft.model.commons import FilterOption, sort_filter_values from antares.craft.model.hydro import Hydro, HydroMatrixName, HydroProperties from antares.craft.model.renewable import RenewableCluster, RenewableClusterProperties @@ -29,8 +32,6 @@ from antares.craft.tools.alias_generators import to_space from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase, transform_name_to_id -from pydantic import BaseModel, computed_field -from pydantic.alias_generators import to_camel class AdequacyPatchMode(EnumIgnoreCase): diff --git a/src/antares/craft/model/binding_constraint.py b/src/antares/craft/model/binding_constraint.py index 667415f0..82e09207 100644 --- a/src/antares/craft/model/binding_constraint.py +++ b/src/antares/craft/model/binding_constraint.py @@ -15,11 +15,12 @@ import pandas as pd -from antares.craft.tools.all_optional_meta import all_optional_model -from antares.craft.tools.contents_tool import EnumIgnoreCase, transform_name_to_id from pydantic import BaseModel, Field, model_validator from pydantic.alias_generators import to_camel +from antares.craft.tools.all_optional_meta import all_optional_model +from antares.craft.tools.contents_tool import EnumIgnoreCase, transform_name_to_id + class BindingConstraintFrequency(EnumIgnoreCase): HOURLY = "hourly" diff --git a/src/antares/craft/model/hydro.py b/src/antares/craft/model/hydro.py index 44418ed9..e3788a45 100644 --- a/src/antares/craft/model/hydro.py +++ b/src/antares/craft/model/hydro.py @@ -15,10 +15,11 @@ import pandas as pd -from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel from pydantic.alias_generators import to_camel +from antares.craft.tools.all_optional_meta import all_optional_model + class HydroMatrixName(Enum): SERIES_ROR = "ror" diff --git a/src/antares/craft/model/link.py b/src/antares/craft/model/link.py index b7f00141..0c86ae08 100644 --- a/src/antares/craft/model/link.py +++ b/src/antares/craft/model/link.py @@ -15,11 +15,12 @@ import pandas as pd +from pydantic import BaseModel + from antares.craft.model.commons import FilterOption, sort_filter_values from antares.craft.tools.alias_generators import to_kebab 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 class TransmissionCapacities(Enum): diff --git a/src/antares/craft/model/settings/adequacy_patch.py b/src/antares/craft/model/settings/adequacy_patch.py index 63ba1b3e..2ce88517 100644 --- a/src/antares/craft/model/settings/adequacy_patch.py +++ b/src/antares/craft/model/settings/adequacy_patch.py @@ -13,10 +13,10 @@ from enum import Enum from typing import Optional +from pydantic import BaseModel + from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model -from pydantic import BaseModel, Field -from pydantic.alias_generators import to_camel class PriceTakingOrder(Enum): @@ -37,20 +37,6 @@ class AdequacyPatchParameters: threshold_csr_variable_bounds_relaxation: Optional[int] = None -@all_optional_model -class AdequacyPatchParametersAPI(BaseModel, alias_generator=to_camel): - enable_adequacy_patch: Optional[bool] = None - ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch: bool = True - ntc_between_physical_areas_out_adequacy_patch: bool = True - price_taking_order: PriceTakingOrder = Field(default=PriceTakingOrder.DENS, validate_default=True) - include_hurdle_cost_csr: bool = False - check_csr_cost_function: bool = False - enable_first_step: bool = False - threshold_initiate_curtailment_sharing_rule: int = 0 - threshold_display_local_matching_rule_violations: int = 0 - threshold_csr_variable_bounds_relaxation: int = 3 - - class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): include_adq_patch: bool = False set_to_null_ntc_from_physical_out_to_physical_in_for_first_step: bool = True diff --git a/src/antares/craft/model/settings/advanced_parameters.py b/src/antares/craft/model/settings/advanced_parameters.py index 81fc0d5e..c73206e9 100644 --- a/src/antares/craft/model/settings/advanced_parameters.py +++ b/src/antares/craft/model/settings/advanced_parameters.py @@ -13,11 +13,11 @@ from enum import Enum from typing import Any, Optional +from pydantic import BaseModel, Field + from antares.craft.model.settings.general import OutputChoices from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model -from pydantic import BaseModel, Field -from pydantic.alias_generators import to_camel class InitialReservoirLevel(Enum): @@ -89,30 +89,6 @@ class SeedParameters: seed_initial_reservoir_levels: Optional[int] = None -@all_optional_model -class AdvancedAndSeedParametersAPI(BaseModel, alias_generator=to_camel): - accuracy_on_correlation: set[OutputChoices] - initial_reservoir_levels: InitialReservoirLevel - hydro_heuristic_policy: HydroHeuristicPolicy - hydro_pricing_mode: HydroPricingMode - power_fluctuations: PowerFluctuation - shedding_policy: SheddingPolicy - unit_commitment_mode: UnitCommitmentMode - number_of_cores_mode: SimulationCore - renewable_generation_modelling: RenewableGenerationModeling - seed_tsgen_wind: int - seed_tsgen_load: int - seed_tsgen_hydro: int - seed_tsgen_thermal: int - seed_tsgen_solar: int - seed_tsnumbers: int - seed_unsupplied_energy_costs: int - seed_spilled_energy_costs: int - seed_thermal_costs: int - seed_hydro_costs: int - seed_initial_reservoir_levels: int - - class OtherPreferencesLocalCreation(BaseModel, alias_generator=to_kebab): initial_reservoir_levels: InitialReservoirLevel hydro_heuristic_policy: HydroHeuristicPolicy diff --git a/src/antares/craft/model/settings/general.py b/src/antares/craft/model/settings/general.py index c7291d34..8751e540 100644 --- a/src/antares/craft/model/settings/general.py +++ b/src/antares/craft/model/settings/general.py @@ -12,10 +12,10 @@ from dataclasses import dataclass from typing import Optional +from pydantic import BaseModel, Field + from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase -from pydantic import BaseModel, Field -from pydantic.alias_generators import to_camel class Mode(EnumIgnoreCase): @@ -91,29 +91,6 @@ class GeneralParameters: nb_timeseries_thermal: Optional[int] = None -@all_optional_model -class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): - mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) - horizon: str - nb_years: int - first_day: int - last_day: int - first_january: WeekDay - first_month: Month - first_week_day: WeekDay - leap_year: bool - year_by_year: bool - building_mode: BuildingMode - selection_mode: bool - thematic_trimming: bool - geographic_trimming: bool - active_rules_scenario: str - read_only: bool - simulation_synthesis: bool - mc_scenario: bool - result_format: OutputFormat - - class GeneralSectionLocal(BaseModel): mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) horizon: str = "" diff --git a/src/antares/craft/model/settings/optimization.py b/src/antares/craft/model/settings/optimization.py index 85099db1..114ab54e 100644 --- a/src/antares/craft/model/settings/optimization.py +++ b/src/antares/craft/model/settings/optimization.py @@ -13,10 +13,10 @@ from enum import Enum from typing import Optional +from pydantic import BaseModel + from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model -from pydantic import BaseModel -from pydantic.alias_generators import to_camel class OptimizationTransmissionCapacities(Enum): @@ -63,23 +63,6 @@ class OptimizationParameters: include_unfeasible_problem_behavior: Optional[UnfeasibleProblemBehavior] = None -@all_optional_model -class OptimizationParametersAPI(BaseModel, alias_generator=to_camel): - simplex_optimization_range: SimplexOptimizationRange - transmission_capacities: OptimizationTransmissionCapacities - binding_constraints: bool - hurdle_costs: bool - thermal_clusters_min_stable_power: bool - thermal_clusters_min_ud_time: bool - day_ahead_reserve: bool - strategic_reserve: bool - spinning_reserve: bool - primary_reserve: bool - export_mps: ExportMPS - include_exportstructure: bool - unfeasible_problem_behavior: UnfeasibleProblemBehavior - - class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): simplex_range: SimplexOptimizationRange = SimplexOptimizationRange.WEEK transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES diff --git a/src/antares/craft/model/settings/thematic_trimming.py b/src/antares/craft/model/settings/thematic_trimming.py index 86ff37cd..4f8e60c0 100644 --- a/src/antares/craft/model/settings/thematic_trimming.py +++ b/src/antares/craft/model/settings/thematic_trimming.py @@ -13,10 +13,6 @@ from enum import Enum from typing import Optional -from antares.craft.tools.all_optional_meta import all_optional_model -from pydantic import BaseModel -from pydantic.alias_generators import to_camel - @dataclass class ThematicTrimmingParameters: @@ -116,104 +112,6 @@ class ThematicTrimmingParameters: sts_cashflow_by_cluster: Optional[bool] = None -@all_optional_model -class ThematicTrimmingParametersAPI(BaseModel, alias_generator=to_camel): - ov_cost: bool - op_cost: bool - mrg_price: bool - co2_emis: bool - dtg_by_plant: bool - balance: bool - row_bal: bool - psp: bool - misc_ndg: bool - load: bool - h_ror: bool - wind: bool - solar: bool - nuclear: bool - lignite: bool - coal: bool - gas: bool - oil: bool - mix_fuel: bool - misc_dtg: bool - h_stor: bool - h_pump: bool - h_lev: bool - h_infl: bool - h_ovfl: bool - h_val: bool - h_cost: bool - unsp_enrg: bool - spil_enrg: bool - lold: bool - lolp: bool - avl_dtg: bool - dtg_mrg: bool - max_mrg: bool - np_cost: bool - np_cost_by_plant: bool - nodu: bool - nodu_by_plant: bool - flow_lin: bool - ucap_lin: bool - loop_flow: bool - flow_quad: bool - cong_fee_alg: bool - cong_fee_abs: bool - marg_cost: bool - cong_prob_plus: bool - cong_prob_minus: bool - hurdle_cost: bool - res_generation_by_plant: bool - misc_dtg_2: bool - misc_dtg_3: bool - misc_dtg_4: bool - wind_offshore: bool - wind_onshore: bool - solar_concrt: bool - solar_pv: bool - solar_rooft: bool - renw_1: bool - renw_2: bool - renw_3: bool - renw_4: bool - dens: bool - profit_by_plant: bool - sts_inj_by_plant: bool - sts_withdrawal_by_plant: bool - sts_lvl_by_plant: bool - psp_open_injection: bool - psp_open_withdrawal: bool - psp_open_level: bool - psp_closed_injection: bool - psp_closed_withdrawal: bool - psp_closed_level: bool - pondage_injection: bool - pondage_withdrawal: bool - pondage_level: bool - battery_injection: bool - battery_withdrawal: bool - battery_level: bool - other1_injection: bool - other1_withdrawal: bool - other1_level: bool - other2_injection: bool - other2_withdrawal: bool - other2_level: bool - other3_injection: bool - other3_withdrawal: bool - other3_level: bool - other4_injection: bool - other4_withdrawal: bool - other4_level: bool - other5_injection: bool - other5_withdrawal: bool - other5_level: bool - sts_cashflow_by_cluster: bool - - class ThematicVarsLocal(Enum): balance = "BALANCE" dens = "DENS" diff --git a/src/antares/craft/model/st_storage.py b/src/antares/craft/model/st_storage.py index edd3bc65..cd6aa771 100644 --- a/src/antares/craft/model/st_storage.py +++ b/src/antares/craft/model/st_storage.py @@ -15,11 +15,12 @@ import pandas as pd -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 +from antares.craft.tools.all_optional_meta import all_optional_model +from antares.craft.tools.contents_tool import transform_name_to_id + class STStorageGroup(Enum): # todo: this class should disappear with Simulator version 9.1 diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py new file mode 100644 index 00000000..fb701554 --- /dev/null +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -0,0 +1,211 @@ +# 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 typing import Optional + +from pydantic import BaseModel, Field +from pydantic.alias_generators import to_camel + +from antares.craft.model.settings.adequacy_patch import PriceTakingOrder +from antares.craft.model.settings.advanced_parameters import ( + HydroHeuristicPolicy, + HydroPricingMode, + InitialReservoirLevel, + PowerFluctuation, + RenewableGenerationModeling, + SheddingPolicy, + SimulationCore, + UnitCommitmentMode, +) +from antares.craft.model.settings.general import BuildingMode, Mode, Month, OutputChoices, OutputFormat, WeekDay +from antares.craft.model.settings.optimization import ( + ExportMPS, + OptimizationTransmissionCapacities, + SimplexOptimizationRange, + UnfeasibleProblemBehavior, +) +from antares.craft.tools.all_optional_meta import all_optional_model + + +@all_optional_model +class AdequacyPatchParametersAPI(BaseModel, alias_generator=to_camel): + enable_adequacy_patch: Optional[bool] = None + ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch: bool = True + ntc_between_physical_areas_out_adequacy_patch: bool = True + price_taking_order: PriceTakingOrder = Field(default=PriceTakingOrder.DENS, validate_default=True) + include_hurdle_cost_csr: bool = False + check_csr_cost_function: bool = False + enable_first_step: bool = False + threshold_initiate_curtailment_sharing_rule: int = 0 + threshold_display_local_matching_rule_violations: int = 0 + threshold_csr_variable_bounds_relaxation: int = 3 + + +@all_optional_model +class AdvancedAndSeedParametersAPI(BaseModel, alias_generator=to_camel): + accuracy_on_correlation: set[OutputChoices] + initial_reservoir_levels: InitialReservoirLevel + hydro_heuristic_policy: HydroHeuristicPolicy + hydro_pricing_mode: HydroPricingMode + power_fluctuations: PowerFluctuation + shedding_policy: SheddingPolicy + unit_commitment_mode: UnitCommitmentMode + number_of_cores_mode: SimulationCore + renewable_generation_modelling: RenewableGenerationModeling + seed_tsgen_wind: int + seed_tsgen_load: int + seed_tsgen_hydro: int + seed_tsgen_thermal: int + seed_tsgen_solar: int + seed_tsnumbers: int + seed_unsupplied_energy_costs: int + seed_spilled_energy_costs: int + seed_thermal_costs: int + seed_hydro_costs: int + seed_initial_reservoir_levels: int + + +@all_optional_model +class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): + mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) + horizon: str + nb_years: int + first_day: int + last_day: int + first_january: WeekDay + first_month: Month + first_week_day: WeekDay + leap_year: bool + year_by_year: bool + building_mode: BuildingMode + selection_mode: bool + thematic_trimming: bool + geographic_trimming: bool + active_rules_scenario: str + read_only: bool + simulation_synthesis: bool + mc_scenario: bool + result_format: OutputFormat + + +@all_optional_model +class OptimizationParametersAPI(BaseModel, alias_generator=to_camel): + simplex_optimization_range: SimplexOptimizationRange + transmission_capacities: OptimizationTransmissionCapacities + binding_constraints: bool + hurdle_costs: bool + thermal_clusters_min_stable_power: bool + thermal_clusters_min_ud_time: bool + day_ahead_reserve: bool + strategic_reserve: bool + spinning_reserve: bool + primary_reserve: bool + export_mps: ExportMPS + include_exportstructure: bool + unfeasible_problem_behavior: UnfeasibleProblemBehavior + + +@all_optional_model +class ThematicTrimmingParametersAPI(BaseModel, alias_generator=to_camel): + ov_cost: bool + op_cost: bool + mrg_price: bool + co2_emis: bool + dtg_by_plant: bool + balance: bool + row_bal: bool + psp: bool + misc_ndg: bool + load: bool + h_ror: bool + wind: bool + solar: bool + nuclear: bool + lignite: bool + coal: bool + gas: bool + oil: bool + mix_fuel: bool + misc_dtg: bool + h_stor: bool + h_pump: bool + h_lev: bool + h_infl: bool + h_ovfl: bool + h_val: bool + h_cost: bool + unsp_enrg: bool + spil_enrg: bool + lold: bool + lolp: bool + avl_dtg: bool + dtg_mrg: bool + max_mrg: bool + np_cost: bool + np_cost_by_plant: bool + nodu: bool + nodu_by_plant: bool + flow_lin: bool + ucap_lin: bool + loop_flow: bool + flow_quad: bool + cong_fee_alg: bool + cong_fee_abs: bool + marg_cost: bool + cong_prob_plus: bool + cong_prob_minus: bool + hurdle_cost: bool + res_generation_by_plant: bool + misc_dtg_2: bool + misc_dtg_3: bool + misc_dtg_4: bool + wind_offshore: bool + wind_onshore: bool + solar_concrt: bool + solar_pv: bool + solar_rooft: bool + renw_1: bool + renw_2: bool + renw_3: bool + renw_4: bool + dens: bool + profit_by_plant: bool + sts_inj_by_plant: bool + sts_withdrawal_by_plant: bool + sts_lvl_by_plant: bool + psp_open_injection: bool + psp_open_withdrawal: bool + psp_open_level: bool + psp_closed_injection: bool + psp_closed_withdrawal: bool + psp_closed_level: bool + pondage_injection: bool + pondage_withdrawal: bool + pondage_level: bool + battery_injection: bool + battery_withdrawal: bool + battery_level: bool + other1_injection: bool + other1_withdrawal: bool + other1_level: bool + other2_injection: bool + other2_withdrawal: bool + other2_level: bool + other3_injection: bool + other3_withdrawal: bool + other3_level: bool + other4_injection: bool + other4_withdrawal: bool + other4_level: bool + other5_injection: bool + other5_withdrawal: bool + other5_level: bool + sts_cashflow_by_cluster: bool diff --git a/src/antares/craft/service/local_services/binding_constraint_local.py b/src/antares/craft/service/local_services/binding_constraint_local.py index 941a270c..a1e5ac74 100644 --- a/src/antares/craft/service/local_services/binding_constraint_local.py +++ b/src/antares/craft/service/local_services/binding_constraint_local.py @@ -14,6 +14,8 @@ import numpy as np import pandas as pd +from pydantic import Field + from antares.craft.config.local_configuration import LocalConfiguration from antares.craft.exceptions.exceptions import BindingConstraintCreationError from antares.craft.model.binding_constraint import ( @@ -29,7 +31,6 @@ from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes from antares.craft.tools.matrix_tool import df_read, df_save from antares.craft.tools.time_series_tool import TimeSeriesFileType -from pydantic import Field class BindingConstraintPropertiesLocal(DefaultBindingConstraintProperties): diff --git a/src/antares/craft/tools/contents_tool.py b/src/antares/craft/tools/contents_tool.py index 98963681..8d14836d 100644 --- a/src/antares/craft/tools/contents_tool.py +++ b/src/antares/craft/tools/contents_tool.py @@ -17,9 +17,10 @@ from pathlib import Path from typing import Any, Dict, Optional -from antares.craft.tools.custom_raw_config_parser import CustomRawConfigParser from pydantic import BaseModel +from antares.craft.tools.custom_raw_config_parser import CustomRawConfigParser + # Invalid chars was taken from Antares Simulator (C++). _sub_invalid_chars = re.compile(r"[^a-zA-Z0-9_(),& -]+").sub diff --git a/src/antares/craft/tools/ini_tool.py b/src/antares/craft/tools/ini_tool.py index 417e668c..1d188ff2 100644 --- a/src/antares/craft/tools/ini_tool.py +++ b/src/antares/craft/tools/ini_tool.py @@ -14,9 +14,10 @@ from pathlib import Path from typing import Any, Iterable, Optional, Union +from pydantic import BaseModel + from antares.craft.tools.custom_raw_config_parser import CustomRawConfigParser from antares.craft.tools.model_tools import filter_out_empty_model_fields -from pydantic import BaseModel class InitializationFilesTypes(Enum): From b30e77a5e482f1457c333f50139f0b748a20af04 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 17:58:12 +0100 Subject: [PATCH 09/69] move local classes --- .../craft/model/settings/adequacy_patch.py | 23 -- .../model/settings/advanced_parameters.py | 65 +--- src/antares/craft/model/settings/general.py | 53 ---- .../craft/model/settings/optimization.py | 26 -- .../craft/model/settings/thematic_trimming.py | 98 ------ .../local_services/study_settings_local.py | 280 ++++++++++++++++++ 6 files changed, 281 insertions(+), 264 deletions(-) create mode 100644 src/antares/craft/service/local_services/study_settings_local.py diff --git a/src/antares/craft/model/settings/adequacy_patch.py b/src/antares/craft/model/settings/adequacy_patch.py index 2ce88517..6a9d3234 100644 --- a/src/antares/craft/model/settings/adequacy_patch.py +++ b/src/antares/craft/model/settings/adequacy_patch.py @@ -13,11 +13,6 @@ from enum import Enum from typing import Optional -from pydantic import BaseModel - -from antares.craft.tools.alias_generators import to_kebab -from antares.craft.tools.all_optional_meta import all_optional_model - class PriceTakingOrder(Enum): DENS = "DENS" @@ -35,21 +30,3 @@ class AdequacyPatchParameters: threshold_initiate_curtailment_sharing_rule: Optional[int] = None threshold_display_local_matching_rule_violations: Optional[int] = None threshold_csr_variable_bounds_relaxation: Optional[int] = None - - -class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): - include_adq_patch: bool = False - set_to_null_ntc_from_physical_out_to_physical_in_for_first_step: bool = True - set_to_null_ntc_between_physical_out_for_first_step: bool = True - price_taking_order: PriceTakingOrder = PriceTakingOrder.DENS - include_hurdle_cost_csr: bool = False - check_csr_cost_function: bool = False - threshold_initiate_curtailment_sharing_rule: int = 0 - threshold_display_local_matching_rule_violations: int = 0 - threshold_csr_variable_bounds_relaxation: int = 3 - enable_first_step: bool = False - - -@all_optional_model -class AdequacyPatchParametersLocalEdition(AdequacyPatchParametersLocalCreation): - pass diff --git a/src/antares/craft/model/settings/advanced_parameters.py b/src/antares/craft/model/settings/advanced_parameters.py index c73206e9..5674f3e4 100644 --- a/src/antares/craft/model/settings/advanced_parameters.py +++ b/src/antares/craft/model/settings/advanced_parameters.py @@ -11,13 +11,9 @@ # This file is part of the Antares project. from dataclasses import dataclass from enum import Enum -from typing import Any, Optional - -from pydantic import BaseModel, Field +from typing import Optional from antares.craft.model.settings.general import OutputChoices -from antares.craft.tools.alias_generators import to_kebab -from antares.craft.tools.all_optional_meta import all_optional_model class InitialReservoirLevel(Enum): @@ -87,62 +83,3 @@ class SeedParameters: seed_thermal_costs: Optional[int] = None seed_hydro_costs: Optional[int] = None seed_initial_reservoir_levels: Optional[int] = None - - -class OtherPreferencesLocalCreation(BaseModel, alias_generator=to_kebab): - initial_reservoir_levels: InitialReservoirLevel - hydro_heuristic_policy: HydroHeuristicPolicy - hydro_pricing_mode: HydroPricingMode - power_fluctuations: PowerFluctuation - shedding_policy: SheddingPolicy - shedding_strategy: Any - unit_commitment_mode: UnitCommitmentMode - number_of_cores_mode: SimulationCore - renewable_generation_modelling: RenewableGenerationModeling - day_ahead_reserve_management: Any - - -@all_optional_model -class OtherPreferencesLocalEdition(OtherPreferencesLocalCreation): - pass - - -class AdvancedParametersLocalCreation(BaseModel, alias_generator=to_kebab): - accuracy_on_correlation: set[OutputChoices] = set() - adequacy_block_size: int = 100 - - -@all_optional_model -class AdvancedParametersLocalEdition(AdvancedParametersLocalCreation): - pass - - -class SeedParametersLocalCreation(BaseModel, alias_generator=to_kebab): - seed_tsgen_wind: int = 5489 - seed_tsgen_load: int = 1005489 - seed_tsgen_hydro: int = 2005489 - seed_tsgen_thermal: int = 3005489 - seed_tsgen_solar: int = 4005489 - seed_tsnumbers: int = 5005489 - seed_unsupplied_energy_costs: int = 6005489 - seed_spilled_energy_costs: int = 7005489 - seed_thermal_costs: int = 8005489 - seed_hydro_costs: int = 9005489 - seed_initial_reservoir_levels: int = 10005489 - - -@all_optional_model -class SeedParametersLocalEdition(SeedParametersLocalCreation): - pass - - -class AdvancedAndSeedParametersLocalCreation(BaseModel): - other_preferences: OtherPreferencesLocalCreation = Field(alias="other preferences") - advanced_parameters: AdvancedParametersLocalCreation = Field(alias="advanced parameters") - seeds: SeedParametersLocalCreation = Field(alias="seeds - Mersenne Twister") - - -class AdvancedAndSeedParametersLocalEdition(BaseModel): - other_preferences: OtherPreferencesLocalEdition = Field(default=None, alias="other preferences") - advanced_parameters: AdvancedParametersLocalEdition = Field(default_factory=None, alias="advanced parameters") - seeds: SeedParametersLocalEdition = Field(default_factory=None, alias="seeds - Mersenne Twister") diff --git a/src/antares/craft/model/settings/general.py b/src/antares/craft/model/settings/general.py index 8751e540..d5dc67b9 100644 --- a/src/antares/craft/model/settings/general.py +++ b/src/antares/craft/model/settings/general.py @@ -12,9 +12,6 @@ from dataclasses import dataclass from typing import Optional -from pydantic import BaseModel, Field - -from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase @@ -89,53 +86,3 @@ class GeneralParameters: thematic_trimming: Optional[bool] = None geographic_trimming: Optional[bool] = None nb_timeseries_thermal: Optional[int] = None - - -class GeneralSectionLocal(BaseModel): - mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) - horizon: str = "" - nb_years: int = Field(default=1, alias="nb.years") - simulation_start: int = Field(default=1, alias="simulation.start") - simulation_end: int = Field(default=365, alias="simulation.end") - first_january: WeekDay = Field(default=WeekDay.MONDAY, alias="january.1st") - first_month: Month = Field(default=Month.JANUARY, alias="first-month-in-year") - first_week_day: WeekDay = Field(default=WeekDay.MONDAY, alias="first.weekday") - leap_year: bool = Field(default=False, alias="leapyear") - year_by_year: bool = Field(default=False, alias="year-by-year") - derated: bool = False - custom_scenario: bool = Field(default=False, alias="custom-scenario") - user_playlist: bool = Field(default=False, alias="user-playlist") - thematic_trimming: bool = Field(default=False, alias="thematic-trimming") - geographic_trimming: bool = Field(default=False, alias="geographic-trimming") - generate: bool = False - nb_timeseries_load: int = Field(default=1, alias="nbtimeseriesload") - nb_timeseries_hydro: int = Field(default=1, alias="nbtimeserieshydro") - nb_timeseries_wind: int = Field(default=1, alias="nbtimeserieswind") - nb_timeseries_thermal: int = Field(default=1, alias="nbtimeseriesthermal") - nb_timeseries_solar: int = Field(default=1, alias="nbtimeseriessolar") - refresh_timeseries: bool = Field(default=False, alias="refreshtimeseries") - intra_model: bool = Field(default=False, alias="intra-model") - inter_model: bool = Field(default=False, alias="inter-model") - refresh_interval_load: int = Field(default=100, alias="refreshintervalload") - refresh_interval_hydro: int = Field(default=100, alias="refreshintervalhydro") - refresh_interval_wind: int = Field(default=100, alias="refreshintervalwind") - refresh_interval_thermal: int = Field(default=100, alias="refreshintervalthermal") - refresh_interval_solar: int = Field(default=100, alias="refreshintervalsolar") - read_only: bool = Field(default=False, alias="readonly") - - -class OutputSectionLocal(BaseModel): - synthesis: bool = True - store_new_set: bool = Field(default=True, alias="storenewset") - archives: set[OutputChoices] = set() - - -class GeneralParametersLocalCreation(BaseModel): - general: GeneralSectionLocal - input: dict = {"input": ""} - output: OutputSectionLocal - - -@all_optional_model -class GeneralParametersLocalEdition(GeneralParametersLocalCreation): - pass diff --git a/src/antares/craft/model/settings/optimization.py b/src/antares/craft/model/settings/optimization.py index 114ab54e..6c28b82e 100644 --- a/src/antares/craft/model/settings/optimization.py +++ b/src/antares/craft/model/settings/optimization.py @@ -13,11 +13,6 @@ from enum import Enum from typing import Optional -from pydantic import BaseModel - -from antares.craft.tools.alias_generators import to_kebab -from antares.craft.tools.all_optional_meta import all_optional_model - class OptimizationTransmissionCapacities(Enum): LOCAL_VALUES = "local-values" @@ -61,24 +56,3 @@ class OptimizationParameters: include_export_mps: Optional[ExportMPS] = None include_export_structure: Optional[bool] = None include_unfeasible_problem_behavior: Optional[UnfeasibleProblemBehavior] = None - - -class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): - simplex_range: SimplexOptimizationRange = SimplexOptimizationRange.WEEK - transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES - include_constraints: bool = True - include_hurdle_costs: bool = True - include_tc_min_stable_power: bool = True - include_tc_min_ud_time: bool = True - include_dayahead: bool = True - include_strategicreserve: bool = True - include_spinningreserve: bool = True - include_primaryreserve: bool = True - include_exportmps: bool = False - include_exportstructure: bool = False - include_unfeasible_problem_behavior: UnfeasibleProblemBehavior = UnfeasibleProblemBehavior.ERROR_VERBOSE - - -@all_optional_model -class OptimizationSettingsLocalEdition(OptimizationParametersLocalCreation): - pass diff --git a/src/antares/craft/model/settings/thematic_trimming.py b/src/antares/craft/model/settings/thematic_trimming.py index 4f8e60c0..7b8f3270 100644 --- a/src/antares/craft/model/settings/thematic_trimming.py +++ b/src/antares/craft/model/settings/thematic_trimming.py @@ -10,7 +10,6 @@ # # This file is part of the Antares project. from dataclasses import dataclass -from enum import Enum from typing import Optional @@ -110,100 +109,3 @@ class ThematicTrimmingParameters: other5_withdrawal: Optional[bool] = None other5_level: Optional[bool] = None sts_cashflow_by_cluster: Optional[bool] = None - - -class ThematicVarsLocal(Enum): - balance = "BALANCE" - dens = "DENS" - load = "LOAD" - lold = "LOLD" - lolp = "LOLP" - miscNdg = "MISC. NDG" - mrgPrice = "MRG. PRICE" - opCost = "OP. COST" - ovCost = "OV. COST" - rowBal = "ROW BAL." - spilEnrg = "SPIL. ENRG" - unspEnrg = "UNSP. ENRG" - hCost = "H. COST" - hInfl = "H. INFL" - hLev = "H. LEV" - hOvfl = "H. OVFL" - hPump = "H. PUMP" - hRor = "H. ROR" - hStor = "H. STOR" - hVal = "H. VAL" - psp = "PSP" - renw1 = "RENW. 1" - renw2 = "RENW. 2" - renw3 = "RENW. 3" - renw4 = "RENW. 4" - resGenerationByPlant = "RES generation by plant" - solar = "SOLAR" - solarConcrt = "SOLAR CONCRT." - solarPv = "SOLAR PV" - solarRooft = "SOLAR ROOFT" - wind = "WIND" - windOffshore = "WIND OFFSHORE" - windOnshore = "WIND ONSHORE" - batteryInjection = "BATTERY_INJECTION" - batteryLevel = "BATTERY_LEVEL" - batteryWithdrawal = "BATTERY_WITHDRAWAL" - other1Injection = "OTHER1_INJECTION" - other1Level = "OTHER1_LEVEL" - other1Withdrawal = "OTHER1_WITHDRAWAL" - other2Injection = "OTHER2_INJECTION" - other2Level = "OTHER2_LEVEL" - other2Withdrawal = "OTHER2_WITHDRAWAL" - other3Injection = "OTHER3_INJECTION" - other3Level = "OTHER3_LEVEL" - other3Withdrawal = "OTHER3_WITHDRAWAL" - other4Injection = "OTHER4_INJECTION" - other4Level = "OTHER4_LEVEL" - other4Withdrawal = "OTHER4_WITHDRAWAL" - other5Injection = "OTHER5_INJECTION" - other5Level = "OTHER5_LEVEL" - other5Withdrawal = "OTHER5_WITHDRAWAL" - pondageInjection = "PONDAGE_INJECTION" - pondageLevel = "PONDAGE_LEVEL" - pondageWithdrawal = "PONDAGE_WITHDRAWAL" - pspClosedInjection = "PSP_CLOSED_INJECTION" - pspClosedLevel = "PSP_CLOSED_LEVEL" - pspClosedWithdrawal = "PSP_CLOSED_WITHDRAWAL" - pspOpenInjection = "PSP_OPEN_INJECTION" - pspOpenLevel = "PSP_OPEN_LEVEL" - pspOpenWithdrawal = "PSP_OPEN_WITHDRAWAL" - stsCashflowByCluster = "STS CASHFLOW BY CLUSTER" - stsInjByPlant = "STS inj by plant" - stsLvlByPlant = "STS lvl by plant" - stsWithdrawalByPlant = "STS withdrawal by plant" - avlDtg = "AVL DTG" - co2Emis = "CO2 EMIS." - coal = "COAL" - dtgByPlant = "DTG by plant" - dtgMrg = "DTG MRG" - gas = "GAS" - lignite = "LIGNITE" - maxMrg = "MAX MRG" - miscDtg = "MISC. DTG" - miscDtg2 = "MISC. DTG 2" - miscDtg3 = "MISC. DTG 3" - miscDtg4 = "MISC. DTG 4" - mixFuel = "MIX. FUEL" - nodu = "NODU" - noduByPlant = "NODU by plant" - npCost = "NP COST" - npCostByPlant = "NP Cost by plant" - nuclear = "NUCLEAR" - oil = "OIL" - profitByPlant = "Profit by plant" - congFeeAbs = "CONG. FEE (ABS.)" - congFeeAlg = "CONG. FEE (ALG.)" - congProbMinus = "CONG. PROB -" - congProbPlus = "CONG. PROB +" - flowLin = "FLOW LIN." - flowQuad = "FLOW QUAD." - hurdleCost = "HURDLE COST" - loopFlow = "LOOP FLOW" - margCost = "MARG. COST" - ucapLin = "UCAP LIN." diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py new file mode 100644 index 00000000..3cb770ae --- /dev/null +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -0,0 +1,280 @@ +# 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 enum import Enum +from typing import Any + +from pydantic import BaseModel, Field + +from antares.craft.model.settings.adequacy_patch import PriceTakingOrder +from antares.craft.model.settings.advanced_parameters import ( + HydroHeuristicPolicy, + HydroPricingMode, + InitialReservoirLevel, + PowerFluctuation, + RenewableGenerationModeling, + SheddingPolicy, + SimulationCore, + UnitCommitmentMode, +) +from antares.craft.model.settings.general import Mode, Month, OutputChoices, WeekDay +from antares.craft.model.settings.optimization import ( + OptimizationTransmissionCapacities, + SimplexOptimizationRange, + UnfeasibleProblemBehavior, +) +from antares.craft.tools.alias_generators import to_kebab +from antares.craft.tools.all_optional_meta import all_optional_model + + +class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): + include_adq_patch: bool = False + set_to_null_ntc_from_physical_out_to_physical_in_for_first_step: bool = True + set_to_null_ntc_between_physical_out_for_first_step: bool = True + price_taking_order: PriceTakingOrder = PriceTakingOrder.DENS + include_hurdle_cost_csr: bool = False + check_csr_cost_function: bool = False + threshold_initiate_curtailment_sharing_rule: int = 0 + threshold_display_local_matching_rule_violations: int = 0 + threshold_csr_variable_bounds_relaxation: int = 3 + enable_first_step: bool = False + + +@all_optional_model +class AdequacyPatchParametersLocalEdition(AdequacyPatchParametersLocalCreation): + pass + + +class OtherPreferencesLocalCreation(BaseModel, alias_generator=to_kebab): + initial_reservoir_levels: InitialReservoirLevel + hydro_heuristic_policy: HydroHeuristicPolicy + hydro_pricing_mode: HydroPricingMode + power_fluctuations: PowerFluctuation + shedding_policy: SheddingPolicy + shedding_strategy: Any + unit_commitment_mode: UnitCommitmentMode + number_of_cores_mode: SimulationCore + renewable_generation_modelling: RenewableGenerationModeling + day_ahead_reserve_management: Any + + +@all_optional_model +class OtherPreferencesLocalEdition(OtherPreferencesLocalCreation): + pass + + +class AdvancedParametersLocalCreation(BaseModel, alias_generator=to_kebab): + accuracy_on_correlation: set[OutputChoices] = set() + adequacy_block_size: int = 100 + + +@all_optional_model +class AdvancedParametersLocalEdition(AdvancedParametersLocalCreation): + pass + + +class SeedParametersLocalCreation(BaseModel, alias_generator=to_kebab): + seed_tsgen_wind: int = 5489 + seed_tsgen_load: int = 1005489 + seed_tsgen_hydro: int = 2005489 + seed_tsgen_thermal: int = 3005489 + seed_tsgen_solar: int = 4005489 + seed_tsnumbers: int = 5005489 + seed_unsupplied_energy_costs: int = 6005489 + seed_spilled_energy_costs: int = 7005489 + seed_thermal_costs: int = 8005489 + seed_hydro_costs: int = 9005489 + seed_initial_reservoir_levels: int = 10005489 + + +@all_optional_model +class SeedParametersLocalEdition(SeedParametersLocalCreation): + pass + + +class AdvancedAndSeedParametersLocalCreation(BaseModel): + other_preferences: OtherPreferencesLocalCreation = Field(alias="other preferences") + advanced_parameters: AdvancedParametersLocalCreation = Field(alias="advanced parameters") + seeds: SeedParametersLocalCreation = Field(alias="seeds - Mersenne Twister") + + +class AdvancedAndSeedParametersLocalEdition(BaseModel): + other_preferences: OtherPreferencesLocalEdition = Field(default=None, alias="other preferences") + advanced_parameters: AdvancedParametersLocalEdition = Field(default_factory=None, alias="advanced parameters") + seeds: SeedParametersLocalEdition = Field(default_factory=None, alias="seeds - Mersenne Twister") + + +class GeneralSectionLocal(BaseModel): + mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) + horizon: str = "" + nb_years: int = Field(default=1, alias="nb.years") + simulation_start: int = Field(default=1, alias="simulation.start") + simulation_end: int = Field(default=365, alias="simulation.end") + first_january: WeekDay = Field(default=WeekDay.MONDAY, alias="january.1st") + first_month: Month = Field(default=Month.JANUARY, alias="first-month-in-year") + first_week_day: WeekDay = Field(default=WeekDay.MONDAY, alias="first.weekday") + leap_year: bool = Field(default=False, alias="leapyear") + year_by_year: bool = Field(default=False, alias="year-by-year") + derated: bool = False + custom_scenario: bool = Field(default=False, alias="custom-scenario") + user_playlist: bool = Field(default=False, alias="user-playlist") + thematic_trimming: bool = Field(default=False, alias="thematic-trimming") + geographic_trimming: bool = Field(default=False, alias="geographic-trimming") + generate: bool = False + nb_timeseries_load: int = Field(default=1, alias="nbtimeseriesload") + nb_timeseries_hydro: int = Field(default=1, alias="nbtimeserieshydro") + nb_timeseries_wind: int = Field(default=1, alias="nbtimeserieswind") + nb_timeseries_thermal: int = Field(default=1, alias="nbtimeseriesthermal") + nb_timeseries_solar: int = Field(default=1, alias="nbtimeseriessolar") + refresh_timeseries: bool = Field(default=False, alias="refreshtimeseries") + intra_model: bool = Field(default=False, alias="intra-model") + inter_model: bool = Field(default=False, alias="inter-model") + refresh_interval_load: int = Field(default=100, alias="refreshintervalload") + refresh_interval_hydro: int = Field(default=100, alias="refreshintervalhydro") + refresh_interval_wind: int = Field(default=100, alias="refreshintervalwind") + refresh_interval_thermal: int = Field(default=100, alias="refreshintervalthermal") + refresh_interval_solar: int = Field(default=100, alias="refreshintervalsolar") + read_only: bool = Field(default=False, alias="readonly") + + +class OutputSectionLocal(BaseModel): + synthesis: bool = True + store_new_set: bool = Field(default=True, alias="storenewset") + archives: set[OutputChoices] = set() + + +class GeneralParametersLocalCreation(BaseModel): + general: GeneralSectionLocal + input: dict = {"input": ""} + output: OutputSectionLocal + + +@all_optional_model +class GeneralParametersLocalEdition(GeneralParametersLocalCreation): + pass + + +class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): + simplex_range: SimplexOptimizationRange = SimplexOptimizationRange.WEEK + transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES + include_constraints: bool = True + include_hurdle_costs: bool = True + include_tc_min_stable_power: bool = True + include_tc_min_ud_time: bool = True + include_dayahead: bool = True + include_strategicreserve: bool = True + include_spinningreserve: bool = True + include_primaryreserve: bool = True + include_exportmps: bool = False + include_exportstructure: bool = False + include_unfeasible_problem_behavior: UnfeasibleProblemBehavior = UnfeasibleProblemBehavior.ERROR_VERBOSE + + +@all_optional_model +class OptimizationSettingsLocalEdition(OptimizationParametersLocalCreation): + pass + + +class ThematicVarsLocal(Enum): + balance = "BALANCE" + dens = "DENS" + load = "LOAD" + lold = "LOLD" + lolp = "LOLP" + miscNdg = "MISC. NDG" + mrgPrice = "MRG. PRICE" + opCost = "OP. COST" + ovCost = "OV. COST" + rowBal = "ROW BAL." + spilEnrg = "SPIL. ENRG" + unspEnrg = "UNSP. ENRG" + hCost = "H. COST" + hInfl = "H. INFL" + hLev = "H. LEV" + hOvfl = "H. OVFL" + hPump = "H. PUMP" + hRor = "H. ROR" + hStor = "H. STOR" + hVal = "H. VAL" + psp = "PSP" + renw1 = "RENW. 1" + renw2 = "RENW. 2" + renw3 = "RENW. 3" + renw4 = "RENW. 4" + resGenerationByPlant = "RES generation by plant" + solar = "SOLAR" + solarConcrt = "SOLAR CONCRT." + solarPv = "SOLAR PV" + solarRooft = "SOLAR ROOFT" + wind = "WIND" + windOffshore = "WIND OFFSHORE" + windOnshore = "WIND ONSHORE" + batteryInjection = "BATTERY_INJECTION" + batteryLevel = "BATTERY_LEVEL" + batteryWithdrawal = "BATTERY_WITHDRAWAL" + other1Injection = "OTHER1_INJECTION" + other1Level = "OTHER1_LEVEL" + other1Withdrawal = "OTHER1_WITHDRAWAL" + other2Injection = "OTHER2_INJECTION" + other2Level = "OTHER2_LEVEL" + other2Withdrawal = "OTHER2_WITHDRAWAL" + other3Injection = "OTHER3_INJECTION" + other3Level = "OTHER3_LEVEL" + other3Withdrawal = "OTHER3_WITHDRAWAL" + other4Injection = "OTHER4_INJECTION" + other4Level = "OTHER4_LEVEL" + other4Withdrawal = "OTHER4_WITHDRAWAL" + other5Injection = "OTHER5_INJECTION" + other5Level = "OTHER5_LEVEL" + other5Withdrawal = "OTHER5_WITHDRAWAL" + pondageInjection = "PONDAGE_INJECTION" + pondageLevel = "PONDAGE_LEVEL" + pondageWithdrawal = "PONDAGE_WITHDRAWAL" + pspClosedInjection = "PSP_CLOSED_INJECTION" + pspClosedLevel = "PSP_CLOSED_LEVEL" + pspClosedWithdrawal = "PSP_CLOSED_WITHDRAWAL" + pspOpenInjection = "PSP_OPEN_INJECTION" + pspOpenLevel = "PSP_OPEN_LEVEL" + pspOpenWithdrawal = "PSP_OPEN_WITHDRAWAL" + stsCashflowByCluster = "STS CASHFLOW BY CLUSTER" + stsInjByPlant = "STS inj by plant" + stsLvlByPlant = "STS lvl by plant" + stsWithdrawalByPlant = "STS withdrawal by plant" + avlDtg = "AVL DTG" + co2Emis = "CO2 EMIS." + coal = "COAL" + dtgByPlant = "DTG by plant" + dtgMrg = "DTG MRG" + gas = "GAS" + lignite = "LIGNITE" + maxMrg = "MAX MRG" + miscDtg = "MISC. DTG" + miscDtg2 = "MISC. DTG 2" + miscDtg3 = "MISC. DTG 3" + miscDtg4 = "MISC. DTG 4" + mixFuel = "MIX. FUEL" + nodu = "NODU" + noduByPlant = "NODU by plant" + npCost = "NP COST" + npCostByPlant = "NP Cost by plant" + nuclear = "NUCLEAR" + oil = "OIL" + profitByPlant = "Profit by plant" + congFeeAbs = "CONG. FEE (ABS.)" + congFeeAlg = "CONG. FEE (ALG.)" + congProbMinus = "CONG. PROB -" + congProbPlus = "CONG. PROB +" + flowLin = "FLOW LIN." + flowQuad = "FLOW QUAD." + hurdleCost = "HURDLE COST" + loopFlow = "LOOP FLOW" + margCost = "MARG. COST" + ucapLin = "UCAP LIN." From 6b98c00884f149f4ce7a6b1591393e58cfe10d2d Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 18:00:41 +0100 Subject: [PATCH 10/69] remove useless file --- src/antares/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/antares/__init__.py diff --git a/src/antares/__init__.py b/src/antares/__init__.py deleted file mode 100644 index e69de29b..00000000 From f22c4f7255d606e8764a638197ce5557953257cd Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 18:17:07 +0100 Subject: [PATCH 11/69] create first method --- src/antares/craft/model/area.py | 5 +-- src/antares/craft/model/binding_constraint.py | 5 +-- src/antares/craft/model/hydro.py | 3 +- src/antares/craft/model/link.py | 3 +- src/antares/craft/model/st_storage.py | 5 +-- .../api_services/study_settings_api.py | 38 +++++++++++++++++-- .../binding_constraint_local.py | 3 +- .../local_services/study_settings_local.py | 3 +- src/antares/craft/tools/contents_tool.py | 3 +- src/antares/craft/tools/ini_tool.py | 3 +- 10 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/antares/craft/model/area.py b/src/antares/craft/model/area.py index ce05fbfd..3742895e 100644 --- a/src/antares/craft/model/area.py +++ b/src/antares/craft/model/area.py @@ -21,9 +21,6 @@ import pandas as pd -from pydantic import BaseModel, computed_field -from pydantic.alias_generators import to_camel - from antares.craft.model.commons import FilterOption, sort_filter_values from antares.craft.model.hydro import Hydro, HydroMatrixName, HydroProperties from antares.craft.model.renewable import RenewableCluster, RenewableClusterProperties @@ -32,6 +29,8 @@ from antares.craft.tools.alias_generators import to_space from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase, transform_name_to_id +from pydantic import BaseModel, computed_field +from pydantic.alias_generators import to_camel class AdequacyPatchMode(EnumIgnoreCase): diff --git a/src/antares/craft/model/binding_constraint.py b/src/antares/craft/model/binding_constraint.py index 82e09207..667415f0 100644 --- a/src/antares/craft/model/binding_constraint.py +++ b/src/antares/craft/model/binding_constraint.py @@ -15,11 +15,10 @@ import pandas as pd -from pydantic import BaseModel, Field, model_validator -from pydantic.alias_generators import to_camel - from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase, transform_name_to_id +from pydantic import BaseModel, Field, model_validator +from pydantic.alias_generators import to_camel class BindingConstraintFrequency(EnumIgnoreCase): diff --git a/src/antares/craft/model/hydro.py b/src/antares/craft/model/hydro.py index e3788a45..44418ed9 100644 --- a/src/antares/craft/model/hydro.py +++ b/src/antares/craft/model/hydro.py @@ -15,11 +15,10 @@ import pandas as pd +from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel from pydantic.alias_generators import to_camel -from antares.craft.tools.all_optional_meta import all_optional_model - class HydroMatrixName(Enum): SERIES_ROR = "ror" diff --git a/src/antares/craft/model/link.py b/src/antares/craft/model/link.py index 0c86ae08..b7f00141 100644 --- a/src/antares/craft/model/link.py +++ b/src/antares/craft/model/link.py @@ -15,12 +15,11 @@ import pandas as pd -from pydantic import BaseModel - from antares.craft.model.commons import FilterOption, sort_filter_values from antares.craft.tools.alias_generators import to_kebab 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 class TransmissionCapacities(Enum): diff --git a/src/antares/craft/model/st_storage.py b/src/antares/craft/model/st_storage.py index cd6aa771..edd3bc65 100644 --- a/src/antares/craft/model/st_storage.py +++ b/src/antares/craft/model/st_storage.py @@ -15,11 +15,10 @@ import pandas as pd -from pydantic import BaseModel -from pydantic.alias_generators import to_camel - 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): diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index fb701554..19784cae 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -11,9 +11,6 @@ # This file is part of the Antares project. from typing import Optional -from pydantic import BaseModel, Field -from pydantic.alias_generators import to_camel - from antares.craft.model.settings.adequacy_patch import PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( HydroHeuristicPolicy, @@ -25,7 +22,15 @@ SimulationCore, UnitCommitmentMode, ) -from antares.craft.model.settings.general import BuildingMode, Mode, Month, OutputChoices, OutputFormat, WeekDay +from antares.craft.model.settings.general import ( + BuildingMode, + GeneralParameters, + Mode, + Month, + OutputChoices, + OutputFormat, + WeekDay, +) from antares.craft.model.settings.optimization import ( ExportMPS, OptimizationTransmissionCapacities, @@ -33,6 +38,8 @@ UnfeasibleProblemBehavior, ) from antares.craft.tools.all_optional_meta import all_optional_model +from pydantic import BaseModel, Field +from pydantic.alias_generators import to_camel @all_optional_model @@ -95,6 +102,29 @@ class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, ali mc_scenario: bool result_format: OutputFormat + def from_user_model(self, user_class: GeneralParameters) -> "GeneralParametersAPI": + pass + + def to_user_model(self, nb_ts_thermal: int) -> GeneralParameters: + return GeneralParameters( + mode=self.mode, + horizon=self.horizon, + nb_years=self.nb_years, + simulation_start=self.first_day, + simulation_end=self.last_day, + january_first=self.first_january, + first_month_in_year=self.first_month, + first_week_day=self.first_week_day, + leap_year=self.leap_year, + year_by_year=self.year_by_year, + simulation_synthesis=self.simulation_synthesis, + building_mode=self.building_mode, + user_playlist=self.selection_mode, + thematic_trimming=self.thematic_trimming, + geographic_trimming=self.geographic_trimming, + nb_timeseries_thermal=nb_ts_thermal, + ) + @all_optional_model class OptimizationParametersAPI(BaseModel, alias_generator=to_camel): diff --git a/src/antares/craft/service/local_services/binding_constraint_local.py b/src/antares/craft/service/local_services/binding_constraint_local.py index a1e5ac74..941a270c 100644 --- a/src/antares/craft/service/local_services/binding_constraint_local.py +++ b/src/antares/craft/service/local_services/binding_constraint_local.py @@ -14,8 +14,6 @@ import numpy as np import pandas as pd -from pydantic import Field - from antares.craft.config.local_configuration import LocalConfiguration from antares.craft.exceptions.exceptions import BindingConstraintCreationError from antares.craft.model.binding_constraint import ( @@ -31,6 +29,7 @@ from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes from antares.craft.tools.matrix_tool import df_read, df_save from antares.craft.tools.time_series_tool import TimeSeriesFileType +from pydantic import Field class BindingConstraintPropertiesLocal(DefaultBindingConstraintProperties): diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 3cb770ae..fe997c64 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -12,8 +12,6 @@ from enum import Enum from typing import Any -from pydantic import BaseModel, Field - from antares.craft.model.settings.adequacy_patch import PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( HydroHeuristicPolicy, @@ -33,6 +31,7 @@ ) from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model +from pydantic import BaseModel, Field class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): diff --git a/src/antares/craft/tools/contents_tool.py b/src/antares/craft/tools/contents_tool.py index 8d14836d..98963681 100644 --- a/src/antares/craft/tools/contents_tool.py +++ b/src/antares/craft/tools/contents_tool.py @@ -17,9 +17,8 @@ from pathlib import Path from typing import Any, Dict, Optional -from pydantic import BaseModel - from antares.craft.tools.custom_raw_config_parser import CustomRawConfigParser +from pydantic import BaseModel # Invalid chars was taken from Antares Simulator (C++). _sub_invalid_chars = re.compile(r"[^a-zA-Z0-9_(),& -]+").sub diff --git a/src/antares/craft/tools/ini_tool.py b/src/antares/craft/tools/ini_tool.py index 1d188ff2..417e668c 100644 --- a/src/antares/craft/tools/ini_tool.py +++ b/src/antares/craft/tools/ini_tool.py @@ -14,10 +14,9 @@ from pathlib import Path from typing import Any, Iterable, Optional, Union -from pydantic import BaseModel - from antares.craft.tools.custom_raw_config_parser import CustomRawConfigParser from antares.craft.tools.model_tools import filter_out_empty_model_fields +from pydantic import BaseModel class InitializationFilesTypes(Enum): From 0f4dd5c717504b7b2ebaf76109942d4785ceda37 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 18:21:18 +0100 Subject: [PATCH 12/69] make one test ok --- src/antares/craft/model/study.py | 5 +++-- src/antares/craft/service/api_services/study_api.py | 5 +++-- src/antares/craft/service/api_services/study_settings_api.py | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 3aabb6d7..92abb902 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -14,6 +14,7 @@ import os import time +from dataclasses import asdict from pathlib import Path, PurePath from types import MappingProxyType from typing import List, Optional, Union @@ -138,9 +139,9 @@ def create_study_local( with open(desktop_ini_path, "w") as desktop_ini_file: desktop_ini_file.write(desktop_ini_content) - local_settings = StudySettings.model_validate(settings) + local_settings = settings local_settings_file = IniFile(study_directory, InitializationFilesTypes.GENERAL) - local_settings_file.ini_dict = local_settings.model_dump(exclude_none=True, by_alias=True) + local_settings_file.ini_dict = asdict(local_settings) local_settings_file.write_ini_file() # Create various .ini files for the study diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 2f3d2772..b9df33e5 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -9,6 +9,7 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import asdict from pathlib import Path, PurePath from typing import TYPE_CHECKING, Optional @@ -58,7 +59,7 @@ def _returns_study_settings( "playlist_parameters": ("playlist", PlaylistParameters), } if settings: - json_settings = settings.model_dump(mode="json", by_alias=True, exclude_none=True) + json_settings = asdict(settings) if not json_settings and update: return None @@ -78,7 +79,7 @@ def _returns_study_settings( settings_property = settings_class.model_validate(response.json()) # type: ignore json_settings[settings_type] = settings_property - return StudySettings.model_validate(json_settings) + return StudySettings(**json_settings) class StudyApiService(BaseStudyService): diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index 19784cae..2d6cf6f5 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -9,6 +9,7 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import asdict from typing import Optional from antares.craft.model.settings.adequacy_patch import PriceTakingOrder @@ -103,7 +104,9 @@ class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, ali result_format: OutputFormat def from_user_model(self, user_class: GeneralParameters) -> "GeneralParametersAPI": - pass + user_class_as_dict = asdict(user_class) + print(user_class_as_dict) + raise NotImplementedError def to_user_model(self, nb_ts_thermal: int) -> GeneralParameters: return GeneralParameters( From 23ebfa1541d63fee84b70d764597a4751b647366 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 18:38:35 +0100 Subject: [PATCH 13/69] continue work for API --- .../service/api_services/study_settings_api.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index 2d6cf6f5..b855f7a4 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -103,10 +103,17 @@ class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, ali mc_scenario: bool result_format: OutputFormat - def from_user_model(self, user_class: GeneralParameters) -> "GeneralParametersAPI": - user_class_as_dict = asdict(user_class) - print(user_class_as_dict) - raise NotImplementedError + @staticmethod + def from_user_model(user_class: GeneralParameters) -> "GeneralParametersAPI": + user_dict = asdict(user_class) + user_dict["first_day"] = user_dict.pop("simulation_start") + user_dict["last_day"] = user_dict.pop("simulation_end") + user_dict["first_january"] = user_dict.pop("january_first") + user_dict["first_month"] = user_dict.pop("first_month_in_year") + user_dict["first_january"] = user_dict.pop("january_first") + user_dict["selection_mode"] = user_dict.pop("user_playlist") + user_dict.pop("nb_timeseries_thermal") + return GeneralParametersAPI.model_validate(user_dict) def to_user_model(self, nb_ts_thermal: int) -> GeneralParameters: return GeneralParameters( From 658062cc395a3443c623d8d9bfd1cea13b8b7c32 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 18:46:57 +0100 Subject: [PATCH 14/69] continue work --- .../api_services/study_settings_api.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index b855f7a4..4ae29316 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -12,7 +12,7 @@ from dataclasses import asdict from typing import Optional -from antares.craft.model.settings.adequacy_patch import PriceTakingOrder +from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( HydroHeuristicPolicy, HydroPricingMode, @@ -56,6 +56,31 @@ class AdequacyPatchParametersAPI(BaseModel, alias_generator=to_camel): threshold_display_local_matching_rule_violations: int = 0 threshold_csr_variable_bounds_relaxation: int = 3 + @staticmethod + def from_user_model(user_class: AdequacyPatchParameters) -> "AdequacyPatchParametersAPI": + user_dict = asdict(user_class) + user_dict["enable_adequacy_patch"] = user_dict.pop("include_adq_patch") + user_dict["ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch"] = user_dict.pop( + "set_to_null_ntc_from_physical_out_to_physical_in_for_first_step" + ) + user_dict["ntc_between_physical_areas_out_adequacy_patch"] = user_dict.pop( + "set_to_null_ntc_between_physical_out_for_first_step" + ) + return AdequacyPatchParametersAPI.model_validate(user_dict) + + def to_user_model(self) -> AdequacyPatchParameters: + return AdequacyPatchParameters( + include_adq_patch=self.enable_adequacy_patch, + set_to_null_ntc_from_physical_out_to_physical_in_for_first_step=self.ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch, + set_to_null_ntc_between_physical_out_for_first_step=self.ntc_between_physical_areas_out_adequacy_patch, + price_taking_order=self.price_taking_order, + include_hurdle_cost_csr=self.include_hurdle_cost_csr, + check_csr_cost_function=self.check_csr_cost_function, + threshold_initiate_curtailment_sharing_rule=self.threshold_initiate_curtailment_sharing_rule, + threshold_display_local_matching_rule_violations=self.threshold_display_local_matching_rule_violations, + threshold_csr_variable_bounds_relaxation=self.threshold_csr_variable_bounds_relaxation, + ) + @all_optional_model class AdvancedAndSeedParametersAPI(BaseModel, alias_generator=to_camel): From a5383c8ce0426350308ac8331bf460414ad27cb1 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 18:52:14 +0100 Subject: [PATCH 15/69] continue work for API --- .../api_services/study_settings_api.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index 4ae29316..dfc03a8b 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -34,6 +34,7 @@ ) from antares.craft.model.settings.optimization import ( ExportMPS, + OptimizationParameters, OptimizationTransmissionCapacities, SimplexOptimizationRange, UnfeasibleProblemBehavior, @@ -177,6 +178,40 @@ class OptimizationParametersAPI(BaseModel, alias_generator=to_camel): include_exportstructure: bool unfeasible_problem_behavior: UnfeasibleProblemBehavior + @staticmethod + def from_user_model(user_class: OptimizationParameters) -> "OptimizationParametersAPI": + user_dict = asdict(user_class) + user_dict["simplex_optimization_range"] = user_dict.pop("simplex_range") + user_dict["binding_constraints"] = user_dict.pop("include_constraints") + user_dict["hurdle_costs"] = user_dict.pop("include_hurdle_costs") + user_dict["thermal_clusters_min_stable_power"] = user_dict.pop("include_thermal_cluster_min_stable_power") + user_dict["thermal_clusters_min_ud_time"] = user_dict.pop("include_thermal_cluster_min_ud_time") + user_dict["day_ahead_reserve"] = user_dict.pop("include_day_ahead") + user_dict["strategic_reserve"] = user_dict.pop("include_strategic_reserve") + user_dict["spinning_reserve"] = user_dict.pop("include_spinning_reserve") + user_dict["primary_reserve"] = user_dict.pop("include_primary_reserve") + user_dict["export_mps"] = user_dict.pop("include_export_mps") + user_dict["include_exportstructure"] = user_dict.pop("include_export_structure") + user_dict["unfeasible_problem_behavior"] = user_dict.pop("include_unfeasible_problem_behavior") + return OptimizationParametersAPI.model_validate(user_dict) + + def to_user_model(self) -> OptimizationParameters: + return OptimizationParameters( + simplex_range=self.simplex_optimization_range, + transmission_capacities=self.transmission_capacities, + include_constraints=self.binding_constraints, + include_hurdle_costs=self.hurdle_costs, + include_thermal_cluster_min_stable_power=self.thermal_clusters_min_stable_power, + include_thermal_cluster_min_ud_time=self.thermal_clusters_min_ud_time, + include_day_ahead=self.day_ahead_reserve, + include_strategic_reserve=self.strategic_reserve, + include_spinning_reserve=self.spinning_reserve, + include_primary_reserve=self.primary_reserve, + include_export_mps=self.export_mps, + include_export_structure=self.include_exportstructure, + include_unfeasible_problem_behavior=self.unfeasible_problem_behavior, + ) + @all_optional_model class ThematicTrimmingParametersAPI(BaseModel, alias_generator=to_camel): From 9f254cbf22353c45859d18c44c918ffbdf14e829 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Thu, 30 Jan 2025 18:54:55 +0100 Subject: [PATCH 16/69] continue work api --- .../craft/service/api_services/study_settings_api.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index dfc03a8b..37af2b5f 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -39,6 +39,7 @@ SimplexOptimizationRange, UnfeasibleProblemBehavior, ) +from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel, Field from pydantic.alias_generators import to_camel @@ -309,3 +310,11 @@ class ThematicTrimmingParametersAPI(BaseModel, alias_generator=to_camel): other5_withdrawal: bool other5_level: bool sts_cashflow_by_cluster: bool + + @staticmethod + def from_user_model(user_class: ThematicTrimmingParameters) -> "ThematicTrimmingParametersAPI": + user_dict = asdict(user_class) + return ThematicTrimmingParametersAPI.model_validate(user_dict) + + def to_user_model(self) -> ThematicTrimmingParameters: + return ThematicTrimmingParameters(**self.model_dump(mode="json")) From 2b4657a28d1ad3803e5e9e0ed827ca556fede475 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 11:12:03 +0100 Subject: [PATCH 17/69] continue --- .../craft/model/settings/advanced_parameters.py | 2 +- .../craft/service/api_services/study_settings_api.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/antares/craft/model/settings/advanced_parameters.py b/src/antares/craft/model/settings/advanced_parameters.py index 5674f3e4..91a12b53 100644 --- a/src/antares/craft/model/settings/advanced_parameters.py +++ b/src/antares/craft/model/settings/advanced_parameters.py @@ -76,7 +76,7 @@ class AdvancedParameters: @dataclass class SeedParameters: - seed_ts_gen_thermal: Optional[int] = None + seed_tsgen_thermal: Optional[int] = None seed_ts_numbers: Optional[int] = None seed_unsupplied_energy_costs: Optional[int] = None seed_spilled_energy_costs: Optional[int] = None diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index 37af2b5f..fdd8c101 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -14,11 +14,13 @@ from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( + AdvancedParameters, HydroHeuristicPolicy, HydroPricingMode, InitialReservoirLevel, PowerFluctuation, RenewableGenerationModeling, + SeedParameters, SheddingPolicy, SimulationCore, UnitCommitmentMode, @@ -107,6 +109,15 @@ class AdvancedAndSeedParametersAPI(BaseModel, alias_generator=to_camel): seed_hydro_costs: int seed_initial_reservoir_levels: int + @staticmethod + def from_user_model( + advanced_parameters: AdvancedParameters, seed_parameters: SeedParameters + ) -> "AdvancedAndSeedParametersAPI": + advanced_parameters_dict = asdict(advanced_parameters) + seed_parameters_dict = asdict(seed_parameters) + api_dict = advanced_parameters_dict | seed_parameters_dict + return AdvancedAndSeedParametersAPI.model_validate(api_dict) + @all_optional_model class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): From e339f8e3e815e8976bf5df6d243612b8873f1639 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 11:20:35 +0100 Subject: [PATCH 18/69] add all methods inside API classes --- .../api_services/study_settings_api.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index fdd8c101..ea0f7cad 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -109,6 +109,22 @@ class AdvancedAndSeedParametersAPI(BaseModel, alias_generator=to_camel): seed_hydro_costs: int seed_initial_reservoir_levels: int + @staticmethod + def _get_advanced_user_parameters_fields() -> set[str]: + return { + "seed_tsgen_wind", + "seed_tsgen_load", + "seed_tsgen_hydro", + "seed_tsgen_thermal", + "seed_tsgen_solar", + "seed_tsnumbers", + "seed_unsupplied_energy_costs", + "seed_spilled_energy_costs", + "seed_thermal_costs", + "seed_hydro_costs", + "seed_initial_reservoir_levels", + } + @staticmethod def from_user_model( advanced_parameters: AdvancedParameters, seed_parameters: SeedParameters @@ -118,6 +134,14 @@ def from_user_model( api_dict = advanced_parameters_dict | seed_parameters_dict return AdvancedAndSeedParametersAPI.model_validate(api_dict) + def to_user_advanced_parameters_model(self) -> AdvancedParameters: + excluded_fields = self._get_advanced_user_parameters_fields() + return AdvancedParameters(**self.model_dump(mode="json", exclude=excluded_fields)) + + def to_user_seed_parameters_model(self) -> SeedParameters: + included_fields = self._get_advanced_user_parameters_fields() + return SeedParameters(**self.model_dump(mode="json", include=included_fields)) + @all_optional_model class GeneralParametersAPI(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): From 1942f954e535f6bd61384360ad1781d4738b67d9 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 11:23:28 +0100 Subject: [PATCH 19/69] add minor thing --- src/antares/craft/service/api_services/study_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index b9df33e5..0742c16e 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -47,7 +47,7 @@ def _returns_study_settings( - base_url: str, study_id: str, wrapper: RequestWrapper, update: bool, settings: Optional[StudySettings] + base_url: str, study_id: str, wrapper: RequestWrapper, update: bool, settings: Optional[StudySettings] = None ) -> Optional[StudySettings]: settings_base_url = f"{base_url}/studies/{study_id}/config" mapping = { From 501e6d939d2d18a1e2e215a18d3e37896556e32a Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 12:02:30 +0100 Subject: [PATCH 20/69] add method read_study_settings --- src/antares/craft/exceptions/exceptions.py | 6 ++ .../craft/service/api_services/study_api.py | 70 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/antares/craft/exceptions/exceptions.py b/src/antares/craft/exceptions/exceptions.py index 04648d99..611705f1 100644 --- a/src/antares/craft/exceptions/exceptions.py +++ b/src/antares/craft/exceptions/exceptions.py @@ -238,6 +238,12 @@ def __init__(self, study_name: str, message: str) -> None: super().__init__(self.message) +class StudySettingsReadError(Exception): + def __init__(self, study_name: str, message: str) -> None: + self.message = f"Could not read settings for study {study_name}: " + message + super().__init__(self.message) + + class StudyDeletionError(Exception): def __init__(self, study_id: str, message: str) -> None: self.message = f"Could not delete the study {study_id}: " + message diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 0742c16e..730d2f0c 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -24,6 +24,7 @@ OutputsRetrievalError, StudyDeletionError, StudyMoveError, + StudySettingsReadError, StudySettingsUpdateError, StudyVariantCreationError, TaskFailedError, @@ -39,6 +40,13 @@ from antares.craft.model.settings.playlist_parameters import PlaylistData, PlaylistParameters from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters +from antares.craft.service.api_services.study_settings_api import ( + AdequacyPatchParametersAPI, + AdvancedAndSeedParametersAPI, + GeneralParametersAPI, + OptimizationParametersAPI, + ThematicTrimmingParametersAPI, +) from antares.craft.service.api_services.utils import wait_task_completion from antares.craft.service.base_services import BaseOutputService, BaseStudyService @@ -46,6 +54,68 @@ from antares.craft.model.study import Study +def create_study_settings( + base_url: str, study_id: str, wrapper: RequestWrapper, settings: Optional[StudySettings] = None +) -> StudySettings: + raise NotImplementedError + + +def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: + settings_base_url = f"{base_url}/studies/{study_id}/config" + try: + # thematic trimming + thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" + response = wrapper.get(thematic_trimming_url) + thematic_trimming_api_model = ThematicTrimmingParametersAPI.model_validate(response.json()) + thematic_trimming_parameters = thematic_trimming_api_model.to_user_model() + + # playlist + # playlist_url = f"{settings_base_url}/playlist/form" + + # todo + + # optimization + optimization_url = f"{settings_base_url}/optimization/form" + response = wrapper.get(optimization_url) + optimization_api_model = OptimizationParametersAPI.model_validate(response.json()) + optimization_parameters = optimization_api_model.to_user_model() + + # general and timeseries + general_url = f"{settings_base_url}/general/form" + response = wrapper.get(general_url) + general_api_model = GeneralParametersAPI.model_validate(response.json()) + timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" + response = wrapper.get(timeseries_url) + nb_ts_thermal = response.json()["thermal"]["number"] + general_parameters = general_api_model.to_user_model(nb_ts_thermal) + + # advanced and seed parameters + advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" + response = wrapper.get(advanced_parameters_url) + advanced_parameters_api_model = AdvancedAndSeedParametersAPI.model_validate(response.json()) + seed_parameters = advanced_parameters_api_model.to_user_seed_parameters_model() + advanced_parameters = advanced_parameters_api_model.to_user_advanced_parameters_model() + + # adequacy patch + adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" + response = wrapper.get(adequacy_patch_url) + adequacy_patch_api_model = AdequacyPatchParametersAPI.model_validate(response.json()) + adequacy_patch_parameters = adequacy_patch_api_model.to_user_model() + + except APIError as e: + raise StudySettingsReadError(study_id, e.message) from e + + return StudySettings( + general_parameters=general_parameters, + optimization_parameters=optimization_parameters, + seed_parameters=seed_parameters, + advanced_parameters=advanced_parameters, + adequacy_patch_parameters=adequacy_patch_parameters, + playlist_parameters=None, + thematic_trimming_parameters=thematic_trimming_parameters, + ) + + def _returns_study_settings( base_url: str, study_id: str, wrapper: RequestWrapper, update: bool, settings: Optional[StudySettings] = None ) -> Optional[StudySettings]: From 8db9cefbfa78048738b28a9a482a245b16898e36 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 12:04:57 +0100 Subject: [PATCH 21/69] rename method --- src/antares/craft/service/api_services/study_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 730d2f0c..0c8b35b1 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -54,8 +54,8 @@ from antares.craft.model.study import Study -def create_study_settings( - base_url: str, study_id: str, wrapper: RequestWrapper, settings: Optional[StudySettings] = None +def edit_study_settings( + base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings ) -> StudySettings: raise NotImplementedError From 441fd44b15c13b020d8c9b5d6e3ce86ba00f6250 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 13:45:06 +0100 Subject: [PATCH 22/69] create function edit_study_settings --- .../craft/service/api_services/study_api.py | 55 +++++++++++++++++-- .../api_services/study_settings_api.py | 6 +- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 0c8b35b1..c354e6ac 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -54,10 +54,57 @@ from antares.craft.model.study import Study -def edit_study_settings( - base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings -) -> StudySettings: - raise NotImplementedError +def edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: + settings_base_url = f"{base_url}/studies/{study_id}/config" + try: + # thematic trimming + if settings.thematic_trimming_parameters: + thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" + api_model = ThematicTrimmingParametersAPI.from_user_model(settings.thematic_trimming_parameters) + body = api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(thematic_trimming_url, json=body) + + # playlist + # playlist_url = f"{settings_base_url}/playlist/form" + + # todo + + # optimization + if settings.optimization_parameters: + optimization_url = f"{settings_base_url}/optimization/form" + optimization_api_model = OptimizationParametersAPI.from_user_model(settings.optimization_parameters) + body = optimization_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(optimization_url, json=body) + + # general and timeseries + if settings.general_parameters: + general_url = f"{settings_base_url}/general/form" + general_api_model = GeneralParametersAPI.from_user_model(settings.general_parameters) + body = general_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(general_url, json=body) + + if nb_ts_thermal := settings.general_parameters.nb_timeseries_thermal: + timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" + wrapper.put(timeseries_url, json={"thermal": {"number": nb_ts_thermal}}) + + # advanced and seed parameters + if settings.advanced_parameters or settings.seed_parameters: + advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" + advanced_api_model = AdvancedAndSeedParametersAPI.from_user_model( + settings.advanced_parameters, settings.seed_parameters + ) + body = advanced_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(advanced_parameters_url, json=body) + + # adequacy patch + if settings.adequacy_patch_parameters: + adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" + adequacy_patch_api_model = AdequacyPatchParametersAPI.from_user_model(settings.adequacy_patch_parameters) + body = adequacy_patch_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(adequacy_patch_url, json=body) + + except APIError as e: + raise StudySettingsUpdateError(study_id, e.message) from e def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index ea0f7cad..e4e56ba7 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -127,10 +127,10 @@ def _get_advanced_user_parameters_fields() -> set[str]: @staticmethod def from_user_model( - advanced_parameters: AdvancedParameters, seed_parameters: SeedParameters + advanced_parameters: Optional[AdvancedParameters] = None, seed_parameters: Optional[SeedParameters] = None ) -> "AdvancedAndSeedParametersAPI": - advanced_parameters_dict = asdict(advanced_parameters) - seed_parameters_dict = asdict(seed_parameters) + advanced_parameters_dict = asdict(advanced_parameters) if advanced_parameters else {} + seed_parameters_dict = asdict(seed_parameters) if seed_parameters else {} api_dict = advanced_parameters_dict | seed_parameters_dict return AdvancedAndSeedParametersAPI.model_validate(api_dict) From 26109f40de83e33a3b6c726a27c8047404919f03 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 13:51:12 +0100 Subject: [PATCH 23/69] refactoring --- src/antares/craft/model/study.py | 5 ++--- src/antares/craft/service/api_services/study_api.py | 5 ++--- src/antares/craft/service/base_services.py | 2 +- src/antares/craft/service/local_services/study_local.py | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 92abb902..a0abb68a 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -357,9 +357,8 @@ def read_binding_constraints(self) -> list[BindingConstraint]: return constraints def update_settings(self, settings: StudySettings) -> None: - new_settings = self._study_service.update_study_settings(settings) - if new_settings: - self._settings = new_settings + self._study_service.update_study_settings(settings) + self._settings = settings def delete_binding_constraint(self, constraint: BindingConstraint) -> None: self._study_service.delete_binding_constraint(constraint) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index c354e6ac..52461605 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -223,12 +223,11 @@ def output_service(self) -> Optional[BaseOutputService]: def set_output_service(self, output_service: BaseOutputService) -> None: self._output_service = output_service - def update_study_settings(self, settings: StudySettings) -> Optional[StudySettings]: + def update_study_settings(self, settings: StudySettings) -> None: try: - new_settings = _returns_study_settings(self._base_url, self.study_id, self._wrapper, True, settings) + edit_study_settings(self._base_url, self.study_id, self._wrapper, settings) except APIError as e: raise StudySettingsUpdateError(self.study_id, e.message) from e - return new_settings def delete_binding_constraint(self, constraint: BindingConstraint) -> None: url = f"{self._base_url}/studies/{self.study_id}/bindingconstraints/{constraint.id}" diff --git a/src/antares/craft/service/base_services.py b/src/antares/craft/service/base_services.py index d74ed61f..591f0c31 100644 --- a/src/antares/craft/service/base_services.py +++ b/src/antares/craft/service/base_services.py @@ -564,7 +564,7 @@ def config(self) -> BaseConfiguration: pass @abstractmethod - def update_study_settings(self, settings: StudySettings) -> Optional[StudySettings]: + def update_study_settings(self, settings: StudySettings) -> None: """ Args: settings: new study settings. Only registered fields will be updated. diff --git a/src/antares/craft/service/local_services/study_local.py b/src/antares/craft/service/local_services/study_local.py index d4ae9a7a..60601a7e 100644 --- a/src/antares/craft/service/local_services/study_local.py +++ b/src/antares/craft/service/local_services/study_local.py @@ -44,7 +44,7 @@ def output_service(self) -> Optional[BaseOutputService]: def set_output_service(self, output_service: BaseOutputService) -> None: self._output_service = output_service - def update_study_settings(self, settings: StudySettings) -> Optional[StudySettings]: + def update_study_settings(self, settings: StudySettings) -> None: raise NotImplementedError def delete_binding_constraint(self, constraint: BindingConstraint) -> None: From bf4746f4413e8c6f93ae175c3b5b64f7461c4559 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 13:56:59 +0100 Subject: [PATCH 24/69] remove legacy function --- src/antares/craft/model/study.py | 10 +++-- .../craft/service/api_services/study_api.py | 43 ------------------- 2 files changed, 7 insertions(+), 46 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index a0abb68a..ee6e37f5 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -35,7 +35,7 @@ from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.simulation import AntaresSimulationParameters, Job -from antares.craft.service.api_services.study_api import _returns_study_settings +from antares.craft.service.api_services.study_api import read_study_settings from antares.craft.service.base_services import BaseStudyService from antares.craft.service.service_factory import ServiceFactory from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes @@ -76,8 +76,12 @@ def create_study_api( url = f"{base_url}/studies?name={study_name}&version={version}" response = wrapper.post(url) study_id = response.json() - study_settings = _returns_study_settings(base_url, study_id, wrapper, False, settings) + # Settings part + study_settings = None if settings else read_study_settings(base_url, study_id, wrapper) study = Study(study_name, version, ServiceFactory(api_config, study_id), study_settings) + if settings: + study.update_settings(settings) + # Move part if parent_path: study.move(parent_path) url = f"{base_url}/studies/{study_id}" @@ -199,7 +203,7 @@ def read_study_api(api_config: APIconf, study_id: str) -> "Study": path = json_study.pop("folder") pure_path = PurePath(path) if path else PurePath(".") - study_settings = _returns_study_settings(base_url, study_id, wrapper, False, None) + study_settings = read_study_settings(base_url, study_id, wrapper) study = Study( study_name, study_version, ServiceFactory(api_config, study_id, study_name), study_settings, pure_path ) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 52461605..b9f64208 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -9,7 +9,6 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. -from dataclasses import asdict from pathlib import Path, PurePath from typing import TYPE_CHECKING, Optional @@ -33,13 +32,7 @@ ) from antares.craft.model.binding_constraint import BindingConstraint from antares.craft.model.output import Output -from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters -from antares.craft.model.settings.advanced_parameters import AdvancedParameters -from antares.craft.model.settings.general import GeneralParameters -from antares.craft.model.settings.optimization import OptimizationParameters -from antares.craft.model.settings.playlist_parameters import PlaylistData, PlaylistParameters from antares.craft.model.settings.study_settings import StudySettings -from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters from antares.craft.service.api_services.study_settings_api import ( AdequacyPatchParametersAPI, AdvancedAndSeedParametersAPI, @@ -163,42 +156,6 @@ def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) - ) -def _returns_study_settings( - base_url: str, study_id: str, wrapper: RequestWrapper, update: bool, settings: Optional[StudySettings] = None -) -> Optional[StudySettings]: - settings_base_url = f"{base_url}/studies/{study_id}/config" - mapping = { - "general_parameters": ("general", GeneralParameters), - "thematic_trimming_parameters": ("thematictrimming", ThematicTrimmingParameters), - "adequacy_patch_parameters": ("adequacypatch", AdequacyPatchParameters), - "advanced_parameters": ("advancedparameters", AdvancedParameters), - "optimization_parameters": ("optimization", OptimizationParameters), - "playlist_parameters": ("playlist", PlaylistParameters), - } - if settings: - json_settings = asdict(settings) - if not json_settings and update: - return None - - for key, value in json_settings.items(): - url = f"{settings_base_url}/{mapping[key][0]}/form" - wrapper.put(url, json=value) - - json_settings = {} - for settings_type, settings_tuple in mapping.items(): - settings_class = settings_tuple[1] - url = f"{settings_base_url}/{settings_tuple[0]}/form" - response = wrapper.get(url) - if settings_type == "playlist_parameters": - mc_years = [PlaylistData.model_validate(year) for year in response.json().values()] - settings_property = settings_class(playlist=mc_years) if mc_years else None - else: - settings_property = settings_class.model_validate(response.json()) # type: ignore - json_settings[settings_type] = settings_property - - return StudySettings(**json_settings) - - class StudyApiService(BaseStudyService): def __init__(self, config: APIconf, study_id: str): super().__init__() From 2155c94410c564a5e2885f365004eec22abfc871 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 13:57:37 +0100 Subject: [PATCH 25/69] make function private --- src/antares/craft/service/api_services/study_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index b9f64208..38941b04 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -47,7 +47,7 @@ from antares.craft.model.study import Study -def edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: +def _edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: settings_base_url = f"{base_url}/studies/{study_id}/config" try: # thematic trimming @@ -182,7 +182,7 @@ def set_output_service(self, output_service: BaseOutputService) -> None: def update_study_settings(self, settings: StudySettings) -> None: try: - edit_study_settings(self._base_url, self.study_id, self._wrapper, settings) + _edit_study_settings(self._base_url, self.study_id, self._wrapper, settings) except APIError as e: raise StudySettingsUpdateError(self.study_id, e.message) from e From 3e18e335698f7ce1712372c4c36ea653446d1d5b Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 13:58:25 +0100 Subject: [PATCH 26/69] remove try catch --- .../craft/service/api_services/study_api.py | 81 +++++++++---------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 38941b04..f8c71e1c 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -49,55 +49,52 @@ def _edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: settings_base_url = f"{base_url}/studies/{study_id}/config" - try: - # thematic trimming - if settings.thematic_trimming_parameters: - thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" - api_model = ThematicTrimmingParametersAPI.from_user_model(settings.thematic_trimming_parameters) - body = api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(thematic_trimming_url, json=body) - - # playlist - # playlist_url = f"{settings_base_url}/playlist/form" - # todo + # thematic trimming + if settings.thematic_trimming_parameters: + thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" + api_model = ThematicTrimmingParametersAPI.from_user_model(settings.thematic_trimming_parameters) + body = api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(thematic_trimming_url, json=body) - # optimization - if settings.optimization_parameters: - optimization_url = f"{settings_base_url}/optimization/form" - optimization_api_model = OptimizationParametersAPI.from_user_model(settings.optimization_parameters) - body = optimization_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(optimization_url, json=body) + # playlist + # playlist_url = f"{settings_base_url}/playlist/form" - # general and timeseries - if settings.general_parameters: - general_url = f"{settings_base_url}/general/form" - general_api_model = GeneralParametersAPI.from_user_model(settings.general_parameters) - body = general_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(general_url, json=body) + # todo - if nb_ts_thermal := settings.general_parameters.nb_timeseries_thermal: - timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" - wrapper.put(timeseries_url, json={"thermal": {"number": nb_ts_thermal}}) + # optimization + if settings.optimization_parameters: + optimization_url = f"{settings_base_url}/optimization/form" + optimization_api_model = OptimizationParametersAPI.from_user_model(settings.optimization_parameters) + body = optimization_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(optimization_url, json=body) - # advanced and seed parameters - if settings.advanced_parameters or settings.seed_parameters: - advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" - advanced_api_model = AdvancedAndSeedParametersAPI.from_user_model( - settings.advanced_parameters, settings.seed_parameters - ) - body = advanced_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(advanced_parameters_url, json=body) + # general and timeseries + if settings.general_parameters: + general_url = f"{settings_base_url}/general/form" + general_api_model = GeneralParametersAPI.from_user_model(settings.general_parameters) + body = general_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(general_url, json=body) - # adequacy patch - if settings.adequacy_patch_parameters: - adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" - adequacy_patch_api_model = AdequacyPatchParametersAPI.from_user_model(settings.adequacy_patch_parameters) - body = adequacy_patch_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(adequacy_patch_url, json=body) + if nb_ts_thermal := settings.general_parameters.nb_timeseries_thermal: + timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" + wrapper.put(timeseries_url, json={"thermal": {"number": nb_ts_thermal}}) - except APIError as e: - raise StudySettingsUpdateError(study_id, e.message) from e + # advanced and seed parameters + if settings.advanced_parameters or settings.seed_parameters: + advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" + advanced_api_model = AdvancedAndSeedParametersAPI.from_user_model( + settings.advanced_parameters, settings.seed_parameters + ) + body = advanced_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(advanced_parameters_url, json=body) + + # adequacy patch + if settings.adequacy_patch_parameters: + adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" + adequacy_patch_api_model = AdequacyPatchParametersAPI.from_user_model(settings.adequacy_patch_parameters) + body = adequacy_patch_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(adequacy_patch_url, json=body) def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: From 06f0af74678cea9ce934df957b5218145467f83c Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 14:01:08 +0100 Subject: [PATCH 27/69] fix duplicata --- src/antares/craft/service/api_services/study_settings_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index e4e56ba7..af57e352 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -172,7 +172,6 @@ def from_user_model(user_class: GeneralParameters) -> "GeneralParametersAPI": user_dict["last_day"] = user_dict.pop("simulation_end") user_dict["first_january"] = user_dict.pop("january_first") user_dict["first_month"] = user_dict.pop("first_month_in_year") - user_dict["first_january"] = user_dict.pop("january_first") user_dict["selection_mode"] = user_dict.pop("user_playlist") user_dict.pop("nb_timeseries_thermal") return GeneralParametersAPI.model_validate(user_dict) From e956074a33baf95ad3cad8769658b10bb4edee9f Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 14:38:49 +0100 Subject: [PATCH 28/69] handle playlist --- .../model/settings/playlist_parameters.py | 79 +------------------ .../craft/model/settings/study_settings.py | 2 +- .../craft/service/api_services/study_api.py | 10 ++- 3 files changed, 11 insertions(+), 80 deletions(-) diff --git a/src/antares/craft/model/settings/playlist_parameters.py b/src/antares/craft/model/settings/playlist_parameters.py index fa00e767..38bf77a0 100644 --- a/src/antares/craft/model/settings/playlist_parameters.py +++ b/src/antares/craft/model/settings/playlist_parameters.py @@ -9,83 +9,10 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import dataclass -from typing import Any -from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_validator, model_serializer, model_validator - - -class PlaylistData(BaseModel): +@dataclass +class PlaylistParameters: status: bool = True weight: float = 1.0 - - -class PlaylistParameters(BaseModel): - """ - Parameters for playlists. - - Attributes: - playlist (list[PlaylistData]): A list of years (in **PlaylistData** format) in the playlist - """ - - model_config = ConfigDict(validate_assignment=True) - - playlist: list[PlaylistData] = Field(default=[], exclude=True) - - @field_validator("playlist", mode="before") - @classmethod - def playlist_is_list(cls, value: Any) -> list[PlaylistData]: - if isinstance(value, PlaylistParameters): - ret_val = [PlaylistData.model_validate(year) for year in value.mc_years.values()] - elif isinstance(value, list): - ret_val = [PlaylistData.model_validate(year) for year in value] - else: - raise ValueError("Not a valid playlist.") - return ret_val - - @model_validator(mode="before") - @classmethod - def handle_dict_validation(cls, data: Any) -> Any: - return_value = data - if isinstance(data, dict): - if "playlist" in data.keys() and isinstance(data["playlist"], dict): - try: - playlist = [PlaylistData.model_validate(year) for year in data["playlist"]["mc_years"].values()] - except KeyError: - try: - playlist = [PlaylistData.model_validate(year) for year in data["playlist"].values()] - except ValidationError: - raise ValueError("Not a valid playlist dictionary.") - return_value = {"playlist": playlist} - elif "mc_years" in data.keys(): - playlist = [PlaylistData.model_validate(year) for year in data["mc_years"].values()] - return_value = {"playlist": playlist} - return return_value - - # Custom serializer is necessary to preserve compatibility with AntaREST - @model_serializer - def ser_mod(self) -> dict[int, PlaylistData]: - return self.mc_years - - @property - def playlist_reset(self) -> bool: - return sum([year.status for year in self.playlist]) > (len(self.playlist) / 2) - - @property - def mc_years(self) -> dict[int, PlaylistData]: - return {year + 1: self.playlist[year] for year in range(len(self.playlist))} - - @property - def ini_fields(self) -> dict: - playlist_years = repr( - [str(year) for year, year_obj in enumerate(self.playlist) if year_obj.status ^ self.playlist_reset] - ) - - playlist_year_dict = {"playlist_year " + ("-" if self.playlist_reset else "+"): playlist_years} - - return { - "playlist": { - "playlist_reset": str(self.playlist_reset).lower(), - } - | playlist_year_dict - } diff --git a/src/antares/craft/model/settings/study_settings.py b/src/antares/craft/model/settings/study_settings.py index 0c3ff6b2..2cb48355 100644 --- a/src/antares/craft/model/settings/study_settings.py +++ b/src/antares/craft/model/settings/study_settings.py @@ -27,5 +27,5 @@ class StudySettings: advanced_parameters: Optional[AdvancedParameters] = None seed_parameters: Optional[SeedParameters] = None adequacy_patch_parameters: Optional[AdequacyPatchParameters] = None - playlist_parameters: Optional[PlaylistParameters] = None + playlist_parameters: Optional[dict[int, PlaylistParameters]] = None thematic_trimming_parameters: Optional[ThematicTrimmingParameters] = None diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index f8c71e1c..883cc811 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -9,6 +9,7 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import asdict from pathlib import Path, PurePath from typing import TYPE_CHECKING, Optional @@ -58,9 +59,12 @@ def _edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, wrapper.put(thematic_trimming_url, json=body) # playlist - # playlist_url = f"{settings_base_url}/playlist/form" - - # todo + if settings.playlist_parameters: + playlist_url = f"{settings_base_url}/playlist/form" + body = {} + for key, value in settings.playlist_parameters.items(): + body[str(key)] = asdict(value) + wrapper.put(playlist_url, json=body) # optimization if settings.optimization_parameters: From 7a8e2163450da0455c0c92892066f033affbc5d5 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 14:41:02 +0100 Subject: [PATCH 29/69] use playlist --- src/antares/craft/service/api_services/study_api.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 883cc811..bf6bf4e8 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -33,6 +33,7 @@ ) from antares.craft.model.binding_constraint import BindingConstraint from antares.craft.model.output import Output +from antares.craft.model.settings.playlist_parameters import PlaylistParameters from antares.craft.model.settings.study_settings import StudySettings from antares.craft.service.api_services.study_settings_api import ( AdequacyPatchParametersAPI, @@ -111,9 +112,12 @@ def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) - thematic_trimming_parameters = thematic_trimming_api_model.to_user_model() # playlist - # playlist_url = f"{settings_base_url}/playlist/form" - - # todo + playlist_url = f"{settings_base_url}/playlist/form" + response = wrapper.get(playlist_url) + json_response = response.json() + user_playlist = {} + for key, value in json_response.items(): + user_playlist[int(key)] = PlaylistParameters(**value) # optimization optimization_url = f"{settings_base_url}/optimization/form" @@ -152,7 +156,7 @@ def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) - seed_parameters=seed_parameters, advanced_parameters=advanced_parameters, adequacy_patch_parameters=adequacy_patch_parameters, - playlist_parameters=None, + playlist_parameters=user_playlist, thematic_trimming_parameters=thematic_trimming_parameters, ) From 9e4da02458034b726d89143c84b5e0c8720f0e68 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 14:44:04 +0100 Subject: [PATCH 30/69] add todo --- src/antares/craft/model/study.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index ee6e37f5..4c0a1e4d 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -146,6 +146,7 @@ def create_study_local( local_settings = settings local_settings_file = IniFile(study_directory, InitializationFilesTypes.GENERAL) local_settings_file.ini_dict = asdict(local_settings) + # todo: replace this as dict with a specific method that does the mapping. local_settings_file.write_ini_file() # Create various .ini files for the study From 04663bb6efc136828f3004be216db800790d8960 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 15:15:01 +0100 Subject: [PATCH 31/69] start work for local models --- .../craft/model/settings/optimization.py | 16 +++++----- .../api_services/study_settings_api.py | 32 +++++++++---------- .../local_services/study_settings_local.py | 24 ++++++++++++-- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/antares/craft/model/settings/optimization.py b/src/antares/craft/model/settings/optimization.py index 6c28b82e..e6f527df 100644 --- a/src/antares/craft/model/settings/optimization.py +++ b/src/antares/craft/model/settings/optimization.py @@ -47,12 +47,12 @@ class OptimizationParameters: transmission_capacities: Optional[OptimizationTransmissionCapacities] = None include_constraints: Optional[bool] = None include_hurdle_costs: Optional[bool] = None - include_thermal_cluster_min_stable_power: Optional[bool] = None - include_thermal_cluster_min_ud_time: Optional[bool] = None - include_day_ahead: Optional[bool] = None - include_strategic_reserve: Optional[bool] = None - include_spinning_reserve: Optional[bool] = None - include_primary_reserve: Optional[bool] = None - include_export_mps: Optional[ExportMPS] = None - include_export_structure: Optional[bool] = None + include_tc_minstablepower: Optional[bool] = None + include_tc_min_ud_time: Optional[bool] = None + include_dayahead: Optional[bool] = None + include_strategicreserve: Optional[bool] = None + include_spinningreserve: Optional[bool] = None + include_primaryreserve: Optional[bool] = None + include_exportmps: Optional[ExportMPS] = None + include_exportstructure: Optional[bool] = None include_unfeasible_problem_behavior: Optional[UnfeasibleProblemBehavior] = None diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index af57e352..ba6e4fce 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -219,14 +219,14 @@ def from_user_model(user_class: OptimizationParameters) -> "OptimizationParamete user_dict["simplex_optimization_range"] = user_dict.pop("simplex_range") user_dict["binding_constraints"] = user_dict.pop("include_constraints") user_dict["hurdle_costs"] = user_dict.pop("include_hurdle_costs") - user_dict["thermal_clusters_min_stable_power"] = user_dict.pop("include_thermal_cluster_min_stable_power") - user_dict["thermal_clusters_min_ud_time"] = user_dict.pop("include_thermal_cluster_min_ud_time") - user_dict["day_ahead_reserve"] = user_dict.pop("include_day_ahead") - user_dict["strategic_reserve"] = user_dict.pop("include_strategic_reserve") - user_dict["spinning_reserve"] = user_dict.pop("include_spinning_reserve") - user_dict["primary_reserve"] = user_dict.pop("include_primary_reserve") - user_dict["export_mps"] = user_dict.pop("include_export_mps") - user_dict["include_exportstructure"] = user_dict.pop("include_export_structure") + user_dict["thermal_clusters_min_stable_power"] = user_dict.pop("include_tc_minstablepower") + user_dict["thermal_clusters_min_ud_time"] = user_dict.pop("include_tc_min_ud_time") + user_dict["day_ahead_reserve"] = user_dict.pop("include_dayahead") + user_dict["strategic_reserve"] = user_dict.pop("include_strategicreserve") + user_dict["spinning_reserve"] = user_dict.pop("include_spinningreserve") + user_dict["primary_reserve"] = user_dict.pop("include_primaryreserve") + user_dict["export_mps"] = user_dict.pop("include_exportmps") + user_dict["include_exportstructure"] = user_dict.pop("include_exportstructure") user_dict["unfeasible_problem_behavior"] = user_dict.pop("include_unfeasible_problem_behavior") return OptimizationParametersAPI.model_validate(user_dict) @@ -236,14 +236,14 @@ def to_user_model(self) -> OptimizationParameters: transmission_capacities=self.transmission_capacities, include_constraints=self.binding_constraints, include_hurdle_costs=self.hurdle_costs, - include_thermal_cluster_min_stable_power=self.thermal_clusters_min_stable_power, - include_thermal_cluster_min_ud_time=self.thermal_clusters_min_ud_time, - include_day_ahead=self.day_ahead_reserve, - include_strategic_reserve=self.strategic_reserve, - include_spinning_reserve=self.spinning_reserve, - include_primary_reserve=self.primary_reserve, - include_export_mps=self.export_mps, - include_export_structure=self.include_exportstructure, + include_tc_minstablepower=self.thermal_clusters_min_stable_power, + include_tc_min_ud_time=self.thermal_clusters_min_ud_time, + include_dayahead=self.day_ahead_reserve, + include_strategicreserve=self.strategic_reserve, + include_spinningreserve=self.spinning_reserve, + include_primaryreserve=self.primary_reserve, + include_exportmps=self.export_mps, + include_exportstructure=self.include_exportstructure, include_unfeasible_problem_behavior=self.unfeasible_problem_behavior, ) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index fe997c64..10e6b215 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -9,10 +9,11 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import asdict from enum import Enum from typing import Any -from antares.craft.model.settings.adequacy_patch import PriceTakingOrder +from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( HydroHeuristicPolicy, HydroPricingMode, @@ -25,6 +26,7 @@ ) from antares.craft.model.settings.general import Mode, Month, OutputChoices, WeekDay from antares.craft.model.settings.optimization import ( + OptimizationParameters, OptimizationTransmissionCapacities, SimplexOptimizationRange, UnfeasibleProblemBehavior, @@ -46,6 +48,15 @@ class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): threshold_csr_variable_bounds_relaxation: int = 3 enable_first_step: bool = False + @staticmethod + def from_user_model(user_class: AdequacyPatchParameters) -> "AdequacyPatchParametersLocalCreation": + user_dict = asdict(user_class) + return AdequacyPatchParametersLocalCreation.model_validate(user_dict) + + def to_user_model(self) -> AdequacyPatchParameters: + local_dict = self.model_dump(mode="json", by_alias=False, exclude={"enable_first_step"}) + return AdequacyPatchParameters(**local_dict) + @all_optional_model class AdequacyPatchParametersLocalEdition(AdequacyPatchParametersLocalCreation): @@ -166,7 +177,7 @@ class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES include_constraints: bool = True include_hurdle_costs: bool = True - include_tc_min_stable_power: bool = True + include_tc_minstablepower: bool = True include_tc_min_ud_time: bool = True include_dayahead: bool = True include_strategicreserve: bool = True @@ -176,6 +187,15 @@ class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): include_exportstructure: bool = False include_unfeasible_problem_behavior: UnfeasibleProblemBehavior = UnfeasibleProblemBehavior.ERROR_VERBOSE + @staticmethod + def from_user_model(user_class: OptimizationParameters) -> "OptimizationParametersLocalCreation": + user_dict = asdict(user_class) + return OptimizationParametersLocalCreation.model_validate(user_dict) + + def to_user_model(self) -> OptimizationParameters: + local_dict = self.model_dump(mode="json", by_alias=False) + return OptimizationParameters(**local_dict) + @all_optional_model class OptimizationSettingsLocalEdition(OptimizationParametersLocalCreation): From be62d6388225e51846f0ad52e548ae12dcdc3767 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 15:36:27 +0100 Subject: [PATCH 32/69] add mc scenario --- src/antares/craft/model/settings/general.py | 1 + src/antares/craft/service/api_services/study_settings_api.py | 2 ++ .../craft/service/local_services/study_settings_local.py | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/antares/craft/model/settings/general.py b/src/antares/craft/model/settings/general.py index d5dc67b9..2245b9ff 100644 --- a/src/antares/craft/model/settings/general.py +++ b/src/antares/craft/model/settings/general.py @@ -85,4 +85,5 @@ class GeneralParameters: user_playlist: Optional[bool] = None thematic_trimming: Optional[bool] = None geographic_trimming: Optional[bool] = None + store_new_set: Optional[bool] = None nb_timeseries_thermal: Optional[int] = None diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index ba6e4fce..fecc18d4 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -173,6 +173,7 @@ def from_user_model(user_class: GeneralParameters) -> "GeneralParametersAPI": user_dict["first_january"] = user_dict.pop("january_first") user_dict["first_month"] = user_dict.pop("first_month_in_year") user_dict["selection_mode"] = user_dict.pop("user_playlist") + user_dict["mc_scenario"] = user_dict.pop("store_new_set") user_dict.pop("nb_timeseries_thermal") return GeneralParametersAPI.model_validate(user_dict) @@ -193,6 +194,7 @@ def to_user_model(self, nb_ts_thermal: int) -> GeneralParameters: user_playlist=self.selection_mode, thematic_trimming=self.thematic_trimming, geographic_trimming=self.geographic_trimming, + store_new_set=self.mc_scenario, nb_timeseries_thermal=nb_ts_thermal, ) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 10e6b215..469640e2 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -163,7 +163,7 @@ class OutputSectionLocal(BaseModel): class GeneralParametersLocalCreation(BaseModel): general: GeneralSectionLocal - input: dict = {"input": ""} + input: dict = {"import": ""} output: OutputSectionLocal From b2ea01b2c079ffd8b27875981d53ecbaf5a9df9a Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 16:19:49 +0100 Subject: [PATCH 33/69] continue work --- .../local_services/study_settings_local.py | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 469640e2..3545d9f7 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -11,7 +11,7 @@ # This file is part of the Antares project. from dataclasses import asdict from enum import Enum -from typing import Any +from typing import Any, Set from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( @@ -24,7 +24,7 @@ SimulationCore, UnitCommitmentMode, ) -from antares.craft.model.settings.general import Mode, Month, OutputChoices, WeekDay +from antares.craft.model.settings.general import BuildingMode, GeneralParameters, Mode, Month, OutputChoices, WeekDay from antares.craft.model.settings.optimization import ( OptimizationParameters, OptimizationTransmissionCapacities, @@ -128,13 +128,12 @@ class GeneralSectionLocal(BaseModel): nb_years: int = Field(default=1, alias="nb.years") simulation_start: int = Field(default=1, alias="simulation.start") simulation_end: int = Field(default=365, alias="simulation.end") - first_january: WeekDay = Field(default=WeekDay.MONDAY, alias="january.1st") - first_month: Month = Field(default=Month.JANUARY, alias="first-month-in-year") + january_first: WeekDay = Field(default=WeekDay.MONDAY, alias="january.1st") + first_month_in_year: Month = Field(default=Month.JANUARY, alias="first-month-in-year") first_week_day: WeekDay = Field(default=WeekDay.MONDAY, alias="first.weekday") leap_year: bool = Field(default=False, alias="leapyear") year_by_year: bool = Field(default=False, alias="year-by-year") - derated: bool = False - custom_scenario: bool = Field(default=False, alias="custom-scenario") + building_mode: BuildingMode = BuildingMode.AUTOMATIC user_playlist: bool = Field(default=False, alias="user-playlist") thematic_trimming: bool = Field(default=False, alias="thematic-trimming") geographic_trimming: bool = Field(default=False, alias="geographic-trimming") @@ -166,6 +165,44 @@ class GeneralParametersLocalCreation(BaseModel): input: dict = {"import": ""} output: OutputSectionLocal + @staticmethod + def from_user_model(user_class: GeneralParameters) -> "GeneralParametersLocalCreation": + user_dict = asdict(user_class) + + output_dict = { + "output": { + "store_new_set": user_dict.pop("store_new_set"), + "synthesis": user_dict.pop("simulation_synthesis"), + } + } + general_dict = {"general": user_dict} + local_dict = general_dict | output_dict + + return GeneralParametersLocalCreation.model_validate(local_dict) + + @staticmethod + def get_excluded_fields_for_user_class() -> Set[str]: + return { + "generate", + "nb_timeseries_load", + "nb_timeseries_hydro", + "nb_timeseries_wind", + "nb_timeseries_solar", + "refresh_timeseries", + "intra_model", + "inter_model", + "refresh_interval_load", + "refresh_interval_hydro", + "refresh_interval_wind", + "refresh_interval_thermal", + "refresh_interval_solar", + "read_only", + } + + def to_user_model(self) -> GeneralParameters: + local_dict = self.model_dump(mode="json", by_alias=False, exclude=self.get_excluded_fields_for_user_class()) + return GeneralParameters(**local_dict) + @all_optional_model class GeneralParametersLocalEdition(GeneralParametersLocalCreation): From 939d4ff2ebc49dcaf92757cb3bf341aea586c179 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 16:36:15 +0100 Subject: [PATCH 34/69] continue work --- .../local_services/study_settings_local.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 3545d9f7..b4cec6ae 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -15,11 +15,13 @@ from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( + AdvancedParameters, HydroHeuristicPolicy, HydroPricingMode, InitialReservoirLevel, PowerFluctuation, RenewableGenerationModeling, + SeedParameters, SheddingPolicy, SimulationCore, UnitCommitmentMode, @@ -115,6 +117,30 @@ class AdvancedAndSeedParametersLocalCreation(BaseModel): advanced_parameters: AdvancedParametersLocalCreation = Field(alias="advanced parameters") seeds: SeedParametersLocalCreation = Field(alias="seeds - Mersenne Twister") + @staticmethod + def from_user_model( + advanced_parameters: AdvancedParameters, seed_parameters: SeedParameters + ) -> "AdvancedAndSeedParametersLocalCreation": + other_preferences_local_dict = asdict(advanced_parameters) + advanced_local_dict = { + "advanced_parameters": { + "accuracy_on_correlation": other_preferences_local_dict.pop("accuracy_on_correlation") + } + } + seed_local_dict = {"seeds": asdict(seed_parameters)} + + local_dict = {"other_preferences": other_preferences_local_dict} | advanced_local_dict | seed_local_dict + + return AdvancedAndSeedParametersLocalCreation.model_validate(local_dict) + + @staticmethod + def get_seed_fields() -> set[str]: + return set(asdict(SeedParameters()).keys()) + + def to_seed_parameters_model(self) -> SeedParameters: + seed_values = self.model_dump(mode="json", by_alias=False, include=set(asdict(SeedParameters()).keys())) + return SeedParameters(**seed_values) + class AdvancedAndSeedParametersLocalEdition(BaseModel): other_preferences: OtherPreferencesLocalEdition = Field(default=None, alias="other preferences") From 42a9afae0e49df03cb79aeb9b33ca6c5b5b67de2 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 16:36:51 +0100 Subject: [PATCH 35/69] use inheritance --- .../craft/service/local_services/study_settings_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index b4cec6ae..66ff15b8 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -142,7 +142,7 @@ def to_seed_parameters_model(self) -> SeedParameters: return SeedParameters(**seed_values) -class AdvancedAndSeedParametersLocalEdition(BaseModel): +class AdvancedAndSeedParametersLocalEdition(AdvancedAndSeedParametersLocalCreation): other_preferences: OtherPreferencesLocalEdition = Field(default=None, alias="other preferences") advanced_parameters: AdvancedParametersLocalEdition = Field(default_factory=None, alias="advanced parameters") seeds: SeedParametersLocalEdition = Field(default_factory=None, alias="seeds - Mersenne Twister") From 5929fe07c0b9065fa18f84b15c4638f10bfd89a0 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 16:43:24 +0100 Subject: [PATCH 36/69] put back method --- .../craft/service/local_services/study_settings_local.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 66ff15b8..a6cb4b06 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -133,14 +133,14 @@ def from_user_model( return AdvancedAndSeedParametersLocalCreation.model_validate(local_dict) - @staticmethod - def get_seed_fields() -> set[str]: - return set(asdict(SeedParameters()).keys()) - def to_seed_parameters_model(self) -> SeedParameters: seed_values = self.model_dump(mode="json", by_alias=False, include=set(asdict(SeedParameters()).keys())) return SeedParameters(**seed_values) + def to_advanced_parameters_model(self) -> AdvancedParameters: + advanced_values = self.model_dump(mode="json", by_alias=False, include=set(asdict(AdvancedParameters()).keys())) + return AdvancedParameters(**advanced_values) + class AdvancedAndSeedParametersLocalEdition(AdvancedAndSeedParametersLocalCreation): other_preferences: OtherPreferencesLocalEdition = Field(default=None, alias="other preferences") From 5be9698fa8fdf92280148ff02f8bec5a7b2c4961 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 16:47:30 +0100 Subject: [PATCH 37/69] refactor exisiting function --- src/antares/craft/model/study.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 4c0a1e4d..51fdfae4 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -143,24 +143,27 @@ def create_study_local( with open(desktop_ini_path, "w") as desktop_ini_file: desktop_ini_file.write(desktop_ini_content) - local_settings = settings - local_settings_file = IniFile(study_directory, InitializationFilesTypes.GENERAL) - local_settings_file.ini_dict = asdict(local_settings) - # todo: replace this as dict with a specific method that does the mapping. - local_settings_file.write_ini_file() - # Create various .ini files for the study - _create_correlation_ini_files(local_settings, study_directory) + _create_correlation_ini_files(study_directory) logging.info(f"Study successfully created: {study_name}") - return Study( + study = Study( name=study_name, version=version, service_factory=ServiceFactory(config=local_config, study_name=study_name), - settings=local_settings, + settings=None, path=study_directory, ) + # handle the settings part here + local_settings = settings + local_settings_file = IniFile(study_directory, InitializationFilesTypes.GENERAL) + local_settings_file.ini_dict = asdict(local_settings) + # todo: replace this as dict with a specific method that does the mapping. + local_settings_file.write_ini_file() + + return study + def read_study_local(study_directory: Path) -> "Study": """ @@ -479,7 +482,7 @@ def _create_directory_structure(study_path: Path) -> None: (study_path / subdirectory).mkdir(parents=True, exist_ok=True) -def _create_correlation_ini_files(local_settings: StudySettings, study_directory: Path) -> None: +def _create_correlation_ini_files(study_directory: Path) -> None: fields_to_check = ["hydro", "load", "solar", "wind"] correlation_inis_to_create = [ ( From 882231ffe4ae452b78e212f5bebdfacf3cab2086 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 17:02:57 +0100 Subject: [PATCH 38/69] create 2 new methods --- .../service/local_services/study_settings_local.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index a6cb4b06..af8aa599 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -11,6 +11,7 @@ # This file is part of the Antares project. from dataclasses import asdict from enum import Enum +from pathlib import Path from typing import Any, Set from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder @@ -33,6 +34,7 @@ SimplexOptimizationRange, UnfeasibleProblemBehavior, ) +from antares.craft.model.settings.study_settings import StudySettings from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel, Field @@ -360,3 +362,11 @@ class ThematicVarsLocal(Enum): loopFlow = "LOOP FLOW" margCost = "MARG. COST" ucapLin = "UCAP LIN." + + +def read_study_settings(study_directory: Path) -> StudySettings: + raise NotImplementedError + + +def edit_study_settings(study_directory: Path, settings: StudySettings) -> None: + raise NotImplementedError From c9a3449b4f81630419e1b5ffd7faa81cc42f1110 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 17:06:19 +0100 Subject: [PATCH 39/69] move API methods --- src/antares/craft/model/study.py | 2 +- .../craft/service/api_services/study_api.py | 125 +----------------- .../api_services/study_settings_api.py | 116 ++++++++++++++++ 3 files changed, 119 insertions(+), 124 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 51fdfae4..a6aecb1e 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -35,7 +35,7 @@ from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.simulation import AntaresSimulationParameters, Job -from antares.craft.service.api_services.study_api import read_study_settings +from antares.craft.service.api_services.study_settings_api import read_study_settings from antares.craft.service.base_services import BaseStudyService from antares.craft.service.service_factory import ServiceFactory from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index bf6bf4e8..39d1f9dd 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -9,7 +9,6 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. -from dataclasses import asdict from pathlib import Path, PurePath from typing import TYPE_CHECKING, Optional @@ -24,7 +23,6 @@ OutputsRetrievalError, StudyDeletionError, StudyMoveError, - StudySettingsReadError, StudySettingsUpdateError, StudyVariantCreationError, TaskFailedError, @@ -33,15 +31,8 @@ ) from antares.craft.model.binding_constraint import BindingConstraint from antares.craft.model.output import Output -from antares.craft.model.settings.playlist_parameters import PlaylistParameters from antares.craft.model.settings.study_settings import StudySettings -from antares.craft.service.api_services.study_settings_api import ( - AdequacyPatchParametersAPI, - AdvancedAndSeedParametersAPI, - GeneralParametersAPI, - OptimizationParametersAPI, - ThematicTrimmingParametersAPI, -) +from antares.craft.service.api_services.study_settings_api import edit_study_settings from antares.craft.service.api_services.utils import wait_task_completion from antares.craft.service.base_services import BaseOutputService, BaseStudyService @@ -49,118 +40,6 @@ from antares.craft.model.study import Study -def _edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: - settings_base_url = f"{base_url}/studies/{study_id}/config" - - # thematic trimming - if settings.thematic_trimming_parameters: - thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" - api_model = ThematicTrimmingParametersAPI.from_user_model(settings.thematic_trimming_parameters) - body = api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(thematic_trimming_url, json=body) - - # playlist - if settings.playlist_parameters: - playlist_url = f"{settings_base_url}/playlist/form" - body = {} - for key, value in settings.playlist_parameters.items(): - body[str(key)] = asdict(value) - wrapper.put(playlist_url, json=body) - - # optimization - if settings.optimization_parameters: - optimization_url = f"{settings_base_url}/optimization/form" - optimization_api_model = OptimizationParametersAPI.from_user_model(settings.optimization_parameters) - body = optimization_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(optimization_url, json=body) - - # general and timeseries - if settings.general_parameters: - general_url = f"{settings_base_url}/general/form" - general_api_model = GeneralParametersAPI.from_user_model(settings.general_parameters) - body = general_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(general_url, json=body) - - if nb_ts_thermal := settings.general_parameters.nb_timeseries_thermal: - timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" - wrapper.put(timeseries_url, json={"thermal": {"number": nb_ts_thermal}}) - - # advanced and seed parameters - if settings.advanced_parameters or settings.seed_parameters: - advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" - advanced_api_model = AdvancedAndSeedParametersAPI.from_user_model( - settings.advanced_parameters, settings.seed_parameters - ) - body = advanced_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(advanced_parameters_url, json=body) - - # adequacy patch - if settings.adequacy_patch_parameters: - adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" - adequacy_patch_api_model = AdequacyPatchParametersAPI.from_user_model(settings.adequacy_patch_parameters) - body = adequacy_patch_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(adequacy_patch_url, json=body) - - -def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: - settings_base_url = f"{base_url}/studies/{study_id}/config" - try: - # thematic trimming - thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" - response = wrapper.get(thematic_trimming_url) - thematic_trimming_api_model = ThematicTrimmingParametersAPI.model_validate(response.json()) - thematic_trimming_parameters = thematic_trimming_api_model.to_user_model() - - # playlist - playlist_url = f"{settings_base_url}/playlist/form" - response = wrapper.get(playlist_url) - json_response = response.json() - user_playlist = {} - for key, value in json_response.items(): - user_playlist[int(key)] = PlaylistParameters(**value) - - # optimization - optimization_url = f"{settings_base_url}/optimization/form" - response = wrapper.get(optimization_url) - optimization_api_model = OptimizationParametersAPI.model_validate(response.json()) - optimization_parameters = optimization_api_model.to_user_model() - - # general and timeseries - general_url = f"{settings_base_url}/general/form" - response = wrapper.get(general_url) - general_api_model = GeneralParametersAPI.model_validate(response.json()) - timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" - response = wrapper.get(timeseries_url) - nb_ts_thermal = response.json()["thermal"]["number"] - general_parameters = general_api_model.to_user_model(nb_ts_thermal) - - # advanced and seed parameters - advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" - response = wrapper.get(advanced_parameters_url) - advanced_parameters_api_model = AdvancedAndSeedParametersAPI.model_validate(response.json()) - seed_parameters = advanced_parameters_api_model.to_user_seed_parameters_model() - advanced_parameters = advanced_parameters_api_model.to_user_advanced_parameters_model() - - # adequacy patch - adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" - response = wrapper.get(adequacy_patch_url) - adequacy_patch_api_model = AdequacyPatchParametersAPI.model_validate(response.json()) - adequacy_patch_parameters = adequacy_patch_api_model.to_user_model() - - except APIError as e: - raise StudySettingsReadError(study_id, e.message) from e - - return StudySettings( - general_parameters=general_parameters, - optimization_parameters=optimization_parameters, - seed_parameters=seed_parameters, - advanced_parameters=advanced_parameters, - adequacy_patch_parameters=adequacy_patch_parameters, - playlist_parameters=user_playlist, - thematic_trimming_parameters=thematic_trimming_parameters, - ) - - class StudyApiService(BaseStudyService): def __init__(self, config: APIconf, study_id: str): super().__init__() @@ -187,7 +66,7 @@ def set_output_service(self, output_service: BaseOutputService) -> None: def update_study_settings(self, settings: StudySettings) -> None: try: - _edit_study_settings(self._base_url, self.study_id, self._wrapper, settings) + edit_study_settings(self._base_url, self.study_id, self._wrapper, settings) except APIError as e: raise StudySettingsUpdateError(self.study_id, e.message) from e diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index fecc18d4..02f6d22a 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -12,6 +12,8 @@ from dataclasses import asdict from typing import Optional +from antares.craft.api_conf.request_wrapper import RequestWrapper +from antares.craft.exceptions.exceptions import APIError, StudySettingsReadError from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( AdvancedParameters, @@ -41,6 +43,8 @@ SimplexOptimizationRange, UnfeasibleProblemBehavior, ) +from antares.craft.model.settings.playlist_parameters import PlaylistParameters +from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel, Field @@ -354,3 +358,115 @@ def from_user_model(user_class: ThematicTrimmingParameters) -> "ThematicTrimming def to_user_model(self) -> ThematicTrimmingParameters: return ThematicTrimmingParameters(**self.model_dump(mode="json")) + + +def edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: + settings_base_url = f"{base_url}/studies/{study_id}/config" + + # thematic trimming + if settings.thematic_trimming_parameters: + thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" + api_model = ThematicTrimmingParametersAPI.from_user_model(settings.thematic_trimming_parameters) + body = api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(thematic_trimming_url, json=body) + + # playlist + if settings.playlist_parameters: + playlist_url = f"{settings_base_url}/playlist/form" + body = {} + for key, value in settings.playlist_parameters.items(): + body[str(key)] = asdict(value) + wrapper.put(playlist_url, json=body) + + # optimization + if settings.optimization_parameters: + optimization_url = f"{settings_base_url}/optimization/form" + optimization_api_model = OptimizationParametersAPI.from_user_model(settings.optimization_parameters) + body = optimization_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(optimization_url, json=body) + + # general and timeseries + if settings.general_parameters: + general_url = f"{settings_base_url}/general/form" + general_api_model = GeneralParametersAPI.from_user_model(settings.general_parameters) + body = general_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(general_url, json=body) + + if nb_ts_thermal := settings.general_parameters.nb_timeseries_thermal: + timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" + wrapper.put(timeseries_url, json={"thermal": {"number": nb_ts_thermal}}) + + # advanced and seed parameters + if settings.advanced_parameters or settings.seed_parameters: + advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" + advanced_api_model = AdvancedAndSeedParametersAPI.from_user_model( + settings.advanced_parameters, settings.seed_parameters + ) + body = advanced_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(advanced_parameters_url, json=body) + + # adequacy patch + if settings.adequacy_patch_parameters: + adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" + adequacy_patch_api_model = AdequacyPatchParametersAPI.from_user_model(settings.adequacy_patch_parameters) + body = adequacy_patch_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(adequacy_patch_url, json=body) + + +def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: + settings_base_url = f"{base_url}/studies/{study_id}/config" + try: + # thematic trimming + thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" + response = wrapper.get(thematic_trimming_url) + thematic_trimming_api_model = ThematicTrimmingParametersAPI.model_validate(response.json()) + thematic_trimming_parameters = thematic_trimming_api_model.to_user_model() + + # playlist + playlist_url = f"{settings_base_url}/playlist/form" + response = wrapper.get(playlist_url) + json_response = response.json() + user_playlist = {} + for key, value in json_response.items(): + user_playlist[int(key)] = PlaylistParameters(**value) + + # optimization + optimization_url = f"{settings_base_url}/optimization/form" + response = wrapper.get(optimization_url) + optimization_api_model = OptimizationParametersAPI.model_validate(response.json()) + optimization_parameters = optimization_api_model.to_user_model() + + # general and timeseries + general_url = f"{settings_base_url}/general/form" + response = wrapper.get(general_url) + general_api_model = GeneralParametersAPI.model_validate(response.json()) + timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" + response = wrapper.get(timeseries_url) + nb_ts_thermal = response.json()["thermal"]["number"] + general_parameters = general_api_model.to_user_model(nb_ts_thermal) + + # advanced and seed parameters + advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" + response = wrapper.get(advanced_parameters_url) + advanced_parameters_api_model = AdvancedAndSeedParametersAPI.model_validate(response.json()) + seed_parameters = advanced_parameters_api_model.to_user_seed_parameters_model() + advanced_parameters = advanced_parameters_api_model.to_user_advanced_parameters_model() + + # adequacy patch + adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" + response = wrapper.get(adequacy_patch_url) + adequacy_patch_api_model = AdequacyPatchParametersAPI.model_validate(response.json()) + adequacy_patch_parameters = adequacy_patch_api_model.to_user_model() + + except APIError as e: + raise StudySettingsReadError(study_id, e.message) from e + + return StudySettings( + general_parameters=general_parameters, + optimization_parameters=optimization_parameters, + seed_parameters=seed_parameters, + advanced_parameters=advanced_parameters, + adequacy_patch_parameters=adequacy_patch_parameters, + playlist_parameters=user_playlist, + thematic_trimming_parameters=thematic_trimming_parameters, + ) From 7467574ec5b5fd7a89d52d4d3ae5553fb33601da Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 17:09:03 +0100 Subject: [PATCH 40/69] use general data path --- .../craft/service/local_services/study_settings_local.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index af8aa599..d60e674a 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -37,6 +37,7 @@ from antares.craft.model.settings.study_settings import StudySettings from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model +from antares.craft.tools.ini_tool import InitializationFilesTypes from pydantic import BaseModel, Field @@ -365,8 +366,10 @@ class ThematicVarsLocal(Enum): def read_study_settings(study_directory: Path) -> StudySettings: + general_data_path = study_directory / InitializationFilesTypes.GENERAL.value raise NotImplementedError def edit_study_settings(study_directory: Path, settings: StudySettings) -> None: + general_data_path = study_directory / InitializationFilesTypes.GENERAL.value raise NotImplementedError From c22f8a8283e1ca77cfb40e6c8585136c5cb119b8 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 17:32:06 +0100 Subject: [PATCH 41/69] start coding --- .../local_services/study_settings_local.py | 137 +++++------------- 1 file changed, 35 insertions(+), 102 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index d60e674a..60ebe913 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -10,7 +10,6 @@ # # This file is part of the Antares project. from dataclasses import asdict -from enum import Enum from pathlib import Path from typing import Any, Set @@ -37,7 +36,7 @@ from antares.craft.model.settings.study_settings import StudySettings from antares.craft.tools.alias_generators import to_kebab from antares.craft.tools.all_optional_meta import all_optional_model -from antares.craft.tools.ini_tool import InitializationFilesTypes +from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes from pydantic import BaseModel, Field @@ -268,108 +267,42 @@ class OptimizationSettingsLocalEdition(OptimizationParametersLocalCreation): pass -class ThematicVarsLocal(Enum): - balance = "BALANCE" - dens = "DENS" - load = "LOAD" - lold = "LOLD" - lolp = "LOLP" - miscNdg = "MISC. NDG" - mrgPrice = "MRG. PRICE" - opCost = "OP. COST" - ovCost = "OV. COST" - rowBal = "ROW BAL." - spilEnrg = "SPIL. ENRG" - unspEnrg = "UNSP. ENRG" - hCost = "H. COST" - hInfl = "H. INFL" - hLev = "H. LEV" - hOvfl = "H. OVFL" - hPump = "H. PUMP" - hRor = "H. ROR" - hStor = "H. STOR" - hVal = "H. VAL" - psp = "PSP" - renw1 = "RENW. 1" - renw2 = "RENW. 2" - renw3 = "RENW. 3" - renw4 = "RENW. 4" - resGenerationByPlant = "RES generation by plant" - solar = "SOLAR" - solarConcrt = "SOLAR CONCRT." - solarPv = "SOLAR PV" - solarRooft = "SOLAR ROOFT" - wind = "WIND" - windOffshore = "WIND OFFSHORE" - windOnshore = "WIND ONSHORE" - batteryInjection = "BATTERY_INJECTION" - batteryLevel = "BATTERY_LEVEL" - batteryWithdrawal = "BATTERY_WITHDRAWAL" - other1Injection = "OTHER1_INJECTION" - other1Level = "OTHER1_LEVEL" - other1Withdrawal = "OTHER1_WITHDRAWAL" - other2Injection = "OTHER2_INJECTION" - other2Level = "OTHER2_LEVEL" - other2Withdrawal = "OTHER2_WITHDRAWAL" - other3Injection = "OTHER3_INJECTION" - other3Level = "OTHER3_LEVEL" - other3Withdrawal = "OTHER3_WITHDRAWAL" - other4Injection = "OTHER4_INJECTION" - other4Level = "OTHER4_LEVEL" - other4Withdrawal = "OTHER4_WITHDRAWAL" - other5Injection = "OTHER5_INJECTION" - other5Level = "OTHER5_LEVEL" - other5Withdrawal = "OTHER5_WITHDRAWAL" - pondageInjection = "PONDAGE_INJECTION" - pondageLevel = "PONDAGE_LEVEL" - pondageWithdrawal = "PONDAGE_WITHDRAWAL" - pspClosedInjection = "PSP_CLOSED_INJECTION" - pspClosedLevel = "PSP_CLOSED_LEVEL" - pspClosedWithdrawal = "PSP_CLOSED_WITHDRAWAL" - pspOpenInjection = "PSP_OPEN_INJECTION" - pspOpenLevel = "PSP_OPEN_LEVEL" - pspOpenWithdrawal = "PSP_OPEN_WITHDRAWAL" - stsCashflowByCluster = "STS CASHFLOW BY CLUSTER" - stsInjByPlant = "STS inj by plant" - stsLvlByPlant = "STS lvl by plant" - stsWithdrawalByPlant = "STS withdrawal by plant" - avlDtg = "AVL DTG" - co2Emis = "CO2 EMIS." - coal = "COAL" - dtgByPlant = "DTG by plant" - dtgMrg = "DTG MRG" - gas = "GAS" - lignite = "LIGNITE" - maxMrg = "MAX MRG" - miscDtg = "MISC. DTG" - miscDtg2 = "MISC. DTG 2" - miscDtg3 = "MISC. DTG 3" - miscDtg4 = "MISC. DTG 4" - mixFuel = "MIX. FUEL" - nodu = "NODU" - noduByPlant = "NODU by plant" - npCost = "NP COST" - npCostByPlant = "NP Cost by plant" - nuclear = "NUCLEAR" - oil = "OIL" - profitByPlant = "Profit by plant" - congFeeAbs = "CONG. FEE (ABS.)" - congFeeAlg = "CONG. FEE (ALG.)" - congProbMinus = "CONG. PROB -" - congProbPlus = "CONG. PROB +" - flowLin = "FLOW LIN." - flowQuad = "FLOW QUAD." - hurdleCost = "HURDLE COST" - loopFlow = "LOOP FLOW" - margCost = "MARG. COST" - ucapLin = "UCAP LIN." - - def read_study_settings(study_directory: Path) -> StudySettings: - general_data_path = study_directory / InitializationFilesTypes.GENERAL.value - raise NotImplementedError + general_data_ini = IniFile(study_directory, InitializationFilesTypes.THERMAL_AREAS_INI) + ini_content = general_data_ini.ini_dict + + # general + general_params_ini = {"general": ini_content["general"]} + if general_params_ini.pop("derated", None): + general_params_ini["building_mode"] = BuildingMode.DERATED.value + if general_params_ini.pop("custom-scenario", None): + general_params_ini["building_mode"] = BuildingMode.CUSTOM.value + else: + general_params_ini["building_mode"] = BuildingMode.AUTOMATIC.value + + excluded_keys = GeneralParametersLocalCreation.get_excluded_fields_for_user_class() + for key in excluded_keys: + general_params_ini.pop(key, None) + + output_parameters_ini = {"output": ini_content["output"]} + local_general_ini = general_params_ini | output_parameters_ini + general_parameters_local = GeneralParametersLocalCreation.model_validate(local_general_ini) + general_parameters = general_parameters_local.to_user_model() + + # optimization + + # todo + + return StudySettings( + general_parameters=general_parameters, + optimization_parameters=None, + seed_parameters=None, + advanced_parameters=None, + adequacy_patch_parameters=None, + playlist_parameters=None, + thematic_trimming_parameters=None, + ) def edit_study_settings(study_directory: Path, settings: StudySettings) -> None: - general_data_path = study_directory / InitializationFilesTypes.GENERAL.value raise NotImplementedError From 0e72ef783c722cc664cf1b1a5d2013de3f39656d Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 17:47:34 +0100 Subject: [PATCH 42/69] continue work --- .../local_services/study_settings_local.py | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 60ebe913..596b3f9a 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -290,15 +290,40 @@ def read_study_settings(study_directory: Path) -> StudySettings: general_parameters = general_parameters_local.to_user_model() # optimization - + optimization_ini = ini_content["optimization"] + optimization_ini.pop("link-type", None) + optimization_parameters_local = OptimizationParametersLocalCreation.model_validate(optimization_ini) + optimization_parameters = optimization_parameters_local.to_user_model() + + # adequacy_patch + adequacy_ini = ini_content["adequacy patch"] + adequacy_parameters_local = AdequacyPatchParametersLocalCreation.model_validate(adequacy_ini) + adequacy_patch_parameters = adequacy_parameters_local.to_user_model() + + # seed and advanced + seed_local_parameters = SeedParametersLocalCreation.model_validate(ini_content["seeds - Mersenne Twister"]) + advanced_local_parameters = AdvancedParametersLocalCreation.model_validate(ini_content["advanced parameters"]) + other_preferences_local_parameters = OtherPreferencesLocalCreation.model_validate(ini_content["other preferences"]) + args = { + "other_preferences": other_preferences_local_parameters, + "seeds": seed_local_parameters, + "advanced_parameters": advanced_local_parameters, + } + seed_and_advanced_local_parameters = AdvancedAndSeedParametersLocalCreation.model_validate(args) + seed_parameters = seed_and_advanced_local_parameters.to_seed_parameters_model() + advanced_parameters = seed_and_advanced_local_parameters.to_advanced_parameters_model() + + # playlist + # todo + # thematic trimming # todo return StudySettings( general_parameters=general_parameters, - optimization_parameters=None, - seed_parameters=None, - advanced_parameters=None, - adequacy_patch_parameters=None, + optimization_parameters=optimization_parameters, + seed_parameters=seed_parameters, + advanced_parameters=advanced_parameters, + adequacy_patch_parameters=adequacy_patch_parameters, playlist_parameters=None, thematic_trimming_parameters=None, ) From fd6cb90a215b3023aa075a3379e4de6893df0d1e Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 31 Jan 2025 17:53:11 +0100 Subject: [PATCH 43/69] do the first lines of thematic and playlist --- .../local_services/study_settings_local.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 596b3f9a..736f46b8 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -314,9 +314,16 @@ def read_study_settings(study_directory: Path) -> StudySettings: advanced_parameters = seed_and_advanced_local_parameters.to_advanced_parameters_model() # playlist - # todo + playlist_parameters = None + if "playlist" in ini_content: + playlist_parameters = None + # todo + # thematic trimming - # todo + thematic_trimming_parameters = None + if "variables selection" in ini_content: + thematic_trimming_parameters = None + # todo return StudySettings( general_parameters=general_parameters, @@ -324,8 +331,8 @@ def read_study_settings(study_directory: Path) -> StudySettings: seed_parameters=seed_parameters, advanced_parameters=advanced_parameters, adequacy_patch_parameters=adequacy_patch_parameters, - playlist_parameters=None, - thematic_trimming_parameters=None, + playlist_parameters=playlist_parameters, + thematic_trimming_parameters=thematic_trimming_parameters, ) From 4a8d8459e5030fdda922967e879874e8546e234a Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 10:28:02 +0100 Subject: [PATCH 44/69] remove edition class --- .../local_services/study_settings_local.py | 102 +++++++----------- 1 file changed, 37 insertions(+), 65 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 736f46b8..2506781d 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -35,12 +35,11 @@ ) from antares.craft.model.settings.study_settings import StudySettings from antares.craft.tools.alias_generators import to_kebab -from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes from pydantic import BaseModel, Field -class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): +class AdequacyPatchParametersLocal(BaseModel, alias_generator=to_kebab): include_adq_patch: bool = False set_to_null_ntc_from_physical_out_to_physical_in_for_first_step: bool = True set_to_null_ntc_between_physical_out_for_first_step: bool = True @@ -53,21 +52,16 @@ class AdequacyPatchParametersLocalCreation(BaseModel, alias_generator=to_kebab): enable_first_step: bool = False @staticmethod - def from_user_model(user_class: AdequacyPatchParameters) -> "AdequacyPatchParametersLocalCreation": + def from_user_model(user_class: AdequacyPatchParameters) -> "AdequacyPatchParametersLocal": user_dict = asdict(user_class) - return AdequacyPatchParametersLocalCreation.model_validate(user_dict) + return AdequacyPatchParametersLocal.model_validate(user_dict) def to_user_model(self) -> AdequacyPatchParameters: local_dict = self.model_dump(mode="json", by_alias=False, exclude={"enable_first_step"}) return AdequacyPatchParameters(**local_dict) -@all_optional_model -class AdequacyPatchParametersLocalEdition(AdequacyPatchParametersLocalCreation): - pass - - -class OtherPreferencesLocalCreation(BaseModel, alias_generator=to_kebab): +class OtherPreferencesLocal(BaseModel, alias_generator=to_kebab): initial_reservoir_levels: InitialReservoirLevel hydro_heuristic_policy: HydroHeuristicPolicy hydro_pricing_mode: HydroPricingMode @@ -80,22 +74,12 @@ class OtherPreferencesLocalCreation(BaseModel, alias_generator=to_kebab): day_ahead_reserve_management: Any -@all_optional_model -class OtherPreferencesLocalEdition(OtherPreferencesLocalCreation): - pass - - -class AdvancedParametersLocalCreation(BaseModel, alias_generator=to_kebab): +class AdvancedParametersLocal(BaseModel, alias_generator=to_kebab): accuracy_on_correlation: set[OutputChoices] = set() adequacy_block_size: int = 100 -@all_optional_model -class AdvancedParametersLocalEdition(AdvancedParametersLocalCreation): - pass - - -class SeedParametersLocalCreation(BaseModel, alias_generator=to_kebab): +class SeedParametersLocal(BaseModel, alias_generator=to_kebab): seed_tsgen_wind: int = 5489 seed_tsgen_load: int = 1005489 seed_tsgen_hydro: int = 2005489 @@ -109,20 +93,15 @@ class SeedParametersLocalCreation(BaseModel, alias_generator=to_kebab): seed_initial_reservoir_levels: int = 10005489 -@all_optional_model -class SeedParametersLocalEdition(SeedParametersLocalCreation): - pass - - -class AdvancedAndSeedParametersLocalCreation(BaseModel): - other_preferences: OtherPreferencesLocalCreation = Field(alias="other preferences") - advanced_parameters: AdvancedParametersLocalCreation = Field(alias="advanced parameters") - seeds: SeedParametersLocalCreation = Field(alias="seeds - Mersenne Twister") +class AdvancedAndSeedParametersLocal(BaseModel): + other_preferences: OtherPreferencesLocal = Field(alias="other preferences") + advanced_parameters: AdvancedParametersLocal = Field(alias="advanced parameters") + seeds: SeedParametersLocal = Field(alias="seeds - Mersenne Twister") @staticmethod def from_user_model( advanced_parameters: AdvancedParameters, seed_parameters: SeedParameters - ) -> "AdvancedAndSeedParametersLocalCreation": + ) -> "AdvancedAndSeedParametersLocal": other_preferences_local_dict = asdict(advanced_parameters) advanced_local_dict = { "advanced_parameters": { @@ -133,7 +112,7 @@ def from_user_model( local_dict = {"other_preferences": other_preferences_local_dict} | advanced_local_dict | seed_local_dict - return AdvancedAndSeedParametersLocalCreation.model_validate(local_dict) + return AdvancedAndSeedParametersLocal.model_validate(local_dict) def to_seed_parameters_model(self) -> SeedParameters: seed_values = self.model_dump(mode="json", by_alias=False, include=set(asdict(SeedParameters()).keys())) @@ -144,12 +123,6 @@ def to_advanced_parameters_model(self) -> AdvancedParameters: return AdvancedParameters(**advanced_values) -class AdvancedAndSeedParametersLocalEdition(AdvancedAndSeedParametersLocalCreation): - other_preferences: OtherPreferencesLocalEdition = Field(default=None, alias="other preferences") - advanced_parameters: AdvancedParametersLocalEdition = Field(default_factory=None, alias="advanced parameters") - seeds: SeedParametersLocalEdition = Field(default_factory=None, alias="seeds - Mersenne Twister") - - class GeneralSectionLocal(BaseModel): mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) horizon: str = "" @@ -188,13 +161,13 @@ class OutputSectionLocal(BaseModel): archives: set[OutputChoices] = set() -class GeneralParametersLocalCreation(BaseModel): +class GeneralParametersLocal(BaseModel): general: GeneralSectionLocal input: dict = {"import": ""} output: OutputSectionLocal @staticmethod - def from_user_model(user_class: GeneralParameters) -> "GeneralParametersLocalCreation": + def from_user_model(user_class: GeneralParameters) -> "GeneralParametersLocal": user_dict = asdict(user_class) output_dict = { @@ -206,7 +179,7 @@ def from_user_model(user_class: GeneralParameters) -> "GeneralParametersLocalCre general_dict = {"general": user_dict} local_dict = general_dict | output_dict - return GeneralParametersLocalCreation.model_validate(local_dict) + return GeneralParametersLocal.model_validate(local_dict) @staticmethod def get_excluded_fields_for_user_class() -> Set[str]: @@ -232,12 +205,7 @@ def to_user_model(self) -> GeneralParameters: return GeneralParameters(**local_dict) -@all_optional_model -class GeneralParametersLocalEdition(GeneralParametersLocalCreation): - pass - - -class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): +class OptimizationParametersLocal(BaseModel, alias_generator=to_kebab): simplex_range: SimplexOptimizationRange = SimplexOptimizationRange.WEEK transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES include_constraints: bool = True @@ -253,20 +221,15 @@ class OptimizationParametersLocalCreation(BaseModel, alias_generator=to_kebab): include_unfeasible_problem_behavior: UnfeasibleProblemBehavior = UnfeasibleProblemBehavior.ERROR_VERBOSE @staticmethod - def from_user_model(user_class: OptimizationParameters) -> "OptimizationParametersLocalCreation": + def from_user_model(user_class: OptimizationParameters) -> "OptimizationParametersLocal": user_dict = asdict(user_class) - return OptimizationParametersLocalCreation.model_validate(user_dict) + return OptimizationParametersLocal.model_validate(user_dict) def to_user_model(self) -> OptimizationParameters: local_dict = self.model_dump(mode="json", by_alias=False) return OptimizationParameters(**local_dict) -@all_optional_model -class OptimizationSettingsLocalEdition(OptimizationParametersLocalCreation): - pass - - def read_study_settings(study_directory: Path) -> StudySettings: general_data_ini = IniFile(study_directory, InitializationFilesTypes.THERMAL_AREAS_INI) ini_content = general_data_ini.ini_dict @@ -280,36 +243,36 @@ def read_study_settings(study_directory: Path) -> StudySettings: else: general_params_ini["building_mode"] = BuildingMode.AUTOMATIC.value - excluded_keys = GeneralParametersLocalCreation.get_excluded_fields_for_user_class() + excluded_keys = GeneralParametersLocal.get_excluded_fields_for_user_class() for key in excluded_keys: general_params_ini.pop(key, None) output_parameters_ini = {"output": ini_content["output"]} local_general_ini = general_params_ini | output_parameters_ini - general_parameters_local = GeneralParametersLocalCreation.model_validate(local_general_ini) + general_parameters_local = GeneralParametersLocal.model_validate(local_general_ini) general_parameters = general_parameters_local.to_user_model() # optimization optimization_ini = ini_content["optimization"] optimization_ini.pop("link-type", None) - optimization_parameters_local = OptimizationParametersLocalCreation.model_validate(optimization_ini) + optimization_parameters_local = OptimizationParametersLocal.model_validate(optimization_ini) optimization_parameters = optimization_parameters_local.to_user_model() # adequacy_patch adequacy_ini = ini_content["adequacy patch"] - adequacy_parameters_local = AdequacyPatchParametersLocalCreation.model_validate(adequacy_ini) + adequacy_parameters_local = AdequacyPatchParametersLocal.model_validate(adequacy_ini) adequacy_patch_parameters = adequacy_parameters_local.to_user_model() # seed and advanced - seed_local_parameters = SeedParametersLocalCreation.model_validate(ini_content["seeds - Mersenne Twister"]) - advanced_local_parameters = AdvancedParametersLocalCreation.model_validate(ini_content["advanced parameters"]) - other_preferences_local_parameters = OtherPreferencesLocalCreation.model_validate(ini_content["other preferences"]) + seed_local_parameters = SeedParametersLocal.model_validate(ini_content["seeds - Mersenne Twister"]) + advanced_local_parameters = AdvancedParametersLocal.model_validate(ini_content["advanced parameters"]) + other_preferences_local_parameters = OtherPreferencesLocal.model_validate(ini_content["other preferences"]) args = { "other_preferences": other_preferences_local_parameters, "seeds": seed_local_parameters, "advanced_parameters": advanced_local_parameters, } - seed_and_advanced_local_parameters = AdvancedAndSeedParametersLocalCreation.model_validate(args) + seed_and_advanced_local_parameters = AdvancedAndSeedParametersLocal.model_validate(args) seed_parameters = seed_and_advanced_local_parameters.to_seed_parameters_model() advanced_parameters = seed_and_advanced_local_parameters.to_advanced_parameters_model() @@ -336,5 +299,14 @@ def read_study_settings(study_directory: Path) -> StudySettings: ) -def edit_study_settings(study_directory: Path, settings: StudySettings) -> None: - raise NotImplementedError +def edit_study_settings(study_directory: Path, settings: StudySettings, update: bool) -> None: + general_data_ini = IniFile(study_directory, InitializationFilesTypes.THERMAL_AREAS_INI) + ini_content = general_data_ini.ini_dict if update else {} + + # general + general_parameters = settings.general_parameters or GeneralParameters() + general_local_parameters = GeneralParametersLocal.from_user_model(general_parameters) + ini_content.update(general_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) + + # optimization + # todo From 736306d3240e0aeb396318aeac08bd4f9a1c56e5 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 10:32:58 +0100 Subject: [PATCH 45/69] finalize function --- .../local_services/study_settings_local.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 2506781d..5ef84f10 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -309,4 +309,23 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: ini_content.update(general_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) # optimization + optimization_parameters = settings.optimization_parameters or OptimizationParameters() + optimization_local_parameters = OptimizationParametersLocal.from_user_model(optimization_parameters) + ini_content.update(optimization_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) + + # adequacy_patch + adequacy_parameters = settings.adequacy_patch_parameters or AdequacyPatchParameters() + adequacy_local_parameters = AdequacyPatchParametersLocal.from_user_model(adequacy_parameters) + ini_content.update(adequacy_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) + + # seed and advanced + seed_parameters = settings.seed_parameters or SeedParameters() + advanced_parameters = settings.advanced_parameters or AdvancedParameters() + advanced_parameters_local = AdvancedAndSeedParametersLocal.from_user_model(advanced_parameters, seed_parameters) + ini_content.update(advanced_parameters_local.model_dump(mode="json", by_alias=True, exclude_unset=update)) + + # playlist + # todo + + # thematic trimming # todo From 790e33bfc89c51bc6c32a11b52aa49da07f154ce Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 10:35:20 +0100 Subject: [PATCH 46/69] add the writing --- .../craft/service/local_services/study_settings_local.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 5ef84f10..4c3ea68b 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -329,3 +329,7 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: # thematic trimming # todo + + # writing + general_data_ini.ini_dict = ini_content + general_data_ini.write_ini_file() From 30ba1e9bbfece59f78ae7b42788b6563aab0c770 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 10:50:29 +0100 Subject: [PATCH 47/69] use read and edit --- src/antares/craft/model/study.py | 25 ++++++++----------- .../api_services/study_settings_api.py | 2 +- .../service/local_services/study_local.py | 3 ++- .../local_services/study_settings_local.py | 2 +- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index a6aecb1e..2e037840 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -14,7 +14,6 @@ import os import time -from dataclasses import asdict from pathlib import Path, PurePath from types import MappingProxyType from typing import List, Optional, Union @@ -35,8 +34,9 @@ from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.simulation import AntaresSimulationParameters, Job -from antares.craft.service.api_services.study_settings_api import read_study_settings +from antares.craft.service.api_services.study_settings_api import read_study_settings_api from antares.craft.service.base_services import BaseStudyService +from antares.craft.service.local_services.study_settings_local import edit_study_settings, read_study_settings_local from antares.craft.service.service_factory import ServiceFactory from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes @@ -77,7 +77,7 @@ def create_study_api( response = wrapper.post(url) study_id = response.json() # Settings part - study_settings = None if settings else read_study_settings(base_url, study_id, wrapper) + study_settings = None if settings else read_study_settings_api(base_url, study_id, wrapper) study = Study(study_name, version, ServiceFactory(api_config, study_id), study_settings) if settings: study.update_settings(settings) @@ -147,23 +147,15 @@ def create_study_local( _create_correlation_ini_files(study_directory) logging.info(f"Study successfully created: {study_name}") - study = Study( + edit_study_settings(study_directory, settings, update=False) + return Study( name=study_name, version=version, service_factory=ServiceFactory(config=local_config, study_name=study_name), - settings=None, + settings=settings, path=study_directory, ) - # handle the settings part here - local_settings = settings - local_settings_file = IniFile(study_directory, InitializationFilesTypes.GENERAL) - local_settings_file.ini_dict = asdict(local_settings) - # todo: replace this as dict with a specific method that does the mapping. - local_settings_file.write_ini_file() - - return study - def read_study_local(study_directory: Path) -> "Study": """ @@ -188,11 +180,14 @@ def _directory_not_exists(local_path: Path) -> None: local_config = LocalConfiguration(study_directory.parent, study_directory.name) + settings = read_study_settings_local(study_directory) + return Study( name=study_params["caption"], version=study_params["version"], service_factory=ServiceFactory(config=local_config, study_name=study_params["caption"]), path=study_directory, + settings=settings, ) @@ -207,7 +202,7 @@ def read_study_api(api_config: APIconf, study_id: str) -> "Study": path = json_study.pop("folder") pure_path = PurePath(path) if path else PurePath(".") - study_settings = read_study_settings(base_url, study_id, wrapper) + study_settings = read_study_settings_api(base_url, study_id, wrapper) study = Study( study_name, study_version, ServiceFactory(api_config, study_id, study_name), study_settings, pure_path ) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index 02f6d22a..2b19759f 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -413,7 +413,7 @@ def edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, s wrapper.put(adequacy_patch_url, json=body) -def read_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: +def read_study_settings_api(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: settings_base_url = f"{base_url}/studies/{study_id}/config" try: # thematic trimming diff --git a/src/antares/craft/service/local_services/study_local.py b/src/antares/craft/service/local_services/study_local.py index 60601a7e..f65b9369 100644 --- a/src/antares/craft/service/local_services/study_local.py +++ b/src/antares/craft/service/local_services/study_local.py @@ -17,6 +17,7 @@ from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import StudySettings from antares.craft.service.base_services import BaseOutputService, BaseStudyService +from antares.craft.service.local_services.study_settings_local import edit_study_settings if TYPE_CHECKING: from antares.craft.model.study import Study @@ -45,7 +46,7 @@ def set_output_service(self, output_service: BaseOutputService) -> None: self._output_service = output_service def update_study_settings(self, settings: StudySettings) -> None: - raise NotImplementedError + edit_study_settings(self.config.study_path, settings, update=True) def delete_binding_constraint(self, constraint: BindingConstraint) -> None: raise NotImplementedError diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 4c3ea68b..aaa687dc 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -230,7 +230,7 @@ def to_user_model(self) -> OptimizationParameters: return OptimizationParameters(**local_dict) -def read_study_settings(study_directory: Path) -> StudySettings: +def read_study_settings_local(study_directory: Path) -> StudySettings: general_data_ini = IniFile(study_directory, InitializationFilesTypes.THERMAL_AREAS_INI) ini_content = general_data_ini.ini_dict From f365333c4bcdf4b1c1d1f90a9e6a8908809d4a13 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 10:57:42 +0100 Subject: [PATCH 48/69] remove stupid test --- tests/antares/model/settings/conftest.py | 25 ------ .../settings/test_playlist_parameters.py | 88 ------------------- 2 files changed, 113 deletions(-) delete mode 100644 tests/antares/model/settings/conftest.py delete mode 100644 tests/antares/model/settings/test_playlist_parameters.py diff --git a/tests/antares/model/settings/conftest.py b/tests/antares/model/settings/conftest.py deleted file mode 100644 index 8b5255e6..00000000 --- a/tests/antares/model/settings/conftest.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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. - -import pytest - -from antares.craft.model.settings.playlist_parameters import PlaylistData, PlaylistParameters - - -@pytest.fixture -def test_playlist() -> PlaylistParameters: - return PlaylistParameters(playlist=[PlaylistData(status=False, weight=2.1)]) - - -@pytest.fixture -def test_playlist_model_dump(test_playlist) -> dict[str, str]: - return test_playlist.model_dump() diff --git a/tests/antares/model/settings/test_playlist_parameters.py b/tests/antares/model/settings/test_playlist_parameters.py deleted file mode 100644 index d709e931..00000000 --- a/tests/antares/model/settings/test_playlist_parameters.py +++ /dev/null @@ -1,88 +0,0 @@ -# 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. -import pytest - -from antares.craft.model.settings.playlist_parameters import PlaylistData, PlaylistParameters - - -class TestCreatePlaylistParameters: - def test_create_playlist_parameters_with_list(self): - created_params = PlaylistParameters(playlist=[PlaylistData(status=False, weight=2.1)]) - expected_model_dump = {1: {"status": False, "weight": 2.1}} - assert created_params - assert isinstance(created_params, PlaylistParameters) - assert created_params.model_dump() == expected_model_dump - - def test_create_playlist_parameters_with_dict(self): - created_params = PlaylistParameters( - playlist={"playlist_reset": True, "mc_years": {1: {"status": True, "weight": 3}}} - ) - expected_model_dump = {1: {"status": True, "weight": 3}} - assert created_params - assert isinstance(created_params, PlaylistParameters) - assert created_params.model_dump() == expected_model_dump - - def test_create_playlist_with_small_dict(self, test_playlist_model_dump): - # Given - created_params = PlaylistParameters(playlist={1: {"status": False, "weight": 2.1}}) - - # Then - assert created_params - assert isinstance(created_params, PlaylistParameters) - assert created_params.model_dump() == test_playlist_model_dump - - def test_create_playlist_parameters_with_object(self, test_playlist, test_playlist_model_dump): - created_params = PlaylistParameters(playlist=test_playlist) - - assert created_params - assert isinstance(created_params, PlaylistParameters) - assert created_params.model_dump() == test_playlist_model_dump - - def test_creating_wrong_dictionary_errors(self): - # Given - wrong_playlist_parameters_dict = {"playlist_reset": True, "mcyears": {1: {"status": True, "weight": 3}}} - - # Then - with pytest.raises(ValueError, match="Not a valid playlist dictionary."): - PlaylistParameters(playlist=wrong_playlist_parameters_dict) - - -class TestValidatePlaylistParameters: - def test_playlist_parameters_validate_dict(self): - created_params = PlaylistParameters.model_validate( - {"playlist_reset": True, "mc_years": {1: {"status": False, "weight": 2}}} - ) - expected_model_dump = {1: {"status": False, "weight": 2}} - - assert created_params - assert isinstance(created_params, PlaylistParameters) - assert created_params.model_dump() == expected_model_dump - - def test_playlist_parameters_validate_object(self, test_playlist, test_playlist_model_dump): - created_params = PlaylistParameters.model_validate(test_playlist) - - assert created_params - assert isinstance(created_params, PlaylistParameters) - assert created_params.model_dump() == test_playlist_model_dump - - def test_validating_wrong_dictionary_gives_empty_playlist(self): - # Given - wrong_playlist_parameters_dict = {"playlist_reset": True, "mcyears": {1: {"status": True, "weight": 3}}} - expected_model_dump = {} - - # When - created_params = PlaylistParameters.model_validate(wrong_playlist_parameters_dict) - - # Then - assert created_params - assert isinstance(created_params, PlaylistParameters) - assert created_params.model_dump() == expected_model_dump From 1fa808ae3c777d695b0c5f816a920f5bc675dc2f Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 11:29:32 +0100 Subject: [PATCH 49/69] remove useless functions --- .../local_services/study_settings_local.py | 2 +- src/antares/craft/tools/contents_tool.py | 44 ------------------- 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index aaa687dc..a20149b5 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -124,7 +124,7 @@ def to_advanced_parameters_model(self) -> AdvancedParameters: class GeneralSectionLocal(BaseModel): - mode: Mode = Field(default=Mode.ECONOMY, validate_default=True) + mode: Mode = Mode.ECONOMY horizon: str = "" nb_years: int = Field(default=1, alias="nb.years") simulation_start: int = Field(default=1, alias="simulation.start") diff --git a/src/antares/craft/tools/contents_tool.py b/src/antares/craft/tools/contents_tool.py index 98963681..5c1c5cb4 100644 --- a/src/antares/craft/tools/contents_tool.py +++ b/src/antares/craft/tools/contents_tool.py @@ -32,50 +32,6 @@ def transform_name_to_id(name: str) -> str: return _sub_invalid_chars(" ", name).strip().lower() -def retrieve_file_content(file_to_retrieve: str) -> Dict[str, Any]: - module_path = Path(__file__).resolve().parent - - path_resources = module_path.parent.parent / "resources" - path_to_file = path_resources / file_to_retrieve - - with open(path_to_file, "r") as read_content: - return json.load(read_content) - - -def transform_ui_data_to_text(data_from_json: Dict[str, Any]) -> str: - """ - Args: - data_from_json: ini data to be inserted - - Returns: - str to be written in .ini file - """ - ini_content = "" - for key, value in data_from_json.items(): - if isinstance(value, dict): - section_header = f"[{key}]" - ini_content += f"{section_header}\n" - for inner_key, inner_value in value.items(): - if isinstance(inner_value, list): - inner_value_str = " , ".join(map(str, inner_value)) - ini_content += f"{inner_key} = {inner_value_str}\n" - else: - ini_content += f"{inner_key} = {inner_value}\n" - else: - ini_content += f"{key} = {value}\n" - - return ini_content - - -def extract_content(key: str, file_to_retrieve: str) -> str: - ini_data = retrieve_file_content(file_to_retrieve) - data_for_file = ini_data.get(key) - if data_for_file is not None: - return transform_ui_data_to_text(data_for_file) - else: - raise KeyError(f"Key '{key}' not defined in {file_to_retrieve}") - - class EnumIgnoreCase(Enum): @classmethod def _missing_(cls, value: object) -> Optional["EnumIgnoreCase"]: From cce8bc8f0254dfda0d9f855a647c97cb94a8f104 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 15:12:20 +0100 Subject: [PATCH 50/69] fix lots of tests --- .../local_services/study_settings_local.py | 72 +++++++++++-------- src/antares/craft/tools/all_optional_meta.py | 17 ++++- src/antares/craft/tools/contents_tool.py | 2 - 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index a20149b5..990a6c92 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -9,9 +9,11 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +import ast + from dataclasses import asdict from pathlib import Path -from typing import Any, Set +from typing import Any, Sequence, Set from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( @@ -35,11 +37,12 @@ ) from antares.craft.model.settings.study_settings import StudySettings from antares.craft.tools.alias_generators import to_kebab +from antares.craft.tools.all_optional_meta import LocalBaseModel from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes -from pydantic import BaseModel, Field +from pydantic import Field, field_validator -class AdequacyPatchParametersLocal(BaseModel, alias_generator=to_kebab): +class AdequacyPatchParametersLocal(LocalBaseModel, alias_generator=to_kebab): include_adq_patch: bool = False set_to_null_ntc_from_physical_out_to_physical_in_for_first_step: bool = True set_to_null_ntc_between_physical_out_for_first_step: bool = True @@ -61,25 +64,30 @@ def to_user_model(self) -> AdequacyPatchParameters: return AdequacyPatchParameters(**local_dict) -class OtherPreferencesLocal(BaseModel, alias_generator=to_kebab): - initial_reservoir_levels: InitialReservoirLevel - hydro_heuristic_policy: HydroHeuristicPolicy - hydro_pricing_mode: HydroPricingMode - power_fluctuations: PowerFluctuation - shedding_policy: SheddingPolicy - shedding_strategy: Any - unit_commitment_mode: UnitCommitmentMode - number_of_cores_mode: SimulationCore - renewable_generation_modelling: RenewableGenerationModeling - day_ahead_reserve_management: Any +class OtherPreferencesLocal(LocalBaseModel, alias_generator=to_kebab): + initial_reservoir_levels: InitialReservoirLevel = InitialReservoirLevel.COLD_START + hydro_heuristic_policy: HydroHeuristicPolicy = HydroHeuristicPolicy.ACCOMMODATE_RULES_CURVES + hydro_pricing_mode: HydroPricingMode = HydroPricingMode.FAST + power_fluctuations: PowerFluctuation = PowerFluctuation.FREE_MODULATIONS + shedding_policy: SheddingPolicy = SheddingPolicy.SHAVE_PEAKS + shedding_strategy: Any = "shave margins" + unit_commitment_mode: UnitCommitmentMode = UnitCommitmentMode.FAST + number_of_cores_mode: SimulationCore = SimulationCore.MEDIUM + renewable_generation_modelling: RenewableGenerationModeling = RenewableGenerationModeling.CLUSTERS + day_ahead_reserve_management: Any = "global" -class AdvancedParametersLocal(BaseModel, alias_generator=to_kebab): +class AdvancedParametersLocal(LocalBaseModel, alias_generator=to_kebab): accuracy_on_correlation: set[OutputChoices] = set() adequacy_block_size: int = 100 + @field_validator("accuracy_on_correlation", mode="before") + def validate_accuracy_on_correlation(cls, v: Any) -> Sequence[str]: + """Ensure the ID is lower case.""" + return ast.literal_eval(v) + -class SeedParametersLocal(BaseModel, alias_generator=to_kebab): +class SeedParametersLocal(LocalBaseModel, alias_generator=to_kebab): seed_tsgen_wind: int = 5489 seed_tsgen_load: int = 1005489 seed_tsgen_hydro: int = 2005489 @@ -93,7 +101,7 @@ class SeedParametersLocal(BaseModel, alias_generator=to_kebab): seed_initial_reservoir_levels: int = 10005489 -class AdvancedAndSeedParametersLocal(BaseModel): +class AdvancedAndSeedParametersLocal(LocalBaseModel): other_preferences: OtherPreferencesLocal = Field(alias="other preferences") advanced_parameters: AdvancedParametersLocal = Field(alias="advanced parameters") seeds: SeedParametersLocal = Field(alias="seeds - Mersenne Twister") @@ -111,7 +119,7 @@ def from_user_model( seed_local_dict = {"seeds": asdict(seed_parameters)} local_dict = {"other_preferences": other_preferences_local_dict} | advanced_local_dict | seed_local_dict - + # fake_dict = {"seeds": {}, "other_preferences": {}, "advanced_parameters": {}} return AdvancedAndSeedParametersLocal.model_validate(local_dict) def to_seed_parameters_model(self) -> SeedParameters: @@ -123,7 +131,7 @@ def to_advanced_parameters_model(self) -> AdvancedParameters: return AdvancedParameters(**advanced_values) -class GeneralSectionLocal(BaseModel): +class GeneralSectionLocal(LocalBaseModel): mode: Mode = Mode.ECONOMY horizon: str = "" nb_years: int = Field(default=1, alias="nb.years") @@ -155,13 +163,13 @@ class GeneralSectionLocal(BaseModel): read_only: bool = Field(default=False, alias="readonly") -class OutputSectionLocal(BaseModel): +class OutputSectionLocal(LocalBaseModel): synthesis: bool = True store_new_set: bool = Field(default=True, alias="storenewset") - archives: set[OutputChoices] = set() + archives: Any = "" -class GeneralParametersLocal(BaseModel): +class GeneralParametersLocal(LocalBaseModel): general: GeneralSectionLocal input: dict = {"import": ""} output: OutputSectionLocal @@ -201,11 +209,15 @@ def get_excluded_fields_for_user_class() -> Set[str]: } def to_user_model(self) -> GeneralParameters: - local_dict = self.model_dump(mode="json", by_alias=False, exclude=self.get_excluded_fields_for_user_class()) + local_dict = self.general.model_dump( + mode="json", by_alias=False, exclude=self.get_excluded_fields_for_user_class() + ) + local_dict.update(self.output.model_dump(mode="json", by_alias=False, exclude={"archives"})) + local_dict["simulation_synthesis"] = local_dict.pop("synthesis") return GeneralParameters(**local_dict) -class OptimizationParametersLocal(BaseModel, alias_generator=to_kebab): +class OptimizationParametersLocal(LocalBaseModel, alias_generator=to_kebab): simplex_range: SimplexOptimizationRange = SimplexOptimizationRange.WEEK transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES include_constraints: bool = True @@ -231,7 +243,7 @@ def to_user_model(self) -> OptimizationParameters: def read_study_settings_local(study_directory: Path) -> StudySettings: - general_data_ini = IniFile(study_directory, InitializationFilesTypes.THERMAL_AREAS_INI) + general_data_ini = IniFile(study_directory, InitializationFilesTypes.GENERAL) ini_content = general_data_ini.ini_dict # general @@ -300,7 +312,7 @@ def read_study_settings_local(study_directory: Path) -> StudySettings: def edit_study_settings(study_directory: Path, settings: StudySettings, update: bool) -> None: - general_data_ini = IniFile(study_directory, InitializationFilesTypes.THERMAL_AREAS_INI) + general_data_ini = IniFile(study_directory, InitializationFilesTypes.GENERAL) ini_content = general_data_ini.ini_dict if update else {} # general @@ -311,12 +323,16 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: # optimization optimization_parameters = settings.optimization_parameters or OptimizationParameters() optimization_local_parameters = OptimizationParametersLocal.from_user_model(optimization_parameters) - ini_content.update(optimization_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) + ini_content.update( + {"optimization": optimization_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} + ) # adequacy_patch adequacy_parameters = settings.adequacy_patch_parameters or AdequacyPatchParameters() adequacy_local_parameters = AdequacyPatchParametersLocal.from_user_model(adequacy_parameters) - ini_content.update(adequacy_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) + ini_content.update( + {"adequacy patch": adequacy_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} + ) # seed and advanced seed_parameters = settings.seed_parameters or SeedParameters() diff --git a/src/antares/craft/tools/all_optional_meta.py b/src/antares/craft/tools/all_optional_meta.py index 9aa394e3..c66cf100 100644 --- a/src/antares/craft/tools/all_optional_meta.py +++ b/src/antares/craft/tools/all_optional_meta.py @@ -13,7 +13,8 @@ import copy import typing as t -from pydantic import BaseModel, create_model +from pydantic import BaseModel, ConfigDict, create_model, field_validator +from pydantic_core import PydanticUseDefault ModelClass = t.TypeVar("ModelClass", bound=BaseModel) @@ -36,3 +37,17 @@ def all_optional_model(model: t.Type[ModelClass]) -> t.Type[ModelClass]: kwargs[field_name] = (new.annotation, new) return create_model(f"Partial{model.__name__}", __base__=model, __module__=model.__module__, **kwargs) # type: ignore + + +class LocalBaseModel(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + @field_validator("*", mode="before") + @classmethod + def _usedefault_for_none(cls, value: t.Any) -> t.Any: + """ + Will use the default value for the field if the value is None and the annotation doesn't allow for a None input. + """ + if value is None: + raise PydanticUseDefault() + return value diff --git a/src/antares/craft/tools/contents_tool.py b/src/antares/craft/tools/contents_tool.py index 5c1c5cb4..41964338 100644 --- a/src/antares/craft/tools/contents_tool.py +++ b/src/antares/craft/tools/contents_tool.py @@ -10,11 +10,9 @@ # # This file is part of the Antares project. -import json import re from enum import Enum -from pathlib import Path from typing import Any, Dict, Optional from antares.craft.tools.custom_raw_config_parser import CustomRawConfigParser From 7e84e80ee610d4b3711ae3456b0296b2f3dd4c0e Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 15:14:30 +0100 Subject: [PATCH 51/69] finalize tests --- .../craft/service/local_services/study_settings_local.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 990a6c92..145db75d 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -84,6 +84,8 @@ class AdvancedParametersLocal(LocalBaseModel, alias_generator=to_kebab): @field_validator("accuracy_on_correlation", mode="before") def validate_accuracy_on_correlation(cls, v: Any) -> Sequence[str]: """Ensure the ID is lower case.""" + if v is None: + return [] return ast.literal_eval(v) @@ -119,7 +121,6 @@ def from_user_model( seed_local_dict = {"seeds": asdict(seed_parameters)} local_dict = {"other_preferences": other_preferences_local_dict} | advanced_local_dict | seed_local_dict - # fake_dict = {"seeds": {}, "other_preferences": {}, "advanced_parameters": {}} return AdvancedAndSeedParametersLocal.model_validate(local_dict) def to_seed_parameters_model(self) -> SeedParameters: From a0627b3f01122bd147435ac02b86a1b272939310 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 15:20:53 +0100 Subject: [PATCH 52/69] fix tets --- src/antares/craft/service/api_services/study_settings_api.py | 2 +- tests/antares/services/api_services/test_study_api.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index 2b19759f..98d5c774 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -143,7 +143,7 @@ def to_user_advanced_parameters_model(self) -> AdvancedParameters: return AdvancedParameters(**self.model_dump(mode="json", exclude=excluded_fields)) def to_user_seed_parameters_model(self) -> SeedParameters: - included_fields = self._get_advanced_user_parameters_fields() + included_fields = set(asdict(SeedParameters()).keys()) return SeedParameters(**self.model_dump(mode="json", include=included_fields)) diff --git a/tests/antares/services/api_services/test_study_api.py b/tests/antares/services/api_services/test_study_api.py index cc8474b2..438f7f50 100644 --- a/tests/antares/services/api_services/test_study_api.py +++ b/tests/antares/services/api_services/test_study_api.py @@ -77,6 +77,8 @@ def test_create_study_test_ok(self) -> None: mocker.post(expected_url, json=self.study_id, status_code=200) config_urls = re.compile(f"https://antares.com/api/v1/studies/{self.study_id}/config/.*") mocker.get(config_urls, json={}, status_code=200) + ts_settings_url = f"https://antares.com/api/v1/studies/{self.study_id}/timeseries/config" + mocker.get(ts_settings_url, json={"thermal": {"number": 1}}, status_code=200) expected_url_path = f"https://antares.com/api/v1/studies/{self.study_id}" mocker.get( expected_url_path, From 611fce9c3724b545dead11504da2f705b36764ad Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 15:23:53 +0100 Subject: [PATCH 53/69] fix test --- tests/antares/services/api_services/test_study_api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/antares/services/api_services/test_study_api.py b/tests/antares/services/api_services/test_study_api.py index 438f7f50..1042fb04 100644 --- a/tests/antares/services/api_services/test_study_api.py +++ b/tests/antares/services/api_services/test_study_api.py @@ -232,6 +232,7 @@ def test_read_study_api(self): } config_urls = re.compile(f"https://antares.com/api/v1/studies/{self.study_id}/config/.*") + ts_settings_url = f"https://antares.com/api/v1/studies/{self.study_id}/timeseries/config" base_url = "https://antares.com/api/v1" url = f"{base_url}/studies/{self.study_id}" @@ -246,6 +247,7 @@ def test_read_study_api(self): with requests_mock.Mocker() as mocker: mocker.get(url, json=json_study) mocker.get(config_urls, json={}) + mocker.get(ts_settings_url, json={"thermal": {"number": 1}}, status_code=200) mocker.get(area_url, json=json_ui) mocker.get(area_props_url, json={}) mocker.get(renewable_url, json=[]) @@ -290,6 +292,8 @@ def test_create_variant_success(self): config_urls = re.compile(f"{base_url}/studies/{variant_id}/config/.*") mocker.get(config_urls, json={}, status_code=200) + ts_settings_url = f"https://antares.com/api/v1/studies/{variant_id}/timeseries/config" + mocker.get(ts_settings_url, json={"thermal": {"number": 1}}, status_code=200) areas_url = f"{base_url}/studies/{variant_id}/areas?ui=true" mocker.get(areas_url, json={}, status_code=200) From b9843e4e8ecc1cf467a8ed78101dc000361b2fef Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 16:10:23 +0100 Subject: [PATCH 54/69] start rewriting test --- .../services/local_services/test_study.py | 301 ++---------------- 1 file changed, 26 insertions(+), 275 deletions(-) diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index 285f30ad..c4e25d15 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -9,6 +9,8 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. +from dataclasses import asdict + import pytest import logging @@ -50,11 +52,11 @@ TransmissionCapacities, ) from antares.craft.model.settings.adequacy_patch import ( - DefaultAdequacyPatchParameters, + AdequacyPatchParameters, PriceTakingOrder, ) from antares.craft.model.settings.advanced_parameters import ( - AdvancedParametersLocal, + AdvancedParameters, HydroHeuristicPolicy, HydroPricingMode, InitialReservoirLevel, @@ -62,28 +64,26 @@ RenewableGenerationModeling, SheddingPolicy, SimulationCore, - UnitCommitmentMode, + UnitCommitmentMode, SeedParameters, ) from antares.craft.model.settings.general import ( BuildingMode, - GeneralParametersLocal, + GeneralParameters, Mode, Month, WeekDay, ) from antares.craft.model.settings.optimization import ( ExportMPS, - OptimizationParametersLocal, + OptimizationParameters, OptimizationTransmissionCapacities, SimplexOptimizationRange, UnfeasibleProblemBehavior, ) -from antares.craft.model.settings.playlist_parameters import PlaylistData, PlaylistParameters -from antares.craft.model.settings.study_settings import DefaultStudySettings, StudySettingsLocal -from antares.craft.model.settings.thematic_trimming import ( - DefaultThematicTrimmingParameters, - ThematicTrimmingParametersLocal, -) +from antares.craft.model.settings.playlist_parameters import PlaylistParameters +from antares.craft.model.settings.study_settings import StudySettings +from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters + from antares.craft.model.study import create_study_local from antares.craft.tools.ini_tool import InitializationFilesTypes @@ -213,146 +213,21 @@ def test_local_study_has_settings(self, local_study): local_study_settings = local_study.get_settings() # Then assert local_study.get_settings() - assert isinstance(local_study_settings, DefaultStudySettings) - - def test_local_study_has_correct_default_general_properties(self, local_study): - # Given - # https://antares-simulator.readthedocs.io/en/latest/user-guide/solver/04-parameters/ - expected_general_properties = GeneralParametersLocal.model_validate( - { - "mode": Mode.ECONOMY, - "horizon": "", - "nb_years": 1, - "first_day": 1, - "last_day": 365, - "first_january": WeekDay.MONDAY, - "first_month": Month.JANUARY, - "first_week_day": WeekDay.MONDAY, - "leap_year": False, - "year_by_year": False, - "building_mode": BuildingMode.AUTOMATIC, - "selection_mode": False, - "thematic_trimming": False, - "geographic_trimming": False, - "active_rules_scenario": "default ruleset", - "read_only": False, - "simulation_synthesis": True, - "mc_scenario": False, - } - ) - # When - expected_study_settings = StudySettingsLocal(general_properties=expected_general_properties) - - # Then - assert local_study.get_settings().general_parameters == expected_general_properties - assert local_study.get_settings() == expected_study_settings - - def test_local_study_has_correct_default_adequacy_patch_properties(self, local_study): - # Given - expected_adequacy_patch_properties = DefaultAdequacyPatchParameters.model_validate( - { - "enable_adequacy_patch": False, - "ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch": True, - "ntc_between_physical_areas_out_adequacy_patch": True, - "price_taking_order": PriceTakingOrder.DENS, - "include_hurdle_cost_csr": False, - "check_csr_cost_function": False, - "enable_first_step": False, - "threshold_initiate_curtailment_sharing_rule": 0, - "threshold_display_local_matching_rule_violations": 0, - "threshold_csr_variable_bounds_relaxation": 3, - } - ) - expected_study_settings = StudySettingsLocal( - adequacy_patch_properties=DefaultAdequacyPatchParameters.model_validate( - expected_adequacy_patch_properties.model_dump(exclude_none=True) - ) - ) + assert isinstance(local_study_settings, StudySettings) - # When - actual_adequacy_patch_properties = DefaultAdequacyPatchParameters.model_validate( - local_study.get_settings().adequacy_patch_parameters.model_dump(exclude_none=True) - ) - actual_study_settings = StudySettingsLocal.model_validate( - local_study.get_settings().model_dump(exclude_none=True) - ) + def test_default_values_when_creating_study(self, local_study): + default_general_parameters = GeneralParameters() + default_optimization_parameters = OptimizationParameters() + default_advanced_parameters = AdvancedParameters() + default_seed_parameters = SeedParameters() + default_adequacy_parameters = AdequacyPatchParameters() + default_playlist_parameters = {} + default_thematic_trimming_parameters = ThematicTrimmingParameters() - # Then - assert actual_adequacy_patch_properties == expected_adequacy_patch_properties - assert actual_study_settings == expected_study_settings + study_settings = local_study.get_settings() + # todo: currently it doesn't work ... + assert study_settings.general_parameters == default_general_parameters - def test_local_study_has_correct_advanced_parameters(self, local_study): - # Given - expected_advanced_parameters = AdvancedParametersLocal.model_validate( - { - "accuracy_on_correlation": "", - "initial_reservoir_levels": InitialReservoirLevel.COLD_START, - "hydro_heuristic_policy": HydroHeuristicPolicy.ACCOMMODATE_RULES_CURVES, - "hydro_pricing_mode": HydroPricingMode.FAST, - "power_fluctuations": PowerFluctuation.FREE_MODULATIONS, - "shedding_policy": SheddingPolicy.SHAVE_PEAKS, - "unit_commitment_mode": UnitCommitmentMode.FAST, - "number_of_cores_mode": SimulationCore.MEDIUM, - "renewable_generation_modelling": RenewableGenerationModeling.AGGREGATED, - "seed_tsgen_wind": 5489, - "seed_tsgen_load": 1005489, - "seed_tsgen_hydro": 2005489, - "seed_tsgen_thermal": 3005489, - "seed_tsgen_solar": 4005489, - "seed_tsnumbers": 5005489, - "seed_unsupplied_energy_costs": 6005489, - "seed_spilled_energy_costs": 7005489, - "seed_thermal_costs": 8005489, - "seed_hydro_costs": 9005489, - "seed_initial_reservoir_levels": 10005489, - } - ) - expected_study_settings = StudySettingsLocal(advanced_parameters=expected_advanced_parameters) - - # When - actual_advanced_parameters = AdvancedParametersLocal.model_validate( - local_study.get_settings().advanced_parameters.model_dump(exclude_none=True) - ) - actual_study_settings = StudySettingsLocal.model_validate( - local_study.get_settings().model_dump(exclude_none=True) - ) - - # Then - assert actual_advanced_parameters == expected_advanced_parameters - assert actual_study_settings == expected_study_settings - - def test_local_study_has_correct_optimization_parameters(self, local_study): - # Given - expected_optimization_parameters = OptimizationParametersLocal.model_validate( - { - "simplex_optimization_range": SimplexOptimizationRange.WEEK, - "transmission_capacities": OptimizationTransmissionCapacities.LOCAL_VALUES, - "binding_constraints": True, - "hurdle_costs": True, - "thermal_clusters_min_stable_power": True, - "thermal_clusters_min_ud_time": True, - "day_ahead_reserve": True, - "strategic_reserve": True, - "spinning_reserve": True, - "primary_reserve": True, - "export_mps": ExportMPS.NONE, - "include_exportstructure": False, - "unfeasible_problem_behavior": UnfeasibleProblemBehavior.ERROR_VERBOSE, - } - ) - expected_study_settings = StudySettingsLocal(optimization_parameters=expected_optimization_parameters) - - # When - actual_optimization_parameters = OptimizationParametersLocal.model_validate( - local_study.get_settings().optimization_parameters.model_dump(exclude_none=True) - ) - actual_study_settings = StudySettingsLocal.model_validate( - local_study.get_settings().model_dump(exclude_none=True) - ) - - # Then - assert actual_optimization_parameters == expected_optimization_parameters - assert actual_study_settings == expected_study_settings def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): # Given @@ -361,9 +236,9 @@ def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): "test_study", "880", str(tmp_path.absolute()), - StudySettingsLocal( - general_parameters=GeneralParametersLocal(nb_years=nb_years, selection_mode=True), - playlist_parameters=PlaylistParameters(playlist=[PlaylistData()] * nb_years), + StudySettings( + general_parameters=GeneralParameters(nb_years=nb_years, user_playlist=True), + playlist_parameters={year: PlaylistParameters() for year in range(nb_years)}, ), ) @@ -378,130 +253,6 @@ def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): assert actual_playlist_parameters_dict == expected_playlist_parameters_dict assert actual_playlist_parameters == expected_playlist_parameters - def test_local_study_has_correct_thematic_trimming_parameters(self, tmp_path): - # Given - expected_thematic_trimming_parameters = ThematicTrimmingParametersLocal.model_validate( - { - "ov_cost": True, - "op_cost": True, - "mrg_price": True, - "co2_emis": True, - "dtg_by_plant": True, - "balance": True, - "row_bal": True, - "psp": True, - "misc_ndg": True, - "load": True, - "h_ror": True, - "wind": True, - "solar": True, - "nuclear": True, - "lignite": True, - "coal": True, - "gas": True, - "oil": True, - "mix_fuel": True, - "misc_dtg": True, - "h_stor": True, - "h_pump": True, - "h_lev": True, - "h_infl": True, - "h_ovfl": True, - "h_val": True, - "h_cost": True, - "unsp_enrg": True, - "spil_enrg": True, - "lold": True, - "lolp": True, - "avl_dtg": True, - "dtg_mrg": True, - "max_mrg": True, - "np_cost": True, - "np_cost_by_plant": True, - "nodu": True, - "nodu_by_plant": True, - "flow_lin": True, - "ucap_lin": True, - "loop_flow": True, - "flow_quad": True, - "cong_fee_alg": True, - "cong_fee_abs": True, - "marg_cost": True, - "cong_prob_plus": True, - "cong_prob_minus": True, - "hurdle_cost": True, - "res_generation_by_plant": True, - "misc_dtg_2": True, - "misc_dtg_3": True, - "misc_dtg_4": True, - "wind_offshore": True, - "wind_onshore": True, - "solar_concrt": True, - "solar_pv": True, - "solar_rooft": True, - "renw_1": True, - "renw_2": True, - "renw_3": True, - "renw_4": True, - "dens": True, - "profit_by_plant": True, - "sts_inj_by_plant": True, - "sts_withdrawal_by_plant": True, - "sts_lvl_by_plant": True, - "psp_open_injection": True, - "psp_open_withdrawal": True, - "psp_open_level": True, - "psp_closed_injection": True, - "psp_closed_withdrawal": True, - "psp_closed_level": True, - "pondage_injection": True, - "pondage_withdrawal": True, - "pondage_level": True, - "battery_injection": True, - "battery_withdrawal": True, - "battery_level": True, - "other1_injection": True, - "other1_withdrawal": True, - "other1_level": True, - "other2_injection": True, - "other2_withdrawal": True, - "other2_level": True, - "other3_injection": True, - "other3_withdrawal": True, - "other3_level": True, - "other4_injection": True, - "other4_withdrawal": True, - "other4_level": True, - "other5_injection": True, - "other5_withdrawal": True, - "other5_level": True, - "sts_cashflow_by_cluster": True, - } - ) - expected_study_settings = StudySettingsLocal( - general_parameters=GeneralParametersLocal(thematic_trimming=True), - thematic_trimming_parameters=expected_thematic_trimming_parameters, - ) - thematic_trimming_study = create_study_local( - "test_study", - "880", - str(tmp_path.absolute()), - StudySettingsLocal( - general_parameters=GeneralParametersLocal(thematic_trimming=True), - thematic_trimming_parameters=ThematicTrimmingParametersLocal(), - ), - ) - - # When - actual_thematic_trimming_parameters = DefaultThematicTrimmingParameters.model_validate( - thematic_trimming_study.get_settings().thematic_trimming_parameters - ) - actual_study_settings = DefaultStudySettings.model_validate(thematic_trimming_study.get_settings()) - - # Then - assert actual_thematic_trimming_parameters == expected_thematic_trimming_parameters - assert actual_study_settings == expected_study_settings - def test_generaldata_ini_exists(self, local_study): # Given expected_file = local_study.service.config.study_path / "settings/generaldata.ini" From 45b3d6bbd20026fb25519205eba12a520f84f85f Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 16:20:13 +0100 Subject: [PATCH 55/69] return the right settings inside edit settings --- src/antares/craft/model/study.py | 4 ++-- .../local_services/study_settings_local.py | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 2e037840..476d000b 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -147,12 +147,12 @@ def create_study_local( _create_correlation_ini_files(study_directory) logging.info(f"Study successfully created: {study_name}") - edit_study_settings(study_directory, settings, update=False) + new_settings = edit_study_settings(study_directory, settings, update=False) return Study( name=study_name, version=version, service_factory=ServiceFactory(config=local_config, study_name=study_name), - settings=settings, + settings=new_settings, path=study_directory, ) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 145db75d..ebe350c6 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -312,7 +312,7 @@ def read_study_settings_local(study_directory: Path) -> StudySettings: ) -def edit_study_settings(study_directory: Path, settings: StudySettings, update: bool) -> None: +def edit_study_settings(study_directory: Path, settings: StudySettings, update: bool) -> StudySettings: general_data_ini = IniFile(study_directory, InitializationFilesTypes.GENERAL) ini_content = general_data_ini.ini_dict if update else {} @@ -320,6 +320,7 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: general_parameters = settings.general_parameters or GeneralParameters() general_local_parameters = GeneralParametersLocal.from_user_model(general_parameters) ini_content.update(general_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) + new_general_parameters = general_local_parameters.to_user_model() # optimization optimization_parameters = settings.optimization_parameters or OptimizationParameters() @@ -327,6 +328,7 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: ini_content.update( {"optimization": optimization_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} ) + new_optimization_parameters = optimization_local_parameters.to_user_model() # adequacy_patch adequacy_parameters = settings.adequacy_patch_parameters or AdequacyPatchParameters() @@ -334,12 +336,15 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: ini_content.update( {"adequacy patch": adequacy_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} ) + new_adequacy_parameters = adequacy_local_parameters.to_user_model() # seed and advanced seed_parameters = settings.seed_parameters or SeedParameters() advanced_parameters = settings.advanced_parameters or AdvancedParameters() advanced_parameters_local = AdvancedAndSeedParametersLocal.from_user_model(advanced_parameters, seed_parameters) ini_content.update(advanced_parameters_local.model_dump(mode="json", by_alias=True, exclude_unset=update)) + new_seed_parameters = advanced_parameters_local.to_seed_parameters_model() + new_advanced_parameters = advanced_parameters_local.to_advanced_parameters_model() # playlist # todo @@ -350,3 +355,14 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: # writing general_data_ini.ini_dict = ini_content general_data_ini.write_ini_file() + + # returning new_settings + return StudySettings( + general_parameters=new_general_parameters, + optimization_parameters=new_optimization_parameters, + adequacy_patch_parameters=new_adequacy_parameters, + seed_parameters=new_seed_parameters, + advanced_parameters=new_advanced_parameters, + playlist_parameters=None, + thematic_trimming_parameters=None, + ) From 2ba1c37869c2b9e0efc795f9c24625e8c605bd9e Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 17:02:56 +0100 Subject: [PATCH 56/69] fix default value --- .../craft/service/local_services/study_settings_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index ebe350c6..51719c0f 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -166,7 +166,7 @@ class GeneralSectionLocal(LocalBaseModel): class OutputSectionLocal(LocalBaseModel): synthesis: bool = True - store_new_set: bool = Field(default=True, alias="storenewset") + store_new_set: bool = Field(default=False, alias="storenewset") archives: Any = "" From cb781488a2cf0b61758d0c8729575fb0030a368c Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 17:11:17 +0100 Subject: [PATCH 57/69] start re writing test --- .../services/local_services/test_study.py | 237 +++++++++++++++--- 1 file changed, 202 insertions(+), 35 deletions(-) diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index c4e25d15..4888af95 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -9,8 +9,6 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. -from dataclasses import asdict - import pytest import logging @@ -53,37 +51,18 @@ ) from antares.craft.model.settings.adequacy_patch import ( AdequacyPatchParameters, - PriceTakingOrder, ) from antares.craft.model.settings.advanced_parameters import ( AdvancedParameters, - HydroHeuristicPolicy, - HydroPricingMode, - InitialReservoirLevel, - PowerFluctuation, - RenewableGenerationModeling, - SheddingPolicy, - SimulationCore, - UnitCommitmentMode, SeedParameters, ) from antares.craft.model.settings.general import ( - BuildingMode, GeneralParameters, - Mode, - Month, - WeekDay, ) from antares.craft.model.settings.optimization import ( - ExportMPS, OptimizationParameters, - OptimizationTransmissionCapacities, - SimplexOptimizationRange, - UnfeasibleProblemBehavior, ) from antares.craft.model.settings.playlist_parameters import PlaylistParameters from antares.craft.model.settings.study_settings import StudySettings -from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters - from antares.craft.model.study import create_study_local from antares.craft.tools.ini_tool import InitializationFilesTypes @@ -215,19 +194,83 @@ def test_local_study_has_settings(self, local_study): assert local_study.get_settings() assert isinstance(local_study_settings, StudySettings) - def test_default_values_when_creating_study(self, local_study): - default_general_parameters = GeneralParameters() - default_optimization_parameters = OptimizationParameters() - default_advanced_parameters = AdvancedParameters() - default_seed_parameters = SeedParameters() - default_adequacy_parameters = AdequacyPatchParameters() - default_playlist_parameters = {} - default_thematic_trimming_parameters = ThematicTrimmingParameters() + def test_local_study_has_correct_default_general_properties(self, local_study): + expected_general_properties = GeneralParameters(**{ + "mode": "economy", + "horizon": "", + "nb_years": 1, + "simulation_start": 1, + "simulation_end": 365, + "january_first": "monday", + "first_month_in_year": "january", + "first_week_day": "monday", + "leap_year": False, + "year_by_year": False, + "building_mode": "automatic", + "thematic_trimming": False, + "geographic_trimming": False, + "simulation_synthesis": True, + "user_playlist": False, + "store_new_set": False, + "nb_timeseries_thermal": 1 + }) + + assert local_study.get_settings().general_parameters == expected_general_properties + + def test_local_study_has_correct_default_adequacy_patch_properties(self, local_study): + expected_adequacy_patch_properties = AdequacyPatchParameters(** + { + "include_adq_patch": False, + "set_to_null_ntc_from_physical_out_to_physical_in_for_first_step": True, + "set_to_null_ntc_between_physical_out_for_first_step": True, + "price_taking_order": "DENS", + "include_hurdle_cost_csr": False, + "check_csr_cost_function": False, + "threshold_initiate_curtailment_sharing_rule": 0, + "threshold_display_local_matching_rule_violations": 0, + "threshold_csr_variable_bounds_relaxation": 3, + } + ) + + assert local_study.get_settings().adequacy_patch_parameters == expected_adequacy_patch_properties + + def test_local_study_has_correct_advanced_parameters(self, local_study): + expected_advanced_parameters = AdvancedParameters(** + { + "accuracy_on_correlation": "[]", + "initial_reservoir_levels": "cold start", + "hydro_heuristic_policy": "accomodate rule curves", + "hydro_pricing_mode": "fast", + "power_fluctuations": "free modulations", + "shedding_policy": "shave peaks", + "unit_commitment_mode": "fast", + "number_of_cores_mode": "medium", + "renewable_generation_modelling": "clusters", + } + ) - study_settings = local_study.get_settings() - # todo: currently it doesn't work ... - assert study_settings.general_parameters == default_general_parameters + assert local_study.get_settings().advanced_parameters == expected_advanced_parameters + + def test_local_study_has_correct_optimization_parameters(self, local_study): + expected_optimization_parameters = OptimizationParameters(** + { + "simplex_range": "week", + "transmission_capacities": "local-values", + "include_constraints": True, + "include_hurdle_costs": True, + "include_tc_minstablepower": True, + "include_tc_min_ud_time": True, + "include_dayahead": True, + "include_strategicreserve": True, + "include_spinningreserve": True, + "include_primaryreserve": True, + "include_exportmps": False, + "include_exportstructure": False, + "include_unfeasible_problem_behavior": "error-verbose", + } + ) + assert local_study.get_settings().optimization_parameters == expected_optimization_parameters def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): # Given @@ -236,9 +279,9 @@ def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): "test_study", "880", str(tmp_path.absolute()), - StudySettings( - general_parameters=GeneralParameters(nb_years=nb_years, user_playlist=True), - playlist_parameters={year: PlaylistParameters() for year in range(nb_years)}, + StudySettingsLocal( + general_parameters=GeneralParametersLocal(nb_years=nb_years, selection_mode=True), + playlist_parameters=PlaylistParameters(playlist=[PlaylistData()] * nb_years), ), ) @@ -253,6 +296,130 @@ def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): assert actual_playlist_parameters_dict == expected_playlist_parameters_dict assert actual_playlist_parameters == expected_playlist_parameters + def test_local_study_has_correct_thematic_trimming_parameters(self, tmp_path): + # Given + expected_thematic_trimming_parameters = ThematicTrimmingParametersLocal.model_validate( + { + "ov_cost": True, + "op_cost": True, + "mrg_price": True, + "co2_emis": True, + "dtg_by_plant": True, + "balance": True, + "row_bal": True, + "psp": True, + "misc_ndg": True, + "load": True, + "h_ror": True, + "wind": True, + "solar": True, + "nuclear": True, + "lignite": True, + "coal": True, + "gas": True, + "oil": True, + "mix_fuel": True, + "misc_dtg": True, + "h_stor": True, + "h_pump": True, + "h_lev": True, + "h_infl": True, + "h_ovfl": True, + "h_val": True, + "h_cost": True, + "unsp_enrg": True, + "spil_enrg": True, + "lold": True, + "lolp": True, + "avl_dtg": True, + "dtg_mrg": True, + "max_mrg": True, + "np_cost": True, + "np_cost_by_plant": True, + "nodu": True, + "nodu_by_plant": True, + "flow_lin": True, + "ucap_lin": True, + "loop_flow": True, + "flow_quad": True, + "cong_fee_alg": True, + "cong_fee_abs": True, + "marg_cost": True, + "cong_prob_plus": True, + "cong_prob_minus": True, + "hurdle_cost": True, + "res_generation_by_plant": True, + "misc_dtg_2": True, + "misc_dtg_3": True, + "misc_dtg_4": True, + "wind_offshore": True, + "wind_onshore": True, + "solar_concrt": True, + "solar_pv": True, + "solar_rooft": True, + "renw_1": True, + "renw_2": True, + "renw_3": True, + "renw_4": True, + "dens": True, + "profit_by_plant": True, + "sts_inj_by_plant": True, + "sts_withdrawal_by_plant": True, + "sts_lvl_by_plant": True, + "psp_open_injection": True, + "psp_open_withdrawal": True, + "psp_open_level": True, + "psp_closed_injection": True, + "psp_closed_withdrawal": True, + "psp_closed_level": True, + "pondage_injection": True, + "pondage_withdrawal": True, + "pondage_level": True, + "battery_injection": True, + "battery_withdrawal": True, + "battery_level": True, + "other1_injection": True, + "other1_withdrawal": True, + "other1_level": True, + "other2_injection": True, + "other2_withdrawal": True, + "other2_level": True, + "other3_injection": True, + "other3_withdrawal": True, + "other3_level": True, + "other4_injection": True, + "other4_withdrawal": True, + "other4_level": True, + "other5_injection": True, + "other5_withdrawal": True, + "other5_level": True, + "sts_cashflow_by_cluster": True, + } + ) + expected_study_settings = StudySettingsLocal( + general_parameters=GeneralParametersLocal(thematic_trimming=True), + thematic_trimming_parameters=expected_thematic_trimming_parameters, + ) + thematic_trimming_study = create_study_local( + "test_study", + "880", + str(tmp_path.absolute()), + StudySettingsLocal( + general_parameters=GeneralParametersLocal(thematic_trimming=True), + thematic_trimming_parameters=ThematicTrimmingParametersLocal(), + ), + ) + + # When + actual_thematic_trimming_parameters = DefaultThematicTrimmingParameters.model_validate( + thematic_trimming_study.get_settings().thematic_trimming_parameters + ) + actual_study_settings = DefaultStudySettings.model_validate(thematic_trimming_study.get_settings()) + + # Then + assert actual_thematic_trimming_parameters == expected_thematic_trimming_parameters + assert actual_study_settings == expected_study_settings + def test_generaldata_ini_exists(self, local_study): # Given expected_file = local_study.service.config.study_path / "settings/generaldata.ini" From 575d6a4507ce1340aa8ba384987a0b7eb77dc4a6 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 17:54:11 +0100 Subject: [PATCH 58/69] fix correlation code --- src/antares/craft/model/study.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 476d000b..5fdca8b1 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -478,16 +478,19 @@ def _create_directory_structure(study_path: Path) -> None: def _create_correlation_ini_files(study_directory: Path) -> None: - fields_to_check = ["hydro", "load", "solar", "wind"] correlation_inis_to_create = [ - ( - field + "_correlation", - getattr(InitializationFilesTypes, field.upper() + "_CORRELATION_INI"), - field, - ) - for field in fields_to_check + getattr(InitializationFilesTypes, field.upper() + "_CORRELATION_INI") + for field in ["hydro", "load", "solar", "wind"] ] - for correlation, file_type, field in correlation_inis_to_create: - ini_file = IniFile(study_directory, file_type, ini_contents=None) + ini_content = {"general": {"mode": "annual"}, "annual": {}} + for k in range(12): + ini_content[str(k)] = {} + + for file_type in correlation_inis_to_create: + ini_file = IniFile( + study_directory, + file_type, + ini_contents=ini_content, + ) ini_file.write_ini_file() From 35176a8b5fe657859ac4e339eea8ee14fb8605ce Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 18:04:22 +0100 Subject: [PATCH 59/69] fix code --- .../model/settings/advanced_parameters.py | 2 +- .../local_services/study_settings_local.py | 6 +++-- .../services/local_services/test_study.py | 23 +++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/antares/craft/model/settings/advanced_parameters.py b/src/antares/craft/model/settings/advanced_parameters.py index 91a12b53..682cec66 100644 --- a/src/antares/craft/model/settings/advanced_parameters.py +++ b/src/antares/craft/model/settings/advanced_parameters.py @@ -77,7 +77,7 @@ class AdvancedParameters: @dataclass class SeedParameters: seed_tsgen_thermal: Optional[int] = None - seed_ts_numbers: Optional[int] = None + seed_tsnumbers: Optional[int] = None seed_unsupplied_energy_costs: Optional[int] = None seed_spilled_energy_costs: Optional[int] = None seed_thermal_costs: Optional[int] = None diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index 51719c0f..dbd4b2c5 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -124,11 +124,13 @@ def from_user_model( return AdvancedAndSeedParametersLocal.model_validate(local_dict) def to_seed_parameters_model(self) -> SeedParameters: - seed_values = self.model_dump(mode="json", by_alias=False, include=set(asdict(SeedParameters()).keys())) + seed_values = self.seeds.model_dump(mode="json", by_alias=False, include=set(asdict(SeedParameters()).keys())) return SeedParameters(**seed_values) def to_advanced_parameters_model(self) -> AdvancedParameters: - advanced_values = self.model_dump(mode="json", by_alias=False, include=set(asdict(AdvancedParameters()).keys())) + advanced_values = self.advanced_parameters.model_dump( + mode="json", by_alias=False, include=set(asdict(AdvancedParameters()).keys()) + ) return AdvancedParameters(**advanced_values) diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index 4888af95..2a0e5b35 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -53,7 +53,7 @@ AdequacyPatchParameters, ) from antares.craft.model.settings.advanced_parameters import ( - AdvancedParameters, + AdvancedParameters, SeedParameters, ) from antares.craft.model.settings.general import ( GeneralParameters, @@ -237,7 +237,7 @@ def test_local_study_has_correct_default_adequacy_patch_properties(self, local_s def test_local_study_has_correct_advanced_parameters(self, local_study): expected_advanced_parameters = AdvancedParameters(** { - "accuracy_on_correlation": "[]", + "accuracy_on_correlation": [], "initial_reservoir_levels": "cold start", "hydro_heuristic_policy": "accomodate rule curves", "hydro_pricing_mode": "fast", @@ -251,6 +251,21 @@ def test_local_study_has_correct_advanced_parameters(self, local_study): assert local_study.get_settings().advanced_parameters == expected_advanced_parameters + def test_local_study_has_correct_seed_parameters(self, local_study): + expected_seed_parameters = SeedParameters(** + { + "seed_tsgen_thermal": 3005489, + "seed_tsnumbers": 5005489, + "seed_unsupplied_energy_costs": 6005489, + "seed_spilled_energy_costs": 7005489, + "seed_thermal_costs": 8005489, + "seed_hydro_costs": 9005489, + "seed_initial_reservoir_levels": 10005489, + } + ) + + assert local_study.get_settings().seed_parameters == expected_seed_parameters + def test_local_study_has_correct_optimization_parameters(self, local_study): expected_optimization_parameters = OptimizationParameters(** { @@ -272,6 +287,10 @@ def test_local_study_has_correct_optimization_parameters(self, local_study): assert local_study.get_settings().optimization_parameters == expected_optimization_parameters + def test_local_study_has_correct_playlist_parameters(self, local_study): + assert local_study.get_settings().playlist_parameters is None + assert local_study.get_settings().thematic_trimming_parameters is None + def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): # Given nb_years = 2 From 6cffccddcc92fd23f280076af786d1450c567cac Mon Sep 17 00:00:00 2001 From: belthlemar Date: Mon, 3 Feb 2025 18:07:21 +0100 Subject: [PATCH 60/69] fix one tets --- .../craft/service/local_services/study_settings_local.py | 9 +++++---- tests/antares/services/local_services/test_study.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index dbd4b2c5..b75fa4b5 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -128,10 +128,11 @@ def to_seed_parameters_model(self) -> SeedParameters: return SeedParameters(**seed_values) def to_advanced_parameters_model(self) -> AdvancedParameters: - advanced_values = self.advanced_parameters.model_dump( - mode="json", by_alias=False, include=set(asdict(AdvancedParameters()).keys()) - ) - return AdvancedParameters(**advanced_values) + includes = set(asdict(AdvancedParameters()).keys()) + advanced_values = self.advanced_parameters.model_dump(mode="json", by_alias=False, include=includes) + other_preferences_values = self.other_preferences.model_dump(mode="json", by_alias=False, include=includes) + merged_values = advanced_values | other_preferences_values + return AdvancedParameters(**merged_values) class GeneralSectionLocal(LocalBaseModel): diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index 2a0e5b35..81f416a1 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -239,7 +239,7 @@ def test_local_study_has_correct_advanced_parameters(self, local_study): { "accuracy_on_correlation": [], "initial_reservoir_levels": "cold start", - "hydro_heuristic_policy": "accomodate rule curves", + "hydro_heuristic_policy": "accommodate rule curves", "hydro_pricing_mode": "fast", "power_fluctuations": "free modulations", "shedding_policy": "shave peaks", From 9abb538f8a5c83c1c22af89ac464fd1ee7358b5d Mon Sep 17 00:00:00 2001 From: belthlemar Date: Tue, 4 Feb 2025 09:43:25 +0100 Subject: [PATCH 61/69] fix code --- .../craft/model/settings/optimization.py | 2 +- .../local_services/study_settings_local.py | 23 ++++++++++----- .../services/local_services/test_study.py | 28 ++++++++++--------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/antares/craft/model/settings/optimization.py b/src/antares/craft/model/settings/optimization.py index e6f527df..ef519911 100644 --- a/src/antares/craft/model/settings/optimization.py +++ b/src/antares/craft/model/settings/optimization.py @@ -46,7 +46,7 @@ class OptimizationParameters: simplex_range: Optional[SimplexOptimizationRange] = None transmission_capacities: Optional[OptimizationTransmissionCapacities] = None include_constraints: Optional[bool] = None - include_hurdle_costs: Optional[bool] = None + include_hurdlecosts: Optional[bool] = None include_tc_minstablepower: Optional[bool] = None include_tc_min_ud_time: Optional[bool] = None include_dayahead: Optional[bool] = None diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/study_settings_local.py index b75fa4b5..89e8ece0 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/study_settings_local.py @@ -138,7 +138,7 @@ def to_advanced_parameters_model(self) -> AdvancedParameters: class GeneralSectionLocal(LocalBaseModel): mode: Mode = Mode.ECONOMY horizon: str = "" - nb_years: int = Field(default=1, alias="nb.years") + nb_years: int = Field(default=1, alias="nbyears") simulation_start: int = Field(default=1, alias="simulation.start") simulation_end: int = Field(default=365, alias="simulation.end") january_first: WeekDay = Field(default=WeekDay.MONDAY, alias="january.1st") @@ -157,8 +157,8 @@ class GeneralSectionLocal(LocalBaseModel): nb_timeseries_thermal: int = Field(default=1, alias="nbtimeseriesthermal") nb_timeseries_solar: int = Field(default=1, alias="nbtimeseriessolar") refresh_timeseries: bool = Field(default=False, alias="refreshtimeseries") - intra_model: bool = Field(default=False, alias="intra-model") - inter_model: bool = Field(default=False, alias="inter-model") + intra_modal: bool = Field(default=False, alias="intra-modal") + inter_modal: bool = Field(default=False, alias="inter-modal") refresh_interval_load: int = Field(default=100, alias="refreshintervalload") refresh_interval_hydro: int = Field(default=100, alias="refreshintervalhydro") refresh_interval_wind: int = Field(default=100, alias="refreshintervalwind") @@ -202,8 +202,8 @@ def get_excluded_fields_for_user_class() -> Set[str]: "nb_timeseries_wind", "nb_timeseries_solar", "refresh_timeseries", - "intra_model", - "inter_model", + "intra_modal", + "inter_modal", "refresh_interval_load", "refresh_interval_hydro", "refresh_interval_wind", @@ -225,7 +225,7 @@ class OptimizationParametersLocal(LocalBaseModel, alias_generator=to_kebab): simplex_range: SimplexOptimizationRange = SimplexOptimizationRange.WEEK transmission_capacities: OptimizationTransmissionCapacities = OptimizationTransmissionCapacities.LOCAL_VALUES include_constraints: bool = True - include_hurdle_costs: bool = True + include_hurdlecosts: bool = True include_tc_minstablepower: bool = True include_tc_min_ud_time: bool = True include_dayahead: bool = True @@ -322,7 +322,16 @@ def edit_study_settings(study_directory: Path, settings: StudySettings, update: # general general_parameters = settings.general_parameters or GeneralParameters() general_local_parameters = GeneralParametersLocal.from_user_model(general_parameters) - ini_content.update(general_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)) + + json_content = general_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update) + if "general" in json_content and "building_mode" in json_content["general"]: + general_values = json_content["general"] + del general_values["building_mode"] + building_mode = general_local_parameters.general.building_mode + general_values["derated"] = building_mode == BuildingMode.DERATED + general_values["custom-scenario"] = building_mode == BuildingMode.CUSTOM + + ini_content.update(json_content) new_general_parameters = general_local_parameters.to_user_model() # optimization diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index 81f416a1..f08385a8 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -459,26 +459,26 @@ def test_generaldata_ini_has_correct_default_values(self, local_study): first.weekday = Monday leapyear = false year-by-year = false -derated = false -custom-scenario = false user-playlist = false thematic-trimming = false geographic-trimming = false -generate = +generate = false nbtimeseriesload = 1 nbtimeserieshydro = 1 -nbtimeseriesthermal = 1 nbtimeserieswind = 1 +nbtimeseriesthermal = 1 nbtimeseriessolar = 1 -refreshtimeseries = -intra-modal = -inter-modal = +refreshtimeseries = false +intra-modal = false +inter-modal = false refreshintervalload = 100 refreshintervalhydro = 100 -refreshintervalthermal = 100 refreshintervalwind = 100 +refreshintervalthermal = 100 refreshintervalsolar = 100 readonly = false +derated = false +custom-scenario = false [input] import = @@ -487,7 +487,6 @@ def test_generaldata_ini_has_correct_default_values(self, local_study): synthesis = true storenewset = false archives = -result-format = txt-files [optimization] simplex-range = week @@ -500,7 +499,7 @@ def test_generaldata_ini_has_correct_default_values(self, local_study): include-strategicreserve = true include-spinningreserve = true include-primaryreserve = true -include-exportmps = none +include-exportmps = false include-exportstructure = false include-unfeasible-problem-behavior = error-verbose @@ -508,13 +507,13 @@ def test_generaldata_ini_has_correct_default_values(self, local_study): include-adq-patch = false set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = true set-to-null-ntc-between-physical-out-for-first-step = true -enable-first-step = false price-taking-order = DENS include-hurdle-cost-csr = false check-csr-cost-function = false threshold-initiate-curtailment-sharing-rule = 0.000000 threshold-display-local-matching-rule-violations = 0.000000 threshold-csr-variable-bounds-relaxation = 3 +enable-first-step = false [other preferences] initial-reservoir-levels = cold start @@ -522,12 +521,15 @@ def test_generaldata_ini_has_correct_default_values(self, local_study): hydro-pricing-mode = fast power-fluctuations = free modulations shedding-policy = shave peaks +shedding-strategy = shave margins unit-commitment-mode = fast number-of-cores-mode = medium -renewable-generation-modelling = aggregated +renewable-generation-modelling = clusters +day-ahead-reserve-management = global [advanced parameters] -accuracy-on-correlation = +accuracy-on-correlation = [] +adequacy-block-size = 100 [seeds - Mersenne Twister] seed-tsgen-wind = 5489 From d0e30e2a77ec669c0023a411de2e96551f7d60da Mon Sep 17 00:00:00 2001 From: belthlemar Date: Tue, 4 Feb 2025 09:50:33 +0100 Subject: [PATCH 62/69] fix test --- src/antares/craft/model/settings/general.py | 43 +- .../api_services/study_settings_api.py | 2 +- .../services/local_services/test_study.py | 472 +++++++++--------- 3 files changed, 258 insertions(+), 259 deletions(-) diff --git a/src/antares/craft/model/settings/general.py b/src/antares/craft/model/settings/general.py index 2245b9ff..ea5bcc5b 100644 --- a/src/antares/craft/model/settings/general.py +++ b/src/antares/craft/model/settings/general.py @@ -16,34 +16,33 @@ class Mode(EnumIgnoreCase): - ECONOMY = "economy" - ADEQUACY = "adequacy" - DRAFT = "draft" + ECONOMY = "Economy" + ADEQUACY = "Adequacy" class Month(EnumIgnoreCase): - JANUARY = "january" - FEBRUARY = "february" - MARCH = "march" - APRIL = "april" - MAY = "may" - JUNE = "june" - JULY = "july" - AUGUST = "august" - SEPTEMBER = "september" - OCTOBER = "october" - NOVEMBER = "november" - DECEMBER = "december" + JANUARY = "January" + FEBRUARY = "February" + MARCH = "March" + APRIL = "April" + MAY = "May" + JUNE = "June" + JULY = "July" + AUGUST = "August" + SEPTEMBER = "September" + OCTOBER = "October" + NOVEMBER = "November" + DECEMBER = "December" class WeekDay(EnumIgnoreCase): - MONDAY = "monday" - TUESDAY = "tuesday" - WEDNESDAY = "wednesday" - THURSDAY = "thursday" - FRIDAY = "friday" - SATURDAY = "saturday" - SUNDAY = "sunday" + MONDAY = "Monday" + TUESDAY = "Tuesday" + WEDNESDAY = "Wednesday" + THURSDAY = "Thursday" + FRIDAY = "Friday" + SATURDAY = "Saturday" + SUNDAY = "Sunday" class BuildingMode(EnumIgnoreCase): diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/study_settings_api.py index 98d5c774..f9a414f0 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/study_settings_api.py @@ -241,7 +241,7 @@ def to_user_model(self) -> OptimizationParameters: simplex_range=self.simplex_optimization_range, transmission_capacities=self.transmission_capacities, include_constraints=self.binding_constraints, - include_hurdle_costs=self.hurdle_costs, + include_hurdlecosts=self.hurdle_costs, include_tc_minstablepower=self.thermal_clusters_min_stable_power, include_tc_min_ud_time=self.thermal_clusters_min_ud_time, include_dayahead=self.day_ahead_reserve, diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index f08385a8..e7efa2d6 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -457,63 +457,63 @@ def test_generaldata_ini_has_correct_default_values(self, local_study): january.1st = Monday first-month-in-year = January first.weekday = Monday -leapyear = false -year-by-year = false -user-playlist = false -thematic-trimming = false -geographic-trimming = false -generate = false +leapyear = False +year-by-year = False +user-playlist = False +thematic-trimming = False +geographic-trimming = False +generate = False nbtimeseriesload = 1 nbtimeserieshydro = 1 nbtimeserieswind = 1 nbtimeseriesthermal = 1 nbtimeseriessolar = 1 -refreshtimeseries = false -intra-modal = false -inter-modal = false +refreshtimeseries = False +intra-modal = False +inter-modal = False refreshintervalload = 100 refreshintervalhydro = 100 refreshintervalwind = 100 refreshintervalthermal = 100 refreshintervalsolar = 100 -readonly = false -derated = false -custom-scenario = false +readonly = False +derated = False +custom-scenario = False [input] import = [output] -synthesis = true -storenewset = false +synthesis = True +storenewset = False archives = [optimization] simplex-range = week transmission-capacities = local-values -include-constraints = true -include-hurdlecosts = true -include-tc-minstablepower = true -include-tc-min-ud-time = true -include-dayahead = true -include-strategicreserve = true -include-spinningreserve = true -include-primaryreserve = true -include-exportmps = false -include-exportstructure = false +include-constraints = True +include-hurdlecosts = True +include-tc-minstablepower = True +include-tc-min-ud-time = True +include-dayahead = True +include-strategicreserve = True +include-spinningreserve = True +include-primaryreserve = True +include-exportmps = False +include-exportstructure = False include-unfeasible-problem-behavior = error-verbose [adequacy patch] -include-adq-patch = false -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = true -set-to-null-ntc-between-physical-out-for-first-step = true +include-adq-patch = False +set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True +set-to-null-ntc-between-physical-out-for-first-step = True price-taking-order = DENS -include-hurdle-cost-csr = false -check-csr-cost-function = false -threshold-initiate-curtailment-sharing-rule = 0.000000 -threshold-display-local-matching-rule-violations = 0.000000 +include-hurdle-cost-csr = False +check-csr-cost-function = False +threshold-initiate-curtailment-sharing-rule = 0 +threshold-display-local-matching-rule-violations = 0 threshold-csr-variable-bounds-relaxation = 3 -enable-first-step = false +enable-first-step = False [other preferences] initial-reservoir-levels = cold start @@ -569,13 +569,13 @@ def test_generaldata_ini_with_thematic_trimming_has_negative_sign(self, tmp_path january.1st = Monday first-month-in-year = January first.weekday = Monday -leapyear = false -year-by-year = false -derated = false -custom-scenario = false -user-playlist = false -thematic-trimming = true -geographic-trimming = false +leapyear = False +year-by-year = False +derated = False +custom-scenario = False +user-playlist = False +thematic-trimming = True +geographic-trimming = False generate = nbtimeseriesload = 1 nbtimeserieshydro = 1 @@ -590,42 +590,42 @@ def test_generaldata_ini_with_thematic_trimming_has_negative_sign(self, tmp_path refreshintervalthermal = 100 refreshintervalwind = 100 refreshintervalsolar = 100 -readonly = false +readonly = False [input] import = [output] -synthesis = true -storenewset = false +synthesis = True +storenewset = False archives = result-format = txt-files [optimization] simplex-range = week transmission-capacities = local-values -include-constraints = true -include-hurdlecosts = true -include-tc-minstablepower = true -include-tc-min-ud-time = true -include-dayahead = true -include-strategicreserve = true -include-spinningreserve = true -include-primaryreserve = true +include-constraints = True +include-hurdlecosts = True +include-tc-minstablepower = True +include-tc-min-ud-time = True +include-dayahead = True +include-strategicreserve = True +include-spinningreserve = True +include-primaryreserve = True include-exportmps = none -include-exportstructure = false +include-exportstructure = False include-unfeasible-problem-behavior = error-verbose [adequacy patch] -include-adq-patch = false -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = true -set-to-null-ntc-between-physical-out-for-first-step = true -enable-first-step = false +include-adq-patch = False +set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True +set-to-null-ntc-between-physical-out-for-first-step = True +enable-first-step = False price-taking-order = DENS -include-hurdle-cost-csr = false -check-csr-cost-function = false -threshold-initiate-curtailment-sharing-rule = 0.000000 -threshold-display-local-matching-rule-violations = 0.000000 +include-hurdle-cost-csr = False +check-csr-cost-function = False +threshold-initiate-curtailment-sharing-rule = 0 +threshold-display-local-matching-rule-violations = 0 threshold-csr-variable-bounds-relaxation = 3 [other preferences] @@ -655,7 +655,7 @@ def test_generaldata_ini_with_thematic_trimming_has_negative_sign(self, tmp_path seed-initial-reservoir-levels = 10005489 [variables selection] -selected_vars_reset = true +selected_vars_reset = True select_var - = OP. COST """ @@ -694,13 +694,13 @@ def test_generaldata_ini_with_thematic_trimming_has_positive_sign(self, tmp_path january.1st = Monday first-month-in-year = January first.weekday = Monday -leapyear = false -year-by-year = false -derated = false -custom-scenario = false -user-playlist = false -thematic-trimming = true -geographic-trimming = false +leapyear = False +year-by-year = False +derated = False +custom-scenario = False +user-playlist = False +thematic-trimming = True +geographic-trimming = False generate = nbtimeseriesload = 1 nbtimeserieshydro = 1 @@ -715,42 +715,42 @@ def test_generaldata_ini_with_thematic_trimming_has_positive_sign(self, tmp_path refreshintervalthermal = 100 refreshintervalwind = 100 refreshintervalsolar = 100 -readonly = false +readonly = False [input] import = [output] -synthesis = true -storenewset = false +synthesis = True +storenewset = False archives = result-format = txt-files [optimization] simplex-range = week transmission-capacities = local-values -include-constraints = true -include-hurdlecosts = true -include-tc-minstablepower = true -include-tc-min-ud-time = true -include-dayahead = true -include-strategicreserve = true -include-spinningreserve = true -include-primaryreserve = true +include-constraints = True +include-hurdlecosts = True +include-tc-minstablepower = True +include-tc-min-ud-time = True +include-dayahead = True +include-strategicreserve = True +include-spinningreserve = True +include-primaryreserve = True include-exportmps = none -include-exportstructure = false +include-exportstructure = False include-unfeasible-problem-behavior = error-verbose [adequacy patch] -include-adq-patch = false -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = true -set-to-null-ntc-between-physical-out-for-first-step = true -enable-first-step = false +include-adq-patch = False +set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True +set-to-null-ntc-between-physical-out-for-first-step = True +enable-first-step = False price-taking-order = DENS -include-hurdle-cost-csr = false -check-csr-cost-function = false -threshold-initiate-curtailment-sharing-rule = 0.000000 -threshold-display-local-matching-rule-violations = 0.000000 +include-hurdle-cost-csr = False +check-csr-cost-function = False +threshold-initiate-curtailment-sharing-rule = 0 +threshold-display-local-matching-rule-violations = 0 threshold-csr-variable-bounds-relaxation = 3 [other preferences] @@ -780,7 +780,7 @@ def test_generaldata_ini_with_thematic_trimming_has_positive_sign(self, tmp_path seed-initial-reservoir-levels = 10005489 [variables selection] -selected_vars_reset = false +selected_vars_reset = False select_var + = OP. COST """ @@ -821,13 +821,13 @@ def test_generaldata_ini_with_thematic_trimming_two_variables(self, tmp_path): january.1st = Monday first-month-in-year = January first.weekday = Monday -leapyear = false -year-by-year = false -derated = false -custom-scenario = false -user-playlist = false -thematic-trimming = true -geographic-trimming = false +leapyear = False +year-by-year = False +derated = False +custom-scenario = False +user-playlist = False +thematic-trimming = True +geographic-trimming = False generate = nbtimeseriesload = 1 nbtimeserieshydro = 1 @@ -842,42 +842,42 @@ def test_generaldata_ini_with_thematic_trimming_two_variables(self, tmp_path): refreshintervalthermal = 100 refreshintervalwind = 100 refreshintervalsolar = 100 -readonly = false +readonly = False [input] import = [output] -synthesis = true -storenewset = false +synthesis = True +storenewset = False archives = result-format = txt-files [optimization] simplex-range = week transmission-capacities = local-values -include-constraints = true -include-hurdlecosts = true -include-tc-minstablepower = true -include-tc-min-ud-time = true -include-dayahead = true -include-strategicreserve = true -include-spinningreserve = true -include-primaryreserve = true +include-constraints = True +include-hurdlecosts = True +include-tc-minstablepower = True +include-tc-min-ud-time = True +include-dayahead = True +include-strategicreserve = True +include-spinningreserve = True +include-primaryreserve = True include-exportmps = none -include-exportstructure = false +include-exportstructure = False include-unfeasible-problem-behavior = error-verbose [adequacy patch] -include-adq-patch = false -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = true -set-to-null-ntc-between-physical-out-for-first-step = true -enable-first-step = false +include-adq-patch = False +set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True +set-to-null-ntc-between-physical-out-for-first-step = True +enable-first-step = False price-taking-order = DENS -include-hurdle-cost-csr = false -check-csr-cost-function = false -threshold-initiate-curtailment-sharing-rule = 0.000000 -threshold-display-local-matching-rule-violations = 0.000000 +include-hurdle-cost-csr = False +check-csr-cost-function = False +threshold-initiate-curtailment-sharing-rule = 0 +threshold-display-local-matching-rule-violations = 0 threshold-csr-variable-bounds-relaxation = 3 [other preferences] @@ -907,7 +907,7 @@ def test_generaldata_ini_with_thematic_trimming_two_variables(self, tmp_path): seed-initial-reservoir-levels = 10005489 [variables selection] -selected_vars_reset = false +selected_vars_reset = False """ expected_file_content += "select_var + = OV. COST\nselect_var + = OP. COST\n\n" @@ -948,13 +948,13 @@ def test_generaldata_ini_with_playlist_has_negative_sign(self, tmp_path): january.1st = Monday first-month-in-year = January first.weekday = Monday -leapyear = false -year-by-year = false -derated = false -custom-scenario = false -user-playlist = true -thematic-trimming = true -geographic-trimming = false +leapyear = False +year-by-year = False +derated = False +custom-scenario = False +user-playlist = True +thematic-trimming = True +geographic-trimming = False generate = nbtimeseriesload = 1 nbtimeserieshydro = 1 @@ -969,42 +969,42 @@ def test_generaldata_ini_with_playlist_has_negative_sign(self, tmp_path): refreshintervalthermal = 100 refreshintervalwind = 100 refreshintervalsolar = 100 -readonly = false +readonly = False [input] import = [output] -synthesis = true -storenewset = false +synthesis = True +storenewset = False archives = result-format = txt-files [optimization] simplex-range = week transmission-capacities = local-values -include-constraints = true -include-hurdlecosts = true -include-tc-minstablepower = true -include-tc-min-ud-time = true -include-dayahead = true -include-strategicreserve = true -include-spinningreserve = true -include-primaryreserve = true +include-constraints = True +include-hurdlecosts = True +include-tc-minstablepower = True +include-tc-min-ud-time = True +include-dayahead = True +include-strategicreserve = True +include-spinningreserve = True +include-primaryreserve = True include-exportmps = none -include-exportstructure = false +include-exportstructure = False include-unfeasible-problem-behavior = error-verbose [adequacy patch] -include-adq-patch = false -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = true -set-to-null-ntc-between-physical-out-for-first-step = true -enable-first-step = false +include-adq-patch = False +set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True +set-to-null-ntc-between-physical-out-for-first-step = True +enable-first-step = False price-taking-order = DENS -include-hurdle-cost-csr = false -check-csr-cost-function = false -threshold-initiate-curtailment-sharing-rule = 0.000000 -threshold-display-local-matching-rule-violations = 0.000000 +include-hurdle-cost-csr = False +check-csr-cost-function = False +threshold-initiate-curtailment-sharing-rule = 0 +threshold-display-local-matching-rule-violations = 0 threshold-csr-variable-bounds-relaxation = 3 [other preferences] @@ -1034,11 +1034,11 @@ def test_generaldata_ini_with_playlist_has_negative_sign(self, tmp_path): seed-initial-reservoir-levels = 10005489 [playlist] -playlist_reset = true +playlist_reset = True playlist_year - = 2 [variables selection] -selected_vars_reset = false +selected_vars_reset = False select_var + = OP. COST """ @@ -1085,13 +1085,13 @@ def test_generaldata_ini_with_playlist_has_positive_sign(self, tmp_path): january.1st = Monday first-month-in-year = January first.weekday = Monday -leapyear = false -year-by-year = false -derated = false -custom-scenario = false -user-playlist = true -thematic-trimming = true -geographic-trimming = false +leapyear = False +year-by-year = False +derated = False +custom-scenario = False +user-playlist = True +thematic-trimming = True +geographic-trimming = False generate = nbtimeseriesload = 1 nbtimeserieshydro = 1 @@ -1106,42 +1106,42 @@ def test_generaldata_ini_with_playlist_has_positive_sign(self, tmp_path): refreshintervalthermal = 100 refreshintervalwind = 100 refreshintervalsolar = 100 -readonly = false +readonly = False [input] import = [output] -synthesis = true -storenewset = false +synthesis = True +storenewset = False archives = result-format = txt-files [optimization] simplex-range = week transmission-capacities = local-values -include-constraints = true -include-hurdlecosts = true -include-tc-minstablepower = true -include-tc-min-ud-time = true -include-dayahead = true -include-strategicreserve = true -include-spinningreserve = true -include-primaryreserve = true +include-constraints = True +include-hurdlecosts = True +include-tc-minstablepower = True +include-tc-min-ud-time = True +include-dayahead = True +include-strategicreserve = True +include-spinningreserve = True +include-primaryreserve = True include-exportmps = none -include-exportstructure = false +include-exportstructure = False include-unfeasible-problem-behavior = error-verbose [adequacy patch] -include-adq-patch = false -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = true -set-to-null-ntc-between-physical-out-for-first-step = true -enable-first-step = false +include-adq-patch = False +set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True +set-to-null-ntc-between-physical-out-for-first-step = True +enable-first-step = False price-taking-order = DENS -include-hurdle-cost-csr = false -check-csr-cost-function = false -threshold-initiate-curtailment-sharing-rule = 0.000000 -threshold-display-local-matching-rule-violations = 0.000000 +include-hurdle-cost-csr = False +check-csr-cost-function = False +threshold-initiate-curtailment-sharing-rule = 0 +threshold-display-local-matching-rule-violations = 0 threshold-csr-variable-bounds-relaxation = 3 [other preferences] @@ -1171,11 +1171,11 @@ def test_generaldata_ini_with_playlist_has_positive_sign(self, tmp_path): seed-initial-reservoir-levels = 10005489 [playlist] -playlist_reset = false +playlist_reset = False playlist_year + = 2 [variables selection] -selected_vars_reset = false +selected_vars_reset = False select_var + = OP. COST """ @@ -1225,7 +1225,7 @@ def test_areas_sets_ini_content(self, tmp_path, local_study): expected_sets_ini_content = """[all areas] caption = All areas comments = Spatial aggregates on all areas -output = false +output = False apply-filter = add-all """ @@ -1289,11 +1289,11 @@ def test_area_optimization_ini_content(self, tmp_path, local_study): expected_optimization_ini_path = study_antares_path / "input" / "areas" / "area1" / "optimization.ini" expected_optimization_ini_content = """[nodal optimization] -non-dispatchable-power = true -dispatchable-hydro-power = true -other-dispatchable-power = true -spread-unsupplied-energy-cost = 0.000000 -spread-spilled-energy-cost = 0.000000 +non-dispatchable-power = True +dispatchable-hydro-power = True +other-dispatchable-power = True +spread-unsupplied-energy-cost = 0 +spread-spilled-energy-cost = 0 [filtering] filter-synthesis = hourly, daily, weekly, monthly, annual @@ -1332,11 +1332,11 @@ def test_custom_area_optimization_ini_content(self, tmp_path, local_study): tmp_path / local_study.name / "input/areas" / area_to_create / "optimization.ini" ) expected_optimization_ini_content = """[nodal optimization] -non-dispatchable-power = true -dispatchable-hydro-power = false -other-dispatchable-power = true -spread-unsupplied-energy-cost = 0.000000 -spread-spilled-energy-cost = 0.000000 +non-dispatchable-power = True +dispatchable-hydro-power = False +other-dispatchable-power = True +spread-unsupplied-energy-cost = 0 +spread-spilled-energy-cost = 0 [filtering] filter-synthesis = hourly, daily, weekly, monthly, annual @@ -1465,11 +1465,11 @@ def test_areas_have_default_properties(self, tmp_path, local_study_w_areas): # Given expected_default_properties = { "nodal optimization": { - "non-dispatchable-power": "true", - "dispatchable-hydro-power": "true", - "other-dispatchable-power": "true", - "spread-unsupplied-energy-cost": "0.000000", - "spread-spilled-energy-cost": "0.000000", + "non-dispatchable-power": "True", + "dispatchable-hydro-power": "True", + "other-dispatchable-power": "True", + "spread-unsupplied-energy-cost": "0", + "spread-spilled-energy-cost": "0", }, "filtering": { "filter-synthesis": "hourly, daily, weekly, monthly, annual", @@ -1495,11 +1495,11 @@ def test_areas_with_custom_properties(self, tmp_path, local_study): ) expected_properties = { "nodal optimization": { - "non-dispatchable-power": "true", - "dispatchable-hydro-power": "false", - "other-dispatchable-power": "true", + "non-dispatchable-power": "True", + "dispatchable-hydro-power": "False", + "other-dispatchable-power": "True", "spread-unsupplied-energy-cost": "1.000000", - "spread-spilled-energy-cost": "0.000000", + "spread-spilled-energy-cost": "0", }, "filtering": { "filter-synthesis": "hourly, daily, weekly, monthly, annual", @@ -1621,9 +1621,9 @@ def test_create_link_sets_ini_content(self, tmp_path, local_study_w_areas): # Given link_to_create = "fr_it" expected_content = """[it] -hurdles-cost = false -loop-flow = false -use-phase-shifter = false +hurdles-cost = False +loop-flow = False +use-phase-shifter = False transmission-capacities = enabled asset-type = ac link-style = plain @@ -1631,7 +1631,7 @@ def test_create_link_sets_ini_content(self, tmp_path, local_study_w_areas): colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1656,9 +1656,9 @@ def test_created_link_has_default_local_properties(self, tmp_path, local_study_w # Given link_to_create = "fr_it" expected_ini_content = """[it] -hurdles-cost = false -loop-flow = false -use-phase-shifter = false +hurdles-cost = False +loop-flow = False +use-phase-shifter = False transmission-capacities = enabled asset-type = ac link-style = plain @@ -1666,7 +1666,7 @@ def test_created_link_has_default_local_properties(self, tmp_path, local_study_w colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1705,9 +1705,9 @@ def test_created_link_has_custom_properties(self, tmp_path, local_study_w_areas) filter_year_by_year={FilterOption.WEEKLY, FilterOption.DAILY}, ) expected_ini_content = """[it] -hurdles-cost = false -loop-flow = true -use-phase-shifter = true +hurdles-cost = False +loop-flow = True +use-phase-shifter = True transmission-capacities = infinite asset-type = ac link-style = plain @@ -1715,7 +1715,7 @@ def test_created_link_has_custom_properties(self, tmp_path, local_study_w_areas) colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = daily, weekly comments = @@ -1745,9 +1745,9 @@ def test_multiple_links_created_from_same_area(self, tmp_path, local_study_w_are local_study_w_areas.create_area("at") links_to_create = ["fr_at", "at_it"] expected_ini_string = """[fr] -hurdles-cost = false -loop-flow = false -use-phase-shifter = false +hurdles-cost = False +loop-flow = False +use-phase-shifter = False transmission-capacities = enabled asset-type = ac link-style = plain @@ -1755,15 +1755,15 @@ def test_multiple_links_created_from_same_area(self, tmp_path, local_study_w_are colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = [it] -hurdles-cost = false -loop-flow = false -use-phase-shifter = false +hurdles-cost = False +loop-flow = False +use-phase-shifter = False transmission-capacities = enabled asset-type = ac link-style = plain @@ -1771,7 +1771,7 @@ def test_multiple_links_created_from_same_area(self, tmp_path, local_study_w_are colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1804,9 +1804,9 @@ def test_multiple_links_created_from_same_area_are_alphabetical(self, tmp_path, local_study_w_areas.create_area("at") links_to_create = ["at_it", "fr_at"] expected_ini_string = """[fr] -hurdles-cost = false -loop-flow = false -use-phase-shifter = false +hurdles-cost = False +loop-flow = False +use-phase-shifter = False transmission-capacities = enabled asset-type = ac link-style = plain @@ -1814,15 +1814,15 @@ def test_multiple_links_created_from_same_area_are_alphabetical(self, tmp_path, colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = [it] -hurdles-cost = false -loop-flow = false -use-phase-shifter = false +hurdles-cost = False +loop-flow = False +use-phase-shifter = False transmission-capacities = enabled asset-type = ac link-style = plain @@ -1830,7 +1830,7 @@ def test_multiple_links_created_from_same_area_are_alphabetical(self, tmp_path, colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1878,9 +1878,9 @@ def test_created_link_has_default_ui_values(self, tmp_path, local_study_w_areas) actual_ini_file = tmp_path / local_study_w_areas.name / "input" / "links" / "fr" / "properties.ini" actual_ini = ConfigParser() expected_ini_string = """[it] -hurdles-cost = false -loop-flow = false -use-phase-shifter = false +hurdles-cost = False +loop-flow = False +use-phase-shifter = False transmission-capacities = enabled asset-type = ac link-style = plain @@ -1888,7 +1888,7 @@ def test_created_link_has_default_ui_values(self, tmp_path, local_study_w_areas) colorr = 112 colorg = 112 colorb = 112 -display-comments = true +display-comments = True filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1916,9 +1916,9 @@ def test_created_link_with_custom_ui_values(self, tmp_path, local_study_w_areas) actual_ini_file = tmp_path / local_study_w_areas.name / "input" / "links" / "fr" / "properties.ini" actual_ini = ConfigParser() expected_ini_string = """[it] -hurdles-cost = true -loop-flow = false -use-phase-shifter = false +hurdles-cost = True +loop-flow = False +use-phase-shifter = False transmission-capacities = ignore asset-type = gaz link-style = dot @@ -1926,7 +1926,7 @@ def test_created_link_with_custom_ui_values(self, tmp_path, local_study_w_areas) colorr = 234 colorg = 123 colorb = 0 -display-comments = true +display-comments = True filter-synthesis = hourly, weekly, monthly filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -2014,7 +2014,7 @@ def test_constraints_ini_have_correct_default_content( expected_ini_contents = """[0] name = test constraint id = test constraint -enabled = true +enabled = True type = hourly operator = less filter-year-by-year = hourly @@ -2049,7 +2049,7 @@ def test_constraints_and_ini_have_custom_properties(self, local_study_with_const expected_ini_content = """[0] name = test constraint id = test constraint -enabled = true +enabled = True type = hourly operator = less filter-year-by-year = hourly @@ -2059,7 +2059,7 @@ def test_constraints_and_ini_have_custom_properties(self, local_study_with_const [1] name = test constraint two id = test constraint two -enabled = false +enabled = False type = weekly operator = both comments = test comment @@ -2093,7 +2093,7 @@ def test_constraint_term_and_ini_have_correct_defaults(self, local_study_with_co expected_ini_contents = """[0] name = test constraint id = test constraint -enabled = true +enabled = True type = hourly operator = less filter-year-by-year = hourly @@ -2117,13 +2117,13 @@ def test_constraint_term_with_offset_and_ini_have_correct_values( expected_ini_contents = """[0] name = test constraint id = test constraint -enabled = true +enabled = True type = hourly operator = less filter-year-by-year = hourly filter-synthesis = hourly group = default -at%fr = 0.000000%1 +at%fr = 0%1 """ # When From 2d1c7c5aa78e7e5ea521b2655b31013e7a391d1d Mon Sep 17 00:00:00 2001 From: belthlemar Date: Tue, 4 Feb 2025 10:00:33 +0100 Subject: [PATCH 63/69] put back old values --- .../services/local_services/test_study.py | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index e7efa2d6..62c2d5e3 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -196,14 +196,14 @@ def test_local_study_has_settings(self, local_study): def test_local_study_has_correct_default_general_properties(self, local_study): expected_general_properties = GeneralParameters(**{ - "mode": "economy", + "mode": "Economy", "horizon": "", "nb_years": 1, "simulation_start": 1, "simulation_end": 365, - "january_first": "monday", - "first_month_in_year": "january", - "first_week_day": "monday", + "january_first": "Monday", + "first_month_in_year": "January", + "first_week_day": "Monday", "leap_year": False, "year_by_year": False, "building_mode": "automatic", @@ -272,7 +272,7 @@ def test_local_study_has_correct_optimization_parameters(self, local_study): "simplex_range": "week", "transmission_capacities": "local-values", "include_constraints": True, - "include_hurdle_costs": True, + "include_hurdlecosts": True, "include_tc_minstablepower": True, "include_tc_min_ud_time": True, "include_dayahead": True, @@ -1225,7 +1225,7 @@ def test_areas_sets_ini_content(self, tmp_path, local_study): expected_sets_ini_content = """[all areas] caption = All areas comments = Spatial aggregates on all areas -output = False +output = false apply-filter = add-all """ @@ -1289,9 +1289,9 @@ def test_area_optimization_ini_content(self, tmp_path, local_study): expected_optimization_ini_path = study_antares_path / "input" / "areas" / "area1" / "optimization.ini" expected_optimization_ini_content = """[nodal optimization] -non-dispatchable-power = True -dispatchable-hydro-power = True -other-dispatchable-power = True +non-dispatchable-power = true +dispatchable-hydro-power = true +other-dispatchable-power = true spread-unsupplied-energy-cost = 0 spread-spilled-energy-cost = 0 @@ -1332,9 +1332,9 @@ def test_custom_area_optimization_ini_content(self, tmp_path, local_study): tmp_path / local_study.name / "input/areas" / area_to_create / "optimization.ini" ) expected_optimization_ini_content = """[nodal optimization] -non-dispatchable-power = True -dispatchable-hydro-power = False -other-dispatchable-power = True +non-dispatchable-power = true +dispatchable-hydro-power = false +other-dispatchable-power = true spread-unsupplied-energy-cost = 0 spread-spilled-energy-cost = 0 @@ -1465,11 +1465,11 @@ def test_areas_have_default_properties(self, tmp_path, local_study_w_areas): # Given expected_default_properties = { "nodal optimization": { - "non-dispatchable-power": "True", - "dispatchable-hydro-power": "True", - "other-dispatchable-power": "True", - "spread-unsupplied-energy-cost": "0", - "spread-spilled-energy-cost": "0", + "non-dispatchable-power": "true", + "dispatchable-hydro-power": "true", + "other-dispatchable-power": "true", + "spread-unsupplied-energy-cost": "0.000000", + "spread-spilled-energy-cost": "0.000000", }, "filtering": { "filter-synthesis": "hourly, daily, weekly, monthly, annual", @@ -1495,11 +1495,11 @@ def test_areas_with_custom_properties(self, tmp_path, local_study): ) expected_properties = { "nodal optimization": { - "non-dispatchable-power": "True", - "dispatchable-hydro-power": "False", - "other-dispatchable-power": "True", + "non-dispatchable-power": "true", + "dispatchable-hydro-power": "false", + "other-dispatchable-power": "true", "spread-unsupplied-energy-cost": "1.000000", - "spread-spilled-energy-cost": "0", + "spread-spilled-energy-cost": "0.000000", }, "filtering": { "filter-synthesis": "hourly, daily, weekly, monthly, annual", @@ -1621,9 +1621,9 @@ def test_create_link_sets_ini_content(self, tmp_path, local_study_w_areas): # Given link_to_create = "fr_it" expected_content = """[it] -hurdles-cost = False -loop-flow = False -use-phase-shifter = False +hurdles-cost = false +loop-flow = false +use-phase-shifter = false transmission-capacities = enabled asset-type = ac link-style = plain @@ -1631,7 +1631,7 @@ def test_create_link_sets_ini_content(self, tmp_path, local_study_w_areas): colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1656,9 +1656,9 @@ def test_created_link_has_default_local_properties(self, tmp_path, local_study_w # Given link_to_create = "fr_it" expected_ini_content = """[it] -hurdles-cost = False -loop-flow = False -use-phase-shifter = False +hurdles-cost = false +loop-flow = false +use-phase-shifter = false transmission-capacities = enabled asset-type = ac link-style = plain @@ -1666,7 +1666,7 @@ def test_created_link_has_default_local_properties(self, tmp_path, local_study_w colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1705,9 +1705,9 @@ def test_created_link_has_custom_properties(self, tmp_path, local_study_w_areas) filter_year_by_year={FilterOption.WEEKLY, FilterOption.DAILY}, ) expected_ini_content = """[it] -hurdles-cost = False -loop-flow = True -use-phase-shifter = True +hurdles-cost = false +loop-flow = true +use-phase-shifter = true transmission-capacities = infinite asset-type = ac link-style = plain @@ -1715,7 +1715,7 @@ def test_created_link_has_custom_properties(self, tmp_path, local_study_w_areas) colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = daily, weekly comments = @@ -1745,9 +1745,9 @@ def test_multiple_links_created_from_same_area(self, tmp_path, local_study_w_are local_study_w_areas.create_area("at") links_to_create = ["fr_at", "at_it"] expected_ini_string = """[fr] -hurdles-cost = False -loop-flow = False -use-phase-shifter = False +hurdles-cost = false +loop-flow = false +use-phase-shifter = false transmission-capacities = enabled asset-type = ac link-style = plain @@ -1755,15 +1755,15 @@ def test_multiple_links_created_from_same_area(self, tmp_path, local_study_w_are colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = [it] -hurdles-cost = False -loop-flow = False -use-phase-shifter = False +hurdles-cost = false +loop-flow = false +use-phase-shifter = false transmission-capacities = enabled asset-type = ac link-style = plain @@ -1771,7 +1771,7 @@ def test_multiple_links_created_from_same_area(self, tmp_path, local_study_w_are colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1804,9 +1804,9 @@ def test_multiple_links_created_from_same_area_are_alphabetical(self, tmp_path, local_study_w_areas.create_area("at") links_to_create = ["at_it", "fr_at"] expected_ini_string = """[fr] -hurdles-cost = False -loop-flow = False -use-phase-shifter = False +hurdles-cost = false +loop-flow = false +use-phase-shifter = false transmission-capacities = enabled asset-type = ac link-style = plain @@ -1814,15 +1814,15 @@ def test_multiple_links_created_from_same_area_are_alphabetical(self, tmp_path, colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = [it] -hurdles-cost = False -loop-flow = False -use-phase-shifter = False +hurdles-cost = false +loop-flow = false +use-phase-shifter = false transmission-capacities = enabled asset-type = ac link-style = plain @@ -1830,7 +1830,7 @@ def test_multiple_links_created_from_same_area_are_alphabetical(self, tmp_path, colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1878,9 +1878,9 @@ def test_created_link_has_default_ui_values(self, tmp_path, local_study_w_areas) actual_ini_file = tmp_path / local_study_w_areas.name / "input" / "links" / "fr" / "properties.ini" actual_ini = ConfigParser() expected_ini_string = """[it] -hurdles-cost = False -loop-flow = False -use-phase-shifter = False +hurdles-cost = false +loop-flow = false +use-phase-shifter = false transmission-capacities = enabled asset-type = ac link-style = plain @@ -1888,7 +1888,7 @@ def test_created_link_has_default_ui_values(self, tmp_path, local_study_w_areas) colorr = 112 colorg = 112 colorb = 112 -display-comments = True +display-comments = true filter-synthesis = hourly, daily, weekly, monthly, annual filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -1916,9 +1916,9 @@ def test_created_link_with_custom_ui_values(self, tmp_path, local_study_w_areas) actual_ini_file = tmp_path / local_study_w_areas.name / "input" / "links" / "fr" / "properties.ini" actual_ini = ConfigParser() expected_ini_string = """[it] -hurdles-cost = True -loop-flow = False -use-phase-shifter = False +hurdles-cost = true +loop-flow = false +use-phase-shifter = false transmission-capacities = ignore asset-type = gaz link-style = dot @@ -1926,7 +1926,7 @@ def test_created_link_with_custom_ui_values(self, tmp_path, local_study_w_areas) colorr = 234 colorg = 123 colorb = 0 -display-comments = True +display-comments = true filter-synthesis = hourly, weekly, monthly filter-year-by-year = hourly, daily, weekly, monthly, annual comments = @@ -2014,7 +2014,7 @@ def test_constraints_ini_have_correct_default_content( expected_ini_contents = """[0] name = test constraint id = test constraint -enabled = True +enabled = true type = hourly operator = less filter-year-by-year = hourly @@ -2049,7 +2049,7 @@ def test_constraints_and_ini_have_custom_properties(self, local_study_with_const expected_ini_content = """[0] name = test constraint id = test constraint -enabled = True +enabled = true type = hourly operator = less filter-year-by-year = hourly @@ -2059,7 +2059,7 @@ def test_constraints_and_ini_have_custom_properties(self, local_study_with_const [1] name = test constraint two id = test constraint two -enabled = False +enabled = false type = weekly operator = both comments = test comment @@ -2093,7 +2093,7 @@ def test_constraint_term_and_ini_have_correct_defaults(self, local_study_with_co expected_ini_contents = """[0] name = test constraint id = test constraint -enabled = True +enabled = true type = hourly operator = less filter-year-by-year = hourly @@ -2117,13 +2117,13 @@ def test_constraint_term_with_offset_and_ini_have_correct_values( expected_ini_contents = """[0] name = test constraint id = test constraint -enabled = True +enabled = true type = hourly operator = less filter-year-by-year = hourly filter-synthesis = hourly group = default -at%fr = 0%1 +at%fr = 0.000000%1 """ # When From 1d3056ad92471c8d84281093092295e61e6debd5 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Tue, 4 Feb 2025 10:01:39 +0100 Subject: [PATCH 64/69] put back old values --- tests/antares/services/local_services/test_study.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index 62c2d5e3..515619f7 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -1292,8 +1292,8 @@ def test_area_optimization_ini_content(self, tmp_path, local_study): non-dispatchable-power = true dispatchable-hydro-power = true other-dispatchable-power = true -spread-unsupplied-energy-cost = 0 -spread-spilled-energy-cost = 0 +spread-unsupplied-energy-cost = 0.000000 +spread-spilled-energy-cost = 0.000000 [filtering] filter-synthesis = hourly, daily, weekly, monthly, annual @@ -1335,8 +1335,8 @@ def test_custom_area_optimization_ini_content(self, tmp_path, local_study): non-dispatchable-power = true dispatchable-hydro-power = false other-dispatchable-power = true -spread-unsupplied-energy-cost = 0 -spread-spilled-energy-cost = 0 +spread-unsupplied-energy-cost = 0.000000 +spread-spilled-energy-cost = 0.000000 [filtering] filter-synthesis = hourly, daily, weekly, monthly, annual From 820428ec35204bc0229b2cc03d6e8815e3c4c730 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Tue, 4 Feb 2025 13:21:37 +0100 Subject: [PATCH 65/69] remove temporarily playlist and thematic trimming tests --- .../services/local_services/test_study.py | 795 +----------------- 1 file changed, 1 insertion(+), 794 deletions(-) diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index 515619f7..12db7318 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -287,158 +287,10 @@ def test_local_study_has_correct_optimization_parameters(self, local_study): assert local_study.get_settings().optimization_parameters == expected_optimization_parameters - def test_local_study_has_correct_playlist_parameters(self, local_study): + def test_local_study_has_correct_playlist_and_thematic_parameters(self, local_study): assert local_study.get_settings().playlist_parameters is None assert local_study.get_settings().thematic_trimming_parameters is None - def test_local_study_with_playlist_has_correct_defaults(self, tmp_path): - # Given - nb_years = 2 - playlist_study = create_study_local( - "test_study", - "880", - str(tmp_path.absolute()), - StudySettingsLocal( - general_parameters=GeneralParametersLocal(nb_years=nb_years, selection_mode=True), - playlist_parameters=PlaylistParameters(playlist=[PlaylistData()] * nb_years), - ), - ) - - # When - expected_playlist_parameters_dict = {1: {"status": True, "weight": 1.0}, 2: {"status": True, "weight": 1.0}} - expected_playlist_parameters = PlaylistParameters(playlist=expected_playlist_parameters_dict) - - actual_playlist_parameters_dict = playlist_study.get_settings().playlist_parameters.model_dump() - actual_playlist_parameters = playlist_study.get_settings().playlist_parameters - - # Then - assert actual_playlist_parameters_dict == expected_playlist_parameters_dict - assert actual_playlist_parameters == expected_playlist_parameters - - def test_local_study_has_correct_thematic_trimming_parameters(self, tmp_path): - # Given - expected_thematic_trimming_parameters = ThematicTrimmingParametersLocal.model_validate( - { - "ov_cost": True, - "op_cost": True, - "mrg_price": True, - "co2_emis": True, - "dtg_by_plant": True, - "balance": True, - "row_bal": True, - "psp": True, - "misc_ndg": True, - "load": True, - "h_ror": True, - "wind": True, - "solar": True, - "nuclear": True, - "lignite": True, - "coal": True, - "gas": True, - "oil": True, - "mix_fuel": True, - "misc_dtg": True, - "h_stor": True, - "h_pump": True, - "h_lev": True, - "h_infl": True, - "h_ovfl": True, - "h_val": True, - "h_cost": True, - "unsp_enrg": True, - "spil_enrg": True, - "lold": True, - "lolp": True, - "avl_dtg": True, - "dtg_mrg": True, - "max_mrg": True, - "np_cost": True, - "np_cost_by_plant": True, - "nodu": True, - "nodu_by_plant": True, - "flow_lin": True, - "ucap_lin": True, - "loop_flow": True, - "flow_quad": True, - "cong_fee_alg": True, - "cong_fee_abs": True, - "marg_cost": True, - "cong_prob_plus": True, - "cong_prob_minus": True, - "hurdle_cost": True, - "res_generation_by_plant": True, - "misc_dtg_2": True, - "misc_dtg_3": True, - "misc_dtg_4": True, - "wind_offshore": True, - "wind_onshore": True, - "solar_concrt": True, - "solar_pv": True, - "solar_rooft": True, - "renw_1": True, - "renw_2": True, - "renw_3": True, - "renw_4": True, - "dens": True, - "profit_by_plant": True, - "sts_inj_by_plant": True, - "sts_withdrawal_by_plant": True, - "sts_lvl_by_plant": True, - "psp_open_injection": True, - "psp_open_withdrawal": True, - "psp_open_level": True, - "psp_closed_injection": True, - "psp_closed_withdrawal": True, - "psp_closed_level": True, - "pondage_injection": True, - "pondage_withdrawal": True, - "pondage_level": True, - "battery_injection": True, - "battery_withdrawal": True, - "battery_level": True, - "other1_injection": True, - "other1_withdrawal": True, - "other1_level": True, - "other2_injection": True, - "other2_withdrawal": True, - "other2_level": True, - "other3_injection": True, - "other3_withdrawal": True, - "other3_level": True, - "other4_injection": True, - "other4_withdrawal": True, - "other4_level": True, - "other5_injection": True, - "other5_withdrawal": True, - "other5_level": True, - "sts_cashflow_by_cluster": True, - } - ) - expected_study_settings = StudySettingsLocal( - general_parameters=GeneralParametersLocal(thematic_trimming=True), - thematic_trimming_parameters=expected_thematic_trimming_parameters, - ) - thematic_trimming_study = create_study_local( - "test_study", - "880", - str(tmp_path.absolute()), - StudySettingsLocal( - general_parameters=GeneralParametersLocal(thematic_trimming=True), - thematic_trimming_parameters=ThematicTrimmingParametersLocal(), - ), - ) - - # When - actual_thematic_trimming_parameters = DefaultThematicTrimmingParameters.model_validate( - thematic_trimming_study.get_settings().thematic_trimming_parameters - ) - actual_study_settings = DefaultStudySettings.model_validate(thematic_trimming_study.get_settings()) - - # Then - assert actual_thematic_trimming_parameters == expected_thematic_trimming_parameters - assert actual_study_settings == expected_study_settings - def test_generaldata_ini_exists(self, local_study): # Given expected_file = local_study.service.config.study_path / "settings/generaldata.ini" @@ -553,651 +405,6 @@ def test_generaldata_ini_has_correct_default_values(self, local_study): # Then assert actual_file_content == expected_file_content - def test_generaldata_ini_with_thematic_trimming_has_negative_sign(self, tmp_path): - # Given - study_name = "test study" - study_version = "880" - general_parameters = GeneralParametersLocal(thematic_trimming=True) - thematic_trimming_parameters = ThematicTrimmingParametersLocal(op_cost=False) - study_path = str(tmp_path.absolute()) - expected_file_content = """[general] -mode = Economy -horizon = -nbyears = 1 -simulation.start = 1 -simulation.end = 365 -january.1st = Monday -first-month-in-year = January -first.weekday = Monday -leapyear = False -year-by-year = False -derated = False -custom-scenario = False -user-playlist = False -thematic-trimming = True -geographic-trimming = False -generate = -nbtimeseriesload = 1 -nbtimeserieshydro = 1 -nbtimeseriesthermal = 1 -nbtimeserieswind = 1 -nbtimeseriessolar = 1 -refreshtimeseries = -intra-modal = -inter-modal = -refreshintervalload = 100 -refreshintervalhydro = 100 -refreshintervalthermal = 100 -refreshintervalwind = 100 -refreshintervalsolar = 100 -readonly = False - -[input] -import = - -[output] -synthesis = True -storenewset = False -archives = -result-format = txt-files - -[optimization] -simplex-range = week -transmission-capacities = local-values -include-constraints = True -include-hurdlecosts = True -include-tc-minstablepower = True -include-tc-min-ud-time = True -include-dayahead = True -include-strategicreserve = True -include-spinningreserve = True -include-primaryreserve = True -include-exportmps = none -include-exportstructure = False -include-unfeasible-problem-behavior = error-verbose - -[adequacy patch] -include-adq-patch = False -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True -set-to-null-ntc-between-physical-out-for-first-step = True -enable-first-step = False -price-taking-order = DENS -include-hurdle-cost-csr = False -check-csr-cost-function = False -threshold-initiate-curtailment-sharing-rule = 0 -threshold-display-local-matching-rule-violations = 0 -threshold-csr-variable-bounds-relaxation = 3 - -[other preferences] -initial-reservoir-levels = cold start -hydro-heuristic-policy = accommodate rule curves -hydro-pricing-mode = fast -power-fluctuations = free modulations -shedding-policy = shave peaks -unit-commitment-mode = fast -number-of-cores-mode = medium -renewable-generation-modelling = aggregated - -[advanced parameters] -accuracy-on-correlation = - -[seeds - Mersenne Twister] -seed-tsgen-wind = 5489 -seed-tsgen-load = 1005489 -seed-tsgen-hydro = 2005489 -seed-tsgen-thermal = 3005489 -seed-tsgen-solar = 4005489 -seed-tsnumbers = 5005489 -seed-unsupplied-energy-costs = 6005489 -seed-spilled-energy-costs = 7005489 -seed-thermal-costs = 8005489 -seed-hydro-costs = 9005489 -seed-initial-reservoir-levels = 10005489 - -[variables selection] -selected_vars_reset = True -select_var - = OP. COST - -""" - - # When - new_study = create_study_local( - study_name, - study_version, - study_path, - StudySettingsLocal( - general_parameters=general_parameters, thematic_trimming_parameters=thematic_trimming_parameters - ), - ) - actual_generaldata_ini_file = new_study.service.config.study_path / InitializationFilesTypes.GENERAL.value - actual_file_content = actual_generaldata_ini_file.read_text() - - # Then - assert actual_file_content == expected_file_content - - def test_generaldata_ini_with_thematic_trimming_has_positive_sign(self, tmp_path): - # Given - study_name = "test study" - study_version = "880" - general_parameters = GeneralParametersLocal(thematic_trimming=True) - thematic_trimming_parameters = ThematicTrimmingParametersLocal(op_cost=False) - thematic_trimming_parameters = { - key: not value for key, value in thematic_trimming_parameters.model_dump().items() - } - study_path = str(tmp_path.absolute()) - expected_file_content = """[general] -mode = Economy -horizon = -nbyears = 1 -simulation.start = 1 -simulation.end = 365 -january.1st = Monday -first-month-in-year = January -first.weekday = Monday -leapyear = False -year-by-year = False -derated = False -custom-scenario = False -user-playlist = False -thematic-trimming = True -geographic-trimming = False -generate = -nbtimeseriesload = 1 -nbtimeserieshydro = 1 -nbtimeseriesthermal = 1 -nbtimeserieswind = 1 -nbtimeseriessolar = 1 -refreshtimeseries = -intra-modal = -inter-modal = -refreshintervalload = 100 -refreshintervalhydro = 100 -refreshintervalthermal = 100 -refreshintervalwind = 100 -refreshintervalsolar = 100 -readonly = False - -[input] -import = - -[output] -synthesis = True -storenewset = False -archives = -result-format = txt-files - -[optimization] -simplex-range = week -transmission-capacities = local-values -include-constraints = True -include-hurdlecosts = True -include-tc-minstablepower = True -include-tc-min-ud-time = True -include-dayahead = True -include-strategicreserve = True -include-spinningreserve = True -include-primaryreserve = True -include-exportmps = none -include-exportstructure = False -include-unfeasible-problem-behavior = error-verbose - -[adequacy patch] -include-adq-patch = False -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True -set-to-null-ntc-between-physical-out-for-first-step = True -enable-first-step = False -price-taking-order = DENS -include-hurdle-cost-csr = False -check-csr-cost-function = False -threshold-initiate-curtailment-sharing-rule = 0 -threshold-display-local-matching-rule-violations = 0 -threshold-csr-variable-bounds-relaxation = 3 - -[other preferences] -initial-reservoir-levels = cold start -hydro-heuristic-policy = accommodate rule curves -hydro-pricing-mode = fast -power-fluctuations = free modulations -shedding-policy = shave peaks -unit-commitment-mode = fast -number-of-cores-mode = medium -renewable-generation-modelling = aggregated - -[advanced parameters] -accuracy-on-correlation = - -[seeds - Mersenne Twister] -seed-tsgen-wind = 5489 -seed-tsgen-load = 1005489 -seed-tsgen-hydro = 2005489 -seed-tsgen-thermal = 3005489 -seed-tsgen-solar = 4005489 -seed-tsnumbers = 5005489 -seed-unsupplied-energy-costs = 6005489 -seed-spilled-energy-costs = 7005489 -seed-thermal-costs = 8005489 -seed-hydro-costs = 9005489 -seed-initial-reservoir-levels = 10005489 - -[variables selection] -selected_vars_reset = False -select_var + = OP. COST - -""" - - # When - new_study = create_study_local( - study_name, - study_version, - study_path, - StudySettingsLocal( - general_parameters=general_parameters, thematic_trimming_parameters=thematic_trimming_parameters - ), - ) - actual_generaldata_ini_file = new_study.service.config.study_path / InitializationFilesTypes.GENERAL.value - actual_file_content = actual_generaldata_ini_file.read_text() - - # Then - assert actual_file_content == expected_file_content - - def test_generaldata_ini_with_thematic_trimming_two_variables(self, tmp_path): - # Given - study_name = "test study" - study_version = "880" - general_parameters = GeneralParametersLocal(thematic_trimming=True) - thematic_trimming_parameters = ThematicTrimmingParametersLocal(op_cost=False, ov_cost=False) - # Invert selection - thematic_trimming_parameters = { - key: not value for key, value in thematic_trimming_parameters.model_dump().items() - } - - study_path = str(tmp_path.absolute()) - expected_file_content = """[general] -mode = Economy -horizon = -nbyears = 1 -simulation.start = 1 -simulation.end = 365 -january.1st = Monday -first-month-in-year = January -first.weekday = Monday -leapyear = False -year-by-year = False -derated = False -custom-scenario = False -user-playlist = False -thematic-trimming = True -geographic-trimming = False -generate = -nbtimeseriesload = 1 -nbtimeserieshydro = 1 -nbtimeseriesthermal = 1 -nbtimeserieswind = 1 -nbtimeseriessolar = 1 -refreshtimeseries = -intra-modal = -inter-modal = -refreshintervalload = 100 -refreshintervalhydro = 100 -refreshintervalthermal = 100 -refreshintervalwind = 100 -refreshintervalsolar = 100 -readonly = False - -[input] -import = - -[output] -synthesis = True -storenewset = False -archives = -result-format = txt-files - -[optimization] -simplex-range = week -transmission-capacities = local-values -include-constraints = True -include-hurdlecosts = True -include-tc-minstablepower = True -include-tc-min-ud-time = True -include-dayahead = True -include-strategicreserve = True -include-spinningreserve = True -include-primaryreserve = True -include-exportmps = none -include-exportstructure = False -include-unfeasible-problem-behavior = error-verbose - -[adequacy patch] -include-adq-patch = False -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True -set-to-null-ntc-between-physical-out-for-first-step = True -enable-first-step = False -price-taking-order = DENS -include-hurdle-cost-csr = False -check-csr-cost-function = False -threshold-initiate-curtailment-sharing-rule = 0 -threshold-display-local-matching-rule-violations = 0 -threshold-csr-variable-bounds-relaxation = 3 - -[other preferences] -initial-reservoir-levels = cold start -hydro-heuristic-policy = accommodate rule curves -hydro-pricing-mode = fast -power-fluctuations = free modulations -shedding-policy = shave peaks -unit-commitment-mode = fast -number-of-cores-mode = medium -renewable-generation-modelling = aggregated - -[advanced parameters] -accuracy-on-correlation = - -[seeds - Mersenne Twister] -seed-tsgen-wind = 5489 -seed-tsgen-load = 1005489 -seed-tsgen-hydro = 2005489 -seed-tsgen-thermal = 3005489 -seed-tsgen-solar = 4005489 -seed-tsnumbers = 5005489 -seed-unsupplied-energy-costs = 6005489 -seed-spilled-energy-costs = 7005489 -seed-thermal-costs = 8005489 -seed-hydro-costs = 9005489 -seed-initial-reservoir-levels = 10005489 - -[variables selection] -selected_vars_reset = False -""" - - expected_file_content += "select_var + = OV. COST\nselect_var + = OP. COST\n\n" - - # When - new_study = create_study_local( - study_name, - study_version, - study_path, - StudySettingsLocal( - general_parameters=general_parameters, thematic_trimming_parameters=thematic_trimming_parameters - ), - ) - - actual_generaldata_ini_file_path = new_study.service.config.study_path / InitializationFilesTypes.GENERAL.value - actual_file_content = actual_generaldata_ini_file_path.read_text() - - # Then - assert actual_file_content == expected_file_content - - def test_generaldata_ini_with_playlist_has_negative_sign(self, tmp_path): - # Given - study_name = "test study" - study_version = "880" - general_parameters = GeneralParametersLocal(selection_mode=True, thematic_trimming=True) - playlist_parameters = PlaylistParameters(playlist=[PlaylistData(), PlaylistData(), PlaylistData(status=False)]) - thematic_trimming_parameters = ThematicTrimmingParametersLocal(op_cost=False) - thematic_trimming_parameters = ThematicTrimmingParametersLocal.model_validate( - {key: not value for key, value in thematic_trimming_parameters.model_dump().items()} - ) - study_path = str(tmp_path.absolute()) - expected_file_content = """[general] -mode = Economy -horizon = -nbyears = 1 -simulation.start = 1 -simulation.end = 365 -january.1st = Monday -first-month-in-year = January -first.weekday = Monday -leapyear = False -year-by-year = False -derated = False -custom-scenario = False -user-playlist = True -thematic-trimming = True -geographic-trimming = False -generate = -nbtimeseriesload = 1 -nbtimeserieshydro = 1 -nbtimeseriesthermal = 1 -nbtimeserieswind = 1 -nbtimeseriessolar = 1 -refreshtimeseries = -intra-modal = -inter-modal = -refreshintervalload = 100 -refreshintervalhydro = 100 -refreshintervalthermal = 100 -refreshintervalwind = 100 -refreshintervalsolar = 100 -readonly = False - -[input] -import = - -[output] -synthesis = True -storenewset = False -archives = -result-format = txt-files - -[optimization] -simplex-range = week -transmission-capacities = local-values -include-constraints = True -include-hurdlecosts = True -include-tc-minstablepower = True -include-tc-min-ud-time = True -include-dayahead = True -include-strategicreserve = True -include-spinningreserve = True -include-primaryreserve = True -include-exportmps = none -include-exportstructure = False -include-unfeasible-problem-behavior = error-verbose - -[adequacy patch] -include-adq-patch = False -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True -set-to-null-ntc-between-physical-out-for-first-step = True -enable-first-step = False -price-taking-order = DENS -include-hurdle-cost-csr = False -check-csr-cost-function = False -threshold-initiate-curtailment-sharing-rule = 0 -threshold-display-local-matching-rule-violations = 0 -threshold-csr-variable-bounds-relaxation = 3 - -[other preferences] -initial-reservoir-levels = cold start -hydro-heuristic-policy = accommodate rule curves -hydro-pricing-mode = fast -power-fluctuations = free modulations -shedding-policy = shave peaks -unit-commitment-mode = fast -number-of-cores-mode = medium -renewable-generation-modelling = aggregated - -[advanced parameters] -accuracy-on-correlation = - -[seeds - Mersenne Twister] -seed-tsgen-wind = 5489 -seed-tsgen-load = 1005489 -seed-tsgen-hydro = 2005489 -seed-tsgen-thermal = 3005489 -seed-tsgen-solar = 4005489 -seed-tsnumbers = 5005489 -seed-unsupplied-energy-costs = 6005489 -seed-spilled-energy-costs = 7005489 -seed-thermal-costs = 8005489 -seed-hydro-costs = 9005489 -seed-initial-reservoir-levels = 10005489 - -[playlist] -playlist_reset = True -playlist_year - = 2 - -[variables selection] -selected_vars_reset = False -select_var + = OP. COST - -""" - - # When - new_study = create_study_local( - study_name, - study_version, - study_path, - StudySettingsLocal( - general_parameters=general_parameters, - playlist_parameters=playlist_parameters, - thematic_trimming_parameters=thematic_trimming_parameters, - ), - ) - - actual_general_parameters_file_path = ( - new_study.service.config.study_path / InitializationFilesTypes.GENERAL.value - ) - actual_file_content = actual_general_parameters_file_path.read_text() - - # Then - assert actual_file_content == expected_file_content - - def test_generaldata_ini_with_playlist_has_positive_sign(self, tmp_path): - # Given - study_name = "test study" - study_version = "880" - general_parameters = GeneralParametersLocal(selection_mode=True, thematic_trimming=True) - playlist_parameters = PlaylistParameters( - playlist=[PlaylistData(status=False), PlaylistData(status=False), PlaylistData()] - ) - thematic_trimming_parameters = ThematicTrimmingParametersLocal(op_cost=False) - thematic_trimming_parameters = ThematicTrimmingParametersLocal.model_validate( - {key: not value for key, value in thematic_trimming_parameters.model_dump().items()} - ) - study_path = str(tmp_path.absolute()) - expected_file_content = """[general] -mode = Economy -horizon = -nbyears = 1 -simulation.start = 1 -simulation.end = 365 -january.1st = Monday -first-month-in-year = January -first.weekday = Monday -leapyear = False -year-by-year = False -derated = False -custom-scenario = False -user-playlist = True -thematic-trimming = True -geographic-trimming = False -generate = -nbtimeseriesload = 1 -nbtimeserieshydro = 1 -nbtimeseriesthermal = 1 -nbtimeserieswind = 1 -nbtimeseriessolar = 1 -refreshtimeseries = -intra-modal = -inter-modal = -refreshintervalload = 100 -refreshintervalhydro = 100 -refreshintervalthermal = 100 -refreshintervalwind = 100 -refreshintervalsolar = 100 -readonly = False - -[input] -import = - -[output] -synthesis = True -storenewset = False -archives = -result-format = txt-files - -[optimization] -simplex-range = week -transmission-capacities = local-values -include-constraints = True -include-hurdlecosts = True -include-tc-minstablepower = True -include-tc-min-ud-time = True -include-dayahead = True -include-strategicreserve = True -include-spinningreserve = True -include-primaryreserve = True -include-exportmps = none -include-exportstructure = False -include-unfeasible-problem-behavior = error-verbose - -[adequacy patch] -include-adq-patch = False -set-to-null-ntc-from-physical-out-to-physical-in-for-first-step = True -set-to-null-ntc-between-physical-out-for-first-step = True -enable-first-step = False -price-taking-order = DENS -include-hurdle-cost-csr = False -check-csr-cost-function = False -threshold-initiate-curtailment-sharing-rule = 0 -threshold-display-local-matching-rule-violations = 0 -threshold-csr-variable-bounds-relaxation = 3 - -[other preferences] -initial-reservoir-levels = cold start -hydro-heuristic-policy = accommodate rule curves -hydro-pricing-mode = fast -power-fluctuations = free modulations -shedding-policy = shave peaks -unit-commitment-mode = fast -number-of-cores-mode = medium -renewable-generation-modelling = aggregated - -[advanced parameters] -accuracy-on-correlation = - -[seeds - Mersenne Twister] -seed-tsgen-wind = 5489 -seed-tsgen-load = 1005489 -seed-tsgen-hydro = 2005489 -seed-tsgen-thermal = 3005489 -seed-tsgen-solar = 4005489 -seed-tsnumbers = 5005489 -seed-unsupplied-energy-costs = 6005489 -seed-spilled-energy-costs = 7005489 -seed-thermal-costs = 8005489 -seed-hydro-costs = 9005489 -seed-initial-reservoir-levels = 10005489 - -[playlist] -playlist_reset = False -playlist_year + = 2 - -[variables selection] -selected_vars_reset = False -select_var + = OP. COST - -""" - - # When - new_study = create_study_local( - study_name, - study_version, - study_path, - StudySettingsLocal( - general_parameters=general_parameters, - playlist_parameters=playlist_parameters, - thematic_trimming_parameters=thematic_trimming_parameters, - ), - ) - - actual_general_ini_file_path = new_study.service.config.study_path / InitializationFilesTypes.GENERAL.value - actual_file_content = actual_general_ini_file_path.read_text() - - # Then - assert actual_file_content == expected_file_content - class TestCreateArea: def test_initialization_when_creating_area(self, tmp_path, local_study): From 940f29ecb59e70f78fc42dcca7c5eda310425c41 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Tue, 4 Feb 2025 13:26:32 +0100 Subject: [PATCH 66/69] fix integration test --- .../services/local_services/test_study.py | 28 ++++++++++--------- tests/integration/test_web_client.py | 17 +++++------ 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/antares/services/local_services/test_study.py b/tests/antares/services/local_services/test_study.py index 12db7318..9f1c95b2 100644 --- a/tests/antares/services/local_services/test_study.py +++ b/tests/antares/services/local_services/test_study.py @@ -53,7 +53,8 @@ AdequacyPatchParameters, ) from antares.craft.model.settings.advanced_parameters import ( - AdvancedParameters, SeedParameters, + AdvancedParameters, + SeedParameters, ) from antares.craft.model.settings.general import ( GeneralParameters, @@ -61,7 +62,6 @@ from antares.craft.model.settings.optimization import ( OptimizationParameters, ) -from antares.craft.model.settings.playlist_parameters import PlaylistParameters from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.study import create_study_local from antares.craft.tools.ini_tool import InitializationFilesTypes @@ -195,7 +195,8 @@ def test_local_study_has_settings(self, local_study): assert isinstance(local_study_settings, StudySettings) def test_local_study_has_correct_default_general_properties(self, local_study): - expected_general_properties = GeneralParameters(**{ + expected_general_properties = GeneralParameters( + **{ "mode": "Economy", "horizon": "", "nb_years": 1, @@ -212,14 +213,15 @@ def test_local_study_has_correct_default_general_properties(self, local_study): "simulation_synthesis": True, "user_playlist": False, "store_new_set": False, - "nb_timeseries_thermal": 1 - }) + "nb_timeseries_thermal": 1, + } + ) assert local_study.get_settings().general_parameters == expected_general_properties def test_local_study_has_correct_default_adequacy_patch_properties(self, local_study): - expected_adequacy_patch_properties = AdequacyPatchParameters(** - { + expected_adequacy_patch_properties = AdequacyPatchParameters( + **{ "include_adq_patch": False, "set_to_null_ntc_from_physical_out_to_physical_in_for_first_step": True, "set_to_null_ntc_between_physical_out_for_first_step": True, @@ -235,8 +237,8 @@ def test_local_study_has_correct_default_adequacy_patch_properties(self, local_s assert local_study.get_settings().adequacy_patch_parameters == expected_adequacy_patch_properties def test_local_study_has_correct_advanced_parameters(self, local_study): - expected_advanced_parameters = AdvancedParameters(** - { + expected_advanced_parameters = AdvancedParameters( + **{ "accuracy_on_correlation": [], "initial_reservoir_levels": "cold start", "hydro_heuristic_policy": "accommodate rule curves", @@ -252,8 +254,8 @@ def test_local_study_has_correct_advanced_parameters(self, local_study): assert local_study.get_settings().advanced_parameters == expected_advanced_parameters def test_local_study_has_correct_seed_parameters(self, local_study): - expected_seed_parameters = SeedParameters(** - { + expected_seed_parameters = SeedParameters( + **{ "seed_tsgen_thermal": 3005489, "seed_tsnumbers": 5005489, "seed_unsupplied_energy_costs": 6005489, @@ -267,8 +269,8 @@ def test_local_study_has_correct_seed_parameters(self, local_study): assert local_study.get_settings().seed_parameters == expected_seed_parameters def test_local_study_has_correct_optimization_parameters(self, local_study): - expected_optimization_parameters = OptimizationParameters(** - { + expected_optimization_parameters = OptimizationParameters( + **{ "simplex_range": "week", "transmission_capacities": "local-values", "include_constraints": True, diff --git a/tests/integration/test_web_client.py b/tests/integration/test_web_client.py index 2d6b3eba..2f62cad3 100644 --- a/tests/integration/test_web_client.py +++ b/tests/integration/test_web_client.py @@ -489,27 +489,24 @@ def test_creation_lifecycle(self, antares_web: AntaresWebDesktop): # test study creation with settings settings = StudySettings() - settings.general_parameters = GeneralParameters(mode="Adequacy") + settings.general_parameters = GeneralParameters(mode=Mode.ADEQUACY) settings.general_parameters.year_by_year = False - settings.playlist_parameters = PlaylistParameters() - settings.playlist_parameters.playlist = [{"status": False, "weight": 1}] + settings.playlist_parameters = {1: PlaylistParameters(status=False, weight=1)} new_study = create_study_api("second_study", "880", api_config, settings) settings = new_study.get_settings() - assert settings.general_parameters.mode == Mode.ADEQUACY.value + assert settings.general_parameters.mode == Mode.ADEQUACY assert not settings.general_parameters.year_by_year - assert settings.playlist_parameters.model_dump() == {1: {"status": False, "weight": 1}} + assert settings.playlist_parameters == {1: PlaylistParameters(status=False, weight=1)} # tests update settings new_settings = StudySettings() - # Really important note. To instance such object with value you must respect camel case. - # Another way to do so is to instance the object and then fill its values - new_settings.general_parameters = GeneralParameters(nbYears=4) + new_settings.general_parameters = GeneralParameters(nb_years=4) new_settings.advanced_parameters = AdvancedParameters() new_settings.advanced_parameters.unit_commitment_mode = UnitCommitmentMode.MILP new_study.update_settings(new_settings) - assert new_study.get_settings().general_parameters.mode == Mode.ADEQUACY.value + assert new_study.get_settings().general_parameters.mode == Mode.ADEQUACY assert new_study.get_settings().general_parameters.nb_years == 4 - assert new_study.get_settings().advanced_parameters.unit_commitment_mode == UnitCommitmentMode.MILP.value + assert new_study.get_settings().advanced_parameters.unit_commitment_mode == UnitCommitmentMode.MILP old_settings = new_study.get_settings() empty_settings = StudySettings() From 45157aee716979426316184b5136c7117aa6fe09 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Tue, 4 Feb 2025 13:31:28 +0100 Subject: [PATCH 67/69] fix tests --- .../antares/integration/test_local_client.py | 20 ++++++++----------- tests/integration/test_web_client.py | 6 +++--- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/antares/integration/test_local_client.py b/tests/antares/integration/test_local_client.py index 8087f3ce..34a71ec3 100644 --- a/tests/antares/integration/test_local_client.py +++ b/tests/antares/integration/test_local_client.py @@ -20,9 +20,9 @@ from antares.craft.model.commons import FilterOption from antares.craft.model.link import Link, LinkProperties, LinkUi from antares.craft.model.renewable import RenewableClusterGroup, RenewableClusterProperties -from antares.craft.model.settings.general import GeneralParametersLocal, Mode -from antares.craft.model.settings.playlist_parameters import PlaylistParameters -from antares.craft.model.settings.study_settings import StudySettingsLocal +from antares.craft.model.settings.advanced_parameters import AdvancedParameters, UnitCommitmentMode +from antares.craft.model.settings.general import GeneralParameters +from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.st_storage import STStorageGroup, STStorageProperties from antares.craft.model.study import Study, create_study_local from antares.craft.model.thermal import ThermalCluster, ThermalClusterGroup, ThermalClusterProperties @@ -248,13 +248,9 @@ def test_local_study(self, tmp_path, unknown_area): } # test study creation with settings - settings = StudySettingsLocal() - settings.general_parameters = GeneralParametersLocal(mode="Adequacy") - settings.general_parameters.year_by_year = False - settings.playlist_parameters = PlaylistParameters() - settings.playlist_parameters.playlist = [{"status": False, "weight": 1}] + settings = StudySettings() + settings.general_parameters = GeneralParameters(nb_years=4) + settings.advanced_parameters = AdvancedParameters(unit_commitment_mode=UnitCommitmentMode.MILP) new_study = create_study_local("second_study", "880", tmp_path, settings) - settings = new_study.get_settings() - assert settings.general_parameters.mode == Mode.ADEQUACY.value - assert not settings.general_parameters.year_by_year - assert settings.playlist_parameters.model_dump() == {1: {"status": False, "weight": 1}} + assert new_study.get_settings().general_parameters.nb_years == 4 + assert new_study.get_settings().advanced_parameters.unit_commitment_mode == UnitCommitmentMode.MILP.value diff --git a/tests/integration/test_web_client.py b/tests/integration/test_web_client.py index 2f62cad3..1b38d253 100644 --- a/tests/integration/test_web_client.py +++ b/tests/integration/test_web_client.py @@ -494,7 +494,7 @@ def test_creation_lifecycle(self, antares_web: AntaresWebDesktop): settings.playlist_parameters = {1: PlaylistParameters(status=False, weight=1)} new_study = create_study_api("second_study", "880", api_config, settings) settings = new_study.get_settings() - assert settings.general_parameters.mode == Mode.ADEQUACY + assert settings.general_parameters.mode == Mode.ADEQUACY.value assert not settings.general_parameters.year_by_year assert settings.playlist_parameters == {1: PlaylistParameters(status=False, weight=1)} @@ -504,9 +504,9 @@ def test_creation_lifecycle(self, antares_web: AntaresWebDesktop): new_settings.advanced_parameters = AdvancedParameters() new_settings.advanced_parameters.unit_commitment_mode = UnitCommitmentMode.MILP new_study.update_settings(new_settings) - assert new_study.get_settings().general_parameters.mode == Mode.ADEQUACY + assert new_study.get_settings().general_parameters.mode == Mode.ADEQUACY.value assert new_study.get_settings().general_parameters.nb_years == 4 - assert new_study.get_settings().advanced_parameters.unit_commitment_mode == UnitCommitmentMode.MILP + assert new_study.get_settings().advanced_parameters.unit_commitment_mode == UnitCommitmentMode.MILP.value old_settings = new_study.get_settings() empty_settings = StudySettings() From aadaddef2b532e7a14f1f5d0a535e8b1f4b7bd84 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Wed, 5 Feb 2025 18:17:30 +0100 Subject: [PATCH 68/69] fix tets --- tests/antares/services/api_services/test_study_api.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/antares/services/api_services/test_study_api.py b/tests/antares/services/api_services/test_study_api.py index 8f3c9e94..b4d31102 100644 --- a/tests/antares/services/api_services/test_study_api.py +++ b/tests/antares/services/api_services/test_study_api.py @@ -743,6 +743,7 @@ def test_import_study_success(self, tmp_path): output_url = f"{url}/outputs" constraints_url = f"{base_url}/studies/{self.study_id}/bindingconstraints" config_urls = re.compile(f"{base_url}/studies/{self.study_id}/config/.*") + ts_settings_url = f"https://antares.com/api/v1/studies/{self.study_id}/timeseries/config" url_import = f"{base_url}/studies/_import" url_move = f"{base_url}/studies/{self.study_id}/move?folder_dest={new_path}" @@ -753,15 +754,13 @@ def test_import_study_success(self, tmp_path): mocker.get(url, json=json_study) mocker.get(config_urls, json={}) + mocker.get(ts_settings_url, json={"thermal": {"number": 1}}, status_code=200) mocker.get(area_url, json={}) mocker.get(area_props_url, json={}) mocker.get(renewable_url, json=[]) mocker.get(thermal_url, json=[]) mocker.get(storage_url, json=[]) - mocker.get( - output_url, - json=[], - ) + mocker.get(output_url, json=[]) mocker.get(constraints_url, json=[]) mocker.put(url_move) From 9be60f95127cf426ec5f41272e812d502b568ba7 Mon Sep 17 00:00:00 2001 From: belthlemar Date: Fri, 7 Feb 2025 15:34:47 +0100 Subject: [PATCH 69/69] resolve architecture commits --- src/antares/craft/model/study.py | 4 +- .../service/api_services/models/__init__.py | 11 ++ .../settings.py} | 116 ------------ .../service/api_services/services/__init__.py | 11 ++ .../service/api_services/services/settings.py | 136 ++++++++++++++ .../craft/service/api_services/study_api.py | 2 +- .../service/local_services/models/__init__.py | 11 ++ .../settings.py} | 156 ++-------------- .../local_services/services/__init__.py | 11 ++ .../local_services/services/settings.py | 168 ++++++++++++++++++ .../service/local_services/study_local.py | 2 +- src/antares/craft/tools/all_optional_meta.py | 17 +- 12 files changed, 370 insertions(+), 275 deletions(-) create mode 100644 src/antares/craft/service/api_services/models/__init__.py rename src/antares/craft/service/api_services/{study_settings_api.py => models/settings.py} (69%) create mode 100644 src/antares/craft/service/api_services/services/__init__.py create mode 100644 src/antares/craft/service/api_services/services/settings.py create mode 100644 src/antares/craft/service/local_services/models/__init__.py rename src/antares/craft/service/local_services/{study_settings_local.py => models/settings.py} (62%) create mode 100644 src/antares/craft/service/local_services/services/__init__.py create mode 100644 src/antares/craft/service/local_services/services/settings.py diff --git a/src/antares/craft/model/study.py b/src/antares/craft/model/study.py index 083b4743..83dd2992 100644 --- a/src/antares/craft/model/study.py +++ b/src/antares/craft/model/study.py @@ -40,9 +40,9 @@ from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.simulation import AntaresSimulationParameters, Job -from antares.craft.service.api_services.study_settings_api import read_study_settings_api +from antares.craft.service.api_services.services.settings import read_study_settings_api from antares.craft.service.base_services import BaseStudyService -from antares.craft.service.local_services.study_settings_local import edit_study_settings, read_study_settings_local +from antares.craft.service.local_services.services.settings import edit_study_settings, read_study_settings_local from antares.craft.service.service_factory import ServiceFactory from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes diff --git a/src/antares/craft/service/api_services/models/__init__.py b/src/antares/craft/service/api_services/models/__init__.py new file mode 100644 index 00000000..058c6b22 --- /dev/null +++ b/src/antares/craft/service/api_services/models/__init__.py @@ -0,0 +1,11 @@ +# 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. diff --git a/src/antares/craft/service/api_services/study_settings_api.py b/src/antares/craft/service/api_services/models/settings.py similarity index 69% rename from src/antares/craft/service/api_services/study_settings_api.py rename to src/antares/craft/service/api_services/models/settings.py index f9a414f0..77a5cfa7 100644 --- a/src/antares/craft/service/api_services/study_settings_api.py +++ b/src/antares/craft/service/api_services/models/settings.py @@ -12,8 +12,6 @@ from dataclasses import asdict from typing import Optional -from antares.craft.api_conf.request_wrapper import RequestWrapper -from antares.craft.exceptions.exceptions import APIError, StudySettingsReadError from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder from antares.craft.model.settings.advanced_parameters import ( AdvancedParameters, @@ -43,8 +41,6 @@ SimplexOptimizationRange, UnfeasibleProblemBehavior, ) -from antares.craft.model.settings.playlist_parameters import PlaylistParameters -from antares.craft.model.settings.study_settings import StudySettings from antares.craft.model.settings.thematic_trimming import ThematicTrimmingParameters from antares.craft.tools.all_optional_meta import all_optional_model from pydantic import BaseModel, Field @@ -358,115 +354,3 @@ def from_user_model(user_class: ThematicTrimmingParameters) -> "ThematicTrimming def to_user_model(self) -> ThematicTrimmingParameters: return ThematicTrimmingParameters(**self.model_dump(mode="json")) - - -def edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: - settings_base_url = f"{base_url}/studies/{study_id}/config" - - # thematic trimming - if settings.thematic_trimming_parameters: - thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" - api_model = ThematicTrimmingParametersAPI.from_user_model(settings.thematic_trimming_parameters) - body = api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(thematic_trimming_url, json=body) - - # playlist - if settings.playlist_parameters: - playlist_url = f"{settings_base_url}/playlist/form" - body = {} - for key, value in settings.playlist_parameters.items(): - body[str(key)] = asdict(value) - wrapper.put(playlist_url, json=body) - - # optimization - if settings.optimization_parameters: - optimization_url = f"{settings_base_url}/optimization/form" - optimization_api_model = OptimizationParametersAPI.from_user_model(settings.optimization_parameters) - body = optimization_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(optimization_url, json=body) - - # general and timeseries - if settings.general_parameters: - general_url = f"{settings_base_url}/general/form" - general_api_model = GeneralParametersAPI.from_user_model(settings.general_parameters) - body = general_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(general_url, json=body) - - if nb_ts_thermal := settings.general_parameters.nb_timeseries_thermal: - timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" - wrapper.put(timeseries_url, json={"thermal": {"number": nb_ts_thermal}}) - - # advanced and seed parameters - if settings.advanced_parameters or settings.seed_parameters: - advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" - advanced_api_model = AdvancedAndSeedParametersAPI.from_user_model( - settings.advanced_parameters, settings.seed_parameters - ) - body = advanced_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(advanced_parameters_url, json=body) - - # adequacy patch - if settings.adequacy_patch_parameters: - adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" - adequacy_patch_api_model = AdequacyPatchParametersAPI.from_user_model(settings.adequacy_patch_parameters) - body = adequacy_patch_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) - wrapper.put(adequacy_patch_url, json=body) - - -def read_study_settings_api(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: - settings_base_url = f"{base_url}/studies/{study_id}/config" - try: - # thematic trimming - thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" - response = wrapper.get(thematic_trimming_url) - thematic_trimming_api_model = ThematicTrimmingParametersAPI.model_validate(response.json()) - thematic_trimming_parameters = thematic_trimming_api_model.to_user_model() - - # playlist - playlist_url = f"{settings_base_url}/playlist/form" - response = wrapper.get(playlist_url) - json_response = response.json() - user_playlist = {} - for key, value in json_response.items(): - user_playlist[int(key)] = PlaylistParameters(**value) - - # optimization - optimization_url = f"{settings_base_url}/optimization/form" - response = wrapper.get(optimization_url) - optimization_api_model = OptimizationParametersAPI.model_validate(response.json()) - optimization_parameters = optimization_api_model.to_user_model() - - # general and timeseries - general_url = f"{settings_base_url}/general/form" - response = wrapper.get(general_url) - general_api_model = GeneralParametersAPI.model_validate(response.json()) - timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" - response = wrapper.get(timeseries_url) - nb_ts_thermal = response.json()["thermal"]["number"] - general_parameters = general_api_model.to_user_model(nb_ts_thermal) - - # advanced and seed parameters - advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" - response = wrapper.get(advanced_parameters_url) - advanced_parameters_api_model = AdvancedAndSeedParametersAPI.model_validate(response.json()) - seed_parameters = advanced_parameters_api_model.to_user_seed_parameters_model() - advanced_parameters = advanced_parameters_api_model.to_user_advanced_parameters_model() - - # adequacy patch - adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" - response = wrapper.get(adequacy_patch_url) - adequacy_patch_api_model = AdequacyPatchParametersAPI.model_validate(response.json()) - adequacy_patch_parameters = adequacy_patch_api_model.to_user_model() - - except APIError as e: - raise StudySettingsReadError(study_id, e.message) from e - - return StudySettings( - general_parameters=general_parameters, - optimization_parameters=optimization_parameters, - seed_parameters=seed_parameters, - advanced_parameters=advanced_parameters, - adequacy_patch_parameters=adequacy_patch_parameters, - playlist_parameters=user_playlist, - thematic_trimming_parameters=thematic_trimming_parameters, - ) diff --git a/src/antares/craft/service/api_services/services/__init__.py b/src/antares/craft/service/api_services/services/__init__.py new file mode 100644 index 00000000..058c6b22 --- /dev/null +++ b/src/antares/craft/service/api_services/services/__init__.py @@ -0,0 +1,11 @@ +# 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. diff --git a/src/antares/craft/service/api_services/services/settings.py b/src/antares/craft/service/api_services/services/settings.py new file mode 100644 index 00000000..0d5eb0c3 --- /dev/null +++ b/src/antares/craft/service/api_services/services/settings.py @@ -0,0 +1,136 @@ +# 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 antares.craft.api_conf.request_wrapper import RequestWrapper +from antares.craft.exceptions.exceptions import APIError, StudySettingsReadError +from antares.craft.model.settings.playlist_parameters import PlaylistParameters +from antares.craft.model.settings.study_settings import StudySettings +from antares.craft.service.api_services.models.settings import ( + AdequacyPatchParametersAPI, + AdvancedAndSeedParametersAPI, + GeneralParametersAPI, + OptimizationParametersAPI, + ThematicTrimmingParametersAPI, +) + + +def edit_study_settings(base_url: str, study_id: str, wrapper: RequestWrapper, settings: StudySettings) -> None: + settings_base_url = f"{base_url}/studies/{study_id}/config" + + # thematic trimming + if settings.thematic_trimming_parameters: + thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" + api_model = ThematicTrimmingParametersAPI.from_user_model(settings.thematic_trimming_parameters) + body = api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(thematic_trimming_url, json=body) + + # playlist + if settings.playlist_parameters: + playlist_url = f"{settings_base_url}/playlist/form" + body = {} + for key, value in settings.playlist_parameters.items(): + body[str(key)] = asdict(value) + wrapper.put(playlist_url, json=body) + + # optimization + if settings.optimization_parameters: + optimization_url = f"{settings_base_url}/optimization/form" + optimization_api_model = OptimizationParametersAPI.from_user_model(settings.optimization_parameters) + body = optimization_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(optimization_url, json=body) + + # general and timeseries + if settings.general_parameters: + general_url = f"{settings_base_url}/general/form" + general_api_model = GeneralParametersAPI.from_user_model(settings.general_parameters) + body = general_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(general_url, json=body) + + if nb_ts_thermal := settings.general_parameters.nb_timeseries_thermal: + timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" + wrapper.put(timeseries_url, json={"thermal": {"number": nb_ts_thermal}}) + + # advanced and seed parameters + if settings.advanced_parameters or settings.seed_parameters: + advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" + advanced_api_model = AdvancedAndSeedParametersAPI.from_user_model( + settings.advanced_parameters, settings.seed_parameters + ) + body = advanced_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(advanced_parameters_url, json=body) + + # adequacy patch + if settings.adequacy_patch_parameters: + adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" + adequacy_patch_api_model = AdequacyPatchParametersAPI.from_user_model(settings.adequacy_patch_parameters) + body = adequacy_patch_api_model.model_dump(mode="json", exclude_unset=True, by_alias=True) + wrapper.put(adequacy_patch_url, json=body) + + +def read_study_settings_api(base_url: str, study_id: str, wrapper: RequestWrapper) -> StudySettings: + settings_base_url = f"{base_url}/studies/{study_id}/config" + try: + # thematic trimming + thematic_trimming_url = f"{settings_base_url}/thematictrimming/form" + response = wrapper.get(thematic_trimming_url) + thematic_trimming_api_model = ThematicTrimmingParametersAPI.model_validate(response.json()) + thematic_trimming_parameters = thematic_trimming_api_model.to_user_model() + + # playlist + playlist_url = f"{settings_base_url}/playlist/form" + response = wrapper.get(playlist_url) + json_response = response.json() + user_playlist = {} + for key, value in json_response.items(): + user_playlist[int(key)] = PlaylistParameters(**value) + + # optimization + optimization_url = f"{settings_base_url}/optimization/form" + response = wrapper.get(optimization_url) + optimization_api_model = OptimizationParametersAPI.model_validate(response.json()) + optimization_parameters = optimization_api_model.to_user_model() + + # general and timeseries + general_url = f"{settings_base_url}/general/form" + response = wrapper.get(general_url) + general_api_model = GeneralParametersAPI.model_validate(response.json()) + timeseries_url = f"{base_url}/studies/{study_id}/timeseries/config" + response = wrapper.get(timeseries_url) + nb_ts_thermal = response.json()["thermal"]["number"] + general_parameters = general_api_model.to_user_model(nb_ts_thermal) + + # advanced and seed parameters + advanced_parameters_url = f"{settings_base_url}/advancedparameters/form" + response = wrapper.get(advanced_parameters_url) + advanced_parameters_api_model = AdvancedAndSeedParametersAPI.model_validate(response.json()) + seed_parameters = advanced_parameters_api_model.to_user_seed_parameters_model() + advanced_parameters = advanced_parameters_api_model.to_user_advanced_parameters_model() + + # adequacy patch + adequacy_patch_url = f"{settings_base_url}/adequacypatch/form" + response = wrapper.get(adequacy_patch_url) + adequacy_patch_api_model = AdequacyPatchParametersAPI.model_validate(response.json()) + adequacy_patch_parameters = adequacy_patch_api_model.to_user_model() + + except APIError as e: + raise StudySettingsReadError(study_id, e.message) from e + + return StudySettings( + general_parameters=general_parameters, + optimization_parameters=optimization_parameters, + seed_parameters=seed_parameters, + advanced_parameters=advanced_parameters, + adequacy_patch_parameters=adequacy_patch_parameters, + playlist_parameters=user_playlist, + thematic_trimming_parameters=thematic_trimming_parameters, + ) diff --git a/src/antares/craft/service/api_services/study_api.py b/src/antares/craft/service/api_services/study_api.py index 39d1f9dd..39bd44a5 100644 --- a/src/antares/craft/service/api_services/study_api.py +++ b/src/antares/craft/service/api_services/study_api.py @@ -32,7 +32,7 @@ from antares.craft.model.binding_constraint import BindingConstraint from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import StudySettings -from antares.craft.service.api_services.study_settings_api import edit_study_settings +from antares.craft.service.api_services.services.settings import edit_study_settings from antares.craft.service.api_services.utils import wait_task_completion from antares.craft.service.base_services import BaseOutputService, BaseStudyService diff --git a/src/antares/craft/service/local_services/models/__init__.py b/src/antares/craft/service/local_services/models/__init__.py new file mode 100644 index 00000000..058c6b22 --- /dev/null +++ b/src/antares/craft/service/local_services/models/__init__.py @@ -0,0 +1,11 @@ +# 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. diff --git a/src/antares/craft/service/local_services/study_settings_local.py b/src/antares/craft/service/local_services/models/settings.py similarity index 62% rename from src/antares/craft/service/local_services/study_settings_local.py rename to src/antares/craft/service/local_services/models/settings.py index 89e8ece0..5f68b083 100644 --- a/src/antares/craft/service/local_services/study_settings_local.py +++ b/src/antares/craft/service/local_services/models/settings.py @@ -9,10 +9,10 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. + import ast from dataclasses import asdict -from pathlib import Path from typing import Any, Sequence, Set from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters, PriceTakingOrder @@ -35,11 +35,23 @@ SimplexOptimizationRange, UnfeasibleProblemBehavior, ) -from antares.craft.model.settings.study_settings import StudySettings from antares.craft.tools.alias_generators import to_kebab -from antares.craft.tools.all_optional_meta import LocalBaseModel -from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes -from pydantic import Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator +from pydantic_core import PydanticUseDefault + + +class LocalBaseModel(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + @field_validator("*", mode="before") + @classmethod + def _usedefault_for_none(cls, value: Any) -> Any: + """ + Will use the default value for the field if the value is None and the annotation doesn't allow for a None input. + """ + if value is None: + raise PydanticUseDefault() + return value class AdequacyPatchParametersLocal(LocalBaseModel, alias_generator=to_kebab): @@ -244,137 +256,3 @@ def from_user_model(user_class: OptimizationParameters) -> "OptimizationParamete def to_user_model(self) -> OptimizationParameters: local_dict = self.model_dump(mode="json", by_alias=False) return OptimizationParameters(**local_dict) - - -def read_study_settings_local(study_directory: Path) -> StudySettings: - general_data_ini = IniFile(study_directory, InitializationFilesTypes.GENERAL) - ini_content = general_data_ini.ini_dict - - # general - general_params_ini = {"general": ini_content["general"]} - if general_params_ini.pop("derated", None): - general_params_ini["building_mode"] = BuildingMode.DERATED.value - if general_params_ini.pop("custom-scenario", None): - general_params_ini["building_mode"] = BuildingMode.CUSTOM.value - else: - general_params_ini["building_mode"] = BuildingMode.AUTOMATIC.value - - excluded_keys = GeneralParametersLocal.get_excluded_fields_for_user_class() - for key in excluded_keys: - general_params_ini.pop(key, None) - - output_parameters_ini = {"output": ini_content["output"]} - local_general_ini = general_params_ini | output_parameters_ini - general_parameters_local = GeneralParametersLocal.model_validate(local_general_ini) - general_parameters = general_parameters_local.to_user_model() - - # optimization - optimization_ini = ini_content["optimization"] - optimization_ini.pop("link-type", None) - optimization_parameters_local = OptimizationParametersLocal.model_validate(optimization_ini) - optimization_parameters = optimization_parameters_local.to_user_model() - - # adequacy_patch - adequacy_ini = ini_content["adequacy patch"] - adequacy_parameters_local = AdequacyPatchParametersLocal.model_validate(adequacy_ini) - adequacy_patch_parameters = adequacy_parameters_local.to_user_model() - - # seed and advanced - seed_local_parameters = SeedParametersLocal.model_validate(ini_content["seeds - Mersenne Twister"]) - advanced_local_parameters = AdvancedParametersLocal.model_validate(ini_content["advanced parameters"]) - other_preferences_local_parameters = OtherPreferencesLocal.model_validate(ini_content["other preferences"]) - args = { - "other_preferences": other_preferences_local_parameters, - "seeds": seed_local_parameters, - "advanced_parameters": advanced_local_parameters, - } - seed_and_advanced_local_parameters = AdvancedAndSeedParametersLocal.model_validate(args) - seed_parameters = seed_and_advanced_local_parameters.to_seed_parameters_model() - advanced_parameters = seed_and_advanced_local_parameters.to_advanced_parameters_model() - - # playlist - playlist_parameters = None - if "playlist" in ini_content: - playlist_parameters = None - # todo - - # thematic trimming - thematic_trimming_parameters = None - if "variables selection" in ini_content: - thematic_trimming_parameters = None - # todo - - return StudySettings( - general_parameters=general_parameters, - optimization_parameters=optimization_parameters, - seed_parameters=seed_parameters, - advanced_parameters=advanced_parameters, - adequacy_patch_parameters=adequacy_patch_parameters, - playlist_parameters=playlist_parameters, - thematic_trimming_parameters=thematic_trimming_parameters, - ) - - -def edit_study_settings(study_directory: Path, settings: StudySettings, update: bool) -> StudySettings: - general_data_ini = IniFile(study_directory, InitializationFilesTypes.GENERAL) - ini_content = general_data_ini.ini_dict if update else {} - - # general - general_parameters = settings.general_parameters or GeneralParameters() - general_local_parameters = GeneralParametersLocal.from_user_model(general_parameters) - - json_content = general_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update) - if "general" in json_content and "building_mode" in json_content["general"]: - general_values = json_content["general"] - del general_values["building_mode"] - building_mode = general_local_parameters.general.building_mode - general_values["derated"] = building_mode == BuildingMode.DERATED - general_values["custom-scenario"] = building_mode == BuildingMode.CUSTOM - - ini_content.update(json_content) - new_general_parameters = general_local_parameters.to_user_model() - - # optimization - optimization_parameters = settings.optimization_parameters or OptimizationParameters() - optimization_local_parameters = OptimizationParametersLocal.from_user_model(optimization_parameters) - ini_content.update( - {"optimization": optimization_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} - ) - new_optimization_parameters = optimization_local_parameters.to_user_model() - - # adequacy_patch - adequacy_parameters = settings.adequacy_patch_parameters or AdequacyPatchParameters() - adequacy_local_parameters = AdequacyPatchParametersLocal.from_user_model(adequacy_parameters) - ini_content.update( - {"adequacy patch": adequacy_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} - ) - new_adequacy_parameters = adequacy_local_parameters.to_user_model() - - # seed and advanced - seed_parameters = settings.seed_parameters or SeedParameters() - advanced_parameters = settings.advanced_parameters or AdvancedParameters() - advanced_parameters_local = AdvancedAndSeedParametersLocal.from_user_model(advanced_parameters, seed_parameters) - ini_content.update(advanced_parameters_local.model_dump(mode="json", by_alias=True, exclude_unset=update)) - new_seed_parameters = advanced_parameters_local.to_seed_parameters_model() - new_advanced_parameters = advanced_parameters_local.to_advanced_parameters_model() - - # playlist - # todo - - # thematic trimming - # todo - - # writing - general_data_ini.ini_dict = ini_content - general_data_ini.write_ini_file() - - # returning new_settings - return StudySettings( - general_parameters=new_general_parameters, - optimization_parameters=new_optimization_parameters, - adequacy_patch_parameters=new_adequacy_parameters, - seed_parameters=new_seed_parameters, - advanced_parameters=new_advanced_parameters, - playlist_parameters=None, - thematic_trimming_parameters=None, - ) diff --git a/src/antares/craft/service/local_services/services/__init__.py b/src/antares/craft/service/local_services/services/__init__.py new file mode 100644 index 00000000..058c6b22 --- /dev/null +++ b/src/antares/craft/service/local_services/services/__init__.py @@ -0,0 +1,11 @@ +# 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. diff --git a/src/antares/craft/service/local_services/services/settings.py b/src/antares/craft/service/local_services/services/settings.py new file mode 100644 index 00000000..69ef20f9 --- /dev/null +++ b/src/antares/craft/service/local_services/services/settings.py @@ -0,0 +1,168 @@ +# 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 pathlib import Path + +from antares.craft.model.settings.adequacy_patch import AdequacyPatchParameters +from antares.craft.model.settings.advanced_parameters import ( + AdvancedParameters, + SeedParameters, +) +from antares.craft.model.settings.general import BuildingMode, GeneralParameters +from antares.craft.model.settings.optimization import ( + OptimizationParameters, +) +from antares.craft.model.settings.study_settings import StudySettings +from antares.craft.service.local_services.models.settings import ( + AdequacyPatchParametersLocal, + AdvancedAndSeedParametersLocal, + AdvancedParametersLocal, + GeneralParametersLocal, + OptimizationParametersLocal, + OtherPreferencesLocal, + SeedParametersLocal, +) +from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes + + +def read_study_settings_local(study_directory: Path) -> StudySettings: + general_data_ini = IniFile(study_directory, InitializationFilesTypes.GENERAL) + ini_content = general_data_ini.ini_dict + + # general + general_params_ini = {"general": ini_content["general"]} + if general_params_ini.pop("derated", None): + general_params_ini["building_mode"] = BuildingMode.DERATED.value + if general_params_ini.pop("custom-scenario", None): + general_params_ini["building_mode"] = BuildingMode.CUSTOM.value + else: + general_params_ini["building_mode"] = BuildingMode.AUTOMATIC.value + + excluded_keys = GeneralParametersLocal.get_excluded_fields_for_user_class() + for key in excluded_keys: + general_params_ini.pop(key, None) + + output_parameters_ini = {"output": ini_content["output"]} + local_general_ini = general_params_ini | output_parameters_ini + general_parameters_local = GeneralParametersLocal.model_validate(local_general_ini) + general_parameters = general_parameters_local.to_user_model() + + # optimization + optimization_ini = ini_content["optimization"] + optimization_ini.pop("link-type", None) + optimization_parameters_local = OptimizationParametersLocal.model_validate(optimization_ini) + optimization_parameters = optimization_parameters_local.to_user_model() + + # adequacy_patch + adequacy_ini = ini_content["adequacy patch"] + adequacy_parameters_local = AdequacyPatchParametersLocal.model_validate(adequacy_ini) + adequacy_patch_parameters = adequacy_parameters_local.to_user_model() + + # seed and advanced + seed_local_parameters = SeedParametersLocal.model_validate(ini_content["seeds - Mersenne Twister"]) + advanced_local_parameters = AdvancedParametersLocal.model_validate(ini_content["advanced parameters"]) + other_preferences_local_parameters = OtherPreferencesLocal.model_validate(ini_content["other preferences"]) + args = { + "other_preferences": other_preferences_local_parameters, + "seeds": seed_local_parameters, + "advanced_parameters": advanced_local_parameters, + } + seed_and_advanced_local_parameters = AdvancedAndSeedParametersLocal.model_validate(args) + seed_parameters = seed_and_advanced_local_parameters.to_seed_parameters_model() + advanced_parameters = seed_and_advanced_local_parameters.to_advanced_parameters_model() + + # playlist + playlist_parameters = None + if "playlist" in ini_content: + playlist_parameters = None + # todo + + # thematic trimming + thematic_trimming_parameters = None + if "variables selection" in ini_content: + thematic_trimming_parameters = None + # todo + + return StudySettings( + general_parameters=general_parameters, + optimization_parameters=optimization_parameters, + seed_parameters=seed_parameters, + advanced_parameters=advanced_parameters, + adequacy_patch_parameters=adequacy_patch_parameters, + playlist_parameters=playlist_parameters, + thematic_trimming_parameters=thematic_trimming_parameters, + ) + + +def edit_study_settings(study_directory: Path, settings: StudySettings, update: bool) -> StudySettings: + general_data_ini = IniFile(study_directory, InitializationFilesTypes.GENERAL) + ini_content = general_data_ini.ini_dict if update else {} + + # general + general_parameters = settings.general_parameters or GeneralParameters() + general_local_parameters = GeneralParametersLocal.from_user_model(general_parameters) + + json_content = general_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update) + if "general" in json_content and "building_mode" in json_content["general"]: + general_values = json_content["general"] + del general_values["building_mode"] + building_mode = general_local_parameters.general.building_mode + general_values["derated"] = building_mode == BuildingMode.DERATED + general_values["custom-scenario"] = building_mode == BuildingMode.CUSTOM + + ini_content.update(json_content) + new_general_parameters = general_local_parameters.to_user_model() + + # optimization + optimization_parameters = settings.optimization_parameters or OptimizationParameters() + optimization_local_parameters = OptimizationParametersLocal.from_user_model(optimization_parameters) + ini_content.update( + {"optimization": optimization_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} + ) + new_optimization_parameters = optimization_local_parameters.to_user_model() + + # adequacy_patch + adequacy_parameters = settings.adequacy_patch_parameters or AdequacyPatchParameters() + adequacy_local_parameters = AdequacyPatchParametersLocal.from_user_model(adequacy_parameters) + ini_content.update( + {"adequacy patch": adequacy_local_parameters.model_dump(mode="json", by_alias=True, exclude_unset=update)} + ) + new_adequacy_parameters = adequacy_local_parameters.to_user_model() + + # seed and advanced + seed_parameters = settings.seed_parameters or SeedParameters() + advanced_parameters = settings.advanced_parameters or AdvancedParameters() + advanced_parameters_local = AdvancedAndSeedParametersLocal.from_user_model(advanced_parameters, seed_parameters) + ini_content.update(advanced_parameters_local.model_dump(mode="json", by_alias=True, exclude_unset=update)) + new_seed_parameters = advanced_parameters_local.to_seed_parameters_model() + new_advanced_parameters = advanced_parameters_local.to_advanced_parameters_model() + + # playlist + # todo + + # thematic trimming + # todo + + # writing + general_data_ini.ini_dict = ini_content + general_data_ini.write_ini_file() + + # returning new_settings + return StudySettings( + general_parameters=new_general_parameters, + optimization_parameters=new_optimization_parameters, + adequacy_patch_parameters=new_adequacy_parameters, + seed_parameters=new_seed_parameters, + advanced_parameters=new_advanced_parameters, + playlist_parameters=None, + thematic_trimming_parameters=None, + ) diff --git a/src/antares/craft/service/local_services/study_local.py b/src/antares/craft/service/local_services/study_local.py index f65b9369..847c3933 100644 --- a/src/antares/craft/service/local_services/study_local.py +++ b/src/antares/craft/service/local_services/study_local.py @@ -17,7 +17,7 @@ from antares.craft.model.output import Output from antares.craft.model.settings.study_settings import StudySettings from antares.craft.service.base_services import BaseOutputService, BaseStudyService -from antares.craft.service.local_services.study_settings_local import edit_study_settings +from antares.craft.service.local_services.services.settings import edit_study_settings if TYPE_CHECKING: from antares.craft.model.study import Study diff --git a/src/antares/craft/tools/all_optional_meta.py b/src/antares/craft/tools/all_optional_meta.py index c66cf100..9aa394e3 100644 --- a/src/antares/craft/tools/all_optional_meta.py +++ b/src/antares/craft/tools/all_optional_meta.py @@ -13,8 +13,7 @@ import copy import typing as t -from pydantic import BaseModel, ConfigDict, create_model, field_validator -from pydantic_core import PydanticUseDefault +from pydantic import BaseModel, create_model ModelClass = t.TypeVar("ModelClass", bound=BaseModel) @@ -37,17 +36,3 @@ def all_optional_model(model: t.Type[ModelClass]) -> t.Type[ModelClass]: kwargs[field_name] = (new.annotation, new) return create_model(f"Partial{model.__name__}", __base__=model, __module__=model.__module__, **kwargs) # type: ignore - - -class LocalBaseModel(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - @field_validator("*", mode="before") - @classmethod - def _usedefault_for_none(cls, value: t.Any) -> t.Any: - """ - Will use the default value for the field if the value is None and the annotation doesn't allow for a None input. - """ - if value is None: - raise PydanticUseDefault() - return value