diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 2c73097adb..226e8a01b0 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -171,6 +171,7 @@ jobs: cd _build ctest -C Release --output-on-failure -L "unit|end-to-end" + - name: Upload logs for failed tests if: ${{ failure() }} uses: actions/upload-artifact@v4 @@ -195,6 +196,23 @@ jobs: variant: "milp-cbc" os: ${{ env.os }} + - name: Run tests on adequacy patch (CSR) + if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{ env.SIMTEST }} + batch-name: adequacy-patch-CSR + os: ${{ env.os }} + + - name: Run parallel tests + if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{ env.SIMTEST }} + batch-name: valid-parallel + os: ${{ env.os }} + variant: "parallel" + - name: Run tests introduced in 8.6.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} uses: ./.github/workflows/run-tests @@ -248,14 +266,6 @@ jobs: batch-name: valid-mps os: ${{ env.os }} - - name: Run tests for adequacy patch (CSR) - if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} - uses: ./.github/workflows/run-tests - with: - simtest-tag: ${{ env.SIMTEST }} - batch-name: adequacy-patch-CSR - os: ${{ env.os }} - - name: Run parallel tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' && !cancelled() }} uses: ./.github/workflows/run-tests diff --git a/simtest.json b/simtest.json index 11b8cee038..4f6f12a31e 100644 --- a/simtest.json +++ b/simtest.json @@ -1,3 +1,3 @@ { - "version": "v9.2.0g" + "version": "v9.2.0h" } diff --git a/sonar-project.properties b/sonar-project.properties index 0c74bd252a..d4ba8cce37 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,3 +1,24 @@ +# +# Copyright 2007-2024, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Antares-Simulator, +# Adequacy and Performance assessment for interconnected energy networks. +# +# Antares_Simulator is free software: you can redistribute it and/or modify +# it under the terms of the Mozilla Public Licence 2.0 as published by +# the Mozilla Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# 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 +# Mozilla Public Licence 2.0 for more details. +# +# You should have received a copy of the Mozilla Public Licence 2.0 +# along with Antares_Simulator. If not, see . +# + sonar.projectName=Antares_Simulator sonar.projectKey=AntaresSimulatorTeam_Antares_Simulator sonar.organization=antaressimulatorteam diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56dc5e2bf8..1f0a6712b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,10 +5,9 @@ set(ANTARES_VERSION_HI 9) set(ANTARES_VERSION_LO 2) set(ANTARES_VERSION_REVISION 0) - # Beta release set(ANTARES_BETA 0) -set(ANTARES_RC 7) +set(ANTARES_RC 8) set(ANTARES_VERSION_YEAR 2024) @@ -192,6 +191,7 @@ endif() find_package(Boost REQUIRED) find_package(antlr4-runtime REQUIRED) +find_package(yaml-cpp REQUIRED) #Sirius solver if(POLICY CMP0074) diff --git a/src/api/API.cpp b/src/api/API.cpp index 7bcda34502..63b4a9fda8 100644 --- a/src/api/API.cpp +++ b/src/api/API.cpp @@ -34,6 +34,7 @@ namespace Antares::API { SimulationResults APIInternal::run( const IStudyLoader& study_loader, + const std::filesystem::path& output, const Antares::Solver::Optimization::OptimizationOptions& optOptions) { try @@ -43,9 +44,9 @@ SimulationResults APIInternal::run( catch (const ::Antares::Error::StudyFolderDoesNotExist& e) { Antares::API::Error err{.reason = e.what()}; - return {.simulationPath = "", .antares_problems = {}, .error = err}; + return {.antares_problems = {}, .error = err}; } - return execute(optOptions); + return execute(output, optOptions); } /** @@ -56,6 +57,7 @@ SimulationResults APIInternal::run( * dupllication */ SimulationResults APIInternal::execute( + const std::filesystem::path& output, const Antares::Solver::Optimization::OptimizationOptions& optOptions) const { // study_ == nullptr e.g when the -h flag is given @@ -63,7 +65,7 @@ SimulationResults APIInternal::execute( { using namespace std::string_literals; Antares::API::Error err{.reason = "Couldn't create study"s}; - return {.simulationPath{}, .antares_problems{}, .error = err}; + return {.antares_problems{}, .error = err}; } Settings settings; @@ -75,10 +77,19 @@ SimulationResults APIInternal::execute( auto ioQueueService = std::make_shared(); ioQueueService->maximumThreadCount(1); ioQueueService->start(); + + study_->folderOutput = output; auto resultWriter = Solver::resultWriterFactory(parameters.resultFormat, study_->folderOutput, ioQueueService, durationCollector); + + // In some cases (e.g tests) we don't want to write anything + if (!output.empty()) + { + study_->saveAboutTheStudy(*resultWriter); + } + SimulationObserver simulationObserver; optimizationInfo = simulationRun(*study_, @@ -90,8 +101,6 @@ SimulationResults APIInternal::execute( // Importing Time-Series if asked study_->importTimeseriesIntoInput(); - return {.simulationPath = study_->folderOutput, - .antares_problems = simulationObserver.acquireLps(), - .error{}}; + return {.antares_problems = simulationObserver.acquireLps(), .error{}}; } } // namespace Antares::API diff --git a/src/api/include/antares/api/SimulationResults.h b/src/api/include/antares/api/SimulationResults.h index 5a5e93982a..31f0e9aec0 100644 --- a/src/api/include/antares/api/SimulationResults.h +++ b/src/api/include/antares/api/SimulationResults.h @@ -23,6 +23,7 @@ #include #include #include + #include "antares/solver/lps/LpsFromAntares.h" namespace Antares::API @@ -31,7 +32,8 @@ namespace Antares::API * @struct Error * @brief The Error structure is used to represent an error that occurred during the simulation. */ -struct Error { +struct Error +{ /** * @brief The reason for the error. */ @@ -45,10 +47,6 @@ struct Error { */ struct [[nodiscard("Contains results and potential error")]] SimulationResults { - /** - * @brief The path to the simulation (output). - */ - std::filesystem::path simulationPath; /** * @brief weekly problems */ @@ -59,4 +57,4 @@ struct [[nodiscard("Contains results and potential error")]] SimulationResults std::optional error; }; -} \ No newline at end of file +} // namespace Antares::API diff --git a/src/api/include/antares/api/solver.h b/src/api/include/antares/api/solver.h index a8279c00c8..64fa13ac55 100644 --- a/src/api/include/antares/api/solver.h +++ b/src/api/include/antares/api/solver.h @@ -36,5 +36,6 @@ namespace Antares::API */ SimulationResults PerformSimulation( const std::filesystem::path& study_path, + const std::filesystem::path& output, const Antares::Solver::Optimization::OptimizationOptions& optOptions) noexcept; } // namespace Antares::API diff --git a/src/api/private/API.h b/src/api/private/API.h index 3561f6d21b..c52c3304bf 100644 --- a/src/api/private/API.h +++ b/src/api/private/API.h @@ -48,11 +48,13 @@ class APIInternal * @return SimulationResults object which contains the results of the simulation. */ SimulationResults run(const IStudyLoader& study_loader, + const std::filesystem::path& output, const Antares::Solver::Optimization::OptimizationOptions& optOptions); private: std::shared_ptr study_; SimulationResults execute( + const std::filesystem::path& output, const Antares::Solver::Optimization::OptimizationOptions& optOptions) const; }; diff --git a/src/api/solver.cpp b/src/api/solver.cpp index 4e65696f8a..ac6810e7f1 100644 --- a/src/api/solver.cpp +++ b/src/api/solver.cpp @@ -30,18 +30,19 @@ namespace Antares::API SimulationResults PerformSimulation( const std::filesystem::path& study_path, + const std::filesystem::path& output, const Antares::Solver::Optimization::OptimizationOptions& optOptions) noexcept { try { APIInternal api; FileTreeStudyLoader study_loader(study_path); - return api.run(study_loader, optOptions); + return api.run(study_loader, output, optOptions); } catch (const std::exception& e) { Antares::API::Error err{.reason = e.what()}; - return SimulationResults{.simulationPath = study_path, .antares_problems{}, .error = err}; + return SimulationResults{.antares_problems{}, .error = err}; } } diff --git a/src/api_client_example/src/API_client.cpp b/src/api_client_example/src/API_client.cpp index 9cb19d8e59..8ebba07042 100644 --- a/src/api_client_example/src/API_client.cpp +++ b/src/api_client_example/src/API_client.cpp @@ -23,7 +23,8 @@ #include -Antares::API::SimulationResults solve(std::filesystem::path study_path) +Antares::API::SimulationResults solve(std::filesystem::path study_path, + std::filesystem::path output) { - return Antares::API::PerformSimulation(std::move(study_path), {}); + return Antares::API::PerformSimulation(std::move(study_path), std::move(output), {}); } diff --git a/src/api_client_example/src/API_client.h b/src/api_client_example/src/API_client.h index 5d8500649e..7f77a26e7f 100644 --- a/src/api_client_example/src/API_client.h +++ b/src/api_client_example/src/API_client.h @@ -22,7 +22,8 @@ #pragma once -#include #include +#include -Antares::API::SimulationResults solve(std::filesystem::path study_path); +Antares::API::SimulationResults solve(std::filesystem::path study_path, + std::filesystem::path output); diff --git a/src/api_client_example/tests/test.cpp b/src/api_client_example/tests/test.cpp index 4adee7bfd6..bd5b3d86a8 100644 --- a/src/api_client_example/tests/test.cpp +++ b/src/api_client_example/tests/test.cpp @@ -22,10 +22,12 @@ #define BOOST_TEST_MODULE test_client_api #include + #include "API_client.h" -BOOST_AUTO_TEST_CASE(test_run) { - auto results = solve("dummy_study_test_client_api"); +BOOST_AUTO_TEST_CASE(test_run) +{ + auto results = solve("dummy_study_test_client_api", {}); BOOST_CHECK(results.error); BOOST_CHECK(!results.error->reason.empty()); auto c = results.error->reason; @@ -34,4 +36,4 @@ BOOST_AUTO_TEST_CASE(test_run) { BOOST_CHECK(results.error->reason.find("folder") != std::string::npos); BOOST_CHECK(results.error->reason.find("not") != std::string::npos); BOOST_CHECK(results.error->reason.find("exist") != std::string::npos); -} \ No newline at end of file +} diff --git a/src/ext/yuni/src/yuni/core/nonmovable.h b/src/ext/yuni/src/yuni/core/nonmovable.h index e9214e709f..c866e916ec 100644 --- a/src/ext/yuni/src/yuni/core/nonmovable.h +++ b/src/ext/yuni/src/yuni/core/nonmovable.h @@ -49,11 +49,12 @@ class YUNI_DECL NonMovable { protected: //! Default constructor - NonCopyable() + NonMovable() { } + //! Protected non-virtual destructor - ~NonCopyable() + ~NonMovable() { } }; diff --git a/src/format-code.sh b/src/format-code.sh index abac85a423..752080ca58 100755 --- a/src/format-code.sh +++ b/src/format-code.sh @@ -3,7 +3,7 @@ if [ $# -eq 0 ] then # No arguments: format all - SOURCE_DIRS="analyzer/ libs/ solver/ tools/ config/ tests/ packaging/" + SOURCE_DIRS="analyzer/ libs/ solver/ tools/ config/ tests/ packaging/ api/" SOURCE_FILES=$(find $SOURCE_DIRS -regextype egrep -regex ".*/*\.(c|cxx|cpp|cc|h|hxx|hpp)$" ! -path '*/antlr-interface/*') else # Format files provided as arguments diff --git a/src/libs/antares/exception/LoadingError.cpp b/src/libs/antares/exception/LoadingError.cpp index cea447153e..32f4b3b1c1 100644 --- a/src/libs/antares/exception/LoadingError.cpp +++ b/src/libs/antares/exception/LoadingError.cpp @@ -96,9 +96,8 @@ InvalidSolverSpecificParameters::InvalidSolverSpecificParameters(const std::stri { } -InvalidStudy::InvalidStudy(const Yuni::String& study): - LoadingError(std::string("The folder `") + study.c_str() - + "` does not seem to be a valid study") +InvalidStudy::InvalidStudy(const std::string& study): + LoadingError(std::string("The folder `") + study + "` does not seem to be a valid study") { } diff --git a/src/libs/antares/exception/include/antares/exception/LoadingError.hpp b/src/libs/antares/exception/include/antares/exception/LoadingError.hpp index d70820e5e9..9a5ff14002 100644 --- a/src/libs/antares/exception/include/antares/exception/LoadingError.hpp +++ b/src/libs/antares/exception/include/antares/exception/LoadingError.hpp @@ -140,7 +140,7 @@ class InvalidSolverSpecificParameters: public LoadingError class InvalidStudy: public LoadingError { public: - explicit InvalidStudy(const Yuni::String& study); + explicit InvalidStudy(const std::string& study); }; class NoStudyProvided: public LoadingError diff --git a/src/libs/antares/io/CMakeLists.txt b/src/libs/antares/io/CMakeLists.txt index 9e3211e14b..dc62a95aea 100644 --- a/src/libs/antares/io/CMakeLists.txt +++ b/src/libs/antares/io/CMakeLists.txt @@ -12,6 +12,7 @@ source_group("io" FILES ${SRC_IO}) add_library(io ${SRC_IO} ) +add_library(Antares::io ALIAS io) target_link_libraries(io PRIVATE @@ -26,4 +27,4 @@ target_include_directories(io install(DIRECTORY include/antares DESTINATION "include" -) \ No newline at end of file +) diff --git a/src/libs/antares/io/include/antares/io/file.h b/src/libs/antares/io/include/antares/io/file.h index b401b1cece..a9aed8c850 100644 --- a/src/libs/antares/io/include/antares/io/file.h +++ b/src/libs/antares/io/include/antares/io/file.h @@ -22,8 +22,7 @@ #define __LIBS_ANTARES_IO_FILE_H__ #include - -#include +#include namespace Antares::IO { diff --git a/src/libs/antares/study/CMakeLists.txt b/src/libs/antares/study/CMakeLists.txt index 29a89fc631..e7336d020c 100644 --- a/src/libs/antares/study/CMakeLists.txt +++ b/src/libs/antares/study/CMakeLists.txt @@ -74,8 +74,8 @@ set(SRC_STUDY_PART_THERMAL include/antares/study/parts/thermal/cost_provider.h include/antares/study/parts/thermal/cluster.hxx parts/thermal/cluster.cpp - parts/thermal/scenarized_cost_provider.cpp - parts/thermal/constant_cost_provider.cpp + parts/thermal/scenarized_cost_provider.cpp + parts/thermal/constant_cost_provider.cpp include/antares/study/parts/thermal/cluster_list.h parts/thermal/cluster_list.cpp include/antares/study/parts/thermal/pollutant.h @@ -102,7 +102,9 @@ set(SRC_STUDY_PART_SHORT_TERM_STORAGE parts/short-term-storage/series.cpp include/antares/study/parts/short-term-storage/series.h include/antares/study/parts/short-term-storage/cluster.h + include/antares/study/parts/short-term-storage/AdditionalConstraint.h parts/short-term-storage/cluster.cpp + parts/short-term-storage/AdditionalConstraint.cpp ) source_group("study\\part\\short-term-storage" FILES ${SRC_STUDY_PART_SHORT_TERM_SOTRAGE}) @@ -306,12 +308,12 @@ target_link_libraries(study ) target_include_directories(study - PUBLIC + PUBLIC $ # Make more than just study visible but it's the lesser evil for now ) -install(DIRECTORY include/antares +install(DIRECTORY include/antares DESTINATION "include" ) diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index cf2624515f..7af3a6a5a7 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -1199,6 +1199,7 @@ bool AreaList::loadFromFolder(const StudyLoadOptions& options) fs::path folder = stsFolder / "clusters" / area->id.c_str(); ret = area->shortTermStorage.createSTStorageClustersFromIniFile(folder) && ret; + ret = area->shortTermStorage.LoadConstraintsFromIniFile(folder) && ret; } } else diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/AdditionalConstraint.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/AdditionalConstraint.h new file mode 100644 index 0000000000..e16b991a05 --- /dev/null +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/AdditionalConstraint.h @@ -0,0 +1,53 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once +#include +#include + +namespace Antares::Data::ShortTermStorage +{ + +struct AdditionalConstraint +{ + std::string name; + std::string cluster_id; + std::string variable; + std::string operatorType; + std::set hours; + double rhs; + + unsigned int globalIndex = 0; + + struct ValidateResult + { + bool ok; + std::string error_msg; + }; + + ValidateResult validate() const; + +private: + bool isValidVariable() const; + bool isValidOperatorType() const; + bool isValidHoursRange() const; +}; +} // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h index b4074a28c8..df74a350b0 100644 --- a/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h @@ -26,6 +26,7 @@ #include +#include "AdditionalConstraint.h" #include "properties.h" #include "series.h" @@ -35,17 +36,21 @@ class STStorageCluster { public: bool enabled() const; + bool validate() const; bool loadFromSection(const IniFile::Section& section); + bool loadSeries(const std::filesystem::path& folder) const; void saveProperties(IniFile& ini) const; + bool saveSeries(const std::string& path) const; std::string id; std::shared_ptr series = std::make_shared(); mutable Properties properties; + std::vector additional_constraints; }; } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h index 90f8fafbce..d4e0233b5c 100644 --- a/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h @@ -21,9 +21,9 @@ #pragma once #include -#include #include +#include "AdditionalConstraint.h" #include "cluster.h" namespace Antares::Data::ShortTermStorage @@ -32,18 +32,28 @@ class STStorageInput { public: bool validate() const; + /// 1. Read list.ini bool createSTStorageClustersFromIniFile(const std::filesystem::path& path); + /// 2. Read ALL series bool loadSeriesFromFolder(const std::filesystem::path& folder) const; + /// Number of enabled ST storages, ignoring disabled ST storages std::size_t count() const; + + bool LoadConstraintsFromIniFile(const std::filesystem::path& filePath); + /// erase disabled cluster from the vector uint removeDisabledClusters(); bool saveToFolder(const std::string& folder) const; + bool saveDataSeriesToFolder(const std::string& folder) const; std::vector storagesByIndex; + + /// Number cumulative - constraint + std::size_t cumulativeConstraintCount() const; }; } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/include/antares/study/runtime/runtime.h b/src/libs/antares/study/include/antares/study/runtime/runtime.h index 858719ab27..fe394199df 100644 --- a/src/libs/antares/study/include/antares/study/runtime/runtime.h +++ b/src/libs/antares/study/include/antares/study/runtime/runtime.h @@ -110,6 +110,7 @@ class StudyRuntimeInfos uint thermalPlantTotalCountMustRun; uint shortTermStorageCount = 0; + uint shortTermStorageCumulativeConstraintCount = 0; //! Override enable/disable TS generation per cluster bool thermalTSRefresh = false; diff --git a/src/libs/antares/study/parameters/adq-patch-params.cpp b/src/libs/antares/study/parameters/adq-patch-params.cpp index ebf6de321e..800f2d19f8 100644 --- a/src/libs/antares/study/parameters/adq-patch-params.cpp +++ b/src/libs/antares/study/parameters/adq-patch-params.cpp @@ -146,6 +146,10 @@ void AdqPatchParams::addExcludedVariables(std::vector& out) const out.emplace_back("LMR VIOL."); out.emplace_back("UNSP. ENRG CSR"); out.emplace_back("DTG MRG CSR"); + out.emplace_back("LOLD CSR"); + out.emplace_back("LOLP CSR"); + out.emplace_back("MAX MRG CSR"); + out.emplace_back("OV. COST CSR"); } } diff --git a/src/libs/antares/study/parts/short-term-storage/AdditionalConstraint.cpp b/src/libs/antares/study/parts/short-term-storage/AdditionalConstraint.cpp new file mode 100644 index 0000000000..2ca904041c --- /dev/null +++ b/src/libs/antares/study/parts/short-term-storage/AdditionalConstraint.cpp @@ -0,0 +1,65 @@ +/* +** Copyright 2007-2024, RTE (https://www.rte-france.com) +** See AUTHORS.txt +** SPDX-License-Identifier: MPL-2.0 +** This file is part of Antares-Simulator, +** Adequacy and Performance assessment for interconnected energy networks. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the Mozilla Public Licence 2.0 as published by +** the Mozilla Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** 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 +** Mozilla Public Licence 2.0 for more details. +** +** You should have received a copy of the Mozilla Public Licence 2.0 +** along with Antares_Simulator. If not, see . +*/ +#include "antares/study/parts/short-term-storage/AdditionalConstraint.h" + +namespace Antares::Data::ShortTermStorage +{ +AdditionalConstraint::ValidateResult AdditionalConstraint::validate() const +{ + if (cluster_id.empty()) + { + return {false, "Cluster ID is empty."}; + } + + if (!isValidVariable()) + { + return {false, "Invalid variable type. Must be 'injection', 'withdrawal', or 'netting'."}; + } + + if (!isValidOperatorType()) + { + return {false, "Invalid operator type. Must be 'less', 'equal', or 'greater'."}; + } + + if (!isValidHoursRange()) + { + return {false, "Hours set contains invalid values. Must be between 1 and 168."}; + } + + return {true, ""}; +} + +bool AdditionalConstraint::isValidHoursRange() const +{ + // `hours` is a sorted set; begin() gives the smallest and prev(end()) gives the largest. + return !hours.empty() && *hours.begin() >= 1 && *std::prev(hours.end()) <= 168; +} + +bool AdditionalConstraint::isValidVariable() const +{ + return variable == "injection" || variable == "withdrawal" || variable == "netting"; +} + +bool AdditionalConstraint::isValidOperatorType() const +{ + return operatorType == "less" || operatorType == "equal" || operatorType == "greater"; +} +} // namespace Antares::Data::ShortTermStorage 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 787d1c85e6..cbfb7e679f 100644 --- a/src/libs/antares/study/parts/short-term-storage/cluster.cpp +++ b/src/libs/antares/study/parts/short-term-storage/cluster.cpp @@ -28,7 +28,6 @@ namespace Antares::Data::ShortTermStorage { - bool STStorageCluster::loadFromSection(const IniFile::Section& section) { if (!section.firstProperty) @@ -92,5 +91,4 @@ bool STStorageCluster::saveSeries(const std::string& path) const { return series->saveToFolder(path); } - } // namespace Antares::Data::ShortTermStorage 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 bee1546ec4..39a958c5c7 100644 --- a/src/libs/antares/study/parts/short-term-storage/container.cpp +++ b/src/libs/antares/study/parts/short-term-storage/container.cpp @@ -22,11 +22,13 @@ #include "antares/study/parts/short-term-storage/container.h" #include +#include #include #include #include +#include #define SEP Yuni::IO::Separator @@ -73,6 +75,83 @@ bool STStorageInput::createSTStorageClustersFromIniFile(const fs::path& path) return true; } +bool STStorageInput::LoadConstraintsFromIniFile(const fs::path& parent_path) +{ + IniFile ini; + const auto pathIni = parent_path / "additional-constraints.ini"; + if (!ini.open(pathIni, false)) + { + logs.info() << "There is no: " << pathIni; + return true; + } + + for (auto* section = ini.firstSection; section; section = section->next) + { + AdditionalConstraint constraint; + constraint.name = section->name.c_str(); + for (auto* property = section->firstProperty; property; property = property->next) + { + const std::string key = property->key; + const auto value = property->value; + + if (key == "cluster") + { + // TODO do i have to transform the name to id? TransformNameIntoID + std::string clusterName; + value.to(clusterName); + constraint.cluster_id = transformNameIntoID(clusterName); + } + else if (key == "variable") + { + value.to(constraint.variable); + } + else if (key == "operator") + { + value.to(constraint.operatorType); + } + else if (key == "hours") + { + std::stringstream ss(value.c_str()); + std::string hour; + while (std::getline(ss, hour, ',')) + { + int hourVal = std::stoi(hour); + constraint.hours.insert(hourVal); + } + } + else if (key == "rhs") + { + property->value.to(constraint.rhs); + } + } + + if (auto ret = constraint.validate(); !ret.ok) + { + logs.error() << "Invalid constraint in section: " << section->name; + logs.error() << ret.error_msg; + return false; + } + + auto it = std::find_if(storagesByIndex.begin(), + storagesByIndex.end(), + [&constraint](const STStorageCluster& cluster) + { return cluster.id == constraint.cluster_id; }); + if (it == storagesByIndex.end()) + { + logs.warning() << " from file " << pathIni; + logs.warning() << "Constraint " << section->name + << " does not reference an existing cluster"; + return false; + } + else + { + it->additional_constraints.push_back(constraint); + } + } + + return true; +} + bool STStorageInput::loadSeriesFromFolder(const fs::path& folder) const { if (folder.empty()) @@ -113,6 +192,15 @@ bool STStorageInput::saveDataSeriesToFolder(const std::string& folder) const { return storage.saveSeries(folder + SEP + storage.id); }); } +std::size_t STStorageInput::cumulativeConstraintCount() const +{ + return std::accumulate(storagesByIndex.begin(), + storagesByIndex.end(), + 0, + [](int acc, const auto& cluster) + { return acc + cluster.additional_constraints.size(); }); +} + std::size_t STStorageInput::count() const { return std::ranges::count_if(storagesByIndex, @@ -123,5 +211,4 @@ uint STStorageInput::removeDisabledClusters() { return std::erase_if(storagesByIndex, [](const auto& c) { return !c.enabled(); }); } - } // namespace Antares::Data::ShortTermStorage 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 3805b239a2..cbed02122e 100644 --- a/src/libs/antares/study/parts/short-term-storage/properties.cpp +++ b/src/libs/antares/study/parts/short-term-storage/properties.cpp @@ -213,6 +213,12 @@ bool Properties::validate() << " should be inferior to 1, value has been set to " << initialLevel; } + if (groupName.empty()) + { + logs.warning() << "Group name is empty for short term storage " << name; + return false; + } + return true; } diff --git a/src/libs/antares/study/parts/thermal/cluster.cpp b/src/libs/antares/study/parts/thermal/cluster.cpp index 96b2d87826..cfd16f521e 100644 --- a/src/libs/antares/study/parts/thermal/cluster.cpp +++ b/src/libs/antares/study/parts/thermal/cluster.cpp @@ -298,6 +298,11 @@ void Data::ThermalCluster::calculationOfSpinning() void Data::ThermalCluster::reverseCalculationOfSpinning() { + if (tsGenBehavior == LocalTSGenerationBehavior::forceNoGen) + { + return; + } + // Nothing to do if the spinning is equal to zero // because it will the same multiply all entries of the matrix by 1. if (Utils::isZero(spinning)) diff --git a/src/libs/antares/study/runtime/runtime.cpp b/src/libs/antares/study/runtime/runtime.cpp index 3bca4404a5..32bfb0f28e 100644 --- a/src/libs/antares/study/runtime/runtime.cpp +++ b/src/libs/antares/study/runtime/runtime.cpp @@ -94,6 +94,8 @@ static void StudyRuntimeInfosInitializeAllAreas(Study& study, StudyRuntimeInfos& r.thermalPlantTotalCountMustRun += area.thermal.list.enabledAndMustRunCount(); r.shortTermStorageCount += area.shortTermStorage.count(); + r.shortTermStorageCumulativeConstraintCount += area.shortTermStorage + .cumulativeConstraintCount(); } } @@ -363,6 +365,8 @@ bool StudyRuntimeInfos::loadFromStudy(Study& study) logs.info() << " thermal clusters: " << thermalPlantTotalCount; logs.info() << " thermal clusters (must-run): " << thermalPlantTotalCountMustRun; logs.info() << " short-term storages: " << shortTermStorageCount; + logs.info() << " short-term storage cumulative constraints count: " + << shortTermStorageCumulativeConstraintCount; logs.info() << " binding constraints: " << study.bindingConstraints.activeConstraints().size(); logs.info() << " geographic trimming:" << (gd.geographicTrimming ? "true" : "false"); diff --git a/src/ports/sirius-solver/portfile.cmake b/src/ports/sirius-solver/portfile.cmake index 287994c152..bf6ac68a36 100644 --- a/src/ports/sirius-solver/portfile.cmake +++ b/src/ports/sirius-solver/portfile.cmake @@ -1,8 +1,8 @@ vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO "rte-france/sirius-solver" - REF "antares-integration-v1.5" - SHA512 19c6c156861bdeb58c2f17f703124d52020c79f9b81734057bf1bc5dff3dbc464179f99aeab6c8c44a84de1f84ed8f4929f9a919d2bf8bd49ac737f656088e19 + REF "antares-integration-v1.6" + SHA512 8d5992f036f35b73c11261e68e030c58c3ffe22b411921c7e08e62274feeed41227b59365a00a4e32e49f35cdaa733b079cfc0a7d98347825253ae67d9c69e4a HEAD_REF main ) diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index 5ae822209b..b37cb7f1cf 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(lps) add_subdirectory(misc) add_subdirectory(modelConverter) add_subdirectory(modelParser) +add_subdirectory(systemParser) add_subdirectory(modeler) add_subdirectory(optimisation) add_subdirectory(signal-handling) diff --git a/src/solver/application/application.cpp b/src/solver/application/application.cpp index 2d1695bcf0..43dfa3368d 100644 --- a/src/solver/application/application.cpp +++ b/src/solver/application/application.cpp @@ -161,9 +161,6 @@ void Application::readDataForTheStudy(Data::StudyLoadOptions& options) Antares::Solver::initializeSignalHandlers(resultWriter); - // Save about-the-study files (comments, notes, etc.) - study.saveAboutTheStudy(*resultWriter); - // Name of the simulation (again, if the value has been overwritten) if (!pSettings.simulationName.empty()) { @@ -375,6 +372,9 @@ void Application::execute() return; } + // Save about-the-study files (comments, notes, etc.) + pStudy->saveAboutTheStudy(*resultWriter); + SystemMemoryLogger memoryReport; memoryReport.interval(1000 * 60 * 5); // 5 minutes memoryReport.start(); diff --git a/src/solver/misc/options.cpp b/src/solver/misc/options.cpp index d72bd4c692..00572039a9 100644 --- a/src/solver/misc/options.cpp +++ b/src/solver/misc/options.cpp @@ -82,14 +82,14 @@ std::unique_ptr CreateParser(Settings& settings, StudyLoad "Solver used for simulation\nAvailable solver list : " + availableOrToolsSolversString()); - //--xpress-parameters + //--solver-parameters parser->add( options.optOptions.solverParameters, ' ', "solver-parameters", - "Set xpress solver specific parameters. The specified string must be wrapped into quotes: " - "--solver-parameters=\"param1 value1 param2 value2\". The syntax of parameters is solver " - "specfic, examples are given in Antares-Simulator online documentation."); + "Set solver-specific parameters, for instance --solver-parameters=\"THREADS 1 PRESOLVE 1\"" + "for XPRESS or --solver-parameters=\"parallel/maxnthreads 1, lp/presolving TRUE\" for SCIP." + "Syntax is solver-dependent, and only supported for SCIP & XPRESS."); parser->addParagraph("\nParameters"); // --name diff --git a/src/solver/modelParser/CMakeLists.txt b/src/solver/modelParser/CMakeLists.txt index 1881ba78fa..8e5ac13726 100644 --- a/src/solver/modelParser/CMakeLists.txt +++ b/src/solver/modelParser/CMakeLists.txt @@ -1,5 +1,3 @@ -find_package(yaml-cpp REQUIRED) - set(SOURCES parser.cpp encoders.hxx @@ -24,4 +22,4 @@ target_link_libraries(modelParser install(DIRECTORY include/antares DESTINATION "include" -) \ No newline at end of file +) diff --git a/src/solver/modeler/CMakeLists.txt b/src/solver/modeler/CMakeLists.txt index 3c631240ac..cb31b11fd4 100644 --- a/src/solver/modeler/CMakeLists.txt +++ b/src/solver/modeler/CMakeLists.txt @@ -1,2 +1,42 @@ add_subdirectory(api) add_subdirectory(ortoolsImpl) +add_subdirectory(loadFiles) +add_subdirectory(parameters) + +OMESSAGE(" :: modeler") + +set(exec_name "antares-modeler") + +add_library(modeler-lib INTERFACE + ${SRCS} +) + +add_executable(antares-modeler + main.cpp + ${SRCS} +) + +set_target_properties(antares-modeler PROPERTIES OUTPUT_NAME ${exec_name}) + +target_link_libraries(modeler-lib + INTERFACE + Antares::loadModelerFiles + Antares::modelerParameters +) + +target_link_libraries(antares-modeler + PRIVATE + modeler-lib +) + +import_std_libs(antares-modeler) +executable_strip(antares-modeler) + +copy_dependency(sirius_solver antares-modeler) + +install(TARGETS antares-modeler EXPORT antares-modeler DESTINATION bin) + +INSTALL(EXPORT antares-modeler + FILE antares-modelerConfig.cmake + DESTINATION cmake +) diff --git a/src/solver/modeler/loadFiles/CMakeLists.txt b/src/solver/modeler/loadFiles/CMakeLists.txt new file mode 100644 index 0000000000..678aa9386c --- /dev/null +++ b/src/solver/modeler/loadFiles/CMakeLists.txt @@ -0,0 +1,34 @@ +set(SOURCES + readSystem.cpp + readLibraries.cpp + readParameters.cpp + handleErrors.cpp + + include/antares/solver/modeler/loadFiles/loadFiles.h +) + +# Create the library +add_library(loadModelerFiles STATIC ${SOURCES}) +add_library(Antares::loadModelerFiles ALIAS loadModelerFiles) + +# Specify include directories +target_include_directories(loadModelerFiles + PUBLIC + $ +) + +# Link dependencies (if any) +target_link_libraries(loadModelerFiles + PUBLIC + Antares::antares-study-system-model + PRIVATE + Antares::io + Antares::systemParser + Antares::modelParser + Antares::modelConverter + Antares::modelerParameters +) + +install(DIRECTORY include/antares + DESTINATION "include" +) diff --git a/src/solver/modeler/loadFiles/handleErrors.cpp b/src/solver/modeler/loadFiles/handleErrors.cpp new file mode 100644 index 0000000000..4ee2352970 --- /dev/null +++ b/src/solver/modeler/loadFiles/handleErrors.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include +#include "antares/solver/modeler/loadFiles/loadFiles.h" + +namespace Antares::Solver::LoadFiles +{ + +void handleYamlError(const YAML::Exception& e, const std::string& context) +{ + logs.error() << "Error while parsing the yaml file: " << context; + if (!e.mark.is_null()) + { + logs.error() << "Line " << e.mark.line << " column " << e.mark.column; + } + logs.error() << e.what(); +} + +} // namespace Antares::Solver::LoadFiles diff --git a/src/solver/modeler/loadFiles/include/antares/solver/modeler/loadFiles/loadFiles.h b/src/solver/modeler/loadFiles/include/antares/solver/modeler/loadFiles/loadFiles.h new file mode 100644 index 0000000000..ff3882e554 --- /dev/null +++ b/src/solver/modeler/loadFiles/include/antares/solver/modeler/loadFiles/loadFiles.h @@ -0,0 +1,54 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace Antares::Solver::LoadFiles +{ + +ModelerParameters loadParameters(const std::filesystem::path& studyPath); + +std::vector loadLibraries(const std::filesystem::path& studyPath); + +Study::SystemModel::System loadSystem(const std::filesystem::path& studyPath, + const std::vector& libraries); + +void handleYamlError(const YAML::Exception& e, const std::string& context); + +/// Generic error class for all loading errors to catch in the main +class ErrorLoadingYaml: public std::runtime_error +{ +public: + explicit ErrorLoadingYaml(const std::string& s): + runtime_error(s) + { + } +}; + +} // namespace Antares::Solver::LoadFiles diff --git a/src/solver/modeler/loadFiles/readLibraries.cpp b/src/solver/modeler/loadFiles/readLibraries.cpp new file mode 100644 index 0000000000..be6b8e15d8 --- /dev/null +++ b/src/solver/modeler/loadFiles/readLibraries.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include + +#include +#include +#include +#include +#include "antares/solver/modeler/loadFiles/loadFiles.h" + +namespace fs = std::filesystem; + +namespace Antares::Solver::LoadFiles +{ + +static Study::SystemModel::Library loadSingleLibrary(const fs::path& filePath) +{ + std::string libraryStr; + try + { + libraryStr = IO::readFile(filePath); + } + catch (const std::runtime_error& e) + { + logs.error() << "Error while trying to read this library file: " << filePath; + throw ErrorLoadingYaml(e.what()); + } + + ModelParser::Parser parser; + ModelParser::Library libraryObj; + + try + { + libraryObj = parser.parse(libraryStr); + } + catch (const YAML::Exception& e) + { + handleYamlError(e, filePath.string()); + throw ErrorLoadingYaml(e.what()); + } + + try + { + return ModelConverter::convert(libraryObj); + } + catch (const std::runtime_error& e) + { + logs.error() << "Error while converting this library yaml: " << filePath; + throw ErrorLoadingYaml(e.what()); + } +} + +std::vector loadLibraries(const fs::path& studyPath) +{ + std::vector libraries; + + const fs::path directoryPath = studyPath / "input" / "model-libraries"; + for (const auto& entry: fs::directory_iterator(directoryPath)) + { + if (entry.path().extension() != ".yml") + { + logs.info() << entry.path() + << " ignored, only files having the `.yml` extension are loaded"; + continue; + } + + libraries.push_back(loadSingleLibrary(entry.path())); + logs.info() << "Library loaded: " << libraries.back().Id(); + } + + return libraries; +} +} // namespace Antares::Solver::LoadFiles diff --git a/src/solver/modeler/loadFiles/readParameters.cpp b/src/solver/modeler/loadFiles/readParameters.cpp new file mode 100644 index 0000000000..bab889cb7e --- /dev/null +++ b/src/solver/modeler/loadFiles/readParameters.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include + +#include +#include +#include "antares/solver/modeler/loadFiles/loadFiles.h" +#include "antares/solver/modeler/parameters/parseModelerParameters.h" + +namespace fs = std::filesystem; + +namespace Antares::Solver::LoadFiles +{ + +ModelerParameters loadParameters(const fs::path& studyPath) +{ + std::string filename = "parameters.yml"; + std::string paramStr; + try + { + paramStr = IO::readFile(studyPath / filename); + } + catch (const std::runtime_error& e) + { + logs.error() << "Error while trying to read file parameters.yml"; + throw ErrorLoadingYaml(e.what()); + } + + try + { + return parseModelerParameters(paramStr); + } + catch (const YAML::Exception& e) + { + handleYamlError(e, filename); + throw ErrorLoadingYaml(e.what()); + } +} + +} // namespace Antares::Solver::LoadFiles diff --git a/src/solver/modeler/loadFiles/readSystem.cpp b/src/solver/modeler/loadFiles/readSystem.cpp new file mode 100644 index 0000000000..dac47889cc --- /dev/null +++ b/src/solver/modeler/loadFiles/readSystem.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include + +#include +#include +#include +#include +#include "antares/solver/modeler/loadFiles/loadFiles.h" + +namespace fs = std::filesystem; + +namespace Antares::Solver::LoadFiles +{ + +Study::SystemModel::System loadSystem(const fs::path& studyPath, + const std::vector& libraries) +{ + std::string filename = "system.yml"; + std::string systemStr; + try + { + systemStr = IO::readFile(studyPath / "input" / filename); + } + catch (const std::runtime_error& e) + { + logs.error() << "Error while trying to read file system.yml"; + throw ErrorLoadingYaml(e.what()); + } + + SystemParser::Parser parser; + SystemParser::System systemObj; + try + { + systemObj = parser.parse(systemStr); + } + catch (const YAML::Exception& e) + { + handleYamlError(e, filename); + throw ErrorLoadingYaml(e.what()); + } + + try + { + return SystemConverter::convert(systemObj, libraries); + } + catch (const std::runtime_error& e) + { + logs.error() << "Error while converting the system yaml to components"; + throw ErrorLoadingYaml(e.what()); + } +} + +} // namespace Antares::Solver::LoadFiles diff --git a/src/solver/modeler/main.cpp b/src/solver/modeler/main.cpp new file mode 100644 index 0000000000..a4d071660b --- /dev/null +++ b/src/solver/modeler/main.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include +#include +#include + +using namespace Antares; +using namespace Antares::Solver; + +int main(int argc, const char** argv) +{ + if (argc < 1) + { + logs.error() << "No study path provided, exiting."; + return EXIT_FAILURE; + } + + std::filesystem::path studyPath(argv[1]); + logs.info() << "Study path: " << studyPath; + + if (!std::filesystem::is_directory(studyPath)) + { + logs.error() << "The path provided isn't a valid directory, exiting"; + return EXIT_FAILURE; + } + + try + { + const auto parameters = LoadFiles::loadParameters(studyPath); + logs.info() << "Parameters loaded"; + const auto libraries = LoadFiles::loadLibraries(studyPath); + logs.info() << "Libraries loaded"; + const auto system = LoadFiles::loadSystem(studyPath, libraries); + logs.info() << "System loaded"; + } + catch (const LoadFiles::ErrorLoadingYaml&) + { + logs.error() << "Error while loading files, exiting"; + return EXIT_FAILURE; + } + catch (const std::exception& e) + { + logs.error() << e.what(); + logs.error() << "Error during the execution, exiting"; + return EXIT_FAILURE; + } + + return 0; +} diff --git a/src/solver/modeler/parameters/CMakeLists.txt b/src/solver/modeler/parameters/CMakeLists.txt new file mode 100644 index 0000000000..7ab35d5002 --- /dev/null +++ b/src/solver/modeler/parameters/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(modelerParameters + include/antares/solver/modeler/parameters/modelerParameters.h + include/antares/solver/modeler/parameters/parseModelerParameters.h + parseModelerParameters.cpp + encoder.hxx) + +add_library(Antares::modelerParameters ALIAS modelerParameters) + +target_link_libraries(modelerParameters + PRIVATE + yaml-cpp + Antares::io) + +target_include_directories(modelerParameters + PUBLIC + $) diff --git a/src/solver/modeler/parameters/encoder.hxx b/src/solver/modeler/parameters/encoder.hxx new file mode 100644 index 0000000000..c43d195ccc --- /dev/null +++ b/src/solver/modeler/parameters/encoder.hxx @@ -0,0 +1,44 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include + +#include "yaml-cpp/yaml.h" + +namespace YAML +{ +template<> +struct convert +{ + static bool decode(const Node& node, Antares::Solver::ModelerParameters& rhs) + { + if (!node.IsMap()) + { + return false; + } + rhs.solver = node["solver"].as(); + rhs.solverLogs = node["solver-logs"].as(false); + rhs.solverParameters = node["solver-parameters"].as(); + rhs.noOutput = node["no-output"].as(false); + return true; + } +}; +} // namespace YAML diff --git a/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/modelerParameters.h b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/modelerParameters.h new file mode 100644 index 0000000000..6ea6e1072d --- /dev/null +++ b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/modelerParameters.h @@ -0,0 +1,39 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once + +#include + +namespace Antares::Solver +{ +struct ModelerParameters +{ + // OR-Tools solver to be used for the simulation + std::string solver; + // Display solver logs ON/OFF + bool solverLogs = false; + // Specific solver parameters + std::string solverParameters; + // Write output results + bool noOutput = false; +}; +} // namespace Antares::Solver diff --git a/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/parseModelerParameters.h b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/parseModelerParameters.h new file mode 100644 index 0000000000..f0c9bd7238 --- /dev/null +++ b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/parseModelerParameters.h @@ -0,0 +1,33 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once + +#include + +#include + +namespace Antares::Solver::LoadFiles +{ + +ModelerParameters parseModelerParameters(const std::string& content); + +} // namespace Antares::Solver::LoadFiles diff --git a/src/solver/modeler/parameters/parseModelerParameters.cpp b/src/solver/modeler/parameters/parseModelerParameters.cpp new file mode 100644 index 0000000000..470b7a66e4 --- /dev/null +++ b/src/solver/modeler/parameters/parseModelerParameters.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include + +#include +#include + +#include "encoder.hxx" + +namespace Antares::Solver::LoadFiles +{ + +ModelerParameters parseModelerParameters(const std::string& content) +{ + YAML::Node root = YAML::Load(content); + return root.as(); +} + +} // namespace Antares::Solver::LoadFiles diff --git a/src/solver/optimisation/CMakeLists.txt b/src/solver/optimisation/CMakeLists.txt index 57194b388a..0d64d42e44 100644 --- a/src/solver/optimisation/CMakeLists.txt +++ b/src/solver/optimisation/CMakeLists.txt @@ -47,9 +47,7 @@ set(RTESOLVER_OPT post_process_commands.cpp include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h include/antares/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.h - include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h adequacy_patch_csr/adq_patch_post_process_list.cpp - adequacy_patch_csr/post_processing.cpp include/antares/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.h adequacy_patch_csr/adq_patch_curtailment_sharing.cpp adequacy_patch_csr/solve_problem.cpp @@ -82,7 +80,9 @@ set(RTESOLVER_OPT include/antares/solver/optimisation/constraints/ShortTermStorageLevel.h constraints/ShortTermStorageLevel.cpp include/antares/solver/optimisation/constraints/ShortTermStorageCostVariation.h + include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h constraints/ShortTermStorageCostVariation.cpp + constraints/ShortTermStorageCumulation.cpp constraints/ShortTermStorageCostVariationInjectionForward.cpp constraints/ShortTermStorageCostVariationInjectionBackward.cpp constraints/ShortTermStorageCostVariationWithdrawalForward.cpp diff --git a/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp b/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp index b480108379..731aaac79c 100644 --- a/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp +++ b/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp @@ -25,9 +25,10 @@ #include "antares/solver/optimisation/adequacy_patch_csr/count_constraints_variables.h" #include "antares/solver/optimisation/adequacy_patch_csr/csr_quadratic_problem.h" -#include "antares/solver/optimisation/opt_fonctions.h" #include "antares/solver/simulation/adequacy_patch_runtime_data.h" +#include "solve_problem.h" + using namespace Yuni; namespace Antares::Data::AdequacyPatch diff --git a/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp b/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp index 1d54cb0da0..f9f8aa389b 100644 --- a/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp +++ b/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp @@ -36,7 +36,7 @@ AdqPatchPostProcessList::AdqPatchPostProcessList(const AdqPatchParams& adqPatchP { post_process_list.push_back( std::make_unique(problemeHebdo_, numSpace_, areas)); - + // Here a post process particular to adq patch post_process_list.push_back( std::make_unique(problemeHebdo_, areas, false, false)); post_process_list.push_back(std::make_unique(problemeHebdo_, @@ -44,15 +44,14 @@ AdqPatchPostProcessList::AdqPatchPostProcessList(const AdqPatchParams& adqPatchP sheddingPolicy, splxOptimization, numSpace)); - - // Here a post process particular to adq patch post_process_list.push_back(std::make_unique(adqPatchParams, problemeHebdo_, areas, numSpace_)); - // Here a post process particular to adq patch post_process_list.push_back( - std::make_unique(problemeHebdo_, areas, numSpace)); + std::make_unique(problemeHebdo_, areas, numSpace)); + post_process_list.push_back( + std::make_unique(problemeHebdo_, areas, numSpace)); post_process_list.push_back( std::make_unique(problemeHebdo_, areas, true, false)); post_process_list.push_back( diff --git a/src/solver/optimisation/adequacy_patch_csr/post_processing.cpp b/src/solver/optimisation/adequacy_patch_csr/post_processing.cpp deleted file mode 100644 index bc946a3c57..0000000000 --- a/src/solver/optimisation/adequacy_patch_csr/post_processing.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* -** Copyright 2007-2024, RTE (https://www.rte-france.com) -** See AUTHORS.txt -** SPDX-License-Identifier: MPL-2.0 -** This file is part of Antares-Simulator, -** Adequacy and Performance assessment for interconnected energy networks. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the Mozilla Public Licence 2.0 as published by -** the Mozilla Foundation, either version 2 of the License, or -** (at your option) any later version. -** -** 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 -** Mozilla Public Licence 2.0 for more details. -** -** You should have received a copy of the Mozilla Public Licence 2.0 -** along with Antares_Simulator. If not, see . -*/ -#include "antares/solver/optimisation/adequacy_patch_csr/post_processing.h" - -#include - -namespace Antares::Data::AdequacyPatch -{ -double recomputeDTG_MRG(bool triggered, double dtgMrg, double ens) -{ - if (triggered) - { - return std::max(0.0, dtgMrg - ens); - } - else - { - return dtgMrg; - } -} - -double recomputeENS_MRG(bool triggered, double dtgMrg, double ens) -{ - if (triggered) - { - return std::max(0.0, ens - dtgMrg); - } - else - { - return ens; - } -} - -double recomputeMRGPrice(double ensCsr, double originalCost, double unsuppliedEnergyCost) -{ - if (ensCsr > 0.5) - { - return -unsuppliedEnergyCost; - } - else - { - return originalCost; - } -} -} // namespace Antares::Data::AdequacyPatch diff --git a/src/solver/optimisation/adequacy_patch_csr/solve_problem.h b/src/solver/optimisation/adequacy_patch_csr/solve_problem.h new file mode 100644 index 0000000000..59b0619ca2 --- /dev/null +++ b/src/solver/optimisation/adequacy_patch_csr/solve_problem.h @@ -0,0 +1,14 @@ + +#pragma once + +#include "antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h" +#include "antares/solver/optimisation/opt_structure_probleme_a_resoudre.h" +#include "antares/study/parameters/adq-patch-params.h" + +using namespace Antares::Data::AdequacyPatch; + +bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE&, + HourlyCSRProblem&, + const AdqPatchParams&, + unsigned int week, + int year); diff --git a/src/solver/optimisation/constraints/Group1.cpp b/src/solver/optimisation/constraints/Group1.cpp index 810b957aee..61de7cf2b3 100644 --- a/src/solver/optimisation/constraints/Group1.cpp +++ b/src/solver/optimisation/constraints/Group1.cpp @@ -22,6 +22,7 @@ #include "antares/solver/optimisation/constraints/Group1.h" #include "antares/solver/optimisation/constraints/ShortTermStorageCostVariation.h" +#include "antares/solver/optimisation/constraints/ShortTermStorageCumulation.h" AreaBalanceData Group1::GetAreaBalanceData() { @@ -49,6 +50,13 @@ ShortTermStorageData Group1::GetShortTermStorageData() }; } +ShortTermStorageCumulativeConstraintData Group1::GetShortTermStorageCumulativeConstraintData() +{ + return {problemeHebdo_->CorrespondanceCntNativesCntOptim, + problemeHebdo_->ShortTermStorage, + problemeHebdo_->CorrespondanceCntNativesCntOptimHebdomadaires}; +} + FlowDissociationData Group1::GetFlowDissociationData() { return {.CorrespondanceCntNativesCntOptim = problemeHebdo_->CorrespondanceCntNativesCntOptim, @@ -89,6 +97,11 @@ void Group1::BuildConstraints() ShortTermStorageCostVariationWithdrawalForward shortTermStorageCostVariationWithdrawalForward( builder_, shortTermStorageData); + + auto shortTermStorageCumulativeConstraintData = GetShortTermStorageCumulativeConstraintData(); + ShortTermStorageCumulation shortTermStorageCumulation(builder_, + shortTermStorageCumulativeConstraintData); + auto flowDissociationData = GetFlowDissociationData(); FlowDissociation flowDissociation(builder_, flowDissociationData); @@ -123,4 +136,9 @@ void Group1::BuildConstraints() bindingConstraintHour.add(pdt, cntCouplante); } } + + for (uint32_t pays = 0; pays < problemeHebdo_->NombreDePays; ++pays) + { + shortTermStorageCumulation.add(pays); + } } diff --git a/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp b/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp index a1a73ef2fa..bb298a3f8d 100644 --- a/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp +++ b/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp @@ -54,7 +54,7 @@ void NbDispUnitsMinBoundSinceMinUpTime::add(int pays, int index, int pdt) builder.greaterThan(); if (builder.NumberOfVariables() > 1) { - data.CorrespondanceCntNativesCntOptim[pays] + data.CorrespondanceCntNativesCntOptim[pdt] .NumeroDeContrainteDesContraintesDeDureeMinDeMarche[cluster] = builder.data.nombreDeContraintes; diff --git a/src/solver/optimisation/constraints/ShortTermStorageCumulation.cpp b/src/solver/optimisation/constraints/ShortTermStorageCumulation.cpp new file mode 100644 index 0000000000..f6e684db83 --- /dev/null +++ b/src/solver/optimisation/constraints/ShortTermStorageCumulation.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include "antares/solver/optimisation/constraints/ShortTermStorageCumulation.h" + +#include +#include + +class CumulationConstraint +{ +public: + virtual void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES& input) const + = 0; + virtual std::string name() const = 0; + virtual ~CumulationConstraint() = default; +}; + +class WithdrawalCumulationConstraint: public CumulationConstraint +{ +public: + void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES&) const override + { + builder.ShortTermStorageWithdrawal(index, 1.0); + } + + std::string name() const override + { + return "WithdrawalSum"; + } + + ~WithdrawalCumulationConstraint() override = default; +}; + +class InjectionCumulationConstraint: public CumulationConstraint +{ +public: + void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES&) const override + { + builder.ShortTermStorageInjection(index, 1.0); + } + + std::string name() const override + { + return "InjectionSum"; + } + + ~InjectionCumulationConstraint() override = default; +}; + +class NettingCumulationConstraint: public CumulationConstraint +{ +public: + void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES& input) const override + { + builder.ShortTermStorageInjection(index, input.injectionEfficiency) + .ShortTermStorageWithdrawal(index, -input.withdrawalEfficiency); + } + + std::string name() const override + { + return "NettingSum"; + } + + ~NettingCumulationConstraint() override = default; +}; + +std::unique_ptr cumulationConstraintFromVariable(const std::string& variable) +{ + if (variable == "withdrawal") + { + return std::make_unique(); + } + else if (variable == "injection") + { + return std::make_unique(); + } + else if (variable == "netting") + { + return std::make_unique(); + } + throw std::invalid_argument("Invalid cumulation constraint type"); +} + +char ConvertSense(const std::string& sense) +{ + if (sense == "greater") + { + return '>'; + } + else if (sense == "less") + { + return '<'; + } + else + { + return '='; + } +} + +void ShortTermStorageCumulation::add(int pays) +{ + ConstraintNamer namer(builder.data.NomDesContraintes); + namer.UpdateArea(builder.data.NomsDesPays[pays]); + + for (const auto& storage: data.ShortTermStorage[pays]) + { + for (const auto& constraint: storage.additional_constraints) + { + // sum (var[h]) sign rhs, h in list provided by user where: + // var = injection for InjectionCumulationConstraint + // var = withdrawal for WithdrawalCumulationConstraint + // var = injectionEfficiency * injection - withdrawalEfficiency * withdrawal for Netting + auto constraintHelper = cumulationConstraintFromVariable(constraint.variable); + namer.ShortTermStorageCumulation(constraintHelper->name(), + builder.data.nombreDeContraintes, + storage.name, + constraint.name); + const auto index = storage.clusterGlobalIndex; + data.CorrespondanceCntNativesCntOptimHebdomadaires + .ShortTermStorageCumulation[constraint.globalIndex] + = builder.data.nombreDeContraintes; + + for (const auto& hour: constraint.hours) + { + builder.updateHourWithinWeek(hour - 1); + constraintHelper->build(builder, index, storage); + } + builder.SetOperator(ConvertSense(constraint.operatorType)).build(); + } + } +} diff --git a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h index c7fb554c22..6ebb9734e4 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h @@ -31,10 +31,69 @@ #include "../variables/VariableManagerUtils.h" +struct LinkVariable +{ + LinkVariable(): + directVar(-1), + indirectVar(-1) + { + } + + LinkVariable(int direct, int indirect): + directVar(direct), + indirectVar(indirect) + { + } + + inline bool check() const + { + if (directVar < 0) + { + Antares::logs.warning() << "directVar < 0 detected, this should not happen"; + } + if (indirectVar < 0) + { + Antares::logs.warning() << "indirectVar < 0 detected, this should not happen"; + } + + return (directVar >= 0) && (indirectVar >= 0); + } + + int directVar; + int indirectVar; +}; + struct PROBLEME_HEBDO; class HourlyCSRProblem { + using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; + +public: + explicit HourlyCSRProblem(const AdqPatchParams& adqPatchParams, PROBLEME_HEBDO* p): + adqPatchParams_(adqPatchParams), + variableManager_(p->CorrespondanceVarNativesVarOptim, + p->NumeroDeVariableStockFinal, + p->NumeroDeVariableDeTrancheDeStock, + p->NombreDePasDeTempsPourUneOptimisation), + problemeHebdo_(p) + { + double temp = pow(10, -adqPatchParams.curtailmentSharing.thresholdVarBoundsRelaxation); + belowThisThresholdSetToZero = std::min(temp, 0.1); + + allocateProblem(); + } + + HourlyCSRProblem(const HourlyCSRProblem&) = delete; + HourlyCSRProblem& operator=(const HourlyCSRProblem&) = delete; + + inline void setHour(int hour) + { + triggeredHour = hour; + } + + void run(uint week, uint year); + private: void calculateCsrParameters(); @@ -65,85 +124,26 @@ class HourlyCSRProblem void setQuadraticCost(); void setLinearCost(); -private: - using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; +public: + // TODO [gp] : try to make these members private + double belowThisThresholdSetToZero; + std::map numberOfConstraintCsrAreaBalance; + std::set ensVariablesInsideAdqPatch; // place inside only ENS inside adq-patch + std::set varToBeSetToZeroIfBelowThreshold; // place inside only ENS and Spillage variable + int triggeredHour; + const AdqPatchParams& adqPatchParams_; VariableManagement::VariableManager variableManager_; -public: - void run(uint week, uint year); - - // TODO[FOM] Make these members private - int triggeredHour; - double belowThisThresholdSetToZero; PROBLEME_HEBDO* problemeHebdo_; PROBLEME_ANTARES_A_RESOUDRE problemeAResoudre_; - explicit HourlyCSRProblem(const AdqPatchParams& adqPatchParams, PROBLEME_HEBDO* p): - adqPatchParams_(adqPatchParams), - variableManager_(p->CorrespondanceVarNativesVarOptim, - p->NumeroDeVariableStockFinal, - p->NumeroDeVariableDeTrancheDeStock, - p->NombreDePasDeTempsPourUneOptimisation), - problemeHebdo_(p) - { - double temp = pow(10, -adqPatchParams.curtailmentSharing.thresholdVarBoundsRelaxation); - belowThisThresholdSetToZero = std::min(temp, 0.1); - - allocateProblem(); - } - - ~HourlyCSRProblem() = default; - - HourlyCSRProblem(const HourlyCSRProblem&) = delete; - HourlyCSRProblem& operator=(const HourlyCSRProblem&) = delete; - - inline void setHour(int hour) - { - triggeredHour = hour; - } - std::map numberOfConstraintCsrEns; - std::map numberOfConstraintCsrAreaBalance; std::map numberOfConstraintCsrFlowDissociation; std::map numberOfConstraintCsrHourlyBinding; // length is number of binding constraint // contains interco 2-2 std::map rhsAreaBalanceValues; - std::set varToBeSetToZeroIfBelowThreshold; // place inside only ENS and Spillage variable - std::set ensVariablesInsideAdqPatch; // place inside only ENS inside adq-patch - - struct LinkVariable - { - LinkVariable(): - directVar(-1), - indirectVar(-1) - { - } - - LinkVariable(int direct, int indirect): - directVar(direct), - indirectVar(indirect) - { - } - - inline bool check() const - { - if (directVar < 0) - { - Antares::logs.warning() << "directVar < 0 detected, this should not happen"; - } - if (indirectVar < 0) - { - Antares::logs.warning() << "indirectVar < 0 detected, this should not happen"; - } - - return (directVar >= 0) && (indirectVar >= 0); - } - - int directVar; - int indirectVar; - }; // links between two areas inside the adq-patch domain std::map linkInsideAdqPatch; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h deleted file mode 100644 index 3ae5ee2d91..0000000000 --- a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -** Copyright 2007-2024, RTE (https://www.rte-france.com) -** See AUTHORS.txt -** SPDX-License-Identifier: MPL-2.0 -** This file is part of Antares-Simulator, -** Adequacy and Performance assessment for interconnected energy networks. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the Mozilla Public Licence 2.0 as published by -** the Mozilla Foundation, either version 2 of the License, or -** (at your option) any later version. -** -** 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 -** Mozilla Public Licence 2.0 for more details. -** -** You should have received a copy of the Mozilla Public Licence 2.0 -** along with Antares_Simulator. If not, see . -*/ - -#pragma once - -namespace Antares::Data::AdequacyPatch -{ -double recomputeDTG_MRG(bool triggered, double dtgMrg, double ens); -double recomputeENS_MRG(bool triggered, double dtgMrg, double ens); -double recomputeMRGPrice(double ensCsr, double originalCost, double unsuppliedEnergyCost); -} // namespace Antares::Data::AdequacyPatch diff --git a/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h index b06af4614d..31019332d3 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h @@ -305,3 +305,8 @@ struct ShortTermStorageData const std::vector<::ShortTermStorage::AREA_INPUT>& ShortTermStorage; }; + +struct ShortTermStorageCumulativeConstraintData: ShortTermStorageData +{ + CORRESPONDANCES_DES_CONTRAINTES_HEBDOMADAIRES& CorrespondanceCntNativesCntOptimHebdomadaires; +}; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h b/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h index e5134edb6e..c74567716e 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h @@ -38,6 +38,9 @@ class Group1: public ConstraintGroup AreaBalanceData GetAreaBalanceData(); FictitiousLoadData GetFictitiousLoadData(); ShortTermStorageData GetShortTermStorageData(); + + ShortTermStorageCumulativeConstraintData GetShortTermStorageCumulativeConstraintData(); + FlowDissociationData GetFlowDissociationData(); BindingConstraintHourData GetBindingConstraintHourData(); }; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h new file mode 100644 index 0000000000..2c4c540158 --- /dev/null +++ b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h @@ -0,0 +1,39 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once +#include "ConstraintBuilder.h" + +class ShortTermStorageCumulation: ConstraintFactory +{ +public: + ShortTermStorageCumulation(ConstraintBuilder& builder, + ShortTermStorageCumulativeConstraintData& data): + ConstraintFactory(builder), + data(data) + { + } + + void add(int pays); + +private: + ShortTermStorageCumulativeConstraintData& data; +}; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h index 55cccc4e64..4d761bd558 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h @@ -56,13 +56,6 @@ void OPT_InitialiserLesCoutsLineaire(PROBLEME_HEBDO*, const int, const int); void OPT_InitialiserLesCoutsQuadratiques(PROBLEME_HEBDO*, int); bool OPT_AppelDuSolveurQuadratique(PROBLEME_ANTARES_A_RESOUDRE*, const int); -using namespace Antares::Data::AdequacyPatch; -bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE&, - HourlyCSRProblem&, - const AdqPatchParams&, - uint week, - int year); - bool OPT_PilotageOptimisationLineaire(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo, Solver::IResultWriter& writer, diff --git a/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h b/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h index f05b6eb15a..c7c3dff284 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h @@ -166,6 +166,11 @@ class ConstraintNamer: public Namer unsigned int constraint, const std::string& short_term_name); + void ShortTermStorageCumulation(const std::string& constraint_type, + unsigned int constraint, + const std::string& short_term_name, + const std::string& constraint_name); + private: void nameWithTimeGranularity(unsigned int constraint, const std::string& name, @@ -177,6 +182,11 @@ inline std::string TimeIdentifier(unsigned int timeStep, const std::string& time return timeStepType + "<" + std::to_string(timeStep) + ">"; } +inline std::string ShortTermStorageCumulationIdentifier(const std::string& name) +{ + return "Constraint<" + name + ">"; +} + inline std::string LocationIdentifier(const std::string& location, const std::string& locationType) { return locationType + "<" + location + ">"; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h b/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h index 4c3fd49bd1..b7e125ad03 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h @@ -69,16 +69,24 @@ class RemixHydroPostProcessCmd: public basePostProcessCommand SimplexOptimization splx_optimization_; }; -class DTGmarginForAdqPatchPostProcessCmd: public basePostProcessCommand +class UpdateMrgPriceAfterCSRcmd: public basePostProcessCommand { - using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; - public: - DTGmarginForAdqPatchPostProcessCmd(PROBLEME_HEBDO* problemeHebdo, - AreaList& areas, - unsigned int numSpace); + UpdateMrgPriceAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, + AreaList& areas, + unsigned int numSpace); + void execute(const optRuntimeData&) override; - void execute(const optRuntimeData& opt_runtime_data) override; +private: + const AreaList& area_list_; + unsigned int numSpace_ = 0; +}; + +class DTGnettingAfterCSRcmd: public basePostProcessCommand +{ +public: + DTGnettingAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, AreaList& areas, unsigned int numSpace); + void execute(const optRuntimeData&) override; private: const AreaList& area_list_; @@ -112,8 +120,6 @@ class HydroLevelsFinalUpdatePostProcessCmd: public basePostProcessCommand class CurtailmentSharingPostProcessCmd: public basePostProcessCommand { - using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; - public: CurtailmentSharingPostProcessCmd(const AdqPatchParams& adqPatchParams, PROBLEME_HEBDO* problemeHebdo, @@ -128,6 +134,7 @@ class CurtailmentSharingPostProcessCmd: public basePostProcessCommand std::set identifyHoursForCurtailmentSharing(const std::vector& sumENS) const; std::set getHoursRequiringCurtailmentSharing() const; + using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; const AreaList& area_list_; const AdqPatchParams& adqPatchParams_; unsigned int numSpace_ = 0; diff --git a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp index 2f6f907c5a..304aab13df 100644 --- a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp +++ b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp @@ -214,10 +214,9 @@ static SimplexResult OPT_TryToCallSimplex(const OptimizationOptions& options, mps_writer->runIfNeeded(writer, filename); TimeMeasurement measure; - const bool keepBasis = (optimizationNumber == PREMIERE_OPTIMISATION); solver = ORTOOLS_Simplexe(&Probleme, solver, keepBasis, options); - if (solver) + if (solver != nullptr) { ProblemeAResoudre->ProblemesSpx[NumIntervalle] = (void*)solver; } diff --git a/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp b/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp index d1a0f00ea2..79c29c58ab 100644 --- a/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp +++ b/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp @@ -144,12 +144,12 @@ int OPT_DecompteDesVariablesEtDesContraintesDuProblemeAOptimiser(PROBLEME_HEBDO* ProblemeAResoudre->NombreDeContraintes += 2; /* 2 constraints bounding the overall energy generated over the period (10a in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint setting the level variation over the period - (10b in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint bounding the overall energy pumped over the - period (10c in the reference document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint setting the level variation over the period + (10b in the reference document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint bounding the overall energy pumped over the + period (10c in the reference document) */ } if (!Pump && TurbEntreBornes && !MonitorHourlyLev) @@ -192,16 +192,16 @@ int OPT_DecompteDesVariablesEtDesContraintesDuProblemeAOptimiser(PROBLEME_HEBDO* ProblemeAResoudre->NombreDeContraintes += 2; /* 2 constraints bounding the overall energy generated over the period (10a in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint setting the level variation over the period - (10b in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint bounding the overall energy pumped over the - period (10c in the reference document) */ - ProblemeAResoudre->NombreDeContraintes - += nombreDePasDeTempsPourUneOptimisation; /* T constraints expressing the level hourly - variations (14a in the reference - document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint setting the level variation over the period + (10b in the reference document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint bounding the overall energy pumped over the + period (10c in the reference document) */ + ProblemeAResoudre->NombreDeContraintes += nombreDePasDeTempsPourUneOptimisation; + /* T constraints expressing the level hourly + variations (14a in the reference + document) */ } if (!Pump && !TurbEntreBornes && MonitorHourlyLev) { @@ -245,6 +245,10 @@ int OPT_DecompteDesVariablesEtDesContraintesDuProblemeAOptimiser(PROBLEME_HEBDO* ProblemeAResoudre->NombreDeContraintes += 2 * nombreDePasDeTempsPourUneOptimisation; } + if (!storage.additional_constraints.empty()) + { + ProblemeAResoudre->NombreDeContraintes += storage.additional_constraints.size(); + } } } } diff --git a/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp b/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp index 36bb303964..c04d416f00 100644 --- a/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp +++ b/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp @@ -43,6 +43,26 @@ static void shortTermStorageLevelsRHS( } } +static void shortTermStorageCumulationRHS( + const std::vector<::ShortTermStorage::AREA_INPUT>& shortTermStorageInput, + int numberOfAreas, + std::vector& SecondMembre, + const CORRESPONDANCES_DES_CONTRAINTES_HEBDOMADAIRES& CorrespondancesDesContraintesHebdomadaires) +{ + for (int areaIndex = 0; areaIndex < numberOfAreas; areaIndex++) + { + for (auto& storage: shortTermStorageInput[areaIndex]) + { + for (const auto& constraint: storage.additional_constraints) + { + int cnt = CorrespondancesDesContraintesHebdomadaires + .ShortTermStorageCumulation[constraint.globalIndex]; + SecondMembre[cnt] = constraint.rhs; + } + } + } +} + void OPT_InitialiserLeSecondMembreDuProblemeLineaire(PROBLEME_HEBDO* problemeHebdo, int PremierPdtDeLIntervalle, int DernierPdtDeLIntervalle, @@ -145,7 +165,6 @@ void OPT_InitialiserLeSecondMembreDuProblemeLineaire(PROBLEME_HEBDO* problemeHeb ProblemeAResoudre->SecondMembre, CorrespondanceCntNativesCntOptim, hourInTheYear); - for (uint32_t interco = 0; interco < problemeHebdo->NombreDInterconnexions; interco++) { if (const COUTS_DE_TRANSPORT& CoutDeTransport = problemeHebdo->CoutDeTransport[interco]; @@ -377,6 +396,10 @@ void OPT_InitialiserLeSecondMembreDuProblemeLineaire(PROBLEME_HEBDO* problemeHeb } } + shortTermStorageCumulationRHS(problemeHebdo->ShortTermStorage, + problemeHebdo->NombreDePays, + ProblemeAResoudre->SecondMembre, + problemeHebdo->CorrespondanceCntNativesCntOptimHebdomadaires); if (problemeHebdo->OptimisationAvecCoutsDeDemarrage) { OPT_InitialiserLeSecondMembreDuProblemeLineaireCoutsDeDemarrage(problemeHebdo, diff --git a/src/solver/optimisation/opt_rename_problem.cpp b/src/solver/optimisation/opt_rename_problem.cpp index a92553e5e6..b385c0f8f5 100644 --- a/src/solver/optimisation/opt_rename_problem.cpp +++ b/src/solver/optimisation/opt_rename_problem.cpp @@ -35,9 +35,9 @@ const std::string AREA("area"); std::string BuildName(const std::string& name, const std::string& location, - const std::string& timeIdentifier) + const std::string& additional_identifier) { - std::string result = name + SEPARATOR + location + SEPARATOR + timeIdentifier; + std::string result = name + SEPARATOR + location + SEPARATOR + additional_identifier; std::replace(result.begin(), result.end(), ' ', '*'); return result; } @@ -414,3 +414,16 @@ void ConstraintNamer::ShortTermStorageCostVariation(const std::string& constrain TimeIdentifier(timeStep_, HOUR)), constraint); } + +void ConstraintNamer::ShortTermStorageCumulation(const std::string& constraint_type, + unsigned int constraint, + const std::string& short_term_name, + const std::string& constraint_name) +{ + targetUpdater_.UpdateTargetAtIndex( + BuildName(constraint_type, + LocationIdentifier(area_, AREA) + SEPARATOR + "ShortTermStorage" + "<" + + short_term_name + ">", + ShortTermStorageCumulationIdentifier(constraint_name)), + constraint); +} diff --git a/src/solver/optimisation/post_process_commands.cpp b/src/solver/optimisation/post_process_commands.cpp index b069050960..3ddc06ddde 100644 --- a/src/solver/optimisation/post_process_commands.cpp +++ b/src/solver/optimisation/post_process_commands.cpp @@ -22,7 +22,6 @@ #include "antares/solver/optimisation/post_process_commands.h" #include "antares/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.h" -#include "antares/solver/optimisation/adequacy_patch_csr/post_processing.h" #include "antares/solver/simulation/adequacy_patch_runtime_data.h" #include "antares/solver/simulation/common-eco-adq.h" @@ -116,53 +115,84 @@ void RemixHydroPostProcessCmd::execute(const optRuntimeData& opt_runtime_data) hourInYear); } -// ----------------------------- -// DTG margin for adq patch -// ----------------------------- +// ---------------------------------- +// Update marginal price after CSR +// ---------------------------------- using namespace Antares::Data::AdequacyPatch; -DTGmarginForAdqPatchPostProcessCmd::DTGmarginForAdqPatchPostProcessCmd( - PROBLEME_HEBDO* problemeHebdo, - AreaList& areas, - unsigned int numSpace): +UpdateMrgPriceAfterCSRcmd::UpdateMrgPriceAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, + AreaList& areas, + unsigned int numSpace): basePostProcessCommand(problemeHebdo), area_list_(areas), numSpace_(numSpace) { } -/*! -** Calculate Dispatchable margin for all areas after CSR optimization and adjust ENS -** values if neccessary. If LOLD=1, Sets MRG COST to the max value (unsupplied energy cost) -** */ -void DTGmarginForAdqPatchPostProcessCmd::execute(const optRuntimeData&) +void UpdateMrgPriceAfterCSRcmd::execute(const optRuntimeData&) { for (uint32_t Area = 0; Area < problemeHebdo_->NombreDePays; Area++) { - if (problemeHebdo_->adequacyPatchRuntimeData->areaMode[Area] != physicalAreaInsideAdqPatch) + auto& hourlyResults = problemeHebdo_->ResultatsHoraires[Area]; + const auto& scratchpad = area_list_[Area]->scratchpad[numSpace_]; + const double unsuppliedEnergyCost = area_list_[Area]->thermal.unsuppliedEnergyCost; + const bool areaInside = problemeHebdo_->adequacyPatchRuntimeData->areaMode[Area] + == physicalAreaInsideAdqPatch; + for (uint hour = 0; hour < nbHoursInWeek; hour++) { - continue; + const bool isHourTriggeredByCsr = problemeHebdo_->adequacyPatchRuntimeData + ->wasCSRTriggeredAtAreaHour(Area, hour); + + if (isHourTriggeredByCsr + && hourlyResults.ValeursHorairesDeDefaillancePositive[hour] > 0.5 && areaInside) + { + hourlyResults.CoutsMarginauxHoraires[hour] = -unsuppliedEnergyCost; + } } + } +} + +// ----------------------------- +// DTG margin for adq patch +// ----------------------------- +DTGnettingAfterCSRcmd::DTGnettingAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, + AreaList& areas, + unsigned int numSpace): + basePostProcessCommand(problemeHebdo), + area_list_(areas), + numSpace_(numSpace) +{ +} + +void DTGnettingAfterCSRcmd::execute(const optRuntimeData&) +{ + for (uint32_t Area = 0; Area < problemeHebdo_->NombreDePays; Area++) + { + auto& hourlyResults = problemeHebdo_->ResultatsHoraires[Area]; + const auto& scratchpad = area_list_[Area]->scratchpad[numSpace_]; for (uint hour = 0; hour < nbHoursInWeek; hour++) { - auto& hourlyResults = problemeHebdo_->ResultatsHoraires[Area]; - const auto& scratchpad = area_list_[Area]->scratchpad[numSpace_]; + const bool isHourTriggeredByCsr = problemeHebdo_->adequacyPatchRuntimeData + ->wasCSRTriggeredAtAreaHour(Area, hour); + const double dtgMrg = scratchpad.dispatchableGenerationMargin[hour]; const double ens = hourlyResults.ValeursHorairesDeDefaillancePositive[hour]; - const bool triggered = problemeHebdo_->adequacyPatchRuntimeData - ->wasCSRTriggeredAtAreaHour(Area, hour); - hourlyResults.ValeursHorairesDtgMrgCsr[hour] = recomputeDTG_MRG(triggered, dtgMrg, ens); - hourlyResults.ValeursHorairesDeDefaillancePositiveCSR[hour] = recomputeENS_MRG( - triggered, - dtgMrg, - ens); - - const double unsuppliedEnergyCost = area_list_[Area]->thermal.unsuppliedEnergyCost; - hourlyResults.CoutsMarginauxHoraires[hour] = recomputeMRGPrice( - hourlyResults.ValeursHorairesDtgMrgCsr[hour], - hourlyResults.CoutsMarginauxHoraires[hour], - unsuppliedEnergyCost); + const bool areaInside = problemeHebdo_->adequacyPatchRuntimeData->areaMode[Area] + == physicalAreaInsideAdqPatch; + if (isHourTriggeredByCsr && areaInside) + { + hourlyResults.ValeursHorairesDtgMrgCsr[hour] = std::max(0.0, dtgMrg - ens); + hourlyResults.ValeursHorairesDeDefaillancePositiveCSR[hour] = std::max(0.0, + ens + - dtgMrg); + } + else + { + // Default value (when the hour is not triggered by CSR) + hourlyResults.ValeursHorairesDtgMrgCsr[hour] = dtgMrg; + hourlyResults.ValeursHorairesDeDefaillancePositiveCSR[hour] = ens; + } } } } diff --git a/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h b/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h index f1ed9bca0c..a4517115a8 100644 --- a/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h +++ b/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h @@ -39,6 +39,8 @@ void SIM_AllocationLinks(PROBLEME_HEBDO& problem, void SIM_AllocationConstraints(PROBLEME_HEBDO& problem, const Antares::Data::Study& study, unsigned NombreDePasDeTemps); +void SIM_AllocationShortermStorageCumulation(PROBLEME_HEBDO& problem, + const Antares::Data::Study& study); void SIM_AllocateAreas(PROBLEME_HEBDO& problem, const Antares::Data::Study& study, diff --git a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h index d24fb25bc4..35051ba2d5 100644 --- a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h +++ b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h @@ -111,6 +111,7 @@ struct CORRESPONDANCES_DES_CONTRAINTES_JOURNALIERES struct CORRESPONDANCES_DES_CONTRAINTES_HEBDOMADAIRES { std::vector NumeroDeContrainteDesContraintesCouplantes; + std::vector ShortTermStorageCumulation; }; struct VALEURS_DE_NTC_ET_RESISTANCES @@ -182,7 +183,7 @@ struct PROPERTIES bool penalizeVariationInjection; std::shared_ptr series; - + std::vector additional_constraints; int clusterGlobalIndex; std::string name; }; diff --git a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp index d735191ff7..dd7480671c 100644 --- a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp +++ b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp @@ -38,6 +38,7 @@ void SIM_AllocationProblemeHebdo(const Data::Study& study, SIM_AllocationProblemePasDeTemps(problem, study, NombreDePasDeTemps); SIM_AllocationLinks(problem, study.runtime.interconnectionsCount(), NombreDePasDeTemps); SIM_AllocationConstraints(problem, study, NombreDePasDeTemps); + SIM_AllocationShortermStorageCumulation(problem, study); SIM_AllocateAreas(problem, study, NombreDePasDeTemps); } catch (const std::bad_alloc& e) @@ -245,6 +246,13 @@ void SIM_AllocationLinks(PROBLEME_HEBDO& problem, const uint linkCount, unsigned } } +void SIM_AllocationShortermStorageCumulation(PROBLEME_HEBDO& problem, + const Antares::Data::Study& study) +{ + problem.CorrespondanceCntNativesCntOptimHebdomadaires.ShortTermStorageCumulation + .assign(study.runtime.shortTermStorageCumulativeConstraintCount, 0); +} + void SIM_AllocationConstraints(PROBLEME_HEBDO& problem, const Antares::Data::Study& study, unsigned NombreDePasDeTemps) @@ -386,6 +394,7 @@ void SIM_AllocateAreas(PROBLEME_HEBDO& problem, problem.ResultatsHoraires[k].ValeursHorairesDeDefaillanceNegative.assign(NombreDePasDeTemps, 0.); + problem.ResultatsHoraires[k].TurbinageHoraire.assign(NombreDePasDeTemps, 0.); problem.ResultatsHoraires[k].PompageHoraire.assign(NombreDePasDeTemps, 0.); problem.ResultatsHoraires[k].CoutsMarginauxHoraires.assign(NombreDePasDeTemps, 0.); diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index 3d29d3ee42..786b1b5aa6 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -39,6 +39,7 @@ static void importShortTermStorages( std::vector<::ShortTermStorage::AREA_INPUT>& ShortTermStorageOut) { int clusterGlobalIndex = 0; + int clusterCumulativeConstraintGlobalIndex = 0; for (uint areaIndex = 0; areaIndex != areas.size(); areaIndex++) { ShortTermStorageOut[areaIndex].resize(areas[areaIndex]->shortTermStorage.count()); @@ -59,7 +60,12 @@ static void importShortTermStorages( toInsert.penalizeVariationInjection = st.properties.penalizeVariationInjection; toInsert.penalizeVariationWithdrawal = st.properties.penalizeVariationWithdrawal; toInsert.name = st.properties.name; - + toInsert.additional_constraints = st.additional_constraints; + for (auto& constraint: toInsert.additional_constraints) + { + constraint.globalIndex = clusterCumulativeConstraintGlobalIndex; + ++clusterCumulativeConstraintGlobalIndex; + } toInsert.series = st.series; // TODO add missing properties, or use the same struct diff --git a/src/solver/systemParser/CMakeLists.txt b/src/solver/systemParser/CMakeLists.txt new file mode 100644 index 0000000000..37e09a616a --- /dev/null +++ b/src/solver/systemParser/CMakeLists.txt @@ -0,0 +1,30 @@ +set(SOURCES + parser.cpp + converter.cpp + encoders.hxx + include/antares/solver/systemParser/parser.h + include/antares/solver/systemParser/converter.h + include/antares/solver/systemParser/system.h +) + +# Create the library +add_library(systemParser STATIC ${SOURCES}) +add_library(Antares::systemParser ALIAS systemParser) + +# Specify include directories +target_include_directories(systemParser + PUBLIC + $ +) + +# Link dependencies (if any) +target_link_libraries(systemParser + PUBLIC + Antares::antares-study-system-model + PRIVATE + yaml-cpp +) + +install(DIRECTORY include/antares + DESTINATION "include" +) diff --git a/src/solver/systemParser/converter.cpp b/src/solver/systemParser/converter.cpp new file mode 100644 index 0000000000..b78b8abb8a --- /dev/null +++ b/src/solver/systemParser/converter.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include "antares/solver/systemParser/converter.h" + +#include + +#include "antares/solver/systemParser/system.h" +#include "antares/study/system-model/system.h" + +using namespace Antares::Study; + +namespace Antares::Solver::SystemConverter +{ + +class ErrorWhileSplittingLibraryAndModel: public std::runtime_error +{ +public: + explicit ErrorWhileSplittingLibraryAndModel(const std::string& s): + runtime_error("'.' not found while splitting library and model: " + s) + { + } +}; + +class LibraryNotFound: public std::runtime_error +{ +public: + explicit LibraryNotFound(const std::string& s): + runtime_error("No library found with this name: " + s) + { + } +}; + +class ModelNotFound: public std::runtime_error +{ +public: + explicit ModelNotFound(const std::string& s): + runtime_error("No model found with this name: " + s) + { + } +}; + +static std::pair splitLibraryModelString(const std::string& s) +{ + size_t pos = s.find('.'); + if (pos == std::string::npos) + { + throw ErrorWhileSplittingLibraryAndModel(s); + } + + std::string library = s.substr(0, pos); + std::string model = s.substr(pos + 1); + return {library, model}; +} + +static const SystemModel::Model& getModel(const std::vector& libraries, + const std::string& libraryId, + const std::string& modelId) +{ + auto lib = std::ranges::find_if(libraries, + [&libraryId](const auto& l) { return l.Id() == libraryId; }); + if (lib == libraries.end()) + { + throw LibraryNotFound(libraryId); + } + + auto search = lib->Models().find(modelId); + if (search == lib->Models().end()) + { + throw ModelNotFound(modelId); + } + + return search->second; +} + +static SystemModel::Component createComponent(const SystemParser::Component& c, + const std::vector& libraries) +{ + const auto [libraryId, modelId] = splitLibraryModelString(c.model); + SystemModel::ModelBuilder model_builder; + + const SystemModel::Model& model = getModel(libraries, libraryId, modelId); + + SystemModel::ComponentBuilder component_builder; + + std::map parameters; + for (const auto& p: c.parameters) + { + parameters.try_emplace(p.id, p.value); + } + + auto component = component_builder.withId(c.id) + .withModel(&model) + .withScenarioGroupId(c.scenarioGroup) + .withParameterValues(parameters) + .build(); + return component; +} + +SystemModel::System convert(const SystemParser::System& parserSystem, + const std::vector& libraries) +{ + std::vector components; + for (const auto& c: parserSystem.components) + { + components.push_back(createComponent(c, libraries)); + } + + SystemModel::SystemBuilder builder; + return builder.withId(parserSystem.id).withComponents(components).build(); +} + +} // namespace Antares::Solver::SystemConverter diff --git a/src/solver/systemParser/encoders.hxx b/src/solver/systemParser/encoders.hxx new file mode 100644 index 0000000000..17135195e9 --- /dev/null +++ b/src/solver/systemParser/encoders.hxx @@ -0,0 +1,95 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once + +#include "antares/solver/systemParser/system.h" + +#include "yaml-cpp/yaml.h" + +// Implement convert specializations +namespace YAML +{ + +/** + * @brief shortend to default construct a value when node is null + * @tparam T Type to convert the node to + * @param n node + * @return Object of type T + * It's just to simplify repertitve and verbose lines + * as_fallback_default>( +node["parameters"]) is equivalent to + node["parameters"].as>(std::vector()) + */ +template +inline T as_fallback_default(const Node& n) +{ + return n.as(T()); +} + +template<> +struct convert +{ + static bool decode(const Node& node, Antares::Solver::SystemParser::Parameter& rhs) + { + if (!node.IsMap()) + { + return false; + } + rhs.id = node["id"].as(); + rhs.type = node["type"].as(); + rhs.value = node["value"].as(); + return true; + } +}; + +template<> +struct convert +{ + static bool decode(const Node& node, Antares::Solver::SystemParser::Component& rhs) + { + if (!node.IsMap()) + { + return false; + } + rhs.id = node["id"].as(); + rhs.model = node["model"].as(); + rhs.scenarioGroup = node["scenario-group"].as(); + rhs.parameters = as_fallback_default>( + node["parameters"]); + return true; + } +}; + +template<> +struct convert +{ + static bool decode(const Node& node, Antares::Solver::SystemParser::System& rhs) + { + rhs.id = node["id"].as(); + rhs.libraries = as_fallback_default>(node["model-libraries"]); + rhs.components = as_fallback_default>( + node["components"]); + return true; + } +}; + +} // namespace YAML diff --git a/src/solver/systemParser/include/antares/solver/systemParser/converter.h b/src/solver/systemParser/include/antares/solver/systemParser/converter.h new file mode 100644 index 0000000000..32c607b6e8 --- /dev/null +++ b/src/solver/systemParser/include/antares/solver/systemParser/converter.h @@ -0,0 +1,35 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once + +#include +#include + +#include "parser.h" + +namespace Antares::Solver::SystemConverter +{ + +Study::SystemModel::System convert(const SystemParser::System& parserSystem, + const std::vector& libraries); + +} // namespace Antares::Solver::SystemConverter diff --git a/src/solver/systemParser/include/antares/solver/systemParser/parser.h b/src/solver/systemParser/include/antares/solver/systemParser/parser.h new file mode 100644 index 0000000000..94aad258be --- /dev/null +++ b/src/solver/systemParser/include/antares/solver/systemParser/parser.h @@ -0,0 +1,32 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once +#include "antares/solver/systemParser/system.h" + +namespace Antares::Solver::SystemParser +{ +class Parser +{ +public: + System parse(const std::string& content); +}; +} // namespace Antares::Solver::SystemParser diff --git a/src/solver/systemParser/include/antares/solver/systemParser/system.h b/src/solver/systemParser/include/antares/solver/systemParser/system.h new file mode 100644 index 0000000000..888d136d5a --- /dev/null +++ b/src/solver/systemParser/include/antares/solver/systemParser/system.h @@ -0,0 +1,55 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once + +#include +#include + +namespace Antares::Solver::SystemParser +{ + +struct Parameter +{ + std::string id; + std::string type; + double value; +}; + +struct Component +{ + std::string id; + std::string model; + std::string scenarioGroup; + std::vector parameters; +}; + +struct System +{ + std::string id; + std::vector libraries; + std::vector components; + + // will be implemented later + // std::vector connections; +}; + +} // namespace Antares::Solver::SystemParser diff --git a/src/solver/systemParser/parser.cpp b/src/solver/systemParser/parser.cpp new file mode 100644 index 0000000000..76f7fa15d5 --- /dev/null +++ b/src/solver/systemParser/parser.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include "antares/solver/systemParser/parser.h" + +#include "antares/solver/systemParser/system.h" + +#include "encoders.hxx" + +namespace Antares::Solver::SystemParser +{ + +System Parser::parse(const std::string& content) +{ + YAML::Node root = YAML::Load(content); + + System system = root["system"].as(); + + return system; +} + +} // namespace Antares::Solver::SystemParser diff --git a/src/solver/variable/CMakeLists.txt b/src/solver/variable/CMakeLists.txt index 5022d5b542..ed7c017f5b 100644 --- a/src/solver/variable/CMakeLists.txt +++ b/src/solver/variable/CMakeLists.txt @@ -78,8 +78,9 @@ set(SRC_VARIABLE_ECONOMY include/antares/solver/variable/economy/links.h # Variables for Economy + include/antares/solver/variable/economy/max-mrg-utils.h + max-mrg-utils.cpp include/antares/solver/variable/economy/max-mrg.h - economy/max-mrg.cpp include/antares/solver/variable/economy/price.h include/antares/solver/variable/economy/balance.h include/antares/solver/variable/economy/operatingCost.h @@ -127,6 +128,7 @@ set(SRC_VARIABLE_ECONOMY include/antares/solver/variable/economy/links/congestionFeeAbs.h include/antares/solver/variable/economy/links/marginalCost.h include/antares/solver/variable/economy/links/congestionProbability.h + include/antares/solver/variable/economy/overallCostCsr.h # Binding constraints include/antares/solver/variable/economy/bindingConstraints/bindingConstraintsMarginalCost.h diff --git a/src/solver/variable/economy/max-mrg.cpp b/src/solver/variable/economy/max-mrg.cpp deleted file mode 100644 index 7cb565037f..0000000000 --- a/src/solver/variable/economy/max-mrg.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* -** Copyright 2007-2024, RTE (https://www.rte-france.com) -** See AUTHORS.txt -** SPDX-License-Identifier: MPL-2.0 -** This file is part of Antares-Simulator, -** Adequacy and Performance assessment for interconnected energy networks. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the Mozilla Public Licence 2.0 as published by -** the Mozilla Foundation, either version 2 of the License, or -** (at your option) any later version. -** -** 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 -** Mozilla Public Licence 2.0 for more details. -** -** You should have received a copy of the Mozilla Public Licence 2.0 -** along with Antares_Simulator. If not, see . -*/ - -#include "antares/solver/variable/economy/max-mrg.h" - -#include - -#include -#include - -using namespace Yuni; - -namespace Antares::Solver::Variable::Economy -{ -template -struct SpillageSelector -{ - template - static auto Value(const State&, - const U& weeklyResults, - uint) -> decltype(weeklyResults.ValeursHorairesDeDefaillanceNegative) - { - return weeklyResults.ValeursHorairesDeDefaillanceNegative; - } -}; - -template<> -struct SpillageSelector -{ - template - static auto Value(const State& state, const U&, uint index) -> decltype(state.resSpilled[index]) - { - return state.resSpilled[index]; - } -}; - -template -inline void PrepareMaxMRGFor(const State& state, double* opmrg, uint numSpace) -{ - assert(168 + state.hourInTheYear <= HOURS_PER_YEAR); - assert(opmrg && "Invalid OP.MRG target"); - - enum - { - offset = 0, - endHour = 168, - }; - - // current area - auto& area = *state.area; - // index of the current area - auto index = area.index; - assert(area.index < 50000 && "seems invalid"); - - // current problem - auto& problem = *state.problemeHebdo; - // Weekly results from solver for the current area - auto& weeklyResults = problem.ResultatsHoraires[index]; - // Unsupplied enery for the current area - auto& D = weeklyResults.ValeursHorairesDeDefaillancePositive; - // Spillage - auto S = SpillageSelector::Value(state, weeklyResults, area.index); - - double OI[168]; - - // H.STOR - std::vector& H = weeklyResults.TurbinageHoraire; - - // energie turbinee de la semaine - { - // DTG MRG - const double* M = area.scratchpad[numSpace].dispatchableGenerationMargin; - - double WH = 0.; - { - // H.STOR - for (uint i = offset; i != endHour; ++i) - { - WH += H[i]; - } - } - - if (Utils::isZero(WH)) // no hydro - { - for (uint i = offset; i != endHour; ++i) - { - opmrg[i] = +S[i] + M[i] - D[i]; - } - return; - } - - // initialisation - for (uint i = offset; i != endHour; ++i) - { - OI[i] = +S[i] + M[i] - D[i]; - } - } - - double bottom = +std::numeric_limits::max(); - double top = 0; - - for (uint i = offset; i != endHour; ++i) - { - double oii = OI[i]; - if (oii > top) - { - top = oii; - } - if (oii < bottom) - { - bottom = oii; - } - } - - double ecart = 1.; - uint loop = 100; // arbitrary - maximum number of iterations - - // Pmax - const uint y = problem.year; - const auto& P = area.hydro.series->maxHourlyGenPower; - - do - { - double niveau = (top + bottom) * 0.5; - double SP = 0; // S+ - double SM = 0; // S- - - for (uint i = offset; i != endHour; ++i) - { - assert(i < HOURS_PER_YEAR && "calendar overflow"); - if (niveau > OI[i]) - { - opmrg[i] = std::min(niveau, - OI[i] + P.getCoefficient(y, i + state.hourInTheYear) - H[i]); - SM += opmrg[i] - OI[i]; - } - else - { - opmrg[i] = std::max(niveau, OI[i] - H[i]); - SP += OI[i] - opmrg[i]; - } - } - - ecart = SP - SM; - if (ecart > 0) - { - bottom = niveau; - } - else - { - top = niveau; - } - - if (!--loop) - { - logs.error() << "OP.MRG: " << area.name - << ": infinite loop detected. please check input data"; - return; - } - } while (ecart * ecart > 0.25); -} - -void PrepareMaxMRG(const State& state, double* opmrg, uint numSpace) -{ - if (state.simplexRunNeeded) - { - PrepareMaxMRGFor(state, opmrg, numSpace); - } - else - { - PrepareMaxMRGFor(state, opmrg, numSpace); - } -} - -} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/all.h b/src/solver/variable/include/antares/solver/variable/economy/all.h index 8e1c2ed307..17207c22db 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/all.h +++ b/src/solver/variable/include/antares/solver/variable/economy/all.h @@ -49,12 +49,16 @@ #include "inflow.h" #include "localMatchingRuleViolations.h" #include "lold.h" +#include "loldCsr.h" #include "lolp.h" +#include "lolpCsr.h" +#include "max-mrg-csr.h" #include "max-mrg.h" #include "nbOfDispatchedUnits.h" #include "nonProportionalCost.h" #include "operatingCost.h" #include "overallCost.h" +#include "overallCostCsr.h" #include "overflow.h" #include "pumping.h" #include "renewableGeneration.h" @@ -91,59 +95,62 @@ namespace Antares::Solver::Variable::Economy /*! ** \brief All variables for a single area (economy) */ -typedef // Prices - OverallCost // Overall Cost (Op. Cost + Unsupplied Eng.) - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +typedef // Prices + OverallCost // Overall Cost (Op. Cost + Unsupplied Eng.) + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> VariablesPerArea; /*! diff --git a/src/solver/variable/include/antares/solver/variable/economy/loldCsr.h b/src/solver/variable/include/antares/solver/variable/economy/loldCsr.h new file mode 100644 index 0000000000..299e83b94f --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/loldCsr.h @@ -0,0 +1,242 @@ +/* +** Copyright 2007-2023 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 "../variable.h" + +namespace Antares::Solver::Variable::Economy +{ + +struct VCardLOLD_CSR +{ + //! Caption + static std::string Caption() + { + return "LOLD CSR"; + } + + //! Unit + static std::string Unit() + { + return "Hours"; + } + + //! The short description of the variable + static std::string Description() + { + return "LOLD for CSR"; + } + + //! The expecte results + typedef Results>>>> + ResultsType; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 2; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief +*/ +template +class LOLD_CSR: public Variable::IVariable, NextT, VCardLOLD_CSR> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardLOLD_CSR VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~LOLD_CSR() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + // Next + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void simulationBegin() + { + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].reset(); + } + // Next + NextType::simulationBegin(); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + + // Next variable + NextType::yearBegin(year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily,weekly,monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); + + // Next variable + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + // Next variable + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void hourForEachArea(State& state, unsigned int numSpace) + { + if (state.hourlyResults->ValeursHorairesDeDefaillancePositiveCSR[state.hourInTheWeek] > 0.5) + { + pValuesForTheCurrentYear[numSpace][state.hourInTheYear] = 1.; + } + + // Next variable + NextType::hourForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class LOLD_CSR + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/lolpCsr.h b/src/solver/variable/include/antares/solver/variable/economy/lolpCsr.h new file mode 100644 index 0000000000..45d9ed61ac --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/lolpCsr.h @@ -0,0 +1,242 @@ +/* +** Copyright 2007-2023 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 "../variable.h" + +namespace Antares::Solver::Variable::Economy +{ +struct VCardLOLP_CSR +{ + //! Caption + static std::string Caption() + { + return "LOLP CSR"; + } + + //! Unit + static std::string Unit() + { + return "%"; + } + + //! The short description of the variable + static std::string Description() + { + return "LOLP for CSR"; + } + + //! The expecte results + typedef Results> + ResultsType; + + //! The VCard to look for for calculating spatial aggregates + typedef VCardLOLP_CSR VCardForSpatialAggregate; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 2; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief +*/ +template +class LOLP_CSR: public Variable::IVariable, NextT, VCardLOLP_CSR> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardLOLP_CSR VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~LOLP_CSR() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + + // Next + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void simulationBegin() + { + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].reset(); + } + // Next + NextType::simulationBegin(); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + + // Next variable + NextType::yearBegin(year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily,weekly,monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsOrForTheCurrentYear(); + + // Next variable + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + // Next variable + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void hourForEachArea(State& state, unsigned int numSpace) + { + if (state.hourlyResults->ValeursHorairesDeDefaillancePositiveCSR[state.hourInTheWeek] > 0.5) + { + pValuesForTheCurrentYear[numSpace][state.hourInTheYear] = 100; + } + + // Next variable + NextType::hourForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class LOLP_CSR + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/max-mrg-csr.h b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-csr.h new file mode 100644 index 0000000000..2ff8edc89d --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-csr.h @@ -0,0 +1,243 @@ +/* +** Copyright 2007-2023 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 "../variable.h" +#include "max-mrg-utils.h" + +namespace Antares::Solver::Variable::Economy +{ +struct VCardMAX_MRG_CSR +{ + //! Caption + static std::string Caption() + { + return "MAX MRG CSR"; + } + + //! Unit + static std::string Unit() + { + return "MWh"; + } + + //! The short description of the variable + static std::string Description() + { + return "Max margin for CSR"; + } + + //! The expecte results + typedef Results>>>> + ResultsType; + + //! The VCard to look for for calculating spatial aggregates + typedef VCardMAX_MRG_CSR VCardForSpatialAggregate; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 0; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief Prepare MAX.MRG results for a given week +*/ +template +void PrepareMaxMRGFor(const State& state, double* opmrg, uint numSpace); + +/*! +** \brief Max MRG +*/ +template +class MaxMrgCsr: public Variable::IVariable, NextT, VCardMAX_MRG_CSR> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardMAX_MRG_CSR VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~MaxMrgCsr() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + + // Next + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + // Next variable + NextType::yearBegin(year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily,weekly,monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); + + // Next variable + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + // Next variable + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void weekForEachArea(State& state, unsigned int numSpace) + { + double* rawhourly = Memory::RawPointer(pValuesForTheCurrentYear[numSpace].hour); + + // Getting data required to compute max margin + MaxMrgCSRdataFactory maxMRGcsrDataFactory(state, numSpace); + MaxMRGinput maxMRGinput = maxMRGcsrDataFactory.data(); + computeMaxMRG(rawhourly + state.hourInTheYear, maxMRGinput); + + // next + NextType::weekForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class MaxMrgCsr + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/max-mrg-utils.h b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-utils.h new file mode 100644 index 0000000000..2a5de71b74 --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-utils.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "../state.h" + +namespace Antares::Solver::Variable::Economy +{ + +struct MaxMRGinput +{ + double* spillage = nullptr; + double* dens = nullptr; + double* hydroGeneration = nullptr; + Antares::Data::TimeSeries* maxHourlyGenPower = nullptr; + double* dtgMargin = nullptr; + unsigned int hourInYear = 0; + unsigned int year = 0; + Date::Calendar* calendar = nullptr; + std::string areaName; +}; + +void computeMaxMRG(double* maxMrgOut, const MaxMRGinput& in); + +class MaxMrgDataFactory +{ +public: + MaxMrgDataFactory(const State& state, unsigned int numSpace); + virtual MaxMRGinput data() = 0; + +protected: + // in data + const State& state_; + const unsigned int numSpace_ = 0; + RESULTATS_HORAIRES& weeklyResults_; + // data to be built + MaxMRGinput maxMRGinput_; +}; + +class MaxMrgUsualDataFactory: public MaxMrgDataFactory +{ + using MaxMrgDataFactory::MaxMrgDataFactory; + +public: + virtual MaxMRGinput data() override; +}; + +class MaxMrgCSRdataFactory: public MaxMrgDataFactory +{ + using MaxMrgDataFactory::MaxMrgDataFactory; + +public: + virtual MaxMRGinput data() override; +}; + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h b/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h index 959d6f16e7..a53b893947 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h +++ b/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h @@ -23,6 +23,8 @@ #include "antares/solver/variable/variable.h" +#include "max-mrg-utils.h" + namespace Antares { namespace Solver @@ -92,11 +94,6 @@ struct VCardMARGE }; // class VCard -/*! -** \brief Prepare MAX.MRG results for a given week -*/ -void PrepareMaxMRG(const State& state, double* opmrg, uint numSpace); - /*! ** \brief Max MRG */ @@ -206,8 +203,6 @@ class Marge: public Variable::IVariable, NextT, VCardMARGE> { // Compute all statistics for the current year (daily,weekly,monthly) pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); - // Merge all those values with the global results - // AncestorType::pResults.merge(year, pValuesForTheCurrentYear); // Next variable NextType::yearEnd(year, numSpace); @@ -242,7 +237,11 @@ class Marge: public Variable::IVariable, NextT, VCardMARGE> void weekForEachArea(State& state, unsigned int numSpace) { double* rawhourly = Memory::RawPointer(pValuesForTheCurrentYear[numSpace].hour); - PrepareMaxMRG(state, rawhourly + state.hourInTheYear, numSpace); + + // Getting data required to compute max margin + MaxMrgUsualDataFactory maxMRGdataFactory(state, numSpace); + MaxMRGinput maxMRGinput = maxMRGdataFactory.data(); + computeMaxMRG(rawhourly + state.hourInTheYear, maxMRGinput); // next NextType::weekForEachArea(state, numSpace); diff --git a/src/solver/variable/include/antares/solver/variable/economy/overallCostCsr.h b/src/solver/variable/include/antares/solver/variable/economy/overallCostCsr.h new file mode 100644 index 0000000000..63739b686b --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/overallCostCsr.h @@ -0,0 +1,253 @@ +/* +** Copyright 2007-2023 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 "../variable.h" + +namespace Antares::Solver::Variable::Economy +{ +struct VCardOverallCostCsr +{ + //! Caption + static std::string Caption() + { + return "OV. COST CSR"; + } + + //! Unit + static std::string Unit() + { + return "Euro"; + } + + //! The short description of the variable + static std::string Description() + { + return "Overall Cost throughout all MC years"; + } + + //! The expecte results + typedef Results, + R::AllYears::Average // Use these values for spatial cluster + > + ResultsType; + + //! The VCard to look for for calculating spatial aggregates + typedef VCardOverallCostCsr VCardForSpatialAggregate; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 0; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief C02 Average value of the overall OverallCostCsr emissions expected from all +** the thermal dispatchable clusters +*/ +template +class OverallCostCsr: public Variable::IVariable, NextT, VCardOverallCostCsr> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardOverallCostCsr VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~OverallCostCsr() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + // Intermediate values + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + + NextType::yearBegin(year, numSpace); + } + + void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) + { + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; + ++i) + { + pValuesForTheCurrentYear[numSpace][i] += state.thermalClusterOperatingCostForYear[i]; + } + + NextType::yearEndBuildForEachThermalCluster(state, year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily, weekly, monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); + + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void hourForEachArea(State& state, unsigned int numSpace) + { + const double costForSpilledOrUnsuppliedEnergyCSR = + // Total UnsupliedEnergy emissions + (state.hourlyResults->ValeursHorairesDeDefaillancePositiveCSR[state.hourInTheWeek] + * state.area->thermal.unsuppliedEnergyCost) + + (state.hourlyResults->ValeursHorairesDeDefaillanceNegative[state.hourInTheWeek] + * state.area->thermal.spilledEnergyCost) + // Current hydro storage and pumping generation costs + + (state.hourlyResults->valeurH2oHoraire[state.hourInTheWeek] + * (state.hourlyResults->TurbinageHoraire[state.hourInTheWeek] + - state.area->hydro.pumpingEfficiency + * state.hourlyResults->PompageHoraire[state.hourInTheWeek])); + + pValuesForTheCurrentYear[numSpace][state.hourInTheYear] + += costForSpilledOrUnsuppliedEnergyCSR; + + NextType::hourForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class OverallCostCsr + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h b/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h index e4a989de2c..a83ccb2e03 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h +++ b/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h @@ -60,8 +60,6 @@ struct VCardUnsupliedEnergyCSR //! The VCard to look for for calculating spatial aggregates typedef VCardUnsupliedEnergyCSR VCardForSpatialAggregate; - - //! Data Level static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; //! File level (provided by the type of the results) static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile diff --git a/src/solver/variable/max-mrg-utils.cpp b/src/solver/variable/max-mrg-utils.cpp new file mode 100644 index 0000000000..bfa93c1567 --- /dev/null +++ b/src/solver/variable/max-mrg-utils.cpp @@ -0,0 +1,171 @@ +/* +** Copyright 2007-2023 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 + +using namespace Yuni; + +const unsigned int nbHoursInWeek = 168; + +namespace Antares::Solver::Variable::Economy +{ + +MaxMrgDataFactory::MaxMrgDataFactory(const State& state, unsigned int numSpace): + state_(state), + numSpace_(numSpace), + weeklyResults_(state.problemeHebdo->ResultatsHoraires[state.area->index]) +{ + maxMRGinput_.hydroGeneration = weeklyResults_.TurbinageHoraire.data(); + maxMRGinput_.maxHourlyGenPower = &state_.area->hydro.series->maxHourlyGenPower; + maxMRGinput_.dtgMargin = state_.area->scratchpad[numSpace].dispatchableGenerationMargin; + maxMRGinput_.hourInYear = state.hourInTheYear; + maxMRGinput_.year = state.problemeHebdo->year; + maxMRGinput_.calendar = &state.study.calendar; + maxMRGinput_.areaName = state_.area->name.c_str(); +} + +MaxMRGinput MaxMrgUsualDataFactory::data() +{ + if (state_.simplexRunNeeded) + { + maxMRGinput_.spillage = weeklyResults_.ValeursHorairesDeDefaillanceNegative.data(); + } + else + { + maxMRGinput_.spillage = state_.resSpilled[state_.area->index]; + } + + maxMRGinput_.dens = weeklyResults_.ValeursHorairesDeDefaillancePositive.data(); + return maxMRGinput_; +} + +MaxMRGinput MaxMrgCSRdataFactory::data() +{ + maxMRGinput_.spillage = weeklyResults_.ValeursHorairesDeDefaillanceNegative.data(); + maxMRGinput_.dens = weeklyResults_.ValeursHorairesDeDefaillancePositiveCSR.data(); + return maxMRGinput_; +} + +void computeMaxMRG(double* maxMrgOut, const MaxMRGinput& in) +{ + assert(maxMrgOut && "Invalid OP.MRG target"); + + // Following block could be replaced with : + // double weekHydroGen = std::accumulate(in.hydroGeneration, in.hydroGeneration + nbHoursInWeek, + // 0.); + double weekHydroGen = 0.; + for (uint h = 0; h != nbHoursInWeek; ++h) + { + weekHydroGen += in.hydroGeneration[h]; + } + + if (Yuni::Math::Zero(weekHydroGen)) + { + for (uint h = 0; h != nbHoursInWeek; ++h) + { + maxMrgOut[h] = in.spillage[h] + in.dtgMargin[h] - in.dens[h]; + } + return; + } + + // Initialisation + std::vector OI(nbHoursInWeek); + for (uint h = 0; h != nbHoursInWeek; ++h) + { + OI[h] = in.spillage[h] + in.dtgMargin[h] - in.dens[h]; + } + + // Following block could be replaced with : + // double bottom = *std::min_element(OI.begin(), OI.end()); + // double top = *std::max_element(OI.begin(), OI.end()); + double bottom = +std::numeric_limits::max(); + double top = 0; + for (uint i = 0; i != nbHoursInWeek; ++i) + { + double oii = OI[i]; + if (oii > top) + { + top = oii; + } + if (oii < bottom) + { + bottom = oii; + } + } + + double ecart = 1.; + uint loop = 100; // arbitrary - maximum number of iterations + + do + { + double niveau = (top + bottom) * 0.5; + double SP = 0; // S+ + double SM = 0; // S- + + for (uint h = 0; h != nbHoursInWeek; ++h) + { + assert(h < HOURS_PER_YEAR && "calendar overflow"); + if (niveau > OI[h]) + { + maxMrgOut[h] = Math::Min(niveau, + OI[h] + + in.maxHourlyGenPower->getCoefficient(in.year, + h + in.hourInYear) + - in.hydroGeneration[h]); + SM += maxMrgOut[h] - OI[h]; + } + else + { + maxMrgOut[h] = Math::Max(niveau, OI[h] - in.hydroGeneration[h]); + SP += OI[h] - maxMrgOut[h]; + } + } + + ecart = SP - SM; + if (ecart > 0) + { + bottom = niveau; + } + else + { + top = niveau; + } + + if (!--loop) + { + logs.error() << "OP.MRG: " << in.areaName + << ": infinite loop detected. please check input data"; + return; + } + } while (ecart * ecart > 0.25); +} + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/study/system-model/component.cpp b/src/study/system-model/component.cpp index 1c3d360e64..87b9991865 100644 --- a/src/study/system-model/component.cpp +++ b/src/study/system-model/component.cpp @@ -45,14 +45,14 @@ static void checkComponentDataValidity(const ComponentData& data) if (data.model->Parameters().size() != data.parameter_values.size()) { throw std::invalid_argument( - "The component has " + std::to_string(data.parameter_values.size()) + "The component \"" + data.id + "\" has " + std::to_string(data.parameter_values.size()) + " parameter(s), but its model has " + std::to_string(data.model->Parameters().size())); } for (const auto param: data.model->Parameters() | std::views::keys) { if (!data.parameter_values.contains(param)) { - throw std::invalid_argument("The component has no value for parameter '" + param + "'"); + throw std::invalid_argument("The component \"" + data.id + "\" has no value for parameter '" + param + "'"); } } } diff --git a/src/study/system-model/include/antares/study/system-model/system.h b/src/study/system-model/include/antares/study/system-model/system.h index 50fa196399..80b042388e 100644 --- a/src/study/system-model/include/antares/study/system-model/system.h +++ b/src/study/system-model/include/antares/study/system-model/system.h @@ -68,7 +68,7 @@ class SystemBuilder { public: SystemBuilder& withId(std::string_view id); - SystemBuilder& withComponents(std::vector&& components); + SystemBuilder& withComponents(std::vector& components); System build() const; private: diff --git a/src/study/system-model/system.cpp b/src/study/system-model/system.cpp index 20a26287e1..70c1807d30 100644 --- a/src/study/system-model/system.cpp +++ b/src/study/system-model/system.cpp @@ -71,7 +71,7 @@ SystemBuilder& SystemBuilder::withId(std::string_view id) * \param components A vector of components to set. * \return Reference to the SystemBuilder object. */ -SystemBuilder& SystemBuilder::withComponents(std::vector&& components) +SystemBuilder& SystemBuilder::withComponents(std::vector& components) { components_ = std::move(components); return *this; diff --git a/src/tests/end-to-end/binding_constraints/CMakeLists.txt b/src/tests/end-to-end/binding_constraints/CMakeLists.txt index 72e2e46f62..788899db61 100644 --- a/src/tests/end-to-end/binding_constraints/CMakeLists.txt +++ b/src/tests/end-to-end/binding_constraints/CMakeLists.txt @@ -1,30 +1,10 @@ -find_package(Boost COMPONENTS unit_test_framework REQUIRED) -enable_testing() - -#bigobj support needed for windows compilation -if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") -endif(MSVC) - -add_executable(tests-binding_constraints - test_binding_constraints.cpp - ) - -target_link_libraries(tests-binding_constraints - PRIVATE - Boost::unit_test_framework - model_antares - antares-solver-simulation - antares-solver-hydro - antares-solver-ts-generator - Antares::tests::in-memory-study - ) - -target_include_directories(tests-binding_constraints - PRIVATE - ${CMAKE_SOURCE_DIR}/solver - ) - -add_test(NAME end-to-end-binding_constraints COMMAND tests-binding_constraints) -set_property(TEST end-to-end-binding_constraints PROPERTY LABELS end-to-end) -set_target_properties(tests-binding_constraints PROPERTIES FOLDER Unit-tests/end_to_end) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + +add_boost_test(tests-binding_constraints + SRC test_binding_constraints.cpp + LIBS + model_antares + antares-solver-simulation + antares-solver-hydro + antares-solver-ts-generator + Antares::tests::in-memory-study) diff --git a/src/tests/end-to-end/simple_study/CMakeLists.txt b/src/tests/end-to-end/simple_study/CMakeLists.txt index cc5c93b410..e7dd6e2fa9 100644 --- a/src/tests/end-to-end/simple_study/CMakeLists.txt +++ b/src/tests/end-to-end/simple_study/CMakeLists.txt @@ -1,35 +1,11 @@ -find_package(Boost COMPONENTS unit_test_framework REQUIRED) -enable_testing() - -#bigobj support needed for windows compilation -if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") -endif(MSVC) - -add_executable(tests-simple-study - simple-study.cpp -) - -target_link_libraries(tests-simple-study - PRIVATE - antares-solver-hydro - antares-solver-variable - antares-solver-simulation - antares-solver-ts-generator - model_antares - ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} - Antares::tests::in-memory-study -) - -target_include_directories(tests-simple-study - PRIVATE - ${CMAKE_SOURCE_DIR}/solver -) - -add_test(NAME end-to-end-simple-study COMMAND tests-simple-study) -set_property(TEST end-to-end-simple-study PROPERTY LABELS end-to-end) -set_target_properties(tests-simple-study PROPERTIES FOLDER Unit-tests/end_to_end) - -# Storing tests-simple-study under the folder Unit-tests in the IDE - -#---------------------------------------------------------- +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + +add_boost_test(tests-simple-study + SRC simple-study.cpp + LIBS + antares-solver-hydro + antares-solver-variable + antares-solver-simulation + antares-solver-ts-generator + model_antares + Antares::tests::in-memory-study) diff --git a/src/tests/macros.cmake b/src/tests/macros.cmake new file mode 100644 index 0000000000..d68c7b4f2e --- /dev/null +++ b/src/tests/macros.cmake @@ -0,0 +1,32 @@ +# The following function allows to add a test in a single line +# Arguments +# SRC path to the sources +# (optional) LIBS path to the libs to link +# (optional) INCLUDE include paths +# NOTE it's not necessary to add Boost::unit_test_framework + +function(add_boost_test) + set(options "") + set(oneValueArgs) + set(multiValueArgs SRC LIBS INCLUDE) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${options}" "${oneValueArgs}" "${multiValueArgs}") + # Bypass cmake_parse_arguments for the 1st argument + set(TEST_NAME ${ARGV0}) + add_executable(${TEST_NAME} ${arg_SRC}) + # All tests use boost + target_link_libraries(${TEST_NAME} PRIVATE ${arg_LIBS} Boost::unit_test_framework) + + # Optional: add private include directories + if (NOT "${arg_INCLUDE}" STREQUAL "") + target_include_directories(${TEST_NAME} PRIVATE ${arg_INCLUDE}) + endif() + + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + + # Adding labels allows ctest filter what tests to run + set_property(TEST ${TEST_NAME} PROPERTY LABELS unit) + + # Give the IDE some directions to display tests in a "Unit-tests" folder + set_target_properties(${TEST_NAME} PROPERTIES FOLDER Unit-tests) +endfunction() diff --git a/src/tests/src/api_internal/CMakeLists.txt b/src/tests/src/api_internal/CMakeLists.txt index 1c972cd4c6..e3bb61472e 100644 --- a/src/tests/src/api_internal/CMakeLists.txt +++ b/src/tests/src/api_internal/CMakeLists.txt @@ -1,28 +1,11 @@ -set(EXECUTABLE_NAME test-api) -add_executable(${EXECUTABLE_NAME}) - -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_api.cpp -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - Antares::solver_api - Antares::tests::in-memory-study - test_utils_unit -) - -target_include_directories(${EXECUTABLE_NAME} - PRIVATE - #Allow to use the private headers - $ -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -add_test(NAME test-api COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-api PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + +add_boost_test(test-api + SRC test_api.cpp + LIBS + Antares::solver_api + Antares::tests::in-memory-study + test_utils_unit + #Allow to use the private headers + INCLUDE + $) diff --git a/src/tests/src/api_internal/test_api.cpp b/src/tests/src/api_internal/test_api.cpp index 5214aa8c10..47034cb19d 100644 --- a/src/tests/src/api_internal/test_api.cpp +++ b/src/tests/src/api_internal/test_api.cpp @@ -52,28 +52,17 @@ class InMemoryStudyLoader: public Antares::IStudyLoader builder.study->initializeRuntimeInfos(); builder.setNumberMCyears(1); builder.study->parameters.resultFormat = ResultFormat::inMemory; - builder.study->prepareOutput(); return std::move(builder.study); } bool success_ = true; }; -BOOST_AUTO_TEST_CASE(simulation_path_points_to_results) -{ - Antares::API::APIInternal api; - auto study_loader = std::make_unique(); - auto results = api.run(*study_loader.get(), {}); - BOOST_CHECK_EQUAL(results.simulationPath, std::filesystem::path{"no_output"}); - // Testing for "no_output" is a bit weird, but it's the only way to test this without actually - // running the simulation -} - BOOST_AUTO_TEST_CASE(api_run_contains_antares_problem) { Antares::API::APIInternal api; auto study_loader = std::make_unique(); - auto results = api.run(*study_loader.get(), {}); + auto results = api.run(*study_loader, {}, {}); BOOST_CHECK(!results.antares_problems.empty()); BOOST_CHECK(!results.error); @@ -83,7 +72,7 @@ BOOST_AUTO_TEST_CASE(result_failure_when_study_is_null) { Antares::API::APIInternal api; auto study_loader = std::make_unique(false); - auto results = api.run(*study_loader.get(), {}); + auto results = api.run(*study_loader, {}, {}); BOOST_CHECK(results.error); } @@ -93,7 +82,7 @@ BOOST_AUTO_TEST_CASE(result_contains_problems) { Antares::API::APIInternal api; auto study_loader = std::make_unique(); - auto results = api.run(*study_loader.get(), {}); + auto results = api.run(*study_loader, {}, {}); BOOST_CHECK(!results.antares_problems.empty()); BOOST_CHECK(!results.error); @@ -109,7 +98,7 @@ BOOST_AUTO_TEST_CASE(result_with_ortools_coin) .solverLogs = false, .solverParameters = ""}; - auto results = api.run(*study_loader.get(), opt); + auto results = api.run(*study_loader, {}, opt); BOOST_CHECK(!results.antares_problems.empty()); BOOST_CHECK(!results.error); @@ -126,7 +115,7 @@ BOOST_AUTO_TEST_CASE(invalid_ortools_solver) .solverLogs = true, .solverParameters = ""}; - auto shouldThrow = [&api, &study_loader, &opt] { return api.run(*study_loader.get(), opt); }; + auto shouldThrow = [&api, &study_loader, &opt] { return api.run(*study_loader, {}, opt); }; BOOST_CHECK_EXCEPTION(shouldThrow(), std::invalid_argument, checkMessage("Solver this-solver-does-not-exist not found")); diff --git a/src/tests/src/api_lib/CMakeLists.txt b/src/tests/src/api_lib/CMakeLists.txt index b5ce633955..f45e9d65e4 100644 --- a/src/tests/src/api_lib/CMakeLists.txt +++ b/src/tests/src/api_lib/CMakeLists.txt @@ -1,20 +1,5 @@ -set(EXECUTABLE_NAME test-client-api) -add_executable(${EXECUTABLE_NAME}) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_api.cpp -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - Antares::solver_api -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -add_test(NAME test-client-api COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-client-api PROPERTY LABELS unit) +add_boost_test(test-client-api + SRC test_api.cpp + LIBS Antares::solver_api) diff --git a/src/tests/src/api_lib/test_api.cpp b/src/tests/src/api_lib/test_api.cpp index c1111b5c6a..8460c4533a 100644 --- a/src/tests/src/api_lib/test_api.cpp +++ b/src/tests/src/api_lib/test_api.cpp @@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(result_failure_when_study_path_invalid) { using namespace std::string_literals; - auto results = Antares::API::PerformSimulation("dummy"s, {}); + auto results = Antares::API::PerformSimulation("dummy"s, {}, {}); BOOST_CHECK(results.error); BOOST_CHECK(!results.error->reason.empty()); } diff --git a/src/tests/src/libs/antares/CMakeLists.txt b/src/tests/src/libs/antares/CMakeLists.txt index 97ef92fd0e..90def4d900 100644 --- a/src/tests/src/libs/antares/CMakeLists.txt +++ b/src/tests/src/libs/antares/CMakeLists.txt @@ -18,108 +18,63 @@ set(SRC_MATRIX_LIB # Necessary cpp files ${src_libs_antares}/jit/jit.cpp - logs/logs.cpp - ) + logs/logs.cpp) add_library(matrix ${SRC_MATRIX_LIB}) target_link_libraries(matrix PUBLIC - yuni-static-core -) + yuni-static-core) target_include_directories(matrix - PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/logs" - "${src_libs_antares}/jit/include" - ) - -# Storing lib-matrix under the folder Unit-tests in the IDE -set_target_properties(matrix PROPERTIES FOLDER Unit-tests) + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/logs" + "${src_libs_antares}/jit/include") # Building tests on Matrix save operations -set(SRC_TEST_MATRIX_SAVE - logs/antares/logs/logs.h - array/fill-matrix.h - array/matrix-bypass-load.h - array/tests-matrix-save.h - array/tests-matrix-save.cpp - ) - -add_executable(tests-matrix-save ${SRC_TEST_MATRIX_SAVE}) - -target_include_directories(tests-matrix-save - PRIVATE - "${src_libs_antares}/array/include" - "${src_libs_antares}/io/include" - "${src_libs_antares}/jit/include" - "${src_libs_antares}/memory/include" - "${CMAKE_CURRENT_SOURCE_DIR}/logs" - "${CMAKE_CURRENT_SOURCE_DIR}/jit" - "${CMAKE_SOURCE_DIR}/tests/src/libs" - - ) - -target_link_libraries(tests-matrix-save - PRIVATE - matrix - yuni-static-core - Boost::unit_test_framework - antares-core -) - -# Storing tests-matrix-save under the folder Unit-tests in the IDE -set_target_properties(tests-matrix-save PROPERTIES FOLDER Unit-tests) - -add_test(NAME save-matrix COMMAND tests-matrix-save) - -set_property(TEST save-matrix PROPERTY LABELS unit) +add_boost_test(tests-matrix-save + SRC + logs/antares/logs/logs.h + array/fill-matrix.h + array/matrix-bypass-load.h + array/tests-matrix-save.h + array/tests-matrix-save.cpp + INCLUDE + "${src_libs_antares}/array/include" + "${src_libs_antares}/io/include" + "${src_libs_antares}/jit/include" + "${src_libs_antares}/memory/include" + "${CMAKE_CURRENT_SOURCE_DIR}/logs" + "${CMAKE_CURRENT_SOURCE_DIR}/jit" + "${CMAKE_SOURCE_DIR}/tests/src/libs" + LIBS + matrix + yuni-static-core + antares-core) # Building tests on Matrix load operations -set(SRC_TEST_MATRIX_LOAD - array/fill-matrix.h - array/matrix-bypass-load.h - array/tests-matrix-load.h - array/tests-matrix-load.cpp - ) - -add_executable(tests-matrix-load ${SRC_TEST_MATRIX_LOAD}) -target_include_directories(tests-matrix-load - PRIVATE - "${src_libs_antares}/array/include" - "${src_libs_antares}/io/include" - "${src_libs_antares}/jit/include" - "${src_libs_antares}/memory/include" - "${CMAKE_CURRENT_SOURCE_DIR}/logs" - "${CMAKE_CURRENT_SOURCE_DIR}/jit" - "${CMAKE_SOURCE_DIR}/tests/src/libs" -) - - -target_link_libraries(tests-matrix-load - PRIVATE - matrix - yuni-static-core - Boost::unit_test_framework - antares-core -) - -# Storing tests-matrix-load under the folder Unit-tests in the IDE -set_target_properties(tests-matrix-load PROPERTIES FOLDER Unit-tests) - -add_test(NAME load-matrix COMMAND tests-matrix-load) - -set_property(TEST load-matrix PROPERTY LABELS unit) - - -add_executable(test-utils test_utils.cpp) -target_link_libraries(test-utils - PRIVATE - Boost::unit_test_framework - Antares::utils - yuni-static-core -) -set_target_properties(test-utils PROPERTIES FOLDER Unit-tests/test-utils) - -add_test(NAME test-utils COMMAND test-utils) -set_property(TEST test-utils PROPERTY LABELS unit) +add_boost_test(tests-matrix-load + SRC + array/fill-matrix.h + array/matrix-bypass-load.h + array/tests-matrix-load.h + array/tests-matrix-load.cpp + INCLUDE + "${src_libs_antares}/array/include" + "${src_libs_antares}/io/include" + "${src_libs_antares}/jit/include" + "${src_libs_antares}/memory/include" + "${CMAKE_CURRENT_SOURCE_DIR}/logs" + "${CMAKE_CURRENT_SOURCE_DIR}/jit" + "${CMAKE_SOURCE_DIR}/tests/src/libs" + LIBS + matrix + yuni-static-core + antares-core) + +# Test utilities +add_boost_test(test-utils + SRC test_utils.cpp + LIBS + Antares::utils + yuni-static-core) diff --git a/src/tests/src/libs/antares/antlr4-interface/CMakeLists.txt b/src/tests/src/libs/antares/antlr4-interface/CMakeLists.txt index f74e12981c..508489e76a 100644 --- a/src/tests/src/libs/antares/antlr4-interface/CMakeLists.txt +++ b/src/tests/src/libs/antares/antlr4-interface/CMakeLists.txt @@ -1,20 +1,9 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) find_package(antlr4-runtime CONFIG REQUIRED) -Set(SRCS test_antlr_interface.cpp -) - -set(execname "antlr-interface-test") -add_executable(${execname} ${SRCS}) -target_link_libraries(${execname} - PRIVATE - antlr-interface - Boost::unit_test_framework - ) - - -target_include_directories(${execname} - PRIVATE - ${ANTLR4_INCLUDE_DIR}) -add_test(NAME antlr-interface COMMAND ${execname}) - -set_tests_properties(antlr-interface PROPERTIES LABELS unit) +add_boost_test(antlr-interface-test + SRC test_antlr_interface.cpp + LIBS + antlr-interface + INCLUDE + ${ANTLR4_INCLUDE_DIR}) diff --git a/src/tests/src/libs/antares/benchmarking/CMakeLists.txt b/src/tests/src/libs/antares/benchmarking/CMakeLists.txt index 911f6e12e4..4ad4a1b712 100644 --- a/src/tests/src/libs/antares/benchmarking/CMakeLists.txt +++ b/src/tests/src/libs/antares/benchmarking/CMakeLists.txt @@ -1,15 +1,5 @@ -set(PROJ test-duration-collector) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -add_executable(${PROJ}) -target_sources(${PROJ} PRIVATE test_duration_collector.cpp) -target_link_libraries(${PROJ} - PRIVATE - Antares::benchmarking - Boost::unit_test_framework -) - -set_target_properties(${PROJ} PROPERTIES FOLDER Unit-tests/${PROJ}) - -add_test(NAME ${PROJ} COMMAND ${PROJ}) - -set_property(TEST ${PROJ} PROPERTY LABELS unit) +add_boost_test(test-duration-collector + SRC test_duration_collector.cpp + LIBS Antares::benchmarking) diff --git a/src/tests/src/libs/antares/concurrency/CMakeLists.txt b/src/tests/src/libs/antares/concurrency/CMakeLists.txt index 563d89524f..07004c0290 100644 --- a/src/tests/src/libs/antares/concurrency/CMakeLists.txt +++ b/src/tests/src/libs/antares/concurrency/CMakeLists.txt @@ -1,14 +1,5 @@ -add_executable(test-concurrency) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -target_sources(test-concurrency PRIVATE test_concurrency.cpp) - -target_link_libraries(test-concurrency - PRIVATE - Boost::unit_test_framework - Antares::concurrency -) - -set_target_properties(test-concurrency PROPERTIES FOLDER Unit-tests/test-concurrency) - -add_test(NAME concurrency COMMAND test-concurrency) -set_property(TEST concurrency PROPERTY LABELS unit) +add_boost_test(test-concurrency + SRC test_concurrency.cpp + LIBS Antares::concurrency) diff --git a/src/tests/src/libs/antares/inifile/CMakeLists.txt b/src/tests/src/libs/antares/inifile/CMakeLists.txt index 1670c13084..e99ae8500d 100644 --- a/src/tests/src/libs/antares/inifile/CMakeLists.txt +++ b/src/tests/src/libs/antares/inifile/CMakeLists.txt @@ -1,15 +1,5 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -add_executable(test_inifile_io test_inifile_io.cpp) - -target_link_libraries(test_inifile_io - PRIVATE - inifile - Boost::unit_test_framework -) - -# Storing executable under the folder Unit-tests in the IDE -set_target_properties(test_inifile_io PROPERTIES FOLDER Unit-tests/test_inifile_io) - -add_test(NAME test_inifile_io COMMAND test_inifile_io) - -set_property(TEST test_inifile_io PROPERTY LABELS unit) +add_boost_test(test_inifile_io + SRC test_inifile_io.cpp + LIBS inifile) diff --git a/src/tests/src/libs/antares/study/CMakeLists.txt b/src/tests/src/libs/antares/study/CMakeLists.txt index c4edfc9dfa..b78478c050 100644 --- a/src/tests/src/libs/antares/study/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/CMakeLists.txt @@ -1,3 +1,5 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + set(src_tests_src_libs_antares_study "${CMAKE_CURRENT_SOURCE_DIR}") add_subdirectory(area) @@ -10,14 +12,6 @@ add_subdirectory(parts) add_subdirectory(series) add_subdirectory(parameters) -add_executable(test-study) -target_sources(test-study PRIVATE test_study.cpp) -target_link_libraries(test-study - PRIVATE - Antares::study - Boost::unit_test_framework -) - -add_test(NAME test-study COMMAND test-study) - -set_property(TEST test-study PROPERTY LABELS unit) +add_boost_test(test-study + SRC test_study.cpp + LIBS Antares::study) diff --git a/src/tests/src/libs/antares/study/area/CMakeLists.txt b/src/tests/src/libs/antares/study/area/CMakeLists.txt index fc726aa02c..947fbeed46 100644 --- a/src/tests/src/libs/antares/study/area/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/area/CMakeLists.txt @@ -1,3 +1,5 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # Useful variables definitions set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") @@ -9,29 +11,10 @@ set(SRC_LINK_PROPERTIES files-helper.cpp test-save-link-properties.cpp ) -add_executable(test-save-link-properties ${SRC_LINK_PROPERTIES}) - -target_include_directories(test-save-link-properties - PRIVATE - "${src_libs_antares_study}/include" -) -target_link_libraries(test-save-link-properties - PRIVATE - Boost::unit_test_framework - model_antares -) -# Linux -if(UNIX AND NOT APPLE) -target_link_libraries(test-save-link-properties PRIVATE stdc++fs) -endif() - - -# Storing test-save-link-properties under the folder Unit-tests in the IDE -set_target_properties(test-save-link-properties PROPERTIES FOLDER Unit-tests) - -add_test(NAME save-link-properties COMMAND test-save-link-properties) - -set_property(TEST save-link-properties PROPERTY LABELS unit) +add_boost_test(test-save-link-properties + SRC ${SRC_LINK_PROPERTIES} + INCLUDE "${src_libs_antares_study}/include" + LIBS model_antares) # =================================== # Tests on area's optimization.ini @@ -39,30 +22,9 @@ set_property(TEST save-link-properties PROPERTY LABELS unit) set(SRC_AREA_OPTIMIZATION files-helper.h files-helper.cpp - test-save-area-optimization-ini.cpp -) -add_executable(test-save-area-optimization-ini ${SRC_AREA_OPTIMIZATION}) - -target_include_directories(test-save-area-optimization-ini - PRIVATE - "${src_libs_antares_study}/include" -) -target_link_libraries(test-save-area-optimization-ini - PRIVATE - Boost::unit_test_framework - model_antares -) - -# Linux -if(UNIX AND NOT APPLE) -target_link_libraries(test-save-area-optimization-ini PRIVATE stdc++fs) -endif() - - -# Storing test-save-area-optimization-ini under the folder Unit-tests in the IDE -set_target_properties(test-save-area-optimization-ini PROPERTIES FOLDER Unit-tests) - -add_test(NAME save-area-optimization COMMAND test-save-area-optimization-ini) - -set_property(TEST save-area-optimization PROPERTY LABELS unit) + test-save-area-optimization-ini.cpp) +add_boost_test(test-save-area-optimization-ini + SRC ${SRC_AREA_OPTIMIZATION} + INCLUDE "${src_libs_antares_study}/include" + LIBS model_antares) diff --git a/src/tests/src/libs/antares/study/constraint/CMakeLists.txt b/src/tests/src/libs/antares/study/constraint/CMakeLists.txt index f9940d2247..c5295f5455 100644 --- a/src/tests/src/libs/antares/study/constraint/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/constraint/CMakeLists.txt @@ -1,45 +1,13 @@ -add_executable(test_constraint - test_constraint.cpp - ) - -target_link_libraries(test_constraint - PRIVATE - test_utils_unit - Boost::unit_test_framework - Antares::study - ) - -# Storing test_constraint under the folder Unit-tests in the IDE -set_target_properties(test_constraint PROPERTIES FOLDER Unit-tests) - -if(UNIX AND NOT APPLE) - target_link_libraries(test_constraint PRIVATE stdc++fs) -endif() - -add_test(NAME test_constraint COMMAND test_constraint) - -set_property(TEST test_constraint PROPERTY LABELS unit) - -################ - -add_executable(test_groups - test_group.cpp - ) - -target_link_libraries(test_groups - PRIVATE - test_utils_unit - Boost::unit_test_framework - Antares::study - ) - -# Storing test_constraint under the folder Unit-tests in the IDE -set_target_properties(test_groups PROPERTIES FOLDER Unit-tests) - -if(UNIX AND NOT APPLE) - target_link_libraries(test_groups PRIVATE stdc++fs) -endif() - -add_test(NAME test_groups COMMAND test_groups) - -set_property(TEST test_groups PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + +add_boost_test(test_constraint + SRC test_constraint.cpp + LIBS + test_utils_unit + Antares::study) + +add_boost_test(test_groups + SRC test_group.cpp + LIBS + test_utils_unit + Antares::study) diff --git a/src/tests/src/libs/antares/study/output-folder/CMakeLists.txt b/src/tests/src/libs/antares/study/output-folder/CMakeLists.txt index feca6f3bb8..06988e3e85 100644 --- a/src/tests/src/libs/antares/study/output-folder/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/output-folder/CMakeLists.txt @@ -1,31 +1,9 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # Useful variables definitions set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") -add_executable(test-folder-output study.cpp) - -target_include_directories(test-folder-output - PRIVATE - "${src_libs_antares_study}" -) - -target_link_libraries(test-folder-output - PRIVATE - Boost::unit_test_framework - Antares::study -) - -# Linux -if(UNIX AND NOT APPLE) -target_link_libraries(test-folder-output PRIVATE stdc++fs) -endif() - - -# Storing test-save-link-properties under the folder Unit-tests in the IDE -set_target_properties(test-folder-output PROPERTIES FOLDER Unit-tests) - -import_std_libs(test-folder-output) - -add_test(NAME folder-output COMMAND test-folder-output) -set_property(TEST folder-output PROPERTY LABELS unit) - - +add_boost_test(test-folder-output + SRC study.cpp + INCLUDE "${src_libs_antares_study}" + LIBS Antares::study) diff --git a/src/tests/src/libs/antares/study/parameters/CMakeLists.txt b/src/tests/src/libs/antares/study/parameters/CMakeLists.txt index 7e22aad5d1..39341e10ae 100644 --- a/src/tests/src/libs/antares/study/parameters/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/parameters/CMakeLists.txt @@ -1,20 +1,8 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # ==================================== # Tests on Parameters class # ==================================== -set(SRC_PARAMETERS_TESTS - parameters-tests.cpp -) -add_executable(parameters-tests ${SRC_PARAMETERS_TESTS}) - -target_link_libraries(parameters-tests - PRIVATE - Boost::unit_test_framework - Antares::study -) - -# Storing parameters-tests under the folder Unit-tests in the IDE -set_target_properties(parameters-tests PROPERTIES FOLDER Unit-tests/parameters-tests) - -add_test(NAME parameters-tests COMMAND parameters-tests) - -set_property(TEST parameters-tests PROPERTY LABELS unit) +add_boost_test(parameters-tests + SRC parameters-tests.cpp + LIBS Antares::study) diff --git a/src/tests/src/libs/antares/study/parts/hydro/CMakeLists.txt b/src/tests/src/libs/antares/study/parts/hydro/CMakeLists.txt index 52937063c8..8b5dee7bc3 100644 --- a/src/tests/src/libs/antares/study/parts/hydro/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/parts/hydro/CMakeLists.txt @@ -1,44 +1,15 @@ -# Hydro data reader -set(SRC_HYDRO_READER - test-hydroreader-class.cpp) - -add_executable(test-hydro-reader ${SRC_HYDRO_READER}) - -target_link_libraries(test-hydro-reader - PRIVATE - Boost::unit_test_framework - Antares::study - test_utils_unit -) - -# Linux -if(UNIX AND NOT APPLE) - target_link_libraries(test-hydro-reader PRIVATE stdc++fs) -endif() - -set_target_properties(test-hydro-reader PROPERTIES FOLDER Unit-tests/hydro) -add_test(NAME hydro-reader COMMAND test-hydro-reader) -set_property(TEST hydro-reader PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) +# Hydro data reader +add_boost_test(test-hydro-reader + SRC test-hydroreader-class.cpp + LIBS + Antares::study + test_utils_unit) # Hydro series -set(SRC_HYDRO_SERIES - test-hydro-series.cpp) - -add_executable(test-hydro-series ${SRC_HYDRO_SERIES}) - -target_link_libraries(test-hydro-series - PRIVATE - Boost::unit_test_framework - Antares::study - test_utils_unit -) - -# Linux -if(UNIX AND NOT APPLE) - target_link_libraries(test-hydro-series PRIVATE stdc++fs) -endif() - -set_target_properties(test-hydro-series PROPERTIES FOLDER Unit-tests/hydro) -add_test(NAME hydro-series COMMAND test-hydro-series) -set_property(TEST hydro-series PROPERTY LABELS unit) \ No newline at end of file +add_boost_test(test-hydro-series + SRC test-hydro-series.cpp + LIBS + Antares::study + test_utils_unit) diff --git a/src/tests/src/libs/antares/study/scenario-builder/CMakeLists.txt b/src/tests/src/libs/antares/study/scenario-builder/CMakeLists.txt index 2f65c75b70..2444753e9c 100644 --- a/src/tests/src/libs/antares/study/scenario-builder/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/scenario-builder/CMakeLists.txt @@ -1,62 +1,26 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # Useful variables definitions set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") # ==================================== # Tests on reading scenario-builder # ==================================== -set(SRC_SC_BUILDER_READ - test-sc-builder-file-read-line.cpp -) -add_executable(test-sc-builder-file-read-line ${SRC_SC_BUILDER_READ}) - -target_include_directories(test-sc-builder-file-read-line - PRIVATE - "${src_libs_antares_study}/include" -) - -target_link_libraries(test-sc-builder-file-read-line - PRIVATE - Boost::unit_test_framework - model_antares -) -# Linux -if(UNIX AND NOT APPLE) -target_link_libraries(test-sc-builder-file-read-line PRIVATE stdc++fs) -endif() - - -# Storing test-sc-builder-file-read-line under the folder Unit-tests in the IDE -add_test(NAME sc-builder-file-read-line COMMAND test-sc-builder-file-read-line) -set_property(TEST sc-builder-file-read-line PROPERTY LABELS unit) -set_target_properties(test-sc-builder-file-read-line PROPERTIES FOLDER Unit-tests/sc-builder) - - +add_boost_test(test-sc-builder-file-read-line + SRC test-sc-builder-file-read-line.cpp + INCLUDE "${src_libs_antares_study}/include" + LIBS model_antares) # ==================================== # Tests on saving scenario-builder # ==================================== -set(SRC_SC_BUILDER_SAVE - test-sc-builder-file-save.cpp - "${src_tests_src_libs_antares_study}/area/files-helper.cpp" -) -add_executable(test-sc-builder-file-save ${SRC_SC_BUILDER_SAVE}) - -target_include_directories(test-sc-builder-file-save - PRIVATE - "${src_libs_antares_study}" - "${src_libs_antares_study}/scenario-builder" - "${src_tests_src_libs_antares_study}" -) - -target_link_libraries(test-sc-builder-file-save - PRIVATE - Boost::unit_test_framework - model_antares -) - -# Storing test-sc-builder-file-savee under the folder Unit-tests in the IDE -set_target_properties(test-sc-builder-file-save PROPERTIES FOLDER Unit-tests/sc-builder) - -add_test(NAME sc-builder-file-save COMMAND test-sc-builder-file-save) - -set_property(TEST sc-builder-file-save PROPERTY LABELS unit) +add_boost_test(test-sc-builder-file-save + SRC + test-sc-builder-file-save.cpp + "${src_tests_src_libs_antares_study}/area/files-helper.cpp" + INCLUDE + "${src_libs_antares_study}" + "${src_libs_antares_study}/scenario-builder" + "${src_tests_src_libs_antares_study}" + LIBS + model_antares) diff --git a/src/tests/src/libs/antares/study/series/CMakeLists.txt b/src/tests/src/libs/antares/study/series/CMakeLists.txt index 27c31d1728..7777264127 100644 --- a/src/tests/src/libs/antares/study/series/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/series/CMakeLists.txt @@ -1,22 +1,11 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # ==================================== # Tests on TimeSeries class # ==================================== -set(SRC_TIMESERIES_TESTS - timeseries-tests.cpp -) -add_executable(timeseries-tests ${SRC_TIMESERIES_TESTS}) - -target_link_libraries(timeseries-tests - PRIVATE - Antares::array - Boost::unit_test_framework - Antares::series - antares-solver-simulation -) - -# Storing timeseries-tests under the folder Unit-tests in the IDE -set_target_properties(timeseries-tests PROPERTIES FOLDER Unit-tests/timeseries-tests) - -add_test(NAME timeseries-tests COMMAND timeseries-tests) - -set_property(TEST timeseries-tests PROPERTY LABELS unit) +add_boost_test(timeseries-tests + SRC timeseries-tests.cpp + LIBS + Antares::array + Antares::series + antares-solver-simulation) 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 045e8ed944..afa269be4e 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 @@ -1,33 +1,6 @@ -# Useful variables definitions -set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -# ==================================== -# Tests on reading scenario-builder -# ==================================== -set(SRC_SC_BUILDER_READ - short-term-storage-input-output.cpp -) -add_executable(short-term-storage-input ${SRC_SC_BUILDER_READ}) - -target_include_directories(short-term-storage-input - PRIVATE - "${src_libs_antares_study}/parts/short-term-storage" -) - -target_link_libraries(short-term-storage-input - PRIVATE - Boost::unit_test_framework - model_antares -) -# Linux -if(UNIX AND NOT APPLE) -target_link_libraries(short-term-storage-input PRIVATE stdc++fs) -endif() - - -# Storing short-term-storage-input under the folder Unit-tests in the IDE -set_target_properties(short-term-storage-input PROPERTIES FOLDER Unit-tests/short-term-storage-input) - -add_test(NAME short-term-storage-input COMMAND short-term-storage-input) - -set_property(TEST short-term-storage-input PROPERTY LABELS unit) +add_boost_test(short-term-storage-input + SRC short-term-storage-input-output.cpp + LIBS model_antares + INCLUDE "${src_libs_antares_study}/parts/short-term-storage") diff --git a/src/tests/src/libs/antares/study/thermal-price-definition/CMakeLists.txt b/src/tests/src/libs/antares/study/thermal-price-definition/CMakeLists.txt index 43c7aca74e..1b37de47a3 100644 --- a/src/tests/src/libs/antares/study/thermal-price-definition/CMakeLists.txt +++ b/src/tests/src/libs/antares/study/thermal-price-definition/CMakeLists.txt @@ -1,3 +1,5 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # Useful variables definitions set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") set(src_libs_antares_checks "${CMAKE_SOURCE_DIR}/libs/antares/checks") @@ -5,36 +7,15 @@ set(src_libs_antares_checks "${CMAKE_SOURCE_DIR}/libs/antares/checks") # ==================================== # Tests on thermal price definition # ==================================== -set(SRC_THERM_PRICE_DEF - thermal-price-definition.cpp -) -add_executable(thermal-price-definition ${SRC_THERM_PRICE_DEF}) - -target_include_directories(thermal-price-definition - PRIVATE - "${src_libs_antares_study}/parts/thermal" - "${src_libs_antares_study}/area" - "${src_libs_antares_study}" - "${src_libs_antares_checks}" -) - -target_link_libraries(thermal-price-definition - PRIVATE - Boost::unit_test_framework - checks - Antares::study - Antares::exception - Antares::checks -) -# Linux -if(UNIX AND NOT APPLE) -target_link_libraries(thermal-price-definition PRIVATE stdc++fs) -endif() - - -# Storing thermal-price-definition under the folder Unit-tests in the IDE -set_target_properties(thermal-price-definition PROPERTIES FOLDER Unit-tests/thermal-price-definition) - -add_test(NAME thermal-price-definition COMMAND thermal-price-definition) - -set_property(TEST thermal-price-definition PROPERTY LABELS unit) +add_boost_test(thermal-price-definition + SRC thermal-price-definition.cpp + LIBS + checks + Antares::study + Antares::exception + Antares::checks + INCLUDE + "${src_libs_antares_study}/parts/thermal" + "${src_libs_antares_study}/area" + "${src_libs_antares_study}" + "${src_libs_antares_checks}") diff --git a/src/tests/src/libs/antares/writer/CMakeLists.txt b/src/tests/src/libs/antares/writer/CMakeLists.txt index b85bcce05c..36f5a678a0 100644 --- a/src/tests/src/libs/antares/writer/CMakeLists.txt +++ b/src/tests/src/libs/antares/writer/CMakeLists.txt @@ -1,15 +1,10 @@ -# Zip writer -add_executable(test-writer test_writer.cpp) - -target_link_libraries(test-writer - PRIVATE - Boost::unit_test_framework - Antares::result_writer - test_utils_unit - MINIZIP::minizip -) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -set_target_properties(test-writer PROPERTIES FOLDER Unit-tests/test-writer) +# Zip writer +add_boost_test(test-writer + SRC test_writer.cpp + LIBS + Antares::result_writer + test_utils_unit + MINIZIP::minizip) -add_test(NAME writer COMMAND test-writer) -set_tests_properties(writer PROPERTIES LABELS unit) diff --git a/src/tests/src/libs/antares/writer/test_zip_writer.cpp b/src/tests/src/libs/antares/writer/test_zip_writer.cpp new file mode 100644 index 0000000000..ffa0d89fed --- /dev/null +++ b/src/tests/src/libs/antares/writer/test_zip_writer.cpp @@ -0,0 +1,127 @@ +/* +** Copyright 2007-2023 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 +*/ +#define BOOST_TEST_MODULE test - writer tests + +#include +#include + +#include "yuni/job/queue/service.h" + +#include "antares/benchmarking/DurationCollector.h" +#include "antares/writer/i_writer.h" +#include "antares/writer/writer_factory.h" + +#include "utils.h" + +extern "C" +{ +#include +#include +#include +#include +} + +using namespace Yuni::Job; +using Antares::Solver::IResultWriter; +using Benchmarking::IDurationCollector; +using Benchmarking::NullDurationCollector; + +// Handles lifetime of necessary objects +struct TestContext +{ + std::shared_ptr threadPool; + std::unique_ptr durationCollector; + std::shared_ptr writer; +}; + +std::shared_ptr createThreadPool(int size) +{ + auto threadPool = std::make_shared(); + threadPool->maximumThreadCount(size); + threadPool->start(); + return threadPool; +} + +std::string removeExtension(const std::string& name, const std::string& ext) +{ + int length = name.size(); + if (name.size() > ext.size() && name.substr(length - ext.size()) == ext) + { + return name.substr(0, length - ext.size()); + } + return name; +} + +TestContext createContext(const std::filesystem::path zipPath, int threadCount) +{ + auto threadPool = createThreadPool(threadCount); + std::unique_ptr + durationCollector = std::make_unique(); + std::string archiveName = zipPath.string(); + auto writer = Antares::Solver::resultWriterFactory(Antares::Data::zipArchive, + removeExtension(zipPath.string(), ".zip"), + threadPool, + *durationCollector); + return {threadPool, std::move(durationCollector), writer}; +} + +using ZipReaderHandle = void*; + +void checkZipContent(ZipReaderHandle handle, + const std::string& path, + const std::string& expectedContent) +{ + BOOST_CHECK(mz_zip_reader_locate_entry(handle, path.c_str(), 0) == MZ_OK); + BOOST_CHECK(mz_zip_reader_entry_open(handle) == MZ_OK); + char buffer[4096]; + int bytesRead = mz_zip_reader_entry_read(handle, buffer, sizeof(buffer)); + std::string stringRead(buffer, bytesRead); + BOOST_CHECK(stringRead == expectedContent); + mz_zip_reader_entry_close(handle); +} + +BOOST_AUTO_TEST_CASE(test_zip) +{ + // Writer some content to test.zip, possibly from 2 threads + auto working_tmp_dir = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + auto zipPath = working_tmp_dir / "test.zip"; + auto context = createContext(zipPath, 2); + std::string content1 = "test-content1"; + std::string content2 = "test-content2"; + context.writer->addEntryFromBuffer("test-path", content1); + context.writer->addEntryFromBuffer("test-second-path", content2); + context.writer->flush(); + context.writer->finalize(true); + + // Check content is correct + ZipReaderHandle readerHandle = mz_zip_reader_create(); + std::string zipPathStr = zipPath.string(); + BOOST_CHECK(mz_zip_reader_open_file(readerHandle, zipPathStr.c_str()) == MZ_OK); + checkZipContent(readerHandle, "test-path", "test-content1"); + checkZipContent(readerHandle, "test-second-path", "test-content2"); + mz_zip_reader_close(readerHandle); +} diff --git a/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt b/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt index 1633966826..dbb1a0535a 100644 --- a/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt +++ b/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt @@ -1,17 +1,5 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -find_package(yaml-cpp CONFIG REQUIRED) -Set(SRCS test_yaml_parser.cpp -) - -set(execname "yaml-parser-test") -add_executable(${execname} ${SRCS}) -target_link_libraries(${execname} - PRIVATE - yaml-cpp - Boost::unit_test_framework - ) - - -add_test(NAME yaml-parser COMMAND ${execname}) - -set_tests_properties(yaml-parser PROPERTIES LABELS unit) +add_boost_test(yaml-parser-test + SRC test_yaml_parser.cpp + LIBS yaml-cpp) diff --git a/src/tests/src/solver/expressions/CMakeLists.txt b/src/tests/src/solver/expressions/CMakeLists.txt index 4eeca6d0fe..c41dde6d3b 100644 --- a/src/tests/src/solver/expressions/CMakeLists.txt +++ b/src/tests/src/solver/expressions/CMakeLists.txt @@ -1,31 +1,17 @@ -set(EXECUTABLE_NAME test-expressions) -add_executable(${EXECUTABLE_NAME}) - -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_main.cpp - test_nodes.cpp - test_PrintAndEvalNodes.cpp - test_TimeIndexVisitor.cpp - test_SubstitutionVisitor.cpp - test_LinearVisitor.cpp - test_CompareVisitor.cpp - test_CloneVisitor.cpp - test_DeepWideTrees.cpp - test_Iterators.cpp - test_AstDOTStyleVisitor.cpp -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - solver-expressions - solver-expressions-iterators -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -add_test(NAME test-expressions COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-expressions PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) +add_boost_test(test-expressions + SRC + test_main.cpp + test_nodes.cpp + test_PrintAndEvalNodes.cpp + test_TimeIndexVisitor.cpp + test_SubstitutionVisitor.cpp + test_LinearVisitor.cpp + test_CompareVisitor.cpp + test_CloneVisitor.cpp + test_DeepWideTrees.cpp + test_Iterators.cpp + test_AstDOTStyleVisitor.cpp + LIBS + solver-expressions + solver-expressions-iterators) diff --git a/src/tests/src/solver/infeasible-problem-analysis/CMakeLists.txt b/src/tests/src/solver/infeasible-problem-analysis/CMakeLists.txt index 8a8a0e9544..845f1cd124 100644 --- a/src/tests/src/solver/infeasible-problem-analysis/CMakeLists.txt +++ b/src/tests/src/solver/infeasible-problem-analysis/CMakeLists.txt @@ -1,16 +1,7 @@ -add_executable(test-unfeasible-problem-analyzer) -target_sources(test-unfeasible-problem-analyzer - PRIVATE - test-unfeasible-problem-analyzer.cpp) -target_link_libraries(test-unfeasible-problem-analyzer - PRIVATE - Boost::unit_test_framework - infeasible_problem_analysis - ortools::ortools -) - -add_test(NAME test-unfeasible-problem-analyzer COMMAND test-unfeasible-problem-analyzer) - -# Storing the executable under the folder Unit-tests in Visual Studio -set_target_properties(test-unfeasible-problem-analyzer PROPERTIES FOLDER Unit-tests) -set_property(TEST test-unfeasible-problem-analyzer PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) +add_boost_test(test-unfeasible-problem-analyzer + SRC + test-unfeasible-problem-analyzer.cpp + LIBS + infeasible_problem_analysis + ortools::ortools) diff --git a/src/tests/src/solver/lps/CMakeLists.txt b/src/tests/src/solver/lps/CMakeLists.txt index 89121152ba..596dd71ff6 100644 --- a/src/tests/src/solver/lps/CMakeLists.txt +++ b/src/tests/src/solver/lps/CMakeLists.txt @@ -1,20 +1,5 @@ -set(EXECUTABLE_NAME test-lps) -add_executable(${EXECUTABLE_NAME}) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_lps.cpp -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - antares-solver-simulation -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -add_test(NAME test-lps COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-lps PROPERTY LABELS unit) +add_boost_test(test-lps + SRC test_lps.cpp + LIBS antares-solver-simulation) diff --git a/src/tests/src/solver/modelParser/CMakeLists.txt b/src/tests/src/solver/modelParser/CMakeLists.txt index 9f884255d8..5969d66c16 100644 --- a/src/tests/src/solver/modelParser/CMakeLists.txt +++ b/src/tests/src/solver/modelParser/CMakeLists.txt @@ -1,31 +1,18 @@ -# Add source files -set(SOURCE_FILES - testModelParser.cpp - testModelTranslator.cpp - testConvertorVisitor.cpp - test_full.cpp - enum_operators.h -) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -# Add executable -add_executable(TestModelParser ${SOURCE_FILES}) - -# Link libraries -target_link_libraries(TestModelParser - PRIVATE - Boost::unit_test_framework - Antares::solver-expressions - Antares::modelConverter - Antares::modelParser - Antares::antares-study-system-model - Antares::antlr-interface -) - -# Storing test-toybox under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -# Add the test -add_test(NAME TestModelParser COMMAND TestModelParser) - -# Set test properties -set_property(TEST TestModelParser PROPERTY LABELS unit) +add_boost_test(TestModelParser + SRC + testModelParser.cpp + testModelTranslator.cpp + testConvertorVisitor.cpp + test_full.cpp + enum_operators.h + testSystemParser.cpp + testSystemConverter.cpp + LIBS + Antares::solver-expressions + Antares::modelConverter + Antares::modelParser + Antares::systemParser + Antares::antares-study-system-model + Antares::antlr-interface) diff --git a/src/tests/src/solver/modelParser/testSystemConverter.cpp b/src/tests/src/solver/modelParser/testSystemConverter.cpp new file mode 100644 index 0000000000..1ba1bbb1fd --- /dev/null +++ b/src/tests/src/solver/modelParser/testSystemConverter.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include +#include +#include "antares/solver/modelConverter/modelConverter.h" +#include "antares/solver/modelParser/parser.h" +#include "antares/study/system-model/library.h" + +using namespace std::string_literals; +using namespace Antares::Solver; +using namespace Antares::Study; + +struct LibraryObjects +{ + ModelParser::Model model1{.id = "node", + .description = "description", + .parameters = {{"cost", true, false}}, + .variables = {}, + .ports = {}, + .port_field_definitions = {}, + .constraints = {{"constraint1", "cost"}}, + .objective = ""}; + + SystemParser::Parser parser; + ModelParser::Library library; + std::vector libraries; + + LibraryObjects() + { + library.id = "std"; + library.models = {model1}; + libraries = {ModelConverter::convert(library)}; + } + + ~LibraryObjects() = default; +}; + +BOOST_FIXTURE_TEST_CASE(full_model_system, LibraryObjects) +{ + const auto system = R"( + system: + id: base_system + description: real application model + model-libraries: [std] + components: + - id: N + model: std.node + scenario-group: group-234 + parameters: + - id: cost + type: constant + value: 30 + )"s; + + SystemParser::System systemObj = parser.parse(system); + + auto systemModel = SystemConverter::convert(systemObj, libraries); + + BOOST_CHECK_EQUAL(systemModel.Components().size(), 1); + BOOST_CHECK_EQUAL(systemModel.Components().at("N").Id(), "N"); + BOOST_CHECK_EQUAL(systemModel.Components().at("N").getModel()->Id(), "node"); + BOOST_CHECK_EQUAL(systemModel.Components().at("N").getParameterValue("cost"), 30); +} + +BOOST_FIXTURE_TEST_CASE(bad_param_name_in_component, LibraryObjects) +{ + const auto system = R"( + system: + id: base_system + description: real application model + model-libraries: [std] + components: + - id: N + model: std.node + scenario-group: group-234 + parameters: + - id: param_not_in_model + type: constant + value: 30 + )"s; + + SystemParser::System systemObj = parser.parse(system); + + BOOST_CHECK_THROW(SystemConverter::convert(systemObj, libraries), std::invalid_argument); +} + +BOOST_FIXTURE_TEST_CASE(library_not_existing, LibraryObjects) +{ + const auto system = R"( + system: + id: base_system + model-libraries: [abc] + components: + - id: N + model: abc.node + scenario-group: group-234 + )"s; + + SystemParser::System systemObj = parser.parse(system); + + BOOST_CHECK_THROW(SystemConverter::convert(systemObj, libraries), std::runtime_error); +} + +BOOST_FIXTURE_TEST_CASE(model_not_existing, LibraryObjects) +{ + const auto system = R"( + system: + id: base_system + model-libraries: [std] + components: + - id: N + model: std.abc + scenario-group: group-234 + )"s; + + SystemParser::System systemObj = parser.parse(system); + + BOOST_CHECK_THROW(SystemConverter::convert(systemObj, libraries), std::runtime_error); +} + +BOOST_FIXTURE_TEST_CASE(bad_library_model_format, LibraryObjects) +{ + const auto system = R"( + system: + id: base_system + model-libraries: [std] + components: + - id: N + model: std___node + scenario-group: group-234 + parameters: + - id: cost + type: constant + value: 30 + )"s; + + SystemParser::System systemObj = parser.parse(system); + + BOOST_CHECK_THROW(SystemConverter::convert(systemObj, libraries), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(Full_system_test) +{ + const auto libraryYaml = R"( + library: + id: std + description: Standard library + port-types: [] + + models: + - id: generator + description: A basic generator model + parameters: + - id: cost + time-dependent: false + scenario-dependent: false + - id: p_max + time-dependent: false + scenario-dependent: false + variables: + - id: generation + lower-bound: 0 + upper-bound: p_max + ports: + - id: injection_port + type: flow + port-field-definitions: + - port: injection_port + field: flow + definition: generation + objective: cost * generation + + - id: node + description: A basic balancing node model + ports: + - id: injection_port + type: flow + binding-constraints: + - id: balance + expression: injection_port.flow = 0 + )"s; + + const auto libraryYaml2 = R"( + library: + id: mylib + description: Extra library + port-types: [] + + models: + - id: demand + description: A basic fixed demand model + parameters: + - id: demand + time-dependent: true + scenario-dependent: true + ports: + - id: injection_port + type: flow + port-field-definitions: + - port: injection_port + field: flow + definition: -demand + )"s; + + const auto systemYaml = R"( + system: + id: system1 + description: basic description + model-libraries: [std, mylib] + + components: + - id: N + model: std.node + scenario-group: group-234 + + - id: G + model: std.generator + scenario-group: group-234 + parameters: + - id: cost + type: constant + value: 30 + - id: p_max + type: constant + value: 100 + + - id: D + model: mylib.demand + scenario-group: group-qsf + parameters: + - id: demand + type: constant + value: 100 + )"s; + + ModelParser::Parser parserModel; + SystemParser::Parser parserSystem; + + std::vector libraries; + libraries.push_back(ModelConverter::convert(parserModel.parse(libraryYaml))); + libraries.push_back(ModelConverter::convert(parserModel.parse(libraryYaml2))); + + SystemParser::System systemObj = parserSystem.parse(systemYaml); + auto systemModel = SystemConverter::convert(systemObj, libraries); + + BOOST_CHECK_EQUAL(systemModel.Components().size(), 3); + BOOST_CHECK_EQUAL(systemModel.Components().at("N").Id(), "N"); + BOOST_CHECK_EQUAL(systemModel.Components().at("N").getModel()->Id(), "node"); + BOOST_CHECK_EQUAL(systemModel.Components().at("N").getScenarioGroupId(), "group-234"); + + BOOST_CHECK_EQUAL(systemModel.Components().at("G").getModel()->Id(), "generator"); + BOOST_CHECK_EQUAL(systemModel.Components().at("G").getParameterValue("cost"), 30); + BOOST_CHECK_EQUAL(systemModel.Components().at("G").getParameterValue("p_max"), 100); + + BOOST_CHECK_EQUAL(systemModel.Components().at("D").getModel()->Id(), "demand"); + BOOST_CHECK_EQUAL(systemModel.Components().at("D").getParameterValue("demand"), 100); +} diff --git a/src/tests/src/solver/modelParser/testSystemParser.cpp b/src/tests/src/solver/modelParser/testSystemParser.cpp new file mode 100644 index 0000000000..892e29467d --- /dev/null +++ b/src/tests/src/solver/modelParser/testSystemParser.cpp @@ -0,0 +1,184 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include +#include + +using namespace std::string_literals; +using namespace Antares::Solver; +using namespace Antares::Study; + +BOOST_AUTO_TEST_CASE(empty_system) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: "" + description: "" + model-libraries: [] + components: [] + )"s; + SystemParser::System systemObj = parser.parse(system); + BOOST_CHECK(systemObj.id.empty()); + BOOST_CHECK(systemObj.libraries.empty()); + BOOST_CHECK(systemObj.components.empty()); +} + +BOOST_AUTO_TEST_CASE(simple_id) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: base_system + description: a basic system + )"s; + SystemParser::System systemObj = parser.parse(system); + BOOST_CHECK_EQUAL(systemObj.id, "base_system"); +} + +BOOST_AUTO_TEST_CASE(libraries_one_model) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: base_system + model-libraries: [abc] + )"s; + SystemParser::System systemObj = parser.parse(system); + BOOST_CHECK_EQUAL(systemObj.libraries[0], "abc"); +} + +BOOST_AUTO_TEST_CASE(libraries_list_of_models) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: base_system + description: 3 model libraries + model-libraries: [abc, def, 123] + components: [] + )"s; + SystemParser::System systemObj = parser.parse(system); + BOOST_CHECK_EQUAL(systemObj.libraries[0], "abc"); + BOOST_CHECK_EQUAL(systemObj.libraries[1], "def"); + BOOST_CHECK_EQUAL(systemObj.libraries[2], "123"); + BOOST_CHECK(systemObj.components.empty()); +} + +BOOST_AUTO_TEST_CASE(one_component) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: base_system + description: one simple component + components: + - id: N + model: abcde + scenario-group: group-234 + )"s; + SystemParser::System systemObj = parser.parse(system); + BOOST_CHECK_EQUAL(systemObj.components[0].id, "N"); + BOOST_CHECK_EQUAL(systemObj.components[0].model, "abcde"); + BOOST_CHECK_EQUAL(systemObj.components[0].scenarioGroup, "group-234"); +} + +BOOST_AUTO_TEST_CASE(two_components) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: base_system + description: two components + components: + - id: N + model: std.node + scenario-group: group-234 + - id: G + model: std.generator + scenario-group: group-thermal + )"s; + SystemParser::System systemObj = parser.parse(system); + BOOST_CHECK_EQUAL(systemObj.components[0].id, "N"); + BOOST_CHECK_EQUAL(systemObj.components[0].model, "std.node"); + BOOST_CHECK_EQUAL(systemObj.components[0].scenarioGroup, "group-234"); + BOOST_CHECK_EQUAL(systemObj.components[1].id, "G"); + BOOST_CHECK_EQUAL(systemObj.components[1].model, "std.generator"); + BOOST_CHECK_EQUAL(systemObj.components[1].scenarioGroup, "group-thermal"); +} + +BOOST_AUTO_TEST_CASE(component_parameter) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: base_system + description: one component with one parameter + components: + - id: N + model: std.node + scenario-group: group-234 + parameters: + - id: cost + type: constant + value: 30 + )"s; + SystemParser::System systemObj = parser.parse(system); + const auto& param = systemObj.components[0].parameters[0]; + BOOST_CHECK_EQUAL(param.id, "cost"); + BOOST_CHECK_EQUAL(param.type, "constant"); + BOOST_CHECK_EQUAL(param.value, 30); +} + +BOOST_AUTO_TEST_CASE(component_two_parameters) +{ + SystemParser::Parser parser; + const auto system = R"( + system: + id: base_system + description: one component with one parameter + components: + - id: N + model: std.node + scenario-group: group-234 + parameters: + - id: cost + type: constant + value: 30 + - id: p_max + type: constant + value: 100 + )"s; + SystemParser::System systemObj = parser.parse(system); + const auto& param = systemObj.components[0].parameters[0]; + const auto& param2 = systemObj.components[0].parameters[1]; + BOOST_CHECK_EQUAL(param.id, "cost"); + BOOST_CHECK_EQUAL(param.type, "constant"); + BOOST_CHECK_EQUAL(param.value, 30); + BOOST_CHECK_EQUAL(param2.id, "p_max"); + BOOST_CHECK_EQUAL(param2.type, "constant"); + BOOST_CHECK_EQUAL(param2.value, 100); +} diff --git a/src/tests/src/solver/modeler/CMakeLists.txt b/src/tests/src/solver/modeler/CMakeLists.txt index 53d257b04e..d532a0a77d 100644 --- a/src/tests/src/solver/modeler/CMakeLists.txt +++ b/src/tests/src/solver/modeler/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(api) +add_subdirectory(loadFiles) diff --git a/src/tests/src/solver/modeler/api/CMakeLists.txt b/src/tests/src/solver/modeler/api/CMakeLists.txt index ae048140aa..76915ff504 100644 --- a/src/tests/src/solver/modeler/api/CMakeLists.txt +++ b/src/tests/src/solver/modeler/api/CMakeLists.txt @@ -1,24 +1,12 @@ -set(EXECUTABLE_NAME unit-tests-for-modeler-api) -add_executable(${EXECUTABLE_NAME}) - -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_main.cpp - testModelerLinearProblemWithOrtools.cpp - testModelerLPbuilder.cpp -) -target_include_directories(${EXECUTABLE_NAME} - PRIVATE - "${src_solver_optimisation}" -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - Antares::modeler-ortools-impl -) - -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) -add_test(NAME ${EXECUTABLE_NAME} COMMAND ${EXECUTABLE_NAME}) -set_property(TEST ${EXECUTABLE_NAME} PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + +add_boost_test(unit-tests-for-modeler-api + SRC + test_main.cpp + testModelerLinearProblemWithOrtools.cpp + testModelerLPbuilder.cpp + INCLUDE + "${src_solver_optimisation}" + LIBS + Antares::modeler-ortools-impl) diff --git a/src/tests/src/solver/modeler/loadFiles/CMakeLists.txt b/src/tests/src/solver/modeler/loadFiles/CMakeLists.txt new file mode 100644 index 0000000000..896c759e06 --- /dev/null +++ b/src/tests/src/solver/modeler/loadFiles/CMakeLists.txt @@ -0,0 +1,10 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + +add_boost_test(loadFiles + SRC + testLoadModelerFiles.cpp + testParameters.cpp + LIBS + Antares::modelerParameters + Antares::loadModelerFiles + test_utils_unit) diff --git a/src/tests/src/solver/modeler/loadFiles/testLoadModelerFiles.cpp b/src/tests/src/solver/modeler/loadFiles/testLoadModelerFiles.cpp new file mode 100644 index 0000000000..61fe4decf1 --- /dev/null +++ b/src/tests/src/solver/modeler/loadFiles/testLoadModelerFiles.cpp @@ -0,0 +1,220 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ +#define WIN32_LEAN_AND_MEAN + +#include +#define BOOST_TEST_MODULE load modeler files + +#include + +#include + +#include "files-system.h" + +BOOST_AUTO_TEST_SUITE(test_modeler_files_loading) + +namespace fs = std::filesystem; + +struct FixtureLoadFile +{ + fs::path studyPath; + fs::path inputPath; + fs::path libraryDirPath; + + FixtureLoadFile() + { + studyPath = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + inputPath = createFolder(studyPath.string(), "input"); + libraryDirPath = createFolder(inputPath.string(), "model-libraries"); + } + + ~FixtureLoadFile() + { + fs::remove_all(studyPath); + } +}; + +BOOST_AUTO_TEST_CASE(files_not_existing) +{ + fs::path studyPath = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + std::vector libraries; + + BOOST_CHECK_THROW(Antares::Solver::LoadFiles::loadLibraries(studyPath), std::runtime_error); + BOOST_CHECK_THROW(Antares::Solver::LoadFiles::loadSystem(studyPath, libraries), + std::runtime_error); +} + +BOOST_FIXTURE_TEST_CASE(read_one_lib_treile, FixtureLoadFile) +{ + std::ofstream libStream(libraryDirPath / "simple.yml"); + libStream << R"( + library: + id: lib_id + description: lib_description + port-types: [] + models: [] + )"; + libStream.close(); + + auto libraries = Antares::Solver::LoadFiles::loadLibraries(studyPath); + BOOST_CHECK_EQUAL(libraries[0].Id(), "lib_id"); +} + +BOOST_FIXTURE_TEST_CASE(dont_read_bad_extension, FixtureLoadFile) +{ + createFile(libraryDirPath.string(), "abc.txt"); + auto libraries = Antares::Solver::LoadFiles::loadLibraries(studyPath); + BOOST_CHECK(libraries.empty()); +} + +BOOST_FIXTURE_TEST_CASE(incorrect_library, FixtureLoadFile) +{ + std::ofstream libStream(libraryDirPath / "simple.yml"); + libStream << R"( + library: + port-types: [] + models: [] + )"; + libStream.close(); + + BOOST_CHECK_THROW(Antares::Solver::LoadFiles::loadLibraries(studyPath), std::runtime_error); +} + +BOOST_FIXTURE_TEST_CASE(incorrect_library2, FixtureLoadFile) +{ + std::ofstream libStream(libraryDirPath / "simple.yml"); + libStream << R"( + library: + id: std + port-types: [] + - id: generator + description: A basic generator model + + )"; + libStream.close(); + BOOST_CHECK_THROW(Antares::Solver::LoadFiles::loadLibraries(studyPath), std::runtime_error); +} + +BOOST_FIXTURE_TEST_CASE(read_several_lib_file, FixtureLoadFile) +{ + std::ofstream libStream(libraryDirPath / "simple.yml"); + libStream << R"( + library: + id: lib_id + description: lib_description + port-types: [] + models: [] + )"; + libStream.close(); + + std::ofstream libStream2(libraryDirPath / "2.yml"); + libStream2 << R"( + library: + id: lib_id2 + description: lib_description + port-types: [] + models: [] + )"; + libStream2.close(); + + std::ofstream libStream3(libraryDirPath / "3.yml"); + libStream3 << R"( + library: + id: lib_id3 + description: lib_description + port-types: [] + models: [] + )"; + libStream3.close(); + + auto libraries = Antares::Solver::LoadFiles::loadLibraries(studyPath); + + auto checkLibIdInVector = [&libraries](const std::string& libId) + { + return std::ranges::find_if(libraries, [&libId](const auto& l) { return l.Id() == libId; }) + != libraries.end(); + }; + + BOOST_CHECK(checkLibIdInVector("lib_id")); + BOOST_CHECK(checkLibIdInVector("lib_id2")); + BOOST_CHECK(checkLibIdInVector("lib_id3")); + + BOOST_CHECK(!checkLibIdInVector("id not in vector")); +} + +BOOST_FIXTURE_TEST_CASE(read_system_file, FixtureLoadFile) +{ + std::ofstream libStream(libraryDirPath / "simple.yml"); + libStream << R"( + library: + id: std + description: lib_description + port-types: [] + models: + - id: generator + description: A basic generator model + + )"; + libStream.close(); + + std::ofstream systemStream(inputPath / "system.yml"); + systemStream << R"( + system: + id: base_system + description: two components + components: + - id: N + model: std.generator + scenario-group: group-234 + )"; + systemStream.close(); + + auto libraries = Antares::Solver::LoadFiles::loadLibraries(studyPath); + auto system = Antares::Solver::LoadFiles::loadSystem(studyPath, libraries); +} + +BOOST_FIXTURE_TEST_CASE(read_invalid_system_file, FixtureLoadFile) +{ + std::ofstream libStream(libraryDirPath / "simple.yml"); + libStream << R"( + library: + id: std + description: lib_description + port-types: [] + models: + - id: generator + description: A basic generator model + + )"; + libStream.close(); + + std::ofstream systemStream(inputPath / "system.yml"); + systemStream << R"( + system: + )"; + systemStream.close(); + + auto libraries = Antares::Solver::LoadFiles::loadLibraries(studyPath); + BOOST_CHECK_THROW(Antares::Solver::LoadFiles::loadSystem(studyPath, libraries), + std::runtime_error); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/src/solver/modeler/loadFiles/testParameters.cpp b/src/tests/src/solver/modeler/loadFiles/testParameters.cpp new file mode 100644 index 0000000000..fb529f3111 --- /dev/null +++ b/src/tests/src/solver/modeler/loadFiles/testParameters.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ +#define WIN32_LEAN_AND_MEAN + +#include + +#include + +#include + +#include "files-system.h" + +BOOST_AUTO_TEST_CASE(read_parameters) +{ + auto studyPath = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + std::ofstream paramStream(studyPath / "parameters.yml"); + paramStream << R"( + solver: sirius + solver-logs: false + solver-parameters: PRESOLVE 1 + no-output: true + )"; + paramStream.close(); + + auto params = Antares::Solver::LoadFiles::loadParameters(studyPath); + BOOST_CHECK_EQUAL(params.solver, "sirius"); + BOOST_CHECK_EQUAL(params.solverLogs, false); + BOOST_CHECK_EQUAL(params.solverParameters, "PRESOLVE 1"); + BOOST_CHECK_EQUAL(params.noOutput, true); +} + +BOOST_AUTO_TEST_CASE(read_parameters_out_of_order) +{ + auto studyPath = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + std::ofstream paramStream(studyPath / "parameters.yml"); + paramStream << R"( + solver-logs: false + solver: sirius + solver-parameters: PRESOLVE 1 + no-output: true + )"; + paramStream.close(); + + auto params = Antares::Solver::LoadFiles::loadParameters(studyPath); + BOOST_CHECK_EQUAL(params.solver, "sirius"); + BOOST_CHECK_EQUAL(params.solverLogs, false); + BOOST_CHECK_EQUAL(params.solverParameters, "PRESOLVE 1"); + BOOST_CHECK_EQUAL(params.noOutput, true); +} + +BOOST_AUTO_TEST_CASE(parameters_missing) +{ + auto studyPath = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + std::ofstream paramStream(studyPath / "parameters.yml"); + paramStream << R"( + solver-logs: false + no-output: true + )"; + paramStream.close(); + + BOOST_CHECK_THROW(Antares::Solver::LoadFiles::loadParameters(studyPath), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(file_missing) +{ + auto studyPath = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + BOOST_CHECK_THROW(Antares::Solver::LoadFiles::loadParameters(studyPath), std::runtime_error); +} diff --git a/src/tests/src/solver/optim-model-filler/CMakeLists.txt b/src/tests/src/solver/optim-model-filler/CMakeLists.txt index fa81c819b7..e6853bf8fc 100644 --- a/src/tests/src/solver/optim-model-filler/CMakeLists.txt +++ b/src/tests/src/solver/optim-model-filler/CMakeLists.txt @@ -1,29 +1,16 @@ -set(EXECUTABLE_NAME unit-tests-for-component-filler) -add_executable(${EXECUTABLE_NAME}) - -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_main.cpp - test_componentFiller.cpp - test_linearExpression.cpp - test_readLinearExpressionVisitor.cpp - test_readLinearConstraintVisitor.cpp -) -target_include_directories(${EXECUTABLE_NAME} - PRIVATE - "${src_solver_optimisation}" -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - antares-study-system-model - Antares::modeler-ortools-impl - Antares::optim-model-filler - test_utils_unit -) - -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) -add_test(NAME ${EXECUTABLE_NAME} COMMAND ${EXECUTABLE_NAME}) -set_property(TEST ${EXECUTABLE_NAME} PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) +add_boost_test(unit-tests-for-component-filler + SRC + test_main.cpp + test_componentFiller.cpp + test_linearExpression.cpp + test_readLinearExpressionVisitor.cpp + test_readLinearConstraintVisitor.cpp + INCLUDE + "${src_solver_optimisation}" + LIBS + antares-study-system-model + Antares::modeler-ortools-impl + Antares::optim-model-filler + test_utils_unit) diff --git a/src/tests/src/solver/optimisation/adequacy_patch.cpp b/src/tests/src/solver/optimisation/adequacy_patch.cpp new file mode 100644 index 0000000000..f6c5fddeb0 --- /dev/null +++ b/src/tests/src/solver/optimisation/adequacy_patch.cpp @@ -0,0 +1,386 @@ +#define BOOST_TEST_MODULE test adequacy patch functions + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include + +#include + +#include +#include "antares/study/parameters/adq-patch-params.h" + +#include "adequacy_patch_csr/adq_patch_curtailment_sharing.h" +#include "adequacy_patch_local_matching/adq_patch_local_matching.h" + +static double origineExtremite = -1; +static double extremiteOrigine = 5; + +using namespace Antares::Data::AdequacyPatch; +namespace tt = boost::test_tools; + +static const double flowArea0toArea1_positive = 10; +static const double flowArea0toArea1_negative = -10; +static const double flowArea2toArea0_positive = 30; +static const double flowArea2toArea0_negative = -30; +static const double positiveEnsInit = 50.0; + +std::pair calculateAreaFlowBalanceForOneTimeStep( + double ensInit, + bool includeFlowsOutsideAdqPatchToDensNew, + AdequacyPatchMode Area1Mode, + AdequacyPatchMode Area2Mode, + double flowToArea1, + double flowFromArea2) +{ + PROBLEME_HEBDO problem; + int Area = 0; + uint hour = 0; + + // allocate memory + problem.adequacyPatchRuntimeData = std::make_shared(); + problem.adequacyPatchRuntimeData->originAreaMode.resize(3); + problem.adequacyPatchRuntimeData->extremityAreaMode.resize(3); + + AdqPatchParams adqPatchParams; + + problem.ResultatsHoraires.resize(1); + problem.ResultatsHoraires[0].ValeursHorairesDeDefaillancePositive = std::vector(1); + problem.ValeursDeNTC = std::vector(1); + problem.ValeursDeNTC[0].ValeurDuFlux = std::vector(3); + problem.IndexSuivantIntercoOrigine = std::vector(3); + problem.IndexSuivantIntercoExtremite = std::vector(3); + problem.IndexDebutIntercoOrigine = std::vector(1); + problem.IndexDebutIntercoExtremite = std::vector(1); + + // input values + adqPatchParams.localMatching.setToZeroOutsideInsideLinks + = !includeFlowsOutsideAdqPatchToDensNew; + problem.ResultatsHoraires[Area].ValeursHorairesDeDefaillancePositive[hour] = ensInit; + int Interco = 1; + problem.IndexDebutIntercoOrigine[Area] = Interco; + problem.adequacyPatchRuntimeData->extremityAreaMode[Interco] = Area1Mode; + problem.ValeursDeNTC[hour].ValeurDuFlux[Interco] = flowToArea1; + problem.IndexSuivantIntercoOrigine[Interco] = -1; + + Interco = 2; + problem.IndexDebutIntercoExtremite[Area] = Interco; + problem.adequacyPatchRuntimeData->originAreaMode[Interco] = Area2Mode; + problem.ValeursDeNTC[hour].ValeurDuFlux[Interco] = flowFromArea2; + problem.IndexSuivantIntercoExtremite[Interco] = -1; + + // get results + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew, std::ignore) = calculateAreaFlowBalance( + &problem, + adqPatchParams.localMatching.setToZeroOutsideInsideLinks, + Area, + hour); + + return std::make_pair(netPositionInit, densNew); +} + +AdqPatchParams createParams() +{ + AdqPatchParams p; + p.enabled = true; + p.curtailmentSharing.includeHurdleCost = true; + p.curtailmentSharing.priceTakingOrder = AdqPatchPTO::isDens; + + return p; +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 virtual-area, and Area2-virtual area +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_virtual_virtual_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + virtualArea, + virtualArea, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, 0.0); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2-virtual area +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_virtual_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + physicalAreaInsideAdqPatch, + virtualArea, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2-virtual area +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +// ensInit = 50.0 +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_virtual_NotIncludeOut_positiveFlow_ensInitGraterThanZero) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + positiveEnsInit, + false, + physicalAreaInsideAdqPatch, + virtualArea, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, positiveEnsInit - flowArea0toArea1_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_outside_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + physicalAreaInsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_inside_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + physicalAreaInsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_positive + flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_inside_outside_IncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaInsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_positive + flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_outside_IncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, 0.0); + BOOST_CHECK_EQUAL(densNew, flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, +flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(densNew, +flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is negative +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_inside_outside_IncludeOut_negativeFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaInsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_negative, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_negative); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_negative); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is negative +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_outside_IncludeOut_negativeFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_negative, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, 0.0); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_negative); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is negative +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_negativeFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_negative, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positiive +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +// ensInit = 50.0 +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_negativeFlow_initEnsEqualTo50) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + positiveEnsInit, + true, + physicalAreaInsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(densNew, positiveEnsInit + netPositionInit); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positiive +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +// ensInit = 2.0 +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_negativeFlow_initEnsEqualTo0) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 2.0, + true, + physicalAreaInsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +BOOST_AUTO_TEST_CASE(check_valid_adq_param) +{ + auto p = createParams(); + BOOST_CHECK_NO_THROW( + p.checkAdqPatchSimulationModeEconomyOnly(Antares::Data::SimulationMode::Economy)); + BOOST_CHECK_NO_THROW(p.checkAdqPatchIncludeHurdleCost(true)); +} + +BOOST_AUTO_TEST_CASE(check_adq_param_wrong_mode) +{ + auto p = createParams(); + BOOST_CHECK_THROW(p.checkAdqPatchSimulationModeEconomyOnly( + Antares::Data::SimulationMode::Adequacy), + Error::IncompatibleSimulationModeForAdqPatch); +} + +BOOST_AUTO_TEST_CASE(check_adq_param_wrong_hurdle_cost) +{ + auto p = createParams(); + BOOST_CHECK_THROW(p.checkAdqPatchIncludeHurdleCost(false), Error::IncompatibleHurdleCostCSR); +} diff --git a/src/tests/src/solver/optimisation/adequacy_patch/CMakeLists.txt b/src/tests/src/solver/optimisation/adequacy_patch/CMakeLists.txt index bb28a99864..94aa87d377 100644 --- a/src/tests/src/solver/optimisation/adequacy_patch/CMakeLists.txt +++ b/src/tests/src/solver/optimisation/adequacy_patch/CMakeLists.txt @@ -1,24 +1,11 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # Useful variables definitions set(src_solver_optimisation "${CMAKE_SOURCE_DIR}/solver/optimisation") -set(EXECUTABLE_NAME tests-adq-patch) -add_executable(${EXECUTABLE_NAME} adequacy_patch.cpp) - -target_include_directories(${EXECUTABLE_NAME} - PRIVATE - "${src_solver_optimisation}" -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - model_antares - array -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -add_test(NAME test-adq-patch COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-adq-patch PROPERTY LABELS unit) +add_boost_test(tests-adq-patch + SRC adequacy_patch.cpp + INCLUDE "${src_solver_optimisation}" + LIBS + model_antares + array) diff --git a/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp b/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp index ea9762838b..9f716ecb77 100644 --- a/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp +++ b/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp @@ -31,7 +31,6 @@ #include #include #include "antares/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.h" -#include "antares/solver/optimisation/adequacy_patch_csr/post_processing.h" #include "antares/study/parameters/adq-patch-params.h" static double origineExtremite = -1; @@ -402,75 +401,3 @@ BOOST_AUTO_TEST_CASE(check_adq_param_wrong_hurdle_cost) auto p = createParams(); BOOST_CHECK_THROW(p.checkAdqPatchIncludeHurdleCost(false), Error::IncompatibleHurdleCostCSR); } - -BOOST_AUTO_TEST_SUITE(adq_patch_post_processing) - -BOOST_AUTO_TEST_CASE(dtg_mrg_triggered_low_ens) -{ - const bool triggered = true; - const double dtgMrg = 32.; - const double ens = 21.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == 11., tt::tolerance(1.e-6)); - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == std::abs(dtgMrg - ens), - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(dtg_mrg_triggered_high_ens) -{ - const bool triggered = true; - const double dtgMrg = 32.; - const double ens = 42.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == 0., tt::tolerance(1.e-6)); - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == std::abs(dtgMrg - ens), - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(dtg_mrg_not_triggered_low_ens) -{ - const bool triggered = false; - const double dtgMrg = 32.; - const double ens = 21.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == dtgMrg, tt::tolerance(1.e-6)); - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == dtgMrg + ens, - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(dtg_mrg_not_triggered_high_ens) -{ - const bool triggered = false; - const double dtgMrg = 32.; - const double ens = 42.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == dtgMrg, tt::tolerance(1.e-6)); - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == dtgMrg + ens, - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(mrgprice_high_enscsr) -{ - const double ensCsr = 21.; - const double originalCost = 3.; - const double unsuppliedEnergyCost = 1000.; - BOOST_TEST(recomputeMRGPrice(ensCsr, originalCost, unsuppliedEnergyCost) - == -unsuppliedEnergyCost, - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(mrgprice_low_enscsr) -{ - const double ensCsr = 0.; - const double originalCost = 3.; - const double unsuppliedEnergyCost = 1000.; - BOOST_TEST(recomputeMRGPrice(ensCsr, originalCost, unsuppliedEnergyCost) == originalCost, - tt::tolerance(1.e-6)); -} -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/src/solver/optimisation/name-translator/CMakeLists.txt b/src/tests/src/solver/optimisation/name-translator/CMakeLists.txt index f5e1f0619d..81275e4af1 100644 --- a/src/tests/src/solver/optimisation/name-translator/CMakeLists.txt +++ b/src/tests/src/solver/optimisation/name-translator/CMakeLists.txt @@ -1,20 +1,5 @@ -set(EXECUTABLE_NAME test-name-translator) -add_executable(${EXECUTABLE_NAME}) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_name_translator.cpp -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - model_antares -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -add_test(NAME test-name-translator COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-name-translator PROPERTY LABELS unit) +add_boost_test(test-name-translator + SRC test_name_translator.cpp + LIBS model_antares) diff --git a/src/tests/src/solver/optimisation/translator/CMakeLists.txt b/src/tests/src/solver/optimisation/translator/CMakeLists.txt index 87f3d5a0cb..c211b7d32e 100644 --- a/src/tests/src/solver/optimisation/translator/CMakeLists.txt +++ b/src/tests/src/solver/optimisation/translator/CMakeLists.txt @@ -1,20 +1,5 @@ -set(EXECUTABLE_NAME test-translator) -add_executable(${EXECUTABLE_NAME}) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_translator.cpp -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - model_antares -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) - -add_test(NAME test-translator COMMAND ${EXECUTABLE_NAME}) - -set_property(TEST test-translator PROPERTY LABELS unit) +add_boost_test(test-translator + SRC test_translator.cpp + LIBS model_antares) diff --git a/src/tests/src/solver/simulation/CMakeLists.txt b/src/tests/src/solver/simulation/CMakeLists.txt index e62fbcd2fa..d49b8164ae 100644 --- a/src/tests/src/solver/simulation/CMakeLists.txt +++ b/src/tests/src/solver/simulation/CMakeLists.txt @@ -1,3 +1,5 @@ +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + # Useful variables definitions set(src_solver_simulation "${CMAKE_SOURCE_DIR}/solver/simulation") set(src_solver_hydro "${CMAKE_SOURCE_DIR}/solver/hydro") @@ -6,146 +8,66 @@ set(src_libs_antares_study "${CMAKE_SOURCE_DIR}/libs/antares/study") set(SRC_TS_NUMBERS # For confort in IDE, but not necessary ${src_solver_simulation}/include/antares/solver/simulation/timeseries-numbers.h - # Necessary cpp files ${src_solver_simulation}/timeseries-numbers.cpp ${src_solver_simulation}/include/antares/solver/simulation/ITimeSeriesNumbersWriter.h) -add_executable(tests-ts-numbers tests-ts-numbers.cpp ${SRC_TS_NUMBERS}) - -target_include_directories(tests-ts-numbers - PRIVATE +add_boost_test(tests-ts-numbers + SRC tests-ts-numbers.cpp ${SRC_TS_NUMBERS} + INCLUDE "${src_solver_simulation}" "${src_libs_antares_study}" -) -target_link_libraries(tests-ts-numbers - PRIVATE + LIBS Antares::utils - Boost::unit_test_framework model_antares antares-solver-simulation - antares-solver-ts-generator -) - -# Storing tests-ts-numbers under the folder Unit-tests in the IDE -set_target_properties(tests-ts-numbers PROPERTIES FOLDER Unit-tests) + antares-solver-ts-generator) -add_test(NAME ts-numbers COMMAND tests-ts-numbers) - -set_property(TEST ts-numbers PROPERTY LABELS unit) # =================================== # Tests on area's store-timeseries-number # =================================== -set(SRC_STORE_TS - test-store-timeseries-number.cpp -) -add_executable(test-store-timeseries-number ${SRC_STORE_TS}) - -target_link_libraries(test-store-timeseries-number - PRIVATE - Boost::unit_test_framework +add_boost_test(test-store-timeseries-number + SRC test-store-timeseries-number.cpp + LIBS test_utils_unit antares-solver-simulation Antares::study - Antares::result_writer -) - -# Linux -if (UNIX AND NOT APPLE) - target_link_libraries(test-store-timeseries-number PRIVATE stdc++fs) -endif () - -set_target_properties(test-store-timeseries-number PROPERTIES FOLDER Unit-tests) - -add_test(NAME store-timeseries-number COMMAND test-store-timeseries-number) - -set_property(TEST store-timeseries-number PROPERTY LABELS unit) + Antares::result_writer) # =================================== # Tests on time series # =================================== -set(SRC_STORE_TS - test-time_series.cpp -) -add_executable(test-time_series ${SRC_STORE_TS}) - -target_link_libraries(test-time_series - PRIVATE - Boost::unit_test_framework +add_boost_test(test-time_series + SRC test-time_series.cpp + LIBS test_utils_unit antares-solver-simulation - Antares::study -) - -# Linux -if (UNIX AND NOT APPLE) - target_link_libraries(test-time_series PRIVATE stdc++fs) -endif () - - -set_target_properties(test-time_series PROPERTIES FOLDER Unit-tests) - -add_test(NAME time_series COMMAND test-time_series) - -set_property(TEST time_series PROPERTY LABELS unit) + Antares::study) # =================================== # Tests on hydro final reservoir level functions # =================================== - -add_executable(test-hydro_final test-hydro-final-reservoir-level-functions.cpp) - -target_include_directories(test-hydro_final - PRIVATE +add_boost_test(test-hydro_final + SRC + test-hydro-final-reservoir-level-functions.cpp + INCLUDE "${src_solver_simulation}" "${src_libs_antares_study}" "${src_solver_hydro}" -) -target_link_libraries(test-hydro_final - PRIVATE - Boost::unit_test_framework + LIBS Antares::study antares-solver-simulation - Antares::array -) - -# Linux -if (UNIX AND NOT APPLE) - target_link_libraries(test-hydro_final PRIVATE stdc++fs) -endif () - -set_target_properties(test-hydro_final PROPERTIES FOLDER Unit-tests) - -add_test(NAME hydro_final COMMAND test-hydro_final) - -set_property(TEST hydro_final PROPERTY LABELS unit) - + Antares::array) # =================================== # Tests on hydro remix algorithm # =================================== -add_executable(test-hydro-remix +add_boost_test(tests-on-hydro-remix + SRC test-hydro-remix.cpp ${src_solver_simulation}/hydro-remix-new.cpp -) - -target_include_directories(test-hydro-remix - PRIVATE - ${src_solver_simulation} -) - -target_link_libraries(test-hydro-remix - PRIVATE - Boost::unit_test_framework - test_utils_unit -) - -# gp : do we need this ? -if (UNIX AND NOT APPLE) - target_link_libraries(test-hydro_final PRIVATE stdc++fs) -endif () - -set_target_properties(test-hydro-remix PROPERTIES FOLDER Unit-tests) -add_test(NAME hydro_remix COMMAND test-hydro-remix) -set_property(TEST hydro_remix PROPERTY LABELS unit) \ No newline at end of file + INCLUDE + "${src_solver_simulation}" + LIBS + test_utils_unit) \ No newline at end of file diff --git a/src/tests/src/solver/utils/CMakeLists.txt b/src/tests/src/solver/utils/CMakeLists.txt index b3331c6249..e93e01b496 100644 --- a/src/tests/src/solver/utils/CMakeLists.txt +++ b/src/tests/src/solver/utils/CMakeLists.txt @@ -1,20 +1,10 @@ -set(src_utils "${CMAKE_SOURCE_DIR}/solver/utils") - -set(EXECUTABLE_NAME tests-basis-status) -add_executable(${EXECUTABLE_NAME} basis_status.cpp) - - -target_include_directories(${EXECUTABLE_NAME} - PRIVATE - "${src_utils}" # basis_status_impl.h is private -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - ortools::ortools - Antares::solverUtils -) - -add_test(NAME test-basis-status COMMAND ${EXECUTABLE_NAME}) -set_property(TEST test-basis-status PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) + +add_boost_test(tests-basis-status + SRC + basis_status.cpp + INCLUDE + "${CMAKE_SOURCE_DIR}/solver/utils" + LIBS + ortools::ortools + Antares::solverUtils) diff --git a/src/tests/src/study/system-model/CMakeLists.txt b/src/tests/src/study/system-model/CMakeLists.txt index 727635be3e..624da708a7 100644 --- a/src/tests/src/study/system-model/CMakeLists.txt +++ b/src/tests/src/study/system-model/CMakeLists.txt @@ -1,20 +1,10 @@ -set(EXECUTABLE_NAME test-system-model) -add_executable(${EXECUTABLE_NAME}) - -target_sources(${EXECUTABLE_NAME} - PRIVATE - test_main.cpp - test_component.cpp - test_system.cpp -) - -target_link_libraries(${EXECUTABLE_NAME} - PRIVATE - Boost::unit_test_framework - antares-study-system-model - test_utils_unit -) - -set_target_properties(${EXECUTABLE_NAME} PROPERTIES FOLDER Unit-tests) -add_test(NAME test-system-model COMMAND ${EXECUTABLE_NAME}) -set_property(TEST test-system-model PROPERTY LABELS unit) +include(${CMAKE_SOURCE_DIR}/tests/macros.cmake) +add_boost_test(test-system-model + SRC + test_main.cpp + test_component.cpp + test_system.cpp + LIBS + Boost::unit_test_framework + antares-study-system-model + test_utils_unit) diff --git a/src/tests/src/study/system-model/test_component.cpp b/src/tests/src/study/system-model/test_component.cpp index 3f52b33880..ca8e0c93f4 100644 --- a/src/tests/src/study/system-model/test_component.cpp +++ b/src/tests/src/study/system-model/test_component.cpp @@ -159,7 +159,8 @@ BOOST_AUTO_TEST_CASE(fail_on_no_params1) .withScenarioGroupId("scenario_group"); BOOST_CHECK_EXCEPTION(component_builder.build(), std::invalid_argument, - checkMessage("The component has 0 parameter(s), but its model has 2")); + checkMessage( + "The component \"component\" has 0 parameter(s), but its model has 2")); } BOOST_AUTO_TEST_CASE(fail_on_no_params2) @@ -171,7 +172,8 @@ BOOST_AUTO_TEST_CASE(fail_on_no_params2) .withScenarioGroupId("scenario_group"); BOOST_CHECK_EXCEPTION(component_builder.build(), std::invalid_argument, - checkMessage("The component has 0 parameter(s), but its model has 2")); + checkMessage( + "The component \"component\" has 0 parameter(s), but its model has 2")); } BOOST_AUTO_TEST_CASE(fail_on_missing_param) @@ -183,7 +185,8 @@ BOOST_AUTO_TEST_CASE(fail_on_missing_param) .withScenarioGroupId("scenario_group"); BOOST_CHECK_EXCEPTION(component_builder.build(), std::invalid_argument, - checkMessage("The component has 1 parameter(s), but its model has 2")); + checkMessage( + "The component \"component\" has 1 parameter(s), but its model has 2")); } BOOST_AUTO_TEST_CASE(fail_on_missing_wrong_param) @@ -195,7 +198,8 @@ BOOST_AUTO_TEST_CASE(fail_on_missing_wrong_param) .withScenarioGroupId("scenario_group"); BOOST_CHECK_EXCEPTION(component_builder.build(), std::invalid_argument, - checkMessage("The component has no value for parameter 'param1'")); + checkMessage( + "The component \"component\" has no value for parameter 'param1'")); } BOOST_AUTO_TEST_CASE(fail_on_too_many_params1) @@ -207,7 +211,8 @@ BOOST_AUTO_TEST_CASE(fail_on_too_many_params1) .withScenarioGroupId("scenario_group"); BOOST_CHECK_EXCEPTION(component_builder.build(), std::invalid_argument, - checkMessage("The component has 3 parameter(s), but its model has 2")); + checkMessage( + "The component \"component\" has 3 parameter(s), but its model has 2")); } BOOST_AUTO_TEST_CASE(fail_on_too_many_params2) @@ -219,7 +224,8 @@ BOOST_AUTO_TEST_CASE(fail_on_too_many_params2) .withScenarioGroupId("scenario_group"); BOOST_CHECK_EXCEPTION(component_builder.build(), std::invalid_argument, - checkMessage("The component has 1 parameter(s), but its model has 0")); + checkMessage( + "The component \"component\" has 1 parameter(s), but its model has 0")); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/src/study/system-model/test_system.cpp b/src/tests/src/study/system-model/test_system.cpp index 550ca59693..03436fa365 100644 --- a/src/tests/src/study/system-model/test_system.cpp +++ b/src/tests/src/study/system-model/test_system.cpp @@ -32,6 +32,7 @@ using namespace Antares::Study::SystemModel; struct SystemBuilderCreationFixture { SystemBuilder system_builder; + std::vector components; }; static Component createComponent(std::string id) @@ -50,9 +51,8 @@ BOOST_AUTO_TEST_SUITE(_System_) BOOST_FIXTURE_TEST_CASE(nominal_build, SystemBuilderCreationFixture) { - auto system = system_builder.withId("system") - .withComponents({createComponent("component1"), createComponent("component2")}) - .build(); + components = {createComponent("component1"), createComponent("component2")}; + auto system = system_builder.withId("system").withComponents(components).build(); BOOST_CHECK_EQUAL(system.Id(), "system"); BOOST_CHECK_EQUAL(system.Components().size(), 2); BOOST_CHECK_EQUAL(system.Components().at("component1").Id(), "component1"); @@ -61,7 +61,8 @@ BOOST_FIXTURE_TEST_CASE(nominal_build, SystemBuilderCreationFixture) BOOST_FIXTURE_TEST_CASE(fail_on_no_id, SystemBuilderCreationFixture) { - system_builder.withComponents({createComponent("component1"), createComponent("component2")}); + components = {createComponent("component1"), createComponent("component2")}; + system_builder.withComponents(components); BOOST_CHECK_EXCEPTION(system_builder.build(), std::invalid_argument, checkMessage("A system can't have an empty id")); @@ -77,7 +78,7 @@ BOOST_FIXTURE_TEST_CASE(fail_on_no_component1, SystemBuilderCreationFixture) BOOST_FIXTURE_TEST_CASE(fail_on_no_component2, SystemBuilderCreationFixture) { - system_builder.withId("system").withComponents({}); + system_builder.withId("system").withComponents(components); BOOST_CHECK_EXCEPTION(system_builder.build(), std::invalid_argument, checkMessage("A system must contain at least one component")); @@ -85,10 +86,10 @@ BOOST_FIXTURE_TEST_CASE(fail_on_no_component2, SystemBuilderCreationFixture) BOOST_FIXTURE_TEST_CASE(fail_on_components_with_same_id, SystemBuilderCreationFixture) { - system_builder.withId("system").withComponents({}).withComponents( - {createComponent("component1"), - createComponent("component2"), - createComponent("component2")}); + components = {createComponent("component1"), + createComponent("component2"), + createComponent("component2")}; + system_builder.withId("system").withComponents({components}); BOOST_CHECK_EXCEPTION(system_builder.build(), std::invalid_argument, checkMessage("System has at least two components with the same id " diff --git a/src/tests/src/utils/files-system.cpp b/src/tests/src/utils/files-system.cpp index f6097ee82b..e8d6163ea7 100644 --- a/src/tests/src/utils/files-system.cpp +++ b/src/tests/src/utils/files-system.cpp @@ -10,7 +10,7 @@ fs::path generateAndCreateDirName(const std::string& dirName) return working_dir; } -void createFolder(const std::string& path, const std::string& folder_name) +fs::path createFolder(const std::string& path, const std::string& folder_name) { fs::path folder_path = fs::path(path.c_str()) / folder_name.c_str(); @@ -22,6 +22,8 @@ void createFolder(const std::string& path, const std::string& folder_name) { std::cerr << "Exception creating folder '" + folder_name + "': " + e.what() + "\n"; } + + return folder_path; } void createFile(const std::string& folder_path, const std::string& file_name) diff --git a/src/tests/src/utils/files-system.h b/src/tests/src/utils/files-system.h index 092b498350..9ad4503e54 100644 --- a/src/tests/src/utils/files-system.h +++ b/src/tests/src/utils/files-system.h @@ -31,6 +31,6 @@ std::filesystem::path generateAndCreateDirName(const std::string&); -void createFolder(const std::string& path, const std::string& folder_name); +std::filesystem::path createFolder(const std::string& path, const std::string& folder_name); void createFile(const std::string& folder_path, const std::string& file_name); void removeFolder(std::string& path, std::string& folder_name); diff --git a/src/ui/simulator/application/main/create.cpp b/src/ui/simulator/application/main/create.cpp index 03d3f57a71..bb2182d148 100644 --- a/src/ui/simulator/application/main/create.cpp +++ b/src/ui/simulator/application/main/create.cpp @@ -293,7 +293,13 @@ void ApplWnd::internalInitialize() statusbar->SetStatusStyles(2, styles); statusbar->SetMinHeight(14); - statusbar->SetStatusText(wxT(""), 1); + statusbar->Connect(statusbar->GetId(), + wxEVT_CONTEXT_MENU, + wxContextMenuEventHandler(ApplWnd::evtOnContextMenuStatusBar), + nullptr, + this); + + statusbar->SetStatusText(wxT("| "), 1); wxFont f = statusbar->GetFont(); f.SetPointSize(f.GetPointSize() - 1); diff --git a/src/ui/simulator/application/main/internal-ids.h b/src/ui/simulator/application/main/internal-ids.h index 33d8f6f764..57893d5187 100644 --- a/src/ui/simulator/application/main/internal-ids.h +++ b/src/ui/simulator/application/main/internal-ids.h @@ -172,6 +172,16 @@ enum MenusID mnIDLaunchAnalyzer, mnIDLaunchConstraintsBuilder, + //! \name Popup Menu Operator for selected cells on any grid + //@{ + mnIDPopupOpNone, + mnIDPopupOpAverage, + mnIDPopupOpCellCount, + mnIDPopupOpMinimum, + mnIDPopupOpMaximum, + mnIDPopupOpSum, + //@} + //! \name Popup Menu Operator for selected nodes on any layer //@{ mnIDPopupSelectionHide, diff --git a/src/ui/simulator/application/main/main.cpp b/src/ui/simulator/application/main/main.cpp index 137b009c2b..38131d6552 100644 --- a/src/ui/simulator/application/main/main.cpp +++ b/src/ui/simulator/application/main/main.cpp @@ -179,6 +179,14 @@ EVT_MENU(mnInternalLogMessage, ApplWnd::onLogMessage) EVT_MENU(mnIDLaunchAnalyzer, ApplWnd::evtLaunchAnalyzer) EVT_MENU(mnIDLaunchConstraintsBuilder, ApplWnd::evtLaunchConstraintsBuilder) +// Context menu : Operator for selected cells (grid) +EVT_MENU(mnIDPopupOpNone, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpAverage, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpCellCount, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpMinimum, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpMaximum, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpSum, ApplWnd::evtOnContextMenuChangeOperator) + EVT_MENU_OPEN(ApplWnd::evtOnMenuOpen) EVT_MENU_CLOSE(ApplWnd::evtOnMenuClose) @@ -245,6 +253,8 @@ ApplWnd::ApplWnd() : pageRenewableCommon(nullptr), pageNodalOptim(nullptr), pWndBindingConstraints(nullptr), + pGridSelectionOperator(new Component::Datagrid::Selection::CellCount()), + pGridSelectionAttachedGrid(nullptr), pMapContextMenu(nullptr), pUserNotes(nullptr), pMainNotebookAlreadyHasItsComponents(false), @@ -308,6 +318,13 @@ ApplWnd::~ApplWnd() OnStudyAreasChanged.clear(); OnStudyAreaDelete.clear(); + // Delete the grid operator + if (pGridSelectionOperator) + { + delete pGridSelectionOperator; + pGridSelectionOperator = nullptr; // May be needed in some cases + } + // Disconnect all events destroyBoundEvents(); // Unregister the global pointer to the instance @@ -346,6 +363,34 @@ void ApplWnd::selectSystem() pNotebook->select(wxT("sys"), true); } +void ApplWnd::evtOnContextMenuChangeOperator(wxCommandEvent& evt) +{ + switch (evt.GetId()) + { + case mnIDPopupOpNone: + gridOperatorSelectedCells(nullptr); + break; + case mnIDPopupOpAverage: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Average()); + break; + case mnIDPopupOpCellCount: + gridOperatorSelectedCells(new Component::Datagrid::Selection::CellCount()); + break; + case mnIDPopupOpMinimum: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Minimum()); + break; + case mnIDPopupOpMaximum: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Maximum()); + break; + case mnIDPopupOpSum: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Sum()); + break; + default: + break; + } + evt.Skip(); +} + static inline void EnableItem(wxMenuBar* menu, int id, bool opened) { auto* item = menu->FindItem(id); @@ -494,6 +539,7 @@ void ApplWnd::evtOnUpdateGUIAfterStudyIO(bool opened) // Reset the status bar resetDefaultStatusBarText(); + gridOperatorSelectedCellsUpdateResult(pGridSelectionAttachedGrid); // reload the user notes and districts if (not aboutToQuit and study) @@ -519,6 +565,7 @@ void ApplWnd::evtOnUpdateGUIAfterStudyIO(bool opened) { GetSizer()->Clear(true); pUserNotes = nullptr; + pGridSelectionAttachedGrid = nullptr; pBigDaddy = nullptr; pMainSizer = nullptr; pData->wipPanel = nullptr; @@ -778,6 +825,24 @@ void ApplWnd::onRenewableGenerationModellingChanged(bool init) refreshInputMenuOnRenewableModellingChanged(aggregated); } +void ApplWnd::gridOperatorSelectedCells(Component::Datagrid::Selection::IOperator* v) +{ + delete pGridSelectionOperator; + pGridSelectionOperator = v; + gridOperatorSelectedCellsUpdateResult(pGridSelectionAttachedGrid); +} + +Component::Datagrid::Selection::IOperator* ApplWnd::gridOperatorSelectedCells() const +{ + return pGridSelectionOperator; +} + +void ApplWnd::disableGridOperatorIfGrid(wxGrid* grid) +{ + if (pGridSelectionAttachedGrid == grid) + gridOperatorSelectedCellsUpdateResult(nullptr); +} + void ApplWnd::title() { assert(wxIsMainThread() == true and "Must be ran from the main thread"); diff --git a/src/ui/simulator/application/main/main.h b/src/ui/simulator/application/main/main.h index 0b1f9dd8bb..7b7683805e 100644 --- a/src/ui/simulator/application/main/main.h +++ b/src/ui/simulator/application/main/main.h @@ -26,6 +26,7 @@ #include #include "../../toolbox/components/notebook/notebook.h" +#include "../../toolbox/components/datagrid/selectionoperation.h" #include "../../toolbox/components/map/settings.h" #include #include "fwd.h" @@ -105,6 +106,41 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent */ Map::Component* map() const; + /*! + ** \name Grid operator (for selected cells) + ** + ** A grid operator computes an operation (Sum, average...) on all selected + ** cells of the grid that currently has the focus. The result of this + ** computation is displayed in the status bar. + ** + ** \see Antares::Component::Datagrid::Component::onGridEnter() + ** \see Antares::Component::Datagrid::Component::onGridLeave() + */ + //@{ + /*! + ** \brief Get the current grid operator for selected cells + */ + Component::Datagrid::Selection::IOperator* gridOperatorSelectedCells() const; + + /*! + ** \brief Set the grid operator for selected cells + */ + void gridOperatorSelectedCells(Component::Datagrid::Selection::IOperator* v); + + /*! + ** \brief Update the GUI to display the result of the grid operator + ** + ** This method should be called each time the cells selection changes. + ** \param grid The `wxGrid` that has currently the focus (may be NULL) + */ + void gridOperatorSelectedCellsUpdateResult(wxGrid* grid); + + /*! + ** \brief Disable the grid operator + */ + void disableGridOperatorIfGrid(wxGrid* grid); + //@} + //! \name Title of the Window //@{ void title(); @@ -327,6 +363,8 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent //! Create a complete menu for the window wxMenuBar* createMenu(); + //! Create a popup menu for all available operators on selected cells (grid) + wxMenu* createPopupMenuOperatorsOnGrid(); //! Create menu: File wxMenu* createMenuFiles(); @@ -398,6 +436,9 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent //! \name Event: Context menu //@{ + //! Show the context menu associated to the status bar + void evtOnContextMenuStatusBar(wxContextMenuEvent& evt); + void evtOnContextMenuChangeOperator(wxCommandEvent& evt); void evtOnContextMenuMap(int x, int y); //@} @@ -696,6 +737,10 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent Component::Notebook::Page* pageScBuilderHydroInitialLevels; Component::Notebook::Page* pageScBuilderHydroFinalLevels; + //! The current grid operator to use on selected cells + Component::Datagrid::Selection::IOperator* pGridSelectionOperator; + wxGrid* pGridSelectionAttachedGrid; + //! A context menu for the map wxMenu* pMapContextMenu; diff --git a/src/ui/simulator/application/main/menu.cpp b/src/ui/simulator/application/main/menu.cpp index 11121bce3c..619269b283 100644 --- a/src/ui/simulator/application/main/menu.cpp +++ b/src/ui/simulator/application/main/menu.cpp @@ -64,6 +64,21 @@ wxMenuBar* ApplWnd::createMenu() return ret; } +wxMenu* ApplWnd::createPopupMenuOperatorsOnGrid() +{ + auto* menu = new wxMenu(); + + // Wizard + Menu::CreateItem(menu, mnIDPopupOpNone, wxT("None"), "images/16x16/empty.png"); + menu->AppendSeparator(); + Menu::CreateItem(menu, mnIDPopupOpAverage, wxT("Average ")); + Menu::CreateItem(menu, mnIDPopupOpCellCount, wxT("Cell count ")); + Menu::CreateItem(menu, mnIDPopupOpMinimum, wxT("Minimum ")); + Menu::CreateItem(menu, mnIDPopupOpMaximum, wxT("Maximum ")); + Menu::CreateItem(menu, mnIDPopupOpSum, wxT("Sum ")); + return menu; +} + wxMenu* ApplWnd::createMenuFiles() { delete pMenuFile; diff --git a/src/ui/simulator/application/main/statusbar.cpp b/src/ui/simulator/application/main/statusbar.cpp index a12dd4a290..ed33372524 100644 --- a/src/ui/simulator/application/main/statusbar.cpp +++ b/src/ui/simulator/application/main/statusbar.cpp @@ -22,17 +22,16 @@ #include #include "main.h" #include "../../windows/version.h" -#include "antares/study/study.h" -// Datagrid -#include "../../toolbox/components/datagrid/component.h" +#include "../study.h" #include "../../toolbox/components/datagrid/gridhelper.h" #include #include -namespace Antares -{ -namespace Forms +using namespace Component::Datagrid; + +namespace Antares::Forms { + void ApplWnd::resetDefaultStatusBarText() { assert(wxIsMainThread() == true && "Must be ran from the main thread"); @@ -41,5 +40,216 @@ void ApplWnd::resetDefaultStatusBarText() #endif } -} // namespace Forms -} // namespace Antares + +bool oneCellSelected(wxGrid& grid) +{ + const wxGridCellCoordsArray& cells(grid.GetSelectedCells()); + return cells.size() > 0; +} + +size_t updateStatisticsOpForOneCell(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + const wxGridCellCoordsArray& cells(grid.GetSelectedCells()); + for (uint i = 0; i < (uint)cells.size(); ++i) + { + auto& cell = cells[i]; + op->appendValue(gridHelper->GetNumericValue(cell.GetRow(), cell.GetCol())); + ++totalCell; + } + return totalCell; +} + +bool rowsSelected(wxGrid& grid) +{ + const wxArrayInt& rows(grid.GetSelectedRows()); + return rows.size() > 0; +} + +size_t updateStatisticsOpForRows(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + int colCount = grid.GetNumberCols(); + const wxArrayInt& rows(grid.GetSelectedRows()); + for (uint i = 0; i < (uint)rows.size(); ++i) + { + for (int col = 0; col < colCount; ++col) + { + op->appendValue(gridHelper->GetNumericValue(rows[i], col)); + ++totalCell; + } + } + return totalCell; +} + +bool columnsSelected(wxGrid& grid) +{ + const wxArrayInt& cols(grid.GetSelectedCols()); + return cols.size() > 0; +} + +size_t updateStatisticsOpForColumns(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + int rowCount = grid.GetNumberRows(); + const wxArrayInt& cols(grid.GetSelectedCols()); + for (uint i = 0; i < (uint)cols.size(); ++i) + { + for (int row = 0; row < rowCount; ++row) + { + op->appendValue(gridHelper->GetNumericValue(row, cols[i])); + ++totalCell; + } + } + return totalCell; +} + +bool blockSelected(wxGrid& grid) +{ + // Blocks. We always expect blocks top left and bottom right to have the same size, since their + // entries are supposed to correspond. + const wxGridCellCoordsArray& blockTopLeft(grid.GetSelectionBlockTopLeft()); + const wxGridCellCoordsArray& blockBottomRight(grid.GetSelectionBlockBottomRight()); + return (blockTopLeft.size() == blockBottomRight.size()) && (blockTopLeft.size() > 0); +} + +size_t updateStatisticsOpForBlock(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + const wxGridCellCoordsArray& blockTopLeft(grid.GetSelectionBlockTopLeft()); + const wxGridCellCoordsArray& blockBottomRight(grid.GetSelectionBlockBottomRight()); + size_t blockSize = blockTopLeft.size(); + + for (uint i = 0; i < blockSize; ++i) + { + const wxGridCellCoords& topLeft = blockTopLeft[i]; + const wxGridCellCoords& bottomRight = blockBottomRight[i]; + for (int row = topLeft.GetRow(); row <= bottomRight.GetRow(); ++row) + { + for (int col = topLeft.GetCol(); col <= bottomRight.GetCol(); ++col) + { + op->appendValue(gridHelper->GetNumericValue(row, col)); + ++totalCell; + } + } + } + return totalCell; +} + +/* +** Applies a functor to all selected cells. Returns the number of selected +** cells. +*/ +static size_t applyOperatorOnSelectedCells(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + assert(wxIsMainThread() == true and "Must be ran from the main thread"); + + if (oneCellSelected(grid)) + { + return updateStatisticsOpForOneCell(grid, gridHelper, op); + } + + if (rowsSelected(grid)) + { + return updateStatisticsOpForRows(grid, gridHelper, op); + } + + if (columnsSelected(grid)) + { + return updateStatisticsOpForColumns(grid, gridHelper, op); + } + + if (blockSelected(grid)) + { + return updateStatisticsOpForBlock(grid, gridHelper, op); + } + + return 0; +} + +void ApplWnd::gridOperatorSelectedCellsUpdateResult(wxGrid* grid) +{ + assert(wxIsMainThread() == true and "Must be ran from the main thread"); + enum + { + fieldIndex = 1, + }; + // The status bar + auto* statusBar = GetStatusBar(); + pGridSelectionAttachedGrid = grid; + if (statusBar) + { + if (not CurrentStudyIsValid()) + { + statusBar->SetStatusText(wxEmptyString, fieldIndex); + return; + } + if (not pGridSelectionOperator) + { + statusBar->SetStatusText(wxT("| (none)"), fieldIndex); + return; + } + + if (grid and grid->GetTable()) + { + auto* gridHelper = dynamic_cast(grid->GetTable()); + if (gridHelper) + { + // Reset of the operator + pGridSelectionOperator->reset(); + // Browse all selected cells + if (applyOperatorOnSelectedCells(*grid, gridHelper, pGridSelectionOperator)) + { + // Update the GUI + statusBar->SetStatusText(wxString() + << wxT("| ") << pGridSelectionOperator->caption() + << wxT(" = ") << pGridSelectionOperator->result(), + fieldIndex); + return; + } + } + } + // Empty + statusBar->SetStatusText( + wxString(wxT("| (")) << pGridSelectionOperator->caption() << wxT(')'), fieldIndex); + } +} + +void ApplWnd::evtOnContextMenuStatusBar(wxContextMenuEvent& evt) +{ + if (GUIIsLock()) + return; + + wxStatusBar* statusBar = GetStatusBar(); + if (statusBar) + { + wxRect rect; + if (statusBar->GetFieldRect(1, rect)) + { + const wxPoint pos = statusBar->ScreenToClient(evt.GetPosition()); + if (rect.Contains(pos)) + { + if (!pPopupMenuOperatorsGrid) + { + // Popup menu: Operators for selected cells on any grid + pPopupMenuOperatorsGrid = createPopupMenuOperatorsOnGrid(); + } + + statusBar->PopupMenu(pPopupMenuOperatorsGrid); + } + } + } + evt.Skip(); +} + +} // namespace Antares::Forms \ No newline at end of file diff --git a/src/ui/simulator/cmake/components.cmake b/src/ui/simulator/cmake/components.cmake index 0586928a48..df72d40b83 100644 --- a/src/ui/simulator/cmake/components.cmake +++ b/src/ui/simulator/cmake/components.cmake @@ -48,6 +48,7 @@ SET(SRC_TOOLBOX_COM_DBGRID_RENDERERS toolbox/components/datagrid/renderer.h toolbox/components/datagrid/renderer.hxx toolbox/components/datagrid/renderer.cpp + toolbox/components/datagrid/selectionoperation.h toolbox/components/datagrid/renderer/adequacy-patch-area-grid.h toolbox/components/datagrid/renderer/adequacy-patch-area-grid.cpp toolbox/components/datagrid/renderer/area.h diff --git a/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp b/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp index dc8624172e..84fe490800 100644 --- a/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp +++ b/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp @@ -120,6 +120,11 @@ DBGrid::DBGrid(Component* parent) : DBGrid::~DBGrid() { + // Remove any remaining reference + auto* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->disableGridOperatorIfGrid(this); + pParentComponent = nullptr; otherGrid_ = nullptr; } @@ -128,6 +133,10 @@ void DBGrid::onGridSelectCell(wxGridEvent& evt) { assert(GetParent() && "invalid parent"); + auto* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->gridOperatorSelectedCellsUpdateResult(this); + pCurrentPosition.x = evt.GetCol(); pCurrentPosition.y = evt.GetRow(); auto* r = ((Component*)GetParent())->renderer(); @@ -141,6 +150,9 @@ void DBGrid::onGridRangeSelect(wxGridRangeSelectEvent& evt) { assert(GetGridWindow() && "invalid grid window"); + Forms::ApplWnd* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->gridOperatorSelectedCellsUpdateResult(this); if (GetGridWindow()) GetGridWindow()->SetFocus(); evt.Skip(); @@ -148,6 +160,9 @@ void DBGrid::onGridRangeSelect(wxGridRangeSelectEvent& evt) void DBGrid::onGridLeave(wxFocusEvent& evt) { + auto* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->gridOperatorSelectedCellsUpdateResult(nullptr); evt.Skip(); } @@ -202,6 +217,10 @@ void DBGrid::ensureDataAreLoaded() r->invalidate = false; // Post an event to update the gui after the data are loaded + Forms::ApplWnd* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->disableGridOperatorIfGrid(this); + assert(pAllowRefresh == true); parent->forceRefresh(); if (GetTable()) diff --git a/src/ui/simulator/toolbox/components/datagrid/selectionoperation.h b/src/ui/simulator/toolbox/components/datagrid/selectionoperation.h new file mode 100644 index 0000000000..a7deebcf32 --- /dev/null +++ b/src/ui/simulator/toolbox/components/datagrid/selectionoperation.h @@ -0,0 +1,247 @@ +/* +** Copyright 2007-2018 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 +*/ +#ifndef __ANTARES_TOOLBOX_COMPONENT_DATAGRID_SELECTION_OPERATION_H__ +#define __ANTARES_TOOLBOX_COMPONENT_DATAGRID_SELECTION_OPERATION_H__ + +#include "wx-wrapper.h" +#include +#include + +namespace Antares +{ +namespace Component +{ +namespace Datagrid +{ +namespace Selection +{ +class IOperator +{ +public: + IOperator() + { + } + virtual ~IOperator() + { + } + + /*! + ** \brief Caption of the operator + */ + virtual const wxChar* caption() const = 0; + + /*! + ** \brief Reset all internal values + */ + virtual void reset() = 0; + + /*! + ** \brief Manage a new value + */ + virtual void appendValue(const double v) = 0; + + /*! + ** \brief Get the result + */ + virtual double result() const = 0; + +}; // class IOperator + +class Average final : public IOperator +{ +public: + Average() : pValue(0.), pCount(0) + { + } + + virtual ~Average() + { + } + + virtual const wxChar* caption() const + { + return wxT("Average"); + } + + virtual void reset() + { + pValue = 0.; + pCount = 0; + } + + virtual void appendValue(const double v) + { + pValue += v; + ++pCount; + } + + virtual double result() const + { + return pValue / (double)pCount; + } + +private: + double pValue; + uint pCount; + +}; // class Average + +class Sum final : public IOperator +{ +public: + Sum() : pValue(0.) + { + } + + virtual const wxChar* caption() const + { + return wxT("Sum"); + } + + virtual void reset() + { + pValue = 0.; + } + + virtual void appendValue(const double v) + { + pValue += v; + } + + virtual double result() const + { + return pValue; + } + +private: + double pValue; + +}; // class Sum + +class CellCount final : public IOperator +{ +public: + CellCount() : pCount(0) + { + } + + virtual const wxChar* caption() const + { + return wxT("Cell Count"); + } + + virtual void reset() + { + pCount = 0; + } + + virtual void appendValue(const double) + { + ++pCount; + } + + virtual double result() const + { + return (double)pCount; + } + +private: + uint pCount; + +}; // class Average + +class Minimum final : public IOperator +{ +public: + Minimum() : pValue(std::numeric_limits::infinity()) + { + } + + virtual const wxChar* caption() const + { + return wxT("Minimum"); + } + + virtual void reset() + { + pValue = std::numeric_limits::infinity(); + } + + virtual void appendValue(const double v) + { + if (v < pValue) + pValue = v; + } + + virtual double result() const + { + return pValue; + } + +private: + double pValue; + +}; // class Sum + +class Maximum final : public IOperator +{ +public: + Maximum() : pValue(-std::numeric_limits::infinity()) + { + } + + virtual const wxChar* caption() const + { + return wxT("Maximum"); + } + virtual void reset() + { + pValue = -std::numeric_limits::infinity(); + } + + virtual void appendValue(const double v) + { + if (v > pValue) + pValue = v; + } + + virtual double result() const + { + return pValue; + } + +private: + double pValue; + +}; // class Sum + +} // namespace Selection +} // namespace Datagrid +} // namespace Component +} // namespace Antares + +#endif // __ANTARES_TOOLBOX_COMPONENT_DATAGRID_SELECTION_OPERATION_H__ diff --git a/src/vcpkg.json b/src/vcpkg.json index 68a34fa53c..0a8e10c378 100644 --- a/src/vcpkg.json +++ b/src/vcpkg.json @@ -1,7 +1,7 @@ { "name": "antares-simulator", "version-string": "9.2.0", - "builtin-baseline": "9484a57dd560b89f0a583be08af6753611c57fd5", + "builtin-baseline": "b322364f06308bdd24823f9d8f03fe0cc86fd46f", "vcpkg-configuration": { "overlay-ports": [ "./ports" @@ -21,11 +21,11 @@ }, { "name": "boost-test", - "version>=": "1.81.0" + "version>=": "1.86.0" }, { "name": "boost-core", - "version>=": "1.81.0" + "version>=": "1.86.0" }, { "name": "minizip-ng",