diff --git a/src/libs/antares/CMakeLists.txt b/src/libs/antares/CMakeLists.txt index 3421188970..7549a701e3 100644 --- a/src/libs/antares/CMakeLists.txt +++ b/src/libs/antares/CMakeLists.txt @@ -447,6 +447,8 @@ set(SRC study.h study/estimate.memory-footprint.cpp study/study.cpp + study/parallel-years.cpp + study/parallel-years.h study/correlation-updater.hxx study/study.importprepro.cpp study/memory-usage.h diff --git a/src/libs/antares/study/load.cpp b/src/libs/antares/study/load.cpp index 2409173268..7c3dc4b96e 100644 --- a/src/libs/antares/study/load.cpp +++ b/src/libs/antares/study/load.cpp @@ -148,15 +148,8 @@ bool Study::internalLoadFromFolder(const String& path, const StudyLoadOptions& o // ------------------------- // Getting the number of logical cores to use before loading and creating the areas : // Areas need this number to be up-to-date at construction. - getNumberOfCores(options.forceParallel, options.maxNbYearsInParallel); - // In case the study is run in the draft mode, only 1 core is allowed - if (parameters.mode == Data::stdmAdequacyDraft) - maxNbYearsInParallel = 1; - - // In case parallel mode was not chosen, only 1 core is allowed - if (!options.enableParallel && !options.forceParallel) - maxNbYearsInParallel = 1; + getNumberOfCores(options.forceParallel, options.enableParallel, options.maxNbYearsInParallel); // End logical core -------- diff --git a/src/libs/antares/study/parallel-years.cpp b/src/libs/antares/study/parallel-years.cpp new file mode 100644 index 0000000000..11cd0ba1f8 --- /dev/null +++ b/src/libs/antares/study/parallel-years.cpp @@ -0,0 +1,429 @@ +/* +** Copyright 2007-2022 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 +#include +#include +#include + +#include // For use of Yuni::System::CPU::Count() +#include +#include +#include + +#include +#include +#include "parameters.h" +#include "parallel-years.h" + +#define SEP Yuni::IO::Separator + +namespace Antares +{ + +/*! +** \brief Checks if clusters have the "gen-ts" param +** +** \warning TODO: REMOVE THIS METHOD ONCE THE TSGEN HAS BEEN DECOUPLED FROM SOLVER +*/ + +bool TempAreaListHolder::checkThermalTSGeneration(YString folder_) +{ + inputFolder = std::move(folder_); + loadAreaList(); //Build areaNames + + return std::any_of(areaNames.begin(), areaNames.end(),[this](const Yuni::String& areaName){ + auto folder = inputFolder; + Yuni::Clob thermalPlant = folder << SEP << "thermal" << SEP << "clusters" << SEP << areaName << SEP << "list.ini"; + IniFile ini; + bool ret = false; + if (ini.open(thermalPlant)) + { + if(ini.firstSection) + { + for (IniFile::Section* section = ini.firstSection; section; section = section->next) + { + for (IniFile::Property* property = section->firstProperty; property; property = property->next) + { + ret = ret || (property->key == "gen-ts"); + } + } + } + return ret; + } + logs.error() << "Thermal Cluster Ini file cannot be opened: " << thermalPlant.c_str(); + return false; + } + ); +} + +/*! +** \brief Loads Area List to later check Thermal Cluster's refresh +** +** \warning TODO: REMOVE THIS METHOD ONCE THE TSGEN HAS BEEN DECOUPLED FROM SOLVER +*/ + +void TempAreaListHolder::loadAreaList() +{ + auto folder = inputFolder; + Yuni::Clob filename = folder << SEP << "areas" << SEP << "list.txt"; + + Yuni::IO::File::Stream file; + Yuni::String buffer; + buffer.reserve(1024 /* to force the allocation */); + + if (!file.open(filename)) + { + logs.error() << "I/O error: " << filename << ": Impossible to open the file"; + return; + } + while(file.readline(buffer)) + { + // The area name + Yuni::String name; + Yuni::String lname; + name = buffer; + name.trim(" \t\n\r"); + TransformNameIntoID(name, lname); + areaNames.push_back(lname); + } + +} + + +void SetsOfParallelYearCalculator::computeRawNbParallelYear() +{ + // In case solver option '--force-parallel n' is used, this computation is not needed + // and n will remain the forcedNbOfParallelYears + if (forceParallel_) + return; + + std::map numberOfMCYearThreads; + const uint nbLogicalCores = number_of_cores_; + + numberOfMCYearThreads[ncMin] = 1; + switch (nbLogicalCores) + { + case 0: + logs.fatal() << "Number of logical cores available is 0."; + break; + case 1: + numberOfMCYearThreads[ncLow] = 1; + numberOfMCYearThreads[ncAvg] = 1; + numberOfMCYearThreads[ncHigh] = 1; + numberOfMCYearThreads[ncMax] = 1; + break; + case 2: + numberOfMCYearThreads[ncLow] = 1; + numberOfMCYearThreads[ncAvg] = 1; + numberOfMCYearThreads[ncHigh] = 2; + numberOfMCYearThreads[ncMax] = 2; + break; + case 3: + numberOfMCYearThreads[ncLow] = 2; + numberOfMCYearThreads[ncAvg] = 2; + numberOfMCYearThreads[ncHigh] = 2; + numberOfMCYearThreads[ncMax] = 3; + break; + case 4: + numberOfMCYearThreads[ncLow] = 2; + numberOfMCYearThreads[ncAvg] = 2; + numberOfMCYearThreads[ncHigh] = 3; + numberOfMCYearThreads[ncMax] = 4; + break; + case 5: + numberOfMCYearThreads[ncLow] = 2; + numberOfMCYearThreads[ncAvg] = 3; + numberOfMCYearThreads[ncHigh] = 4; + numberOfMCYearThreads[ncMax] = 5; + break; + case 6: + numberOfMCYearThreads[ncLow] = 2; + numberOfMCYearThreads[ncAvg] = 3; + numberOfMCYearThreads[ncHigh] = 4; + numberOfMCYearThreads[ncMax] = 6; + break; + case 7: + numberOfMCYearThreads[ncLow] = 2; + numberOfMCYearThreads[ncAvg] = 3; + numberOfMCYearThreads[ncHigh] = 5; + numberOfMCYearThreads[ncMax] = 7; + break; + case 8: + numberOfMCYearThreads[ncLow] = 2; + numberOfMCYearThreads[ncAvg] = 4; + numberOfMCYearThreads[ncHigh] = 6; + numberOfMCYearThreads[ncMax] = 8; + break; + case 9: + numberOfMCYearThreads[ncLow] = 3; + numberOfMCYearThreads[ncAvg] = 5; + numberOfMCYearThreads[ncHigh] = 7; + numberOfMCYearThreads[ncMax] = 8; + break; + case 10: + numberOfMCYearThreads[ncLow] = 3; + numberOfMCYearThreads[ncAvg] = 5; + numberOfMCYearThreads[ncHigh] = 8; + numberOfMCYearThreads[ncMax] = 9; + break; + case 11: + numberOfMCYearThreads[ncLow] = 3; + numberOfMCYearThreads[ncAvg] = 6; + numberOfMCYearThreads[ncHigh] = 8; + numberOfMCYearThreads[ncMax] = 10; + break; + case 12: + numberOfMCYearThreads[ncLow] = 3; + numberOfMCYearThreads[ncAvg] = 6; + numberOfMCYearThreads[ncHigh] = 9; + numberOfMCYearThreads[ncMax] = 11; + break; + default: + numberOfMCYearThreads[ncLow] = (uint)std::ceil(nbLogicalCores / 4.); + numberOfMCYearThreads[ncAvg] = (uint)std::ceil(nbLogicalCores / 2.); + numberOfMCYearThreads[ncHigh] = (uint)std::ceil(3 * nbLogicalCores / 4.); + numberOfMCYearThreads[ncMax] = nbLogicalCores - 1; + break; + } + + /* + Getting the number of parallel years based on the number + of cores level. + This number is limited by the smallest refresh span (if at least + one type of time series is generated) + */ + + try + { + rawNbOfParallelYears_ = numberOfMCYearThreads.at(p.nbCores.ncMode); + } + catch(const std::out_of_range& e) + { + logs.fatal() << "Simulation cores level not correct : " << static_cast(p.nbCores.ncMode); + } +} + + +void SetsOfParallelYearCalculator::limitNbOfParallelYearsbyMinRefreshSpan() +{ + uint TSlimit = UINT_MAX; + if ((p.timeSeriesToGenerate & timeSeriesLoad) && (p.timeSeriesToRefresh & timeSeriesLoad)) + TSlimit = p.refreshIntervalLoad; + if ((p.timeSeriesToGenerate & timeSeriesSolar) && (p.timeSeriesToRefresh & timeSeriesSolar)) + TSlimit = std::min(p.refreshIntervalSolar, TSlimit); + if ((p.timeSeriesToGenerate & timeSeriesHydro) && (p.timeSeriesToRefresh & timeSeriesHydro)) + TSlimit = std::min(p.refreshIntervalHydro, TSlimit); + if ((p.timeSeriesToGenerate & timeSeriesWind) && (p.timeSeriesToRefresh & timeSeriesWind)) + TSlimit = std::min(p.refreshIntervalWind, TSlimit); + if ((p.timeSeriesToGenerate & timeSeriesThermal) && (p.timeSeriesToRefresh & timeSeriesThermal)) + TSlimit = std::min(p.refreshIntervalThermal, TSlimit); + + forcedNbOfParallelYears_ = std::min({p.nbYears, TSlimit, rawNbOfParallelYears_}); +} + +bool SetsOfParallelYearCalculator::isRefreshNeededForCurrentYear(uint y) +{ + bool refreshing = false; + refreshing = (p.timeSeriesToGenerate & timeSeriesLoad) + && (p.timeSeriesToRefresh & timeSeriesLoad) + && ((y % p.refreshIntervalLoad) == 0); + refreshing = refreshing + || ((p.timeSeriesToGenerate & timeSeriesSolar) + && (p.timeSeriesToRefresh & timeSeriesSolar) + && (y % p.refreshIntervalSolar) == 0); + refreshing = refreshing + || ((p.timeSeriesToGenerate & timeSeriesWind) + && (p.timeSeriesToRefresh & timeSeriesWind) + && (y % p.refreshIntervalWind) == 0); + refreshing = refreshing + || ((p.timeSeriesToGenerate & timeSeriesHydro) + && (p.timeSeriesToRefresh & timeSeriesHydro) + && (y % p.refreshIntervalHydro) == 0); + + bool haveToRefreshTSThermal + = ((p.timeSeriesToGenerate & timeSeriesThermal) + && (p.timeSeriesToRefresh & timeSeriesThermal)) || thermalTSRefresh_; + refreshing + = refreshing || (haveToRefreshTSThermal && (y % p.refreshIntervalThermal == 0)); + + return refreshing; + +} + +void SetsOfParallelYearCalculator::buildSetsOfParallelYears() +{ + setOfParallelYears* set = nullptr; + bool buildNewSet = true; + bool foundFirstPerformedYearOfCurrentSet = false; + // Gets information on each parallel years set + for (uint y = 0; y < p.nbYears; ++y) + { + unsigned int indexSpace = 999999; + bool performCalculations = true; + + if(p.userPlaylist) + performCalculations = p.yearsFilter[y]; + + bool refreshing = isRefreshNeededForCurrentYear(y); + buildNewSet = buildNewSet || refreshing; + + if (buildNewSet) + { + setOfParallelYears setToCreate; + setsOfParallelYears.push_back(setToCreate); + set = &(setsOfParallelYears.back()); + + // Initializations + set->nbPerformedYears = 0; + set->nbYears = 0; + set->regenerateTS = false; + set->yearForTSgeneration = 999999; + + // In case we have to regenerate times series before run the current set of parallel + // years + if (refreshing) + { + set->regenerateTS = true; + set->yearForTSgeneration = y; + } + } + + set->yearsIndices.push_back(y); + set->nbYears++; + set->yearFailed[y] = true; + set->isFirstPerformedYearOfASet[y] = false; + + if (performCalculations) + { + set->setsSizes++; + // Another year performed + ++nbYearsReallyPerformed; + + // Number of actually performed years in the current set (up to now). + set->nbPerformedYears++; + // Index of the MC year's space (useful if this year is actually run) + indexSpace = set->nbPerformedYears - 1; + + set->isYearPerformed[y] = true; + set->performedYearToSpace[y] = indexSpace; + set->spaceToPerformedYear[indexSpace] = y; + + if (!foundFirstPerformedYearOfCurrentSet) + { + set->isFirstPerformedYearOfASet[y] = true; + foundFirstPerformedYearOfCurrentSet = true; + } + } + else + { + set->isYearPerformed[y] = false; + } + + // Do we build a new set at next iteration (for years to be executed or not) ? + + // In case the study is run in the draft mode, only 1 core is allowed + if (p.mode == Antares::Data::stdmAdequacyDraft){ + forcedNbOfParallelYears_ = 1; + } + + // In case parallel mode was not chosen, only 1 core is allowed + if (!enableParallel_ && !forceParallel_){ + forcedNbOfParallelYears_ = 1; + } + + if (indexSpace == forcedNbOfParallelYears_ - 1 || y == p.nbYears - 1) + { + buildNewSet = true; + foundFirstPerformedYearOfCurrentSet = false; + if (set->nbPerformedYears > forcedNbOfParallelYears_) + forcedNbOfParallelYears_ = set->nbPerformedYears; + } + else + buildNewSet = false; + + // End of loop over years + + } +} + + +bool SetsOfParallelYearCalculator::allSetsParallelYearsHaveSameSize() +{ + if (p.initialReservoirLevels.iniLevels == Antares::Data::irlHotStart + && !setsOfParallelYears.empty() && forcedNbOfParallelYears_ > 1) + { + uint currentSetSize = (uint)setsOfParallelYears[0].setsSizes; + return all_of(setsOfParallelYears.begin(), setsOfParallelYears.end(), + [currentSetSize](const setOfParallelYears& v){ return v.setsSizes == currentSetSize; } ); + } // End if hot start + return true; + // parameters.allSetsHaveSameSize takes this result; +} + +uint SetsOfParallelYearCalculator::computeMinNbParallelYears() const +{ + // Now finding the smallest size among all sets. + uint minNbYearsInParallel = forcedNbOfParallelYears_; + for (uint s = 0; s < setsOfParallelYears.size(); s++) + { + uint setSize = (uint)setsOfParallelYears[s].setsSizes; + // Empty sets are not taken into account because, on the solver side, + // they will contain only skipped years + if (setSize && (setSize < minNbYearsInParallel)) + minNbYearsInParallel = setSize; + } + return minNbYearsInParallel; +} + +void SetsOfParallelYearCalculator::computeForcedNbYearsInParallelYearSet() +{ + + uint maxNbYearsOverAllSets = 0; + for (uint s = 0; s < setsOfParallelYears.size(); s++) + { + if (setsOfParallelYears[s].setsSizes > maxNbYearsOverAllSets) + maxNbYearsOverAllSets = (uint)setsOfParallelYears[s].setsSizes; + } + + forcedNbOfParallelYears_ = maxNbYearsOverAllSets; + +} + +void SetsOfParallelYearCalculator::build() +{ + + computeRawNbParallelYear(); + limitNbOfParallelYearsbyMinRefreshSpan(); + + buildSetsOfParallelYears(); + + computeForcedNbYearsInParallelYearSet(); + +} + +} // namespace Antares diff --git a/src/libs/antares/study/parallel-years.h b/src/libs/antares/study/parallel-years.h new file mode 100644 index 0000000000..b81c5e0de0 --- /dev/null +++ b/src/libs/antares/study/parallel-years.h @@ -0,0 +1,171 @@ +/* +** Copyright 2007-2022 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 + +namespace Antares +{ + +using namespace Data; + +struct setOfParallelYears +{ + // Un lot d'année à exécuter en parallèle. + // En fonction d'une éventuelle play-list, certaines seront jouées et d'autres non. + + unsigned int setsSizes = 0; + // Numeros des annees en parallele pour ce lot (certaines ne seront pas jouées en cas de + // play-list "trouée") + std::vector yearsIndices; + + // Une annee doit-elle être rejouée ? + std::map yearFailed; + + // Associe le numero d'une année jouée à l'indice de l'espace + std::map performedYearToSpace; + + // L'inverse : pour une année jouée, associe l'indice de l'espace au numero de l'année + std::map spaceToPerformedYear; + + // Pour chaque année, est-elle la première à devoir être jouée dans son lot d'années ? + std::map isFirstPerformedYearOfASet; + + // Pour chaque année du lot, est-elle jouée ou non ? + std::map isYearPerformed; + + // Nbre d'années en parallele vraiment jouées pour ce lot + unsigned int nbPerformedYears; + + // Nbre d'années en parallele jouées ou non pour ce lot + unsigned int nbYears; + + // Regenere-t-on des times series avant de jouer les annees du lot courant + bool regenerateTS; + + // Annee a passer a la fonction "regenerateTimeSeries(y)" (si regenerateTS is "true") + unsigned int yearForTSgeneration; +}; + +/*! +** \brief Temporary Area List Holder +** +** \warning TODO: REMOVE THIS CLASS ONCE THE TSGEN HAS BEEN DECOUPLED FROM SOLVER +** +** To compute the sets of parallel years while using the Time Series Generator +** we need to know in advance if the area's thermal clusters will be refresehd during generation +** Although, since the max number of parallel years is needed to create the areas, this info +** is not available yet (and tht's why, originally, this calculation was done twice) +** +** This temporary holder will load the area list and will check for each one if +** their clusters will require refreshing during generation +*/ +class TempAreaListHolder +{ +public: + TempAreaListHolder() = default; + + bool checkThermalTSGeneration(YString folder_); + +private: + void loadAreaList(); + YString inputFolder; + std::vector areaNames; + +}; + +class SetsOfParallelYearCalculator +{ +public: + + SetsOfParallelYearCalculator(bool forceParallel, bool enableParallel, uint forcedNbOfParallelYears, + uint number_of_cores, bool thermalTSRefresh, Parameters ¶ms) + : forceParallel_{forceParallel}, + enableParallel_ {enableParallel}, + forcedNbOfParallelYears_{forcedNbOfParallelYears}, + number_of_cores_{number_of_cores}, + thermalTSRefresh_{thermalTSRefresh}, + p{params}{ + this->build(); + } + + + bool allSetsParallelYearsHaveSameSize(); + + [[nodiscard]] uint getForcedNbOfParallelYears() const + { + return forcedNbOfParallelYears_; + } + + [[nodiscard]] uint getRawNbParallelYearsForGUI() const + { + return rawNbOfParallelYears_; + } + + [[nodiscard]] uint getMinNbParallelYearsForGUI() const + { + return computeMinNbParallelYears(); + } + + [[nodiscard]] std::vector getSetsOfParallelYears() const + { + return setsOfParallelYears; + } + + [[nodiscard]] uint getNbYearsReallyPerformed() const + { + return nbYearsReallyPerformed; + } + +private: + + void build(); + + void computeRawNbParallelYear(); + void limitNbOfParallelYearsbyMinRefreshSpan(); + + void buildSetsOfParallelYears(); + + [[nodiscard]] uint computeMinNbParallelYears() const; + void computeForcedNbYearsInParallelYearSet(); + + bool isRefreshNeededForCurrentYear(uint y); + + std::vector setsOfParallelYears; + + bool forceParallel_; + bool enableParallel_; + uint forcedNbOfParallelYears_; + uint rawNbOfParallelYears_; + uint number_of_cores_; + bool thermalTSRefresh_; + Parameters& p; + uint nbYearsReallyPerformed{0}; + +}; + +} // namespace Anatares \ No newline at end of file diff --git a/src/libs/antares/study/study.cpp b/src/libs/antares/study/study.cpp index 29184733e7..a53d8c8560 100644 --- a/src/libs/antares/study/study.cpp +++ b/src/libs/antares/study/study.cpp @@ -224,284 +224,27 @@ uint64 Study::memoryUsage() const + (uiinfo ? uiinfo->memoryUsage() : 0); } -std::map Study::getRawNumberCoresPerLevel() -{ - std::map table; - - uint nbLogicalCores = Yuni::System::CPU::Count(); - if (!nbLogicalCores) - logs.fatal() << "Number of logical cores available is 0."; - - switch (nbLogicalCores) - { - case 1: - table["min"] = 1; - table["low"] = 1; - table["med"] = 1; - table["high"] = 1; - table["max"] = 1; - break; - case 2: - table["min"] = 1; - table["low"] = 1; - table["med"] = 1; - table["high"] = 2; - table["max"] = 2; - break; - case 3: - table["min"] = 1; - table["low"] = 2; - table["med"] = 2; - table["high"] = 2; - table["max"] = 3; - break; - case 4: - table["min"] = 1; - table["low"] = 2; - table["med"] = 2; - table["high"] = 3; - table["max"] = 4; - break; - case 5: - table["min"] = 1; - table["low"] = 2; - table["med"] = 3; - table["high"] = 4; - table["max"] = 5; - break; - case 6: - table["min"] = 1; - table["low"] = 2; - table["med"] = 3; - table["high"] = 4; - table["max"] = 6; - break; - case 7: - table["min"] = 1; - table["low"] = 2; - table["med"] = 3; - table["high"] = 5; - table["max"] = 7; - break; - case 8: - table["min"] = 1; - table["low"] = 2; - table["med"] = 4; - table["high"] = 6; - table["max"] = 8; - break; - case 9: - table["min"] = 1; - table["low"] = 3; - table["med"] = 5; - table["high"] = 7; - table["max"] = 8; - break; - case 10: - table["min"] = 1; - table["low"] = 3; - table["med"] = 5; - table["high"] = 8; - table["max"] = 9; - break; - case 11: - table["min"] = 1; - table["low"] = 3; - table["med"] = 6; - table["high"] = 8; - table["max"] = 10; - break; - case 12: - table["min"] = 1; - table["low"] = 3; - table["med"] = 6; - table["high"] = 9; - table["max"] = 11; - break; - default: - table["min"] = 1; - table["low"] = (uint)std::ceil(nbLogicalCores / 4.); - table["med"] = (uint)std::ceil(nbLogicalCores / 2.); - table["high"] = (uint)std::ceil(3 * nbLogicalCores / 4.); - table["max"] = nbLogicalCores - 1; - break; - } - - return table; -} - -void Study::getNumberOfCores(const bool forceParallel, const uint nbYearsParallelForced) -{ - /* - Getting the number of parallel years based on the number - of cores level. - This number is limited by the smallest refresh span (if at least - one type of time series is generated) - */ - - std::map table = getRawNumberCoresPerLevel(); - - // Getting the number of parallel years based on the number of cores level. - switch (parameters.nbCores.ncMode) - { - case ncMin: - nbYearsParallelRaw = table["min"]; - break; - case ncLow: - nbYearsParallelRaw = table["low"]; - break; - case ncAvg: - nbYearsParallelRaw = table["med"]; - break; - case ncHigh: - nbYearsParallelRaw = table["high"]; - break; - case ncMax: - nbYearsParallelRaw = table["max"]; - break; - default: - logs.fatal() << "Simulation cores level not correct : " << (int)parameters.nbCores.ncMode; - break; - } - - maxNbYearsInParallel = nbYearsParallelRaw; - - // In case solver option '--force-parallel n' is used, previous computation is overridden. - if (forceParallel) - maxNbYearsInParallel = nbYearsParallelForced; - - // Limiting the number of parallel years by the smallest refresh span - auto& p = parameters; - uint TSlimit = UINT_MAX; - if ((p.timeSeriesToGenerate & timeSeriesLoad) && (p.timeSeriesToRefresh & timeSeriesLoad)) - TSlimit = p.refreshIntervalLoad; - if ((p.timeSeriesToGenerate & timeSeriesSolar) && (p.timeSeriesToRefresh & timeSeriesSolar)) - TSlimit = (p.refreshIntervalSolar < TSlimit) ? p.refreshIntervalSolar : TSlimit; - if ((p.timeSeriesToGenerate & timeSeriesHydro) && (p.timeSeriesToRefresh & timeSeriesHydro)) - TSlimit = (p.refreshIntervalHydro < TSlimit) ? p.refreshIntervalHydro : TSlimit; - if ((p.timeSeriesToGenerate & timeSeriesWind) && (p.timeSeriesToRefresh & timeSeriesWind)) - TSlimit = (p.refreshIntervalWind < TSlimit) ? p.refreshIntervalWind : TSlimit; - if ((p.timeSeriesToGenerate & timeSeriesThermal) && (p.timeSeriesToRefresh & timeSeriesThermal)) - TSlimit = (p.refreshIntervalThermal < TSlimit) ? p.refreshIntervalThermal : TSlimit; - - if (TSlimit < maxNbYearsInParallel) - maxNbYearsInParallel = TSlimit; - - // Limiting the number of parallel years by the total number of years - if (p.nbYears < maxNbYearsInParallel) - maxNbYearsInParallel = p.nbYears; - - // Getting the minimum number of years in a set of parallel years. - // To get this number, we have to divide all years into sets of parallel - // years and pick the size of the smallest set. - - std::vector* set = nullptr; - bool buildNewSet = true; - std::vector> setsOfParallelYears; - - for (uint y = 0; y < p.nbYears; ++y) - { - bool performCalculations = true; - if (p.userPlaylist) - performCalculations = p.yearsFilter[y]; - - // Do we have to refresh ? - bool refreshing = false; - refreshing = (p.timeSeriesToGenerate & timeSeriesLoad) - && (p.timeSeriesToRefresh & timeSeriesLoad) - && (!y || ((y % p.refreshIntervalLoad) == 0)); - refreshing = refreshing - || ((p.timeSeriesToGenerate & timeSeriesSolar) - && (p.timeSeriesToRefresh & timeSeriesSolar) - && (!y || ((y % p.refreshIntervalSolar) == 0))); - refreshing = refreshing - || ((p.timeSeriesToGenerate & timeSeriesWind) - && (p.timeSeriesToRefresh & timeSeriesWind) - && (!y || ((y % p.refreshIntervalWind) == 0))); - refreshing = refreshing - || ((p.timeSeriesToGenerate & timeSeriesHydro) - && (p.timeSeriesToRefresh & timeSeriesHydro) - && (!y || ((y % p.refreshIntervalHydro) == 0))); - refreshing = refreshing - || ((p.timeSeriesToGenerate & timeSeriesThermal) - && (p.timeSeriesToRefresh & timeSeriesThermal) - && (!y || ((y % p.refreshIntervalThermal) == 0))); - - buildNewSet = buildNewSet || refreshing; - - // We build a new set of parallel years if one of these conditions is fulfilled : - // - We have to refresh (or regenerate) some or all time series before running the - // current year - // - This is the first year after the previous set is full with years to be actually - // executed (not skipped). That is : in the previous set filled, the max number of - // years to be actually run is reached. - if (buildNewSet) - { - std::vector setToCreate; - setsOfParallelYears.push_back(setToCreate); - set = &(setsOfParallelYears.back()); - } - - if (performCalculations) - set->push_back(y); - - // Do we build a new set at next iteration (for years to be executed or not) ? - if (set->size() == maxNbYearsInParallel) - buildNewSet = true; - else - buildNewSet = false; - } // End of loop over years - - // Now finding the smallest size among all sets. - minNbYearsInParallel = maxNbYearsInParallel; - for (uint s = 0; s < setsOfParallelYears.size(); s++) - { - uint setSize = (uint)setsOfParallelYears[s].size(); - // Empty sets are not taken into account because, on the solver side, - // they will contain only skipped years - if (setSize && (setSize < minNbYearsInParallel)) - minNbYearsInParallel = setSize; - } - - // GUI : storing minimum number of parallel years (in a set of parallel years). - // Useful in the run window's simulation cores field in case parallel mode is enabled - // by user. - minNbYearsInParallel_save = minNbYearsInParallel; - - // The max number of years to run in parallel is limited by the max number years in a set of - // parallel years. This latter number can be limited by the smallest interval between 2 refresh - // points and determined by the unrun MC years in case of play-list. - uint maxNbYearsOverAllSets = 0; - for (uint s = 0; s < setsOfParallelYears.size(); s++) - { - if (setsOfParallelYears[s].size() > maxNbYearsOverAllSets) - maxNbYearsOverAllSets = (uint)setsOfParallelYears[s].size(); - } - maxNbYearsInParallel = maxNbYearsOverAllSets; - - // GUI : storing max nb of parallel years (in a set of parallel years) in case parallel mode is - // 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 +void Study::getNumberOfCores(const bool forceParallel, const bool enableParallel, const uint nbYearsParallelForced) { + + TempAreaListHolder holder; + bool thermalTSRefresh = holder.checkThermalTSGeneration(folderInput); + + SetsOfParallelYearCalculator setsBuilder(forceParallel, + enableParallel, + nbYearsParallelForced, + Yuni::System::CPU::Count(), + thermalTSRefresh, + parameters); + // For GUI + minNbYearsInParallel_save = setsBuilder.getMinNbParallelYearsForGUI(); + nbYearsParallelRaw = setsBuilder.getRawNbParallelYearsForGUI(); + maxNbYearsInParallel_save = setsBuilder.getForcedNbOfParallelYears(); + + // For the solver + maxNbYearsInParallel = setsBuilder.getForcedNbOfParallelYears(); + parameters.allSetsHaveSameSize = setsBuilder.allSetsParallelYearsHaveSameSize(); + setsOfParallelYears = setsBuilder.getSetsOfParallelYears(); + pNbYearsReallyPerformed = setsBuilder.getNbYearsReallyPerformed(); } bool Study::checkHydroHotStart() diff --git a/src/libs/antares/study/study.h b/src/libs/antares/study/study.h index ea81886ff0..a3594f7e3c 100644 --- a/src/libs/antares/study/study.h +++ b/src/libs/antares/study/study.h @@ -51,6 +51,7 @@ #include "load-options.h" #include "../date.h" #include "layerdata.h" +#include "parallel-years.h" #include @@ -442,21 +443,12 @@ class Study final : public Yuni::NonCopyable, public IObject, public Laye //@{ /*! - ** \brief Computes a raw number of cores table. - ** - ** The table associetes a raw number of cores to each level ("min", "low", "med", "high", - *"max"). - ** - */ - std::map getRawNumberCoresPerLevel(); - - /*! - ** \brief Computes number of cores + ** \brief Gets the number of cores calculated in SetsOfParallelYearCalculator class ** ** From the "Number of Cores" level (in GUI --> Advanced parameters), computes ** the real numbers of logical cores to be involved in the MC years parallelisation. */ - void getNumberOfCores(const bool forceParallel, const uint nbYearsParallelForced); + void getNumberOfCores(const bool forceParallel, const bool enableParallel, const uint nbYearsParallelForced); /*! ** \brief In case hydro hot start is enabled, checking all conditions are met. @@ -619,6 +611,13 @@ class Study final : public Yuni::NonCopyable, public IObject, public Laye // Useful to populate the run window's simulation cores field. uint minNbYearsInParallel_save; + // Used in solver + // ----------------- + // Stores the sets of parallel years to be used by the solver + std::vector setsOfParallelYears; + + uint pNbYearsReallyPerformed; + //! Parameters Parameters parameters; diff --git a/src/solver/simulation/solver.h b/src/solver/simulation/solver.h index a394bcee98..47058e1bfe 100644 --- a/src/solver/simulation/solver.h +++ b/src/solver/simulation/solver.h @@ -158,7 +158,7 @@ class ISimulation : public Impl ** \param firstYear The first real MC year ** \param endYear The last MC year */ - void loopThroughYears(uint firstYear, uint endYear, std::vector& state); + void loopThroughYears(uint endYear, std::vector& state); private: //! Some temporary to avoid performing useless complex checks diff --git a/src/solver/simulation/solver.hxx b/src/solver/simulation/solver.hxx index 62a3566d52..d22c5a438b 100644 --- a/src/solver/simulation/solver.hxx +++ b/src/solver/simulation/solver.hxx @@ -39,6 +39,7 @@ #include "apply-scenario.h" #include #include "../ts-generator/generator.h" +#include "../../libs/antares/study/parallel-years.h" #include "../hydro/management.h" // Added for use of randomReservoirLevel(...) @@ -284,6 +285,7 @@ template void ISimulation::run() { pNbMaxPerformedYearsInParallel = study.maxNbYearsInParallel; + pNbYearsReallyPerformed = study.pNbYearsReallyPerformed; // Initialize all data ImplementationType::variables.initializeFromStudy(study); @@ -362,10 +364,10 @@ void ISimulation::run() ImplementationType::initializeState(state[numSpace], numSpace); logs.info() << " Starting the simulation"; - uint finalYear = 1 + study.runtime->rangeLimits.year[Data::rangeEnd]; { Benchmarking::Timer timer; - loopThroughYears(0, finalYear, state); + uint finalYear = 1 + study.runtime->rangeLimits.year[Data::rangeEnd]; + loopThroughYears(finalYear, state); timer.stop(); pDurationCollector->addDuration("mc_years", timer.get_duration()); } @@ -1062,120 +1064,6 @@ void ISimulation::regenerateTimeSeries(uint year) } } -template -uint ISimulation::buildSetsOfParallelYears( - uint firstYear, - uint endYear, - std::vector& setsOfParallelYears) -{ - // Filter on the years - const auto& yearsFilter = study.parameters.yearsFilter; - - // number max of years (to be executed or not) in a set of parallel years - uint maxNbYearsPerformed = 0; - - setOfParallelYears* set = nullptr; - bool buildNewSet = true; - bool foundFirstPerformedYearOfCurrentSet = false; - - // Gets information on each parallel years set - for (uint y = firstYear; y < endYear; ++y) - { - unsigned int indexSpace = 999999; - bool performCalculations = yearsFilter[y]; - - // Do we refresh just before this year ? If yes a new set of parallel years has to be - // created - bool refreshing = false; - refreshing = pData.haveToRefreshTSLoad && (y % pData.refreshIntervalLoad == 0); - refreshing - = refreshing || (pData.haveToRefreshTSSolar && (y % pData.refreshIntervalSolar == 0)); - refreshing - = refreshing || (pData.haveToRefreshTSWind && (y % pData.refreshIntervalWind == 0)); - refreshing - = refreshing || (pData.haveToRefreshTSHydro && (y % pData.refreshIntervalHydro == 0)); - - // Some thermal clusters may override the global parameter. - // Therefore, we may want to refresh TS even if pData.haveToRefreshTSThermal == false - bool haveToRefreshTSThermal - = pData.haveToRefreshTSThermal || study.runtime->thermalTSRefresh; - refreshing - = refreshing || (haveToRefreshTSThermal && (y % pData.refreshIntervalThermal == 0)); - - // We build a new set of parallel years if one of these conditions is fulfilled : - // - We have to refresh (or regenerate) some or all time series before running the - // current year - // - This is the first year (to be executed or not) after the previous set is full with - // years to be executed. That is : in the previous set filled, the max number of - // years to be actually run is reached. - buildNewSet = buildNewSet || refreshing; - - if (buildNewSet) - { - setOfParallelYears setToCreate; - setsOfParallelYears.push_back(setToCreate); - set = &(setsOfParallelYears.back()); - - // Initializations - set->nbPerformedYears = 0; - set->nbYears = 0; - set->regenerateTS = false; - set->yearForTSgeneration = 999999; - - // In case we have to regenerate times series before run the current set of parallel - // years - if (refreshing) - { - set->regenerateTS = true; - set->yearForTSgeneration = y; - } - } - - set->yearsIndices.push_back(y); - set->nbYears++; - set->yearFailed[y] = true; - set->isFirstPerformedYearOfASet[y] = false; - - if (performCalculations) - { - // Another year performed - ++pNbYearsReallyPerformed; - - // Number of actually performed years in the current set (up to now). - set->nbPerformedYears++; - // Index of the MC year's space (useful if this year is actually run) - indexSpace = set->nbPerformedYears - 1; - - set->isYearPerformed[y] = true; - set->performedYearToSpace[y] = indexSpace; - set->spaceToPerformedYear[indexSpace] = y; - - if (!foundFirstPerformedYearOfCurrentSet) - { - set->isFirstPerformedYearOfASet[y] = true; - foundFirstPerformedYearOfCurrentSet = true; - } - } - else - { - set->isYearPerformed[y] = false; - } - - // Do we build a new set at next iteration (for years to be executed or not) ? - if (indexSpace == pNbMaxPerformedYearsInParallel - 1 || y == endYear - 1) - { - buildNewSet = true; - foundFirstPerformedYearOfCurrentSet = false; - if (set->nbPerformedYears > maxNbYearsPerformed) - maxNbYearsPerformed = set->nbPerformedYears; - } - else - buildNewSet = false; - - } // End of loop over years - - return maxNbYearsPerformed; -} template void ISimulation::allocateMemoryForRandomNumbers(randomNumbers& randomForParallelYears) @@ -1492,21 +1380,22 @@ static void logPerformedYearsInAset(setOfParallelYears& set) } template -void ISimulation::loopThroughYears(uint firstYear, - uint endYear, +void ISimulation::loopThroughYears(uint endYear, std::vector& state) { assert(endYear <= study.parameters.nbYears); // List of parallel years sets - std::vector setsOfParallelYears; + + std::vector setsOfParallelYears = study.setsOfParallelYears; // Gets information on each set of parallel years and returns the max number of years performed // in a set The variable "maxNbYearsPerformedInAset" is the maximum numbers of years to be // actually executed in a set. A set contains some years to be actually executed (at most // "pNbMaxPerformedYearsInParallel" years) and some others to skip. - uint maxNbYearsPerformedInAset - = buildSetsOfParallelYears(firstYear, endYear, setsOfParallelYears); + + uint maxNbYearsPerformedInAset = pNbMaxPerformedYearsInParallel; + // Related to annual costs statistics (printed in output into separate files) pAnnualCostsStatistics.setNbPerformedYears(pNbYearsReallyPerformed); diff --git a/src/solver/simulation/solver.utils.h b/src/solver/simulation/solver.utils.h index 64287285f3..e8376ae2f2 100644 --- a/src/solver/simulation/solver.utils.h +++ b/src/solver/simulation/solver.utils.h @@ -45,43 +45,6 @@ namespace Solver { namespace Simulation { -struct setOfParallelYears -{ - // Un lot d'année à exécuter en parallèle. - // En fonction d'une éventuelle play-list, certaines seront jouées et d'autres non. - -public: - // Numeros des annees en parallele pour ce lot (certaines ne seront pas jouées en cas de - // play-list "trouée") - std::vector yearsIndices; - - // Une annee doit-elle être rejouée ? - std::map yearFailed; - - // Associe le numero d'une année jouée à l'indice de l'espace - std::map performedYearToSpace; - - // L'inverse : pour une année jouée, associe l'indice de l'espace au numero de l'année - std::map spaceToPerformedYear; - - // Pour chaque année, est-elle la première à devoir être jouée dans son lot d'années ? - std::map isFirstPerformedYearOfASet; - - // Pour chaque année du lot, est-elle jouée ou non ? - std::map isYearPerformed; - - // Nbre d'années en parallele vraiment jouées pour ce lot - unsigned int nbPerformedYears; - - // Nbre d'années en parallele jouées ou non pour ce lot - unsigned int nbYears; - - // Regenere-t-on des times series avant de jouer les annees du lot courant - bool regenerateTS; - - // Annee a passer a la fonction "regenerateTimeSeries(y)" (si regenerateTS is "true") - unsigned int yearForTSgeneration; -}; class costStatistics { @@ -95,6 +58,11 @@ class costStatistics { } + uint getNbPerformedYears() const + { + return nbPerformedYears; + } + void setNbPerformedYears(uint n) { assert(n); diff --git a/src/tests/end-to-end/simple-study.cpp b/src/tests/end-to-end/simple-study.cpp index c71c966d6f..516e9f81af 100644 --- a/src/tests/end-to-end/simple-study.cpp +++ b/src/tests/end-to-end/simple-study.cpp @@ -61,7 +61,7 @@ void prepareStudy(Study::Ptr pStudy, int nbYears) // ------------------------- // Getting the number of logical cores to use before loading and creating the areas : // Areas need this number to be up-to-date at construction. - pStudy->getNumberOfCores(false, 0); + pStudy->getNumberOfCores(false, false, 0); // Define as current study Data::Study::Current::Set(pStudy); diff --git a/src/tests/src/libs/antares/study/CMakeLists.txt b/src/tests/src/libs/antares/study/CMakeLists.txt index c5c0bcfb12..3ea4e8a414 100644 --- a/src/tests/src/libs/antares/study/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/CMakeLists.txt @@ -3,3 +3,4 @@ set(src_tests_src_libs_antares_study "${CMAKE_CURRENT_SOURCE_DIR}") add_subdirectory(area) add_subdirectory(scenario-builder) add_subdirectory(output-folder) +add_subdirectory(parallel-years) \ No newline at end of file diff --git a/src/tests/src/libs/antares/study/parallel-years/CMakeLists.txt b/src/tests/src/libs/antares/study/parallel-years/CMakeLists.txt new file mode 100644 index 0000000000..cca96e077a --- /dev/null +++ b/src/tests/src/libs/antares/study/parallel-years/CMakeLists.txt @@ -0,0 +1,35 @@ +# Useful variables definitions +set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") + +# =========================================== +# Tests on calculating forced parallel years +# =========================================== +set(SRC_PARALLEL_YEAR_CALC + test-parallel-years-calculator.cpp +) +add_executable(test-parallel-years-calculator ${SRC_PARALLEL_YEAR_CALC}) + +target_include_directories(test-parallel-years-calculator + PRIVATE + "${src_libs_antares_study}" + "${src_libs_antares_study}/parallel-years" +) + +target_link_libraries(test-parallel-years-calculator + PRIVATE + Boost::unit_test_framework + libantares-core + libmodel_antares +) +# Linux +if(UNIX AND NOT APPLE) +target_link_libraries(test-parallel-years-calculator PRIVATE stdc++fs) +endif() + + +# Storing test-parallel-years-calculator under the folder Unit-tests in the IDE +set_target_properties(test-parallel-years-calculator PROPERTIES FOLDER Unit-tests/parallel-years) + +add_test(NAME parallel-years-calculator COMMAND test-parallel-years-calculator) + +set_property(TEST parallel-years-calculator PROPERTY LABELS unit) \ No newline at end of file diff --git a/src/tests/src/libs/antares/study/parallel-years/test-parallel-years-calculator.cpp b/src/tests/src/libs/antares/study/parallel-years/test-parallel-years-calculator.cpp new file mode 100644 index 0000000000..ff24dbaba2 --- /dev/null +++ b/src/tests/src/libs/antares/study/parallel-years/test-parallel-years-calculator.cpp @@ -0,0 +1,354 @@ +#define BOOST_TEST_MODULE test-end-to-end tests + +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include + +namespace utf = boost::unit_test; +namespace tt = boost::test_tools; + +using namespace Yuni; +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() + { + // using default params + params.reset(); + } + + ~Fixture(){} + + Parameters params{}; + bool forceParallel = false; + bool enableParallel = false; + uint maxNbYearsInParallel = 0; + +}; + +BOOST_FIXTURE_TEST_SUITE(s, Fixture) + +BOOST_AUTO_TEST_CASE(default_params_no_force) +{ + bool thermalTSRefresh = false; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncHigh; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 1, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 1); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 1); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 1); +} + +BOOST_AUTO_TEST_CASE(default_params_no_force_bad_number_of_cores, *utf::expected_failures(1)) +{ + bool thermalTSRefresh = false; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncUnknown; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 0, + thermalTSRefresh, + params); + +} + +BOOST_AUTO_TEST_CASE(default_params_no_force_bad_ncmode, *utf::expected_failures(1)) +{ + bool thermalTSRefresh = false; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncUnknown; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 1, + thermalTSRefresh, + params); + +} + +constexpr uint default_number_of_core = 12; +BOOST_AUTO_TEST_CASE(hundred_years_no_force) +{ + bool thermalTSRefresh = false; + params.nbYears = 100; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncAvg; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, default_number_of_core, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 1); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 1); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 100); +} + + +BOOST_AUTO_TEST_CASE(four_mc_years_force_parallel) +{ + + bool thermalTSRefresh = false; + forceParallel = true; + enableParallel = true; + maxNbYearsInParallel = 2; + params.nbYears = 4; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, default_number_of_core, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 2); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 2); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 4); +} + +BOOST_AUTO_TEST_CASE(hundred_mc_years_force_parallel_sets_size_five_thermal_refresh_on) +{ + + bool thermalTSRefresh = true; + forceParallel = true; + enableParallel = true; + maxNbYearsInParallel = 5; + params.nbYears = 100; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, default_number_of_core, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 5); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 5); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 100); +} + + +BOOST_AUTO_TEST_CASE(hundred_mc_years_no_force_thermal_refresh_on) +{ + + bool thermalTSRefresh = true; + forceParallel = false; + enableParallel = false; + params.nbYears = 100; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncAvg; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 5, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 1); + // BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 1); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 100); +} + +BOOST_AUTO_TEST_CASE(hundred_mc_years_no_force_enable_parallel) +{ + + bool thermalTSRefresh = false; + forceParallel = false; + enableParallel = true; + + // Shouldn't have any effect since force parallel is off + maxNbYearsInParallel = 10; + + params.nbYears = 100; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncHigh; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 16, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 12); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 4); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 100); +} + +BOOST_AUTO_TEST_CASE(hundred_mc_years_no_force_enable_parallel_thermal_refresh_on) +{ + + bool thermalTSRefresh = true; + forceParallel = false; + enableParallel = true; + + // Shouldn't have any effect since force parallel is off + maxNbYearsInParallel = 10; + + params.nbYears = 100; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncAvg; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 8, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 4); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 4); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 100); +} + +BOOST_AUTO_TEST_CASE(hundred_mc_years_no_force_enable_parallel_thermal_refresh_on_draft_mode) +{ + + bool thermalTSRefresh = true; + forceParallel = false; + enableParallel = true; + + params.mode = Antares::Data::stdmAdequacyDraft; + + params.nbYears = 100; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncAvg; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 3, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 1); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 1); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 100); +} + +BOOST_AUTO_TEST_CASE(hundred_mc_years_force_parallel_enable_parallel_thermal_refresh_on_hot_start) +{ + + bool thermalTSRefresh = true; + forceParallel = true; + enableParallel = true; + + maxNbYearsInParallel = 5; + + params.initialReservoirLevels.iniLevels = Antares::Data::irlHotStart; + + params.nbYears = 100; + + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncAvg; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 4, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 5); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 5); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 100); +} + + +BOOST_AUTO_TEST_CASE(hundred_mc_years_force_10_parallel_user_playlist_thermal_refresh_on) +{ + + bool thermalTSRefresh = true; + forceParallel = true; + enableParallel = true; + + maxNbYearsInParallel = 10; + + params.nbYears = 100; + + // Create playlist + params.userPlaylist = true; + params.yearsFilter.reserve(params.nbYears); + for (uint i = 0; i != params.nbYears; ++i) + params.yearsFilter[i] = false; + params.yearsFilter[2] = true; + params.resetYearsWeigth(); + params.setYearWeight(0,4); + params.setYearWeight(1,10); + params.setYearWeight(2,3); + + // Override number of raw cores + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncLow; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 7, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 1); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 1); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 1); +} + +BOOST_AUTO_TEST_CASE(hundred_mc_years_enable_parallel_user_playlist_thermal_refresh_on) +{ + + bool thermalTSRefresh = true; + forceParallel = false; + enableParallel = true; + + // Shouldn't have any effect since force parallel is off + maxNbYearsInParallel = 10; + + params.nbYears = 100; + + // Create playlist + params.userPlaylist = true; + params.yearsFilter.reserve(params.nbYears); + for (uint i = 0; i != params.nbYears; ++i) + { + if(i%2 == 0) + params.yearsFilter[i] = true; + else + params.yearsFilter[i] = false; + } + + params.resetYearsWeigth(); + params.setYearWeight(0,4); + params.setYearWeight(1,10); + params.setYearWeight(2,3); + + // Override number of raw cores + params.nbCores.ncMode = Antares::Data::NumberOfCoresMode::ncHigh; + + SetsOfParallelYearCalculator builder(forceParallel, + enableParallel, + maxNbYearsInParallel, 9, + thermalTSRefresh, + params); + + BOOST_CHECK_EQUAL(builder.getForcedNbOfParallelYears(), 7); + BOOST_CHECK(builder.allSetsParallelYearsHaveSameSize()); + BOOST_CHECK_EQUAL(builder.getMinNbParallelYearsForGUI(), 1); + BOOST_CHECK_EQUAL(builder.getNbYearsReallyPerformed(), 50); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/src/ui/simulator/application/study.cpp b/src/ui/simulator/application/study.cpp index 798d9b0c3a..b1d0753f5c 100644 --- a/src/ui/simulator/application/study.cpp +++ b/src/ui/simulator/application/study.cpp @@ -319,7 +319,7 @@ class JobSaveStudy final : public Toolbox::Jobs::Job // Updating the number of logical cores to use when saving the study // so that the run window is up to date. - study->getNumberOfCores(false, 0); + study->getNumberOfCores(false, false, 0); if (pSaveAs || pShouldInvalidateStudy) { diff --git a/src/ui/simulator/windows/options/advanced/advanced.cpp b/src/ui/simulator/windows/options/advanced/advanced.cpp index 331c011ef3..8d6ce3926f 100644 --- a/src/ui/simulator/windows/options/advanced/advanced.cpp +++ b/src/ui/simulator/windows/options/advanced/advanced.cpp @@ -916,7 +916,7 @@ void AdvancedParameters::onSelectNumberOfCoresLevel(Data::NumberOfCoresMode ncMo { study->parameters.nbCores.ncMode = ncMode; // Force refresh for study->nbYearsParallelRaw - study->getNumberOfCores(false, 1 /* ignored */); + study->getNumberOfCores(false, false, 1 /* ignored */); MarkTheStudyAsModified(); refresh(); }