From f758ed6aadf962367b336358d8d62e8c96a3caad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Omn=C3=A8s?= Date: Fri, 14 Jun 2024 15:06:44 +0200 Subject: [PATCH 01/15] Add I/O & doc for CurtailmentSharing::recomputeDTGMRG [ANT-1809] (#2141) --- docs/user-guide/04-migration-guides.md | 1 + src/libs/antares/study/parameters/adq-patch-params.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/docs/user-guide/04-migration-guides.md b/docs/user-guide/04-migration-guides.md index 354dfa692f..0e5f50726c 100644 --- a/docs/user-guide/04-migration-guides.md +++ b/docs/user-guide/04-migration-guides.md @@ -166,6 +166,7 @@ In file **settings/generaldata.ini**, in section `adequacy patch`, add propertie * `price-taking-order` [string] can take values `DENS` (default value) and `Load`. * `include-hurdle-cost-csr` [bool]. Default value = `false` * `check-csr-cost-function` [bool]. Default value = `false` +* `recompute-dtg-mrg` [bool]. Default value = `false` * `threshold-initiate-curtailment-sharing-rule` [double]. Default value = `0.0` * `threshold-display-local-matching-rule-violations` [double]. Default value = `0.0` * `threshold-csr-variable-bounds-relaxation` [int]. Default value = `3` diff --git a/src/libs/antares/study/parameters/adq-patch-params.cpp b/src/libs/antares/study/parameters/adq-patch-params.cpp index cbce2e51b0..99d0d0db33 100644 --- a/src/libs/antares/study/parameters/adq-patch-params.cpp +++ b/src/libs/antares/study/parameters/adq-patch-params.cpp @@ -71,6 +71,7 @@ void CurtailmentSharing::reset() priceTakingOrder = AdqPatchPTO::isDens; includeHurdleCost = false; checkCsrCostFunction = false; + recomputeDTGMRG = false; resetThresholds(); } @@ -121,6 +122,11 @@ bool CurtailmentSharing::updateFromKeyValue(const Yuni::String& key, const Yuni: { return value.to(checkCsrCostFunction); } + if (key == "recompute-dtg-mrg") + { + return value.to(recomputeDTGMRG); + } + // Thresholds if (key == "threshold-initiate-curtailment-sharing-rule") { @@ -156,6 +162,7 @@ void CurtailmentSharing::addProperties(IniFile::Section* section) const section->add("price-taking-order", PriceTakingOrderToString(priceTakingOrder)); section->add("include-hurdle-cost-csr", includeHurdleCost); section->add("check-csr-cost-function", checkCsrCostFunction); + section->add("recompute-dtg-mrg", recomputeDTGMRG); // Thresholds section->add("threshold-initiate-curtailment-sharing-rule", thresholdRun); From be3301cdb3b2b7ed232db72e68a874c353d51609 Mon Sep 17 00:00:00 2001 From: guilpier-code <62292552+guilpier-code@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:03:41 +0200 Subject: [PATCH 02/15] Parallel years cleaning : few removal of "numspace" (#2128) See ticket [#1751](https://gopro-tickets.rte-france.com/browse/ANT-1751) For now, just a first analyze of how we could do to isolate **years** (from the simulation and what should not be inside a **year**). We take advantage of this to make some removals of **numSpace**. **Comments** : - **year job** and **simulation** are really intertwined, this is a first and important reason why a **year** are not isolated. For instance, when running a year of simulation, we call a function of the **simulation** object, not of the **year** object. - although what we first thought, **each year job creates a fresh new weekly optimization solver object**. This object solves every week problem, using the previous week's optimization solution as a base. Before creating a solver object, a year first **destroys the previous year's solver** (as well as the start base it holds). - I've heard that CI tests for parallel (currently passed at night run) don't generate the same output results as the same tests run in sequential. We assumed that it was because of the bases. Indeed, if the same simulation year is run starting from 2 different bases, it can lead to different simulation results. But given the previous point, that's not true : the simulation results should be identical whether we run a simulation sequentially or not. We may have broken something in the parallelism by not testing the it on CI. **Goals** : We want to isolate year jobs to make cleaner parallelism. Each year job should be designed so that it can be run in a thread independently from any other year job. In particular, a year job should hold its own memory spaces, and not shared memory as done currently. --------- Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> --- .../solver/hydro/management/management.h | 1 - src/solver/hydro/management/management.cpp | 2 - src/solver/simulation/adequacy.cpp | 10 +---- src/solver/simulation/economy.cpp | 8 +--- .../antares/solver/simulation/simulation.h | 2 +- .../antares/solver/simulation/solver.hxx | 40 +++++++++---------- .../simulation/sim_calcul_economique.cpp | 2 +- 7 files changed, 23 insertions(+), 42 deletions(-) diff --git a/src/solver/hydro/include/antares/solver/hydro/management/management.h b/src/solver/hydro/include/antares/solver/hydro/management/management.h index 78f3d4e6ea..3050515ca5 100644 --- a/src/solver/hydro/include/antares/solver/hydro/management/management.h +++ b/src/solver/hydro/include/antares/solver/hydro/management/management.h @@ -107,7 +107,6 @@ class HydroManagement final HydroManagement(const Data::AreaList& areas, const Data::Parameters& params, const Date::Calendar& calendar, - unsigned int maxNbYearsInParallel, Solver::IResultWriter& resultWriter); //! Perform the hydro ventilation diff --git a/src/solver/hydro/management/management.cpp b/src/solver/hydro/management/management.cpp index fe9311280d..b20be5e375 100644 --- a/src/solver/hydro/management/management.cpp +++ b/src/solver/hydro/management/management.cpp @@ -117,12 +117,10 @@ double BetaVariable(double a, double b, MersenneTwister& random) HydroManagement::HydroManagement(const Data::AreaList& areas, const Data::Parameters& params, const Date::Calendar& calendar, - unsigned int maxNbYearsInParallel, Solver::IResultWriter& resultWriter): areas_(areas), calendar_(calendar), parameters_(params), - maxNbYearsInParallel_(maxNbYearsInParallel), resultWriter_(resultWriter) { // Ventilation results memory allocation diff --git a/src/solver/simulation/adequacy.cpp b/src/solver/simulation/adequacy.cpp index f4e7f433d7..63d8462dfc 100644 --- a/src/solver/simulation/adequacy.cpp +++ b/src/solver/simulation/adequacy.cpp @@ -68,15 +68,7 @@ bool Adequacy::simulationBegin() pProblemesHebdo.resize(pNbMaxPerformedYearsInParallel); for (uint numSpace = 0; numSpace < pNbMaxPerformedYearsInParallel; numSpace++) { - SIM_InitialisationProblemeHebdo(study, pProblemesHebdo[numSpace], 168, numSpace); - - assert((uint)nbHoursInAWeek == (uint)pProblemesHebdo[numSpace].NombreDePasDeTemps - && "inconsistency"); - if ((uint)nbHoursInAWeek != (uint)pProblemesHebdo[numSpace].NombreDePasDeTemps) - { - logs.fatal() << "internal error"; - return false; - } + SIM_InitialisationProblemeHebdo(study, pProblemesHebdo[numSpace], nbHoursInAWeek, numSpace); } } diff --git a/src/solver/simulation/economy.cpp b/src/solver/simulation/economy.cpp index 0ffdf71d7b..02fac95314 100644 --- a/src/solver/simulation/economy.cpp +++ b/src/solver/simulation/economy.cpp @@ -73,13 +73,7 @@ bool Economy::simulationBegin() for (uint numSpace = 0; numSpace < pNbMaxPerformedYearsInParallel; numSpace++) { - SIM_InitialisationProblemeHebdo(study, pProblemesHebdo[numSpace], 168, numSpace); - - if ((uint)nbHoursInAWeek != (uint)pProblemesHebdo[numSpace].NombreDePasDeTemps) - { - logs.fatal() << "internal error"; - return false; - } + SIM_InitialisationProblemeHebdo(study, pProblemesHebdo[numSpace], nbHoursInAWeek, numSpace); auto options = createOptimizationOptions(study); weeklyOptProblems_[numSpace] = Antares::Solver::Optimization::WeeklyOptimization:: diff --git a/src/solver/simulation/include/antares/solver/simulation/simulation.h b/src/solver/simulation/include/antares/solver/simulation/simulation.h index 6d814157d1..bf214beff6 100644 --- a/src/solver/simulation/include/antares/solver/simulation/simulation.h +++ b/src/solver/simulation/include/antares/solver/simulation/simulation.h @@ -45,7 +45,7 @@ void SIM_AllocationProblemeHebdo(const Antares::Data::Study& study, */ void SIM_InitialisationProblemeHebdo(Antares::Data::Study& study, PROBLEME_HEBDO& problem, - int NombreDePasDeTemps, + unsigned int NombreDePasDeTemps, uint numspace); void SIM_RenseignementProblemeHebdo(const Antares::Data::Study& study, diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index dc2a7d4e09..87f38225cc 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -55,7 +55,7 @@ public: randomNumbers& pRandomForParallelYears, bool pPerformCalculations, Data::Study& pStudy, - std::vector& pState, + Variable::State& pState, bool pYearByYear, Benchmarking::DurationCollector& durationCollector, IResultWriter& resultWriter): @@ -75,7 +75,6 @@ public: hydroManagement(study.areas, study.parameters, study.calendar, - study.maxNbYearsInParallel, resultWriter) { hydroHotStart = (study.parameters.initialReservoirLevels.iniLevels == Data::irlHotStart); @@ -96,7 +95,7 @@ private: randomNumbers& randomForParallelYears; bool performCalculations; Data::Study& study; - std::vector& state; + Variable::State& state; bool yearByYear; bool hydroHotStart; Benchmarking::DurationCollector& pDurationCollector; @@ -155,8 +154,7 @@ public: // 1 - Applying random levels for current year if (hydroHotStart && firstSetParallelWithAPerformedYearWasRun) { - randomReservoirLevel = state[numSpace] - .problemeHebdo->previousYearFinalLevels.data(); + randomReservoirLevel = state.problemeHebdo->previousYearFinalLevels.data(); } else { @@ -177,7 +175,7 @@ public: }; // Updating the state - state[numSpace].year = y; + state.year = y; // 5 - Resetting all variables for the output simulation_->variables.yearBegin(y, numSpace); @@ -189,7 +187,7 @@ public: OptimizationStatisticsWriter optWriter(pResultWriter, y); yearFailed[y] = !simulation_->year(progression, - state[numSpace], + state, numSpace, randomForCurrentYear, failedWeekList, @@ -201,7 +199,7 @@ public: // Log failing weeks logFailedWeek(y, study, failedWeekList); - simulation_->variables.yearEndBuild(state[numSpace], y, numSpace); + simulation_->variables.yearEndBuild(state, y, numSpace); // 7 - End of the year, this is the last stade where the variables can retrieve // their data for this year. @@ -1059,19 +1057,19 @@ void ISimulation::loopThroughYears(uint firstYear, // continue; auto task = std::make_shared>( - this, - y, - set_it->yearFailed, - set_it->isFirstPerformedYearOfASet, - pFirstSetParallelWithAPerformedYearWasRun, - numSpace, - randomForParallelYears, - performCalculations, - study, - state, - pYearByYear, - pDurationCollector, - pResultWriter); + this, + y, + set_it->yearFailed, + set_it->isFirstPerformedYearOfASet, + pFirstSetParallelWithAPerformedYearWasRun, + numSpace, + randomForParallelYears, + performCalculations, + study, + state[numSpace], + pYearByYear, + pDurationCollector, + pResultWriter); results.add(Concurrency::AddTask(*pQueueService, task)); } // End loop over years of the current set of parallel years diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index 8b921dfec3..c51d8283dc 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -71,7 +71,7 @@ static void importShortTermStorages( void SIM_InitialisationProblemeHebdo(Data::Study& study, PROBLEME_HEBDO& problem, - int NombreDePasDeTemps, + unsigned int NombreDePasDeTemps, uint numspace) { int NombrePaliers; From 6edcad865005d73a8c5d79d6a96515a50bdaa3e0 Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:28:15 +0200 Subject: [PATCH 03/15] Remove hydro hotstart (#2131) https://github.com/AntaresSimulatorTeam/Antares_Simulator_Tests_NR/pull/24 Previously, the default value for property `initial-reservoir-levels` was `cold start`. --------- Co-authored-by: Guillaume PIERRE Co-authored-by: Florian OMNES --- docs/user-guide/solver/04-parameters.md | 3 +- docs/user-guide/solver/06-hydro-heuristics.md | 2 +- docs/user-guide/solver/09-appendix.md | 6 +- simtest.json | 2 +- src/libs/antares/study/fwd.cpp | 37 -------- .../antares/study/include/antares/study/fwd.h | 18 ---- .../study/include/antares/study/parameters.h | 11 --- .../antares/study/parts/hydro/container.h | 5 +- .../study/include/antares/study/study.h | 16 ---- src/libs/antares/study/load.cpp | 7 -- src/libs/antares/study/parameters.cpp | 28 ++---- .../antares/study/parts/hydro/container.cpp | 14 +-- src/libs/antares/study/study.cpp | 91 ------------------- src/solver/simulation/adequacy.cpp | 2 - src/solver/simulation/common-hydro-levels.cpp | 49 ++-------- src/solver/simulation/economy.cpp | 2 - .../solver/simulation/common-eco-adq.h | 8 -- .../sim_structure_probleme_economique.h | 2 - .../antares/solver/simulation/solver.h | 2 - .../antares/solver/simulation/solver.hxx | 45 ++------- .../simulation/sim_alloc_probleme_hebdo.cpp | 14 --- .../simulation/sim_calcul_economique.cpp | 18 +--- .../study/parameters/parameters-tests.cpp | 1 - .../windows/options/advanced/advanced.cpp | 63 +------------ .../windows/options/advanced/advanced.h | 7 +- 25 files changed, 46 insertions(+), 407 deletions(-) diff --git a/docs/user-guide/solver/04-parameters.md b/docs/user-guide/solver/04-parameters.md index bd46430336..fc8492a9e4 100644 --- a/docs/user-guide/solver/04-parameters.md +++ b/docs/user-guide/solver/04-parameters.md @@ -588,8 +588,7 @@ _**This section is under construction**_ These parameters are listed under the `[other preferences]` section in the `.ini` file. --- -#### initial-reservoir-levels -[//]: # (TODO: complete the usage paragraph) +#### initial-reservoir-levels (DEPRECATED since 9.2: cold start is default behavior) - **Expected value:** one of the following (case-insensitive): - `cold start` - `hot start` diff --git a/docs/user-guide/solver/06-hydro-heuristics.md b/docs/user-guide/solver/06-hydro-heuristics.md index 4d4de31f48..78ff065933 100644 --- a/docs/user-guide/solver/06-hydro-heuristics.md +++ b/docs/user-guide/solver/06-hydro-heuristics.md @@ -188,6 +188,6 @@ $$V_t^- + S_t \geq \underline{S_t}$$ $$Y - V_t^- \geq 0$$ -[^monthly_allocation]: In the first equation, $S_{t-1}$ is either the initial stock $S_0$ or the final stock of the previous year (hydro hot start) +[^monthly_allocation]: In the first equation, $S_{t-1}$ is equal to initial stock $S_0$ [^daily_allocation]: In the first equation, $S_{t-1}$ is either the initial stock used in M or the final stock of the previous month ($D(m-1)$) diff --git a/docs/user-guide/solver/09-appendix.md b/docs/user-guide/solver/09-appendix.md index 3ab1b49f6d..1f8e24673d 100644 --- a/docs/user-guide/solver/09-appendix.md +++ b/docs/user-guide/solver/09-appendix.md @@ -64,9 +64,9 @@ With `..._MPS` options, the full expression of the faulty problem(s) is printed thus allowing further analysis of the infeasibility issue. -## Details on the "initial-reservoir-levels" parameter -[//]: # (TODO: update this paragraph) -_**This section is under construction**_ +## Details on the "initial-reservoir-levels" parameter (DEPRECATED since 9.2) + +### version 9.2: The reservoir level is now always determined with cold start behavior. This parameter can take the two values "cold start" or "hot start". [default: cold start]. Simulations results may in some circumstances be heavily impacted by this setting, hence proper attention should be paid to its meaning before considering changing the default value. diff --git a/simtest.json b/simtest.json index 9029e0c9bb..422fd8e3bd 100644 --- a/simtest.json +++ b/simtest.json @@ -1,3 +1,3 @@ { - "version": "v9.1.0-rc5" + "version": "v9.2.0a" } diff --git a/src/libs/antares/study/fwd.cpp b/src/libs/antares/study/fwd.cpp index 68dae733e6..100a4b127e 100644 --- a/src/libs/antares/study/fwd.cpp +++ b/src/libs/antares/study/fwd.cpp @@ -91,43 +91,6 @@ const char* SeedToID(SeedIndex seed) return ""; } -// ... Initial reservoir levels ... -InitialReservoirLevels StringToInitialReservoirLevels(const AnyString& text) -{ - if (!text) - { - return irlUnknown; - } - - CString<24, false> s = text; - s.trim(); - s.toLower(); - if (s == "cold start") - { - return irlColdStart; - } - if (s == "hot start") - { - return irlHotStart; - } - - return irlUnknown; -} - -const char* InitialReservoirLevelsToCString(InitialReservoirLevels iniLevels) -{ - switch (iniLevels) - { - case irlColdStart: - return "cold start"; - case irlHotStart: - return "hot start"; - case irlUnknown: - return ""; - } - return ""; -} - // ... Hydro heuristic policy ... HydroHeuristicPolicy StringToHydroHeuristicPolicy(const AnyString& text) { diff --git a/src/libs/antares/study/include/antares/study/fwd.h b/src/libs/antares/study/include/antares/study/fwd.h index 56b6e3982b..6fa2819aca 100644 --- a/src/libs/antares/study/include/antares/study/fwd.h +++ b/src/libs/antares/study/include/antares/study/fwd.h @@ -382,24 +382,6 @@ const char* SeedToCString(SeedIndex seed); */ const char* SeedToID(SeedIndex seed); -// ... Initial reservoir levels ... -enum InitialReservoirLevels -{ - irlColdStart = 0, - irlHotStart, - irlUnknown, -}; - -/*! -** \brief Convert an Initial Reservoir Levels strategy into a text -*/ -const char* InitialReservoirLevelsToCString(InitialReservoirLevels iniLevels); - -/*! -** \brief Convert a text into an Initial Reservoir Levels strategy -*/ -InitialReservoirLevels StringToInitialReservoirLevels(const AnyString& text); - // ... Hydro heuristic policy ... enum HydroHeuristicPolicy { diff --git a/src/libs/antares/study/include/antares/study/parameters.h b/src/libs/antares/study/include/antares/study/parameters.h index fcbb8b0080..d9a1d467f0 100644 --- a/src/libs/antares/study/include/antares/study/parameters.h +++ b/src/libs/antares/study/include/antares/study/parameters.h @@ -463,12 +463,6 @@ class Parameters final RenewableGeneration renewableGeneration; - struct - { - //! Initial reservoir levels - InitialReservoirLevels iniLevels; - } initialReservoirLevels; - struct { //! Hydro heuristic policy @@ -481,11 +475,6 @@ class Parameters final HydroPricingMode hpMode; } hydroPricing; - // In case of hydro hot start and MC years simultaneous run - // ... Answers the question : do all sets of simultaneous years have the same size ? - // (obvious if the parallel mode is not required : answer is yes). - bool allSetsHaveSameSize; - //! Transmission capacities GlobalTransmissionCapacities transmissionCapacities; //! Simplex optimization range (day/week) diff --git a/src/libs/antares/study/include/antares/study/parts/hydro/container.h b/src/libs/antares/study/include/antares/study/parts/hydro/container.h index 5d79c65d30..e7467d9fb7 100644 --- a/src/libs/antares/study/include/antares/study/parts/hydro/container.h +++ b/src/libs/antares/study/include/antares/study/parts/hydro/container.h @@ -167,10 +167,9 @@ class PartHydro // As this function can be called a lot of times, we pass working variables and returned variables // as arguments, so that we don't have to create them locally (as in a classical function) each // time. -void getWaterValue(const double& level, +double getWaterValue(const double& level, const Matrix& waterValues, - const uint day, - double& waterValueToReturn); + const uint day); // Interpolates a rate from the credit modulation table according to a level double getWeeklyModulation(const double& level /* format : in % of reservoir capacity */, diff --git a/src/libs/antares/study/include/antares/study/study.h b/src/libs/antares/study/include/antares/study/study.h index cbe251805b..709bab62fe 100644 --- a/src/libs/antares/study/include/antares/study/study.h +++ b/src/libs/antares/study/include/antares/study/study.h @@ -421,22 +421,6 @@ class Study: public Yuni::NonCopyable, public LayerData */ void getNumberOfCores(const bool forceParallel, const uint nbYearsParallelForced); - /*! - ** \brief In case hydro hot start is enabled, checking all conditions are met. - ** - ** If hydro hot start is enabled, check that : - ** - For all areas for which reservoir management is enabled : - ** + Their starting level is initialized on the same day - ** + This day is the first day of the simulation calendar - ** - The simulation lasts exactly one year - ** - All batches (or sets) of simultaneous years have the same size (obvious if a parallel run - *is not required : answer is yes). - ** - ** If these conditions are not met, some error message is raised, when attempting to run the - *study. - */ - bool checkHydroHotStart(); - /*! ** \brief Remove timeseries if ts-generator is enabled */ diff --git a/src/libs/antares/study/load.cpp b/src/libs/antares/study/load.cpp index eefe3b2606..dca1b1eb15 100644 --- a/src/libs/antares/study/load.cpp +++ b/src/libs/antares/study/load.cpp @@ -166,13 +166,6 @@ void Study::parameterFiller(const StudyLoadOptions& options) parameters.firstMonthInYear, parameters.leapYear}); - // In case hydro hot start is enabled, check all conditions are met. - // (has to be called after areas load and calendar building) - if (usedByTheSolver && !checkHydroHotStart()) - { - logs.error() << "hydro hot start is enabled, conditions are not met. Aborting"; - } - // Reducing memory footprint reduceMemoryUsage(); } diff --git a/src/libs/antares/study/parameters.cpp b/src/libs/antares/study/parameters.cpp index 3ac64a284e..3517b09d21 100644 --- a/src/libs/antares/study/parameters.cpp +++ b/src/libs/antares/study/parameters.cpp @@ -311,15 +311,11 @@ void Parameters::reset() readonly = false; synthesis = true; - // Initial reservoir levels - initialReservoirLevels.iniLevels = irlColdStart; - // Hydro heuristic policy hydroHeuristicPolicy.hhPolicy = hhpAccommodateRuleCurves; // Hydro pricing hydroPricing.hpMode = hpHeuristic; - allSetsHaveSameSize = true; // Shedding strategies power.fluctuations = lssFreeModulations; @@ -810,19 +806,6 @@ static bool SGDIntLoadFamily_OtherPreferences(Parameters& d, d.hydroPricing.hpMode = hpHeuristic; return false; } - if (key == "initial-reservoir-levels") - { - auto iniLevels = StringToInitialReservoirLevels(value); - if (iniLevels != irlUnknown) - { - d.initialReservoirLevels.iniLevels = iniLevels; - return true; - } - logs.warning() << "parameters: invalid initital reservoir levels mode. Got '" << value - << "'. reset to cold start mode."; - d.initialReservoirLevels.iniLevels = irlColdStart; - return false; - } if (key == "number-of-cores-mode") { @@ -1155,6 +1138,15 @@ static bool SGDIntLoadFamily_Legacy(Parameters& d, return true; } + if (key == "initial-reservoir-levels") // ignored since 9.2 + { + if (version >= StudyVersion(9,2)) + logs.warning() << "Option initial-reservoir-levels is deprecated, please remove it from the study"; + else if (value == "hot start") + logs.warning() << "Hydro hot start not supported with this solver, please use a version < 9.2"; + return true; + } + return false; } @@ -1874,8 +1866,6 @@ void Parameters::saveToINI(IniFile& ini) const // Other preferences { auto* section = ini.addSection("other preferences"); - section->add("initial-reservoir-levels", - InitialReservoirLevelsToCString(initialReservoirLevels.iniLevels)); section->add("hydro-heuristic-policy", HydroHeuristicPolicyToCString(hydroHeuristicPolicy.hhPolicy)); section->add("hydro-pricing-mode", HydroPricingModeToCString(hydroPricing.hpMode)); diff --git a/src/libs/antares/study/parts/hydro/container.cpp b/src/libs/antares/study/parts/hydro/container.cpp index e86b32a568..103d7a83bb 100644 --- a/src/libs/antares/study/parts/hydro/container.cpp +++ b/src/libs/antares/study/parts/hydro/container.cpp @@ -938,10 +938,9 @@ bool PartHydro::CheckDailyMaxEnergy(const AnyString& areaName) return ret; } -void getWaterValue(const double& level /* format : in % of reservoir capacity */, +double getWaterValue(const double& level /* format : in % of reservoir capacity */, const Matrix& waterValues, - const uint day, - double& waterValueToReturn) + const uint day) { assert((level >= 0. && level <= 100.) && "getWaterValue function : invalid level"); double levelUp = ceil(level); @@ -949,13 +948,10 @@ void getWaterValue(const double& level /* format : in % of reservoir capacity */ if ((int)(levelUp) == (int)(levelDown)) { - waterValueToReturn = waterValues[(int)(levelUp)][day]; - } - else - { - waterValueToReturn = waterValues[(int)(levelUp)][day] * (level - levelDown) - + waterValues[(int)(levelDown)][day] * (levelUp - level); + return waterValues[(int)(levelUp)][day]; } + return waterValues[(int)(levelUp)][day] * (level - levelDown) + + waterValues[(int)(levelDown)][day] * (levelUp - level); } double getWeeklyModulation(const double& level /* format : in % of reservoir capacity */, diff --git a/src/libs/antares/study/study.cpp b/src/libs/antares/study/study.cpp index 8337795ca1..23f547783d 100644 --- a/src/libs/antares/study/study.cpp +++ b/src/libs/antares/study/study.cpp @@ -500,97 +500,6 @@ void Study::getNumberOfCores(const bool forceParallel, const uint nbYearsParalle // enabled. // Useful for RAM estimation. maxNbYearsInParallel_save = maxNbYearsInParallel; - - // Here we answer the question (useful only if hydro hot start is asked) : do all sets of - // parallel years have the same size ? - if (parameters.initialReservoirLevels.iniLevels == Antares::Data::irlHotStart - && setsOfParallelYears.size() && maxNbYearsInParallel > 1) - { - uint currentSetSize = (uint)setsOfParallelYears[0].size(); - if (setsOfParallelYears.size() > 1) - { - for (uint s = 1; s < setsOfParallelYears.size(); s++) - { - if (setsOfParallelYears[s].size() != currentSetSize) - { - parameters.allSetsHaveSameSize = false; - break; - } - } - } - } // End if hot start -} - -bool Study::checkHydroHotStart() -{ - bool hydroHotStart = (parameters.initialReservoirLevels.iniLevels == irlHotStart); - - // No need to check further if hydro hot start is not required - if (!hydroHotStart) - { - return true; - } - - // Here we answer the question (useful only if hydro hot start is asked) : In case of parallel - // run, do all sets of parallel years have the same size ? - if (maxNbYearsInParallel != 1 && !parameters.allSetsHaveSameSize) - { - logs.error() << "Hot Start Hydro option : conflict with parallelization parameters."; - logs.error() - << "Please update relevant simulation parameters or use Cold Start option. "; - return false; - } - - // Checking calendar conditions - // ... The simulation lasts one year exactly - uint nbDaysInSimulation = parameters.simulationDays.end - parameters.simulationDays.first + 1; - if (nbDaysInSimulation < 364) - { - logs.error() - << "Hot Start Hydro option : simulation calendar must cover one complete year. "; - logs.error() << "Please update data or use Cold Start option."; - return false; - } - - // ... For all areas for which reservoir management is enabled : - // - Their starting level is initialized on the same day - // - This day is the first day of the simulation calendar - const Area::Map::iterator end = areas.end(); - for (Area::Map::iterator i = areas.begin(); i != end; ++i) - { - // Reference to the area - Area* area = i->second; - - // No need to make a check on level initialization when reservoir management - // is not activated for the current area - if (!area->hydro.reservoirManagement) - { - continue; - } - - // Month the reservoir level is initialized according to. - // This month number is given in the civil calendar, from january to december (0 is - // january). - int initLevelOnMonth = area->hydro.initializeReservoirLevelDate; - - // Conversion of the previous month into simulation calendar - uint initLevelOnSimMonth = calendar.mapping.months[initLevelOnMonth]; - - // Previous month's first day in the year - uint initLevelOnSimDay = calendar.months[initLevelOnSimMonth].daysYear.first; - - // Check the day of level initialization is the first day of simulation - if (initLevelOnSimDay != parameters.simulationDays.first) - { - logs.error() - << "Hot Start Hydro option : area '" << area->name - << "' - hydro level must be initialized on the first simulation month. "; - logs.error() << "Please update data or use Cold Start option."; - return false; - } - } // End loop over areas - - return true; } bool Study::initializeRuntimeInfos() diff --git a/src/solver/simulation/adequacy.cpp b/src/solver/simulation/adequacy.cpp index 63d8462dfc..2b03f7bcdc 100644 --- a/src/solver/simulation/adequacy.cpp +++ b/src/solver/simulation/adequacy.cpp @@ -361,8 +361,6 @@ bool Adequacy::year(Progression::Task& progression, ++progression; } - updatingAnnualFinalHydroLevel(study.areas, currentProblem); - optWriter.finalize(); finalizeOptimizationStatistics(currentProblem, state); diff --git a/src/solver/simulation/common-hydro-levels.cpp b/src/solver/simulation/common-hydro-levels.cpp index 8564418f62..b322d62dec 100644 --- a/src/solver/simulation/common-hydro-levels.cpp +++ b/src/solver/simulation/common-hydro-levels.cpp @@ -105,12 +105,8 @@ void interpolateWaterValue(const Data::AreaList& areas, RESULTATS_HORAIRES& weeklyResults = problem.ResultatsHoraires[index]; - std::vector& waterVal = weeklyResults.valeurH2oHoraire; - - for (uint h = 0; h < nbHoursInAWeek; h++) - { - waterVal[h] = 0.; - } + auto& waterVal = weeklyResults.valeurH2oHoraire; + std::fill(waterVal.begin(), waterVal.end(), 0.); if (!area.hydro.reservoirManagement || !area.hydro.useWaterValue) { @@ -126,17 +122,16 @@ void interpolateWaterValue(const Data::AreaList& areas, std::vector& niv = weeklyResults.niveauxHoraires; - Antares::Data::getWaterValue(problem.previousSimulationFinalLevel[index] * 100 - / reservoirCapacity, - area.hydro.waterValues, - weekFirstDay, - waterVal[0]); + waterVal[0] = Data::getWaterValue(problem.previousSimulationFinalLevel[index] * 100 + / reservoirCapacity, + area.hydro.waterValues, + weekFirstDay); + for (uint h = 1; h < nbHoursInAWeek; h++) { - Antares::Data::getWaterValue(niv[h - 1], - area.hydro.waterValues, - daysOfWeek[h / 24], - waterVal[h]); + waterVal[h] = Data::getWaterValue(niv[h - 1], + area.hydro.waterValues, + daysOfWeek[h / 24]); } }); } @@ -164,28 +159,4 @@ void updatingWeeklyFinalHydroLevel(const Data::AreaList& areas, PROBLEME_HEBDO& }); } -void updatingAnnualFinalHydroLevel(const Data::AreaList& areas, PROBLEME_HEBDO& problem) -{ - if (!problem.hydroHotStart) - { - return; - } - - areas.each( - [&](const Data::Area& area) - { - if (!area.hydro.reservoirManagement) - { - return; - } - - uint index = area.index; - - double reservoirCapacity = area.hydro.reservoirCapacity; - - problem.previousYearFinalLevels[index] = problem.previousSimulationFinalLevel[index] - / reservoirCapacity; - }); -} - } // namespace Antares::Solver::Simulation diff --git a/src/solver/simulation/economy.cpp b/src/solver/simulation/economy.cpp index 02fac95314..934ebc4c2c 100644 --- a/src/solver/simulation/economy.cpp +++ b/src/solver/simulation/economy.cpp @@ -224,8 +224,6 @@ bool Economy::year(Progression::Task& progression, ++progression; } - updatingAnnualFinalHydroLevel(study.areas, currentProblem); - optWriter.finalize(); finalizeOptimizationStatistics(currentProblem, state); diff --git a/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h b/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h index 14d260af09..6ea4a9e003 100644 --- a/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h +++ b/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h @@ -141,14 +141,6 @@ void interpolateWaterValue(const Data::AreaList& areas, */ void updatingWeeklyFinalHydroLevel(const Data::AreaList& areas, PROBLEME_HEBDO& problem); -/* -** \brief Updating the year final reservoir level, to be used as a start for the year. -** -** \param areas : the areas of study -** \param problem The weekly problem, living over the whole simuation. -*/ -void updatingAnnualFinalHydroLevel(const Data::AreaList& areas, PROBLEME_HEBDO& problem); - /* ** \brief Compute the weighted average NTC for a link ** diff --git a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h index 8f74ccb94a..66400ed678 100644 --- a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h +++ b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h @@ -559,7 +559,6 @@ struct PROBLEME_HEBDO bool YaDeLaReserveJmoins1 = false; - std::vector previousYearFinalLevels; std::vector AllMustRunGeneration; OptimizationStatistics optimizationStatistics[2]; @@ -569,7 +568,6 @@ struct PROBLEME_HEBDO /* Hydro management */ std::vector CoefficientEcretementPMaxHydraulique; - bool hydroHotStart = false; std::vector previousSimulationFinalLevel; /* Results */ diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.h b/src/solver/simulation/include/antares/solver/simulation/solver.h index 29c087759f..fe16b660b9 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.h +++ b/src/solver/simulation/include/antares/solver/simulation/solver.h @@ -145,8 +145,6 @@ class ISimulation: public Impl uint pNbMaxPerformedYearsInParallel; //! Year by year output results bool pYearByYear; - //! Hydro hot start - bool pHydroHotStart; //! The first set of parallel year(s) with a performed year was already run ? bool pFirstSetParallelWithAPerformedYearWasRun; diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index 87f38225cc..adf8c23c4d 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -77,7 +77,6 @@ public: study.calendar, resultWriter) { - hydroHotStart = (study.parameters.initialReservoirLevels.iniLevels == Data::irlHotStart); scratchmap = study.areas.buildScratchMap(numSpace); } @@ -97,7 +96,6 @@ private: Data::Study& study; Variable::State& state; bool yearByYear; - bool hydroHotStart; Benchmarking::DurationCollector& pDurationCollector; IResultWriter& pResultWriter; HydroManagement hydroManagement; @@ -152,14 +150,7 @@ public: double* randomReservoirLevel = nullptr; // 1 - Applying random levels for current year - if (hydroHotStart && firstSetParallelWithAPerformedYearWasRun) - { - randomReservoirLevel = state.problemeHebdo->previousYearFinalLevels.data(); - } - else - { - randomReservoirLevel = randomForCurrentYear.pReservoirLevels; - } + randomReservoirLevel = randomForCurrentYear.pReservoirLevels; // 2 - Preparing the Time-series numbers // removed @@ -265,8 +256,6 @@ inline ISimulation::ISimulation( { pYearByYear = false; } - - pHydroHotStart = (study.parameters.initialReservoirLevels.iniLevels == Data::irlHotStart); } template @@ -765,34 +754,12 @@ void ISimulation::computeRandomNumbers( } } - if (pHydroHotStart) - { - if (!isPerformed || !area.hydro.reservoirManagement) - { - // This initial level should be unused, so -1, as impossible value, is - // suitable. - randomForYears.pYears[indexYear].pReservoirLevels[areaIndex] = -1.; - areaIndex++; - return; // Skipping the current area - } - - if (!pFirstSetParallelWithAPerformedYearWasRun) - { - randomForYears.pYears[indexYear].pReservoirLevels[areaIndex] = randomLevel; - } - // Else : means the start levels (multiple areas are affected) of a year are - // retrieved from a previous year and - // these levels are updated inside the year job (see year job). - } - else + // Current area's hydro starting (or initial) level computation + // (no matter if the year is performed or not, we always draw a random initial + // reservoir level to ensure the same results) + if (isPerformed) { - // Current area's hydro starting (or initial) level computation - // (no matter if the year is performed or not, we always draw a random initial - // reservoir level to ensure the same results) - if (isPerformed) - { - randomForYears.pYears[indexYear].pReservoirLevels[areaIndex] = randomLevel; - } + randomForYears.pYears[indexYear].pReservoirLevels[areaIndex] = randomLevel; } areaIndex++; diff --git a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp index 860d2b8bfe..03126fa752 100644 --- a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp +++ b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp @@ -119,20 +119,6 @@ void SIM_AllocationProblemeDonneesGenerales(PROBLEME_HEBDO& problem, problem.ShortTermStorage.resize(nbPays); - problem.previousYearFinalLevels.resize(0); - if (problem.hydroHotStart) - { - for (uint i = 0; i <= nbPays; i++) - { - auto& area = *(study.areas[i]); - if (area.hydro.reservoirManagement) - { - problem.previousYearFinalLevels.assign(nbPays, 0.); - break; - } - } - } - problem.ReserveJMoins1.resize(nbPays); problem.ResultatsHoraires.resize(nbPays); diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index c51d8283dc..155cb6b3bc 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -81,9 +81,6 @@ void SIM_InitialisationProblemeHebdo(Data::Study& study, problem.Expansion = (parameters.mode == Data::SimulationMode::Expansion); problem.firstWeekOfSimulation = false; - problem.hydroHotStart = (parameters.initialReservoirLevels.iniLevels - == Antares::Data::irlHotStart); - // gp adq : to be removed if (parameters.adqPatchParams.enabled) { @@ -210,11 +207,6 @@ void SIM_InitialisationProblemeHebdo(Data::Study& study, problem.previousSimulationFinalLevel[i] = -1.; - if (!problem.previousYearFinalLevels.empty()) - { - problem.previousYearFinalLevels[i] = -1.; - } - problem.CaracteristiquesHydrauliques[i].WeeklyWaterValueStateRegular = 0.; problem.CaracteristiquesHydrauliques[i].WeeklyGeneratingModulation = 1.; @@ -521,11 +513,11 @@ void SIM_RenseignementProblemeHebdo(const Study& study, if (area.hydro.useWaterValue) { - Antares::Data::getWaterValue( - problem.previousSimulationFinalLevel[k] * 100 / area.hydro.reservoirCapacity, - area.hydro.waterValues, - weekFirstDay, - problem.CaracteristiquesHydrauliques[k].WeeklyWaterValueStateRegular); + problem.CaracteristiquesHydrauliques[k].WeeklyWaterValueStateRegular = + getWaterValue( + problem.previousSimulationFinalLevel[k] * 100 / area.hydro.reservoirCapacity, + area.hydro.waterValues, + weekFirstDay); } if (problem.CaracteristiquesHydrauliques[k].PresenceDHydrauliqueModulable > 0) diff --git a/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp b/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp index f690649668..14ee6a4435 100644 --- a/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp +++ b/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp @@ -189,7 +189,6 @@ void Fixture::writeValidFile() 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 diff --git a/src/ui/simulator/windows/options/advanced/advanced.cpp b/src/ui/simulator/windows/options/advanced/advanced.cpp index 97b0589bd4..a361fc227b 100644 --- a/src/ui/simulator/windows/options/advanced/advanced.cpp +++ b/src/ui/simulator/windows/options/advanced/advanced.cpp @@ -156,14 +156,12 @@ AdvancedParameters::AdvancedParameters(wxWindow* parent) : // Initial reservoir levels { label = Component::CreateLabel(this, wxT("Initial reservoir levels")); - button = new Component::Button(this, wxT("cold start"), "images/16x16/tag.png"); + button = new Component::Button(this, wxT(""), "images/16x16/tag.png"); button->SetBackgroundColour(bgColor); - button->menu(true); - onPopup.bind(this, &AdvancedParameters::onInitialReservoirLevels); button->onPopupMenu(onPopup); + button->caption("hydro hot start deprecated"); s->Add(label, 0, wxRIGHT | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); s->Add(button, 0, wxLEFT | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - pBtnInitialReservoirLevels = button; } // Hydro heuristic policy { @@ -327,7 +325,6 @@ void AdvancedParameters::onResetToDefault(void*) parameters.timeSeriesAccuracyOnCorrelation &= ~Data::timeSeriesWind; parameters.timeSeriesAccuracyOnCorrelation &= ~Data::timeSeriesSolar; - parameters.initialReservoirLevels.iniLevels = Data::irlColdStart; parameters.hydroHeuristicPolicy.hhPolicy = Data::hhpAccommodateRuleCurves; parameters.hydroPricing.hpMode = Data::hpHeuristic; parameters.power.fluctuations = Data::lssFreeModulations; @@ -375,10 +372,6 @@ void AdvancedParameters::refresh() wxString text; - text = wxStringFromUTF8( - InitialReservoirLevelsToCString(study.parameters.initialReservoirLevels.iniLevels)); - pBtnInitialReservoirLevels->caption(text); - text = wxStringFromUTF8( HydroHeuristicPolicyToCString(study.parameters.hydroHeuristicPolicy.hhPolicy)); pBtnHydroHeuristicPolicy->caption(text); @@ -508,58 +501,6 @@ void AdvancedParameters::onSelectNumericQualityHigh(wxCommandEvent&) } } -void AdvancedParameters::onInitialReservoirLevels(Component::Button&, wxMenu& menu, void*) -{ - wxMenuItem* it; - wxString text; - - text = wxStringFromUTF8(InitialReservoirLevelsToCString(Data::irlColdStart)); - text << wxT(" [default]"); - it = Menu::CreateItem(&menu, wxID_ANY, text, "images/16x16/tag.png"); - menu.Connect(it->GetId(), - wxEVT_COMMAND_MENU_SELECTED, - wxCommandEventHandler(AdvancedParameters::onSelectColdStart), - nullptr, - this); - - text.clear(); - text << wxStringFromUTF8(InitialReservoirLevelsToCString(Data::irlHotStart)); - it = Menu::CreateItem(&menu, wxID_ANY, text, "images/16x16/tag.png"); - menu.Connect(it->GetId(), - wxEVT_COMMAND_MENU_SELECTED, - wxCommandEventHandler(AdvancedParameters::onSelectHotStart), - nullptr, - this); -} - -void AdvancedParameters::onSelectColdStart(wxCommandEvent&) -{ - if (not CurrentStudyIsValid()) - return; - auto& study = *GetCurrentStudy(); - - if (study.parameters.initialReservoirLevels.iniLevels != Data::irlColdStart) - { - study.parameters.initialReservoirLevels.iniLevels = Data::irlColdStart; - MarkTheStudyAsModified(); - refresh(); - } -} - -void AdvancedParameters::onSelectHotStart(wxCommandEvent&) -{ - if (not CurrentStudyIsValid()) - return; - auto& study = *GetCurrentStudy(); - - if (study.parameters.initialReservoirLevels.iniLevels != Data::irlHotStart) - { - study.parameters.initialReservoirLevels.iniLevels = Data::irlHotStart; - MarkTheStudyAsModified(); - refresh(); - } -} - // ... Hydro heuristic policy void AdvancedParameters::onHydroHeuristicPolicy(Component::Button&, wxMenu& menu, void*) { diff --git a/src/ui/simulator/windows/options/advanced/advanced.h b/src/ui/simulator/windows/options/advanced/advanced.h index a42d87c9c8..1410d05595 100644 --- a/src/ui/simulator/windows/options/advanced/advanced.h +++ b/src/ui/simulator/windows/options/advanced/advanced.h @@ -69,11 +69,7 @@ class AdvancedParameters final : public wxDialog void onNumericQuality(Component::Button&, wxMenu&, void*, Data::TimeSeriesType ts); void onSelectNumericQualityStandard(wxCommandEvent& evt); void onSelectNumericQualityHigh(wxCommandEvent& evt); - - void onInitialReservoirLevels(Component::Button&, wxMenu&, void*); - void onSelectHotStart(wxCommandEvent& evt); - void onSelectColdStart(wxCommandEvent& evt); - + void onHydroHeuristicPolicy(Component::Button&, wxMenu& menu, void*); void onSelectAccomodateRuleCurves(wxCommandEvent& evt); void onSelectMaximizeGeneration(wxCommandEvent& evt); @@ -115,7 +111,6 @@ class AdvancedParameters final : public wxDialog Component::Button* pBtnNumericQualityWind; Component::Button* pBtnNumericQualitySolar; Component::Button* pBtnPowerFluctuations; - Component::Button* pBtnInitialReservoirLevels; Component::Button* pBtnHydroHeuristicPolicy; Component::Button* pBtnHydroPricing; Component::Button* pBtnSheddingPolicy; From 08003e3275588c4dff4edb7767951e0063f0ed85 Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:16:08 +0200 Subject: [PATCH 04/15] Move data validation to specialized functions [ANT-1213] (#2149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The goal of this PR is to separate loading and validation of data. This separation will allow to have a modular loading. Slight change for admissible values of reservoir capacity (from ]1e-6, +oo[ to ]0, +oo[). --------- Co-authored-by: Florian Omnès Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> --- src/libs/antares/study/area/list.cpp | 2 + .../antares/study/parts/hydro/container.h | 14 +- .../antares/study/parts/hydro/prepro.h | 3 +- .../antares/study/parts/hydro/container.cpp | 600 ++++++------------ .../parts/hydro/hydromaxtimeseriesreader.cpp | 34 +- src/libs/antares/study/parts/hydro/prepro.cpp | 107 ++-- 6 files changed, 270 insertions(+), 490 deletions(-) diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index e7bd727287..417e60bae4 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -944,6 +944,7 @@ static bool AreaListLoadFromFolderSingleArea(Study& study, // if changes are required, please update reloadXCastData() buffer.clear() << study.folderInput << SEP << "hydro" << SEP << "prepro"; ret = area.hydro.prepro->loadFromFolder(study, area.id, buffer.c_str()) && ret; + ret = area.hydro.prepro->validate(area.id) && ret; } auto* hydroSeries = area.hydro.series; @@ -1160,6 +1161,7 @@ bool AreaList::loadFromFolder(const StudyLoadOptions& options) logs.info() << "Loading global hydro data..."; buffer.clear() << pStudy.folderInput << SEP << "hydro"; ret = PartHydro::LoadFromFolder(pStudy, buffer) && ret; + ret = PartHydro::validate(pStudy) && ret; } // Thermal data, specific to areas diff --git a/src/libs/antares/study/include/antares/study/parts/hydro/container.h b/src/libs/antares/study/include/antares/study/parts/hydro/container.h index e7467d9fb7..0297540a4d 100644 --- a/src/libs/antares/study/include/antares/study/parts/hydro/container.h +++ b/src/libs/antares/study/include/antares/study/parts/hydro/container.h @@ -52,7 +52,6 @@ class PartHydro pumpMod, }; -public: /*! ** \brief Load data for hydro container from a folder ** @@ -61,6 +60,13 @@ class PartHydro */ static bool LoadFromFolder(Study& study, const AnyString& folder); + /*! + ** \brief Check and validate the loaded datas + ** + ** \return A non-zero value if the operation succeeded, 0 otherwise + */ + static bool validate(Study& study); + /*! ** \brief Save data from several containers to a folder (except data for the prepro and *time-series) @@ -71,7 +77,6 @@ class PartHydro */ static bool SaveToFolder(const AreaList& areas, const AnyString& folder); -public: /*! ** \brief Default Constructor */ @@ -100,7 +105,6 @@ class PartHydro bool CheckDailyMaxEnergy(const AnyString& areaName); -public: //! Inter-daily breakdown (previously called Smoothing Factor or alpha) double interDailyBreakdown; //! Intra-daily modulation @@ -161,6 +165,10 @@ class PartHydro Matrix dailyNbHoursAtGenPmax; Matrix dailyNbHoursAtPumpPmax; +private: + static bool checkReservoirLevels(const Study& study); + static bool checkProperties(Study& study); + }; // class PartHydro // Interpolates a water value from a table according to a level and a day. diff --git a/src/libs/antares/study/include/antares/study/parts/hydro/prepro.h b/src/libs/antares/study/include/antares/study/parts/hydro/prepro.h index dbc015ecce..b35798f862 100644 --- a/src/libs/antares/study/include/antares/study/parts/hydro/prepro.h +++ b/src/libs/antares/study/include/antares/study/parts/hydro/prepro.h @@ -95,8 +95,9 @@ class PreproHydro ** \param folder The source folder (ex: `input/hydro/prepro`) ** \return A non-zero value if the operation succeeded, 0 otherwise */ - bool loadFromFolder(Study& s, const AreaName& areaID, const char folder[]); + bool loadFromFolder(Study& s, const AreaName& areaID, const std::string& folder); + bool validate(const std::string& areaID); /*! ** \brief Save hydro settings for the prepro into a folder ** diff --git a/src/libs/antares/study/parts/hydro/container.cpp b/src/libs/antares/study/parts/hydro/container.cpp index 103d7a83bb..0343c2878f 100644 --- a/src/libs/antares/study/parts/hydro/container.cpp +++ b/src/libs/antares/study/parts/hydro/container.cpp @@ -106,6 +106,37 @@ void PartHydro::reset() } } +template +static bool loadProperties(Study& study, + IniFile::Property* property, + const std::string& filename, + T PartHydro::*ptr) +{ + if (!property) + return false; + + bool ret = true; + + // Browse all properties + for (; property; property = property->next) + { + AreaName id = property->key; + id.toLower(); + + Area* area = study.areas.find(id); + if (area) + { + ret = property->value.to(area->hydro.*ptr) && ret; + } + else + { + logs.warning() << filename << ": `" << id << "`: Unknown area"; + return false; + } + } + return ret; +} + bool PartHydro::LoadFromFolder(Study& study, const AnyString& folder) { auto& buffer = study.bufferLoadingTS; @@ -200,46 +231,6 @@ bool PartHydro::LoadFromFolder(Study& study, const AnyString& folder) Matrix<>::optFixedSize, &study.dataBuffer) && ret; - - if (study.usedByTheSolver) - { - auto& col = area.hydro.inflowPattern[0]; - bool errorInflow = false; - for (unsigned int day = 0; day < DAYS_PER_YEAR; day++) - { - if (col[day] < 0 && !errorInflow) - { - logs.error() << area.name << ": invalid inflow value"; - errorInflow = true; - ret = false; - } - } - bool errorLevels = false; - auto& colMin = area.hydro.reservoirLevel[minimum]; - auto& colAvg = area.hydro.reservoirLevel[average]; - auto& colMax = area.hydro.reservoirLevel[maximum]; - for (unsigned int day = 0; day < DAYS_PER_YEAR; day++) - { - if (!errorLevels - && (colMin[day] < 0 || colAvg[day] < 0 || colMin[day] > colMax[day] - || colAvg[day] > 100 || colMax[day] > 100)) - { - logs.error() << area.name << ": invalid reservoir level value"; - errorLevels = true; - ret = false; - } - } - - for (int i = 0; i < 101; i++) - { - if ((area.hydro.creditModulation[i][0] < 0) - || (area.hydro.creditModulation[i][1] < 0)) - { - logs.error() << area.name << ": invalid credit modulation value"; - ret = false; - } - } - } }); IniFile ini; @@ -248,432 +239,217 @@ bool PartHydro::LoadFromFolder(Study& study, const AnyString& folder) return false; } - const char* const sectionName = "inter-daily-breakdown"; + if (IniFile::Section* section = ini.find("inter-daily-breakdown")) + { + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::interDailyBreakdown) && ret; + } - IniFile::Section* section; - IniFile::Property* property; + if (IniFile::Section* section = ini.find("intra-daily-modulation")) + { + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::intraDailyModulation) && ret; + } - if ((section = ini.find(sectionName))) + if (IniFile::Section* section = ini.find("reservoir")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - Area* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.interDailyBreakdown) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::reservoirManagement) && ret; } - if ((section = ini.find("intra-daily-modulation"))) + if (IniFile::Section* section = ini.find("reservoir capacity")) { - if ((property = section->firstProperty)) - { - AreaName id; + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::reservoirCapacity) && ret; + } - // Browse all properties - for (; property; property = property->next) - { - id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.intraDailyModulation) && ret; - if (area->hydro.intraDailyModulation < 1.) - { - logs.error() - << area->id << ": Invalid intra-daily modulation. It must be >= 1.0, Got " - << area->hydro.intraDailyModulation << " (truncated to 1)"; - area->hydro.intraDailyModulation = 1.; - } - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + if (IniFile::Section* section = ini.find("follow load")) + { + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::followLoadModulations) && ret; } - if ((section = ini.find("reservoir"))) + if (IniFile::Section* section = ini.find("use water")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.reservoirManagement) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::useWaterValue) && ret; } - if ((section = ini.find("reservoir capacity"))) + if (IniFile::Section* section = ini.find("hard bounds")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.reservoirCapacity) && ret; - if (area->hydro.reservoirCapacity < 1e-6) - { - logs.error() << area->id << ": Invalid reservoir capacity."; - area->hydro.reservoirCapacity = 0.; - } - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::hardBoundsOnRuleCurves) && ret; } - // Check on reservoir capacity (has to be done after reservoir management and capacity reading, - // not before). Some areas reservoir capacities may not be printed in hydro ini file when saving - // the study, because they are too small (< 1e-6). We cannot have reservoir management = yes and - // capacity = 0 because of further division by capacity. reservoir management = no and capacity - // = 0 is possible (no use of capacity further) - study.areas.each( - [&](Data::Area& area) - { - if (area.hydro.reservoirCapacity < 1e-3 && area.hydro.reservoirManagement) - { - logs.error() << area.name - << ": reservoir capacity not defined. Impossible to manage."; - ret = false && ret; - } - }); + if (IniFile::Section* section = ini.find("use heuristic")) + { + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::useHeuristicTarget) && ret; + } - if ((section = ini.find("inter-monthly-breakdown"))) + if (IniFile::Section* section = ini.find("power to level")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.intermonthlyBreakdown) && ret; - if (area->hydro.intermonthlyBreakdown < 0) - { - logs.error() << area->id << ": Invalid intermonthly breakdown"; - area->hydro.intermonthlyBreakdown = 0.; - } - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::powerToLevel) && ret; } - if ((section = ini.find("follow load"))) + + if (IniFile::Section* section = ini.find("initialize reservoir date")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.followLoadModulations) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::initializeReservoirLevelDate) && ret; } - if ((section = ini.find("use water"))) + + if (IniFile::Section* section = ini.find("use leeway")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.useWaterValue) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::useLeeway) && ret; } - if ((section = ini.find("hard bounds"))) + + if (IniFile::Section* section = ini.find("leeway low")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.hardBoundsOnRuleCurves) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::leewayLowerBound) && ret; } - if ((section = ini.find("use heuristic"))) + + if (IniFile::Section* section = ini.find("leeway up")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.useHeuristicTarget) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::leewayUpperBound) && ret; } - if ((section = ini.find("power to level"))) + + if (IniFile::Section* section = ini.find("pumping efficiency")) { - if ((property = section->firstProperty)) - { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.powerToLevel) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } - } + ret = loadProperties(study, section->firstProperty, buffer, &PartHydro::pumpingEfficiency) && ret; } - if ((section = ini.find("initialize reservoir date"))) + + return ret; +} + +bool PartHydro::checkReservoirLevels(const Study& study) +{ + bool ret = true; + + for (const auto& [areaName, area] : study.areas) { - if ((property = section->firstProperty)) + if (!study.usedByTheSolver) + return true; + + auto& col = area->hydro.inflowPattern[0]; + bool errorInflow = false; + for (unsigned int day = 0; day < DAYS_PER_YEAR; day++) { - // Browse all properties - for (; property; property = property->next) + if (col[day] < 0 && !errorInflow) { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.initializeReservoirLevelDate) && ret; - if (area->hydro.initializeReservoirLevelDate < 0) - { - logs.error() << area->id << ": Invalid initialize reservoir date"; - area->hydro.initializeReservoirLevelDate = 0; - } - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } + logs.error() << areaName << ": invalid inflow value"; + errorInflow = true; + ret = false; } } - } - // Leeways : use leeway bounds (upper and lower) - if ((section = ini.find("use leeway"))) - { - if ((property = section->firstProperty)) + bool errorLevels = false; + auto& colMin = area->hydro.reservoirLevel[minimum]; + auto& colAvg = area->hydro.reservoirLevel[average]; + auto& colMax = area->hydro.reservoirLevel[maximum]; + for (unsigned int day = 0; day < DAYS_PER_YEAR; day++) { - // Browse all properties - for (; property; property = property->next) + if (!errorLevels + && (colMin[day] < 0 || colAvg[day] < 0 || colMin[day] > colMax[day] + || colAvg[day] > 100 || colMax[day] > 100)) { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.useLeeway) && ret; - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } + logs.error() << areaName << ": invalid reservoir level value"; + errorLevels = true; + ret = false; } } - } - if ((section = ini.find("leeway low"))) - { - if ((property = section->firstProperty)) + + for (int i = 0; i < 101; i++) { - // Browse all properties - for (; property; property = property->next) + if ((area->hydro.creditModulation[i][0] < 0) + || (area->hydro.creditModulation[i][1] < 0)) { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.leewayLowerBound) && ret; - if (area->hydro.leewayLowerBound < 0.) - { - logs.error() - << area->id << ": Invalid leeway lower bound. It must be >= 0.0, Got " - << area->hydro.leewayLowerBound; - area->hydro.leewayLowerBound = 0.; - } - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } + logs.error() << areaName << ": invalid credit modulation value"; + ret = false; } } } - if ((section = ini.find("leeway up"))) + + return ret; +} + +bool PartHydro::checkProperties(Study& study) +{ + bool ret = true; + + // Check on reservoir capacity (has to be done after reservoir management and capacity reading, + // not before). Some areas reservoir capacities may not be printed in hydro ini file when saving + // the study, because they are too small (< 1e-6). We cannot have reservoir management = yes and + // capacity = 0 because of further division by capacity. reservoir management = no and capacity + // = 0 is possible (no use of capacity further) + study.areas.each([&ret](Data::Area& area) { - if ((property = section->firstProperty)) + if (area.hydro.reservoirCapacity < 1e-3 && area.hydro.reservoirManagement) { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.leewayUpperBound) && ret; - if (area->hydro.leewayUpperBound < 0.) - { - logs.error() - << area->id << ": Invalid leeway upper bound. It must be >= 0.0, Got " - << area->hydro.leewayUpperBound; - area->hydro.leewayUpperBound = 0.; - } - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } + logs.error() << area.name + << ": reservoir capacity not defined. Impossible to manage."; + ret = false; } - } - // they are too small (< 1e-6). We cannot allow these areas to have reservoir management = - // true. - study.areas.each( - [&](Data::Area& area) - { - if (area.hydro.leewayLowerBound > area.hydro.leewayUpperBound) - { - logs.error() << area.id << ": Leeway lower bound greater than leeway upper bound."; - } - }); + if (!area.hydro.useHeuristicTarget && !area.hydro.useWaterValue) + { + logs.error() << area.name + << " : use water value = no conflicts with use heuristic target = no"; + ret = false; + } - if ((section = ini.find("pumping efficiency"))) - { - if ((property = section->firstProperty)) + if (area.hydro.intraDailyModulation < 1.) { - // Browse all properties - for (; property; property = property->next) - { - AreaName id = property->key; - id.toLower(); - - auto* area = study.areas.find(id); - if (area) - { - ret = property->value.to(area->hydro.pumpingEfficiency) && ret; - if (area->hydro.pumpingEfficiency < 0) - { - logs.error() << area->id << ": Invalid pumping efficiency"; - area->hydro.pumpingEfficiency = 0.; - } - } - else - { - logs.warning() << buffer << ": `" << id << "`: Unknown area"; - } - } + logs.error() + << area.id << ": Invalid intra-daily modulation. It must be >= 1.0, Got " + << area.hydro.intraDailyModulation << " (truncated to 1)"; + area.hydro.intraDailyModulation = 1.; } - } - study.areas.each( - [&](Data::Area& area) - { - if (not area.hydro.useHeuristicTarget && not area.hydro.useWaterValue) - { - logs.error() << area.name - << " : use water value = no conflicts with use heuristic target = no"; - ret = false && ret; - } - }); + if (area.hydro.reservoirCapacity < 0) + { + logs.error() << area.id << ": Invalid reservoir capacity."; + area.hydro.reservoirCapacity = 0.; + } + + if (area.hydro.intermonthlyBreakdown < 0) + { + logs.error() << area.id << ": Invalid intermonthly breakdown"; + area.hydro.intermonthlyBreakdown = 0.; + } + + if (area.hydro.initializeReservoirLevelDate < 0) + { + logs.error() << area.id << ": Invalid initialize reservoir date"; + area.hydro.initializeReservoirLevelDate = 0; + } + + if (area.hydro.leewayLowerBound < 0.) + { + logs.error() + << area.id << ": Invalid leeway lower bound. It must be >= 0.0, Got " + << area.hydro.leewayLowerBound; + area.hydro.leewayLowerBound = 0.; + } + + if (area.hydro.leewayUpperBound < 0.) + { + logs.error() + << area.id << ": Invalid leeway upper bound. It must be >= 0.0, Got " + << area.hydro.leewayUpperBound; + area.hydro.leewayUpperBound = 0.; + } + + if (area.hydro.leewayLowerBound > area.hydro.leewayUpperBound) + { + logs.error() << area.id << ": Leeway lower bound greater than leeway upper bound."; + } + + if (area.hydro.pumpingEfficiency < 0) + { + logs.error() << area.id << ": Invalid pumping efficiency"; + area.hydro.pumpingEfficiency = 0.; + } + }); return ret; } +bool PartHydro::validate(Study& study) +{ + bool ret = checkReservoirLevels(study); + return checkProperties(study) && ret; +} + bool PartHydro::SaveToFolder(const AreaList& areas, const AnyString& folder) { if (!folder) diff --git a/src/libs/antares/study/parts/hydro/hydromaxtimeseriesreader.cpp b/src/libs/antares/study/parts/hydro/hydromaxtimeseriesreader.cpp index b7864e5b31..fa2827fc88 100644 --- a/src/libs/antares/study/parts/hydro/hydromaxtimeseriesreader.cpp +++ b/src/libs/antares/study/parts/hydro/hydromaxtimeseriesreader.cpp @@ -47,6 +47,24 @@ HydroMaxTimeSeriesReader::HydroMaxTimeSeriesReader(PartHydro& hydro, dailyMaxPumpAndGen.reset(4U, DAYS_PER_YEAR, true); } +static bool checkPower(const Matrix<>& dailyMaxPumpAndGen, const std::string& areaName) +{ + for (uint i = 0; i < 4U; ++i) + { + auto& col = dailyMaxPumpAndGen[i]; + for (uint day = 0; day < DAYS_PER_YEAR; ++day) + { + if (col[day] < 0. || (i % 2U /*column hours*/ && col[day] > 24.)) + { + logs.error() << areaName << ": invalid power or energy value"; + return false; + } + } + } + + return true; +} + bool HydroMaxTimeSeriesReader::loadDailyMaxPowersAndEnergies(const AnyString& folder, bool usedBySolver) { @@ -90,21 +108,6 @@ bool HydroMaxTimeSeriesReader::loadDailyMaxPowersAndEnergies(const AnyString& fo Matrix<>::optFixedSize, &fileContent) && ret; - - bool errorPowers = false; - for (uint i = 0; i < 4U; ++i) - { - auto& col = dailyMaxPumpAndGen[i]; - for (uint day = 0; day < DAYS_PER_YEAR; ++day) - { - if (!errorPowers && (col[day] < 0. || (i % 2U /*column hours*/ && col[day] > 24.))) - { - logs.error() << areaName_ << ": invalid power or energy value"; - errorPowers = true; - ret = false; - } - } - } } return ret; } @@ -138,6 +141,7 @@ void HydroMaxTimeSeriesReader::copyDailyMaxPumpingEnergy() const bool HydroMaxTimeSeriesReader::read(const AnyString& folder, bool usedBySolver) { bool ret = loadDailyMaxPowersAndEnergies(folder, usedBySolver); + ret = checkPower(dailyMaxPumpAndGen, areaName_) && ret; copyDailyMaxEnergy(); hydro_.series->buildHourlyMaxPowerFromDailyTS(dailyMaxPumpAndGen[genMaxP], dailyMaxPumpAndGen[pumpMaxP]); diff --git a/src/libs/antares/study/parts/hydro/prepro.cpp b/src/libs/antares/study/parts/hydro/prepro.cpp index d8ddb72352..9370c562fa 100644 --- a/src/libs/antares/study/parts/hydro/prepro.cpp +++ b/src/libs/antares/study/parts/hydro/prepro.cpp @@ -145,22 +145,30 @@ bool PreproHydro::saveToFolder(const AreaName& areaID, const char* folder) return false; } -bool PreproHydro::loadFromFolder(Study& s, const AreaName& areaID, const char* folder) +bool PreproHydro::loadFromFolder(Study& s, const AreaName& areaID, const std::string& folder) { - /* Asserts */ - assert(folder); - assert('\0' != *folder); - enum { mtrxOption = Matrix<>::optFixedSize | Matrix<>::optImmediate, }; + constexpr int maxNbOfLineToLoad = 12; data.resize(hydroPreproMax, 12, true); String& buffer = s.bufferLoadingTS; buffer.clear() << folder << SEP << areaID << SEP << "prepro.ini"; bool ret = PreproHydroLoadSettings(this, buffer); + + buffer.clear() << folder << SEP << areaID << SEP << "energy.txt"; + ret = data.loadFromCSVFile(buffer, hydroPreproMax, maxNbOfLineToLoad, mtrxOption, &s.dataBuffer) && ret; + + return ret; +} + +bool PreproHydro::validate(const std::string& areaID) +{ + bool ret = true; + if (intermonthlyCorrelation < 0. || intermonthlyCorrelation > 1.) { logs.error() << "Hydro: Prepro: `" << areaID @@ -175,77 +183,58 @@ bool PreproHydro::loadFromFolder(Study& s, const AreaName& areaID, const char* f } } - buffer.clear() << folder << SEP << areaID << SEP << "energy.txt"; - ret = data.loadFromCSVFile(buffer, hydroPreproMax, 12, mtrxOption, &s.dataBuffer) && ret; - - if (JIT::enabled) - { - return ret; - } - - // Checks + const auto& col = data[powerOverWater]; + for (unsigned i = 0; i != data.height; ++i) { - auto& col = data[powerOverWater]; - for (uint i = 0; i != data.height; ++i) + const double d = col[i]; + if (d < 0. || d > 1.) { - const double d = col[i]; - if (d < 0. || d > 1.) - { - logs.error() << "Hydro: Prepro: " << areaID - << ": invalid value for ROR (line: " << (i + 1) << ")"; - } + logs.error() << "Hydro: Prepro: " << areaID + << ": invalid value for ROR (line: " << (i + 1) << ")"; } } - { - auto& colMin = data[minimumEnergy]; - auto& colMax = data[maximumEnergy]; + const auto& colMin = data[minimumEnergy]; + const auto& colMax = data[maximumEnergy]; - for (uint i = 0; i != data.height; ++i) + for (unsigned i = 0; i != data.height; ++i) + { + if (colMin[i] < 0.) { - if (colMin[i] < 0.) - { - ret = false; - logs.error() << "Hydro: Prepro: `" << areaID - << "`: minimum energy: At least one value is negative (line: " - << (i + 1) << ')'; - continue; - } - if (colMin[i] > colMax[i]) - { - ret = false; - logs.error() << "Hydro: Prepro: `" << areaID - << "`: the minimum energy must be less than the maximum energy (line: " - << (i + 1) << ')'; - } + ret = false; + logs.error() << "Hydro: Prepro: `" << areaID + << "`: minimum energy: At least one value is negative (line: " + << (i + 1) << ')'; + continue; + } + if (colMin[i] > colMax[i]) + { + ret = false; + logs.error() << "Hydro: Prepro: `" << areaID + << "`: the minimum energy must be less than the maximum energy (line: " + << (i + 1) << ')'; } } + const auto& colExp = data[expectation]; + for (unsigned i = 0; i != data.height; i++) { - auto& colExp = data[expectation]; - - for (uint i = 0; i != data.height; i++) + if (colExp[i] < 0.) { - if (colExp[i] < 0.) - { - ret = false; - logs.error() << "Hydro: Prepro: `" << areaID - << "`: invalid value for expectation (line: " << (i + 1) << ")"; - } + ret = false; + logs.error() << "Hydro: Prepro: `" << areaID + << "`: invalid value for expectation (line: " << (i + 1) << ")"; } } + const auto& colStdDev = data[stdDeviation]; + for (unsigned i = 0; i != data.height; i++) { - auto& colStdDev = data[stdDeviation]; - - for (uint i = 0; i != data.height; i++) + if (colStdDev[i] < 0.) { - if (colStdDev[i] < 0.) - { - ret = false; - logs.error() << "Hydro: Prepro: `" << areaID - << "`: invalid value for standard deviation (line: " << (i + 1) << ")"; - } + ret = false; + logs.error() << "Hydro: Prepro: `" << areaID + << "`: invalid value for standard deviation (line: " << (i + 1) << ")"; } } From 708128c633bcc9645e3b855290f6756126f083c0 Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:57:27 +0200 Subject: [PATCH 05/15] Capture explicit var for lambdas (#2170) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #2169 --------- Co-authored-by: Florian Omnès --- src/libs/antares/inifile/inifile.cpp | 12 +- src/libs/antares/study/area/list.cpp | 27 ++-- .../BindingConstraintGroupRepository.cpp | 22 +-- .../antares/study/cleaner/cleaner-v20.cpp | 10 +- src/libs/antares/study/filter.cpp | 2 +- .../study/include/antares/study/parameters.h | 19 +-- src/libs/antares/study/load.cpp | 4 +- src/libs/antares/study/parameters.cpp | 15 +- .../study/parts/common/cluster_list.cpp | 4 +- .../antares/study/parts/hydro/allocation.cpp | 41 +++-- .../antares/study/parts/hydro/container.cpp | 88 +++++++---- .../parts/short-term-storage/container.cpp | 36 ++--- src/libs/antares/study/runtime/runtime.cpp | 13 +- .../BindingConstraintsTSNumbersData.cpp | 5 +- .../antares/study/scenario-builder/sets.cpp | 2 +- src/libs/antares/study/study.cpp | 50 +++--- src/libs/antares/study/study.importprepro.cpp | 79 +++++----- src/libs/antares/study/xcast/xcast.cpp | 10 +- src/libs/antares/sys/policy.cpp | 12 +- src/solver/application/application.cpp | 2 +- src/solver/hydro/management/daily.cpp | 6 +- src/solver/hydro/management/management.cpp | 6 +- src/solver/hydro/management/monthly.cpp | 2 +- .../optimisation/post_process_commands.cpp | 2 +- src/solver/simulation/common-eco-adq.cpp | 2 +- src/solver/simulation/common-hydro-levels.cpp | 148 +++++++++--------- src/solver/simulation/common-hydro-remix.cpp | 3 +- .../antares/solver/simulation/solver.hxx | 9 +- .../simulation/sim_calcul_economique.cpp | 2 +- src/solver/ts-generator/generator.cpp | 2 +- src/solver/ts-generator/hydro.cpp | 9 +- src/solver/ts-generator/xcast/xcast.cpp | 4 +- .../include/antares/solver/variable/area.hxx | 6 +- .../variable/surveyresults/surveyresults.cpp | 2 +- 34 files changed, 303 insertions(+), 353 deletions(-) diff --git a/src/libs/antares/inifile/inifile.cpp b/src/libs/antares/inifile/inifile.cpp index e6524920ee..67190a0654 100644 --- a/src/libs/antares/inifile/inifile.cpp +++ b/src/libs/antares/inifile/inifile.cpp @@ -65,7 +65,9 @@ void IniFile::Section::saveToStream(std::ostream& stream_out, uint64_t& written) stream_out << '[' << name << "]\n"; written += 4 /* []\n\n */ + name.size(); - each([&](const IniFile::Property& p) { p.saveToStream(stream_out, written); }); + each([&stream_out, &written](const IniFile::Property& p) { + p.saveToStream(stream_out, written); + }); stream_out << '\n'; } @@ -168,9 +170,9 @@ static std::string getSectionName(const std::string& line) return splitLine[1]; } -static bool isProperty(const std::string& line) +static bool isProperty(std::string_view line) { - return std::ranges::count(line.begin(), line.end(), '=') == 1; + return std::ranges::count(line, '=') == 1; } static IniFile::Property getProperty(std::string line) @@ -264,7 +266,9 @@ bool IniFile::open(const fs::path& filename, bool warnings) void IniFile::saveToStream(std::ostream& stream_out, uint64_t& written) const { - each([&](const IniFile::Section& s) {s.saveToStream(stream_out, written); }); + each([&stream_out, &written](const IniFile::Section& s) { + s.saveToStream(stream_out, written); + }); if (written != 0) { diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index 417e60bae4..219f7ee8aa 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -1275,13 +1275,10 @@ Area* AreaList::findFromPosition(const int x, const int y) const for (auto i = this->areas.rbegin(); i != end; ++i) { auto lastArea = i->second; - if (lastArea->ui) + if (lastArea->ui && std::abs(lastArea->ui->x - x) < nearestDistance + && std::abs(lastArea->ui->y - y) < nearestDistance) { - if (std::abs(lastArea->ui->x - x) < nearestDistance - && std::abs(lastArea->ui->y - y) < nearestDistance) - { - nearestItem = lastArea; - } + nearestItem = lastArea; } } return nearestItem; @@ -1327,14 +1324,12 @@ void AreaListEnsureDataLoadPrepro(AreaList* l) /* Asserts */ assert(l); - l->each( - [&](Data::Area& area) - { - if (!area.load.prepro) - { - area.load.prepro = new Antares::Data::Load::Prepro(); - } - }); + l->each([](Data::Area& area) { + if (!area.load.prepro) + { + area.load.prepro = new Antares::Data::Load::Prepro(); + } + }); } void AreaListEnsureDataSolarPrepro(AreaList* l) @@ -1700,9 +1695,9 @@ void AreaList::removeWindTimeseries() void AreaList::removeThermalTimeseries() { each( - [](Data::Area& area) + [](const Data::Area& area) { - for (auto& c: area.thermal.list.all()) + for (const auto& c: area.thermal.list.all()) { c->series.reset(); } diff --git a/src/libs/antares/study/binding_constraint/BindingConstraintGroupRepository.cpp b/src/libs/antares/study/binding_constraint/BindingConstraintGroupRepository.cpp index ce168c7eef..ff181e7ed8 100644 --- a/src/libs/antares/study/binding_constraint/BindingConstraintGroupRepository.cpp +++ b/src/libs/antares/study/binding_constraint/BindingConstraintGroupRepository.cpp @@ -60,10 +60,7 @@ bool BindingConstraintGroupRepository::buildFrom(const BindingConstraintsReposit bool BindingConstraintGroupRepository::timeSeriesWidthConsistentInGroups() const { - bool allConsistent = !std::any_of( - groups_.begin(), - groups_.end(), - [](const auto& group) + bool allConsistent = !std::ranges::any_of(groups_, [](const auto& group) { const auto& constraints = group->constraints(); if (constraints.empty()) @@ -71,9 +68,7 @@ bool BindingConstraintGroupRepository::timeSeriesWidthConsistentInGroups() const return false; } auto width = (*constraints.begin())->RHSTimeSeries().width; - bool isConsistent = std::all_of( - constraints.begin(), - constraints.end(), + bool isConsistent = std::ranges::all_of(constraints, [&width](const std::shared_ptr& bc) { bool sameWidth = bc->RHSTimeSeries().width == width; @@ -94,18 +89,15 @@ bool BindingConstraintGroupRepository::timeSeriesWidthConsistentInGroups() const void BindingConstraintGroupRepository::resizeAllTimeseriesNumbers(unsigned int nb_years) { - std::for_each(groups_.begin(), - groups_.end(), - [&](auto& group) { group->timeseriesNumbers.reset(nb_years); }); + std::ranges::for_each(groups_, [&nb_years](auto& group) + { group->timeseriesNumbers.reset(nb_years); }); } BindingConstraintGroup* BindingConstraintGroupRepository::operator[](const std::string& name) const { - if (auto group = std::find_if(groups_.begin(), - groups_.end(), - [&name](auto& group_of_constraint) - { return group_of_constraint->name() == name; }); - group != groups_.end()) + if (auto group = std::ranges::find_if(groups_, [&name](auto& group_of_constraint) + { return group_of_constraint->name() == name; }); + group != groups_.end()) { return group->get(); } diff --git a/src/libs/antares/study/cleaner/cleaner-v20.cpp b/src/libs/antares/study/cleaner/cleaner-v20.cpp index b3dc2ed6f6..3c8f183be4 100644 --- a/src/libs/antares/study/cleaner/cleaner-v20.cpp +++ b/src/libs/antares/study/cleaner/cleaner-v20.cpp @@ -391,20 +391,20 @@ bool listOfFilesAnDirectoriesToKeep(StudyCleaningInfos* infos) buffer.clear() << infos->folder << "/input/bindingconstraints/bindingconstraints.ini"; if (ini.open(buffer)) { - String v; ini.each( - [&](const IniFile::Section& section) + [&e](const IniFile::Section& section) { auto* property = section.firstProperty; for (; property; property = property->next) { if (property->key == "id") { - v = property->value; + String v = property->value; v.toLower(); - buffer.clear() << "input/bindingconstraints/" << v << ".txt"; - e.add(buffer); + String tmp; + tmp << "input/bindingconstraints/" << v << ".txt"; + e.add(tmp); // Go to the next binding constraint break; } diff --git a/src/libs/antares/study/filter.cpp b/src/libs/antares/study/filter.cpp index aa8916c26c..cb94bf5d30 100644 --- a/src/libs/antares/study/filter.cpp +++ b/src/libs/antares/study/filter.cpp @@ -82,7 +82,7 @@ uint stringIntoDatePrecision(const AnyString& string) uint flag = 0; string.words(",; \r\n\t", - [&](const AnyString& word) -> bool + [&flag](const AnyString& word) -> bool { ShortString16 s = word; s.toLower(); diff --git a/src/libs/antares/study/include/antares/study/parameters.h b/src/libs/antares/study/include/antares/study/parameters.h index d9a1d467f0..c66cf9757f 100644 --- a/src/libs/antares/study/include/antares/study/parameters.h +++ b/src/libs/antares/study/include/antares/study/parameters.h @@ -50,21 +50,6 @@ namespace Antares::Data class Parameters final { public: - //! \name Constructor - //@{ - /*! - ** \brief Default Constructor - ** - ** \warning None of the variables are initialized. You must explicitly use - ** the method `reset()` or the method `loadFromFile()` - ** \see reset() - ** \see loadFromFile() - */ - Parameters(); - //! Destructor - ~Parameters(); - //@} - //! \name Simulation mode //@{ //! Get if the simulation is in economy mode @@ -91,7 +76,7 @@ class Parameters final ** \return True if the settings have been loaded, false if at least one error has occured */ bool loadFromFile(const AnyString& filename, - StudyVersion& version, + const StudyVersion& version, const StudyLoadOptions& options); /*! @@ -493,7 +478,7 @@ class Parameters final //@{ //! No output // This variable is not stored within the study but only used by the solver - bool noOutput; + bool noOutput = false; //@} bool hydroDebug; diff --git a/src/libs/antares/study/load.cpp b/src/libs/antares/study/load.cpp index dca1b1eb15..00214717e8 100644 --- a/src/libs/antares/study/load.cpp +++ b/src/libs/antares/study/load.cpp @@ -286,7 +286,7 @@ bool Study::internalLoadBindingConstraints(const StudyLoadOptions& options) class SetHandlerAreas { public: - SetHandlerAreas(Study& study): + explicit SetHandlerAreas(Study& study): pStudy(study) { } @@ -411,7 +411,7 @@ bool Study::reloadXCastData() // if changes are required, please update AreaListLoadFromFolderSingleArea() bool ret = true; areas.each( - [&](Data::Area& area) + [this, &ret](Data::Area& area) { assert(area.load.prepro); assert(area.solar.prepro); diff --git a/src/libs/antares/study/parameters.cpp b/src/libs/antares/study/parameters.cpp index 3517b09d21..a598ee1566 100644 --- a/src/libs/antares/study/parameters.cpp +++ b/src/libs/antares/study/parameters.cpp @@ -54,7 +54,7 @@ static bool ConvertCStrToListTimeSeries(const String& value, uint& v) } value.words(" ,;\t\r\n", - [&](const AnyString& element) -> bool + [&v](const AnyString& element) { ShortString16 word(element); word.toLower(); @@ -208,13 +208,6 @@ const char* SimulationModeToCString(SimulationMode mode) } } -Parameters::Parameters(): - noOutput(false) -{ -} - -Parameters::~Parameters() = default; - bool Parameters::economy() const { return mode == SimulationMode::Economy; @@ -231,8 +224,8 @@ void Parameters::resetSeeds() // For retro-compatibility, the wind ts-generator should produce the // same results than before 3.8. // It must have the same seed than before - auto increment = (unsigned)antaresSeedIncrement; - auto s = (unsigned)antaresSeedDefaultValue; + auto increment = antaresSeedIncrement; + auto s = antaresSeedDefaultValue; seed[seedTsGenWind] = s; // The same way for all others @@ -1981,7 +1974,7 @@ void Parameters::saveToINI(IniFile& ini) const } bool Parameters::loadFromFile(const AnyString& filename, - StudyVersion& version, + const StudyVersion& version, const StudyLoadOptions& options) { // Loading the INI file diff --git a/src/libs/antares/study/parts/common/cluster_list.cpp b/src/libs/antares/study/parts/common/cluster_list.cpp index e00934dfe7..9be5c2ca57 100644 --- a/src/libs/antares/study/parts/common/cluster_list.cpp +++ b/src/libs/antares/study/parts/common/cluster_list.cpp @@ -263,8 +263,8 @@ bool ClusterList::saveDataSeriesToFolder(const AnyString& folder) cons template bool ClusterList::loadDataSeriesFromFolder(Study& s, const AnyString& folder) { - return std::ranges::all_of(allClusters_, - [&](auto c) { return c->loadDataSeriesFromFolder(s, folder); }); + return std::ranges::all_of(allClusters_, [&s, &folder](auto c) + { return c->loadDataSeriesFromFolder(s, folder); }); } template diff --git a/src/libs/antares/study/parts/hydro/allocation.cpp b/src/libs/antares/study/parts/hydro/allocation.cpp index 93ee0ef5d0..597264cf20 100644 --- a/src/libs/antares/study/parts/hydro/allocation.cpp +++ b/src/libs/antares/study/parts/hydro/allocation.cpp @@ -170,31 +170,28 @@ bool HydroAllocation::loadFromFile(const AreaName& referencearea, clear(); IniFile ini; - if (fs::exists(filename) && ini.open(filename)) - { - if (!ini.empty()) - { - AreaName areaname; - ini.each( - [&](const IniFile::Section& section) - { - for (auto* p = section.firstProperty; p; p = p->next) - { - double coeff = p->value.to(); - if (!Utils::isZero(coeff)) - { - areaname = p->key; - areaname.toLower(); - pValues[areaname] = coeff; - } - } - }); - } - } - else + if (!fs::exists(filename) || !ini.open(filename)) { pValues[referencearea] = 1.0; + return true; } + + if (ini.empty()) + return true; + + ini.each([this](const IniFile::Section& section) + { + for (auto* p = section.firstProperty; p; p = p->next) + { + double coeff = p->value.to(); + if (!Utils::isZero(coeff)) + { + AreaName areaname = p->key; + areaname.toLower(); + pValues[areaname] = coeff; + } + } + }); return true; } diff --git a/src/libs/antares/study/parts/hydro/container.cpp b/src/libs/antares/study/parts/hydro/container.cpp index 0343c2878f..d955da1c81 100644 --- a/src/libs/antares/study/parts/hydro/container.cpp +++ b/src/libs/antares/study/parts/hydro/container.cpp @@ -144,7 +144,7 @@ bool PartHydro::LoadFromFolder(Study& study, const AnyString& folder) // Initialize all alpha values to 0 study.areas.each( - [&](Data::Area& area) + [&ret, &buffer, &study, &folder](Data::Area& area) { area.hydro.interDailyBreakdown = 1.; area.hydro.intraDailyModulation = 24.; @@ -462,69 +462,93 @@ bool PartHydro::SaveToFolder(const AreaList& areas, const AnyString& folder) String buffer; buffer.clear() << folder << SEP << "common" << SEP << "capacity"; + struct AllSections + { + IniFile::Section* s; + IniFile::Section* smod; + IniFile::Section* sIMB; + IniFile::Section* sreservoir; + IniFile::Section* sreservoirCapacity; + IniFile::Section* sFollowLoad; + IniFile::Section* sUseWater; + IniFile::Section* sHardBounds; + IniFile::Section* sInitializeReservoirDate; + IniFile::Section* sUseHeuristic; + IniFile::Section* sUseLeeway; + IniFile::Section* sPowerToLevel; + IniFile::Section* sLeewayLow; + IniFile::Section* sLeewayUp; + IniFile::Section* spumpingEfficiency; + + AllSections(IniFile& ini) : + s(ini.addSection("inter-daily-breakdown")), + smod(ini.addSection("intra-daily-modulation")), + sIMB(ini.addSection("inter-monthly-breakdown")), + sreservoir(ini.addSection("reservoir")), + sreservoirCapacity(ini.addSection("reservoir capacity")), + sFollowLoad(ini.addSection("follow load")), + sUseWater(ini.addSection("use water")), + sHardBounds(ini.addSection("hard bounds")), + sInitializeReservoirDate(ini.addSection("initialize reservoir date")), + sUseHeuristic(ini.addSection("use heuristic")), + sUseLeeway(ini.addSection("use leeway")), + sPowerToLevel(ini.addSection("power to level")), + sLeewayLow(ini.addSection("leeway low")), + sLeewayUp(ini.addSection("leeway up")), + spumpingEfficiency(ini.addSection("pumping efficiency")) + { + } + }; + // Init IniFile ini; - auto* s = ini.addSection("inter-daily-breakdown"); - auto* smod = ini.addSection("intra-daily-modulation"); - auto* sIMB = ini.addSection("inter-monthly-breakdown"); - auto* sreservoir = ini.addSection("reservoir"); - auto* sreservoirCapacity = ini.addSection("reservoir capacity"); - auto* sFollowLoad = ini.addSection("follow load"); - auto* sUseWater = ini.addSection("use water"); - auto* sHardBounds = ini.addSection("hard bounds"); - auto* sInitializeReservoirDate = ini.addSection("initialize reservoir date"); - auto* sUseHeuristic = ini.addSection("use heuristic"); - auto* sUseLeeway = ini.addSection("use leeway"); - auto* sPowerToLevel = ini.addSection("power to level"); - auto* sLeewayLow = ini.addSection("leeway low"); - auto* sLeewayUp = ini.addSection("leeway up"); - auto* spumpingEfficiency = ini.addSection("pumping efficiency"); + AllSections allSections(ini); // return status bool ret = true; // Add all alpha values for each area areas.each( - [&](const Data::Area& area) + [&allSections, &buffer, &folder, &ret](const Data::Area& area) { - s->add(area.id, area.hydro.interDailyBreakdown); - smod->add(area.id, area.hydro.intraDailyModulation); - sIMB->add(area.id, area.hydro.intermonthlyBreakdown); - sInitializeReservoirDate->add(area.id, area.hydro.initializeReservoirLevelDate); - sLeewayLow->add(area.id, area.hydro.leewayLowerBound); - sLeewayUp->add(area.id, area.hydro.leewayUpperBound); - spumpingEfficiency->add(area.id, area.hydro.pumpingEfficiency); + allSections.s->add(area.id, area.hydro.interDailyBreakdown); + allSections.smod->add(area.id, area.hydro.intraDailyModulation); + allSections.sIMB->add(area.id, area.hydro.intermonthlyBreakdown); + allSections.sInitializeReservoirDate->add(area.id, area.hydro.initializeReservoirLevelDate); + allSections.sLeewayLow->add(area.id, area.hydro.leewayLowerBound); + allSections.sLeewayUp->add(area.id, area.hydro.leewayUpperBound); + allSections.spumpingEfficiency->add(area.id, area.hydro.pumpingEfficiency); if (area.hydro.reservoirCapacity > 1e-6) { - sreservoirCapacity->add(area.id, area.hydro.reservoirCapacity); + allSections.sreservoirCapacity->add(area.id, area.hydro.reservoirCapacity); } if (area.hydro.reservoirManagement) { - sreservoir->add(area.id, true); + allSections.sreservoir->add(area.id, true); } if (!area.hydro.followLoadModulations) { - sFollowLoad->add(area.id, false); + allSections.sFollowLoad->add(area.id, false); } if (area.hydro.useWaterValue) { - sUseWater->add(area.id, true); + allSections.sUseWater->add(area.id, true); } if (area.hydro.hardBoundsOnRuleCurves) { - sHardBounds->add(area.id, true); + allSections.sHardBounds->add(area.id, true); } if (!area.hydro.useHeuristicTarget) { - sUseHeuristic->add(area.id, false); + allSections.sUseHeuristic->add(area.id, false); } if (area.hydro.useLeeway) { - sUseLeeway->add(area.id, true); + allSections.sUseLeeway->add(area.id, true); } if (area.hydro.powerToLevel) { - sPowerToLevel->add(area.id, true); + allSections.sPowerToLevel->add(area.id, true); } // max hours gen diff --git a/src/libs/antares/study/parts/short-term-storage/container.cpp b/src/libs/antares/study/parts/short-term-storage/container.cpp index 1c9ca78bf7..749613a14b 100644 --- a/src/libs/antares/study/parts/short-term-storage/container.cpp +++ b/src/libs/antares/study/parts/short-term-storage/container.cpp @@ -37,9 +37,8 @@ namespace Antares::Data::ShortTermStorage { bool STStorageInput::validate() const { - return std::all_of(storagesByIndex.cbegin(), - storagesByIndex.cend(), - [](auto& cluster) { return cluster.validate(); }); + return std::ranges::all_of(storagesByIndex, [](auto& cluster) + { return cluster.validate(); }); } bool STStorageInput::createSTStorageClustersFromIniFile(const fs::path& path) @@ -69,9 +68,8 @@ bool STStorageInput::createSTStorageClustersFromIniFile(const fs::path& path) storagesByIndex.push_back(cluster); } - std::sort(storagesByIndex.begin(), - storagesByIndex.end(), - [&](const auto& a, const auto& b) { return a.properties.name < b.properties.name; }); + std::ranges::sort(storagesByIndex, [](const auto& a, const auto& b) + { return a.properties.name < b.properties.name; }); return true; } @@ -102,9 +100,8 @@ bool STStorageInput::saveToFolder(const std::string& folder) const IniFile ini; logs.debug() << "saving file " << pathIni; - std::for_each(storagesByIndex.cbegin(), - storagesByIndex.cend(), - [&ini](auto& storage) { return storage.saveProperties(ini); }); + std::ranges::for_each(storagesByIndex, [&ini](auto& storage) + { return storage.saveProperties(ini); }); return ini.save(pathIni); } @@ -112,29 +109,20 @@ bool STStorageInput::saveToFolder(const std::string& folder) const bool STStorageInput::saveDataSeriesToFolder(const std::string& folder) const { Yuni::IO::Directory::Create(folder); - return std::all_of(storagesByIndex.cbegin(), - storagesByIndex.cend(), - [&folder](auto& storage) - { return storage.saveSeries(folder + SEP + storage.id); }); + return std::ranges::all_of(storagesByIndex, [&folder](auto& storage) + { return storage.saveSeries(folder + SEP + storage.id); }); } std::size_t STStorageInput::count() const { - return std::count_if(storagesByIndex.begin(), - storagesByIndex.end(), - [](const STStorageCluster& st) { return st.properties.enabled; }); + return std::ranges::count_if(storagesByIndex, [](const STStorageCluster& st) + { return st.properties.enabled; }); } uint STStorageInput::removeDisabledClusters() { - const auto& it = std::remove_if(storagesByIndex.begin(), - storagesByIndex.end(), - [](const auto& c) { return !c.enabled(); }); - - uint disabledCount = std::distance(it, storagesByIndex.end()); - storagesByIndex.erase(it, storagesByIndex.end()); - - return disabledCount; + return std::erase_if(storagesByIndex, [](const auto& c) + { return !c.enabled(); }); } } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/runtime/runtime.cpp b/src/libs/antares/study/runtime/runtime.cpp index f10da99345..035faf86ac 100644 --- a/src/libs/antares/study/runtime/runtime.cpp +++ b/src/libs/antares/study/runtime/runtime.cpp @@ -103,7 +103,7 @@ static void StudyRuntimeInfosInitializeAreaLinks(Study& study, StudyRuntimeInfos uint indx = 0; study.areas.each( - [&](Data::Area& area) + [&indx, &r](Data::Area& area) { area.buildLinksIndexes(); @@ -213,9 +213,9 @@ void StudyRuntimeInfos::initializeRangeLimits(const Study& study, StudyRangeLimi } else { - simulationDaysPerMonth[(uint)ca.month] = study.calendar.months[(uint)ca.month].days + simulationDaysPerMonth[ca.month] = study.calendar.months[ca.month].days - ca.dayMonth; - simulationDaysPerMonth[(uint)cb.month] = cb.dayMonth + 1; + simulationDaysPerMonth[cb.month] = cb.dayMonth + 1; for (uint i = ca.month + 1; i < cb.month; ++i) { simulationDaysPerMonth[i] = study.calendar.months[i].days; @@ -270,9 +270,9 @@ void StudyRuntimeInfos::checkThermalTSGeneration(Study& study) thermalTSRefresh = globalThermalTSgeneration; study.areas.each( - [this, globalThermalTSgeneration](Data::Area& area) + [this, globalThermalTSgeneration](const Data::Area& area) { - for (auto& c: area.thermal.list.each_enabled_and_not_mustrun()) + for (const auto& c: area.thermal.list.each_enabled_and_not_mustrun()) { thermalTSRefresh = thermalTSRefresh || c->doWeGenerateTS(globalThermalTSgeneration); } @@ -441,8 +441,7 @@ void StudyRangeLimits::checkIntegrity() const void StudyRuntimeInfos::disableAllFilters(Study& study) { - study.areas.each( - [&](Data::Area& area) + study.areas.each([](Data::Area& area) { area.filterSynthesis = filterAll; area.filterYearByYear = filterAll; diff --git a/src/libs/antares/study/scenario-builder/BindingConstraintsTSNumbersData.cpp b/src/libs/antares/study/scenario-builder/BindingConstraintsTSNumbersData.cpp index 24b5b8cb6b..46a0694a58 100644 --- a/src/libs/antares/study/scenario-builder/BindingConstraintsTSNumbersData.cpp +++ b/src/libs/antares/study/scenario-builder/BindingConstraintsTSNumbersData.cpp @@ -80,9 +80,8 @@ bool BindingConstraintsTSNumberData::apply(Study& study) bool BindingConstraintsTSNumberData::reset(const Study& study) { const uint nbYears = study.parameters.nbYears; - std::for_each(study.bindingConstraintsGroups.begin(), - study.bindingConstraintsGroups.end(), - [&](const auto& group) + std::ranges::for_each(study.bindingConstraintsGroups, + [this, &nbYears](const auto& group) { auto& ts_numbers = rules_[group->name()]; ts_numbers.reset(1, nbYears); diff --git a/src/libs/antares/study/scenario-builder/sets.cpp b/src/libs/antares/study/scenario-builder/sets.cpp index 962f7c122d..76dbcb9166 100644 --- a/src/libs/antares/study/scenario-builder/sets.cpp +++ b/src/libs/antares/study/scenario-builder/sets.cpp @@ -195,7 +195,7 @@ bool Sets::internalLoadFromINIFile(const AnyString& filename) } ini.each( - [&](const IniFile::Section& section) + [this](const IniFile::Section& section) { if (section.name.empty()) { diff --git a/src/libs/antares/study/study.cpp b/src/libs/antares/study/study.cpp index 23f547783d..511e61a592 100644 --- a/src/libs/antares/study/study.cpp +++ b/src/libs/antares/study/study.cpp @@ -520,7 +520,7 @@ void Study::performTransformationsBeforeLaunchingSimulation() // ForEach area areas.each( - [&](Data::Area& area) + [this](Data::Area& area) { if (not parameters.geographicTrimming) { @@ -698,7 +698,7 @@ void Study::saveAboutTheStudy(Solver::IResultWriter& resultWriter) buffer << "@ " << i->first << "\r\n"; } } - areas.each([&](const Data::Area& area) { buffer << area.name << "\r\n"; }); + areas.each([&buffer](const Data::Area& area) { buffer << area.name << "\r\n"; }); resultWriter.addEntryFromBuffer(path.c_str(), buffer); } @@ -789,7 +789,7 @@ bool Study::areaDelete(Area* area) // and the scenario builder data *before* reloading uiinfo. { // Updating all hydro allocation - areas.each([&](Data::Area& areait) { areait.hydro.allocation.remove(area->id); }); + areas.each([&area](Data::Area& areait) { areait.hydro.allocation.remove(area->id); }); // We __must__ update the scenario builder data // We may delete an area and re-create a new one with the same @@ -856,7 +856,7 @@ void Study::areaDelete(Area::Vector& arealist) << area.name; // Updating all hydro allocation - areas.each([&](Data::Area& areait) { areait.hydro.allocation.remove(area.id); }); + areas.each([&area](Data::Area& areait) { areait.hydro.allocation.remove(area.id); }); // Remove all binding constraints attached to the area bindingConstraints.remove(*i); @@ -955,7 +955,8 @@ bool Study::areaRename(Area* area, AreaName newName) logs.info() << "renaming area " << area->name << " into " << newName; // Updating all hydro allocation - areas.each([&](Data::Area& areait) { areait.hydro.allocation.rename(oldid, newid); }); + areas.each([&oldid, &newid](Data::Area& areait) + { areait.hydro.allocation.rename(oldid, newid); }); ScenarioBuilderUpdater updaterSB(*this); bool ret = true; @@ -1088,34 +1089,33 @@ bool Study::clusterRename(Cluster* cluster, ClusterName newName) void Study::destroyAllLoadTSGeneratorData() { - areas.each([&](Data::Area& area) { FreeAndNil(area.load.prepro); }); + areas.each([](Data::Area& area) { FreeAndNil(area.load.prepro); }); } void Study::destroyAllSolarTSGeneratorData() { - areas.each([&](Data::Area& area) { FreeAndNil(area.solar.prepro); }); + areas.each([](Data::Area& area) { FreeAndNil(area.solar.prepro); }); } void Study::destroyAllHydroTSGeneratorData() { - areas.each([&](Data::Area& area) { FreeAndNil(area.hydro.prepro); }); + areas.each([](Data::Area& area) { FreeAndNil(area.hydro.prepro); }); } void Study::destroyAllWindTSGeneratorData() { - areas.each([&](Data::Area& area) { FreeAndNil(area.wind.prepro); }); + areas.each([](Data::Area& area) { FreeAndNil(area.wind.prepro); }); } void Study::destroyAllThermalTSGeneratorData() { - areas.each( - [&](Data::Area& area) - { - for (const auto& cluster: area.thermal.list.each_enabled_and_not_mustrun()) - { - FreeAndNil(cluster->prepro); - } - }); + areas.each([](const Data::Area& area) + { + for (const auto& cluster: area.thermal.list.each_enabled_and_not_mustrun()) + { + FreeAndNil(cluster->prepro); + } + }); } void Study::ensureDataAreLoadedForAllBindingConstraints() @@ -1356,8 +1356,9 @@ bool Study::checkForFilenameLimits(bool output, const String& chfolder) const String areaname; areas.each( - [&](const Area& area) + [&output, &linkname, &areaname](const Area& area) { + if (areaname.size() < area.id.size()) { areaname = area.id; @@ -1368,19 +1369,12 @@ bool Study::checkForFilenameLimits(bool output, const String& chfolder) const { auto& link = *(i->second); uint len = link.from->id.size() + link.with->id.size(); - len += output ? 3 : 1; + len += 3; if (len > linkname.size()) { linkname.clear(); linkname << i->second->from->id; - if (output) - { - linkname << " - "; // 3 - } - else - { - linkname << SEP; - } + linkname << " - "; // 3 linkname << i->second->with->id; } } @@ -1436,7 +1430,7 @@ bool Study::checkForFilenameLimits(bool output, const String& chfolder) const // or even constraints areas.each( - [&](const Area& area) + [&areaname, &clustername](const Area& area) { if (areaname.size() < area.id.size()) { diff --git a/src/libs/antares/study/study.importprepro.cpp b/src/libs/antares/study/study.importprepro.cpp index 837f37a485..3b8263eadf 100644 --- a/src/libs/antares/study/study.importprepro.cpp +++ b/src/libs/antares/study/study.importprepro.cpp @@ -50,56 +50,52 @@ bool Study::importTimeseriesIntoInput() if (parameters.haveToImport(timeSeriesLoad)) { logs.info() << "Importing load timeseries..."; - areas.each( - [&](const Data::Area& area) - { - logs.info() << "Importing load timeseries : " << area.name; - buffer.clear() << folderInput << SEP << "load" << SEP << "series"; - ret = area.load.series.saveToFolder(area.id, buffer.c_str(), "load_") && ret; - ++progression; - }); + for (const auto& [areaName, area] : areas) + { + logs.info() << "Importing load timeseries : " << areaName; + buffer.clear() << folderInput << SEP << "load" << SEP << "series"; + ret = area->load.series.saveToFolder(area->id, buffer.c_str(), "load_") && ret; + ++progression; + } } // Solar if (parameters.haveToImport(timeSeriesSolar)) { logs.info() << "Importing solar timeseries..."; - areas.each( - [&](const Data::Area& area) - { - logs.info() << "Importing solar timeseries : " << area.name; - buffer.clear() << folderInput << SEP << "solar" << SEP << "series"; - ret = area.solar.series.saveToFolder(area.id, buffer.c_str(), "solar_") && ret; - ++progression; - }); + for (const auto& [areaName, area] : areas) + { + logs.info() << "Importing solar timeseries : " << areaName; + buffer.clear() << folderInput << SEP << "solar" << SEP << "series"; + ret = area->solar.series.saveToFolder(area->id, buffer.c_str(), "solar_") && ret; + ++progression; + } } // Hydro if (parameters.haveToImport(timeSeriesHydro)) { logs.info() << "Importing hydro timeseries..."; - areas.each( - [&](const Data::Area& area) - { - logs.info() << "Importing hydro timeseries : " << area.name; - buffer.clear() << folderInput << SEP << "hydro" << SEP << "series"; - ret = area.hydro.series->saveToFolder(area.id, buffer) && ret; - ++progression; - }); + for (const auto& [areaName, area] : areas) + { + logs.info() << "Importing hydro timeseries : " << areaName; + buffer.clear() << folderInput << SEP << "hydro" << SEP << "series"; + ret = area->hydro.series->saveToFolder(area->id, buffer) && ret; + ++progression; + } } // Wind if (parameters.haveToImport(timeSeriesWind)) { logs.info() << "Importing wind timeseries..."; - areas.each( - [&](const Data::Area& area) - { - logs.info() << "Importing wind timeseries : " << area.name; - buffer.clear() << folderInput << SEP << "wind" << SEP << "series"; - area.wind.series.saveToFolder(area.id, buffer.c_str(), "wind_") && ret; - ++progression; - }); + for (const auto& [areaName, area] : areas) + { + logs.info() << "Importing wind timeseries : " << areaName; + buffer.clear() << folderInput << SEP << "wind" << SEP << "series"; + area->wind.series.saveToFolder(area->id, buffer.c_str(), "wind_") && ret; + ++progression; + } } // Thermal @@ -108,18 +104,17 @@ bool Study::importTimeseriesIntoInput() logs.info() << "Importing thermal timeseries..."; String msg; - areas.each( - [&](Data::Area& area) - { - msg.clear() << "Importing thermal timeseries : " << area.name; + for (const auto& [areaName, area] : areas) + { + msg.clear() << "Importing thermal timeseries : " << areaName; - // Spinning - area.thermal.list.reverseCalculationOfSpinning(); + // Spinning + area->thermal.list.reverseCalculationOfSpinning(); - buffer.clear() << folderInput << SEP << "thermal" << SEP << "series"; - ret = area.thermal.list.saveDataSeriesToFolder(buffer.c_str()) && ret; - ++progression; - }); + buffer.clear() << folderInput << SEP << "thermal" << SEP << "series"; + ret = area->thermal.list.saveDataSeriesToFolder(buffer.c_str()) && ret; + ++progression; + } } return ret; diff --git a/src/libs/antares/study/xcast/xcast.cpp b/src/libs/antares/study/xcast/xcast.cpp index 4233784088..51ed1624b0 100644 --- a/src/libs/antares/study/xcast/xcast.cpp +++ b/src/libs/antares/study/xcast/xcast.cpp @@ -201,19 +201,15 @@ bool XCast::loadFromFolder(const AnyString& folder) IniFile ini; if (ini.open(buffer)) { - // For each section - const IniFile::Property* p; - CString<30, false> key; - ini.each( - [&](const IniFile::Section& section) + [this, &buffer](const IniFile::Section& section) { // For each property if (section.name == "general") { - for (p = section.firstProperty; p != nullptr; p = p->next) + for (const IniFile::Property* p = section.firstProperty; p != nullptr; p = p->next) { - key = p->key; + CString<30, false> key = p->key; key.toLower(); if (key == "distribution") { diff --git a/src/libs/antares/sys/policy.cpp b/src/libs/antares/sys/policy.cpp index b9a60ea4af..0427c7441c 100644 --- a/src/libs/antares/sys/policy.cpp +++ b/src/libs/antares/sys/policy.cpp @@ -65,22 +65,20 @@ static void OpenFromINIFileWL(const String& filename, const StringT& hostname) return; } - PolicyKey key; - ShortString128 hostnameVersion; - ShortString128 hostnameAll; - hostnameVersion << hostname << ':' << ANTARES_VERSION; - hostnameAll << hostname << ":*"; + std::string hostnameVersion = hostname + ':' + ANTARES_VERSION; + std::string hostnameAll = hostname + ":*"; ini.each( - [&](const IniFile::Section& section) + [&hostnameVersion, &hostnameAll](const IniFile::Section& section) { // This section is dedicated to another host if (section.name == "*:*" or section.name == "*:" ANTARES_VERSION or section.name.equals(hostnameAll) or section.name.equals(hostnameVersion)) { section.each( - [&](const IniFile::Property& property) + [](const IniFile::Property& property) { + PolicyKey key; key = property.key; key.trim(" \t"); (*entries)[key] = property.value; diff --git a/src/solver/application/application.cpp b/src/solver/application/application.cpp index d221d19eb6..d222ad88c0 100644 --- a/src/solver/application/application.cpp +++ b/src/solver/application/application.cpp @@ -118,7 +118,7 @@ void Application::readDataForTheStudy(Data::StudyLoadOptions& options) std::exception_ptr loadingException; try { - pDurationCollector("study_loading") << [&] + pDurationCollector("study_loading") << [this, &study, &options] { if (study.loadFromFolder(pSettings.studyFolder, options)) { diff --git a/src/solver/hydro/management/daily.cpp b/src/solver/hydro/management/daily.cpp index 8391b523d0..5c6bcab897 100644 --- a/src/solver/hydro/management/daily.cpp +++ b/src/solver/hydro/management/daily.cpp @@ -414,10 +414,8 @@ inline void HydroManagement::prepareDailyOptimalGenerations( break; case NON: throw solutionNotFound(area.name.c_str(), y); - break; case EMERGENCY_SHUT_DOWN: throw fatalError(area.name.c_str(), y); - break; } H2O_J_Free(problem); @@ -535,10 +533,8 @@ inline void HydroManagement::prepareDailyOptimalGenerations( break; case NON: throw solutionNotFound(area.name.c_str(), y); - break; case EMERGENCY_SHUT_DOWN: throw fatalError(area.name.c_str(), y); - break; } H2O2_J_Free(problem); @@ -554,7 +550,7 @@ inline void HydroManagement::prepareDailyOptimalGenerations( void HydroManagement::prepareDailyOptimalGenerations(uint y, Antares::Data::Area::ScratchMap& scratchmap) { - areas_.each([&](Data::Area& area) + areas_.each([this, &scratchmap, &y](Data::Area& area) { prepareDailyOptimalGenerations(area, y, scratchmap); }); } } // namespace Antares diff --git a/src/solver/hydro/management/management.cpp b/src/solver/hydro/management/management.cpp index b20be5e375..6cea703442 100644 --- a/src/solver/hydro/management/management.cpp +++ b/src/solver/hydro/management/management.cpp @@ -143,7 +143,7 @@ HydroManagement::HydroManagement(const Data::AreaList& areas, void HydroManagement::prepareInflowsScaling(uint year) { areas_.each( - [&](const Data::Area& area) + [this, &year](const Data::Area& area) { const auto& srcinflows = area.hydro.series->storage.getColumn(year); @@ -440,7 +440,7 @@ void HydroManagement::prepareNetDemand(uint year, void HydroManagement::prepareEffectiveDemand() { areas_.each( - [&](Data::Area& area) + [this](Data::Area& area) { auto& data = tmpDataByArea_[&area]; @@ -453,7 +453,7 @@ void HydroManagement::prepareEffectiveDemand() double effectiveDemand = 0; // area.hydro.allocation is indexed by area index area.hydro.allocation.eachNonNull( - [&](unsigned areaIndex, double value) + [this, &effectiveDemand, &day](unsigned areaIndex, double value) { const auto* area = areas_.byIndex[areaIndex]; effectiveDemand += tmpDataByArea_[area].DLN[day] * value; diff --git a/src/solver/hydro/management/monthly.cpp b/src/solver/hydro/management/monthly.cpp index 2cb868da72..62ce897eb8 100644 --- a/src/solver/hydro/management/monthly.cpp +++ b/src/solver/hydro/management/monthly.cpp @@ -151,7 +151,7 @@ void HydroManagement::prepareMonthlyOptimalGenerations(double* random_reservoir_ { uint indexArea = 0; areas_.each( - [&](Data::Area& area) + [this, &random_reservoir_level, &y, &indexArea](Data::Area& area) { auto& data = tmpDataByArea_[&area]; diff --git a/src/solver/optimisation/post_process_commands.cpp b/src/solver/optimisation/post_process_commands.cpp index c90c8b4809..1b170ae353 100644 --- a/src/solver/optimisation/post_process_commands.cpp +++ b/src/solver/optimisation/post_process_commands.cpp @@ -47,7 +47,7 @@ void DispatchableMarginPostProcessCmd::execute(const optRuntimeData& opt_runtime unsigned int hourInYear = opt_runtime_data.hourInTheYear; unsigned int year = opt_runtime_data.year; area_list_.each( - [&](Data::Area& area) + [this, &hourInYear, &year](Data::Area& area) { double* dtgmrg = area.scratchpad[thread_number_].dispatchableGenerationMargin; for (uint h = 0; h != nbHoursInWeek; ++h) diff --git a/src/solver/simulation/common-eco-adq.cpp b/src/solver/simulation/common-eco-adq.cpp index b413b632d5..1f56235499 100644 --- a/src/solver/simulation/common-eco-adq.cpp +++ b/src/solver/simulation/common-eco-adq.cpp @@ -401,7 +401,7 @@ void SetInitialHydroLevel(Data::Study& study, const HYDRO_VENTILATION_RESULTS& hydroVentilationResults) { uint firstDaySimu = study.parameters.simulationDays.first; - study.areas.each([&](Data::Area& area) + study.areas.each([&problem, &firstDaySimu, &hydroVentilationResults](const Data::Area& area) { if (area.hydro.reservoirManagement) { diff --git a/src/solver/simulation/common-hydro-levels.cpp b/src/solver/simulation/common-hydro-levels.cpp index b322d62dec..81602073a1 100644 --- a/src/solver/simulation/common-hydro-levels.cpp +++ b/src/solver/simulation/common-hydro-levels.cpp @@ -33,54 +33,50 @@ void computingHydroLevels(const Data::AreaList& areas, bool remixWasRun, bool computeAnyway) { - areas.each( - [&](const Data::Area& area) - { - if (!area.hydro.reservoirManagement) - { - return; - } + for (const auto& [_, area] : areas) + { + if (!area->hydro.reservoirManagement) + { + continue; + } - if (not computeAnyway) - { - if (area.hydro.useHeuristicTarget != remixWasRun) - { - return; - } - } + if (!computeAnyway && area->hydro.useHeuristicTarget != remixWasRun) + { + continue; + } - uint index = area.index; + uint index = area->index; - double reservoirCapacity = area.hydro.reservoirCapacity; + double reservoirCapacity = area->hydro.reservoirCapacity; - std::vector& inflows = problem.CaracteristiquesHydrauliques[index] - .ApportNaturelHoraire; + std::vector& inflows = problem.CaracteristiquesHydrauliques[index] + .ApportNaturelHoraire; - RESULTATS_HORAIRES& weeklyResults = problem.ResultatsHoraires[index]; + RESULTATS_HORAIRES& weeklyResults = problem.ResultatsHoraires[index]; - std::vector& turb = weeklyResults.TurbinageHoraire; + std::vector& turb = weeklyResults.TurbinageHoraire; - std::vector& pump = weeklyResults.PompageHoraire; - double pumpingRatio = area.hydro.pumpingEfficiency; + std::vector& pump = weeklyResults.PompageHoraire; + double pumpingRatio = area->hydro.pumpingEfficiency; - double nivInit = problem.CaracteristiquesHydrauliques[index].NiveauInitialReservoir; - std::vector& niv = weeklyResults.niveauxHoraires; + double nivInit = problem.CaracteristiquesHydrauliques[index].NiveauInitialReservoir; + std::vector& niv = weeklyResults.niveauxHoraires; - std::vector& ovf = weeklyResults.debordementsHoraires; + std::vector& ovf = weeklyResults.debordementsHoraires; - computeTimeStepLevel + computeTimeStepLevel computeLvlObj(nivInit, inflows, ovf, turb, pumpingRatio, pump, reservoirCapacity); - for (uint h = 0; h < nbHoursInAWeek - 1; h++) - { - computeLvlObj.run(); - niv[h] = computeLvlObj.getLevel() * 100 / reservoirCapacity; - computeLvlObj.prepareNextStep(); - } + for (uint h = 0; h < nbHoursInAWeek - 1; h++) + { + computeLvlObj.run(); + niv[h] = computeLvlObj.getLevel() * 100 / reservoirCapacity; + computeLvlObj.prepareNextStep(); + } - computeLvlObj.run(); - niv[nbHoursInAWeek - 1] = computeLvlObj.getLevel() * 100 / reservoirCapacity; - }); + computeLvlObj.run(); + niv[nbHoursInAWeek - 1] = computeLvlObj.getLevel() * 100 / reservoirCapacity; + } } void interpolateWaterValue(const Data::AreaList& areas, @@ -98,65 +94,63 @@ void interpolateWaterValue(const Data::AreaList& areas, daysOfWeek[d] = weekFirstDay + d; } - areas.each( - [&](const Data::Area& area) - { - uint index = area.index; + for (const auto& [_, area] : areas) + { + uint index = area->index; - RESULTATS_HORAIRES& weeklyResults = problem.ResultatsHoraires[index]; + RESULTATS_HORAIRES& weeklyResults = problem.ResultatsHoraires[index]; - auto& waterVal = weeklyResults.valeurH2oHoraire; - std::fill(waterVal.begin(), waterVal.end(), 0.); + auto& waterVal = weeklyResults.valeurH2oHoraire; + std::fill(waterVal.begin(), waterVal.end(), 0.); - if (!area.hydro.reservoirManagement || !area.hydro.useWaterValue) - { - return; - } + if (!area->hydro.reservoirManagement || !area->hydro.useWaterValue) + { + return; + } - if (!area.hydro.useWaterValue) - { - return; - } + if (!area->hydro.useWaterValue) + { + return; + } - double reservoirCapacity = area.hydro.reservoirCapacity; + double reservoirCapacity = area->hydro.reservoirCapacity; - std::vector& niv = weeklyResults.niveauxHoraires; + std::vector& niv = weeklyResults.niveauxHoraires; - waterVal[0] = Data::getWaterValue(problem.previousSimulationFinalLevel[index] * 100 - / reservoirCapacity, - area.hydro.waterValues, - weekFirstDay); + waterVal[0] = Data::getWaterValue(problem.previousSimulationFinalLevel[index] * 100 + / reservoirCapacity, + area->hydro.waterValues, + weekFirstDay); - for (uint h = 1; h < nbHoursInAWeek; h++) - { - waterVal[h] = Data::getWaterValue(niv[h - 1], - area.hydro.waterValues, - daysOfWeek[h / 24]); - } - }); + for (uint h = 1; h < nbHoursInAWeek; h++) + { + waterVal[h] = Data::getWaterValue(niv[h - 1], + area->hydro.waterValues, + daysOfWeek[h / 24]); + } + } } void updatingWeeklyFinalHydroLevel(const Data::AreaList& areas, PROBLEME_HEBDO& problem) { - areas.each( - [&](const Data::Area& area) - { - if (!area.hydro.reservoirManagement) - { - return; - } + for (const auto& [_, area] : areas) + { + if (!area->hydro.reservoirManagement) + { + continue; + } - uint index = area.index; + uint index = area->index; - double reservoirCapacity = area.hydro.reservoirCapacity; + double reservoirCapacity = area->hydro.reservoirCapacity; - RESULTATS_HORAIRES& weeklyResults = problem.ResultatsHoraires[index]; + RESULTATS_HORAIRES& weeklyResults = problem.ResultatsHoraires[index]; - std::vector& niv = weeklyResults.niveauxHoraires; + std::vector& niv = weeklyResults.niveauxHoraires; - problem.previousSimulationFinalLevel[index] = niv[nbHoursInAWeek - 1] * reservoirCapacity - / 100; - }); + problem.previousSimulationFinalLevel[index] = niv[nbHoursInAWeek - 1] * reservoirCapacity + / 100; + } } } // namespace Antares::Solver::Simulation diff --git a/src/solver/simulation/common-hydro-remix.cpp b/src/solver/simulation/common-hydro-remix.cpp index a94b8f1dfe..dcf39052ce 100644 --- a/src/solver/simulation/common-hydro-remix.cpp +++ b/src/solver/simulation/common-hydro-remix.cpp @@ -51,7 +51,8 @@ static bool Remix(const Data::AreaList& areas, bool status = true; areas.each( - [&](const Data::Area& area) + [&HE, &DE, &remix, &G, &status, &problem, &numSpace, &hourInYear] + (const Data::Area& area) { auto index = area.index; diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index adf8c23c4d..8084130e9f 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -159,7 +159,7 @@ public: simulation_->prepareClustersInMustRunMode(scratchmap, y); // 4 - Hydraulic ventilation - pDurationCollector("hydro_ventilation") << [&] { + pDurationCollector("hydro_ventilation") << [this, &randomReservoirLevel] { hydroManagement.makeVentilation(randomReservoirLevel, y, scratchmap); @@ -205,7 +205,7 @@ public: // 9 - Write results for the current year if (yearByYear) { - pDurationCollector("yby_export") << [&] + pDurationCollector("yby_export") << [this] { // Before writing, some variable may require minor modifications simulation_->variables.beforeYearByYearExport(y, numSpace); @@ -776,7 +776,8 @@ void ISimulation::computeRandomNumbers( bool SpilledEnergySeedIsDefault = (currentSpilledEnergySeed == defaultSpilledEnergySeed); areaIndex = 0; study.areas.each( - [&](Data::Area& area) + [&isPerformed, &areaIndex, &randomUnsupplied, &randomSpilled, &randomForYears, &indexYear, + &SpilledEnergySeedIsDefault](Data::Area& area) { (void)area; // Avoiding warnings at compilation (unused variable) on linux if (isPerformed) @@ -938,7 +939,7 @@ static inline void logPerformedYearsInAset(setOfParallelYears& set) std::string performedYearsToLog = ""; std::ranges::for_each(set.yearsIndices, - [&](const uint& y) + [&set, &performedYearsToLog](const uint& y) { if (set.isYearPerformed[y]) { diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index 155cb6b3bc..ed13d54fe1 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -198,7 +198,7 @@ void SIM_InitialisationProblemeHebdo(Data::Study& study, problem.CaracteristiquesHydrauliques[i].TailleReservoir = area.hydro.reservoirCapacity; - for (int pdt = 0; pdt < NombreDePasDeTemps; pdt++) + for (unsigned pdt = 0; pdt < NombreDePasDeTemps; pdt++) { problem.CaracteristiquesHydrauliques[i].NiveauHoraireInf[pdt] = 0; problem.CaracteristiquesHydrauliques[i].NiveauHoraireSup[pdt] diff --git a/src/solver/ts-generator/generator.cpp b/src/solver/ts-generator/generator.cpp index 2cb4227755..3423a9a8ad 100644 --- a/src/solver/ts-generator/generator.cpp +++ b/src/solver/ts-generator/generator.cpp @@ -27,7 +27,7 @@ namespace Antares::TSGenerator void ResizeGeneratedTimeSeries(Data::AreaList& areas, Data::Parameters& params) { areas.each( - [&](Data::Area& area) + [¶ms](Data::Area& area) { // Load if (params.timeSeriesToGenerate & Data::timeSeriesLoad) diff --git a/src/solver/ts-generator/hydro.cpp b/src/solver/ts-generator/hydro.cpp index 1b4ac3496d..29d0a29f42 100644 --- a/src/solver/ts-generator/hydro.cpp +++ b/src/solver/ts-generator/hydro.cpp @@ -42,7 +42,7 @@ static void PreproRoundAllEntriesPlusDerated(Data::Study& study) bool derated = study.parameters.derated; study.areas.each( - [&](Data::Area& area) + [&derated](Data::Area& area) { auto& hydroseries = *(area.hydro.series); @@ -180,7 +180,6 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu uint month = i % 12; uint realmonth = calendar.months[month].realmonth; - uint daysPerMonth = calendar.months[month].days; assert(l < series.ror.timeSeries.width); assert(not std::isnan(colPOW[realmonth])); @@ -283,11 +282,11 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu else { logs.info() << "Archiving the hydro time-series"; - const int precision = 0; - Yuni::String output; study.areas.each( - [&](const Data::Area& area) + [&study, ¤tYear, &writer, &progression](const Data::Area& area) { + const int precision = 0; + Yuni::String output; study.buffer.clear() << "ts-generator" << SEP << "hydro" << SEP << "mc-" << currentYear << SEP << area.id; diff --git a/src/solver/ts-generator/xcast/xcast.cpp b/src/solver/ts-generator/xcast/xcast.cpp index feb8412f49..ca0c96087a 100644 --- a/src/solver/ts-generator/xcast/xcast.cpp +++ b/src/solver/ts-generator/xcast/xcast.cpp @@ -86,7 +86,7 @@ void XCast::exportTimeSeriesToTheOutput(Progression::Task& progression, Predicat filename.reserve(output.size() + 80); study.areas.each( - [&](Data::Area& area) + [this, &filename, &progression, &predicate, &output](Data::Area& area) { filename.clear() << output << SEP << area.id << ".txt"; std::string buffer; @@ -593,7 +593,7 @@ bool XCast::runWithPredicate(PredicateT& predicate, Progression::Task& progressi if (study.parameters.derated) { - study.areas.each([&](Data::Area& area) { predicate.matrix(area).averageTimeseries(); }); + study.areas.each([&predicate](Data::Area& area) { predicate.matrix(area).averageTimeseries(); }); } if (study.parameters.timeSeriesToArchive & timeSeriesType) diff --git a/src/solver/variable/include/antares/solver/variable/area.hxx b/src/solver/variable/include/antares/solver/variable/area.hxx index 4fdeb096c0..d3b34bb973 100644 --- a/src/solver/variable/include/antares/solver/variable/area.hxx +++ b/src/solver/variable/include/antares/solver/variable/area.hxx @@ -369,7 +369,7 @@ void Areas::hourForEachArea(State& state, uint numSpace) { // For each area... state.study.areas.each( - [&](Data::Area& area) + [this, &state, &numSpace](Data::Area& area) { state.area = &area; // the current area @@ -402,7 +402,7 @@ void Areas::weekForEachArea(State& state, uint numSpace) { // For each area... state.study.areas.each( - [&](Data::Area& area) + [this, &state, &numSpace](Data::Area& area) { state.area = &area; // the current area @@ -437,7 +437,7 @@ void Areas::yearEndBuild(State& state, uint year, uint numSpace) { // For each area... state.study.areas.each( - [&](Data::Area& area) + [this, &state, &year, &numSpace](Data::Area& area) { state.area = &area; // the current area diff --git a/src/solver/variable/surveyresults/surveyresults.cpp b/src/solver/variable/surveyresults/surveyresults.cpp index e05283ff5a..0402fb6c15 100644 --- a/src/solver/variable/surveyresults/surveyresults.cpp +++ b/src/solver/variable/surveyresults/surveyresults.cpp @@ -127,7 +127,7 @@ static void ExportGridInfosAreas(const Data::Study& study, "marginal cost\tfixed cost\tstartup cost\tmarket bid cost\tspread cost\n"; study.areas.each( - [&](const Data::Area& area) + [&out, &outLinks, &outThermal](const Data::Area& area) { out << area.id << '\t'; out << area.name << '\n'; From 9be866cf4aec0a3a8b4b415ef8e59508adedddda Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:02:03 +0200 Subject: [PATCH 06/15] Thermal validation and loading (#2173) Modular loading: separating loading and validation functions --- src/libs/antares/study/area/list.cpp | 3 +- .../study/include/antares/study/area/area.h | 2 +- .../study/parts/thermal/cluster_list.h | 3 + .../antares/study/parts/thermal/cluster.cpp | 14 -- .../study/parts/thermal/cluster_list.cpp | 151 ++++++++---------- 5 files changed, 75 insertions(+), 98 deletions(-) diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index 219f7ee8aa..b622b94af9 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -376,7 +376,6 @@ bool saveAreaAdequacyPatchIniFile(const Area& area, const Clob& buffer) } AreaList::AreaList(Study& study): - byIndex(nullptr), pStudy(study) { } @@ -994,6 +993,7 @@ static bool AreaListLoadFromFolderSingleArea(Study& study, { buffer.clear() << study.folderInput << SEP << "thermal" << SEP << "prepro"; ret = area.thermal.list.loadPreproFromFolder(study, buffer) && ret; + ret = area.thermal.list.validatePrepro(study) && ret; buffer.clear() << study.folderInput << SEP << "thermal" << SEP << "series"; ret = area.thermal.list.loadDataSeriesFromFolder(study, buffer) && ret; ret = area.thermal.list.loadEconomicCosts(study, buffer) && ret; @@ -1182,6 +1182,7 @@ bool AreaList::loadFromFolder(const StudyLoadOptions& options) Area& area = *(i->second); buffer.clear() << pStudy.folderInput << thermalPlant << area.id; ret = area.thermal.list.loadFromFolder(pStudy, buffer.c_str(), &area) && ret; + ret = area.thermal.list.validateClusters(pStudy.parameters) && ret; } } diff --git a/src/libs/antares/study/include/antares/study/area/area.h b/src/libs/antares/study/include/antares/study/area/area.h index ede65679a2..6661ab8556 100644 --- a/src/libs/antares/study/include/antares/study/area/area.h +++ b/src/libs/antares/study/include/antares/study/area/area.h @@ -689,7 +689,7 @@ class AreaList final: public Yuni::NonCopyable public: //! All areas by their index - Area** byIndex; + Area** byIndex = nullptr; //! All areas in the list Area::Map areas; diff --git a/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h b/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h index ea1518750d..9f091defda 100644 --- a/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h +++ b/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h @@ -113,6 +113,9 @@ class ThermalClusterList: public ClusterList ** \return A non-zero value if the operation succeeded, 0 otherwise */ bool loadPreproFromFolder(Study& s, const AnyString& folder); + bool validatePrepro(const Study& study); + + bool validateClusters(const Parameters& param) const; bool loadEconomicCosts(Study& s, const AnyString& folder); diff --git a/src/libs/antares/study/parts/thermal/cluster.cpp b/src/libs/antares/study/parts/thermal/cluster.cpp index 6a008f8929..da502ac29e 100644 --- a/src/libs/antares/study/parts/thermal/cluster.cpp +++ b/src/libs/antares/study/parts/thermal/cluster.cpp @@ -512,20 +512,6 @@ bool Data::ThermalCluster::integrityCheck() bool ret = true; - if (minUpTime > 168 or 0 == minUpTime) - { - logs.error() << "Thermal cluster " << parentArea->name << "/" << pName - << ": The min. up time must be between 1 and 168"; - minUpTime = 1; - ret = false; - } - if (minDownTime > 168 or 0 == minDownTime) - { - logs.error() << "Thermal cluster " << parentArea->name << "/" << pName - << ": The min. down time must be between 1 and 168"; - minDownTime = 1; - ret = false; - } if (nominalCapacity < 0.) { logs.error() << "Thermal cluster " << parentArea->name << "/" << pName diff --git a/src/libs/antares/study/parts/thermal/cluster_list.cpp b/src/libs/antares/study/parts/thermal/cluster_list.cpp index 2059cf9238..4bf2e042c2 100644 --- a/src/libs/antares/study/parts/thermal/cluster_list.cpp +++ b/src/libs/antares/study/parts/thermal/cluster_list.cpp @@ -159,52 +159,56 @@ bool ThermalClusterList::loadFromFolder(Study& study, const AnyString& folder, A options = Matrix<>::optFixedSize, }; - bool r = cluster->modulation.loadFromCSVFile(modulationFile, - thermalModulationMax, - HOURS_PER_YEAR, - options); - if (!r && study.usedByTheSolver) + ret = cluster->modulation.loadFromCSVFile(modulationFile, + thermalModulationMax, + HOURS_PER_YEAR, + options) && ret; + + // Check the data integrity of the cluster + addToCompleteList(cluster); + } + + rebuildIndexes(); + rebuildIndex(); + + return ret; +} + + +bool ThermalClusterList::validateClusters(const Parameters& parameters) const +{ + bool ret = true; + + for (const auto& cluster : allClusters_) + { + cluster->minUpTime = std::clamp(cluster->minUpTime, 1u, 168u); + cluster->minDownTime = std::clamp(cluster->minDownTime, 1u, 168u); + + // update the minUpDownTime + cluster->minUpDownTime = std::max(cluster->minUpTime, cluster->minDownTime); + + if (!parameters.include.thermal.minStablePower) + { + cluster->minStablePower = 0.; + } + if (!parameters.include.thermal.minUPTime) { - cluster->modulation.reset(thermalModulationMax, HOURS_PER_YEAR); - cluster->modulation.fill(1.); - cluster->modulation.fillColumn(thermalMinGenModulation, 0.); + cluster->minUpDownTime = 1; + cluster->minUpTime = 1; + cluster->minDownTime = 1; } - ret = ret && r; - // Special operations when not ran from the interface (aka solver) - if (study.usedByTheSolver) + if (!parameters.include.reserve.spinning) { - if (!study.parameters.include.thermal.minStablePower) - { - cluster->minStablePower = 0.; - } - if (!study.parameters.include.thermal.minUPTime) - { - cluster->minUpDownTime = 1; - cluster->minUpTime = 1; - cluster->minDownTime = 1; - } - else - { - cluster->minUpDownTime = std::max(cluster->minUpTime, cluster->minDownTime); - } + cluster->spinning = 0; + } - if (!study.parameters.include.reserve.spinning) - { - cluster->spinning = 0; - } + cluster->nominalCapacityWithSpinning = cluster->nominalCapacity; - cluster->nominalCapacityWithSpinning = cluster->nominalCapacity; - } + ret = cluster->integrityCheck() && ret; - // Check the data integrity of the cluster - cluster->integrityCheck(); - addToCompleteList(cluster); } - rebuildIndexes(); - rebuildIndex(); - return ret; } @@ -268,35 +272,11 @@ static bool ThermalClusterLoadFromProperty(ThermalCluster& cluster, const IniFil if (p->key == "min-up-time") { - if (p->value.to(cluster.minUpTime)) - { - if (cluster.minUpTime < 1) - { - cluster.minUpTime = 1; - } - if (cluster.minUpTime > 168) - { - cluster.minUpTime = 168; - } - return true; - } - return false; + return p->value.to(cluster.minUpTime); } if (p->key == "min-down-time") { - if (p->value.to(cluster.minDownTime)) - { - if (cluster.minDownTime < 1) - { - cluster.minDownTime = 1; - } - if (cluster.minDownTime > 168) - { - cluster.minDownTime = 168; - } - return true; - } - return false; + return p->value.to(cluster.minDownTime); } if (p->key == "name") { @@ -375,8 +355,6 @@ bool ThermalClusterLoadFromSection(const AnyString& filename, << property->key << "`: The property is unknown and ignored"; } } - // update the minUpDownTime - cluster.minUpDownTime = std::max(cluster.minUpTime, cluster.minDownTime); } return true; } @@ -399,7 +377,7 @@ void ThermalClusterList::reverseCalculationOfSpinning() void ThermalClusterList::enableMustrunForEveryone() { - for (auto& c: allClusters_) + for (const auto& c : allClusters_) { c->mustrun = true; } @@ -606,32 +584,41 @@ bool ThermalClusterList::saveEconomicCosts(const AnyString& folder) const bool ThermalClusterList::loadPreproFromFolder(Study& study, const AnyString& folder) { - const bool globalThermalTSgeneration = study.parameters.timeSeriesToGenerate - & timeSeriesThermal; - Clob buffer; auto hasPrepro = [](auto c) { return (bool)c->prepro; }; - auto loadAndCheckPrepro = [&buffer, &folder, &study, &globalThermalTSgeneration](auto c) + auto loadPrepro = [&buffer, &folder, &study](auto& c) { assert(c->parentArea && "cluster: invalid parent area"); buffer.clear() << folder << SEP << c->parentArea->id << SEP << c->id(); - bool result = c->prepro->loadFromFolder(study, buffer); + return c->prepro->loadFromFolder(study, buffer); + }; - if (study.usedByTheSolver && globalThermalTSgeneration) - { - result = c->prepro->validate() && result; - } + return std::ranges::all_of(allClusters_ | std::views::filter(hasPrepro), loadPrepro); +} - if (result && study.usedByTheSolver && c->doWeGenerateTS(globalThermalTSgeneration)) - { - result = c->prepro->normalizeAndCheckNPO(); - } - return result; - }; +bool ThermalClusterList::validatePrepro(const Study& study) { + auto hasPrepro = [](auto c) { return (bool)c->prepro; }; + + const bool globalThermalTSgeneration = + study.parameters.timeSeriesToGenerate & timeSeriesThermal; + + if (!study.usedByTheSolver) + return true; - return std::ranges::all_of(allClusters_ | std::views::filter(hasPrepro), loadAndCheckPrepro); + return std::ranges::all_of( + allClusters_ | std::views::filter(hasPrepro), + [&globalThermalTSgeneration](auto& c) { + if (globalThermalTSgeneration && !c->prepro->validate()) { + return false; + } + + if (c->doWeGenerateTS(globalThermalTSgeneration)) { + return c->prepro->normalizeAndCheckNPO(); + } + return true; + }); } bool ThermalClusterList::loadEconomicCosts(Study& study, const AnyString& folder) From 7dbb39cf91dd3d63d49fe55222407af550668859 Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Thu, 20 Jun 2024 09:37:18 +0200 Subject: [PATCH 07/15] Separation of loading and validation for renewables clusters [ANT-1213] (#2175) --- src/libs/antares/study/area/list.cpp | 1 + .../study/parts/renewable/cluster_list.h | 10 +++++----- .../study/parts/renewable/cluster_list.cpp | 19 +++++++++++++------ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index b622b94af9..fd2b3bb5b7 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -1222,6 +1222,7 @@ bool AreaList::loadFromFolder(const StudyLoadOptions& options) Area& area = *(i->second); buffer.clear() << pStudy.folderInput << renewablePlant << area.id; ret = area.renewable.list.loadFromFolder(buffer.c_str(), &area) && ret; + ret = area.renewable.list.validateClusters() && ret; } } diff --git a/src/libs/antares/study/include/antares/study/parts/renewable/cluster_list.h b/src/libs/antares/study/include/antares/study/parts/renewable/cluster_list.h index edec4d1468..275d495020 100644 --- a/src/libs/antares/study/include/antares/study/parts/renewable/cluster_list.h +++ b/src/libs/antares/study/include/antares/study/parts/renewable/cluster_list.h @@ -26,9 +26,7 @@ #include "../common/cluster_list.h" #include "cluster.h" -namespace Antares -{ -namespace Data +namespace Antares::Data { /*! ** \brief List of renewable clusters @@ -39,10 +37,12 @@ class RenewableClusterList: public ClusterList public: std::string typeID() const override; uint64_t memoryUsage() const override; + bool loadFromFolder(const AnyString& folder, Area* area); + bool validateClusters() const; + bool saveToFolder(const AnyString& folder) const override; }; // class RenewableClusterList -} // namespace Data -} // namespace Antares +} // namespace Antares::Data #endif /* __ANTARES_LIBS_STUDY_PARTS_RENEWABLE_CLUSTER_LIST_H__ */ diff --git a/src/libs/antares/study/parts/renewable/cluster_list.cpp b/src/libs/antares/study/parts/renewable/cluster_list.cpp index d42e9c8d89..62db61a900 100644 --- a/src/libs/antares/study/parts/renewable/cluster_list.cpp +++ b/src/libs/antares/study/parts/renewable/cluster_list.cpp @@ -28,9 +28,7 @@ using namespace Yuni; -namespace Antares -{ -namespace Data +namespace Antares::Data { #define SEP IO::Separator @@ -211,7 +209,6 @@ bool RenewableClusterList::loadFromFolder(const AnyString& folder, Area* area) continue; } - cluster->integrityCheck(); addToCompleteList(cluster); } } @@ -223,7 +220,17 @@ bool RenewableClusterList::loadFromFolder(const AnyString& folder, Area* area) return false; } +bool RenewableClusterList::validateClusters() const +{ + bool ret = true; + for (const auto& cluster : allClusters_) + { + ret = cluster->integrityCheck() && ret; + } + + return ret; +} + #undef SEP -} // namespace Data -} // namespace Antares +} // namespace Antares::Data From 9e6cdcc717a4fbdd2bc11c55a939d99a90b9428a Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:01:05 +0200 Subject: [PATCH 08/15] Add ts-generation for links [ANT-1084] (#1986) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Florian Omnès <26088210+flomnes@users.noreply.github.com> Co-authored-by: Florian OMNES Co-authored-by: guilpier-code <62292552+guilpier-code@users.noreply.github.com> --- .github/workflows/ubuntu.yml | 9 ++ .github/workflows/windows-vcpkg.yml | 11 +- docs/user-guide/04-migration-guides.md | 20 ++++ src/libs/antares/study/area/links.cpp | 106 +++++++++++++++++- src/libs/antares/study/area/list.cpp | 2 +- .../antares/study/cleaner/cleaner-v20.cpp | 2 +- src/libs/antares/study/fwd.cpp | 4 + .../study/include/antares/study/area/area.h | 3 +- .../study/include/antares/study/area/links.h | 5 + .../antares/study/include/antares/study/fwd.h | 2 + .../include/antares/study/load-options.h | 4 + .../study/include/antares/study/parameters.h | 2 + src/libs/antares/study/parameters.cpp | 9 ++ .../antares/solver/simulation/solver.hxx | 1 + src/solver/ts-generator/availability.cpp | 90 ++++++++++++++- .../antares/solver/ts-generator/generator.h | 13 +++ .../antares/solver/ts-generator/prepro.h | 21 ++++ src/tools/ts-generator/main.cpp | 70 +++++++++++- 18 files changed, 362 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index c774854d16..85713eeb70 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -229,6 +229,15 @@ jobs: os: ${{ env.os }} variant: "parallel" + - name: Run tests for time series generator tool + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{steps.simtest-version.outputs.prop}} + batch-name: ts-generator + os: ${{ env.os }} + variant: "tsgenerator" + - name: Run medium-tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests diff --git a/.github/workflows/windows-vcpkg.yml b/.github/workflows/windows-vcpkg.yml index ccc8794dcb..59e931e6a8 100644 --- a/.github/workflows/windows-vcpkg.yml +++ b/.github/workflows/windows-vcpkg.yml @@ -246,6 +246,15 @@ jobs: os: ${{ env.test-platform }} variant: "parallel" + - name: Run tests for time series generator tool + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{steps.simtest-version.outputs.prop}} + batch-name: ts-generator + os: ${{ env.test-platform }} + variant: "tsgenerator" + - name: Run medium-tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests @@ -304,7 +313,7 @@ jobs: run: | cd _build cpack -G ZIP - + - name: Installer upload uses: actions/upload-artifact@v4 with: diff --git a/docs/user-guide/04-migration-guides.md b/docs/user-guide/04-migration-guides.md index 0e5f50726c..5dbc3f8885 100644 --- a/docs/user-guide/04-migration-guides.md +++ b/docs/user-guide/04-migration-guides.md @@ -1,5 +1,25 @@ # Migration guides This is a list of all recent changes that came with new Antares Simulator features. The main goal of this document is to lower the costs of changing existing interfaces, both GUI and scripts. + +## v9.2.0 +### (TS-generator only) TS generation for link capacities +In files input/links//properties.ini, add the following properties +- tsgen_direct_XXX, +- tsgen_indirect_XXX +with XXX in +- unitcount (unsigned int, default 1) +- nominalcapacity (float) +- law.planned (string "uniform"/"geometric") +- law.forced (same) +- volatility.planned (double in [0,1]) +- volatility.forced (same) + +- "prepro" timeseries => input/links//prepro/_{direct, indirect}.txt, 365x6 values, respectively "forced outage duration", "planned outage duration", "forced outage rate", "planned outage rate", "minimum of groups in maintenance", "maximum of groups in maintenance". +- "modulation" timeseries => input/links//prepro/_mod_{direct, indirect}.txt, 8760x1 values each in [0, 1] +- number of TS to generate => generaldata.ini/General/nbtimeserieslinks (unsigned int, default value 1) + + + ## v9.1.0 ### Input #### Hydro Maximum Generation/Pumping Power diff --git a/src/libs/antares/study/area/links.cpp b/src/libs/antares/study/area/links.cpp index 4d4b6af140..40da9df149 100644 --- a/src/libs/antares/study/area/links.cpp +++ b/src/libs/antares/study/area/links.cpp @@ -21,8 +21,11 @@ #include "antares/study/area/links.h" +#include #include +#include + #include #include @@ -134,6 +137,58 @@ bool AreaLink::linkLoadTimeSeries_for_version_820_and_later(const AnyString& fol return success; } +// This function is "lazy", it only loads files if they exist +// and set a `valid` flag +bool AreaLink::loadTSGenTimeSeries(const fs::path& folder) +{ + const std::string idprepro = std::string(from->id) + "/" + std::string(with->id); + tsGeneration.prepro = + std::make_unique(idprepro, tsGeneration.unitCount); + + bool anyFileWasLoaded = false; + + // file name without suffix, .txt for general infos and mod_direct/indirect.txt + fs::path preproFile = folder / "prepro" / with->id.c_str(); + + // Prepro + fs::path filepath = preproFile; + filepath += ".txt"; + if (fs::exists(filepath)) + { + anyFileWasLoaded = true; + tsGeneration.valid = tsGeneration.prepro->data.loadFromCSVFile( + filepath.string(), + Antares::Data::PreproAvailability::preproAvailabilityMax, + DAYS_PER_YEAR) + && tsGeneration.prepro->validate(); + } + + // Modulation + filepath = preproFile; + filepath += "_mod_direct.txt"; + if (fs::exists(filepath)) + { + anyFileWasLoaded = true; + tsGeneration.valid &= tsGeneration.modulationCapacityDirect + .loadFromCSVFile(filepath.string(), 1, HOURS_PER_YEAR); + } + + filepath = preproFile; + filepath += "_mod_indirect.txt"; + if (fs::exists(filepath)) + { + anyFileWasLoaded = true; + tsGeneration.valid &= tsGeneration.modulationCapacityIndirect + .loadFromCSVFile(filepath.string(), 1, HOURS_PER_YEAR); + } + + if (anyFileWasLoaded) + { + return tsGeneration.valid; + } + return true; +} + bool AreaLink::isLinkPhysical() const { // All link types are physical, except arVirt @@ -448,13 +503,55 @@ bool handleKey(Data::AreaLink& link, const String& key, const String& value) link.filterYearByYear = stringIntoDatePrecision(value); return true; } + return false; +} + +bool handleTSGenKey(Data::LinkTsGeneration& out, + const std::string& key, + const String& value) +{ + + if (key == "unitcount") + { + return value.to(out.unitCount); + } + + if (key == "nominalcapacity") + { + return value.to(out.nominalCapacity); + } + + if (key == "law.planned") + { + return value.to(out.plannedLaw); + } + + if (key == "law.forced") + { + return value.to(out.forcedLaw); + } + + if (key == "volatility.planned") + { + return value.to(out.plannedVolatility); + } + + if (key == "volatility.forced") + { + return value.to(out.forcedVolatility); + } + + if (key == "force-no-generation") + { + return value.to(out.forceNoGeneration); + } return false; } bool AreaLinksInternalLoadFromProperty(AreaLink& link, const String& key, const String& value) { - return handleKey(link, key, value); + return handleKey(link, key, value) || handleTSGenKey(link.tsGeneration, key, value); } [[noreturn]] void logLinkDataCheckError(const AreaLink& link, const String& msg, int hour) @@ -475,7 +572,7 @@ bool AreaLinksInternalLoadFromProperty(AreaLink& link, const String& key, const } } // anonymous namespace -bool AreaLinksLoadFromFolder(Study& study, AreaList* l, Area* area, const fs::path& folder) +bool AreaLinksLoadFromFolder(Study& study, AreaList* l, Area* area, const fs::path& folder, bool loadTSGen) { // Assert assert(area); @@ -601,6 +698,11 @@ bool AreaLinksLoadFromFolder(Study& study, AreaList* l, Area* area, const fs::pa } } + if (loadTSGen) + { + ret = link.loadTSGenTimeSeries(folder) && ret; + } + // From the solver only if (study.usedByTheSolver) { diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index fd2b3bb5b7..f76afb5686 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -881,7 +881,7 @@ static bool AreaListLoadFromFolderSingleArea(Study& study, // Links { fs::path folder = fs::path(study.folderInput.c_str()) / "links" / area.id.c_str(); - ret = AreaLinksLoadFromFolder(study, list, &area, folder) && ret; + ret = AreaLinksLoadFromFolder(study, list, &area, folder, options.linksLoadTSGen) && ret; } // UI diff --git a/src/libs/antares/study/cleaner/cleaner-v20.cpp b/src/libs/antares/study/cleaner/cleaner-v20.cpp index 3c8f183be4..7d1aadf58e 100644 --- a/src/libs/antares/study/cleaner/cleaner-v20.cpp +++ b/src/libs/antares/study/cleaner/cleaner-v20.cpp @@ -362,7 +362,7 @@ bool listOfFilesAnDirectoriesToKeep(StudyCleaningInfos* infos) logs.verbosityLevel = Logs::Verbosity::Warning::level; // load all links buffer.clear() << infos->folder << "/input/links/" << area->id; - if (not AreaLinksLoadFromFolder(*study, arealist, area, buffer.c_str())) + if (not AreaLinksLoadFromFolder(*study, arealist, area, buffer.c_str(), false)) { delete arealist; delete study; diff --git a/src/libs/antares/study/fwd.cpp b/src/libs/antares/study/fwd.cpp index 100a4b127e..f45f093aaf 100644 --- a/src/libs/antares/study/fwd.cpp +++ b/src/libs/antares/study/fwd.cpp @@ -53,6 +53,8 @@ const char* SeedToCString(SeedIndex seed) return "Noise on virtual Hydro costs"; case seedHydroManagement: return "Initial reservoir levels"; + case seedTsGenLinks: + return "Links time-series generation"; case seedMax: return ""; } @@ -85,6 +87,8 @@ const char* SeedToID(SeedIndex seed) return "seed-hydro-costs"; case seedHydroManagement: return "seed-initial-reservoir-levels"; + case seedTsGenLinks: + return "seed-tsgen-links"; case seedMax: return ""; } diff --git a/src/libs/antares/study/include/antares/study/area/area.h b/src/libs/antares/study/include/antares/study/area/area.h index 6661ab8556..6fbb69dde4 100644 --- a/src/libs/antares/study/include/antares/study/area/area.h +++ b/src/libs/antares/study/include/antares/study/area/area.h @@ -727,7 +727,8 @@ AreaLink* AreaAddLinkBetweenAreas(Area* area, Area* with, bool warning = true); bool AreaLinksLoadFromFolder(Study& s, AreaList* l, Area* area, - const std::filesystem::path& folder); + const std::filesystem::path& folder, + bool loadTSGen); /*! ** \brief Save interconnections of a given area into a folder (`input/areas/[area]/ntc`) diff --git a/src/libs/antares/study/include/antares/study/area/links.h b/src/libs/antares/study/include/antares/study/area/links.h index fd4b8e69d8..5b01ba3efd 100644 --- a/src/libs/antares/study/include/antares/study/area/links.h +++ b/src/libs/antares/study/include/antares/study/area/links.h @@ -71,6 +71,8 @@ class AreaLink final: public Yuni::NonCopyable bool loadTimeSeries(const StudyVersion& version, const AnyString& folder); + bool loadTSGenTimeSeries(const std::filesystem::path& folder); + void storeTimeseriesNumbers(Solver::IResultWriter& writer) const; //! \name Area @@ -206,6 +208,9 @@ class AreaLink final: public Yuni::NonCopyable int linkWidth; friend struct CompareLinkName; + + LinkTsGeneration tsGeneration; + }; // class AreaLink struct CompareLinkName final diff --git a/src/libs/antares/study/include/antares/study/fwd.h b/src/libs/antares/study/include/antares/study/fwd.h index 6fa2819aca..7256eda4a2 100644 --- a/src/libs/antares/study/include/antares/study/fwd.h +++ b/src/libs/antares/study/include/antares/study/fwd.h @@ -361,6 +361,8 @@ enum SeedIndex seedHydroCosts, //! Seed - Hydro management seedHydroManagement, + //! The seed for links + seedTsGenLinks, //! The number of seeds seedMax, }; diff --git a/src/libs/antares/study/include/antares/study/load-options.h b/src/libs/antares/study/include/antares/study/load-options.h index 898b641653..1ecd34b968 100644 --- a/src/libs/antares/study/include/antares/study/load-options.h +++ b/src/libs/antares/study/include/antares/study/load-options.h @@ -52,6 +52,10 @@ class StudyLoadOptions bool loadOnlyNeeded; //! Force the year-by-year flag bool forceYearByYear; + + //! Load data associated to link TS generation + bool linksLoadTSGen = false; + //! Force the derated mode bool forceDerated; diff --git a/src/libs/antares/study/include/antares/study/parameters.h b/src/libs/antares/study/include/antares/study/parameters.h index c66cf9757f..cb69fb4326 100644 --- a/src/libs/antares/study/include/antares/study/parameters.h +++ b/src/libs/antares/study/include/antares/study/parameters.h @@ -256,6 +256,8 @@ class Parameters final uint nbTimeSeriesThermal; //! Nb of timeSeries : Solar uint nbTimeSeriesSolar; + //! Nb of timeSeries : Links + uint nbLinkTStoGenerate = 1; //@} //! \name Time-series refresh diff --git a/src/libs/antares/study/parameters.cpp b/src/libs/antares/study/parameters.cpp index a598ee1566..5c0cdac583 100644 --- a/src/libs/antares/study/parameters.cpp +++ b/src/libs/antares/study/parameters.cpp @@ -528,6 +528,10 @@ static bool SGDIntLoadFamily_General(Parameters& d, { return value.to(d.nbTimeSeriesSolar); } + if (key == "nbtimeserieslinks") + { + return value.to(d.nbLinkTStoGenerate); + } // Interval values if (key == "refreshintervalload") { @@ -1054,6 +1058,10 @@ static bool SGDIntLoadFamily_SeedsMersenneTwister(Parameters& d, { return value.to(d.seed[seedTsGenSolar]); } + if (key == "seed_links") + { + return value.to(d.seed[seedTsGenLinks]); + } if (key == "seed_timeseriesnumbers") { return value.to(d.seed[seedTimeseriesNumbers]); @@ -1783,6 +1791,7 @@ void Parameters::saveToINI(IniFile& ini) const section->add("nbTimeSeriesWind", nbTimeSeriesWind); section->add("nbTimeSeriesThermal", nbTimeSeriesThermal); section->add("nbTimeSeriesSolar", nbTimeSeriesSolar); + section->add("nbtimeserieslinks", nbLinkTStoGenerate); // Refresh ParametersSaveTimeSeries(section, "refreshTimeSeries", timeSeriesToRefresh); diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index 8084130e9f..5e36fcaca8 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -938,6 +938,7 @@ static inline void logPerformedYearsInAset(setOfParallelYears& set) << " perfomed)"; std::string performedYearsToLog = ""; + std::ranges::for_each(set.yearsIndices, [&set, &performedYearsToLog](const uint& y) { diff --git a/src/solver/ts-generator/availability.cpp b/src/solver/ts-generator/availability.cpp index f9b50696df..69f62a10a1 100644 --- a/src/solver/ts-generator/availability.cpp +++ b/src/solver/ts-generator/availability.cpp @@ -50,12 +50,29 @@ AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(Data::ThermalCluster* s { } +AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(Data::LinkTsGeneration& source, + Data::TimeSeries& capacity, + Matrix<>& modulation, + const std::string& areaDestName): + unitCount(source.unitCount), + nominalCapacity(source.nominalCapacity), + forcedVolatility(source.forcedVolatility), + plannedVolatility(source.plannedVolatility), + forcedLaw(source.forcedLaw), + plannedLaw(source.plannedLaw), + prepro(source.prepro.get()), + series(capacity.timeSeries), + modulationCapacity(modulation[0]), + name(areaDestName) +{ +} + namespace { class GeneratorTempData final { public: - explicit GeneratorTempData(Data::Study&, unsigned); + explicit GeneratorTempData(Data::Study&, unsigned, MersenneTwister&); void generateTS(const Data::Area& area, AvailabilityTSGeneratorData& cluster) const; @@ -82,10 +99,10 @@ class GeneratorTempData final const T& duration) const; }; -GeneratorTempData::GeneratorTempData(Data::Study& study, unsigned nbOfSeriesToGen): +GeneratorTempData::GeneratorTempData(Data::Study& study, unsigned nbOfSeriesToGen, MersenneTwister& rndGenerator): derated(study.parameters.derated), nbOfSeriesToGen_(nbOfSeriesToGen), - rndgenerator(study.runtime->random[Data::seedTsGenThermal]) + rndgenerator(rndGenerator) { } @@ -592,6 +609,23 @@ std::vector getAllClustersToGen(const Data::AreaList& are return clusters; } +listOfLinks getAllLinksToGen(Data::AreaList& areas) +{ + listOfLinks links; + + areas.each( + [&links](const Data::Area& area) + { + std::ranges::for_each(area.links, [&links](auto& l) + { + if (!l.second->tsGeneration.forceNoGeneration) + links.push_back(l.second); + }); + }); + + return links; +} + void writeResultsToDisk(const Data::Study& study, Solver::IResultWriter& writer, const Matrix<>& series, @@ -617,7 +651,9 @@ bool generateThermalTimeSeries(Data::Study& study, bool archive = study.parameters.timeSeriesToArchive & Data::timeSeriesThermal; - auto generator = GeneratorTempData(study, study.parameters.nbTimeSeriesThermal); + auto generator = GeneratorTempData(study, + study.parameters.nbTimeSeriesThermal, + study.runtime->random[Data::seedTsGenThermal]); // TODO VP: parallel for (auto* cluster: clusters) @@ -636,4 +672,50 @@ bool generateThermalTimeSeries(Data::Study& study, return true; } +bool generateLinkTimeSeries(Data::Study& study, + const listOfLinks& links, + Solver::IResultWriter& writer, + const std::string& savePath) +{ + logs.info(); + logs.info() << "Generating the links time-series"; + + auto generator = GeneratorTempData(study, + study.parameters.nbLinkTStoGenerate, + study.runtime->random[Data::seedTsGenLinks]); + + for (const auto& link: links) + { + Data::TimeSeries ts(link->timeseriesNumbers); + ts.resize(study.parameters.nbLinkTStoGenerate, HOURS_PER_YEAR); + + auto& tsGenStruct = link->tsGeneration; + + if (!tsGenStruct.valid) + { + logs.error() << "Missing data for link " << link->from->id << "/" << link->with->id; + return false; + } + + // DIRECT + AvailabilityTSGeneratorData tsConfigDataDirect(tsGenStruct, ts, tsGenStruct.modulationCapacityDirect, link->with->name); + + generator.generateTS(*link->from, tsConfigDataDirect); + + std::string filePath = savePath + SEP + link->from->id + SEP + link->with->id.c_str() + + "_direct.txt"; + writeResultsToDisk(study, writer, ts.timeSeries, filePath); + + // INDIRECT + AvailabilityTSGeneratorData tsConfigDataIndirect(tsGenStruct, ts, tsGenStruct.modulationCapacityIndirect, link->with->name); + + generator.generateTS(*link->from, tsConfigDataIndirect); + + filePath = savePath + SEP + link->from->id + SEP + link->with->id.c_str() + + "_indirect.txt"; + writeResultsToDisk(study, writer, ts.timeSeries, filePath); + } + + return true; +} } // namespace Antares::TSGenerator diff --git a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h index 77e09b9b79..1c32e67bdd 100644 --- a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h +++ b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h @@ -39,6 +39,10 @@ class AvailabilityTSGeneratorData { public: explicit AvailabilityTSGeneratorData(Data::ThermalCluster*); + AvailabilityTSGeneratorData(Data::LinkTsGeneration&, + Data::TimeSeries&, + Matrix<>& modulation, + const std::string& name); const unsigned& unitCount; const double& nominalCapacity; @@ -58,6 +62,8 @@ class AvailabilityTSGeneratorData const std::string& name; }; +using listOfLinks = std::vector; + void ResizeGeneratedTimeSeries(Data::AreaList& areas, Data::Parameters& params); /*! @@ -71,9 +77,16 @@ bool generateThermalTimeSeries(Data::Study& study, Solver::IResultWriter& writer, const std::string& savePath); +bool generateLinkTimeSeries(Data::Study& study, + const listOfLinks& links, + Solver::IResultWriter& writer, + const std::string& savePath); + std::vector getAllClustersToGen(const Data::AreaList& areas, bool globalThermalTSgeneration); +listOfLinks getAllLinksToGen(Data::AreaList& areas); + /*! ** \brief Destroy all TS Generators */ diff --git a/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h b/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h index cc02a50619..ac36a826ea 100644 --- a/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h +++ b/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h @@ -115,6 +115,27 @@ class PreproAvailability unsigned int unitCount; }; // class PreproAvailability +struct LinkTsGeneration +{ + unsigned unitCount = 0; + double nominalCapacity = 0; + + double forcedVolatility = 0.; + double plannedVolatility = 0.; + + Data::StatisticalLaw forcedLaw = LawUniform; + Data::StatisticalLaw plannedLaw = LawUniform; + + std::unique_ptr prepro; + + Matrix<> modulationCapacityDirect; + Matrix<> modulationCapacityIndirect; + + bool valid = false; + + bool forceNoGeneration = false; +}; + } // namespace Antares::Data #endif // __ANTARES_LIBS_STUDY_PARTS_THERMAL_PREPRO_HXX__ diff --git a/src/tools/ts-generator/main.cpp b/src/tools/ts-generator/main.cpp index f59a5dc614..8f23f60619 100644 --- a/src/tools/ts-generator/main.cpp +++ b/src/tools/ts-generator/main.cpp @@ -35,6 +35,10 @@ #include #include +using namespace Antares; + +namespace fs = std::filesystem; + struct Settings { std::string studyFolder; @@ -43,6 +47,11 @@ struct Settings bool allThermal = false; /// generate TS for a list "area.cluster;area2.cluster2;" std::string thermalListToGen = ""; + + /// generate TS for all links if activated + bool allLinks = false; + /// generate TS for a list "area.link;area2.link2;" + std::string linksListToGen = ""; }; std::unique_ptr createTsGeneratorParser(Settings& settings) @@ -58,7 +67,14 @@ std::unique_ptr createTsGeneratorParser(Settings& settings ' ', "thermal", "Generate TS for a list of area IDs and thermal clusters IDs, " - "usage:\n\t--thermal=\"areaID.clusterID;area2ID.clusterID\""); + "\nusage: --thermal=\"areaID.clusterID;area2ID.clusterID\""); + + parser->addFlag(settings.allLinks, ' ', "all-links", "Generate TS capacities for all links"); + parser->addFlag(settings.linksListToGen, + ' ', + "links", + "Generate TS capacities for a list of 2 area IDs, " + "usage: --links=\"areaID.area2ID;area3ID.area1ID\""); parser->remainingArguments(settings.studyFolder); @@ -95,8 +111,32 @@ std::vector getClustersToGen(Data::AreaList& areas, return clusters; } +TSGenerator::listOfLinks getLinksToGen(Data::AreaList& areas, const std::string& linksToGen) +{ + TSGenerator::listOfLinks links; + const auto ids = splitStringIntoPairs(linksToGen, ';', '.'); + + for (const auto& [areaFromID, areaWithID]: ids) + { + logs.info() << "Searching for link: " << areaFromID << "/" << areaWithID; + + auto* link = areas.findLink(areaFromID, areaWithID); + if (!link) + { + logs.warning() << "Link not found: " << areaFromID << "/" << areaWithID; + continue; + } + + links.emplace_back(link); + } + + return links; +} + int main(int argc, char* argv[]) { + logs.applicationName("ts-generator"); + Settings settings; auto parser = createTsGeneratorParser(settings); @@ -119,9 +159,16 @@ int main(int argc, char* argv[]) return 1; } + if (settings.allLinks && !settings.linksListToGen.empty()) + { + logs.error() << "Conflicting options, either choose all links or a list"; + return 1; + } + auto study = std::make_shared(true); Data::StudyLoadOptions studyOptions; studyOptions.prepareOutput = true; + studyOptions.linksLoadTSGen = true; if (!study->loadFromFolder(settings.studyFolder, studyOptions)) { @@ -149,7 +196,8 @@ int main(int argc, char* argv[]) nullptr, durationCollector); - const auto thermalSavePath = std::filesystem::path("ts-generator") / "thermal"; + const auto thermalSavePath = fs::path("ts-generator") / "thermal"; + const auto linksSavePath = fs::path("ts-generator") / "links"; // THERMAL std::vector clusters; @@ -167,10 +215,28 @@ int main(int argc, char* argv[]) logs.debug() << c->id(); } + // LINKS + TSGenerator::listOfLinks links; + if (settings.allLinks) + { + links = TSGenerator::getAllLinksToGen(study->areas); + } + else if (!settings.linksListToGen.empty()) + { + links = getLinksToGen(study->areas, settings.linksListToGen); + } + + for (auto& l: links) + { + logs.debug() << l->getName(); + } + bool ret = TSGenerator::generateThermalTimeSeries(*study, clusters, *resultWriter, thermalSavePath.string()); + ret = TSGenerator::generateLinkTimeSeries(*study, links, *resultWriter, linksSavePath.string()) + && ret; return !ret; // return 0 for success } From d23369185ef4851a2b9c63345aab73f2b0cddea1 Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:36:38 +0200 Subject: [PATCH 09/15] Separation of loading and validation for parameters [ANT-1213] (#2177) --- .../study/include/antares/study/parameters.h | 5 +- src/libs/antares/study/load.cpp | 8 +- src/libs/antares/study/parameters.cpp | 153 ++++++++---------- .../study/parameters/parameters-tests.cpp | 4 + 4 files changed, 78 insertions(+), 92 deletions(-) diff --git a/src/libs/antares/study/include/antares/study/parameters.h b/src/libs/antares/study/include/antares/study/parameters.h index cb69fb4326..791e20f865 100644 --- a/src/libs/antares/study/include/antares/study/parameters.h +++ b/src/libs/antares/study/include/antares/study/parameters.h @@ -137,6 +137,8 @@ class Parameters final */ void fixBadValues(); + void validateOptions(const StudyLoadOptions&); + /*! ** \brief Try to detect then fix refresh intervals */ @@ -503,8 +505,7 @@ class Parameters final private: //! Load data from an INI file bool loadFromINI(const IniFile& ini, - const StudyVersion& version, - const StudyLoadOptions& options); + const StudyVersion& version); void resetPlayedYears(uint nbOfYears); diff --git a/src/libs/antares/study/load.cpp b/src/libs/antares/study/load.cpp index 00214717e8..4555e106bd 100644 --- a/src/libs/antares/study/load.cpp +++ b/src/libs/antares/study/load.cpp @@ -83,7 +83,13 @@ bool Study::internalLoadIni(const String& path, const StudyLoadOptions& options) } // Load the general data buffer.clear() << folderSettings << SEP << "generaldata.ini"; - if (!parameters.loadFromFile(buffer, header.version, options)) + bool errorWhileLoading = !parameters.loadFromFile(buffer, header.version, options); + + parameters.validateOptions(options); + + parameters.fixBadValues(); + + if (errorWhileLoading) { if (options.loadOnlyNeeded) { diff --git a/src/libs/antares/study/parameters.cpp b/src/libs/antares/study/parameters.cpp index 5c0cdac583..7337b9c8b0 100644 --- a/src/libs/antares/study/parameters.cpp +++ b/src/libs/antares/study/parameters.cpp @@ -566,43 +566,11 @@ static bool SGDIntLoadFamily_General(Parameters& d, if (key == "simulation.start") { - uint day; - if (not value.to(day)) - { - return false; - } - if (day == 0) - { - day = 1; - } - else - { - if (day > 365) - { - day = 365; - } - --day; - } - d.simulationDays.first = day; - return true; + return value.to(d.simulationDays.first); } if (key == "simulation.end") { - uint day; - if (not value.to(day)) - { - return false; - } - if (day == 0) - { - day = 1; - } - else if (day > 365) - { - day = 365; - } - d.simulationDays.end = day; // not included - return true; + return value.to(d.simulationDays.end); } if (key == "thematic-trimming") @@ -1158,8 +1126,7 @@ bool firstKeyLetterIsValid(const String& name) } bool Parameters::loadFromINI(const IniFile& ini, - const StudyVersion& version, - const StudyLoadOptions& options) + const StudyVersion& version) { // Reset inner data reset(); @@ -1229,62 +1196,10 @@ bool Parameters::loadFromINI(const IniFile& ini, } } - // forcing value - if (options.nbYears != 0) - { - if (options.nbYears > nbYears) - { - // The variable `yearsFilter` must be enlarged - yearsFilter.resize(options.nbYears, false); - } - nbYears = options.nbYears; - - // Resize years weight (add or remove item) - if (yearsWeight.size() != nbYears) - { - yearsWeight.resize(nbYears, 1.f); - } - } - - // Simulation mode - // ... Enforcing simulation mode - if (options.forceMode != SimulationMode::Unknown) - { - mode = options.forceMode; - logs.info() << " forcing the simulation mode " << SimulationModeToCString(mode); - } - else - { - logs.info() << " simulation mode: " << SimulationModeToCString(mode); - } - - if (options.forceDerated) - { - derated = true; - } - - namedProblems = options.namedProblems; - - handleOptimizationOptions(options); - - // Attempt to fix bad values if any - fixBadValues(); - fixRefreshIntervals(); fixGenRefreshForNTC(); - // Specific action before launching a simulation - if (options.usedByTheSolver) - { - prepareForSimulation(options); - } - - if (options.mpsToExport || options.namedProblems) - { - this->include.exportMPS = mpsExportStatus::EXPORT_BOTH_OPTIMS; - } - // We currently always returns true to not block any loading process // Anyway we already have reported all problems return true; @@ -1375,6 +1290,66 @@ void Parameters::fixBadValues() { nbTimeSeriesSolar = 1; } + + if (simulationDays.first == 0) + simulationDays.first = 1; + else + { + simulationDays.first = std::clamp(simulationDays.first, 1u, 365u); + --simulationDays.first; // value between 0 and 364 for edge cases + } + + simulationDays.end = std::clamp(simulationDays.end, 1u, 365u); +} + +void Parameters::validateOptions(const StudyLoadOptions& options) +{ + if (options.forceDerated) + { + derated = true; + } + // forcing value + if (options.nbYears != 0) + { + if (options.nbYears > nbYears) + { + // The variable `yearsFilter` must be enlarged + yearsFilter.resize(options.nbYears, false); + } + nbYears = options.nbYears; + + // Resize years weight (add or remove item) + if (yearsWeight.size() != nbYears) + { + yearsWeight.resize(nbYears, 1.f); + } + } + + // Simulation mode + // ... Enforcing simulation mode + if (options.forceMode != SimulationMode::Unknown) + { + mode = options.forceMode; + logs.info() << " forcing the simulation mode " << SimulationModeToCString(mode); + } + else + { + logs.info() << " simulation mode: " << SimulationModeToCString(mode); + } + // Specific action before launching a simulation + if (options.usedByTheSolver) + { + prepareForSimulation(options); + } + + if (options.mpsToExport || options.namedProblems) + { + this->include.exportMPS = mpsExportStatus::EXPORT_BOTH_OPTIMS; + } + + namedProblems = options.namedProblems; + + handleOptimizationOptions(options); } uint64_t Parameters::memoryUsage() const @@ -1990,7 +1965,7 @@ bool Parameters::loadFromFile(const AnyString& filename, IniFile ini; if (ini.open(filename)) { - return loadFromINI(ini, version, options); + return loadFromINI(ini, version); } // Error otherwise diff --git a/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp b/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp index 14ee6a4435..07c8e7fa1d 100644 --- a/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp +++ b/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp @@ -70,6 +70,8 @@ BOOST_FIXTURE_TEST_CASE(loadValid, Fixture) writeValidFile(); p.loadFromFile(path.string(), version, options); + p.validateOptions(options); + p.fixBadValues(); BOOST_CHECK_EQUAL(p.nbYears, 5); BOOST_CHECK_EQUAL(p.seed[seedTsGenThermal], 5489); @@ -93,6 +95,8 @@ BOOST_FIXTURE_TEST_CASE(invalidValues, Fixture) { writeInvalidFile(); BOOST_CHECK(p.loadFromFile(path.string(), version, options)); + p.validateOptions(options); + p.fixBadValues(); BOOST_CHECK_EQUAL(p.nbYears, 1); BOOST_CHECK_EQUAL(p.useCustomScenario, 0); From b3f3fc0b9147816adf22f9f29d17b83be4e5125c Mon Sep 17 00:00:00 2001 From: guilpier-code <62292552+guilpier-code@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:42:07 +0200 Subject: [PATCH 10/15] Hydro final lvl (CR 25) to merge in develop [ANT-1084] (#1521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR attempts some code enhancements on branch **feature/final-reservoir-level-cr25**. Therefore, this PR's associated branch (**fix/hydro-final-levels-cr25--try-fixes**) is based on **feature/final-reservoir-level-cr25**. - [x] in class **FinalLevelInflowsModifier**, - try to reduce the number of data members, - making data members **private** whenever it's possible, - define a clear interface with a smallest number of public functions - and so make the more methods private as possible - [x] **Unit tests** : clarify their intention and reduce their size - [ ] **caution** : could not remove yet the **initialize** method. This method should be merged with the constructor, but when we call the constructor, all input pieces of data are not available. that's why **initialize** exists. We should try to make these pieces of data available, for instance by changing the order of data loading For any purpose, here are doc resources : - specs [here](https://rteinternational.sharepoint.com/:w:/r/sites/AntaresUser'sClub-Interne/_layouts/15/Doc.aspx?sourcedoc=%7B7774488E-7E4C-4AEC-B11F-1C9728B96AC0%7D&file=Functional_Specifications-Final_reservoir_level_V0.1-MA.docx&action=default&mobileredirect=true) - a discussion on that matter [here](https://github.com/AntaresSimulatorTeam/Antares_Simulator/discussions/1145) --- ### CAUTION : **Some cleaning and corrections were introduced in a new pull request** (see [this PR](https://github.com/AntaresSimulatorTeam/Antares_Simulator/pull/2040)). This new PR was merged into this one. **This PR contains a test under the form of a study. This study should be downloaded and added to the right test repo.** --- ### Left to to - [ ] Merge from branch develop and remove conflicts - [ ] Check that all tests exist (unit test for raising failures and study case for fine working) - [ ] what else ? --------- Co-authored-by: Milos A Co-authored-by: Milos <97689304+Milos-RTEi@users.noreply.github.com> Co-authored-by: Florian Omnès Co-authored-by: NikolaIlic --- docs/user-guide/04-migration-guides.md | 4 + src/CMakeLists.txt | 1 + src/antares-deps | 1 + src/libs/antares/study/CMakeLists.txt | 2 + src/libs/antares/study/area/area.cpp | 3 + .../antares/study/parts/hydro/container.h | 5 +- .../study/parts/hydro/finalLevelValidator.h | 73 +++++ .../study/scenario-builder/hydroLevelsData.h | 24 +- .../antares/study/scenario-builder/rules.h | 9 +- .../study/include/antares/study/study.h | 5 +- .../antares/study/parts/hydro/container.cpp | 3 +- .../study/parts/hydro/finalLevelValidator.cpp | 157 ++++++++++ .../scenario-builder/hydroLevelsData.cpp | 15 +- .../antares/study/scenario-builder/rules.cpp | 129 ++++---- .../solver/hydro/management/management.h | 2 + src/solver/hydro/management/daily.cpp | 2 + src/solver/hydro/management/management.cpp | 22 +- src/solver/hydro/management/monthly.cpp | 32 +- src/solver/simulation/CMakeLists.txt | 59 ++-- .../hydro-final-reservoir-level-functions.cpp | 68 +++++ .../hydro-final-reservoir-level-functions.h | 37 +++ .../antares/solver/simulation/solver.hxx | 22 +- .../end-to-end/simple_study/simple-study.cpp | 3 +- src/tests/end-to-end/utils/utils.cpp | 1 + .../test-sc-builder-file-read-line.cpp | 70 ++++- .../test-sc-builder-file-save.cpp | 31 +- .../src/solver/simulation/CMakeLists.txt | 29 ++ ...-hydro-final-reservoir-level-functions.cpp | 275 ++++++++++++++++++ .../main/build/scenario-builder.cpp | 30 +- src/ui/simulator/application/main/main.h | 3 +- src/ui/simulator/cmake/components.cmake | 2 + ...io-builder-hydro-final-levels-renderer.cpp | 82 ++++++ ...ario-builder-hydro-final-levels-renderer.h | 56 ++++ ...scenario-builder-hydro-levels-renderer.cpp | 30 +- 34 files changed, 1120 insertions(+), 167 deletions(-) create mode 160000 src/antares-deps create mode 100644 src/libs/antares/study/include/antares/study/parts/hydro/finalLevelValidator.h create mode 100644 src/libs/antares/study/parts/hydro/finalLevelValidator.cpp create mode 100644 src/solver/simulation/hydro-final-reservoir-level-functions.cpp create mode 100644 src/solver/simulation/include/antares/solver/simulation/hydro-final-reservoir-level-functions.h create mode 100644 src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp create mode 100644 src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.cpp create mode 100644 src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.h diff --git a/docs/user-guide/04-migration-guides.md b/docs/user-guide/04-migration-guides.md index 5dbc3f8885..c423bc5346 100644 --- a/docs/user-guide/04-migration-guides.md +++ b/docs/user-guide/04-migration-guides.md @@ -129,6 +129,10 @@ For each thermal cluster, in existing file **input/thermal/clusters/<area> For each thermal cluster, new files added **input/thermal/series/<area>/<cluster>/CO2Cost.txt** and **input/thermal/series/<area>/<cluster>/fuelCost.txt**. **fuelCost.txt** and **CO2Cost.txt** must either have one column, or the same number of columns as existing file **series.txt** (availability). The number of rows for these new matrices is 8760. +#### Hydro Final Reservoir Level +In the existing file **settings/scenariobuilder.dat**, under **<ruleset>** section following properties added (if final reservoir level specified, different from `init`): +* **hfl,<area>,<year> = <hfl-value>** + ### Output #### Scenarized RHS for binding constraints Add directory **bindingconstraints** to output directory **ts-numbers**. For every binding constraint group, add a file **ts-numbers/bindingconstraints/<group>.txt** containing the TS numbers used for that group. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51fdc67271..862c7fdbc8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ set(ANTARES_VERSION_HI 9) set(ANTARES_VERSION_LO 1) set(ANTARES_VERSION_REVISION 0) + # Beta release set(ANTARES_BETA 0) set(ANTARES_RC 0) diff --git a/src/antares-deps b/src/antares-deps new file mode 160000 index 0000000000..0d6bebfb90 --- /dev/null +++ b/src/antares-deps @@ -0,0 +1 @@ +Subproject commit 0d6bebfb901e47ec6ac1c73cb61a522803c81b98 diff --git a/src/libs/antares/study/CMakeLists.txt b/src/libs/antares/study/CMakeLists.txt index c0b5b84c56..c90d543fff 100644 --- a/src/libs/antares/study/CMakeLists.txt +++ b/src/libs/antares/study/CMakeLists.txt @@ -122,6 +122,8 @@ set(SRC_STUDY_PART_HYDRO include/antares/study/parts/hydro/allocation.h include/antares/study/parts/hydro/allocation.hxx parts/hydro/allocation.cpp + include/antares/study/parts/hydro/finalLevelValidator.h + parts/hydro/finalLevelValidator.cpp include/antares/study/parts/hydro/hydromaxtimeseriesreader.h parts/hydro/hydromaxtimeseriesreader.cpp ) diff --git a/src/libs/antares/study/area/area.cpp b/src/libs/antares/study/area/area.cpp index 77f0fe6dd9..2984704716 100644 --- a/src/libs/antares/study/area/area.cpp +++ b/src/libs/antares/study/area/area.cpp @@ -45,6 +45,7 @@ void Area::internalInitialize() } Area::Area(): + hydro(*this), reserves(fhrMax, HOURS_PER_YEAR), miscGen(fhhMax, HOURS_PER_YEAR) { @@ -52,6 +53,7 @@ Area::Area(): } Area::Area(const AnyString& name): + hydro(*this), reserves(fhrMax, HOURS_PER_YEAR), miscGen(fhhMax, HOURS_PER_YEAR) { @@ -62,6 +64,7 @@ Area::Area(const AnyString& name): Area::Area(const AnyString& name, const AnyString& id): + hydro(*this), reserves(fhrMax, HOURS_PER_YEAR), miscGen(fhhMax, HOURS_PER_YEAR) { diff --git a/src/libs/antares/study/include/antares/study/parts/hydro/container.h b/src/libs/antares/study/include/antares/study/parts/hydro/container.h index 0297540a4d..e09970b79c 100644 --- a/src/libs/antares/study/include/antares/study/parts/hydro/container.h +++ b/src/libs/antares/study/include/antares/study/parts/hydro/container.h @@ -21,6 +21,7 @@ #ifndef __ANTARES_LIBS_STUDY_PARTS_HYDRO_CONTAINER_H__ #define __ANTARES_LIBS_STUDY_PARTS_HYDRO_CONTAINER_H__ +#include #include "../../fwd.h" #include "allocation.h" #include "prepro.h" @@ -80,7 +81,7 @@ class PartHydro /*! ** \brief Default Constructor */ - PartHydro(); + PartHydro(const Data::Area& area); //! Destructor ~PartHydro(); @@ -165,6 +166,8 @@ class PartHydro Matrix dailyNbHoursAtGenPmax; Matrix dailyNbHoursAtPumpPmax; + std::vector> deltaBetweenFinalAndInitialLevels; + private: static bool checkReservoirLevels(const Study& study); static bool checkProperties(Study& study); diff --git a/src/libs/antares/study/include/antares/study/parts/hydro/finalLevelValidator.h b/src/libs/antares/study/include/antares/study/parts/hydro/finalLevelValidator.h new file mode 100644 index 0000000000..518e29d40b --- /dev/null +++ b/src/libs/antares/study/include/antares/study/parts/hydro/finalLevelValidator.h @@ -0,0 +1,73 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#pragma once + +#include "antares/study/parts/hydro/container.h" + +namespace Antares::Data +{ +class PartHydro; + +class FinalLevelValidator +{ +public: + FinalLevelValidator(PartHydro& hydro, + unsigned int areaIndex, + const AreaName areaName, + double initialLevel, + double finalLevel, + const unsigned int year, + const unsigned int lastSimulationDay, + const unsigned int firstMonthOfSimulation); + bool check(); + bool finalLevelFineForUse(); + +private: + bool wasSetInScenarioBuilder(); + bool compatibleWithReservoirProperties(); + bool skippingFinalLevelUse(); + bool checkForInfeasibility(); + bool hydroAllocationStartMatchesSimulation() const; + bool isFinalLevelReachable() const; + double calculateTotalInflows() const; + bool isBetweenRuleCurves() const; + + // Data from simulation + unsigned int year_ = 0; + unsigned int lastSimulationDay_ = 0; + unsigned int firstMonthOfSimulation_ = 0; + + // Data from area + PartHydro& hydro_; + unsigned int areaIndex_; + const AreaName areaName_; + double initialLevel_; + double finalLevel_; + + bool finalLevelFineForUse_ = false; +}; +} // namespace Antares::Data diff --git a/src/libs/antares/study/include/antares/study/scenario-builder/hydroLevelsData.h b/src/libs/antares/study/include/antares/study/scenario-builder/hydroLevelsData.h index 0f01fb9c32..42aff6336a 100644 --- a/src/libs/antares/study/include/antares/study/scenario-builder/hydroLevelsData.h +++ b/src/libs/antares/study/include/antares/study/scenario-builder/hydroLevelsData.h @@ -22,6 +22,7 @@ #define __LIBS_STUDY_SCENARIO_BUILDER_DATA_HYDRO_LEVELS_H__ #include "scBuilderDataInterface.h" +#include namespace Antares { @@ -39,7 +40,10 @@ class hydroLevelsData final: public dataInterface using MatrixType = Matrix; public: - // We use default constructor and destructor + // Constructor + + hydroLevelsData(const std::string& iniFilePrefix, + std::function applyToTarget); //! \name Data manupulation //@{ @@ -51,7 +55,7 @@ class hydroLevelsData final: public dataInterface /*! ** \brief Export the data into a mere INI file */ - void saveToINIFile(const Study& study, Yuni::IO::File::Stream& file) const; + void saveToINIFile(const Study& study, Yuni::IO::File::Stream& file) const override; /*! ** \brief Assign a single value @@ -71,11 +75,15 @@ class hydroLevelsData final: public dataInterface void set_value(uint x, uint y, double value); - bool apply(Study& study); + bool apply(Study& study) override; private: //! Hydro levels overlay (0 if auto) MatrixType pHydroLevelsRules; + // prefix to be added when calling saveToINIFileHydroLevel + const std::string addToPrefix_; + + std::function applyToTarget_; }; // class hydroLevelsData @@ -105,6 +113,16 @@ inline double hydroLevelsData::get_value(uint x, uint y) const return pHydroLevelsRules.entry[y][x]; } +inline void initLevelApply(Study& study, Matrix& matrix) +{ + study.scenarioInitialHydroLevels.copyFrom(matrix); +} + +inline void finalLevelApply(Study& study, Matrix& matrix) +{ + study.scenarioFinalHydroLevels.copyFrom(matrix); +} + } // namespace ScenarioBuilder } // namespace Data } // namespace Antares diff --git a/src/libs/antares/study/include/antares/study/scenario-builder/rules.h b/src/libs/antares/study/include/antares/study/scenario-builder/rules.h index b79d92be86..ff5261d64a 100644 --- a/src/libs/antares/study/include/antares/study/scenario-builder/rules.h +++ b/src/libs/antares/study/include/antares/study/scenario-builder/rules.h @@ -119,8 +119,10 @@ class Rules final: private Yuni::NonCopyable //! Renewable (array [0..pAreaCount - 1]) std::vector renewable; - //! hydro levels - hydroLevelsData hydroLevels; + //! hydro initial levels + hydroLevelsData hydroInitialLevels = {"hl,", initLevelApply}; + //! hydro final levels + hydroLevelsData hydroFinalLevels = {"hfl,", finalLevelApply}; // Links NTC std::vector linksNTC; @@ -135,7 +137,8 @@ class Rules final: private Yuni::NonCopyable bool readWind(const AreaName::Vector& instrs, String value, bool updaterMode); bool readHydro(const AreaName::Vector& instrs, String value, bool updaterMode); bool readSolar(const AreaName::Vector& instrs, String value, bool updaterMode); - bool readHydroLevels(const AreaName::Vector& instrs, String value, bool updaterMode); + bool readInitialHydroLevels(const AreaName::Vector& instrs, String value, bool updaterMode); + bool readFinalHydroLevels(const AreaName::Vector& instrs, String value, bool updaterMode); bool readLink(const AreaName::Vector& instrs, String value, bool updaterMode); bool readBindingConstraints(const AreaName::Vector& splitKey, String value); diff --git a/src/libs/antares/study/include/antares/study/study.h b/src/libs/antares/study/include/antares/study/study.h index 709bab62fe..03e2df8bed 100644 --- a/src/libs/antares/study/include/antares/study/study.h +++ b/src/libs/antares/study/include/antares/study/study.h @@ -588,8 +588,9 @@ class Study: public Yuni::NonCopyable, public LayerData ScenarioBuilder::Sets* scenarioRules = nullptr; //@} - TimeSeries::TS scenarioHydroLevels; - + TimeSeries::TS scenarioInitialHydroLevels; + // Hydro Final Levels + TimeSeries::TS scenarioFinalHydroLevels; /*! ** \brief Runtime informations ** diff --git a/src/libs/antares/study/parts/hydro/container.cpp b/src/libs/antares/study/parts/hydro/container.cpp index d955da1c81..c9dc476de5 100644 --- a/src/libs/antares/study/parts/hydro/container.cpp +++ b/src/libs/antares/study/parts/hydro/container.cpp @@ -32,7 +32,7 @@ using namespace Yuni; namespace Antares::Data { -PartHydro::PartHydro(): +PartHydro::PartHydro(const Data::Area& area) : interDailyBreakdown(0.), intraDailyModulation(2.), intermonthlyBreakdown(0), @@ -161,6 +161,7 @@ bool PartHydro::LoadFromFolder(Study& study, const AnyString& folder) area.hydro.initializeReservoirLevelDate = 0; area.hydro.reservoirCapacity = 0.; area.hydro.pumpingEfficiency = 1.; + area.hydro.deltaBetweenFinalAndInitialLevels.resize(study.parameters.nbYears); if (study.header.version >= StudyVersion(9, 1)) { diff --git a/src/libs/antares/study/parts/hydro/finalLevelValidator.cpp b/src/libs/antares/study/parts/hydro/finalLevelValidator.cpp new file mode 100644 index 0000000000..2229f75f97 --- /dev/null +++ b/src/libs/antares/study/parts/hydro/finalLevelValidator.cpp @@ -0,0 +1,157 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ + +#include "antares/study/parts/hydro/finalLevelValidator.h" + +namespace Antares::Data +{ + +FinalLevelValidator::FinalLevelValidator(PartHydro& hydro, + unsigned int areaIndex, + const AreaName areaName, // gp : to std::string + double initialLevel, + double finalLevel, + const unsigned int year, + const unsigned int lastSimulationDay, + const unsigned int firstMonthOfSimulation) + : hydro_(hydro), + areaName_(areaName), + areaIndex_(areaIndex), + initialLevel_(initialLevel), + finalLevel_(finalLevel), + year_(year), + lastSimulationDay_(lastSimulationDay), + firstMonthOfSimulation_(firstMonthOfSimulation) +{ +} + +bool FinalLevelValidator::check() +{ + if (skippingFinalLevelUse()) + return true; + if (! checkForInfeasibility()) + return false; + finalLevelFineForUse_ = true; + return true; +} + +bool FinalLevelValidator::skippingFinalLevelUse() +{ + if(! wasSetInScenarioBuilder()) + return true; + if (! compatibleWithReservoirProperties()) + return true; + return false; +} + +bool FinalLevelValidator::wasSetInScenarioBuilder() +{ + return ! isnan(finalLevel_); +} + +bool FinalLevelValidator::compatibleWithReservoirProperties() +{ + if (hydro_.reservoirManagement && !hydro_.useWaterValue) + return true; + + logs.warning() << "Final reservoir level not applicable! Year:" << year_ + 1 + << ", Area:" << areaName_ + << ". Check: Reservoir management = Yes, Use water values = No and proper initial " + "reservoir level is provided "; + return false; +} + +bool FinalLevelValidator::checkForInfeasibility() +{ + bool checksOk = hydroAllocationStartMatchesSimulation(); + checksOk = isFinalLevelReachable() && checksOk; + checksOk = isBetweenRuleCurves() && checksOk; + + return checksOk; +} + +bool FinalLevelValidator::hydroAllocationStartMatchesSimulation() const +{ + int initReservoirLvlMonth = hydro_.initializeReservoirLevelDate; // month [0-11] + if (lastSimulationDay_ == DAYS_PER_YEAR && initReservoirLvlMonth == firstMonthOfSimulation_) + return true; + + logs.error() << "Year " << year_ + 1 << ", area '" << areaName_ << "' : " + << "Hydro allocation must start on the 1st simulation month and " + << "simulation last a whole year"; + return false; +} + +bool FinalLevelValidator::isFinalLevelReachable() const +{ + double reservoirCapacity = hydro_.reservoirCapacity; + double totalYearInflows = calculateTotalInflows(); + + if ((finalLevel_ - initialLevel_) * reservoirCapacity > totalYearInflows) + { + logs.error() << "Year: " << year_ + 1 << ". Area: " << areaName_ + << ". Incompatible total inflows: " << totalYearInflows + << " with initial: " << initialLevel_ + << " and final: " << finalLevel_ << " reservoir levels."; + return false; + } + return true; +} + +double FinalLevelValidator::calculateTotalInflows() const +{ + // calculate yearly inflows + auto const& srcinflows = hydro_.series->storage.getColumn(year_); + + double totalYearInflows = 0.0; + for (unsigned int day = 0; day < DAYS_PER_YEAR; ++day) + totalYearInflows += srcinflows[day]; + return totalYearInflows; +} + +bool FinalLevelValidator::isBetweenRuleCurves() const +{ + double lowLevelLastDay = hydro_.reservoirLevel[Data::PartHydro::minimum][DAYS_PER_YEAR - 1]; + double highLevelLastDay = hydro_.reservoirLevel[Data::PartHydro::maximum][DAYS_PER_YEAR - 1]; + + if (finalLevel_ < lowLevelLastDay || finalLevel_ > highLevelLastDay) + { + logs.error() << "Year: " << year_ + 1 << ". Area: " << areaName_ + << ". Specifed final reservoir level: " << finalLevel_ + << " is incompatible with reservoir level rule curve [" << lowLevelLastDay + << " , " << highLevelLastDay << "]"; + return false; + } + return true; +} + +bool FinalLevelValidator::finalLevelFineForUse() +{ + return finalLevelFineForUse_; +} + +} // namespace Antares::Data diff --git a/src/libs/antares/study/scenario-builder/hydroLevelsData.cpp b/src/libs/antares/study/scenario-builder/hydroLevelsData.cpp index 8abb019366..3295df4503 100644 --- a/src/libs/antares/study/scenario-builder/hydroLevelsData.cpp +++ b/src/libs/antares/study/scenario-builder/hydroLevelsData.cpp @@ -28,6 +28,13 @@ namespace Antares::Data::ScenarioBuilder { +hydroLevelsData::hydroLevelsData(const std::string& iniFilePrefix, + std::function applyToTarget) : + addToPrefix_(iniFilePrefix), + applyToTarget_(applyToTarget) +{ +} + bool hydroLevelsData::reset(const Study& study) { const uint nbYears = study.parameters.nbYears; @@ -40,10 +47,6 @@ bool hydroLevelsData::reset(const Study& study) void hydroLevelsData::saveToINIFile(const Study& study, Yuni::IO::File::Stream& file) const { - // Prefix - CString<512, false> prefix; - prefix += "hl,"; - // Turning values into strings (precision 4) std::ostringstream value_into_string; value_into_string << std::setprecision(4); @@ -65,7 +68,7 @@ void hydroLevelsData::saveToINIFile(const Study& study, Yuni::IO::File::Stream& } assert(index < study.areas.size()); value_into_string << value; - file << prefix << study.areas.byIndex[index]->id << ',' << y << " = " + file << addToPrefix_ << study.areas.byIndex[index]->id << ',' << y << " = " << value_into_string.str() << '\n'; value_into_string.str(std::string()); // Clearing converter } @@ -79,7 +82,7 @@ void hydroLevelsData::set_value(uint x, uint y, double value) bool hydroLevelsData::apply(Study& study) { - study.scenarioHydroLevels.copyFrom(pHydroLevelsRules); + applyToTarget_(study, pHydroLevelsRules); return true; } diff --git a/src/libs/antares/study/scenario-builder/rules.cpp b/src/libs/antares/study/scenario-builder/rules.cpp index fb39a275fd..fd79f07f7c 100644 --- a/src/libs/antares/study/scenario-builder/rules.cpp +++ b/src/libs/antares/study/scenario-builder/rules.cpp @@ -59,7 +59,8 @@ void Rules::saveToINIFile(Yuni::IO::File::Stream& file) const linksNTC[i].saveToINIFile(study_, file); } // hydro levels - hydroLevels.saveToINIFile(study_, file); + hydroInitialLevels.saveToINIFile(study_, file); + hydroFinalLevels.saveToINIFile(study_, file); } binding_constraints.saveToINIFile(study_, file); file << '\n'; @@ -95,7 +96,8 @@ bool Rules::reset() renewable[i].reset(study_); } - hydroLevels.reset(study_); + hydroInitialLevels.reset(study_); + hydroFinalLevels.reset(study_); // links NTC linksNTC.clear(); @@ -263,7 +265,7 @@ bool Rules::readSolar(const AreaName::Vector& splitKey, String value, bool updat return true; } -bool Rules::readHydroLevels(const AreaName::Vector& splitKey, String value, bool updaterMode) +bool Rules::readInitialHydroLevels(const AreaName::Vector& splitKey, String value, bool updaterMode) { const uint year = splitKey[2].to(); const AreaName& areaname = splitKey[1]; @@ -275,7 +277,21 @@ bool Rules::readHydroLevels(const AreaName::Vector& splitKey, String value, bool } double val = fromStringToHydroLevel(value, 1.); - hydroLevels.setTSnumber(area->index, year, val); + hydroInitialLevels.setTSnumber(area->index, year, val); + return true; +} + +bool Rules::readFinalHydroLevels(const AreaName::Vector& splitKey, String value, bool updaterMode) +{ + const uint year = splitKey[2].to(); + const AreaName& areaname = splitKey[1]; + + const Data::Area* area = getArea(areaname, updaterMode); + if (!area) + return false; + + double finalLevel = fromStringToHydroLevel(value, 1.); + hydroFinalLevels.setTSnumber(area->index, year, finalLevel); return true; } @@ -389,7 +405,11 @@ bool Rules::readLine(const AreaName::Vector& splitKey, String value, bool update } else if (kind_of_scenario == "hl") { - return readHydroLevels(splitKey, value, updaterMode); + return readInitialHydroLevels(splitKey, value, updaterMode); + } + else if (kind_of_scenario == "hfl") + { + return readFinalHydroLevels(splitKey, value, updaterMode); } else if (kind_of_scenario == "ntc") { @@ -402,63 +422,64 @@ bool Rules::readLine(const AreaName::Vector& splitKey, String value, bool update return false; } - bool Rules::apply() +bool Rules::apply() +{ + bool returned_status = true; + if (pAreaCount) { - bool returned_status = true; - if (pAreaCount) + returned_status = load.apply(study_) && returned_status; + returned_status = solar.apply(study_) && returned_status; + returned_status = hydro.apply(study_) && returned_status; + returned_status = wind.apply(study_) && returned_status; + for (uint i = 0; i != pAreaCount; ++i) { - returned_status = load.apply(study_) && returned_status; - returned_status = solar.apply(study_) && returned_status; - returned_status = hydro.apply(study_) && returned_status; - returned_status = wind.apply(study_) && returned_status; - for (uint i = 0; i != pAreaCount; ++i) - { - returned_status = thermal[i].apply(study_) && returned_status; - returned_status = renewable[i].apply(study_) && returned_status; - returned_status = linksNTC[i].apply(study_) && returned_status; - } - returned_status = hydroLevels.apply(study_) && returned_status; - returned_status = binding_constraints.apply(study_) && returned_status; + returned_status = thermal[i].apply(study_) && returned_status; + returned_status = renewable[i].apply(study_) && returned_status; + returned_status = linksNTC[i].apply(study_) && returned_status; } - else + returned_status = hydroInitialLevels.apply(study_) && returned_status; + returned_status = hydroFinalLevels.apply(study_) && returned_status; + returned_status = binding_constraints.apply(study_) && returned_status; + } + else + { + returned_status = false; + } + return returned_status; +} + +void Rules::sendWarningsForDisabledClusters() +{ + for (auto it = disabledClustersOnRuleActive.begin(); + it != disabledClustersOnRuleActive.end(); + it++) + { + std::vector& scenariiForCurrentCluster = it->second; + int nbScenariiForCluster = (int)scenariiForCurrentCluster.size(); + std::vector::iterator itv = scenariiForCurrentCluster.begin(); + + // Listing the 10 first years for which the current cluster was given a specific TS + // number in the scenario builder. Note that this list of years size could be less then + // 10, but are at least 1. + std::string listYears = std::to_string(*itv); + itv++; + for (int year_count = 1; itv != scenariiForCurrentCluster.end() && year_count < 10; + itv++, year_count++) { - returned_status = false; + listYears += ", " + std::to_string(*itv); } - return returned_status; - } - void Rules::sendWarningsForDisabledClusters() - { - for (auto it = disabledClustersOnRuleActive.begin(); - it != disabledClustersOnRuleActive.end(); - it++) + // Adding last scenario to the list + if (nbScenariiForCluster > 10) { - std::vector& scenariiForCurrentCluster = it->second; - int nbScenariiForCluster = (int)scenariiForCurrentCluster.size(); - std::vector::iterator itv = scenariiForCurrentCluster.begin(); - - // Listing the 10 first years for which the current cluster was given a specific TS - // number in the scenario builder. Note that this list of years size could be less then - // 10, but are at least 1. - std::string listYears = std::to_string(*itv); - itv++; - for (int year_count = 1; itv != scenariiForCurrentCluster.end() && year_count < 10; - itv++, year_count++) - { - listYears += ", " + std::to_string(*itv); - } - - // Adding last scenario to the list - if (nbScenariiForCluster > 10) - { - listYears += ", ..., " + std::to_string(scenariiForCurrentCluster.back()); - } - - logs.warning() << "Cluster " << it->first - << " not found: it may be disabled, though given TS numbers in sc " - "builder for year(s) :"; - logs.warning() << listYears; + listYears += ", ..., " + std::to_string(scenariiForCurrentCluster.back()); } + + logs.warning() << "Cluster " << it->first + << " not found: it may be disabled, though given TS numbers in sc " + "builder for year(s) :"; + logs.warning() << listYears; } +} } // namespace Antares::Data::ScenarioBuilder diff --git a/src/solver/hydro/include/antares/solver/hydro/management/management.h b/src/solver/hydro/include/antares/solver/hydro/management/management.h index 3050515ca5..b642e441c2 100644 --- a/src/solver/hydro/include/antares/solver/hydro/management/management.h +++ b/src/solver/hydro/include/antares/solver/hydro/management/management.h @@ -122,6 +122,8 @@ class HydroManagement final private: //! Prepare inflows scaling for each area void prepareInflowsScaling(uint year); + //! prepare data for Final reservoir level + void changeInflowsToAccommodateFinalLevels(uint yearIndex); //! Prepare minimum generation scaling for each area void minGenerationScaling(uint year); //! check Monthly minimum generation is lower than available inflows diff --git a/src/solver/hydro/management/daily.cpp b/src/solver/hydro/management/daily.cpp index 5c6bcab897..f7e4665159 100644 --- a/src/solver/hydro/management/daily.cpp +++ b/src/solver/hydro/management/daily.cpp @@ -362,6 +362,7 @@ inline void HydroManagement::prepareDailyOptimalGenerations( if (debugData) { + dayYear = 0; for (uint month = 0; month != 12; ++month) { auto daysPerMonth = calendar_.months[month].days; @@ -371,6 +372,7 @@ inline void HydroManagement::prepareDailyOptimalGenerations( auto dYear = day + dayYear; debugData->DailyTargetGen[dYear] = dailyTargetGen[dYear]; } + dayYear += daysPerMonth; } } diff --git a/src/solver/hydro/management/management.cpp b/src/solver/hydro/management/management.cpp index 6cea703442..703235fc61 100644 --- a/src/solver/hydro/management/management.cpp +++ b/src/solver/hydro/management/management.cpp @@ -383,8 +383,25 @@ bool HydroManagement::checkMinGeneration(uint year) const return ret; } -void HydroManagement::prepareNetDemand(uint year, - Data::SimulationMode mode, +void HydroManagement::changeInflowsToAccommodateFinalLevels(uint year) +{ + areas_.each([this, &year](Data::Area& area) + { + auto& data = tmpDataByArea_[&area]; + + if (!area.hydro.deltaBetweenFinalAndInitialLevels[year].has_value()) + return; + + // Must be done before prepareMonthlyTargetGenerations + double delta = area.hydro.deltaBetweenFinalAndInitialLevels[year].value(); + if (delta < 0) + data.inflows[0] -= delta; + else if (delta > 0) + data.inflows[11] -= delta; + }); +} + +void HydroManagement::prepareNetDemand(uint year, Data::SimulationMode mode, const Antares::Data::Area::ScratchMap& scratchmap) { areas_.each( @@ -527,6 +544,7 @@ void HydroManagement::makeVentilation(double* randomReservoirLevel, throw FatalError("hydro management: invalid minimum generation"); } + changeInflowsToAccommodateFinalLevels(y); prepareNetDemand(y, parameters_.mode, scratchmap); prepareEffectiveDemand(); diff --git a/src/solver/hydro/management/monthly.cpp b/src/solver/hydro/management/monthly.cpp index 62ce897eb8..aa10b7c10e 100644 --- a/src/solver/hydro/management/monthly.cpp +++ b/src/solver/hydro/management/monthly.cpp @@ -166,8 +166,6 @@ void HydroManagement::prepareMonthlyOptimalGenerations(double* random_reservoir_ lvi = random_reservoir_level[indexArea]; } - indexArea++; - double solutionCost = 0.; double solutionCostNoised = 0.; @@ -292,20 +290,22 @@ void HydroManagement::prepareMonthlyOptimalGenerations(double* random_reservoir_ auto monthName = calendar_.text.months[simulationMonth].name; - buffer << monthName[0] << monthName[1] << monthName[2] << '\t'; - buffer << '\t'; - buffer << data.inflows[realmonth] << '\t'; - buffer << data.MTG[realmonth] << '\t'; - buffer << data.MOG[realmonth] / area.hydro.reservoirCapacity << '\t'; - buffer << data.MOL[realmonth] << '\t'; - buffer << minLvl[firstDay] << '\t'; - buffer << maxLvl[firstDay] << '\t'; - buffer << '\n'; - } - auto content = buffer.str(); - resultWriter_.addEntryFromBuffer(path.str(), content); - } - }); + buffer << monthName[0] << monthName[1] << monthName[2] << '\t'; + buffer << '\t'; + buffer << data.inflows[realmonth] << '\t'; + buffer << data.MTG[realmonth] << '\t'; + buffer << data.MOG[realmonth] / area.hydro.reservoirCapacity << '\t'; + buffer << data.MOL[realmonth] << '\t'; + buffer << minLvl[firstDay] << '\t'; + buffer << maxLvl[firstDay] << '\t'; + buffer << '\n'; + } + auto content = buffer.str(); + resultWriter_.addEntryFromBuffer(path.str(), content); + } + + indexArea++; + }); } } // namespace Antares diff --git a/src/solver/simulation/CMakeLists.txt b/src/solver/simulation/CMakeLists.txt index 3c59c59d55..0d2bc666eb 100644 --- a/src/solver/simulation/CMakeLists.txt +++ b/src/solver/simulation/CMakeLists.txt @@ -22,35 +22,36 @@ set(SRC_SIMULATION apply-scenario.cpp - # Solver - include/antares/solver/simulation/solver_utils.h - solver_utils.cpp - include/antares/solver/simulation/solver.h - include/antares/solver/simulation/solver.hxx - include/antares/solver/simulation/solver.data.h - solver.data.cpp - include/antares/solver/simulation/common-eco-adq.h - common-eco-adq.cpp - common-hydro-remix.cpp - common-hydro-levels.cpp - include/antares/solver/simulation/adequacy.h - adequacy.cpp - include/antares/solver/simulation/economy.h - economy.cpp - include/antares/solver/simulation/base_post_process.h - base_post_process.cpp - include/antares/solver/simulation/opt_time_writer.h - opt_time_writer.cpp - include/antares/solver/simulation/adequacy_patch_runtime_data.h - adequacy_patch_runtime_data.cpp - include/antares/solver/simulation/ITimeSeriesNumbersWriter.h - TimeSeriesNumbersWriter.cpp - include/antares/solver/simulation/BindingConstraintsTimeSeriesNumbersWriter.h - - economy_mode.cpp - adequacy_mode.cpp - include/antares/solver/simulation/economy_mode.h - include/antares/solver/simulation/adequacy_mode.h + # Solver + include/antares/solver/simulation/solver_utils.h + solver_utils.cpp + include/antares/solver/simulation/solver.h + include/antares/solver/simulation/solver.hxx + include/antares/solver/simulation/solver.data.h + solver.data.cpp + include/antares/solver/simulation/common-eco-adq.h + common-eco-adq.cpp + common-hydro-remix.cpp + common-hydro-levels.cpp + include/antares/solver/simulation/adequacy.h + adequacy.cpp + include/antares/solver/simulation/economy.h + economy.cpp + include/antares/solver/simulation/base_post_process.h + base_post_process.cpp + include/antares/solver/simulation/opt_time_writer.h + opt_time_writer.cpp + include/antares/solver/simulation/adequacy_patch_runtime_data.h + adequacy_patch_runtime_data.cpp + include/antares/solver/simulation/ITimeSeriesNumbersWriter.h + include/antares/solver/simulation/hydro-final-reservoir-level-functions.h + hydro-final-reservoir-level-functions.cpp + TimeSeriesNumbersWriter.cpp + include/antares/solver/simulation/BindingConstraintsTimeSeriesNumbersWriter.h + economy_mode.cpp + adequacy_mode.cpp + include/antares/solver/simulation/economy_mode.h + include/antares/solver/simulation/adequacy_mode.h ) source_group("simulation" FILES ${SRC_SIMULATION}) diff --git a/src/solver/simulation/hydro-final-reservoir-level-functions.cpp b/src/solver/simulation/hydro-final-reservoir-level-functions.cpp new file mode 100644 index 0000000000..32f2d4dfa2 --- /dev/null +++ b/src/solver/simulation/hydro-final-reservoir-level-functions.cpp @@ -0,0 +1,68 @@ +/* +** Copyright 2007-2023 RTE +** Authors: RTE-international / Redstork / Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ + +#include "antares/solver/simulation/hydro-final-reservoir-level-functions.h" +#include "antares/study/parts/hydro/finalLevelValidator.h" +#include + +namespace Antares::Solver +{ + +void CheckFinalReservoirLevelsConfiguration(const Data::Study& study) +{ + study.areas.each([&study](Data::Area &area) + { + uint nbYears = study.parameters.nbYears; + for (uint year = 0; year != nbYears; ++year) + { + if (! study.parameters.yearsFilter.at(year)) + continue; + + double initialLevel = study.scenarioInitialHydroLevels.entry[area.index][year]; + double finalLevel = study.scenarioFinalHydroLevels.entry[area.index][year]; + + Data::FinalLevelValidator validator(area.hydro, + area.index, + area.name, + initialLevel, + finalLevel, + year, + study.parameters.simulationDays.end, + study.parameters.firstMonthInYear); + if (! validator.check()) + { + throw FatalError("hydro final level : infeasibility"); + } + if (validator.finalLevelFineForUse()) + { + area.hydro.deltaBetweenFinalAndInitialLevels[year] = finalLevel - initialLevel; + } + } + }); +} // End function CheckFinalReservoirLevelsConfiguration + +} // namespace Antares::Solver \ No newline at end of file diff --git a/src/solver/simulation/include/antares/solver/simulation/hydro-final-reservoir-level-functions.h b/src/solver/simulation/include/antares/solver/simulation/hydro-final-reservoir-level-functions.h new file mode 100644 index 0000000000..4b60c4cc8f --- /dev/null +++ b/src/solver/simulation/include/antares/solver/simulation/hydro-final-reservoir-level-functions.h @@ -0,0 +1,37 @@ +/* +** Copyright 2007-2023 RTE +** Authors: RTE-international / Redstork / Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#ifndef __SOLVER_SIMULATION_HYDRO_FINAL_RESERVOIR_PRE_CHECKS_H__ +#define __SOLVER_SIMULATION_HYDRO_FINAL_RESERVOIR_PRE_CHECKS_H__ + +#include "antares/study/study.h" + +namespace Antares::Solver +{ +void CheckFinalReservoirLevelsConfiguration(const Data::Study& study); +} // namespace Antares::Solver + +#endif // __SOLVER_SIMULATION_HYDRO_FINAL_RESERVOIR_PRE_CHECKS_H__ diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index 5e36fcaca8..232fa520fc 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -39,6 +39,9 @@ #include "antares/solver/simulation/timeseries-numbers.h" #include "antares/solver/ts-generator/generator.h" + +#include "hydro-final-reservoir-level-functions.h" + namespace Antares::Solver::Simulation { @@ -349,6 +352,7 @@ void ISimulation::run() if (study.parameters.useCustomScenario) { ApplyCustomScenario(study); + CheckFinalReservoirLevelsConfiguration(study); } // Launching the simulation for all years @@ -744,15 +748,15 @@ void ISimulation::computeRandomNumbers( max[firstDayOfMonth], randomHydroGenerator); - // Possibly update the intial level from scenario builder - if (study.parameters.useCustomScenario) - { - double levelFromScenarioBuilder = study.scenarioHydroLevels[areaIndex][y]; - if (levelFromScenarioBuilder >= 0.) - { - randomLevel = levelFromScenarioBuilder; - } - } + // Possibly update the intial level from scenario builder + if (study.parameters.useCustomScenario) + { + double levelFromScenarioBuilder = study.scenarioInitialHydroLevels[areaIndex][y]; + if (levelFromScenarioBuilder >= 0.) + { + randomLevel = levelFromScenarioBuilder; + } + } // Current area's hydro starting (or initial) level computation // (no matter if the year is performed or not, we always draw a random initial diff --git a/src/tests/end-to-end/simple_study/simple-study.cpp b/src/tests/end-to-end/simple_study/simple-study.cpp index e5813befd8..bfad50326f 100644 --- a/src/tests/end-to-end/simple_study/simple-study.cpp +++ b/src/tests/end-to-end/simple_study/simple-study.cpp @@ -78,11 +78,12 @@ struct HydroMaxPowerStudy: public StudyBuilder HydroMaxPowerStudy::HydroMaxPowerStudy() { simulationBetweenDays(0, 14); - setNumberMCyears(1); area = addAreaToStudy("Area"); area->thermal.unsuppliedEnergyCost = 1; + setNumberMCyears(1); + TimeSeriesConfigurer loadTSconfig(area->load.series.timeSeries); loadTSconfig.setColumnCount(1).fillColumnWith(0, loadInArea); diff --git a/src/tests/end-to-end/utils/utils.cpp b/src/tests/end-to-end/utils/utils.cpp index 769683412d..a42891ea45 100644 --- a/src/tests/end-to-end/utils/utils.cpp +++ b/src/tests/end-to-end/utils/utils.cpp @@ -219,6 +219,7 @@ void StudyBuilder::setNumberMCyears(unsigned int nbYears) { study->parameters.resetPlaylist(nbYears); study->areas.resizeAllTimeseriesNumbers(nbYears); + study->areas.each([&](Data::Area& area) { area.hydro.deltaBetweenFinalAndInitialLevels.resize(nbYears); }); } void StudyBuilder::playOnlyYear(unsigned int year) diff --git a/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-read-line.cpp b/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-read-line.cpp index 36346b2014..7879559fb0 100644 --- a/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-read-line.cpp +++ b/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-read-line.cpp @@ -346,7 +346,7 @@ BOOST_FIXTURE_TEST_CASE( } // ======================== -// Tests on Hydro levels +// Tests on Hydro initial levels // ======================== BOOST_FIXTURE_TEST_CASE(on_area1_and_on_year_17__hydro_level_0_123_is_chosen__reading_OK, Fixture) { @@ -355,12 +355,10 @@ BOOST_FIXTURE_TEST_CASE(on_area1_and_on_year_17__hydro_level_0_123_is_chosen__re AreaName::Vector splitKey = {"hl", "area 1", yearNumber}; my_rule.readLine(splitKey, level); - BOOST_CHECK_EQUAL(my_rule.hydroLevels.get_value(yearNumber.to(), area_1->index), - level.to()); + BOOST_CHECK_EQUAL(my_rule.hydroInitialLevels.get_value(yearNumber.to(), area_1->index), level.to()); - BOOST_CHECK(my_rule.apply()); - BOOST_CHECK_EQUAL(study->scenarioHydroLevels[area_1->index][yearNumber.to()], - level.to()); + BOOST_CHECK(my_rule.apply()); + BOOST_CHECK_EQUAL(study->scenarioInitialHydroLevels[area_1->index][yearNumber.to()], level.to()); } BOOST_FIXTURE_TEST_CASE( @@ -372,10 +370,10 @@ BOOST_FIXTURE_TEST_CASE( AreaName::Vector splitKey = {"hl", "area 2", yearNumber}; BOOST_CHECK(my_rule.readLine(splitKey, level)); - BOOST_CHECK_EQUAL(my_rule.hydroLevels.get_value(yearNumber.to(), area_2->index), 1.); + BOOST_CHECK_EQUAL(my_rule.hydroInitialLevels.get_value(yearNumber.to(), area_2->index), 1.); - BOOST_CHECK(my_rule.apply()); - BOOST_CHECK_EQUAL(study->scenarioHydroLevels[area_2->index][yearNumber.to()], 1.); + BOOST_CHECK(my_rule.apply()); + BOOST_CHECK_EQUAL(study->scenarioInitialHydroLevels[area_2->index][yearNumber.to()], 1.); } BOOST_FIXTURE_TEST_CASE( @@ -387,10 +385,58 @@ BOOST_FIXTURE_TEST_CASE( AreaName::Vector splitKey = {"hl", "area 3", yearNumber}; BOOST_CHECK(my_rule.readLine(splitKey, level)); - BOOST_CHECK_EQUAL(my_rule.hydroLevels.get_value(yearNumber.to(), area_3->index), 0.); + BOOST_CHECK_EQUAL(my_rule.hydroInitialLevels.get_value(yearNumber.to(), area_3->index), 0.); - BOOST_CHECK(my_rule.apply()); - BOOST_CHECK_EQUAL(study->scenarioHydroLevels[area_3->index][yearNumber.to()], 0.); + BOOST_CHECK(my_rule.apply()); + BOOST_CHECK_EQUAL(study->scenarioInitialHydroLevels[area_3->index][yearNumber.to()], 0.); +} + +// ======================== +// Tests on Hydro final levels +// ======================== +BOOST_FIXTURE_TEST_CASE(on_area1_and_on_year_8__hydro_level_0_342_is_chosen__reading_OK, Fixture) +{ + AreaName yearNumber = "8"; + String level = "0.342"; + AreaName::Vector splitKey = {"hfl", "area 1", yearNumber}; + my_rule.readLine(splitKey, level, false); + + BOOST_CHECK_EQUAL(my_rule.hydroFinalLevels.get_value(yearNumber.to(), area_1->index), + level.to()); + + BOOST_CHECK(my_rule.apply()); + BOOST_CHECK_EQUAL(study->scenarioFinalHydroLevels[area_1->index][yearNumber.to()], + level.to()); +} + +BOOST_FIXTURE_TEST_CASE(on_area2_and_on_year_1__hydro_level_2_4_is_chosen_level_lowered_to_1__reading_OK, Fixture) +{ + AreaName yearNumber = "1"; + String level = "2.4"; + AreaName::Vector splitKey = {"hfl", "area 2", yearNumber}; + BOOST_CHECK(my_rule.readLine(splitKey, level, false)); + + BOOST_CHECK_EQUAL(my_rule.hydroFinalLevels.get_value(yearNumber.to(), area_2->index), + 1.); + + BOOST_CHECK(my_rule.apply()); + BOOST_CHECK_EQUAL(study->scenarioFinalHydroLevels[area_2->index][yearNumber.to()], + 1.); +} + +BOOST_FIXTURE_TEST_CASE(on_area3_and_on_year_3__hydro_level_neg_5_2_is_chosen__level_raised_to_0__reading_OK, Fixture) +{ + AreaName yearNumber = "3"; + String level = "-5.2"; + AreaName::Vector splitKey = {"hfl", "area 3", yearNumber}; + BOOST_CHECK(my_rule.readLine(splitKey, level, false)); + + BOOST_CHECK_EQUAL(my_rule.hydroFinalLevels.get_value(yearNumber.to(), area_3->index), + 0.); + + BOOST_CHECK(my_rule.apply()); + BOOST_CHECK_EQUAL(study->scenarioFinalHydroLevels[area_3->index][yearNumber.to()], + 0.); } // ====================== diff --git a/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-save.cpp b/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-save.cpp index e1b0666b7d..13d5a38b48 100644 --- a/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-save.cpp +++ b/src/tests/src/libs/antares/study/scenario-builder/test-sc-builder-file-save.cpp @@ -411,15 +411,15 @@ BOOST_FIXTURE_TEST_CASE( } // ======================== -// Tests on Hydro levels +// Tests on Hydro initial levels // ======================== BOOST_FIXTURE_TEST_CASE( HYDRO_LEVEL__TS_number_for_many_areas_and_years__generated_and_ref_sc_buider_files_are_identical, saveFixture) { - my_rule->hydroLevels.setTSnumber(area_1->index, 9, 9); - my_rule->hydroLevels.setTSnumber(area_3->index, 18, 7); - my_rule->hydroLevels.setTSnumber(area_1->index, 5, 8); + my_rule->hydroInitialLevels.setTSnumber(area_1->index, 9, 9); + my_rule->hydroInitialLevels.setTSnumber(area_3->index, 18, 7); + my_rule->hydroInitialLevels.setTSnumber(area_1->index, 5, 8); saveScenarioBuilder(); @@ -433,6 +433,27 @@ BOOST_FIXTURE_TEST_CASE( BOOST_CHECK(files_identical(path_to_generated_file, referenceFile.path())); } +// ======================== +// Tests on Hydro final levels +// ======================== +BOOST_FIXTURE_TEST_CASE(HYDRO_FINAL_LEVEL__TS_number_for_many_areas_and_years__generated_and_ref_sc_buider_files_are_identical, saveFixture) +{ + my_rule->hydroFinalLevels.setTSnumber(area_1->index, 4, 8); + my_rule->hydroFinalLevels.setTSnumber(area_2->index, 11, 3); + my_rule->hydroFinalLevels.setTSnumber(area_3->index, 15, 2); + + saveScenarioBuilder(); + + // Build reference scenario builder file + referenceFile.append("[my rule name]"); + referenceFile.append("hfl,area 1,4 = 8"); + referenceFile.append("hfl,area 2,11 = 3"); + referenceFile.append("hfl,area 3,15 = 2"); + referenceFile.write(); + + BOOST_CHECK(files_identical(path_to_generated_file, referenceFile.path())); +} + // ====================== // Tests on Links NTC // ====================== @@ -496,7 +517,7 @@ BOOST_FIXTURE_TEST_CASE( my_rule->renewable[area_3->index].setTSnumber(rnCluster_32.get(), 5, 13); my_rule->linksNTC[area_1->index].setDataForLink(link_13, 19, 8); my_rule->linksNTC[area_2->index].setDataForLink(link_23, 2, 4); - my_rule->hydroLevels.setTSnumber(area_1->index, 5, 8); + my_rule->hydroInitialLevels.setTSnumber(area_1->index, 5, 8); my_rule->binding_constraints.setTSnumber("group3", 10, 6); saveScenarioBuilder(); diff --git a/src/tests/src/solver/simulation/CMakeLists.txt b/src/tests/src/solver/simulation/CMakeLists.txt index e056ac388d..3ba8f09755 100644 --- a/src/tests/src/solver/simulation/CMakeLists.txt +++ b/src/tests/src/solver/simulation/CMakeLists.txt @@ -89,3 +89,32 @@ add_test(NAME time_series COMMAND test-time_series) set_property(TEST time_series PROPERTY LABELS unit) +# =================================== +# Tests on hydro final reservoir level functions +# =================================== + +add_executable(test-hydro_final test-hydro-final-reservoir-level-functions.cpp) + +target_include_directories(test-hydro_final + PRIVATE + "${src_solver_simulation}" + "${src_libs_antares_study}" +) +target_link_libraries(test-hydro_final + PRIVATE + Boost::unit_test_framework + Antares::study + antares-solver-simulation + Antares::array +) + +# Linux +if(UNIX AND NOT APPLE) + target_link_libraries(test-hydro_final PRIVATE stdc++fs) +endif() + +set_target_properties(test-hydro_final PROPERTIES FOLDER Unit-tests) + +add_test(NAME hydro_final COMMAND test-hydro_final) + +set_property(TEST hydro_final PROPERTY LABELS unit) \ No newline at end of file diff --git a/src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp b/src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp new file mode 100644 index 0000000000..d4a6c3512b --- /dev/null +++ b/src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp @@ -0,0 +1,275 @@ +// +// Created by Nikola Ilic on 23/06/23. +// + +#define BOOST_TEST_MODULE hydro - final - level +#define WIN32_LEAN_AND_MEAN +#include + +#include "include/antares/solver/simulation/hydro-final-reservoir-level-functions.h" +#include "include/antares/study/parts/hydro/finalLevelValidator.h" +#include + +using namespace Antares::Solver; +using namespace Antares::Data; + + +struct Fixture +{ + Fixture(const Fixture& f) = delete; + Fixture(const Fixture&& f) = delete; + Fixture& operator=(const Fixture& f) = delete; + Fixture& operator=(const Fixture&& f) = delete; + Fixture() + { + // Simulation last day must be 365 so that final level checks succeeds + study->parameters.simulationDays.end = 365; + study->parameters.firstMonthInYear = january; + uint nbYears = study->parameters.nbYears = 2; + + area_1 = study->areaAdd("Area1"); + area_2 = study->areaAdd("Area2"); + + area_1->hydro.reservoirManagement = true; + area_2->hydro.reservoirManagement = true; + + area_1->hydro.useWaterValue = false; + area_2->hydro.useWaterValue = false; + + // Level date must be 0, see hydroAllocationStartMatchesSimulation function + area_1->hydro.initializeReservoirLevelDate = 0; + area_2->hydro.initializeReservoirLevelDate = 0; + + area_1->hydro.reservoirCapacity = 340.; + area_2->hydro.reservoirCapacity = 300.; + + // Set reservoir max and min daily levels, but just for the last day in year + area_1->hydro.reservoirLevel.resize(3, DAYS_PER_YEAR); + area_1->hydro.reservoirLevel[PartHydro::minimum][DAYS_PER_YEAR - 1] = 2.4; + area_1->hydro.reservoirLevel[PartHydro::maximum][DAYS_PER_YEAR - 1] = 6.5; + + area_2->hydro.reservoirLevel.resize(3, DAYS_PER_YEAR); + area_2->hydro.reservoirLevel[PartHydro::minimum][DAYS_PER_YEAR - 1] = 2.7; + area_2->hydro.reservoirLevel[PartHydro::maximum][DAYS_PER_YEAR - 1] = 6.4; + + // Resize vector final levels delta with initial levels + area_1->hydro.deltaBetweenFinalAndInitialLevels.resize(nbYears); + area_2->hydro.deltaBetweenFinalAndInitialLevels.resize(nbYears); + + + // Scenario builder for initial and final reservoir levels + // ------------------------------------------------------- + uint areasCount = study->areas.size(); + + study->parameters.yearsFilter.assign(2, true); + + study->scenarioInitialHydroLevels.resize(nbYears, areasCount); + study->scenarioFinalHydroLevels.resize(nbYears, areasCount); + + study->scenarioInitialHydroLevels[0][0] = 2.3; + study->scenarioInitialHydroLevels[0][1] = 4.2; + study->scenarioInitialHydroLevels[1][0] = 1.5; + study->scenarioInitialHydroLevels[1][1] = 2.4; + + study->scenarioFinalHydroLevels[0][0] = 3.4; + study->scenarioFinalHydroLevels[0][1] = 5.1; + study->scenarioFinalHydroLevels[1][0] = 3.5; + study->scenarioFinalHydroLevels[1][1] = 4.3; + + // Inflows time series matrices + // ----------------------------- + uint nbInflowTS = 2; + // ... Area 1 : Inflows time series numbers for each year + area_1->hydro.series->timeseriesNumbers.reset(nbYears); + area_1->hydro.series->timeseriesNumbers[0] = 0; + area_1->hydro.series->timeseriesNumbers[1] = 1; + // ... Area 1 : Inflows time series + area_1->hydro.series->storage.resize(nbInflowTS, 365); + area_1->hydro.series->storage.timeSeries.fill(200.); + area_1->hydro.series->storage[0][0] = 200. + 1.; + area_1->hydro.series->storage[0][DAYS_PER_YEAR - 1] = 200. + 2.; + + // ... Area 2 : time series numbers for each year + area_2->hydro.series->timeseriesNumbers.reset(nbYears); + area_2->hydro.series->timeseriesNumbers[0] = 0; + area_2->hydro.series->timeseriesNumbers[1] = 1; + // ... Area 2 : Inflows time series + area_2->hydro.series->storage.resize(nbInflowTS, 365); + area_2->hydro.series->storage.timeSeries.fill(300.); + area_2->hydro.series->storage[0][0] = 300. + 1.; //DAYS_PER_YEAR + area_2->hydro.series->storage[0][DAYS_PER_YEAR - 1] = 300. + 2.; + } + + ~Fixture() = default; + + Study::Ptr study = std::make_shared(); + Area* area_1; + Area* area_2; +}; + +BOOST_FIXTURE_TEST_SUITE(final_level_validator, Fixture) + +BOOST_AUTO_TEST_CASE(all_parameters_good___check_succeeds_and_final_level_is_usable) +{ + uint year = 0; + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), true); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), true); +} + +BOOST_AUTO_TEST_CASE(no_reservoir_management___check_succeeds_but_final_level_not_usable) +{ + uint year = 0; + area_1->hydro.reservoirManagement = false; + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), true); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); +} + +BOOST_AUTO_TEST_CASE(use_water_value_is_true___check_succeeds_but_final_level_not_usable) +{ + area_1->hydro.useWaterValue = true; + uint year = 0; + + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), true); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); +} + +BOOST_AUTO_TEST_CASE(final_level_not_set_by_user____check_succeeds_but_final_level_not_usable) +{ + uint year = 0; + study->scenarioFinalHydroLevels[area_1->index][year] = std::numeric_limits::quiet_NaN(); + + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), true); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); +} + +BOOST_AUTO_TEST_CASE(initial_level_month_and_simulation_first_month_different___check_fails_and_final_level_not_usable) +{ + uint year = 0; + area_1->hydro.initializeReservoirLevelDate = 3; // initialize reservoir level != January + + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), false); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); +} + +BOOST_AUTO_TEST_CASE(simulation_does_last_a_whole_year___check_fails_and_final_level_not_usable) +{ + uint year = 0; + study->parameters.simulationDays.end = 300; + + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), false); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); +} + +BOOST_AUTO_TEST_CASE(final_level_out_of_rule_curves___check_fails_and_final_level_not_usable) +{ + uint year = 0; + // Rule Curves on last simulation day = [2.4 - 6.5] + study->scenarioFinalHydroLevels[area_1->index][year] = 6.6; + + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), false); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); +} + +BOOST_AUTO_TEST_CASE(final_level_unreachable_because_of_too_few_inflows___check_fails_and_final_level_not_usable) +{ + area_1->hydro.reservoirCapacity = 185000; + uint year = 0; + study->scenarioInitialHydroLevels[area_1->index][year] = 10; + study->scenarioFinalHydroLevels[area_1->index][year] = 50; + + // Inflows = 200 MWh/day = 73 000 MWh/year + // (50 - 10) x Reservoir capacity == 74 000 > 73 000. + FinalLevelValidator validator(area_1->hydro, + area_1->index, + area_1->name, + study->scenarioInitialHydroLevels[area_1->index][year], + study->scenarioFinalHydroLevels[area_1->index][year], + year, + study->parameters.simulationDays.end, + study->parameters.firstMonthInYear); + + BOOST_CHECK_EQUAL(validator.check(), false); + BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); +} + +BOOST_AUTO_TEST_CASE(check_all_areas_final_levels_when_config_is_ok___all_checks_succeed) +{ + CheckFinalReservoirLevelsConfiguration(*study); + + // Checks on Area 1 modifier + BOOST_CHECK_EQUAL(area_1->hydro.deltaBetweenFinalAndInitialLevels[0].has_value(), true); + BOOST_CHECK_EQUAL(area_1->hydro.deltaBetweenFinalAndInitialLevels[1].has_value(), true); + BOOST_CHECK_EQUAL(area_1->hydro.deltaBetweenFinalAndInitialLevels[0].value(), 3.4 - 2.3); + BOOST_CHECK_EQUAL(area_1->hydro.deltaBetweenFinalAndInitialLevels[1].value(), 5.1 - 4.2); + + // Checks on Area 2 modifier + BOOST_CHECK_EQUAL(area_2->hydro.deltaBetweenFinalAndInitialLevels[0].has_value(), true); + BOOST_CHECK_EQUAL(area_2->hydro.deltaBetweenFinalAndInitialLevels[1].has_value(), true); + BOOST_CHECK_EQUAL(area_2->hydro.deltaBetweenFinalAndInitialLevels[0].value(), 3.5 - 1.5); + BOOST_CHECK_EQUAL(area_2->hydro.deltaBetweenFinalAndInitialLevels[1].value(), 4.3 - 2.4); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/src/ui/simulator/application/main/build/scenario-builder.cpp b/src/ui/simulator/application/main/build/scenario-builder.cpp index 5d46aaf6c0..350c1633f7 100644 --- a/src/ui/simulator/application/main/build/scenario-builder.cpp +++ b/src/ui/simulator/application/main/build/scenario-builder.cpp @@ -35,6 +35,7 @@ #include "toolbox/components/datagrid/renderer/scenario-builder-wind-renderer.h" #include "toolbox/components/datagrid/renderer/scenario-builder-solar-renderer.h" #include "toolbox/components/datagrid/renderer/scenario-builder-hydro-levels-renderer.h" +#include "toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.h" #include "toolbox/components/datagrid/renderer/scenario-builder-ntc-renderer.h" using namespace Yuni; @@ -199,8 +200,8 @@ class solarScBuilderPageMaker final : public simpleScBuilderPageMaker } }; -// Hydro levels ... -class hydroLevelsScBuilderPageMaker final : public simpleScBuilderPageMaker +// Hydro Initial levels ... +class hydroInitialLevelsScBuilderPageMaker final : public simpleScBuilderPageMaker { using simpleScBuilderPageMaker::simpleScBuilderPageMaker; @@ -210,7 +211,22 @@ class hydroLevelsScBuilderPageMaker final : public simpleScBuilderPageMaker } Notebook::Page* addPageToNotebook() override { - return notebook()->add(grid(), wxT("hydro levels"), wxT("Hydro Levels")); + return notebook()->add(grid(), wxT("hydro initial levels"), wxT("Hydro Initial Levels")); + } +}; + +// Hydro Final levels ... +class hydroFinalLevelsScBuilderPageMaker final : public simpleScBuilderPageMaker +{ + using simpleScBuilderPageMaker::simpleScBuilderPageMaker; + + Renderer::ScBuilderRendererBase* getRenderer() override + { + return new_check_allocation(); + } + Notebook::Page* addPageToNotebook() override + { + return notebook()->add(grid(), wxT("hydro final levels"), wxT("Hydro Final Levels")); } }; @@ -366,9 +382,13 @@ void ApplWnd::createNBScenarioBuilder() pScenarioBuilderNotebook->addSeparator(); - hydroLevelsScBuilderPageMaker hydroLevelsSBpageMaker(scenarioBuilderPanel, + hydroInitialLevelsScBuilderPageMaker hydroInitialLevelsSBpageMaker(scenarioBuilderPanel, + pScenarioBuilderNotebook); + pageScBuilderHydroInitialLevels = hydroInitialLevelsSBpageMaker.createPage(); + + hydroFinalLevelsScBuilderPageMaker hydroFinalLevelsSBpageMaker(scenarioBuilderPanel, pScenarioBuilderNotebook); - pageScBuilderHydroLevels = hydroLevelsSBpageMaker.createPage(); + pageScBuilderHydroFinalLevels = hydroFinalLevelsSBpageMaker.createPage(); } void ApplWnd::createNBOutputViewer() diff --git a/src/ui/simulator/application/main/main.h b/src/ui/simulator/application/main/main.h index aa16c92f33..0b1f9dd8bb 100644 --- a/src/ui/simulator/application/main/main.h +++ b/src/ui/simulator/application/main/main.h @@ -693,7 +693,8 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent Component::Notebook::Page* pageScBuilderSolar; Component::Notebook::Page* pageScBuilderNTC; Component::Notebook::Page* pageScBuilderRenewable; - Component::Notebook::Page* pageScBuilderHydroLevels; + Component::Notebook::Page* pageScBuilderHydroInitialLevels; + Component::Notebook::Page* pageScBuilderHydroFinalLevels; //! A context menu for the map wxMenu* pMapContextMenu; diff --git a/src/ui/simulator/cmake/components.cmake b/src/ui/simulator/cmake/components.cmake index ad9dfba7a7..0586928a48 100644 --- a/src/ui/simulator/cmake/components.cmake +++ b/src/ui/simulator/cmake/components.cmake @@ -92,6 +92,8 @@ SET(SRC_TOOLBOX_COM_DBGRID_RENDERERS toolbox/components/datagrid/renderer/scenario-builder-solar-renderer.h toolbox/components/datagrid/renderer/scenario-builder-hydro-levels-renderer.h toolbox/components/datagrid/renderer/scenario-builder-hydro-levels-renderer.cpp + toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.h + toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.cpp toolbox/components/datagrid/renderer/scenario-builder-ntc-renderer.h toolbox/components/datagrid/renderer/scenario-builder-ntc-renderer.cpp toolbox/components/datagrid/renderer/layers.cpp diff --git a/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.cpp b/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.cpp new file mode 100644 index 0000000000..809767e8e6 --- /dev/null +++ b/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.cpp @@ -0,0 +1,82 @@ +/* +** Copyright 2007-2023 RTE +** Authors: RTE-international / Redstork / Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ + +#include "scenario-builder-hydro-final-levels-renderer.h" +#include "antares/study/scenario-builder/scBuilderUtils.h" + +using namespace Antares::Data::ScenarioBuilder; + +namespace Antares +{ +namespace Component +{ +namespace Datagrid +{ +namespace Renderer +{ +wxString hydroFinalLevelsScBuilderRenderer::cellValue(int x, int y) const +{ + const double d = cellNumericValue(x, y); + return (std::isnan(d)) ? wxString() << wxT("init") : wxString() << fromHydroLevelToString(d); +} + +bool hydroFinalLevelsScBuilderRenderer::cellValue(int x, int y, const Yuni::String& value) +{ + if (!(!study) && !(!pRules) && (uint)x < study->parameters.nbYears + && (uint)y < study->areas.size()) + { + assert((uint)y < pRules->hydroFinalLevels.width()); + assert((uint)x < pRules->hydroFinalLevels.height()); + double val = fromStringToHydroLevel(value, 100.) / 100.; + pRules->hydroFinalLevels.set_value(x, y, val); + return true; + } + return false; +} + +double hydroFinalLevelsScBuilderRenderer::cellNumericValue(int x, int y) const +{ + if (!(!study) && !(!pRules) && (uint)x < study->parameters.nbYears + && (uint)y < study->areas.size()) + { + assert((uint)y < pRules->hydroFinalLevels.width()); + assert((uint)x < pRules->hydroFinalLevels.height()); + return pRules->hydroFinalLevels.get_value(x, y) * 100.; + } + return 0.; +} + +IRenderer::CellStyle hydroFinalLevelsScBuilderRenderer::cellStyle(int x, int y) const +{ + bool valid = (!(!study) && !(!pRules) && std::isnan(cellNumericValue(x, y))); + return valid ? cellStyleDefaultCenterDisabled : cellStyleDefaultCenter; +} + +} // namespace Renderer +} // namespace Datagrid +} // namespace Component +} // namespace Antares diff --git a/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.h b/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.h new file mode 100644 index 0000000000..85a7c87e0f --- /dev/null +++ b/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-final-levels-renderer.h @@ -0,0 +1,56 @@ +/* +** Copyright 2007-2023 RTE +** Authors: RTE-international / Redstork / Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#ifndef __ANTARES_TOOLBOX_COMPONENT_DATAGRID_RENDERER_HYDRO_FINAL_LEVELS_SCENARIO_BUILDER_H__ +#define __ANTARES_TOOLBOX_COMPONENT_DATAGRID_RENDERER_HYDRO_FINAL_LEVELS_SCENARIO_BUILDER_H__ + +#include "scenario-builder-renderer-base.h" + +namespace Antares +{ +namespace Component +{ +namespace Datagrid +{ +namespace Renderer +{ +class hydroFinalLevelsScBuilderRenderer : public ScBuilderRendererAreasAsRows +{ +public: + hydroFinalLevelsScBuilderRenderer() = default; + + wxString cellValue(int x, int y) const; + bool cellValue(int x, int y, const Yuni::String& value); + double cellNumericValue(int x, int y) const; + IRenderer::CellStyle cellStyle(int x, int y) const; +}; // class hydroLevelsScBuilderRenderer + +} // namespace Renderer +} // namespace Datagrid +} // namespace Component +} // namespace Antares + +#endif // __ANTARES_TOOLBOX_COMPONENT_DATAGRID_RENDERER_HYDRO_FINAL_LEVELS_SCENARIO_BUILDER_H__ diff --git a/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-levels-renderer.cpp b/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-levels-renderer.cpp index ae8e43076f..4dce437a7d 100644 --- a/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-levels-renderer.cpp +++ b/src/ui/simulator/toolbox/components/datagrid/renderer/scenario-builder-hydro-levels-renderer.cpp @@ -40,30 +40,26 @@ wxString hydroLevelsScBuilderRenderer::cellValue(int x, int y) const bool hydroLevelsScBuilderRenderer::cellValue(int x, int y, const Yuni::String& value) { - if (!(!study) && !(!pRules) && (uint)x < study->parameters.nbYears) + if (!(!study) && !(!pRules) && (uint)x < study->parameters.nbYears + && (uint)y < study->areas.size()) { - if ((uint)y < study->areas.size()) - { - assert((uint)y < pRules->hydroLevels.width()); - assert((uint)x < pRules->hydroLevels.height()); - double val = fromStringToHydroLevel(value, 100.) / 100.; - pRules->hydroLevels.set_value(x, y, val); - return true; - } + assert((uint)y < pRules->hydroInitialLevels.width()); + assert((uint)x < pRules->hydroInitialLevels.height()); + double val = fromStringToHydroLevel(value, 100.) / 100.; + pRules->hydroInitialLevels.set_value(x, y, val); + return true; } return false; } double hydroLevelsScBuilderRenderer::cellNumericValue(int x, int y) const { - if (!(!study) && !(!pRules) && (uint)x < study->parameters.nbYears) + if (!(!study) && !(!pRules) && (uint)x < study->parameters.nbYears + && (uint)y < study->areas.size()) { - if ((uint)y < study->areas.size()) - { - assert((uint)y < pRules->hydroLevels.width()); - assert((uint)x < pRules->hydroLevels.height()); - return pRules->hydroLevels.get_value(x, y) * 100.; - } + assert((uint)y < pRules->hydroInitialLevels.width()); + assert((uint)x < pRules->hydroInitialLevels.height()); + return pRules->hydroInitialLevels.get_value(x, y) * 100.; } return 0.; } @@ -71,7 +67,7 @@ double hydroLevelsScBuilderRenderer::cellNumericValue(int x, int y) const IRenderer::CellStyle hydroLevelsScBuilderRenderer::cellStyle(int x, int y) const { bool valid = (!(!study) && !(!pRules) && std::isnan(cellNumericValue(x, y))); - return (valid) ? cellStyleDefaultCenterDisabled : cellStyleDefaultCenter; + return valid ? cellStyleDefaultCenterDisabled : cellStyleDefaultCenter; } } // namespace Renderer From 6ce594fcdcddfc2c5bb6b504c16f8cac9be3862a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Omn=C3=A8s?= Date: Fri, 21 Jun 2024 15:31:11 +0200 Subject: [PATCH 11/15] Remove src/antares-deps (#2182) --- src/antares-deps | 1 - 1 file changed, 1 deletion(-) delete mode 160000 src/antares-deps diff --git a/src/antares-deps b/src/antares-deps deleted file mode 160000 index 0d6bebfb90..0000000000 --- a/src/antares-deps +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0d6bebfb901e47ec6ac1c73cb61a522803c81b98 From 249df286b073c9f90a3a0d0e75fc5fde00180f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Omn=C3=A8s?= Date: Fri, 21 Jun 2024 15:52:41 +0200 Subject: [PATCH 12/15] Fix condition for disabling "store in input" (#2180) --- src/libs/antares/study/load.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/antares/study/load.cpp b/src/libs/antares/study/load.cpp index 4555e106bd..4175bc2305 100644 --- a/src/libs/antares/study/load.cpp +++ b/src/libs/antares/study/load.cpp @@ -126,11 +126,11 @@ void Study::parameterFiller(const StudyLoadOptions& options) if (usedByTheSolver) { // We have time-series to import - if (parameters.exportTimeSeriesInInput && header.version == StudyVersion::latest()) + if (parameters.exportTimeSeriesInInput && header.version != StudyVersion::latest()) { logs.info() << "Stochastic TS stored in input parametrized." - " Disabling Store in input because study is not at latest version" - "Prevents writing data in unsupported format at the study version"; + " Disabling Store in input because study is not at latest version." + " This prevents writing data in unsupported format at the study version"; parameters.exportTimeSeriesInInput = 0; } } From 48a97fab3767c532992e8b08334f27b5d1b290f2 Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:39:10 +0200 Subject: [PATCH 13/15] Fix initialization order warnings (#2183) --- src/libs/antares/study/area/area.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/libs/antares/study/area/area.cpp b/src/libs/antares/study/area/area.cpp index 2984704716..da99f973ae 100644 --- a/src/libs/antares/study/area/area.cpp +++ b/src/libs/antares/study/area/area.cpp @@ -45,28 +45,21 @@ void Area::internalInitialize() } Area::Area(): - hydro(*this), reserves(fhrMax, HOURS_PER_YEAR), - miscGen(fhhMax, HOURS_PER_YEAR) + miscGen(fhhMax, HOURS_PER_YEAR), + hydro(*this) { internalInitialize(); } -Area::Area(const AnyString& name): - hydro(*this), - reserves(fhrMax, HOURS_PER_YEAR), - miscGen(fhhMax, HOURS_PER_YEAR) +Area::Area(const AnyString& name) : Area() { internalInitialize(); this->name = name; this->id = Antares::transformNameIntoID(this->name); } -Area::Area(const AnyString& name, const AnyString& id): - - hydro(*this), - reserves(fhrMax, HOURS_PER_YEAR), - miscGen(fhhMax, HOURS_PER_YEAR) +Area::Area(const AnyString& name, const AnyString& id) : Area() { internalInitialize(); this->name = name; From b6d824fba89dd12250202779c80149509d5db772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Omn=C3=A8s?= Date: Fri, 21 Jun 2024 18:09:32 +0200 Subject: [PATCH 14/15] Remove temporary MSVC flag used to mitigate runner issue (#2184) Revert "Add `/D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR` to fix segfault #2151 This reverts commit a121c7571a4b6e7fed2904b325aa8127dd25261e. https://github.com/actions/runner-images/issues/10004 --- src/cmake/common-settings.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmake/common-settings.cmake b/src/cmake/common-settings.cmake index 23326f6158..b659a5f46c 100644 --- a/src/cmake/common-settings.cmake +++ b/src/cmake/common-settings.cmake @@ -11,7 +11,7 @@ if (NOT WIN32) set(COMMON_GCC_FLAGS "${COMMON_GCC_FLAGS} -Werror=return-type") endif() set(COMMON_MSVC_FLAGS "/W3 /MP4") -set(COMMON_MSVC_FLAGS "${COMMON_MSVC_FLAGS} /we4715 /we4716 /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR") #adding no return or no return for all code paths as errors +set(COMMON_MSVC_FLAGS "${COMMON_MSVC_FLAGS} /we4715 /we4716") #adding no return or no return for all code paths as errors set(ADDITIONAL_C_FLAGS " -Wconversion -Wmissing-prototypes -Wstrict-prototypes") set(ADDITIONAL_C_FLAGS "${ADDITIONAL_C_FLAGS} -Wmissing-noreturn -Wpacked -Wredundant-decls -Wbad-function-cast -W -Wcast-align -Wcast-qual -Wsign-compare -fno-exceptions -Wdeclaration-after-statement") From b6d824072be6c7b49d66e92565ff774864ce9f19 Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:17:47 +0200 Subject: [PATCH 15/15] Remove 200ms wait necessary for the GUI (#2186) close #2164 --- src/solver/main.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/solver/main.cpp b/src/solver/main.cpp index 8d07e654b8..169b29321a 100644 --- a/src/solver/main.cpp +++ b/src/solver/main.cpp @@ -130,9 +130,6 @@ int main(int argc, char** argv) application.execute(); application.writeExectutionInfo(); - // to avoid a bug from wxExecute, we should wait a little before returning - SuspendMilliSeconds(200 /*ms*/); - return EXIT_SUCCESS; } catch (const std::bad_alloc& exc)