Loading [MathJax]/extensions/MathMenu.js
Antares Simulator
Power System Simulator
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages Concepts
solver.hxx
1/*
2 * Copyright 2007-2024, RTE (https://www.rte-france.com)
3 * See AUTHORS.txt
4 * SPDX-License-Identifier: MPL-2.0
5 * This file is part of Antares-Simulator,
6 * Adequacy and Performance assessment for interconnected energy networks.
7 *
8 * Antares_Simulator is free software: you can redistribute it and/or modify
9 * it under the terms of the Mozilla Public Licence 2.0 as published by
10 * the Mozilla Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Antares_Simulator is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * Mozilla Public Licence 2.0 for more details.
17 *
18 * You should have received a copy of the Mozilla Public Licence 2.0
19 * along with Antares_Simulator. If not, see <https://opensource.org/license/mpl-2-0/>.
20 */
21#ifndef __SOLVER_SIMULATION_SOLVER_HXX__
22#define __SOLVER_SIMULATION_SOLVER_HXX__
23
24#include <yuni/io/io.h>
25
26#include <antares/antares/fatal-error.h>
27#include <antares/date/date.h>
28#include <antares/exception/InitializationError.hpp>
29#include <antares/logs/logs.h>
30#include "antares/concurrency/concurrency.h"
31#include "antares/solver/hydro/management/HydroInputsChecker.h"
32#include "antares/solver/hydro/management/management.h"
33#include "antares/solver/simulation/opt_time_writer.h"
34#include "antares/solver/simulation/timeseries-numbers.h"
35#include "antares/solver/ts-generator/generator.h"
36#include "antares/solver/variable/print.h"
37
38namespace Antares::Solver::Simulation
39{
40
41template<class Impl>
43{
44public:
45 yearJob(ISimulation<Impl>* simulation,
46 unsigned int pY,
47 std::map<uint, bool>& pYearFailed,
48 std::map<uint, bool>& pIsFirstPerformedYearOfASet,
49 bool pFirstSetParallelWithAPerformedYearWasRun,
50 unsigned int pNumSpace,
51 randomNumbers& pRandomForParallelYears,
52 bool pPerformCalculations,
53 Data::Study& pStudy,
54 std::vector<Variable::State>& pStates,
55 bool pYearByYear,
56 Benchmarking::DurationCollector& durationCollector,
57 IResultWriter& resultWriter,
58 ISimulationObserver& simulationObserver):
59 simulation_(simulation),
60 y(pY),
61 yearFailed(pYearFailed),
62 isFirstPerformedYearOfASet(pIsFirstPerformedYearOfASet),
63 firstSetParallelWithAPerformedYearWasRun(pFirstSetParallelWithAPerformedYearWasRun),
64 numSpace(pNumSpace),
65 randomForParallelYears(pRandomForParallelYears),
66 performCalculations(pPerformCalculations),
67 study(pStudy),
68 states(pStates),
69 yearByYear(pYearByYear),
70 pDurationCollector(durationCollector),
71 pResultWriter(resultWriter),
72 simulationObserver_(simulationObserver),
73 hydroManagement(study.areas, study.parameters, study.calendar, resultWriter)
74 {
75 }
76
77 yearJob(const yearJob&) = delete;
78 yearJob& operator=(const yearJob&) = delete;
79 ~yearJob() = default;
80
81private:
82 ISimulation<Impl>* simulation_;
83 unsigned int y;
84 std::map<uint, bool>& yearFailed;
85 std::map<uint, bool>& isFirstPerformedYearOfASet;
86 bool firstSetParallelWithAPerformedYearWasRun;
87 unsigned int numSpace;
88 randomNumbers& randomForParallelYears;
89 bool performCalculations;
90 Data::Study& study;
91 std::vector<Variable::State>& states;
92 bool yearByYear;
93 Benchmarking::DurationCollector& pDurationCollector;
94 IResultWriter& pResultWriter;
95 std::reference_wrapper<ISimulationObserver> simulationObserver_;
96 HydroManagement hydroManagement;
97
98private:
99 /*
100 ** \brief Log failed week
101 **
102 ** \param y MC Year
103 ** \param study The Antares study
104 ** \param failedWeek List of failing week
105 */
106 void logFailedWeek(int y, const Data::Study& study, const std::list<uint>& failedWeekList)
107 {
108 if (!failedWeekList.empty())
109 {
110 std::stringstream failedWeekStr;
111 std::ranges::copy(failedWeekList, std::ostream_iterator<int>(failedWeekStr, " "));
112
113 std::string s = failedWeekStr.str();
114 s = s.substr(0, s.length() - 1); // get rid of the trailing space
115
116 std::string failedStr = failedWeekList.size() != 1 ? " failed at weeks "
117 : " failed at week ";
118
119 logs.info(); // empty line
120
121 if (Data::stopSimulation(study.parameters.include.unfeasibleProblemBehavior))
122 {
123 logs.fatal() << "Year " << y + 1 << failedStr << s << ".";
124 }
125 else
126 {
127 logs.warning() << "Year " << y + 1 << failedStr << s << ".";
128 }
129 }
130 }
131
132public:
133 void operator()()
134 {
135 Progression::Task progression(study, y, Solver::Progression::sectYear);
136
137 if (performCalculations)
138 {
139 // Index of the current year in the list of structures
140 uint indexYear = randomForParallelYears.yearNumberToIndex[y];
141
142 // Getting random tables for this year
143 yearRandomNumbers& randomForCurrentYear = randomForParallelYears.pYears[indexYear];
144
145 // 1 - Applying random levels for current year
146 auto randomReservoirLevel = randomForCurrentYear.pReservoirLevels;
147
148 // Getting the scratchMap associated to the current year
149 Antares::Data::Area::ScratchMap scratchmap = study.areas.buildScratchMap(numSpace);
150
151 // 3 - Preparing data related to Clusters in 'must-run' mode
152 simulation_->prepareClustersInMustRunMode(scratchmap, y);
153
154 // 4 - Hydraulic ventilation
155 pDurationCollector("hydro_ventilation") << [this, &scratchmap, &randomReservoirLevel]
156 { hydroManagement.makeVentilation(randomReservoirLevel.data(), y, scratchmap); };
157
158 // Updating the state
159 auto& state = states[numSpace];
160 state.year = y;
161
162 // 5 - Resetting all variables for the output
163 simulation_->variables.yearBegin(y, numSpace);
164
165 // 6 - The Solver itself
166 bool isFirstPerformedYearOfSimulation = isFirstPerformedYearOfASet[y]
167 && not firstSetParallelWithAPerformedYearWasRun;
168 std::list<uint> failedWeekList;
169
170 OptimizationStatisticsWriter optWriter(pResultWriter, y);
171 yearFailed[y] = !simulation_->year(progression,
172 state,
173 numSpace,
174 randomForCurrentYear,
175 failedWeekList,
176 isFirstPerformedYearOfSimulation,
177 hydroManagement.ventilationResults(),
178 optWriter,
179 scratchmap);
180
181 // Log failing weeks
182 logFailedWeek(y, study, failedWeekList);
183
184 simulation_->variables.yearEndBuild(state, y, numSpace);
185
186 // 7 - End of the year, this is the last stade where the variables can retrieve
187 // their data for this year.
188 simulation_->variables.yearEnd(y, numSpace);
189
190 // 8 - Spatial clusters
191 // Notifying all variables to perform spatial aggregates.
192 // This must be done only when all variables have finished to compute their
193 // data for the year.
194 simulation_->variables.yearEndSpatialAggregates(simulation_->variables, y, numSpace);
195
196 // 9 - Write results for the current year
197 if (yearByYear)
198 {
199 pDurationCollector("yby_export") << [this]
200 {
201 // Before writing, some variable may require minor modifications
202 simulation_->variables.beforeYearByYearExport(y, numSpace);
203 // writing the results for the current year into the output
204 simulation_->writeResults(false, y, numSpace); // false for synthesis
205 };
206 }
207 }
208 else
209 {
210 simulation_->incrementProgression(progression);
211
212 logs.info() << " playlist: ignoring the year " << (y + 1);
213
214 yearFailed[y] = false;
215
216 } // End if(performCalculations)
217
218 } // End of onExecute() method
219};
220
221template<class ImplementationType>
223 Data::Study& study,
224 const ::Settings& settings,
225 Benchmarking::DurationCollector& duration_collector,
226 IResultWriter& resultWriter,
227 Simulation::ISimulationObserver& simulationObserver):
228 ImplementationType(study, resultWriter, simulationObserver),
229 study(study),
230 settings(settings),
231 pNbYearsReallyPerformed(0),
232 pNbMaxPerformedYearsInParallel(0),
233 pYearByYear(study.parameters.yearByYear),
234 pFirstSetParallelWithAPerformedYearWasRun(false),
235 pDurationCollector(duration_collector),
236 pQueueService(study.pQueueService),
237 pResultWriter(resultWriter),
238 simulationObserver_(simulationObserver)
239{
240 // Ask to the interface to show the messages
241 logs.info();
242 logs.info() << LOG_UI_DISPLAY_MESSAGES_ON;
243
244 // Running !
245 logs.checkpoint() << "Running the simulation (" << ImplementationType::Name() << ')';
246 logs.info() << "Allocating resources...";
247
248 if (pYearByYear && (settings.noOutput || settings.tsGeneratorsOnly))
249 {
250 pYearByYear = false;
251 }
252}
253
254template<class ImplementationType>
256{
257 // The zip writer needs a queue service (async mutexed write)
258 if (!pQueueService && pResultWriter.needsTheJobQueue())
259 {
261 }
262}
263
264template<class ImplementationType>
268
269template<class ImplementationType>
271{
272 pNbMaxPerformedYearsInParallel = study.maxNbYearsInParallel;
273
274 // Initialize all data
275 ImplementationType::variables.initializeFromStudy(study);
276 // Computing the max number columns a report of any kind can contain.
277 study.parameters.variablesPrintInfo.computeMaxColumnsCountInReports();
278
279 logs.info() << "Allocating resources...";
280
281 // Memory usage
282 {
284 ImplementationType::variables.template provideInformations<Variable::PrintInfosStdCout>(c);
285 }
286
287 // Preprocessors
288 // Determine if we have to use the preprocessors at least one time.
289 pData.initialize(study.parameters);
290
291 ImplementationType::setNbPerformedYearsInParallel(pNbMaxPerformedYearsInParallel);
292
293 if (settings.tsGeneratorsOnly)
294 {
295 // Only the preprocessors can be used
296 // We only have to regenerate time-series according the settings
297 // in general data of the study.
298 logs.info() << " Only the preprocessors are enabled.";
299
300 regenerateTimeSeries(0);
301
302 // Destroy the TS Generators if any
303 // It will export the time-series into the output in the same time
304 TSGenerator::DestroyAll(study);
305 }
306 else
307 {
308 // Export ts-numbers into output
309 TimeSeriesNumbers::StoreTimeSeriesNumbersIntoOuput(study, pResultWriter);
310
311 if (not ImplementationType::simulationBegin())
312 {
313 return;
314 }
315 // Allocating the memory
316 ImplementationType::variables.simulationBegin();
317
318 // For beauty
319 logs.info();
320
321 // Launching the simulation for all years
322 logs.info() << "MC-Years : [" << (study.runtime.rangeLimits.year[Data::rangeBegin] + 1)
323 << " .. " << (1 + study.runtime.rangeLimits.year[Data::rangeEnd])
324 << "], total: " << study.runtime.rangeLimits.year[Data::rangeCount];
325
326 // Current state
327 std::vector<Variable::State> state(pNbMaxPerformedYearsInParallel, Variable::State(study));
328 // Initializing states for parallel actually performed years
329 for (uint numSpace = 0; numSpace != pNbMaxPerformedYearsInParallel; ++numSpace)
330 {
331 ImplementationType::initializeState(state[numSpace], numSpace);
332 }
333
334 uint finalYear = 1 + study.runtime.rangeLimits.year[Data::rangeEnd];
335 {
336 pDurationCollector("mc_years")
337 << [finalYear, &state, this] { loopThroughYears(0, finalYear, state); };
338 }
339 // Destroy the TS Generators if any
340 // It will export the time-series into the output in the same time
341 TSGenerator::DestroyAll(study);
342
343 // Post operations
344 pDurationCollector("post_processing") << [this] { ImplementationType::simulationEnd(); };
345
346 ImplementationType::variables.simulationEnd();
347
348 // Spatial clusters
349 // Notifying all variables to perform the final spatial clusters.
350 // This must be done only when all variables have finished to compute their
351 // own data.
352 ImplementationType::variables.simulationEndSpatialAggregates(ImplementationType::variables);
353 }
354}
355
356template<class ImplementationType>
357void ISimulation<ImplementationType>::writeResults(bool synthesis, uint year, uint numSpace)
358{
359 using namespace Yuni;
360
361 // The writer might need the job queue, after it's been stopped
362 // this is the case e.g if synthesis == true (writing mc-all)
363 // Don't restart the queue if the writer doesn't need it
364
365 assert(!settings.noOutput);
366 assert(!settings.tsGeneratorsOnly);
367
368 if (!pNbYearsReallyPerformed)
369 {
370 logs.info();
371 logs.info() << "The writing of the output results is disabled.";
372 logs.info();
373 }
374 else
375 {
376 if (synthesis)
377 {
378 const auto& parameters = study.parameters;
379 if (not parameters.synthesis) // disabled by parameters
380 {
381 logs.info() << "The simulation synthesis is disabled.";
382 return;
383 }
384 }
385
386 // The target folder
387 String newPath;
388 newPath << ImplementationType::Name() << IO::Separator;
389 if (synthesis)
390 {
391 newPath << "mc-all";
392 }
393 else
394 {
395 CString<10, false> tmp;
396 tmp = (year + 1);
397 newPath << "mc-ind" << IO::Separator << "00000";
398 newPath.overwriteRight(tmp);
399 }
400
401 // Dumping
402 ImplementationType::variables.exportSurveyResults(synthesis,
403 newPath,
404 numSpace,
405 pResultWriter);
406 }
407}
408
409template<class ImplementationType>
411{
412 // A preprocessor can be launched for several reasons:
413 // * The option "Preprocessor" is checked in the interface _and_ year == 0
414 // * Both options "Preprocessor" and "Refresh" are checked in the interface
415 // _and_ the refresh must be done for the given year (always done for the first year).
416 using namespace TSGenerator;
417 // Load
418 if (pData.haveToRefreshTSLoad && (year % pData.refreshIntervalLoad == 0))
419 {
420 pDurationCollector("tsgen_load")
421 << [year, this] { GenerateTimeSeries<Data::timeSeriesLoad>(study, year, pResultWriter); };
422 }
423 // Solar
424 if (pData.haveToRefreshTSSolar && (year % pData.refreshIntervalSolar == 0))
425 {
426 pDurationCollector("tsgen_solar") << [year, this]
427 { GenerateTimeSeries<Data::timeSeriesSolar>(study, year, pResultWriter); };
428 }
429 // Wind
430 if (pData.haveToRefreshTSWind && (year % pData.refreshIntervalWind == 0))
431 {
432 pDurationCollector("tsgen_wind")
433 << [year, this] { GenerateTimeSeries<Data::timeSeriesWind>(study, year, pResultWriter); };
434 }
435 // Hydro
436 if (pData.haveToRefreshTSHydro && (year % pData.refreshIntervalHydro == 0))
437 {
438 pDurationCollector("tsgen_hydro") << [year, this]
439 { GenerateTimeSeries<Data::timeSeriesHydro>(study, year, pResultWriter); };
440 }
441
442 // Thermal
443 const bool refreshTSonCurrentYear = (year % pData.refreshIntervalThermal == 0);
444
445 pDurationCollector("tsgen_thermal") << [refreshTSonCurrentYear, year, this]
446 {
447 if (refreshTSonCurrentYear)
448 {
449 auto clusters = getAllClustersToGen(study.areas, pData.haveToRefreshTSThermal);
450 generateThermalTimeSeries(study,
451 clusters,
452 study.runtime.random[Data::seedTsGenThermal]);
453
454 bool archive = study.parameters.timeSeriesToArchive & Data::timeSeriesThermal;
455 bool doWeWrite = archive && !study.parameters.noOutput;
456 if (doWeWrite)
457 {
458 fs::path savePath = study.folderOutput / "ts-generator" / "thermal" / "mc-"
459 / std::to_string(year);
460 writeThermalTimeSeries(clusters, savePath);
461 }
462
463 // apply the spinning if we generated some in memory clusters
464 for (auto* cluster: clusters)
465 {
466 cluster->calculationOfSpinning();
467 }
468 }
469 };
470}
471
472template<class ImplementationType>
473uint ISimulation<ImplementationType>::buildSetsOfParallelYears(
474 uint firstYear,
475 uint endYear,
476 std::vector<setOfParallelYears>& setsOfParallelYears)
477{
478 // Filter on the years
479 const auto& yearsFilter = study.parameters.yearsFilter;
480
481 // number max of years (to be executed or not) in a set of parallel years
482 uint maxNbYearsPerformed = 0;
483
484 setOfParallelYears* set = nullptr;
485 bool buildNewSet = true;
486 bool foundFirstPerformedYearOfCurrentSet = false;
487
488 // Gets information on each parallel years set
489 for (uint y = firstYear; y < endYear; ++y)
490 {
491 unsigned int indexSpace = 999999;
492 bool performCalculations = yearsFilter[y];
493
494 // Do we refresh just before this year ? If yes a new set of parallel years has to be
495 // created
496 bool refreshing = false;
497 refreshing = pData.haveToRefreshTSLoad && (y % pData.refreshIntervalLoad == 0);
498 refreshing = refreshing
499 || (pData.haveToRefreshTSSolar && (y % pData.refreshIntervalSolar == 0));
500 refreshing = refreshing
501 || (pData.haveToRefreshTSWind && (y % pData.refreshIntervalWind == 0));
502 refreshing = refreshing
503 || (pData.haveToRefreshTSHydro && (y % pData.refreshIntervalHydro == 0));
504
505 // Some thermal clusters may override the global parameter.
506 // Therefore, we may want to refresh TS even if pData.haveToRefreshTSThermal == false
507 bool haveToRefreshTSThermal = pData.haveToRefreshTSThermal
508 || study.runtime.thermalTSRefresh;
509 refreshing = refreshing
510 || (haveToRefreshTSThermal && (y % pData.refreshIntervalThermal == 0));
511
512 // We build a new set of parallel years if one of these conditions is fulfilled :
513 // - We have to refresh (or regenerate) some or all time series before running the
514 // current year
515 // - This is the first year (to be executed or not) after the previous set is full with
516 // years to be executed. That is : in the previous set filled, the max number of
517 // years to be actually run is reached.
518 buildNewSet = buildNewSet || refreshing;
519
520 if (buildNewSet)
521 {
522 setOfParallelYears setToCreate;
523 setsOfParallelYears.push_back(setToCreate);
524 set = &(setsOfParallelYears.back());
525
526 // Initializations
527 set->nbPerformedYears = 0;
528 set->nbYears = 0;
529 set->regenerateTS = false;
530 set->yearForTSgeneration = 999999;
531
532 // In case we have to regenerate times series before run the current set of parallel
533 // years
534 if (refreshing)
535 {
536 set->regenerateTS = true;
537 set->yearForTSgeneration = y;
538 }
539 }
540
541 set->yearsIndices.push_back(y);
542 set->nbYears++;
543 set->yearFailed[y] = true;
544 set->isFirstPerformedYearOfASet[y] = false;
545
546 if (performCalculations)
547 {
548 // Another year performed
549 ++pNbYearsReallyPerformed;
550
551 // Number of actually performed years in the current set (up to now).
552 set->nbPerformedYears++;
553 // Index of the MC year's space (useful if this year is actually run)
554 indexSpace = set->nbPerformedYears - 1;
555
556 set->isYearPerformed[y] = true;
557 set->performedYearToSpace[y] = indexSpace;
558 set->spaceToPerformedYear[indexSpace] = y;
559
560 if (!foundFirstPerformedYearOfCurrentSet)
561 {
562 set->isFirstPerformedYearOfASet[y] = true;
563 foundFirstPerformedYearOfCurrentSet = true;
564 }
565 }
566 else
567 {
568 set->isYearPerformed[y] = false;
569 }
570
571 // Do we build a new set at next iteration (for years to be executed or not) ?
572 if (indexSpace == pNbMaxPerformedYearsInParallel - 1 || y == endYear - 1)
573 {
574 buildNewSet = true;
575 foundFirstPerformedYearOfCurrentSet = false;
576 if (set->nbPerformedYears > maxNbYearsPerformed)
577 {
578 maxNbYearsPerformed = set->nbPerformedYears;
579 }
580 }
581 else
582 {
583 buildNewSet = false;
584 }
585
586 } // End of loop over years
587
588 return maxNbYearsPerformed;
589}
590
591template<class ImplementationType>
592void ISimulation<ImplementationType>::allocateMemoryForRandomNumbers(
593 randomNumbers& randomForParallelYears)
594{
595 uint maxNbPerformedYears = randomForParallelYears.pMaxNbPerformedYears;
596 uint nbAreas = study.areas.size();
597
598 for (uint y = 0; y < maxNbPerformedYears; y++)
599 {
600 // General :
601 randomForParallelYears.pYears[y].setNbAreas(nbAreas);
602 randomForParallelYears.pYears[y].pNbClustersByArea.resize(nbAreas);
603
604 // Thermal noises :
605 randomForParallelYears.pYears[y].pThermalNoisesByArea.resize(nbAreas);
606
607 for (uint a = 0; a != nbAreas; ++a)
608 {
609 // logs.info() << " area : " << a << " :";
610 auto& area = *(study.areas.byIndex[a]);
611 size_t nbClusters = area.thermal.list.allClustersCount();
612 randomForParallelYears.pYears[y].pThermalNoisesByArea[a].resize(nbClusters);
613 randomForParallelYears.pYears[y].pNbClustersByArea[a] = nbClusters;
614 }
615
616 // Reservoir levels
617 randomForParallelYears.pYears[y].pReservoirLevels.resize(nbAreas);
618
619 // Noises on unsupplied and spilled energy
620 randomForParallelYears.pYears[y].pUnsuppliedEnergy.resize(nbAreas);
621 randomForParallelYears.pYears[y].pSpilledEnergy.resize(nbAreas);
622
623 // Hydro costs noises
624 switch (study.parameters.power.fluctuations)
625 {
626 case Data::lssFreeModulations:
627 {
628 randomForParallelYears.pYears[y].pHydroCostsByArea_freeMod.resize(nbAreas);
629 for (uint a = 0; a != nbAreas; ++a)
630 {
631 randomForParallelYears.pYears[y].pHydroCostsByArea_freeMod[a].resize(8784);
632 }
633 break;
634 }
635 case Data::lssMinimizeRamping:
636 case Data::lssMinimizeExcursions:
637 {
638 randomForParallelYears.pYears[y].pHydroCosts_rampingOrExcursion.resize(nbAreas);
639 break;
640 }
641 case Data::lssUnknown:
642 {
643 logs.error() << "Power fluctuation unknown";
644 break;
645 }
646 }
647 } // End loop over years
648}
649
650template<class ImplementationType>
651void ISimulation<ImplementationType>::computeRandomNumbers(
652 randomNumbers& randomForYears,
653 std::vector<uint>& years,
654 std::map<unsigned int, bool>& isYearPerformed,
655 MersenneTwister& randomHydroGenerator)
656{
657 uint indexYear = 0;
658 std::vector<unsigned int>::iterator ity;
659
660 for (ity = years.begin(); ity != years.end(); ++ity)
661 {
662 uint y = *ity;
663 bool isPerformed = isYearPerformed[y];
664 if (isPerformed)
665 {
666 randomForYears.yearNumberToIndex[y] = indexYear;
667 }
668
669 // General
670 const unsigned int nbAreas = study.areas.size();
671
672 // ... Thermal noise ...
673 for (unsigned int a = 0; a != nbAreas; ++a)
674 {
675 // logs.info() << " area : " << a << " :";
676 const auto& area = *(study.areas.byIndex[a]);
677
678 for (auto& cluster: area.thermal.list.all())
679 {
680 uint clusterIndex = cluster->areaWideIndex;
681 double thermalNoise = study.runtime.random[Data::seedThermalCosts].next();
682 if (isPerformed)
683 {
684 randomForYears.pYears[indexYear].pThermalNoisesByArea[a][clusterIndex]
685 = thermalNoise;
686 }
687 }
688 }
689
690 // ... Reservoir levels ...
691 uint areaIndex = 0;
692 study.areas.each(
693 [&areaIndex, &indexYear, &randomForYears, &randomHydroGenerator, &y, &isPerformed, this](
694 Data::Area& area)
695 {
696 // looking for the initial reservoir level (begining of the year)
697 auto& min = area.hydro.reservoirLevel[Data::PartHydro::minimum];
698 auto& avg = area.hydro.reservoirLevel[Data::PartHydro::average];
699 auto& max = area.hydro.reservoirLevel[Data::PartHydro::maximum];
700
701 // Month the reservoir level is initialized according to.
702 // This month number is given in the civil calendar, from january to december (0 is
703 // january).
704 int initResLevelOnMonth = area.hydro.initializeReservoirLevelDate;
705
706 // Conversion of the previous month into simulation calendar
707 int initResLevelOnSimMonth = study.calendar.mapping.months[initResLevelOnMonth];
708
709 // Previous month's first day in the year
710 int firstDayOfMonth = study.calendar.months[initResLevelOnSimMonth].daysYear.first;
711
712 double randomLevel = randomReservoirLevel(min[firstDayOfMonth],
713 avg[firstDayOfMonth],
714 max[firstDayOfMonth],
715 randomHydroGenerator);
716
717 // Possibly update the intial level from scenario builder
718 if (study.parameters.useCustomScenario)
719 {
720 double levelFromScenarioBuilder = study.scenarioInitialHydroLevels[areaIndex][y];
721 if (levelFromScenarioBuilder >= 0.)
722 {
723 randomLevel = levelFromScenarioBuilder;
724 }
725 }
726
727 // Current area's hydro starting (or initial) level computation
728 // (no matter if the year is performed or not, we always draw a random initial
729 // reservoir level to ensure the same results)
730 if (isPerformed)
731 {
732 randomForYears.pYears[indexYear].pReservoirLevels[areaIndex] = randomLevel;
733 }
734
735 areaIndex++;
736 }); // each area
737
738 // ... Unsupplied and spilled energy costs noises (french : bruits sur la defaillance
739 // positive et negatives) ... references to the random number generators
740 auto& randomUnsupplied = study.runtime.random[Data::seedUnsuppliedEnergyCosts];
741 auto& randomSpilled = study.runtime.random[Data::seedSpilledEnergyCosts];
742
743 int currentSpilledEnergySeed = study.parameters.seed[Data::seedSpilledEnergyCosts];
744 int defaultSpilledEnergySeed = Data::antaresSeedDefaultValue
745 + Data::seedSpilledEnergyCosts * Data::antaresSeedIncrement;
746 bool SpilledEnergySeedIsDefault = (currentSpilledEnergySeed == defaultSpilledEnergySeed);
747 areaIndex = 0;
748 study.areas.each(
749 [&isPerformed,
750 &areaIndex,
751 &randomUnsupplied,
752 &randomSpilled,
753 &randomForYears,
754 &indexYear,
755 &SpilledEnergySeedIsDefault](Data::Area& area)
756 {
757 (void)area; // Avoiding warnings at compilation (unused variable) on linux
758 if (isPerformed)
759 {
760 double randomNumber = randomUnsupplied();
761 randomForYears.pYears[indexYear].pUnsuppliedEnergy[areaIndex] = randomNumber;
762 randomForYears.pYears[indexYear].pSpilledEnergy[areaIndex] = randomNumber;
763 if (!SpilledEnergySeedIsDefault)
764 {
765 randomForYears.pYears[indexYear].pSpilledEnergy[areaIndex] = randomSpilled();
766 }
767 }
768 else
769 {
770 randomUnsupplied();
771 if (!SpilledEnergySeedIsDefault)
772 {
773 randomSpilled();
774 }
775 }
776
777 areaIndex++;
778 }); // each area
779
780 // ... Hydro costs noises ...
781 auto& randomHydro = study.runtime.random[Data::seedHydroCosts];
782
783 Data::PowerFluctuations powerFluctuations = study.parameters.power.fluctuations;
784 switch (powerFluctuations)
785 {
786 case Data::lssFreeModulations:
787 {
788 areaIndex = 0;
789 auto end = study.areas.end();
790
791 // Computing hourly hydro costs noises so that they are homogeneously spread into :
792 // [-1.e-3, -5*1.e-4] U [+5*1.e-4, +1.e-3]
793 if (isPerformed)
794 {
795 for (auto i = study.areas.begin(); i != end; ++i)
796 {
797 auto& noise = randomForYears.pYears[indexYear]
798 .pHydroCostsByArea_freeMod[areaIndex];
799 std::set<hydroCostNoise, compareHydroCostsNoises> setHydroCostsNoises;
800 for (uint j = 0; j != 8784; ++j)
801 {
802 noise[j] = randomHydro();
803 noise[j] -= 0.5; // Now we have : -0.5 < noise[j] < +0.5
804
805 // This std::set naturally sorts the hydro costs noises into increasing
806 // absolute values order
807 setHydroCostsNoises.insert(hydroCostNoise(noise[j], j));
808 }
809
810 uint rank = 0;
811 std::set<hydroCostNoise, compareHydroCostsNoises>::iterator it;
812 for (it = setHydroCostsNoises.begin(); it != setHydroCostsNoises.end(); it++)
813 {
814 uint index = it->getIndex();
815 double value = it->getValue();
816
817 if (value < 0.)
818 {
819 noise[index] = -5 * 1.e-4 * (1 + rank / 8784.);
820 }
821 else
822 {
823 noise[index] = 5 * 1.e-4 * (1 + rank / 8784.);
824 }
825
826 rank++;
827 }
828
829 areaIndex++;
830 }
831 }
832 else
833 {
834 for (auto i = study.areas.begin(); i != end; ++i)
835 {
836 for (uint j = 0; j != 8784; ++j)
837 {
838 randomHydro();
839 }
840 }
841 }
842
843 break;
844 }
845
846 case Data::lssMinimizeRamping:
847 case Data::lssMinimizeExcursions:
848 {
849 areaIndex = 0;
850 auto end = study.areas.end();
851 for (auto i = study.areas.begin(); i != end; ++i)
852 {
853 if (isPerformed)
854 {
855 randomForYears.pYears[indexYear].pHydroCosts_rampingOrExcursion[areaIndex]
856 = randomHydro();
857 }
858 else
859 {
860 randomHydro();
861 }
862
863 areaIndex++;
864 }
865 break;
866 }
867
868 case Data::lssUnknown:
869 {
870 logs.error() << "Power fluctuation unknown";
871 break;
872 }
873
874 } // end of switch
875
876 if (isPerformed)
877 {
878 indexYear++;
879 }
880
881 } // End loop over years
882} // End function
883
884template<class ImplementationType>
885void ISimulation<ImplementationType>::computeAnnualCostsStatistics(
886 std::vector<Variable::State>& state,
887 setOfParallelYears& batch)
888{
889 // Loop over years contained in the set
890 for (auto y: batch.yearsIndices)
891 {
892 if (batch.isYearPerformed[y])
893 {
894 // Get space number associated to the performed year
895 uint numSpace = batch.performedYearToSpace[y];
896 const Variable::State& s = state[numSpace];
897 pAnnualStatistics.systemCost.addCost(s.annualSystemCost);
898 pAnnualStatistics.criterionCost1.addCost(s.optimalSolutionCost1);
899 pAnnualStatistics.criterionCost2.addCost(s.optimalSolutionCost2);
900 pAnnualStatistics.optimizationTime1.addCost(s.averageOptimizationTime1);
901 pAnnualStatistics.optimizationTime2.addCost(s.averageOptimizationTime2);
902 pAnnualStatistics.updateTime.addCost(s.averageUpdateTime);
903 }
904 }
905}
906
907static inline void logPerformedYearsInAset(setOfParallelYears& set)
908{
909 logs.info() << "parallel batch size : " << set.nbYears << " (" << set.nbPerformedYears
910 << " perfomed)";
911
912 std::string performedYearsToLog = "";
913
914 std::ranges::for_each(set.yearsIndices,
915 [&set, &performedYearsToLog](const uint& y)
916 {
917 if (set.isYearPerformed[y])
918 {
919 performedYearsToLog += std::to_string(y + 1) + " ";
920 }
921 });
922
923 logs.info() << "Year(s) " << performedYearsToLog;
924}
925
926template<class ImplementationType>
927void ISimulation<ImplementationType>::loopThroughYears(uint firstYear,
928 uint endYear,
929 std::vector<Variable::State>& state)
930{
931 assert(endYear <= study.parameters.nbYears);
932
933 // Init random hydro
934 MersenneTwister randomHydroGenerator;
935 randomHydroGenerator.reset(study.parameters.seed[Data::seedHydroManagement]);
936
937 // List of parallel years sets
938 std::vector<setOfParallelYears> setsOfParallelYears;
939
940 // Gets information on each set of parallel years and returns the max number of years performed
941 // in a set The variable "maxNbYearsPerformedInAset" is the maximum numbers of years to be
942 // actually executed in a set. A set contains some years to be actually executed (at most
943 // "pNbMaxPerformedYearsInParallel" years) and some others to skip.
944 uint maxNbYearsPerformedInAset = buildSetsOfParallelYears(firstYear,
945 endYear,
946 setsOfParallelYears);
947 // Related to annual costs statistics (printed in output into separate files)
948 pAnnualStatistics.setNbPerformedYears(pNbYearsReallyPerformed);
949
950 // Container for random numbers of parallel years (to be executed or not)
951 randomNumbers randomForParallelYears(maxNbYearsPerformedInAset,
952 study.parameters.power.fluctuations);
953
954 // Allocating memory to store random numbers of all parallel years
955 allocateMemoryForRandomNumbers(randomForParallelYears);
956
957 // Number of threads to perform the jobs waiting in the queue
958 pQueueService->maximumThreadCount(pNbMaxPerformedYearsInParallel);
959 HydroInputsChecker hydroInputsChecker(study);
960
961 logs.info() << " Doing hydro validation";
962
963 // Loop over sets of parallel years to check hydro inputs
964 for (const auto& batch: setsOfParallelYears)
965 {
966 if (batch.regenerateTS)
967 {
968 break;
969 }
970 for (auto year: batch.yearsIndices)
971 {
972 hydroInputsChecker.Execute(year);
973 }
974 }
975 hydroInputsChecker.CheckForErrors();
976
977 logs.info() << " Starting the simulation";
978
979 // Loop over sets of parallel years to run the simulation
980 for (auto& batch: setsOfParallelYears)
981 {
982 // 1 - We may want to regenerate the time-series this year.
983 // This is the case when the preprocessors are enabled from the
984 // interface and/or the refresh is enabled.
985 if (batch.regenerateTS)
986 {
987 regenerateTimeSeries(batch.yearForTSgeneration);
988 }
989 computeRandomNumbers(randomForParallelYears,
990 batch.yearsIndices,
991 batch.isYearPerformed,
992 randomHydroGenerator);
993
994 bool yearPerformed = false;
996 for (auto y: batch.yearsIndices)
997 {
998 // for each year not handled earlier
999 hydroInputsChecker.Execute(y);
1000 hydroInputsChecker.CheckForErrors();
1001
1002 bool performCalculations = batch.isYearPerformed[y];
1003 unsigned int numSpace = 999999;
1004 if (performCalculations)
1005 {
1006 yearPerformed = true;
1007 numSpace = batch.performedYearToSpace[y];
1008 }
1009
1010 // If the year has not to be rerun, we skip the computation of the year.
1011 // Note that, when we enter for the first time in the "for" loop, all years of the set
1012 // have to be rerun (meaning : they must be run once). if(!batch.yearFailed[y])
1013 // continue;
1014
1015 auto task = std::make_shared<yearJob<ImplementationType>>(
1016 this,
1017 y,
1018 batch.yearFailed,
1019 batch.isFirstPerformedYearOfASet,
1020 pFirstSetParallelWithAPerformedYearWasRun,
1021 numSpace,
1022 randomForParallelYears,
1023 performCalculations,
1024 study,
1025 state,
1026 pYearByYear,
1027 pDurationCollector,
1028 pResultWriter,
1029 simulationObserver_.get());
1030 results.add(Concurrency::AddTask(*pQueueService, task));
1031 } // End loop over years of the current set of parallel years
1032
1033 logPerformedYearsInAset(batch);
1034
1035 pQueueService->start();
1036
1037 pQueueService->wait(Yuni::qseIdle);
1038 pQueueService->stop();
1039 results.join();
1040 pResultWriter.flush();
1041
1042 // At this point, the first set of parallel year(s) was run with at least one year
1043 // performed
1044 if (!pFirstSetParallelWithAPerformedYearWasRun && yearPerformed)
1045 {
1046 pFirstSetParallelWithAPerformedYearWasRun = true;
1047 }
1048
1049 // On regarde si au moins une année du lot n'a pas trouvé de solution
1050 for (auto& [year, failed]: batch.yearFailed)
1051 {
1052 // Si une année du lot d'années n'a pas trouvé de solution, on arrête tout
1053 if (failed)
1054 {
1055 std::ostringstream msg;
1056 msg << "Year " << year + 1 << " has failed in the previous set of parallel year.";
1057 throw FatalError(msg.str());
1058 }
1059 }
1060 // Computing the summary : adding the contribution of MC years
1061 // previously computed in parallel
1062 ImplementationType::variables.computeSummary(batch.spaceToPerformedYear,
1063 batch.nbPerformedYears);
1064
1065 // Computing summary of spatial aggregations
1066 ImplementationType::variables.computeSpatialAggregatesSummary(ImplementationType::variables,
1067 batch.spaceToPerformedYear,
1068 batch.nbPerformedYears);
1069
1070 // Computes statistics on annual (system and solution) costs, to be printed in output into
1071 // separate files
1072 computeAnnualCostsStatistics(state, batch);
1073
1074 // Set to zero the random numbers of all parallel years
1075 randomForParallelYears.reset();
1076
1077 } // End loop over sets of parallel years
1078
1079 // Writing annual costs statistics
1080 pAnnualStatistics.endStandardDeviations();
1081 pAnnualStatistics.writeToOutput(pResultWriter);
1082}
1083
1084} // namespace Antares::Solver::Simulation
1085
1086#endif // __SOLVER_SIMULATION_SOLVER_HXX__
Utility class to gather futures to wait for.
Definition concurrency.h:61
void add(TaskFuture &&f)
Adds one future to be monitored by this set.
Definition concurrency.cpp:71
void join()
Waits for completion of all added futures.
Definition concurrency.cpp:77
Definition for a single area.
Definition area.h:52
@ maximum
The maximum value.
Definition container.h:100
@ average
The average value.
Definition container.h:98
@ minimum
The minimum value.
Definition container.h:96
Definition study.h:61
A generic exception for errors that should end the program.
Definition fatal-error.h:32
Definition HydroInputsChecker.h:33
Definition management.h:62
void makeVentilation(double *randomReservoirLevel, uint y, Antares::Data::Area::ScratchMap &scratchmap)
Perform the hydro ventilation.
Definition management.cpp:269
MersenneTwister Pseudo random number generator.
Definition mersenne-twister.h:41
void reset()
Reset the generator.
Definition mersenne-twister.cpp:126
Definition i_writer.h:34
Definition InitializationError.hpp:29
Definition progression.h:91
The ISimulationObserver class is an interface for observing the simulation.
Definition ISimulationObserver.h:36
const ::Settings & settings
The global settings.
Definition solver.h:83
void writeResults(bool synthesis, uint year=0, uint numSpace=9999)
Export the results to disk.
Definition solver.hxx:357
ISimulation(Data::Study &study, const ::Settings &settings, Benchmarking::DurationCollector &duration_collector, IResultWriter &resultWriter, Simulation::ISimulationObserver &simulationObserver)
Constructor (with a given study)
Definition solver.hxx:222
void run()
Run the simulation.
Definition solver.hxx:270
~ISimulation()
Destructor.
Definition solver.hxx:265
Definition solver_utils.h:193
Definition solver.hxx:43
Definition DurationCollector.h:36
Definition opt_time_writer.h:30
bool tsGeneratorsOnly
Run the TS generator only.
Definition options.h:58
bool noOutput
True to disable the writing in the output folder.
Definition options.h:61