From 9be866cf4aec0a3a8b4b415ef8e59508adedddda Mon Sep 17 00:00:00 2001 From: payetvin <113102157+payetvin@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:02:03 +0200 Subject: [PATCH] Thermal validation and loading (#2173) Modular loading: separating loading and validation functions --- src/libs/antares/study/area/list.cpp | 3 +- .../study/include/antares/study/area/area.h | 2 +- .../study/parts/thermal/cluster_list.h | 3 + .../antares/study/parts/thermal/cluster.cpp | 14 -- .../study/parts/thermal/cluster_list.cpp | 151 ++++++++---------- 5 files changed, 75 insertions(+), 98 deletions(-) diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index 219f7ee8aa..b622b94af9 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -376,7 +376,6 @@ bool saveAreaAdequacyPatchIniFile(const Area& area, const Clob& buffer) } AreaList::AreaList(Study& study): - byIndex(nullptr), pStudy(study) { } @@ -994,6 +993,7 @@ static bool AreaListLoadFromFolderSingleArea(Study& study, { buffer.clear() << study.folderInput << SEP << "thermal" << SEP << "prepro"; ret = area.thermal.list.loadPreproFromFolder(study, buffer) && ret; + ret = area.thermal.list.validatePrepro(study) && ret; buffer.clear() << study.folderInput << SEP << "thermal" << SEP << "series"; ret = area.thermal.list.loadDataSeriesFromFolder(study, buffer) && ret; ret = area.thermal.list.loadEconomicCosts(study, buffer) && ret; @@ -1182,6 +1182,7 @@ bool AreaList::loadFromFolder(const StudyLoadOptions& options) Area& area = *(i->second); buffer.clear() << pStudy.folderInput << thermalPlant << area.id; ret = area.thermal.list.loadFromFolder(pStudy, buffer.c_str(), &area) && ret; + ret = area.thermal.list.validateClusters(pStudy.parameters) && ret; } } diff --git a/src/libs/antares/study/include/antares/study/area/area.h b/src/libs/antares/study/include/antares/study/area/area.h index ede65679a2..6661ab8556 100644 --- a/src/libs/antares/study/include/antares/study/area/area.h +++ b/src/libs/antares/study/include/antares/study/area/area.h @@ -689,7 +689,7 @@ class AreaList final: public Yuni::NonCopyable public: //! All areas by their index - Area** byIndex; + Area** byIndex = nullptr; //! All areas in the list Area::Map areas; diff --git a/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h b/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h index ea1518750d..9f091defda 100644 --- a/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h +++ b/src/libs/antares/study/include/antares/study/parts/thermal/cluster_list.h @@ -113,6 +113,9 @@ class ThermalClusterList: public ClusterList ** \return A non-zero value if the operation succeeded, 0 otherwise */ bool loadPreproFromFolder(Study& s, const AnyString& folder); + bool validatePrepro(const Study& study); + + bool validateClusters(const Parameters& param) const; bool loadEconomicCosts(Study& s, const AnyString& folder); diff --git a/src/libs/antares/study/parts/thermal/cluster.cpp b/src/libs/antares/study/parts/thermal/cluster.cpp index 6a008f8929..da502ac29e 100644 --- a/src/libs/antares/study/parts/thermal/cluster.cpp +++ b/src/libs/antares/study/parts/thermal/cluster.cpp @@ -512,20 +512,6 @@ bool Data::ThermalCluster::integrityCheck() bool ret = true; - if (minUpTime > 168 or 0 == minUpTime) - { - logs.error() << "Thermal cluster " << parentArea->name << "/" << pName - << ": The min. up time must be between 1 and 168"; - minUpTime = 1; - ret = false; - } - if (minDownTime > 168 or 0 == minDownTime) - { - logs.error() << "Thermal cluster " << parentArea->name << "/" << pName - << ": The min. down time must be between 1 and 168"; - minDownTime = 1; - ret = false; - } if (nominalCapacity < 0.) { logs.error() << "Thermal cluster " << parentArea->name << "/" << pName diff --git a/src/libs/antares/study/parts/thermal/cluster_list.cpp b/src/libs/antares/study/parts/thermal/cluster_list.cpp index 2059cf9238..4bf2e042c2 100644 --- a/src/libs/antares/study/parts/thermal/cluster_list.cpp +++ b/src/libs/antares/study/parts/thermal/cluster_list.cpp @@ -159,52 +159,56 @@ bool ThermalClusterList::loadFromFolder(Study& study, const AnyString& folder, A options = Matrix<>::optFixedSize, }; - bool r = cluster->modulation.loadFromCSVFile(modulationFile, - thermalModulationMax, - HOURS_PER_YEAR, - options); - if (!r && study.usedByTheSolver) + ret = cluster->modulation.loadFromCSVFile(modulationFile, + thermalModulationMax, + HOURS_PER_YEAR, + options) && ret; + + // Check the data integrity of the cluster + addToCompleteList(cluster); + } + + rebuildIndexes(); + rebuildIndex(); + + return ret; +} + + +bool ThermalClusterList::validateClusters(const Parameters& parameters) const +{ + bool ret = true; + + for (const auto& cluster : allClusters_) + { + cluster->minUpTime = std::clamp(cluster->minUpTime, 1u, 168u); + cluster->minDownTime = std::clamp(cluster->minDownTime, 1u, 168u); + + // update the minUpDownTime + cluster->minUpDownTime = std::max(cluster->minUpTime, cluster->minDownTime); + + if (!parameters.include.thermal.minStablePower) + { + cluster->minStablePower = 0.; + } + if (!parameters.include.thermal.minUPTime) { - cluster->modulation.reset(thermalModulationMax, HOURS_PER_YEAR); - cluster->modulation.fill(1.); - cluster->modulation.fillColumn(thermalMinGenModulation, 0.); + cluster->minUpDownTime = 1; + cluster->minUpTime = 1; + cluster->minDownTime = 1; } - ret = ret && r; - // Special operations when not ran from the interface (aka solver) - if (study.usedByTheSolver) + if (!parameters.include.reserve.spinning) { - if (!study.parameters.include.thermal.minStablePower) - { - cluster->minStablePower = 0.; - } - if (!study.parameters.include.thermal.minUPTime) - { - cluster->minUpDownTime = 1; - cluster->minUpTime = 1; - cluster->minDownTime = 1; - } - else - { - cluster->minUpDownTime = std::max(cluster->minUpTime, cluster->minDownTime); - } + cluster->spinning = 0; + } - if (!study.parameters.include.reserve.spinning) - { - cluster->spinning = 0; - } + cluster->nominalCapacityWithSpinning = cluster->nominalCapacity; - cluster->nominalCapacityWithSpinning = cluster->nominalCapacity; - } + ret = cluster->integrityCheck() && ret; - // Check the data integrity of the cluster - cluster->integrityCheck(); - addToCompleteList(cluster); } - rebuildIndexes(); - rebuildIndex(); - return ret; } @@ -268,35 +272,11 @@ static bool ThermalClusterLoadFromProperty(ThermalCluster& cluster, const IniFil if (p->key == "min-up-time") { - if (p->value.to(cluster.minUpTime)) - { - if (cluster.minUpTime < 1) - { - cluster.minUpTime = 1; - } - if (cluster.minUpTime > 168) - { - cluster.minUpTime = 168; - } - return true; - } - return false; + return p->value.to(cluster.minUpTime); } if (p->key == "min-down-time") { - if (p->value.to(cluster.minDownTime)) - { - if (cluster.minDownTime < 1) - { - cluster.minDownTime = 1; - } - if (cluster.minDownTime > 168) - { - cluster.minDownTime = 168; - } - return true; - } - return false; + return p->value.to(cluster.minDownTime); } if (p->key == "name") { @@ -375,8 +355,6 @@ bool ThermalClusterLoadFromSection(const AnyString& filename, << property->key << "`: The property is unknown and ignored"; } } - // update the minUpDownTime - cluster.minUpDownTime = std::max(cluster.minUpTime, cluster.minDownTime); } return true; } @@ -399,7 +377,7 @@ void ThermalClusterList::reverseCalculationOfSpinning() void ThermalClusterList::enableMustrunForEveryone() { - for (auto& c: allClusters_) + for (const auto& c : allClusters_) { c->mustrun = true; } @@ -606,32 +584,41 @@ bool ThermalClusterList::saveEconomicCosts(const AnyString& folder) const bool ThermalClusterList::loadPreproFromFolder(Study& study, const AnyString& folder) { - const bool globalThermalTSgeneration = study.parameters.timeSeriesToGenerate - & timeSeriesThermal; - Clob buffer; auto hasPrepro = [](auto c) { return (bool)c->prepro; }; - auto loadAndCheckPrepro = [&buffer, &folder, &study, &globalThermalTSgeneration](auto c) + auto loadPrepro = [&buffer, &folder, &study](auto& c) { assert(c->parentArea && "cluster: invalid parent area"); buffer.clear() << folder << SEP << c->parentArea->id << SEP << c->id(); - bool result = c->prepro->loadFromFolder(study, buffer); + return c->prepro->loadFromFolder(study, buffer); + }; - if (study.usedByTheSolver && globalThermalTSgeneration) - { - result = c->prepro->validate() && result; - } + return std::ranges::all_of(allClusters_ | std::views::filter(hasPrepro), loadPrepro); +} - if (result && study.usedByTheSolver && c->doWeGenerateTS(globalThermalTSgeneration)) - { - result = c->prepro->normalizeAndCheckNPO(); - } - return result; - }; +bool ThermalClusterList::validatePrepro(const Study& study) { + auto hasPrepro = [](auto c) { return (bool)c->prepro; }; + + const bool globalThermalTSgeneration = + study.parameters.timeSeriesToGenerate & timeSeriesThermal; + + if (!study.usedByTheSolver) + return true; - return std::ranges::all_of(allClusters_ | std::views::filter(hasPrepro), loadAndCheckPrepro); + return std::ranges::all_of( + allClusters_ | std::views::filter(hasPrepro), + [&globalThermalTSgeneration](auto& c) { + if (globalThermalTSgeneration && !c->prepro->validate()) { + return false; + } + + if (c->doWeGenerateTS(globalThermalTSgeneration)) { + return c->prepro->normalizeAndCheckNPO(); + } + return true; + }); } bool ThermalClusterList::loadEconomicCosts(Study& study, const AnyString& folder)