diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index c774854d16..85713eeb70 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -229,6 +229,15 @@ jobs: os: ${{ env.os }} variant: "parallel" + - name: Run tests for time series generator tool + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{steps.simtest-version.outputs.prop}} + batch-name: ts-generator + os: ${{ env.os }} + variant: "tsgenerator" + - name: Run medium-tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests diff --git a/.github/workflows/windows-vcpkg.yml b/.github/workflows/windows-vcpkg.yml index ccc8794dcb..59e931e6a8 100644 --- a/.github/workflows/windows-vcpkg.yml +++ b/.github/workflows/windows-vcpkg.yml @@ -246,6 +246,15 @@ jobs: os: ${{ env.test-platform }} variant: "parallel" + - name: Run tests for time series generator tool + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{steps.simtest-version.outputs.prop}} + batch-name: ts-generator + os: ${{ env.test-platform }} + variant: "tsgenerator" + - name: Run medium-tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests @@ -304,7 +313,7 @@ jobs: run: | cd _build cpack -G ZIP - + - name: Installer upload uses: actions/upload-artifact@v4 with: diff --git a/docs/user-guide/04-migration-guides.md b/docs/user-guide/04-migration-guides.md index 0e5f50726c..5dbc3f8885 100644 --- a/docs/user-guide/04-migration-guides.md +++ b/docs/user-guide/04-migration-guides.md @@ -1,5 +1,25 @@ # Migration guides This is a list of all recent changes that came with new Antares Simulator features. The main goal of this document is to lower the costs of changing existing interfaces, both GUI and scripts. + +## v9.2.0 +### (TS-generator only) TS generation for link capacities +In files input/links//properties.ini, add the following properties +- tsgen_direct_XXX, +- tsgen_indirect_XXX +with XXX in +- unitcount (unsigned int, default 1) +- nominalcapacity (float) +- law.planned (string "uniform"/"geometric") +- law.forced (same) +- volatility.planned (double in [0,1]) +- volatility.forced (same) + +- "prepro" timeseries => input/links//prepro/_{direct, indirect}.txt, 365x6 values, respectively "forced outage duration", "planned outage duration", "forced outage rate", "planned outage rate", "minimum of groups in maintenance", "maximum of groups in maintenance". +- "modulation" timeseries => input/links//prepro/_mod_{direct, indirect}.txt, 8760x1 values each in [0, 1] +- number of TS to generate => generaldata.ini/General/nbtimeserieslinks (unsigned int, default value 1) + + + ## v9.1.0 ### Input #### Hydro Maximum Generation/Pumping Power diff --git a/src/libs/antares/study/area/links.cpp b/src/libs/antares/study/area/links.cpp index 4d4b6af140..40da9df149 100644 --- a/src/libs/antares/study/area/links.cpp +++ b/src/libs/antares/study/area/links.cpp @@ -21,8 +21,11 @@ #include "antares/study/area/links.h" +#include #include +#include + #include #include @@ -134,6 +137,58 @@ bool AreaLink::linkLoadTimeSeries_for_version_820_and_later(const AnyString& fol return success; } +// This function is "lazy", it only loads files if they exist +// and set a `valid` flag +bool AreaLink::loadTSGenTimeSeries(const fs::path& folder) +{ + const std::string idprepro = std::string(from->id) + "/" + std::string(with->id); + tsGeneration.prepro = + std::make_unique(idprepro, tsGeneration.unitCount); + + bool anyFileWasLoaded = false; + + // file name without suffix, .txt for general infos and mod_direct/indirect.txt + fs::path preproFile = folder / "prepro" / with->id.c_str(); + + // Prepro + fs::path filepath = preproFile; + filepath += ".txt"; + if (fs::exists(filepath)) + { + anyFileWasLoaded = true; + tsGeneration.valid = tsGeneration.prepro->data.loadFromCSVFile( + filepath.string(), + Antares::Data::PreproAvailability::preproAvailabilityMax, + DAYS_PER_YEAR) + && tsGeneration.prepro->validate(); + } + + // Modulation + filepath = preproFile; + filepath += "_mod_direct.txt"; + if (fs::exists(filepath)) + { + anyFileWasLoaded = true; + tsGeneration.valid &= tsGeneration.modulationCapacityDirect + .loadFromCSVFile(filepath.string(), 1, HOURS_PER_YEAR); + } + + filepath = preproFile; + filepath += "_mod_indirect.txt"; + if (fs::exists(filepath)) + { + anyFileWasLoaded = true; + tsGeneration.valid &= tsGeneration.modulationCapacityIndirect + .loadFromCSVFile(filepath.string(), 1, HOURS_PER_YEAR); + } + + if (anyFileWasLoaded) + { + return tsGeneration.valid; + } + return true; +} + bool AreaLink::isLinkPhysical() const { // All link types are physical, except arVirt @@ -448,13 +503,55 @@ bool handleKey(Data::AreaLink& link, const String& key, const String& value) link.filterYearByYear = stringIntoDatePrecision(value); return true; } + return false; +} + +bool handleTSGenKey(Data::LinkTsGeneration& out, + const std::string& key, + const String& value) +{ + + if (key == "unitcount") + { + return value.to(out.unitCount); + } + + if (key == "nominalcapacity") + { + return value.to(out.nominalCapacity); + } + + if (key == "law.planned") + { + return value.to(out.plannedLaw); + } + + if (key == "law.forced") + { + return value.to(out.forcedLaw); + } + + if (key == "volatility.planned") + { + return value.to(out.plannedVolatility); + } + + if (key == "volatility.forced") + { + return value.to(out.forcedVolatility); + } + + if (key == "force-no-generation") + { + return value.to(out.forceNoGeneration); + } return false; } bool AreaLinksInternalLoadFromProperty(AreaLink& link, const String& key, const String& value) { - return handleKey(link, key, value); + return handleKey(link, key, value) || handleTSGenKey(link.tsGeneration, key, value); } [[noreturn]] void logLinkDataCheckError(const AreaLink& link, const String& msg, int hour) @@ -475,7 +572,7 @@ bool AreaLinksInternalLoadFromProperty(AreaLink& link, const String& key, const } } // anonymous namespace -bool AreaLinksLoadFromFolder(Study& study, AreaList* l, Area* area, const fs::path& folder) +bool AreaLinksLoadFromFolder(Study& study, AreaList* l, Area* area, const fs::path& folder, bool loadTSGen) { // Assert assert(area); @@ -601,6 +698,11 @@ bool AreaLinksLoadFromFolder(Study& study, AreaList* l, Area* area, const fs::pa } } + if (loadTSGen) + { + ret = link.loadTSGenTimeSeries(folder) && ret; + } + // From the solver only if (study.usedByTheSolver) { diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index fd2b3bb5b7..f76afb5686 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -881,7 +881,7 @@ static bool AreaListLoadFromFolderSingleArea(Study& study, // Links { fs::path folder = fs::path(study.folderInput.c_str()) / "links" / area.id.c_str(); - ret = AreaLinksLoadFromFolder(study, list, &area, folder) && ret; + ret = AreaLinksLoadFromFolder(study, list, &area, folder, options.linksLoadTSGen) && ret; } // UI diff --git a/src/libs/antares/study/cleaner/cleaner-v20.cpp b/src/libs/antares/study/cleaner/cleaner-v20.cpp index 3c8f183be4..7d1aadf58e 100644 --- a/src/libs/antares/study/cleaner/cleaner-v20.cpp +++ b/src/libs/antares/study/cleaner/cleaner-v20.cpp @@ -362,7 +362,7 @@ bool listOfFilesAnDirectoriesToKeep(StudyCleaningInfos* infos) logs.verbosityLevel = Logs::Verbosity::Warning::level; // load all links buffer.clear() << infos->folder << "/input/links/" << area->id; - if (not AreaLinksLoadFromFolder(*study, arealist, area, buffer.c_str())) + if (not AreaLinksLoadFromFolder(*study, arealist, area, buffer.c_str(), false)) { delete arealist; delete study; diff --git a/src/libs/antares/study/fwd.cpp b/src/libs/antares/study/fwd.cpp index 100a4b127e..f45f093aaf 100644 --- a/src/libs/antares/study/fwd.cpp +++ b/src/libs/antares/study/fwd.cpp @@ -53,6 +53,8 @@ const char* SeedToCString(SeedIndex seed) return "Noise on virtual Hydro costs"; case seedHydroManagement: return "Initial reservoir levels"; + case seedTsGenLinks: + return "Links time-series generation"; case seedMax: return ""; } @@ -85,6 +87,8 @@ const char* SeedToID(SeedIndex seed) return "seed-hydro-costs"; case seedHydroManagement: return "seed-initial-reservoir-levels"; + case seedTsGenLinks: + return "seed-tsgen-links"; case seedMax: return ""; } diff --git a/src/libs/antares/study/include/antares/study/area/area.h b/src/libs/antares/study/include/antares/study/area/area.h index 6661ab8556..6fbb69dde4 100644 --- a/src/libs/antares/study/include/antares/study/area/area.h +++ b/src/libs/antares/study/include/antares/study/area/area.h @@ -727,7 +727,8 @@ AreaLink* AreaAddLinkBetweenAreas(Area* area, Area* with, bool warning = true); bool AreaLinksLoadFromFolder(Study& s, AreaList* l, Area* area, - const std::filesystem::path& folder); + const std::filesystem::path& folder, + bool loadTSGen); /*! ** \brief Save interconnections of a given area into a folder (`input/areas/[area]/ntc`) diff --git a/src/libs/antares/study/include/antares/study/area/links.h b/src/libs/antares/study/include/antares/study/area/links.h index fd4b8e69d8..5b01ba3efd 100644 --- a/src/libs/antares/study/include/antares/study/area/links.h +++ b/src/libs/antares/study/include/antares/study/area/links.h @@ -71,6 +71,8 @@ class AreaLink final: public Yuni::NonCopyable bool loadTimeSeries(const StudyVersion& version, const AnyString& folder); + bool loadTSGenTimeSeries(const std::filesystem::path& folder); + void storeTimeseriesNumbers(Solver::IResultWriter& writer) const; //! \name Area @@ -206,6 +208,9 @@ class AreaLink final: public Yuni::NonCopyable int linkWidth; friend struct CompareLinkName; + + LinkTsGeneration tsGeneration; + }; // class AreaLink struct CompareLinkName final diff --git a/src/libs/antares/study/include/antares/study/fwd.h b/src/libs/antares/study/include/antares/study/fwd.h index 6fa2819aca..7256eda4a2 100644 --- a/src/libs/antares/study/include/antares/study/fwd.h +++ b/src/libs/antares/study/include/antares/study/fwd.h @@ -361,6 +361,8 @@ enum SeedIndex seedHydroCosts, //! Seed - Hydro management seedHydroManagement, + //! The seed for links + seedTsGenLinks, //! The number of seeds seedMax, }; diff --git a/src/libs/antares/study/include/antares/study/load-options.h b/src/libs/antares/study/include/antares/study/load-options.h index 898b641653..1ecd34b968 100644 --- a/src/libs/antares/study/include/antares/study/load-options.h +++ b/src/libs/antares/study/include/antares/study/load-options.h @@ -52,6 +52,10 @@ class StudyLoadOptions bool loadOnlyNeeded; //! Force the year-by-year flag bool forceYearByYear; + + //! Load data associated to link TS generation + bool linksLoadTSGen = false; + //! Force the derated mode bool forceDerated; diff --git a/src/libs/antares/study/include/antares/study/parameters.h b/src/libs/antares/study/include/antares/study/parameters.h index c66cf9757f..cb69fb4326 100644 --- a/src/libs/antares/study/include/antares/study/parameters.h +++ b/src/libs/antares/study/include/antares/study/parameters.h @@ -256,6 +256,8 @@ class Parameters final uint nbTimeSeriesThermal; //! Nb of timeSeries : Solar uint nbTimeSeriesSolar; + //! Nb of timeSeries : Links + uint nbLinkTStoGenerate = 1; //@} //! \name Time-series refresh diff --git a/src/libs/antares/study/parameters.cpp b/src/libs/antares/study/parameters.cpp index a598ee1566..5c0cdac583 100644 --- a/src/libs/antares/study/parameters.cpp +++ b/src/libs/antares/study/parameters.cpp @@ -528,6 +528,10 @@ static bool SGDIntLoadFamily_General(Parameters& d, { return value.to(d.nbTimeSeriesSolar); } + if (key == "nbtimeserieslinks") + { + return value.to(d.nbLinkTStoGenerate); + } // Interval values if (key == "refreshintervalload") { @@ -1054,6 +1058,10 @@ static bool SGDIntLoadFamily_SeedsMersenneTwister(Parameters& d, { return value.to(d.seed[seedTsGenSolar]); } + if (key == "seed_links") + { + return value.to(d.seed[seedTsGenLinks]); + } if (key == "seed_timeseriesnumbers") { return value.to(d.seed[seedTimeseriesNumbers]); @@ -1783,6 +1791,7 @@ void Parameters::saveToINI(IniFile& ini) const section->add("nbTimeSeriesWind", nbTimeSeriesWind); section->add("nbTimeSeriesThermal", nbTimeSeriesThermal); section->add("nbTimeSeriesSolar", nbTimeSeriesSolar); + section->add("nbtimeserieslinks", nbLinkTStoGenerate); // Refresh ParametersSaveTimeSeries(section, "refreshTimeSeries", timeSeriesToRefresh); diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index 8084130e9f..5e36fcaca8 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -938,6 +938,7 @@ static inline void logPerformedYearsInAset(setOfParallelYears& set) << " perfomed)"; std::string performedYearsToLog = ""; + std::ranges::for_each(set.yearsIndices, [&set, &performedYearsToLog](const uint& y) { diff --git a/src/solver/ts-generator/availability.cpp b/src/solver/ts-generator/availability.cpp index f9b50696df..69f62a10a1 100644 --- a/src/solver/ts-generator/availability.cpp +++ b/src/solver/ts-generator/availability.cpp @@ -50,12 +50,29 @@ AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(Data::ThermalCluster* s { } +AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(Data::LinkTsGeneration& source, + Data::TimeSeries& capacity, + Matrix<>& modulation, + const std::string& areaDestName): + unitCount(source.unitCount), + nominalCapacity(source.nominalCapacity), + forcedVolatility(source.forcedVolatility), + plannedVolatility(source.plannedVolatility), + forcedLaw(source.forcedLaw), + plannedLaw(source.plannedLaw), + prepro(source.prepro.get()), + series(capacity.timeSeries), + modulationCapacity(modulation[0]), + name(areaDestName) +{ +} + namespace { class GeneratorTempData final { public: - explicit GeneratorTempData(Data::Study&, unsigned); + explicit GeneratorTempData(Data::Study&, unsigned, MersenneTwister&); void generateTS(const Data::Area& area, AvailabilityTSGeneratorData& cluster) const; @@ -82,10 +99,10 @@ class GeneratorTempData final const T& duration) const; }; -GeneratorTempData::GeneratorTempData(Data::Study& study, unsigned nbOfSeriesToGen): +GeneratorTempData::GeneratorTempData(Data::Study& study, unsigned nbOfSeriesToGen, MersenneTwister& rndGenerator): derated(study.parameters.derated), nbOfSeriesToGen_(nbOfSeriesToGen), - rndgenerator(study.runtime->random[Data::seedTsGenThermal]) + rndgenerator(rndGenerator) { } @@ -592,6 +609,23 @@ std::vector getAllClustersToGen(const Data::AreaList& are return clusters; } +listOfLinks getAllLinksToGen(Data::AreaList& areas) +{ + listOfLinks links; + + areas.each( + [&links](const Data::Area& area) + { + std::ranges::for_each(area.links, [&links](auto& l) + { + if (!l.second->tsGeneration.forceNoGeneration) + links.push_back(l.second); + }); + }); + + return links; +} + void writeResultsToDisk(const Data::Study& study, Solver::IResultWriter& writer, const Matrix<>& series, @@ -617,7 +651,9 @@ bool generateThermalTimeSeries(Data::Study& study, bool archive = study.parameters.timeSeriesToArchive & Data::timeSeriesThermal; - auto generator = GeneratorTempData(study, study.parameters.nbTimeSeriesThermal); + auto generator = GeneratorTempData(study, + study.parameters.nbTimeSeriesThermal, + study.runtime->random[Data::seedTsGenThermal]); // TODO VP: parallel for (auto* cluster: clusters) @@ -636,4 +672,50 @@ bool generateThermalTimeSeries(Data::Study& study, return true; } +bool generateLinkTimeSeries(Data::Study& study, + const listOfLinks& links, + Solver::IResultWriter& writer, + const std::string& savePath) +{ + logs.info(); + logs.info() << "Generating the links time-series"; + + auto generator = GeneratorTempData(study, + study.parameters.nbLinkTStoGenerate, + study.runtime->random[Data::seedTsGenLinks]); + + for (const auto& link: links) + { + Data::TimeSeries ts(link->timeseriesNumbers); + ts.resize(study.parameters.nbLinkTStoGenerate, HOURS_PER_YEAR); + + auto& tsGenStruct = link->tsGeneration; + + if (!tsGenStruct.valid) + { + logs.error() << "Missing data for link " << link->from->id << "/" << link->with->id; + return false; + } + + // DIRECT + AvailabilityTSGeneratorData tsConfigDataDirect(tsGenStruct, ts, tsGenStruct.modulationCapacityDirect, link->with->name); + + generator.generateTS(*link->from, tsConfigDataDirect); + + std::string filePath = savePath + SEP + link->from->id + SEP + link->with->id.c_str() + + "_direct.txt"; + writeResultsToDisk(study, writer, ts.timeSeries, filePath); + + // INDIRECT + AvailabilityTSGeneratorData tsConfigDataIndirect(tsGenStruct, ts, tsGenStruct.modulationCapacityIndirect, link->with->name); + + generator.generateTS(*link->from, tsConfigDataIndirect); + + filePath = savePath + SEP + link->from->id + SEP + link->with->id.c_str() + + "_indirect.txt"; + writeResultsToDisk(study, writer, ts.timeSeries, filePath); + } + + return true; +} } // namespace Antares::TSGenerator diff --git a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h index 77e09b9b79..1c32e67bdd 100644 --- a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h +++ b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h @@ -39,6 +39,10 @@ class AvailabilityTSGeneratorData { public: explicit AvailabilityTSGeneratorData(Data::ThermalCluster*); + AvailabilityTSGeneratorData(Data::LinkTsGeneration&, + Data::TimeSeries&, + Matrix<>& modulation, + const std::string& name); const unsigned& unitCount; const double& nominalCapacity; @@ -58,6 +62,8 @@ class AvailabilityTSGeneratorData const std::string& name; }; +using listOfLinks = std::vector; + void ResizeGeneratedTimeSeries(Data::AreaList& areas, Data::Parameters& params); /*! @@ -71,9 +77,16 @@ bool generateThermalTimeSeries(Data::Study& study, Solver::IResultWriter& writer, const std::string& savePath); +bool generateLinkTimeSeries(Data::Study& study, + const listOfLinks& links, + Solver::IResultWriter& writer, + const std::string& savePath); + std::vector getAllClustersToGen(const Data::AreaList& areas, bool globalThermalTSgeneration); +listOfLinks getAllLinksToGen(Data::AreaList& areas); + /*! ** \brief Destroy all TS Generators */ diff --git a/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h b/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h index cc02a50619..ac36a826ea 100644 --- a/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h +++ b/src/solver/ts-generator/include/antares/solver/ts-generator/prepro.h @@ -115,6 +115,27 @@ class PreproAvailability unsigned int unitCount; }; // class PreproAvailability +struct LinkTsGeneration +{ + unsigned unitCount = 0; + double nominalCapacity = 0; + + double forcedVolatility = 0.; + double plannedVolatility = 0.; + + Data::StatisticalLaw forcedLaw = LawUniform; + Data::StatisticalLaw plannedLaw = LawUniform; + + std::unique_ptr prepro; + + Matrix<> modulationCapacityDirect; + Matrix<> modulationCapacityIndirect; + + bool valid = false; + + bool forceNoGeneration = false; +}; + } // namespace Antares::Data #endif // __ANTARES_LIBS_STUDY_PARTS_THERMAL_PREPRO_HXX__ diff --git a/src/tools/ts-generator/main.cpp b/src/tools/ts-generator/main.cpp index f59a5dc614..8f23f60619 100644 --- a/src/tools/ts-generator/main.cpp +++ b/src/tools/ts-generator/main.cpp @@ -35,6 +35,10 @@ #include #include +using namespace Antares; + +namespace fs = std::filesystem; + struct Settings { std::string studyFolder; @@ -43,6 +47,11 @@ struct Settings bool allThermal = false; /// generate TS for a list "area.cluster;area2.cluster2;" std::string thermalListToGen = ""; + + /// generate TS for all links if activated + bool allLinks = false; + /// generate TS for a list "area.link;area2.link2;" + std::string linksListToGen = ""; }; std::unique_ptr createTsGeneratorParser(Settings& settings) @@ -58,7 +67,14 @@ std::unique_ptr createTsGeneratorParser(Settings& settings ' ', "thermal", "Generate TS for a list of area IDs and thermal clusters IDs, " - "usage:\n\t--thermal=\"areaID.clusterID;area2ID.clusterID\""); + "\nusage: --thermal=\"areaID.clusterID;area2ID.clusterID\""); + + parser->addFlag(settings.allLinks, ' ', "all-links", "Generate TS capacities for all links"); + parser->addFlag(settings.linksListToGen, + ' ', + "links", + "Generate TS capacities for a list of 2 area IDs, " + "usage: --links=\"areaID.area2ID;area3ID.area1ID\""); parser->remainingArguments(settings.studyFolder); @@ -95,8 +111,32 @@ std::vector getClustersToGen(Data::AreaList& areas, return clusters; } +TSGenerator::listOfLinks getLinksToGen(Data::AreaList& areas, const std::string& linksToGen) +{ + TSGenerator::listOfLinks links; + const auto ids = splitStringIntoPairs(linksToGen, ';', '.'); + + for (const auto& [areaFromID, areaWithID]: ids) + { + logs.info() << "Searching for link: " << areaFromID << "/" << areaWithID; + + auto* link = areas.findLink(areaFromID, areaWithID); + if (!link) + { + logs.warning() << "Link not found: " << areaFromID << "/" << areaWithID; + continue; + } + + links.emplace_back(link); + } + + return links; +} + int main(int argc, char* argv[]) { + logs.applicationName("ts-generator"); + Settings settings; auto parser = createTsGeneratorParser(settings); @@ -119,9 +159,16 @@ int main(int argc, char* argv[]) return 1; } + if (settings.allLinks && !settings.linksListToGen.empty()) + { + logs.error() << "Conflicting options, either choose all links or a list"; + return 1; + } + auto study = std::make_shared(true); Data::StudyLoadOptions studyOptions; studyOptions.prepareOutput = true; + studyOptions.linksLoadTSGen = true; if (!study->loadFromFolder(settings.studyFolder, studyOptions)) { @@ -149,7 +196,8 @@ int main(int argc, char* argv[]) nullptr, durationCollector); - const auto thermalSavePath = std::filesystem::path("ts-generator") / "thermal"; + const auto thermalSavePath = fs::path("ts-generator") / "thermal"; + const auto linksSavePath = fs::path("ts-generator") / "links"; // THERMAL std::vector clusters; @@ -167,10 +215,28 @@ int main(int argc, char* argv[]) logs.debug() << c->id(); } + // LINKS + TSGenerator::listOfLinks links; + if (settings.allLinks) + { + links = TSGenerator::getAllLinksToGen(study->areas); + } + else if (!settings.linksListToGen.empty()) + { + links = getLinksToGen(study->areas, settings.linksListToGen); + } + + for (auto& l: links) + { + logs.debug() << l->getName(); + } + bool ret = TSGenerator::generateThermalTimeSeries(*study, clusters, *resultWriter, thermalSavePath.string()); + ret = TSGenerator::generateLinkTimeSeries(*study, links, *resultWriter, linksSavePath.string()) + && ret; return !ret; // return 0 for success }