diff --git a/src/libs/antares/study/parts/short-term-storage/cluster.cpp b/src/libs/antares/study/parts/short-term-storage/cluster.cpp index 39de343f63..9b4efcb761 100644 --- a/src/libs/antares/study/parts/short-term-storage/cluster.cpp +++ b/src/libs/antares/study/parts/short-term-storage/cluster.cpp @@ -63,8 +63,16 @@ bool STStorageCluster::loadFromSection(const IniFile::Section& section) return true; } +bool STStorageCluster::enabled() const +{ + return properties.enabled; +} + bool STStorageCluster::validate() const { + if (!enabled()) + return true; + logs.debug() << "Validating properties and series for st storage: " << id; return properties.validate() && series->validate(); } @@ -76,9 +84,9 @@ bool STStorageCluster::loadSeries(const std::string& folder) const return ret; } -bool STStorageCluster::saveProperties(const std::string& path) const +void STStorageCluster::saveProperties(IniFile& ini) const { - return properties.saveToFolder(path); + properties.save(ini); } bool STStorageCluster::saveSeries(const std::string& path) const diff --git a/src/libs/antares/study/parts/short-term-storage/cluster.h b/src/libs/antares/study/parts/short-term-storage/cluster.h index cf2573ddd0..3c827c7d35 100644 --- a/src/libs/antares/study/parts/short-term-storage/cluster.h +++ b/src/libs/antares/study/parts/short-term-storage/cluster.h @@ -37,12 +37,13 @@ namespace Antares::Data::ShortTermStorage class STStorageCluster { public: + bool enabled() const; bool validate() const; - bool loadFromSection(const IniFile::Section& section); + bool loadFromSection(const IniFile::Section& section); bool loadSeries(const std::string& folder) const; - bool saveProperties(const std::string& path) const; + void saveProperties(IniFile& ini) const; bool saveSeries(const std::string& path) const; std::string id; diff --git a/src/libs/antares/study/parts/short-term-storage/container.cpp b/src/libs/antares/study/parts/short-term-storage/container.cpp index 707628bc88..5c8291a1f5 100644 --- a/src/libs/antares/study/parts/short-term-storage/container.cpp +++ b/src/libs/antares/study/parts/short-term-storage/container.cpp @@ -92,12 +92,15 @@ bool STStorageInput::saveToFolder(const std::string& folder) const { // create empty list.ini if there's no sts in this area Yuni::IO::Directory::Create(folder); - Yuni::IO::File::CreateEmptyFile(folder + SEP + "list.ini"); - logs.notice() << "created empty ini: " << folder + SEP + "list.ini"; + const std::string pathIni(folder + SEP + "list.ini"); + IniFile ini; - return std::all_of(storagesByIndex.cbegin(), storagesByIndex.cend(), [&folder](auto& storage) { - return storage.saveProperties(folder); + logs.debug() << "saving file " << pathIni; + std::for_each(storagesByIndex.cbegin(), storagesByIndex.cend(), [&ini](auto& storage) { + return storage.saveProperties(ini); }); + + return ini.save(pathIni); } bool STStorageInput::saveDataSeriesToFolder(const std::string& folder) const @@ -110,6 +113,22 @@ bool STStorageInput::saveDataSeriesToFolder(const std::string& folder) const std::size_t STStorageInput::count() const { - return storagesByIndex.size(); + return std::count_if(storagesByIndex.begin(), + storagesByIndex.end(), + [](const STStorageCluster& st) { + return st.properties.enabled; + }); } + +uint STStorageInput::removeDisabledClusters() +{ + const auto& it = std::remove_if(storagesByIndex.begin(), storagesByIndex.end(), + [](const auto& c) { return !c.enabled(); }); + + uint disabledCount = std::distance(it, storagesByIndex.end()); + storagesByIndex.erase(it, storagesByIndex.end()); + + return disabledCount; +} + } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/parts/short-term-storage/container.h b/src/libs/antares/study/parts/short-term-storage/container.h index 8fac3a8d50..b53aebf60b 100644 --- a/src/libs/antares/study/parts/short-term-storage/container.h +++ b/src/libs/antares/study/parts/short-term-storage/container.h @@ -36,12 +36,15 @@ class STStorageInput { public: bool validate() const; - // 1. Read list.ini + /// 1. Read list.ini bool createSTStorageClustersFromIniFile(const std::string& path); - // 2. Read ALL series + /// 2. Read ALL series bool loadSeriesFromFolder(const std::string& folder) const; - // Number of ST storages + /// Number of enabled ST storages, ignoring disabled ST storages std::size_t count() const; + /// erase disabled cluster from the vector + uint removeDisabledClusters(); + bool saveToFolder(const std::string& folder) const; bool saveDataSeriesToFolder(const std::string& folder) const; diff --git a/src/libs/antares/study/parts/short-term-storage/properties.cpp b/src/libs/antares/study/parts/short-term-storage/properties.cpp index 058a8b5f3a..cce442ce98 100644 --- a/src/libs/antares/study/parts/short-term-storage/properties.cpp +++ b/src/libs/antares/study/parts/short-term-storage/properties.cpp @@ -115,23 +115,14 @@ bool Properties::loadKey(const IniFile::Property* p) return false; } + if (p->key == "enabled") + return p->value.to(this->enabled); + return false; } -bool Properties::saveToFolder(const std::string& folder) const +void Properties::save(IniFile& ini) const { - const std::string pathIni(folder + SEP + "list.ini"); - - // Make sure the folder is created - if (!Yuni::IO::Directory::Create(folder)) - { - logs.warning() << "Couldn't create dir for sts: " << folder; - return false; - } - - logs.debug() << "saving file " << pathIni; - - IniFile ini; IniFile::Section* s = ini.addSection(this->name); s->add("name", this->name); @@ -147,9 +138,7 @@ bool Properties::saveToFolder(const std::string& folder) const s->add("efficiency", this->efficiencyFactor); s->add("initialleveloptim", this->initialLevelOptim); - - - return ini.save(pathIni); + s->add("enabled", this->enabled); } bool Properties::validate() diff --git a/src/libs/antares/study/parts/short-term-storage/properties.h b/src/libs/antares/study/parts/short-term-storage/properties.h index 58e79b1241..67c402f5fc 100644 --- a/src/libs/antares/study/parts/short-term-storage/properties.h +++ b/src/libs/antares/study/parts/short-term-storage/properties.h @@ -54,25 +54,28 @@ class Properties public: bool validate(); bool loadKey(const IniFile::Property* p); - bool saveToFolder(const std::string& folder) const; + void save(IniFile& ini) const; - // Not optional Injection nominal capacity, >= 0 + /// Not optional Injection nominal capacity, >= 0 std::optional injectionNominalCapacity; - // Not optional Withdrawal nominal capacity, >= 0 + /// Not optional Withdrawal nominal capacity, >= 0 std::optional withdrawalNominalCapacity; - // Not optional Reservoir capacity in MWh, >= 0 + /// Not optional Reservoir capacity in MWh, >= 0 std::optional reservoirCapacity; - // Initial level, <= 1 + /// Initial level, <= 1 double initialLevel = initiallevelDefault; - // Bool to optimise or not initial level + /// Bool to optimise or not initial level bool initialLevelOptim = false; - // Efficiency factor between 0 and 1 + /// Efficiency factor between 0 and 1 double efficiencyFactor = 1; - // Used to sort outputs + /// Used to sort outputs Group group = Group::Other1; - // cluster name + /// cluster name std::string name; + /// Enabled ? + bool enabled = true; + static const std::map ST_STORAGE_PROPERTY_GROUP_ENUM; private: static constexpr double initiallevelDefault = .5; diff --git a/src/libs/antares/study/runtime/runtime.cpp b/src/libs/antares/study/runtime/runtime.cpp index 039dbea843..32128178b1 100644 --- a/src/libs/antares/study/runtime/runtime.cpp +++ b/src/libs/antares/study/runtime/runtime.cpp @@ -293,6 +293,9 @@ bool StudyRuntimeInfos::loadFromStudy(Study& study) // Removing disabled thermal clusters from solver computations removeDisabledThermalClustersFromSolverComputations(study); + // Removing disabled short-term storage objects from solver computations + removeDisabledShortTermStorageClustersFromSolverComputations(study); + switch (gd.renewableGeneration()) { case rgClusters: @@ -423,6 +426,13 @@ void StudyRuntimeInfos::removeDisabledRenewableClustersFromSolverComputations(St }); } +void StudyRuntimeInfos::removeDisabledShortTermStorageClustersFromSolverComputations(Study& study) +{ + removeClusters( + study, "short term storage", [](Area& area) + { return area.shortTermStorage.removeDisabledClusters(); }); +} + void StudyRuntimeInfos::removeAllRenewableClustersFromSolverComputations(Study& study) { removeClusters( diff --git a/src/libs/antares/study/runtime/runtime.h b/src/libs/antares/study/runtime/runtime.h index bd44257269..e70f9f223a 100644 --- a/src/libs/antares/study/runtime/runtime.h +++ b/src/libs/antares/study/runtime/runtime.h @@ -138,6 +138,7 @@ class StudyRuntimeInfos void initializeThermalClustersInMustRunMode(Study& study) const; void removeDisabledThermalClustersFromSolverComputations(Study& study); void removeDisabledRenewableClustersFromSolverComputations(Study& study); + void removeDisabledShortTermStorageClustersFromSolverComputations(Study& study); void removeAllRenewableClustersFromSolverComputations(Study& study); void disableAllFilters(Study& study); void checkThermalTSGeneration(Study& study); diff --git a/src/solver/variable/economy/STStorageCashFlowByCluster.h b/src/solver/variable/economy/STStorageCashFlowByCluster.h index bbde229c33..1d85df5cb5 100644 --- a/src/solver/variable/economy/STStorageCashFlowByCluster.h +++ b/src/solver/variable/economy/STStorageCashFlowByCluster.h @@ -278,14 +278,16 @@ class STstorageCashFlowByCluster : public Variable::IVariableshortTermStorage; // Write the data for the current year - for (uint clusterIndex = 0; clusterIndex < nbClusters_; ++clusterIndex) + uint clusterIndex = 0; + for (const auto& cluster : shortTermStorage.storagesByIndex) { // Write the data for the current year - const auto& cluster = shortTermStorage.storagesByIndex[clusterIndex]; results.variableCaption = cluster.properties.name; results.variableUnit = VCardType::Unit(); pValuesForTheCurrentYear[numSpace][clusterIndex] .template buildAnnualSurveyReport(results, fileLevel, precision); + + clusterIndex++; } } } diff --git a/src/solver/variable/economy/STStorageInjectionByCluster.h b/src/solver/variable/economy/STStorageInjectionByCluster.h index 280c179b89..07d9858481 100644 --- a/src/solver/variable/economy/STStorageInjectionByCluster.h +++ b/src/solver/variable/economy/STStorageInjectionByCluster.h @@ -280,14 +280,16 @@ class STstorageInjectionByCluster : public Variable::IVariableshortTermStorage; // Write the data for the current year - for (uint clusterIndex = 0; clusterIndex < nbClusters_; ++clusterIndex) + uint clusterIndex = 0; + for (const auto& cluster : shortTermStorage.storagesByIndex) { // Write the data for the current year - const auto& cluster = shortTermStorage.storagesByIndex[clusterIndex]; results.variableCaption = cluster.properties.name; results.variableUnit = VCardType::Unit(); pValuesForTheCurrentYear[numSpace][clusterIndex] .template buildAnnualSurveyReport(results, fileLevel, precision); + + clusterIndex++; } } } diff --git a/src/solver/variable/economy/STStorageLevelsByCluster.h b/src/solver/variable/economy/STStorageLevelsByCluster.h index 4ae6ddc09b..6c8cc2bba1 100644 --- a/src/solver/variable/economy/STStorageLevelsByCluster.h +++ b/src/solver/variable/economy/STStorageLevelsByCluster.h @@ -280,14 +280,16 @@ class STstorageLevelsByCluster const auto& shortTermStorage = results.data.area->shortTermStorage; // Write the data for the current year - for (uint clusterIndex = 0; clusterIndex < nbClusters_; ++clusterIndex) + uint clusterIndex = 0; + for (const auto& cluster : shortTermStorage.storagesByIndex) { // Write the data for the current year - const auto& cluster = shortTermStorage.storagesByIndex[clusterIndex]; results.variableCaption = cluster.properties.name; results.variableUnit = VCardType::Unit(); pValuesForTheCurrentYear[numSpace][clusterIndex].template buildAnnualSurveyReport( results, fileLevel, precision); + + clusterIndex++; } } } diff --git a/src/solver/variable/economy/STStorageWithdrawalByCluster.h b/src/solver/variable/economy/STStorageWithdrawalByCluster.h index bc39d8c70b..277b133088 100644 --- a/src/solver/variable/economy/STStorageWithdrawalByCluster.h +++ b/src/solver/variable/economy/STStorageWithdrawalByCluster.h @@ -280,14 +280,16 @@ class STstorageWithdrawalByCluster const auto& shortTermStorage = results.data.area->shortTermStorage; // Write the data for the current year - for (uint clusterIndex = 0; clusterIndex < nbClusters_; ++clusterIndex) + uint clusterIndex = 0; + for (const auto& cluster : shortTermStorage.storagesByIndex) { // Write the data for the current year - const auto& cluster = shortTermStorage.storagesByIndex[clusterIndex]; results.variableCaption = cluster.properties.name; results.variableUnit = VCardType::Unit(); pValuesForTheCurrentYear[numSpace][clusterIndex].template buildAnnualSurveyReport( results, fileLevel, precision); + + clusterIndex++; } } } diff --git a/src/solver/variable/economy/shortTermStorage.h b/src/solver/variable/economy/shortTermStorage.h index 77aab56a7a..0b9370ddd3 100644 --- a/src/solver/variable/economy/shortTermStorage.h +++ b/src/solver/variable/economy/shortTermStorage.h @@ -235,22 +235,25 @@ class ShortTermStorageByGroup void hourForEachArea(State& state, unsigned int numSpace) { using namespace Antares::Data::ShortTermStorage; - for (uint stsIndex = 0; stsIndex < state.area->shortTermStorage.count(); stsIndex++) + const auto& shortTermStorage = state.area->shortTermStorage; + + // Write the data for the current year + uint clusterIndex = 0; + for (const auto& cluster : shortTermStorage.storagesByIndex) { - const auto& cluster = state.area->shortTermStorage.storagesByIndex[stsIndex]; const uint group = groupIndex(cluster.properties.group); - // Injection pValuesForTheCurrentYear[numSpace][3 * group][state.hourInTheYear] - += state.hourlyResults->ShortTermStorage[state.hourInTheWeek].injection[stsIndex]; + += state.hourlyResults->ShortTermStorage[state.hourInTheWeek].injection[clusterIndex]; // Withdrawal pValuesForTheCurrentYear[numSpace][3 * group + 1][state.hourInTheYear] - += state.hourlyResults->ShortTermStorage[state.hourInTheWeek].withdrawal[stsIndex]; + += state.hourlyResults->ShortTermStorage[state.hourInTheWeek].withdrawal[clusterIndex]; // Levels pValuesForTheCurrentYear[numSpace][3 * group + 2][state.hourInTheYear] - += state.hourlyResults->ShortTermStorage[state.hourInTheWeek].level[stsIndex]; + += state.hourlyResults->ShortTermStorage[state.hourInTheWeek].level[clusterIndex]; + clusterIndex++; } // Next item in the list diff --git a/src/tests/src/libs/antares/study/short-term-storage-input/CMakeLists.txt b/src/tests/src/libs/antares/study/short-term-storage-input/CMakeLists.txt index 458fa7b28f..045e8ed944 100644 --- a/src/tests/src/libs/antares/study/short-term-storage-input/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/short-term-storage-input/CMakeLists.txt @@ -5,7 +5,7 @@ set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") # Tests on reading scenario-builder # ==================================== set(SRC_SC_BUILDER_READ - short-term-storage-input.cpp + short-term-storage-input-output.cpp ) add_executable(short-term-storage-input ${SRC_SC_BUILDER_READ}) diff --git a/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input.cpp b/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp similarity index 90% rename from src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input.cpp rename to src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp index 1cd15334f4..fa73cc6306 100644 --- a/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input.cpp +++ b/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp @@ -15,6 +15,7 @@ using namespace std; using namespace Antares::Data; +namespace { std::string getFolder() { std::filesystem::path tmpDir = std::filesystem::temp_directory_path(); @@ -76,7 +77,7 @@ void createFileSeries(unsigned int size) createIndividualFileSeries(folder + SEP + "upper-rule-curve.txt", size); } -void createIniFile() +void createIniFile(bool enabled) { std::string folder = getFolder(); @@ -91,7 +92,7 @@ void createIniFile() outfile << "reservoircapacity = 31200.000000" << std::endl; outfile << "efficiency = 0.75" << std::endl; outfile << "initiallevel = 0.50000" << std::endl; - + outfile << "enabled = " << (enabled ? "true" : "false") << std::endl; outfile.close(); } @@ -129,6 +130,7 @@ void removeIniFile() std::string folder = getFolder(); std::filesystem::remove(folder + SEP + "list.ini"); } +} // ================= // The fixture @@ -244,12 +246,32 @@ BOOST_FIXTURE_TEST_CASE(check_cluster_series_load_vector, Fixture) && cluster.series->lowerRuleCurve[6392] == 0.5); } -BOOST_FIXTURE_TEST_CASE(check_container_properties_load, Fixture) +BOOST_FIXTURE_TEST_CASE(check_container_properties_enabled_load, Fixture) { - createIniFile(); + createIniFile(true); BOOST_CHECK(container.createSTStorageClustersFromIniFile(folder)); - BOOST_CHECK(container.storagesByIndex[0].properties.validate()); + + auto& properties = container.storagesByIndex[0].properties; + + BOOST_CHECK(properties.enabled); + BOOST_CHECK_EQUAL(container.count(), 1); + BOOST_CHECK(properties.validate()); + + removeIniFile(); +} + +BOOST_FIXTURE_TEST_CASE(check_container_properties_disabled_load, Fixture) +{ + createIniFile(false); + + BOOST_CHECK(container.createSTStorageClustersFromIniFile(folder)); + + auto& properties = container.storagesByIndex[0].properties; + + BOOST_CHECK(!properties.enabled); + BOOST_CHECK_EQUAL(container.count(), 0); + BOOST_CHECK(properties.validate()); removeIniFile(); } @@ -275,7 +297,7 @@ BOOST_FIXTURE_TEST_CASE(check_container_properties_empty_file, Fixture) BOOST_FIXTURE_TEST_CASE(check_file_save, Fixture) { - createIniFile(); + createIniFile(true); BOOST_CHECK(container.createSTStorageClustersFromIniFile(folder));