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 1/8] 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 bd464303365..fc8492a9e42 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 4d4de31f482..78ff065933c 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 3ab1b49f6d9..1f8e24673d5 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 9029e0c9bb2..422fd8e3bd2 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 68dae733e65..100a4b127e0 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 56b6e3982be..6fa2819acad 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 fcbb8b00803..d9a1d467f03 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 5d79c65d30b..e7467d9fb7e 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 cbe251805b3..709bab62fe1 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 eefe3b2606a..dca1b1eb154 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 3ac64a284e0..3517b09d212 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 e86b32a568d..103d7a83bb2 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 8337795ca12..23f547783d0 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 63d8462dfc0..2b03f7bcdc8 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 8564418f629..b322d62decb 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 02fac95314f..934ebc4c2c5 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 14d260af099..6ea4a9e003c 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 8f74ccb94a2..66400ed678e 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 29c087759fa..fe16b660b94 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 87f38225ccd..adf8c23c4d6 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 860d2b8bfe4..03126fa7522 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 c51d8283dc7..155cb6b3bc7 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 f6906496685..14ee6a4435f 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 97b0589bd4c..a361fc227b6 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 a42d87c9c8f..1410d05595b 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 2/8] 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 e7bd7272879..417e60bae4b 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 e7467d9fb7e..0297540a4d4 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 dbc015ecce3..b35798f8622 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 103d7a83bb2..0343c2878fd 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 b7864e5b312..fa2827fc883 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 d8ddb72352b..9370c562faf 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 3/8] 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 e6524920ee5..67190a0654e 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 417e60bae4b..219f7ee8aae 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 ce168c7eefe..ff181e7ed8b 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 b3dc2ed6f61..3c8f183be4a 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 aa8916c26cc..cb94bf5d307 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 d9a1d467f03..c66cf9757fe 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 dca1b1eb154..00214717e8b 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 3517b09d212..a598ee1566c 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 e00934dfe7e..9be5c2ca574 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 93ee0ef5d0e..597264cf20e 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 0343c2878fd..d955da1c81f 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 1c9ca78bf7f..749613a14be 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 f10da99345e..035faf86acf 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 24b5b8cb6b4..46a0694a58e 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 962f7c122d3..76dbcb9166e 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 23f547783d0..511e61a5922 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 837f37a4854..3b8263eadf3 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 4233784088f..51ed1624b06 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 b9a60ea4af2..0427c7441c4 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 d221d19eb68..d222ad88c04 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 8391b523d04..5c6bcab8974 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 b20be5e375e..6cea7034423 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 2cb868da72a..62ce897eb80 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 c90c8b4809c..1b170ae353d 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 b413b632d54..1f562354997 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 b322d62decb..81602073a10 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 a94b8f1dfe6..dcf39052ceb 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 adf8c23c4d6..8084130e9f7 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 155cb6b3bc7..ed13d54fe1e 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 2cb42277556..3423a9a8ade 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 1b4ac3496de..29d0a29f42f 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 feb8412f49c..ca0c96087ac 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 4fdeb096c0b..d3b34bb9734 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 e05283ff5ac..0402fb6c151 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 4/8] 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 219f7ee8aae..b622b94af9f 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 ede65679a23..6661ab8556d 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 ea1518750d6..9f091defda9 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 6a008f89291..da502ac29e6 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 2059cf92381..4bf2e042c24 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 5/8] 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 b622b94af9f..fd2b3bb5b7e 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 edec4d14687..275d495020c 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 d42e9c8d894..62db61a9001 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 6/8] 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 c774854d16b..85713eeb70f 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 ccc8794dcb5..59e931e6a8e 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 0e5f50726c1..5dbc3f88852 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 4d4b6af1409..40da9df1493 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 fd2b3bb5b7e..f76afb5686d 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 3c8f183be4a..7d1aadf58e9 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 100a4b127e0..f45f093aaf5 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 6661ab8556d..6fbb69dde40 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 fd4b8e69d87..5b01ba3efdf 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 6fa2819acad..7256eda4a26 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 898b6416534..1ecd34b9683 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 c66cf9757fe..cb69fb43264 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 a598ee1566c..5c0cdac583d 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 8084130e9f7..5e36fcaca87 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 f9b50696df7..69f62a10a1f 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 77e09b9b79a..1c32e67bdd2 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 cc02a506192..ac36a826eaf 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 f59a5dc614b..8f23f606194 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 7/8] 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 cb69fb43264..791e20f8653 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 00214717e8b..4555e106bdf 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 5c0cdac583d..7337b9c8b02 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 14ee6a4435f..07c8e7fa1da 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 8/8] 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 5dbc3f88852..c423bc53469 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 51fdc672712..862c7fdbc8f 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 00000000000..0d6bebfb901 --- /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 c0b5b84c56d..c90d543fffd 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 77f0fe6dd9d..29847047169 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 0297540a4d4..e09970b79cc 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 00000000000..518e29d40b9 --- /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 0f01fb9c32f..42aff6336a6 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 b79d92be868..ff5261d64ad 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 709bab62fe1..03e2df8bed9 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 d955da1c81f..c9dc476de5c 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 00000000000..2229f75f97f --- /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 8abb0193666..3295df45036 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 fb39a275fd2..fd79f07f7c2 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 3050515ca57..b642e441c2e 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 5c6bcab8974..f7e4665159c 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 6cea7034423..703235fc614 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 62ce897eb80..aa10b7c10e3 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 3c59c59d554..0d2bc666ebe 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 00000000000..32f2d4dfa23 --- /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 00000000000..4b60c4cc8f6 --- /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 5e36fcaca87..232fa520fcf 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 e5813befd84..bfad50326f9 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 769683412d3..a42891ea45d 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 36346b20146..7879559fb09 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 e1b0666b7d2..13d5a38b48b 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 e056ac388db..3ba8f097552 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 00000000000..d4a6c3512be --- /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 5d46aaf6c01..350c1633f71 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 aa16c92f339..0b1f9dd8bb2 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 ad9dfba7a70..0586928a48f 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 00000000000..809767e8e65 --- /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 00000000000..85a7c87e0f1 --- /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 ae8e43076f8..4dce437a7db 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