From 6871e989cef6eb726e70aac1c7ac9ebfe6082954 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 12 Dec 2024 17:58:13 +0100 Subject: [PATCH 01/27] New hydro remix : setting unit tests environment --- src/solver/simulation/hydro-remix-new.cpp | 171 ++++++++++++++++++ .../src/solver/simulation/CMakeLists.txt | 121 ++++++++----- .../solver/simulation/test-hydro-remix.cpp | 46 +++++ 3 files changed, 289 insertions(+), 49 deletions(-) create mode 100644 src/solver/simulation/hydro-remix-new.cpp create mode 100644 src/tests/src/solver/simulation/test-hydro-remix.cpp diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp new file mode 100644 index 0000000000..e44edd813b --- /dev/null +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace Antares::Solver::Simulation +{ + +bool new_remix_hydro() +{ + return true; +} + +int find_min_index(const vector& G_plus_H, + const vector& new_D, + const vector& new_H, + const vector& tried_creux, + const vector& P_max, + double top) +{ + double min_val = top; + int min_idx = -1; + for (size_t i = 0; i < G_plus_H.size(); ++i) + { + if (new_D[i] > 0 && new_H[i] < P_max[i] && tried_creux[i] == 0) + { + if (G_plus_H[i] < min_val) + { + min_val = G_plus_H[i]; + min_idx = i; + } + } + } + return min_idx; +} + +int find_max_index(const vector& G_plus_H, + const vector& new_H, + const vector& tried_pic, + const vector& P_min, + double ref_value, + double eps) +{ + double max_val = 0; + int max_idx = -1; + for (size_t i = 0; i < G_plus_H.size(); ++i) + { + if (new_H[i] > P_min[i] && G_plus_H[i] >= ref_value + eps && tried_pic[i] == 0) + { + if (G_plus_H[i] > max_val) + { + max_val = G_plus_H[i]; + max_idx = i; + } + } + } + return max_idx; +} + +pair, vector> new_remix_hydro(const vector& G, + const vector& H, + const vector& D, + const vector& P_max, + const vector& P_min, + double initial_level, + double capa, + const vector& inflow) +{ + vector new_H = H; + vector new_D = D; + + int loop = 1000; + double eps = 1e-2; + double top = *max_element(G.begin(), G.end()) + *max_element(H.begin(), H.end()) + + *max_element(D.begin(), D.end()) + 1; + + vector G_plus_H(G.size()); + transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), plus<>()); + + vector level(G.size()); + level[0] = initial_level + inflow[0] - new_H[0]; + for (size_t i = 1; i < level.size(); ++i) + { + level[i] = level[i - 1] + inflow[i] - new_H[i]; + } + + while (loop-- > 0) + { + vector tried_creux(G.size(), 0); + double delta = 0; + + while (true) + { + int idx_creux = find_min_index(G_plus_H, new_D, new_H, tried_creux, P_max, top); + if (idx_creux == -1) + { + break; + } + + vector tried_pic(G.size(), 0); + while (true) + { + int idx_pic = find_max_index(G_plus_H, + new_H, + tried_pic, + P_min, + G_plus_H[idx_creux], + eps); + if (idx_pic == -1) + { + break; + } + + vector intermediate_level(level.begin() + min(idx_creux, idx_pic), + level.begin() + max(idx_creux, idx_pic)); + + double max_pic = min(new_H[idx_pic] - P_min[idx_pic], + capa + - *max_element(intermediate_level.begin(), + intermediate_level.end())); + double max_creux = min( + {P_max[idx_creux] - new_H[idx_creux], + new_D[idx_creux], + *min_element(intermediate_level.begin(), intermediate_level.end())}); + double dif_pic_creux = max(G_plus_H[idx_pic] - G_plus_H[idx_creux], 0.0); + + delta = max(min({max_pic, max_creux, dif_pic_creux / 2.0}), 0.0); + + if (delta > 0) + { + new_H[idx_pic] -= delta; + new_H[idx_creux] += delta; + new_D[idx_pic] = H[idx_pic] + D[idx_pic] - new_H[idx_pic]; + new_D[idx_creux] = H[idx_creux] + D[idx_creux] - new_H[idx_creux]; + break; + } + else + { + tried_pic[idx_pic] = 1; + } + } + + if (delta > 0) + { + break; + } + tried_creux[idx_creux] = 1; + } + + if (delta == 0) + { + break; + } + + transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), plus<>()); + level[0] = initial_level + inflow[0] - new_H[0]; + for (size_t i = 1; i < level.size(); ++i) + { + level[i] = level[i - 1] + inflow[i] - new_H[i]; + } + } + + return {new_H, new_D}; +} + +} // End namespace Antares::Solver::Simulation diff --git a/src/tests/src/solver/simulation/CMakeLists.txt b/src/tests/src/solver/simulation/CMakeLists.txt index 294005454b..10ef79b619 100644 --- a/src/tests/src/solver/simulation/CMakeLists.txt +++ b/src/tests/src/solver/simulation/CMakeLists.txt @@ -4,27 +4,27 @@ set(src_solver_hydro "${CMAKE_SOURCE_DIR}/solver/hydro") 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) + # 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 - "${src_solver_simulation}" - "${src_libs_antares_study}" + PRIVATE + "${src_solver_simulation}" + "${src_libs_antares_study}" ) target_link_libraries(tests-ts-numbers - PRIVATE - Antares::utils - Boost::unit_test_framework - model_antares - antares-solver-simulation - antares-solver-ts-generator + PRIVATE + 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 @@ -38,23 +38,23 @@ set_property(TEST ts-numbers PROPERTY LABELS unit) # Tests on area's store-timeseries-number # =================================== set(SRC_STORE_TS - test-store-timeseries-number.cpp - ) + 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 + PRIVATE + Boost::unit_test_framework test_utils_unit - antares-solver-simulation - Antares::study - Antares::result_writer - ) + 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() +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) @@ -66,22 +66,22 @@ set_property(TEST store-timeseries-number PROPERTY LABELS unit) # Tests on time series # =================================== set(SRC_STORE_TS - test-time_series.cpp - ) + test-time_series.cpp +) add_executable(test-time_series ${SRC_STORE_TS}) target_link_libraries(test-time_series - PRIVATE - Boost::unit_test_framework + PRIVATE + Boost::unit_test_framework test_utils_unit - antares-solver-simulation - Antares::study - ) + antares-solver-simulation + Antares::study +) # Linux -if(UNIX AND NOT APPLE) - target_link_libraries(test-time_series PRIVATE stdc++fs) -endif() +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) @@ -97,26 +97,49 @@ set_property(TEST time_series PROPERTY LABELS unit) add_executable(test-hydro_final test-hydro-final-reservoir-level-functions.cpp) target_include_directories(test-hydro_final - PRIVATE - "${src_solver_simulation}" - "${src_libs_antares_study}" - "${src_solver_hydro}" + PRIVATE + "${src_solver_simulation}" + "${src_libs_antares_study}" + "${src_solver_hydro}" ) target_link_libraries(test-hydro_final - PRIVATE - Boost::unit_test_framework - Antares::study - antares-solver-simulation - Antares::array + PRIVATE + Boost::unit_test_framework + Antares::study + antares-solver-simulation + Antares::array ) # Linux -if(UNIX AND NOT APPLE) - target_link_libraries(test-hydro_final PRIVATE stdc++fs) -endif() +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) \ No newline at end of file +set_property(TEST hydro_final PROPERTY LABELS unit) + + +# =================================== +# Tests on hydro remix algorithm +# =================================== +add_executable(test-hydro-remix + test-hydro-remix.cpp + ${src_solver_simulation}/hydro-remix-new.cpp +) + +target_link_libraries(test-hydro-remix + PRIVATE + Boost::unit_test_framework +) + +# 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 diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp new file mode 100644 index 0000000000..4ca8773c4a --- /dev/null +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -0,0 +1,46 @@ +#define BOOST_TEST_MODULE hydro remix + +#define WIN32_LEAN_AND_MEAN + +#include + +#include + +namespace Antares::Solver::Simulation +{ +bool new_remix_hydro(); +std::pair, std::vector> new_remix_hydro( + const std::vector& G, + const std::vector& H, + const std::vector& D, + const std::vector& P_max, + const std::vector& P_min, + double initial_level, + double capa, + const std::vector& inflow); +} // namespace Antares::Solver::Simulation + +using namespace Antares::Solver::Simulation; +using namespace std; + +BOOST_AUTO_TEST_CASE(my_first_unit_test) +{ + BOOST_CHECK(new_remix_hydro()); +} + +BOOST_AUTO_TEST_CASE(my_second_unit_test) +{ + vector G = {1.0, 2.0, 3.0, 4.0, 5.0}; + vector H = {2.0, 3.0, 4.0, 5.0, 6.0}; + vector D = {1.0, 1.5, 2.0, 2.5, 3.0}; + vector P_max = {10.0, 10.0, 10.0, 10.0, 10.0}; + vector P_min = {0.0, 0.0, 0.0, 0.0, 0.0}; + double initial_level = 5.0; + double capa = 20.0; + vector inflow = {3.0, 3.0, 3.0, 3.0, 3.0}; + + // Call the function + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflow); + + BOOST_CHECK(true); +} From 33a96eadf060fb8287cfe3950429ac64e525a6ae Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 12 Dec 2024 18:10:39 +0100 Subject: [PATCH 02/27] New hydro remix : some cleaning --- src/solver/simulation/hydro-remix-new.cpp | 82 ++++++++----------- .../solver/simulation/test-hydro-remix.cpp | 22 ++--- 2 files changed, 43 insertions(+), 61 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index e44edd813b..76e9e9f90c 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -1,26 +1,14 @@ #include -#include -#include -#include -#include -#include #include -using namespace std; - namespace Antares::Solver::Simulation { -bool new_remix_hydro() -{ - return true; -} - -int find_min_index(const vector& G_plus_H, - const vector& new_D, - const vector& new_H, - const vector& tried_creux, - const vector& P_max, +int find_min_index(const std::vector& G_plus_H, + const std::vector& new_D, + const std::vector& new_H, + const std::vector& tried_creux, + const std::vector& P_max, double top) { double min_val = top; @@ -39,10 +27,10 @@ int find_min_index(const vector& G_plus_H, return min_idx; } -int find_max_index(const vector& G_plus_H, - const vector& new_H, - const vector& tried_pic, - const vector& P_min, +int find_max_index(const std::vector& G_plus_H, + const std::vector& new_H, + const std::vector& tried_pic, + const std::vector& P_min, double ref_value, double eps) { @@ -62,27 +50,28 @@ int find_max_index(const vector& G_plus_H, return max_idx; } -pair, vector> new_remix_hydro(const vector& G, - const vector& H, - const vector& D, - const vector& P_max, - const vector& P_min, - double initial_level, - double capa, - const vector& inflow) +std::pair, std::vector> new_remix_hydro( + const std::vector& G, + const std::vector& H, + const std::vector& D, + const std::vector& P_max, + const std::vector& P_min, + double initial_level, + double capa, + const std::vector& inflow) { - vector new_H = H; - vector new_D = D; + std::vector new_H = H; + std::vector new_D = D; int loop = 1000; double eps = 1e-2; double top = *max_element(G.begin(), G.end()) + *max_element(H.begin(), H.end()) + *max_element(D.begin(), D.end()) + 1; - vector G_plus_H(G.size()); - transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), plus<>()); + std::vector G_plus_H(G.size()); + transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); - vector level(G.size()); + std::vector level(G.size()); level[0] = initial_level + inflow[0] - new_H[0]; for (size_t i = 1; i < level.size(); ++i) { @@ -91,7 +80,7 @@ pair, vector> new_remix_hydro(const vector& G, while (loop-- > 0) { - vector tried_creux(G.size(), 0); + std::vector tried_creux(G.size(), 0); double delta = 0; while (true) @@ -102,7 +91,7 @@ pair, vector> new_remix_hydro(const vector& G, break; } - vector tried_pic(G.size(), 0); + std::vector tried_pic(G.size(), 0); while (true) { int idx_pic = find_max_index(G_plus_H, @@ -116,20 +105,21 @@ pair, vector> new_remix_hydro(const vector& G, break; } - vector intermediate_level(level.begin() + min(idx_creux, idx_pic), - level.begin() + max(idx_creux, idx_pic)); + std::vector intermediate_level(level.begin() + std::min(idx_creux, idx_pic), + level.begin() + + std::max(idx_creux, idx_pic)); - double max_pic = min(new_H[idx_pic] - P_min[idx_pic], - capa - - *max_element(intermediate_level.begin(), - intermediate_level.end())); - double max_creux = min( + double max_pic = std::min(new_H[idx_pic] - P_min[idx_pic], + capa + - *max_element(intermediate_level.begin(), + intermediate_level.end())); + double max_creux = std::min( {P_max[idx_creux] - new_H[idx_creux], new_D[idx_creux], *min_element(intermediate_level.begin(), intermediate_level.end())}); - double dif_pic_creux = max(G_plus_H[idx_pic] - G_plus_H[idx_creux], 0.0); + double dif_pic_creux = std::max(G_plus_H[idx_pic] - G_plus_H[idx_creux], 0.0); - delta = max(min({max_pic, max_creux, dif_pic_creux / 2.0}), 0.0); + delta = std::max(std::min({max_pic, max_creux, dif_pic_creux / 2.0}), 0.0); if (delta > 0) { @@ -157,7 +147,7 @@ pair, vector> new_remix_hydro(const vector& G, break; } - transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), plus<>()); + transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); level[0] = initial_level + inflow[0] - new_H[0]; for (size_t i = 1; i < level.size(); ++i) { diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 4ca8773c4a..3f90756f0d 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -8,7 +8,6 @@ namespace Antares::Solver::Simulation { -bool new_remix_hydro(); std::pair, std::vector> new_remix_hydro( const std::vector& G, const std::vector& H, @@ -18,28 +17,21 @@ std::pair, std::vector> new_remix_hydro( double initial_level, double capa, const std::vector& inflow); -} // namespace Antares::Solver::Simulation +} using namespace Antares::Solver::Simulation; -using namespace std; BOOST_AUTO_TEST_CASE(my_first_unit_test) { - BOOST_CHECK(new_remix_hydro()); -} - -BOOST_AUTO_TEST_CASE(my_second_unit_test) -{ - vector G = {1.0, 2.0, 3.0, 4.0, 5.0}; - vector H = {2.0, 3.0, 4.0, 5.0, 6.0}; - vector D = {1.0, 1.5, 2.0, 2.5, 3.0}; - vector P_max = {10.0, 10.0, 10.0, 10.0, 10.0}; - vector P_min = {0.0, 0.0, 0.0, 0.0, 0.0}; + std::vector G = {1.0, 2.0, 3.0, 4.0, 5.0}; + std::vector H = {2.0, 3.0, 4.0, 5.0, 6.0}; + std::vector D = {1.0, 1.5, 2.0, 2.5, 3.0}; + std::vector P_max = {10.0, 10.0, 10.0, 10.0, 10.0}; + std::vector P_min = {0.0, 0.0, 0.0, 0.0, 0.0}; double initial_level = 5.0; double capa = 20.0; - vector inflow = {3.0, 3.0, 3.0, 3.0, 3.0}; + std::vector inflow = {3.0, 3.0, 3.0, 3.0, 3.0}; - // Call the function auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflow); BOOST_CHECK(true); From e40b32f6173f4e52c1890ec9a2856d20f70924b7 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 13 Dec 2024 17:23:17 +0100 Subject: [PATCH 03/27] New hydro remix : unit tests on input data --- src/solver/simulation/hydro-remix-new.cpp | 67 +++++++++++++++---- .../src/solver/simulation/CMakeLists.txt | 1 + .../solver/simulation/test-hydro-remix.cpp | 47 ++++++++++++- 3 files changed, 100 insertions(+), 15 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 76e9e9f90c..80c7530b73 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include namespace Antares::Solver::Simulation @@ -13,7 +15,7 @@ int find_min_index(const std::vector& G_plus_H, { double min_val = top; int min_idx = -1; - for (size_t i = 0; i < G_plus_H.size(); ++i) + for (int i = 0; i < G_plus_H.size(); ++i) { if (new_D[i] > 0 && new_H[i] < P_max[i] && tried_creux[i] == 0) { @@ -36,7 +38,7 @@ int find_max_index(const std::vector& G_plus_H, { double max_val = 0; int max_idx = -1; - for (size_t i = 0; i < G_plus_H.size(); ++i) + for (int i = 0; i < G_plus_H.size(); ++i) { if (new_H[i] > P_min[i] && G_plus_H[i] >= ref_value + eps && tried_pic[i] == 0) { @@ -50,6 +52,41 @@ int find_max_index(const std::vector& G_plus_H, return max_idx; } +static void checkInputCorrectness(const std::vector& G, + const std::vector& H, + const std::vector& D, + const std::vector& P_max, + const std::vector& P_min, + double initial_level, + double capa, + const std::vector& inflows) +{ + std::string msg_prefix = "Remix hydro input : "; + + // Initial level smaller than capacity + if (initial_level > capa) + { + throw std::invalid_argument(msg_prefix + "initial level > reservoir capacity"); + } + // Arrays sizes must be identical + std::vector sizes = {G.size(), + H.size(), + D.size(), + P_max.size(), + P_min.size(), + inflows.size()}; + if (std::ranges::adjacent_find(sizes, std::not_equal_to()) != sizes.end()) + { + throw std::invalid_argument(msg_prefix + "arrays of different sizes"); + } + + // Arrays are of size 0 + if (!G.size()) + { + throw std::invalid_argument(msg_prefix + "all arrays of sizes 0"); + } +} + std::pair, std::vector> new_remix_hydro( const std::vector& G, const std::vector& H, @@ -58,24 +95,26 @@ std::pair, std::vector> new_remix_hydro( const std::vector& P_min, double initial_level, double capa, - const std::vector& inflow) + const std::vector& inflows) { + checkInputCorrectness(G, H, D, P_max, P_min, initial_level, capa, inflows); + std::vector new_H = H; std::vector new_D = D; int loop = 1000; double eps = 1e-2; - double top = *max_element(G.begin(), G.end()) + *max_element(H.begin(), H.end()) - + *max_element(D.begin(), D.end()) + 1; + double top = *std::max_element(G.begin(), G.end()) + *std::max_element(H.begin(), H.end()) + + *std::max_element(D.begin(), D.end()) + 1; std::vector G_plus_H(G.size()); - transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); + std::transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); std::vector level(G.size()); - level[0] = initial_level + inflow[0] - new_H[0]; + level[0] = initial_level + inflows[0] - new_H[0]; for (size_t i = 1; i < level.size(); ++i) { - level[i] = level[i - 1] + inflow[i] - new_H[i]; + level[i] = level[i - 1] + inflows[i] - new_H[i]; } while (loop-- > 0) @@ -111,12 +150,12 @@ std::pair, std::vector> new_remix_hydro( double max_pic = std::min(new_H[idx_pic] - P_min[idx_pic], capa - - *max_element(intermediate_level.begin(), - intermediate_level.end())); + - *std::max_element(intermediate_level.begin(), + intermediate_level.end())); double max_creux = std::min( {P_max[idx_creux] - new_H[idx_creux], new_D[idx_creux], - *min_element(intermediate_level.begin(), intermediate_level.end())}); + *std::min_element(intermediate_level.begin(), intermediate_level.end())}); double dif_pic_creux = std::max(G_plus_H[idx_pic] - G_plus_H[idx_creux], 0.0); delta = std::max(std::min({max_pic, max_creux, dif_pic_creux / 2.0}), 0.0); @@ -147,11 +186,11 @@ std::pair, std::vector> new_remix_hydro( break; } - transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); - level[0] = initial_level + inflow[0] - new_H[0]; + std::transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); + level[0] = initial_level + inflows[0] - new_H[0]; for (size_t i = 1; i < level.size(); ++i) { - level[i] = level[i - 1] + inflow[i] - new_H[i]; + level[i] = level[i - 1] + inflows[i] - new_H[i]; } } diff --git a/src/tests/src/solver/simulation/CMakeLists.txt b/src/tests/src/solver/simulation/CMakeLists.txt index 10ef79b619..be2285170d 100644 --- a/src/tests/src/solver/simulation/CMakeLists.txt +++ b/src/tests/src/solver/simulation/CMakeLists.txt @@ -133,6 +133,7 @@ add_executable(test-hydro-remix target_link_libraries(test-hydro-remix PRIVATE Boost::unit_test_framework + test_utils_unit ) # gp : do we need this ? diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 3f90756f0d..e846113bf3 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -2,6 +2,7 @@ #define WIN32_LEAN_AND_MEAN +#include #include #include @@ -21,7 +22,7 @@ std::pair, std::vector> new_remix_hydro( using namespace Antares::Solver::Simulation; -BOOST_AUTO_TEST_CASE(my_first_unit_test) +BOOST_AUTO_TEST_CASE(first_dummy_unit_test___will_be_removed) { std::vector G = {1.0, 2.0, 3.0, 4.0, 5.0}; std::vector H = {2.0, 3.0, 4.0, 5.0, 6.0}; @@ -36,3 +37,47 @@ BOOST_AUTO_TEST_CASE(my_first_unit_test) BOOST_CHECK(true); } + +BOOST_AUTO_TEST_CASE(input_arrays_of_different_sizes__exception_raised) +{ + std::vector G, D, P_max, P_min, inflows; + std::vector H = {0., 0.}; + double initial_level = 0.; + double capa = 0.; + + BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), + std::invalid_argument, + checkMessage("Remix hydro input : arrays of different sizes")); +} + +BOOST_AUTO_TEST_CASE(input_init_level_exceeds_capacity__exception_raised) +{ + std::vector G, D, P_max, P_min, inflows; + std::vector H = {0., 0.}; + double initial_level = 2.; + double capa = 1.; + + BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), + std::invalid_argument, + checkMessage("Remix hydro input : initial level > reservoir capacity")); +} + +BOOST_AUTO_TEST_CASE(all_input_arrays_of_size_0__exception_raised) +{ + std::vector G, H, D, P_max, P_min, inflows; + double initial_level = 0.; + double capa = 1.; + + BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), + std::invalid_argument, + checkMessage("Remix hydro input : all arrays of sizes 0")); +} + +BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) +{ + std::vector G = {0.}, H = {0.}, D = {0.}, P_max = {0.}, P_min = {0.}, inflows = {0.}; + double initial_level = 0.; + double capa = 1.; + + BOOST_CHECK_NO_THROW(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows)); +} From 34f0ed7b2d4816900d751a204a7ac1f81fdcf50e Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 13 Dec 2024 18:01:25 +0100 Subject: [PATCH 04/27] New hydro remix : add unit test > pmax is supposed to limit hydro generation --- .../solver/simulation/test-hydro-remix.cpp | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index e846113bf3..2d12aaaaa0 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -22,7 +22,7 @@ std::pair, std::vector> new_remix_hydro( using namespace Antares::Solver::Simulation; -BOOST_AUTO_TEST_CASE(first_dummy_unit_test___will_be_removed) +BOOST_AUTO_TEST_CASE(dummy_unit_test___will_be_removed) { std::vector G = {1.0, 2.0, 3.0, 4.0, 5.0}; std::vector H = {2.0, 3.0, 4.0, 5.0, 6.0}; @@ -81,3 +81,24 @@ BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) BOOST_CHECK_NO_THROW(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows)); } + +BOOST_AUTO_TEST_CASE(hydro_prod_very_changing__smoothing_required___smoothing_limited_by_pmax) +{ + double pmax = 10.; + std::vector G(5, 100.); + std::vector H = {0., 20., 40.0, 60.0, 80.0}; + std::vector D = {80.0, 60., 40., 20., 0.}; + std::vector P_max(5, pmax); + std::vector P_min(5, 0.); + double initial_level = 200.; + double capa = 400.; + std::vector inflows(5, 0.); + + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + BOOST_CHECK(new_H[0] < pmax); + BOOST_CHECK(new_H[1] < pmax); + BOOST_CHECK(new_H[2] < pmax); + BOOST_CHECK(new_H[3] < pmax); + BOOST_CHECK(new_H[4] < pmax); +} From cba01ee621bdaf091ecfdaf2d08ae169406d2c16 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Mon, 16 Dec 2024 14:39:41 +0100 Subject: [PATCH 05/27] New hydro remix : adding tests on Pmax respect --- .../solver/simulation/test-hydro-remix.cpp | 94 ++++++++++++++++--- 1 file changed, 83 insertions(+), 11 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 2d12aaaaa0..d29577e627 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -2,6 +2,7 @@ #define WIN32_LEAN_AND_MEAN +#include #include #include @@ -82,23 +83,94 @@ BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) BOOST_CHECK_NO_THROW(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows)); } -BOOST_AUTO_TEST_CASE(hydro_prod_very_changing__smoothing_required___smoothing_limited_by_pmax) +BOOST_AUTO_TEST_CASE(hydro_increases_but_pmax_only_10mwh___10mwh_are_moved_from_last_to_first_hour) { - double pmax = 10.; std::vector G(5, 100.); - std::vector H = {0., 20., 40.0, 60.0, 80.0}; + std::vector H = {0., 20., 40., 60., 80.}; std::vector D = {80.0, 60., 40., 20., 0.}; - std::vector P_max(5, pmax); + std::vector P_max(5, 10.); std::vector P_min(5, 0.); - double initial_level = 200.; - double capa = 400.; + double initial_level = 500.; + double capa = 1000.; std::vector inflows(5, 0.); auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); - BOOST_CHECK(new_H[0] < pmax); - BOOST_CHECK(new_H[1] < pmax); - BOOST_CHECK(new_H[2] < pmax); - BOOST_CHECK(new_H[3] < pmax); - BOOST_CHECK(new_H[4] < pmax); + std::vector expected_H = {10., 20., 40., 60., 70.}; + std::vector expected_D = {70., 60., 40., 20., 10.}; + BOOST_CHECK(new_H == expected_H); + BOOST_CHECK(new_D == expected_D); } + +BOOST_AUTO_TEST_CASE(hydro_increases_but_pmax_only_20mwh___20mwh_are_moved_from_last_to_first_hour) +{ + std::vector G(5, 100.); + std::vector H = {0., 20., 40., 60., 80.}; + std::vector D = {80.0, 60., 40., 20., 0.}; + std::vector P_max(5, 20.); + std::vector P_min(5, 0.); + double initial_level = 500.; + double capa = 1000.; + std::vector inflows(5, 0.); + + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H = {20., 20., 40., 60., 60.}; + BOOST_CHECK(new_H == expected_H); +} + +BOOST_AUTO_TEST_CASE(hydro_increases_but_pmax_only_30mwh___30mwh_are_moved_from_last_to_first_hour) +{ + std::vector G(5, 100.); + std::vector H = {0., 20., 40., 60., 80.}; + std::vector D = {80.0, 60., 40., 20., 0.}; + std::vector P_max(5, 30.); + std::vector P_min(5, 0.); + double initial_level = 500.; + double capa = 1000.; + std::vector inflows(5, 0.); + + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H = {30., 30., 40., 50., 50.}; + BOOST_CHECK(new_H == expected_H); +} + +BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___final_hydro_is_flat_and_limited_by_pmax) +{ + std::vector G(5, 100.); + std::vector H = {0., 20., 40., 60., 80.}; + std::vector D = {80.0, 60., 40., 20., 0.}; + std::vector P_max(5, 40.); + std::vector P_min(5, 0.); + double initial_level = 500.; + double capa = 1000.; + std::vector inflows(5, 0.); + + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H = {40., 40., 40., 40., 40.}; + BOOST_CHECK(new_H == expected_H); +} + +BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_50mwh___final_hydro_is_flat_and_limited_by_pmax) +{ + std::vector G(5, 100.); + std::vector H = {0., 20., 40., 60., 80.}; + std::vector D = {80.0, 60., 40., 20., 0.}; + std::vector P_max(5, 50.); + std::vector P_min(5, 0.); + double initial_level = 500.; + double capa = 1000.; + std::vector inflows(5, 0.); + + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H = {40., 40., 40., 40., 40.}; + BOOST_CHECK(new_H == expected_H); +} + +// Comment for further tests : +// - Remix hydro algorithm seems symetrical (if we have input vectors and corresponding output +// vectors, run the algo on reversed vectors gives reversed output result vectors) +// - After running remix hydro algo, sum(H), sum(H + D) must remain the same. From 9f41dc1faaa845ef6a35584c8dd19e39bdc17cc4 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Mon, 16 Dec 2024 17:13:52 +0100 Subject: [PATCH 06/27] New hydro remix : Hydro must respect condition H <= Pmax in input of algo + consequences --- src/solver/simulation/hydro-remix-new.cpp | 17 ++++ .../solver/simulation/test-hydro-remix.cpp | 95 ++++++++++--------- 2 files changed, 65 insertions(+), 47 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 80c7530b73..7c78b20703 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -85,6 +85,23 @@ static void checkInputCorrectness(const std::vector& G, { throw std::invalid_argument(msg_prefix + "all arrays of sizes 0"); } + + // Hydro production < Pmax + for (int h = 0; h < H.size(); h++) + { + if (H[h] > P_max[h]) + { + throw std::invalid_argument(msg_prefix + "H not smaller than Pmax everywhere"); + } + } + + for (int h = 0; h < H.size(); h++) + { + if (H[h] < P_min[h]) + { + throw std::invalid_argument(msg_prefix + "H not greater than Pmin everywhere"); + } + } } std::pair, std::vector> new_remix_hydro( diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index d29577e627..0ebb2075db 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -74,103 +74,104 @@ BOOST_AUTO_TEST_CASE(all_input_arrays_of_size_0__exception_raised) checkMessage("Remix hydro input : all arrays of sizes 0")); } -BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) +BOOST_AUTO_TEST_CASE(H_not_smaller_than_pmax__exception_raised) { - std::vector G = {0.}, H = {0.}, D = {0.}, P_max = {0.}, P_min = {0.}, inflows = {0.}; + std::vector G(5, 0.), D(5, 0.), P_min(5, 0.), inflows(5, 0.); + std::vector H = {1., 2., 3., 4., 5.}; + std::vector P_max = {2., 2., 2., 4., 5.}; double initial_level = 0.; double capa = 1.; - BOOST_CHECK_NO_THROW(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows)); + BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), + std::invalid_argument, + checkMessage("Remix hydro input : H not smaller than Pmax everywhere")); } -BOOST_AUTO_TEST_CASE(hydro_increases_but_pmax_only_10mwh___10mwh_are_moved_from_last_to_first_hour) +BOOST_AUTO_TEST_CASE(H_not_greater_than_pmin__exception_raised) { - std::vector G(5, 100.); - std::vector H = {0., 20., 40., 60., 80.}; - std::vector D = {80.0, 60., 40., 20., 0.}; - std::vector P_max(5, 10.); - std::vector P_min(5, 0.); - double initial_level = 500.; - double capa = 1000.; - std::vector inflows(5, 0.); - - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + std::vector G(5, 0.), D(5, 0.), P_max(5, 1000.), inflows(5, 0.); + std::vector H = {1., 2., 3., 4., 5.}; + std::vector P_min = {0., 0., 4., 0., 0.}; + double initial_level = 0.; + double capa = 1.; - std::vector expected_H = {10., 20., 40., 60., 70.}; - std::vector expected_D = {70., 60., 40., 20., 10.}; - BOOST_CHECK(new_H == expected_H); - BOOST_CHECK(new_D == expected_D); + BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), + std::invalid_argument, + checkMessage("Remix hydro input : H not greater than Pmin everywhere")); } -BOOST_AUTO_TEST_CASE(hydro_increases_but_pmax_only_20mwh___20mwh_are_moved_from_last_to_first_hour) +BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) { - std::vector G(5, 100.); - std::vector H = {0., 20., 40., 60., 80.}; - std::vector D = {80.0, 60., 40., 20., 0.}; - std::vector P_max(5, 20.); - std::vector P_min(5, 0.); - double initial_level = 500.; - double capa = 1000.; - std::vector inflows(5, 0.); - - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + std::vector G = {0.}, H = {0.}, D = {0.}, P_max = {0.}, P_min = {0.}, inflows = {0.}; + double initial_level = 0.; + double capa = 1.; - std::vector expected_H = {20., 20., 40., 60., 60.}; - BOOST_CHECK(new_H == expected_H); + BOOST_CHECK_NO_THROW(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows)); } -BOOST_AUTO_TEST_CASE(hydro_increases_but_pmax_only_30mwh___30mwh_are_moved_from_last_to_first_hour) +BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20mwh) { + std::vector P_max(5, 40.); + std::vector P_min(5, 0.); std::vector G(5, 100.); - std::vector H = {0., 20., 40., 60., 80.}; + std::vector H = {0., 10., 20., 30., 40.}; // H <= Pmax everywhere std::vector D = {80.0, 60., 40., 20., 0.}; - std::vector P_max(5, 30.); - std::vector P_min(5, 0.); double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); - std::vector expected_H = {30., 30., 40., 50., 50.}; + std::vector expected_H = {20., 20., 20., 20., 20.}; + // D such as G + H + D remains constant at each hour + std::vector expected_D = {60., 50., 40., 30., 20.}; BOOST_CHECK(new_H == expected_H); + BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___final_hydro_is_flat_and_limited_by_pmax) +BOOST_AUTO_TEST_CASE(Pmax_does_not_influence_results_when_greater_than_40mwh) { + std::vector P_max(5, 50.); + std::vector P_min(5, 0.); std::vector G(5, 100.); - std::vector H = {0., 20., 40., 60., 80.}; + std::vector H = {0., 10., 20., 30., 40.}; // H <= Pmax everywhere std::vector D = {80.0, 60., 40., 20., 0.}; - std::vector P_max(5, 40.); - std::vector P_min(5, 0.); double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); - std::vector expected_H = {40., 40., 40., 40., 40.}; + std::vector expected_H = {20., 20., 20., 20., 20.}; + // D such as G + H + D remains constant at each hour + std::vector expected_D = {60., 50., 40., 30., 20.}; BOOST_CHECK(new_H == expected_H); + BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_50mwh___final_hydro_is_flat_and_limited_by_pmax) +BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20mwh) { - std::vector G(5, 100.); - std::vector H = {0., 20., 40., 60., 80.}; - std::vector D = {80.0, 60., 40., 20., 0.}; - std::vector P_max(5, 50.); + std::vector P_max(5, 40.); std::vector P_min(5, 0.); + std::vector G(5, 100.); + std::vector H = {40., 30., 20., 10., 0.}; // H <= Pmax everywhere + // std::vector D = {80.0, 60., 40., 20., 0.}; + std::vector D = {0., 20., 40., 60., 80.}; double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); - std::vector expected_H = {40., 40., 40., 40., 40.}; + std::vector expected_H = {20., 20., 20., 20., 20.}; + // D such as G + H + D remains constant at each hour + std::vector expected_D = {20., 30., 40., 50., 60.}; BOOST_CHECK(new_H == expected_H); + BOOST_CHECK(new_D == expected_D); } // Comment for further tests : // - Remix hydro algorithm seems symetrical (if we have input vectors and corresponding output // vectors, run the algo on reversed vectors gives reversed output result vectors) // - After running remix hydro algo, sum(H), sum(H + D) must remain the same. +// - influence of D : low values of G + H are searched where D > 0 (not where D >= 0) From a52aa7b813dcd218393025b1d0ec87ab7689c8a2 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Mon, 16 Dec 2024 18:20:17 +0100 Subject: [PATCH 07/27] New hydro remix : adding a test on influence of Pmax --- .../solver/simulation/test-hydro-remix.cpp | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 0ebb2075db..f3737b4df9 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(Pmax_does_not_influence_results_when_greater_than_40mwh) +BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) { std::vector P_max(5, 50.); std::vector P_min(5, 0.); @@ -155,7 +155,6 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 std::vector P_min(5, 0.); std::vector G(5, 100.); std::vector H = {40., 30., 20., 10., 0.}; // H <= Pmax everywhere - // std::vector D = {80.0, 60., 40., 20., 0.}; std::vector D = {0., 20., 40., 60., 80.}; double initial_level = 500.; double capa = 1000.; @@ -170,8 +169,36 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 BOOST_CHECK(new_D == expected_D); } -// Comment for further tests : -// - Remix hydro algorithm seems symetrical (if we have input vectors and corresponding output +BOOST_AUTO_TEST_CASE(influence_of_pmax) +{ + std::vector P_max(5, 20.); + std::vector P_min(5, 0.); + + // G decreases + std::vector G = {100., 80., 60., 40., 20.}; + + // H is flat and must respect H <= Pmax everywhere + std::vector H = {20., 20., 20., 20., 20.}; // H <= Pmax everywhere + std::vector D = {50., 50., 50., 50., 50.}; + double initial_level = 500.; + double capa = 1000.; + std::vector inflows(5, 0.); + + // What we expect to happen : + // Algorithm tends to flatten G + H, so it would require to increase H. + // But H is limited by P_max. So Algo does nothing in the end. + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H = {20., 20., 20., 20., 20.}; + std::vector expected_D = {50., 50., 50., 50., 50.}; + BOOST_CHECK(new_H == expected_H); + BOOST_CHECK(new_D == expected_D); +} + +// Ideas for building further tests : +// ================================ +// - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output // vectors, run the algo on reversed vectors gives reversed output result vectors) +// - How to test influence of Pmin ? // - After running remix hydro algo, sum(H), sum(H + D) must remain the same. // - influence of D : low values of G + H are searched where D > 0 (not where D >= 0) From 52b04e0a4f12b3f2ef5b316d4da61071e6f34af6 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Mon, 16 Dec 2024 18:36:33 +0100 Subject: [PATCH 08/27] New hydro remix : adding a test on influence of Pmin --- .../solver/simulation/test-hydro-remix.cpp | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index f3737b4df9..22870da2a5 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -114,7 +114,7 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 std::vector P_max(5, 40.); std::vector P_min(5, 0.); std::vector G(5, 100.); - std::vector H = {0., 10., 20., 30., 40.}; // H <= Pmax everywhere + std::vector H = {0., 10., 20., 30., 40.}; // we have Pmin <= H <= Pmax std::vector D = {80.0, 60., 40., 20., 0.}; double initial_level = 500.; double capa = 1000.; @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) std::vector P_max(5, 50.); std::vector P_min(5, 0.); std::vector G(5, 100.); - std::vector H = {0., 10., 20., 30., 40.}; // H <= Pmax everywhere + std::vector H = {0., 10., 20., 30., 40.}; std::vector D = {80.0, 60., 40., 20., 0.}; double initial_level = 500.; double capa = 1000.; @@ -154,7 +154,7 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 std::vector P_max(5, 40.); std::vector P_min(5, 0.); std::vector G(5, 100.); - std::vector H = {40., 30., 20., 10., 0.}; // H <= Pmax everywhere + std::vector H = {40., 30., 20., 10., 0.}; std::vector D = {0., 20., 40., 60., 80.}; double initial_level = 500.; double capa = 1000.; @@ -178,14 +178,14 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax) std::vector G = {100., 80., 60., 40., 20.}; // H is flat and must respect H <= Pmax everywhere - std::vector H = {20., 20., 20., 20., 20.}; // H <= Pmax everywhere + std::vector H = {20., 20., 20., 20., 20.}; std::vector D = {50., 50., 50., 50., 50.}; double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); // What we expect to happen : - // Algorithm tends to flatten G + H, so it would require to increase H. + // Algorithm tends to flatten G + H, so it would require H to increase. // But H is limited by P_max. So Algo does nothing in the end. auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); @@ -195,6 +195,32 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax) BOOST_CHECK(new_D == expected_D); } +BOOST_AUTO_TEST_CASE(influence_of_pmin) +{ + std::vector P_max(5, std::numeric_limits::max()); + std::vector P_min(5, 20.); + + // G decreases + std::vector G = {100., 80., 60., 40., 20.}; + + // H is flat and must respect Pmin <= H <= Pmax everywhere + std::vector H = {20., 20., 20., 20., 20.}; + std::vector D = {50., 50., 50., 50., 50.}; + double initial_level = 500.; + double capa = 1000.; + std::vector inflows(5, 0.); + + // What we expect to happen : + // Algorithm tends to flatten G + H, so it would require H to decrease. + // But H is low bounded by P_min. So Algo does nothing in the end. + auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H = {20., 20., 20., 20., 20.}; + std::vector expected_D = {50., 50., 50., 50., 50.}; + BOOST_CHECK(new_H == expected_H); + BOOST_CHECK(new_D == expected_D); +} + // Ideas for building further tests : // ================================ // - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output From 56ab22dfa8f7224550f637827f2fcf05b84bf46a Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Tue, 17 Dec 2024 11:48:42 +0100 Subject: [PATCH 09/27] New hydro remix : Pmin & Pmax unit tests enhancement --- .../solver/simulation/test-hydro-remix.cpp | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 22870da2a5..431cbe86a7 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -169,9 +169,8 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(influence_of_pmax) +BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) { - std::vector P_max(5, 20.); std::vector P_min(5, 0.); // G decreases @@ -184,21 +183,29 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax) double capa = 1000.; std::vector inflows(5, 0.); - // What we expect to happen : - // Algorithm tends to flatten G + H, so it would require H to increase. - // But H is limited by P_max. So Algo does nothing in the end. - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + // 1. Algorithm tends to flatten G + H, so it would require H to increase. + // Proof : + std::vector P_max(5., std::numeric_limits::max()); - std::vector expected_H = {20., 20., 20., 20., 20.}; - std::vector expected_D = {50., 50., 50., 50., 50.}; - BOOST_CHECK(new_H == expected_H); - BOOST_CHECK(new_D == expected_D); + auto [new_H1, new_D1] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H1 = {0., 0., 13.33, 33.33, 53.33}; + BOOST_TEST(new_H1 == expected_H1, boost::test_tools::per_element()); + + // 2. But H is limited by P_max. So Algo does nothing in the end. + // Proof : + P_max = {20., 20., 20., 20., 20.}; + auto [new_H2, new_D2] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H2 = {20., 20., 20., 20., 20.}; + std::vector expected_D2 = {50., 50., 50., 50., 50.}; + BOOST_CHECK(new_H2 == expected_H2); + BOOST_CHECK(new_D2 == expected_D2); } -BOOST_AUTO_TEST_CASE(influence_of_pmin) +BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) { std::vector P_max(5, std::numeric_limits::max()); - std::vector P_min(5, 20.); // G decreases std::vector G = {100., 80., 60., 40., 20.}; @@ -210,15 +217,20 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin) double capa = 1000.; std::vector inflows(5, 0.); - // What we expect to happen : - // Algorithm tends to flatten G + H, so it would require H to decrease. - // But H is low bounded by P_min. So Algo does nothing in the end. - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); - - std::vector expected_H = {20., 20., 20., 20., 20.}; - std::vector expected_D = {50., 50., 50., 50., 50.}; - BOOST_CHECK(new_H == expected_H); - BOOST_CHECK(new_D == expected_D); + // 1. Algorithm tends to flatten G + H, so it would require H to increase. + std::vector P_min(5, 0.); + auto [new_H1, new_D1] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + std::vector expected_H1 = {0., 0., 13.33, 33.33, 53.33}; + BOOST_TEST(new_H1 == expected_H1, boost::test_tools::per_element()); + + // 2. But H is low bounded by P_min. So Algo does nothing in the end. + P_min = {20., 20., 20., 20., 20.}; + auto [new_H2, new_D2] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + + std::vector expected_H2 = {20., 20., 20., 20., 20.}; + std::vector expected_D2 = {50., 50., 50., 50., 50.}; + BOOST_CHECK(new_H2 == expected_H2); + BOOST_CHECK(new_D2 == expected_D2); } // Ideas for building further tests : From 548fc1f98cec909dad7a23d131313e3d9b614b83 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Tue, 17 Dec 2024 15:42:34 +0100 Subject: [PATCH 10/27] New hydro remix : change the algorithm due to new specifications --- src/solver/simulation/hydro-remix-new.cpp | 102 ++++++--- .../solver/simulation/test-hydro-remix.cpp | 201 +++++++++++++----- 2 files changed, 220 insertions(+), 83 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 7c78b20703..4a6cd7d3bf 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,13 +12,14 @@ int find_min_index(const std::vector& G_plus_H, const std::vector& new_H, const std::vector& tried_creux, const std::vector& P_max, + const std::vector& filter_hours_remix, double top) { double min_val = top; int min_idx = -1; for (int i = 0; i < G_plus_H.size(); ++i) { - if (new_D[i] > 0 && new_H[i] < P_max[i] && tried_creux[i] == 0) + if (new_D[i] > 0 && new_H[i] < P_max[i] && tried_creux[i] == 0 && filter_hours_remix[i]) { if (G_plus_H[i] < min_val) { @@ -33,6 +35,7 @@ int find_max_index(const std::vector& G_plus_H, const std::vector& new_H, const std::vector& tried_pic, const std::vector& P_min, + const std::vector& filter_hours_remix, double ref_value, double eps) { @@ -40,7 +43,8 @@ int find_max_index(const std::vector& G_plus_H, int max_idx = -1; for (int i = 0; i < G_plus_H.size(); ++i) { - if (new_H[i] > P_min[i] && G_plus_H[i] >= ref_value + eps && tried_pic[i] == 0) + if (new_H[i] > P_min[i] && G_plus_H[i] >= ref_value + eps && tried_pic[i] == 0 + && filter_hours_remix[i]) { if (G_plus_H[i] > max_val) { @@ -59,7 +63,11 @@ static void checkInputCorrectness(const std::vector& G, const std::vector& P_min, double initial_level, double capa, - const std::vector& inflows) + const std::vector& inflows, + const std::vector& overflow, + const std::vector& pump, + const std::vector& S, + const std::vector& DTG_MRG) { std::string msg_prefix = "Remix hydro input : "; @@ -74,7 +82,12 @@ static void checkInputCorrectness(const std::vector& G, D.size(), P_max.size(), P_min.size(), - inflows.size()}; + inflows.size(), + overflow.size(), + pump.size(), + S.size(), + DTG_MRG.size()}; + if (std::ranges::adjacent_find(sizes, std::not_equal_to()) != sizes.end()) { throw std::invalid_argument(msg_prefix + "arrays of different sizes"); @@ -104,17 +117,38 @@ static void checkInputCorrectness(const std::vector& G, } } -std::pair, std::vector> new_remix_hydro( - const std::vector& G, - const std::vector& H, - const std::vector& D, - const std::vector& P_max, - const std::vector& P_min, - double initial_level, - double capa, - const std::vector& inflows) +struct RemixHydroOutput +{ + std::vector new_H; + std::vector new_D; + std::vector levels; +}; + +RemixHydroOutput new_remix_hydro(const std::vector& G, + const std::vector& H, + const std::vector& D, + const std::vector& P_max, + const std::vector& P_min, + double initial_level, + double capa, + const std::vector& inflows, + const std::vector& overflow, + const std::vector& pump, + const std::vector& S, + const std::vector& DTG_MRG) { - checkInputCorrectness(G, H, D, P_max, P_min, initial_level, capa, inflows); + checkInputCorrectness(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + overflow, + pump, + S, + DTG_MRG); std::vector new_H = H; std::vector new_D = D; @@ -124,14 +158,23 @@ std::pair, std::vector> new_remix_hydro( double top = *std::max_element(G.begin(), G.end()) + *std::max_element(H.begin(), H.end()) + *std::max_element(D.begin(), D.end()) + 1; + std::vector filter_hours_remix(G.size(), false); + for (unsigned int h = 0; h < filter_hours_remix.size(); h++) + { + if (S[h] + DTG_MRG[h] == 0. && H[h] + D[h] > 0.) + { + filter_hours_remix[h] = true; + } + } + std::vector G_plus_H(G.size()); std::transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); - std::vector level(G.size()); - level[0] = initial_level + inflows[0] - new_H[0]; - for (size_t i = 1; i < level.size(); ++i) + std::vector levels(G.size()); + levels[0] = initial_level + inflows[0] - overflow[0] + pump[0] - new_H[0]; + for (size_t i = 1; i < levels.size(); ++i) { - level[i] = level[i - 1] + inflows[i] - new_H[i]; + levels[i] = levels[i - 1] + inflows[i] - overflow[i] + pump[i] - new_H[i]; } while (loop-- > 0) @@ -141,7 +184,13 @@ std::pair, std::vector> new_remix_hydro( while (true) { - int idx_creux = find_min_index(G_plus_H, new_D, new_H, tried_creux, P_max, top); + int idx_creux = find_min_index(G_plus_H, + new_D, + new_H, + tried_creux, + P_max, + filter_hours_remix, + top); if (idx_creux == -1) { break; @@ -154,6 +203,7 @@ std::pair, std::vector> new_remix_hydro( new_H, tried_pic, P_min, + filter_hours_remix, G_plus_H[idx_creux], eps); if (idx_pic == -1) @@ -161,8 +211,9 @@ std::pair, std::vector> new_remix_hydro( break; } - std::vector intermediate_level(level.begin() + std::min(idx_creux, idx_pic), - level.begin() + std::vector intermediate_level(levels.begin() + + std::min(idx_creux, idx_pic), + levels.begin() + std::max(idx_creux, idx_pic)); double max_pic = std::min(new_H[idx_pic] - P_min[idx_pic], @@ -204,14 +255,13 @@ std::pair, std::vector> new_remix_hydro( } std::transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); - level[0] = initial_level + inflows[0] - new_H[0]; - for (size_t i = 1; i < level.size(); ++i) + levels[0] = initial_level + inflows[0] - overflow[0] + pump[0] - new_H[0]; + for (size_t i = 1; i < levels.size(); ++i) { - level[i] = level[i - 1] + inflows[i] - new_H[i]; + levels[i] = levels[i - 1] + inflows[i] - overflow[i] + pump[i] - new_H[i]; } } - - return {new_H, new_D}; + return {new_H, new_D, levels}; } } // End namespace Antares::Solver::Simulation diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 431cbe86a7..4e553e9e61 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -10,103 +10,109 @@ namespace Antares::Solver::Simulation { -std::pair, std::vector> new_remix_hydro( - const std::vector& G, - const std::vector& H, - const std::vector& D, - const std::vector& P_max, - const std::vector& P_min, - double initial_level, - double capa, - const std::vector& inflow); -} +// gp : instead of this, we should make a header asociated to hydro-remix-new.cpp +// ==> hydro-remix-new.h +struct RemixHydroOutput +{ + std::vector new_H; + std::vector new_D; + std::vector levels; +}; + +RemixHydroOutput new_remix_hydro(const std::vector& G, + const std::vector& H, + const std::vector& D, + const std::vector& P_max, + const std::vector& P_min, + double initial_level, + double capa, + const std::vector& inflow, + const std::vector& overflow, + const std::vector& pump, + const std::vector& S, + const std::vector& DTG_MRG); + +} // namespace Antares::Solver::Simulation using namespace Antares::Solver::Simulation; -BOOST_AUTO_TEST_CASE(dummy_unit_test___will_be_removed) -{ - std::vector G = {1.0, 2.0, 3.0, 4.0, 5.0}; - std::vector H = {2.0, 3.0, 4.0, 5.0, 6.0}; - std::vector D = {1.0, 1.5, 2.0, 2.5, 3.0}; - std::vector P_max = {10.0, 10.0, 10.0, 10.0, 10.0}; - std::vector P_min = {0.0, 0.0, 0.0, 0.0, 0.0}; - double initial_level = 5.0; - double capa = 20.0; - std::vector inflow = {3.0, 3.0, 3.0, 3.0, 3.0}; - - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflow); - - BOOST_CHECK(true); -} - BOOST_AUTO_TEST_CASE(input_arrays_of_different_sizes__exception_raised) { - std::vector G, D, P_max, P_min, inflows; + std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; std::vector H = {0., 0.}; double initial_level = 0.; double capa = 0.; - BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), - std::invalid_argument, - checkMessage("Remix hydro input : arrays of different sizes")); + BOOST_CHECK_EXCEPTION( + new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + std::invalid_argument, + checkMessage("Remix hydro input : arrays of different sizes")); } BOOST_AUTO_TEST_CASE(input_init_level_exceeds_capacity__exception_raised) { - std::vector G, D, P_max, P_min, inflows; + std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; std::vector H = {0., 0.}; double initial_level = 2.; double capa = 1.; - BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), - std::invalid_argument, - checkMessage("Remix hydro input : initial level > reservoir capacity")); + BOOST_CHECK_EXCEPTION( + new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + std::invalid_argument, + checkMessage("Remix hydro input : initial level > reservoir capacity")); } BOOST_AUTO_TEST_CASE(all_input_arrays_of_size_0__exception_raised) { - std::vector G, H, D, P_max, P_min, inflows; + std::vector G, H, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; double initial_level = 0.; double capa = 1.; - BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), - std::invalid_argument, - checkMessage("Remix hydro input : all arrays of sizes 0")); + BOOST_CHECK_EXCEPTION( + new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + std::invalid_argument, + checkMessage("Remix hydro input : all arrays of sizes 0")); } BOOST_AUTO_TEST_CASE(H_not_smaller_than_pmax__exception_raised) { std::vector G(5, 0.), D(5, 0.), P_min(5, 0.), inflows(5, 0.); + std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); std::vector H = {1., 2., 3., 4., 5.}; std::vector P_max = {2., 2., 2., 4., 5.}; double initial_level = 0.; double capa = 1.; - BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), - std::invalid_argument, - checkMessage("Remix hydro input : H not smaller than Pmax everywhere")); + BOOST_CHECK_EXCEPTION( + new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + std::invalid_argument, + checkMessage("Remix hydro input : H not smaller than Pmax everywhere")); } BOOST_AUTO_TEST_CASE(H_not_greater_than_pmin__exception_raised) { std::vector G(5, 0.), D(5, 0.), P_max(5, 1000.), inflows(5, 0.); + std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); std::vector H = {1., 2., 3., 4., 5.}; std::vector P_min = {0., 0., 4., 0., 0.}; double initial_level = 0.; double capa = 1.; - BOOST_CHECK_EXCEPTION(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows), - std::invalid_argument, - checkMessage("Remix hydro input : H not greater than Pmin everywhere")); + BOOST_CHECK_EXCEPTION( + new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + std::invalid_argument, + checkMessage("Remix hydro input : H not greater than Pmin everywhere")); } BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) { std::vector G = {0.}, H = {0.}, D = {0.}, P_max = {0.}, P_min = {0.}, inflows = {0.}; + std::vector ovf = {0.}, pump = {0.}, S = {0.}, DTG_MRG = {0.}; double initial_level = 0.; double capa = 1.; - BOOST_CHECK_NO_THROW(new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows)); + BOOST_CHECK_NO_THROW( + new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG)); } BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20mwh) @@ -119,8 +125,20 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); - - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + + auto [new_H, new_D, _] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); std::vector expected_H = {20., 20., 20., 20., 20.}; // D such as G + H + D remains constant at each hour @@ -139,8 +157,20 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); - - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + + auto [new_H, new_D, _] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); std::vector expected_H = {20., 20., 20., 20., 20.}; // D such as G + H + D remains constant at each hour @@ -159,8 +189,20 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20 double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); - - auto [new_H, new_D] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + + auto [new_H, new_D, _] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); std::vector expected_H = {20., 20., 20., 20., 20.}; // D such as G + H + D remains constant at each hour @@ -182,12 +224,24 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); + std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); // 1. Algorithm tends to flatten G + H, so it would require H to increase. // Proof : std::vector P_max(5., std::numeric_limits::max()); - auto [new_H1, new_D1] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + auto [new_H1, new_D1, L] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); std::vector expected_H1 = {0., 0., 13.33, 33.33, 53.33}; BOOST_TEST(new_H1 == expected_H1, boost::test_tools::per_element()); @@ -195,7 +249,18 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) // 2. But H is limited by P_max. So Algo does nothing in the end. // Proof : P_max = {20., 20., 20., 20., 20.}; - auto [new_H2, new_D2] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + auto [new_H2, new_D2, _] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); std::vector expected_H2 = {20., 20., 20., 20., 20.}; std::vector expected_D2 = {50., 50., 50., 50., 50.}; @@ -216,16 +281,39 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) double initial_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); + std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); // 1. Algorithm tends to flatten G + H, so it would require H to increase. std::vector P_min(5, 0.); - auto [new_H1, new_D1] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + auto [new_H1, new_D1, L] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); std::vector expected_H1 = {0., 0., 13.33, 33.33, 53.33}; BOOST_TEST(new_H1 == expected_H1, boost::test_tools::per_element()); // 2. But H is low bounded by P_min. So Algo does nothing in the end. P_min = {20., 20., 20., 20., 20.}; - auto [new_H2, new_D2] = new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows); + auto [new_H2, new_D2, _] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); std::vector expected_H2 = {20., 20., 20., 20., 20.}; std::vector expected_D2 = {50., 50., 50., 50., 50.}; @@ -237,6 +325,5 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) // ================================ // - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output // vectors, run the algo on reversed vectors gives reversed output result vectors) -// - How to test influence of Pmin ? // - After running remix hydro algo, sum(H), sum(H + D) must remain the same. -// - influence of D : low values of G + H are searched where D > 0 (not where D >= 0) +// - influence of D : low values of G + H are searched where D > 0 (not where D == 0) From f3708e21d8111648c49bf37cc5d34d458e0cf56c Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Tue, 17 Dec 2024 18:10:13 +0100 Subject: [PATCH 11/27] New hydro remix : adding 2 unit tests on levels computation --- src/solver/simulation/hydro-remix-new.cpp | 2 +- .../solver/simulation/test-hydro-remix.cpp | 73 ++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 4a6cd7d3bf..081cfb6663 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -107,7 +107,7 @@ static void checkInputCorrectness(const std::vector& G, throw std::invalid_argument(msg_prefix + "H not smaller than Pmax everywhere"); } } - + // Hydro production > Pmin for (int h = 0; h < H.size(); h++) { if (H[h] < P_min[h]) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 4e553e9e61..eda4954719 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG)); } -BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20mwh) +BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh) { std::vector P_max(5, 40.); std::vector P_min(5, 0.); @@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_smoothed_to_mean_H_20mwh) +BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh) { std::vector P_max(5, 40.); std::vector P_min(5, 0.); @@ -321,9 +321,78 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) BOOST_CHECK(new_D2 == expected_D2); } +BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed) +{ + // Not important + std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + std::vector D(5, 10.); + double capa = 1000.; + + // Used for level computations + double initial_level = 500.; + std::vector ovf(5, 25.), H(5, 20.); // Cause levels to lower + std::vector inflows(5, 15.), pump(5, 10.); // Cause levels to raise + + auto [new_H, new_D, levels] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capa, + inflows, + ovf, + pump, + S, + DTG_MRG); + + std::vector expected_levels = {480., 460., 440., 420., 400.}; + BOOST_TEST(levels == expected_levels, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(what_if_levels_are_up_bounded_by_capacity) +{ + // Not important + std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + std::vector D(5, 10.); + + // Used for level computations + double initial_level = 500.; + double capacity = 550.; + std::vector ovf(5, 15.), H(5, 10.); // Cause levels to lower + std::vector inflows(5, 25.), pump(5, 20.); // Cause levels to raise + + auto [new_H, new_D, levels] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + initial_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + // Bad ! Levels not limited by capacity. + std::vector expected_levels = {520., 540., 560., 580., 600.}; + BOOST_TEST(levels == expected_levels, boost::test_tools::per_element()); +} + // Ideas for building further tests : // ================================ // - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output // vectors, run the algo on reversed vectors gives reversed output result vectors) // - After running remix hydro algo, sum(H), sum(H + D) must remain the same. // - influence of D : low values of G + H are searched where D > 0 (not where D == 0) + +// Possible simplifications / clarifications of the algorithm itself : +// - remove french from variable names +// - the algo is flat, it's C (not C++), it should be divided in a small number of steps +// - max_pic is a up hydro production margin (H_up_mrg) +// - max_creux is a down hydro production margin (H_down_mrg) +// - an iter updates new_H : it's its main job. So new_D could be updated from new_H at the +// end of an iteration, separately. +// - they are 3 while loops. 2 loops should be enough (the iteration loop and +// another one simply updating new_H and new_D) From 4c8d40757daea5cc20537f9061252da93f36b0d3 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Wed, 18 Dec 2024 11:35:55 +0100 Subject: [PATCH 12/27] New hydro remix : improve comparison between 2 std::vector --- src/solver/simulation/hydro-remix-new.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 081cfb6663..ccb85ece18 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -56,6 +55,13 @@ int find_max_index(const std::vector& G_plus_H, return max_idx; } +static bool isLessThan(const std::vector& a, const std::vector& b) +{ + std::vector a_minus_b; + std::ranges::transform(a, b, std::back_inserter(a_minus_b), std::minus()); + return std::ranges::all_of(a_minus_b, [](double d) { return d <= 0.; }); +} + static void checkInputCorrectness(const std::vector& G, const std::vector& H, const std::vector& D, @@ -100,20 +106,14 @@ static void checkInputCorrectness(const std::vector& G, } // Hydro production < Pmax - for (int h = 0; h < H.size(); h++) + if (!isLessThan(H, P_max)) { - if (H[h] > P_max[h]) - { - throw std::invalid_argument(msg_prefix + "H not smaller than Pmax everywhere"); - } + throw std::invalid_argument(msg_prefix + "H not smaller than Pmax everywhere"); } // Hydro production > Pmin - for (int h = 0; h < H.size(); h++) + if (!isLessThan(P_min, H)) { - if (H[h] < P_min[h]) - { - throw std::invalid_argument(msg_prefix + "H not greater than Pmin everywhere"); - } + throw std::invalid_argument(msg_prefix + "H not greater than Pmin everywhere"); } } From 88dd5ae9b4d74e8ad0d8cc754e00ba15c3f1f77e Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Wed, 18 Dec 2024 11:52:17 +0100 Subject: [PATCH 13/27] New hydro remix : make a test rightfully fail --- src/tests/src/solver/simulation/test-hydro-remix.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index eda4954719..c226fc5281 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -378,6 +378,8 @@ BOOST_AUTO_TEST_CASE(what_if_levels_are_up_bounded_by_capacity) // Bad ! Levels not limited by capacity. std::vector expected_levels = {520., 540., 560., 580., 600.}; BOOST_TEST(levels == expected_levels, boost::test_tools::per_element()); + + BOOST_CHECK(std::ranges::all_of(levels, [&capacity](double e) { return e <= capacity; })); } // Ideas for building further tests : From d3a24ef005f1bd3795a9cb7bf65b77301654cdfb Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Wed, 18 Dec 2024 15:00:52 +0100 Subject: [PATCH 14/27] New hydro remix : adding unit tests on levels computed from input data --- src/solver/simulation/hydro-remix-new.cpp | 42 ++++++-- .../solver/simulation/test-hydro-remix.cpp | 100 +++++++++--------- 2 files changed, 84 insertions(+), 58 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index ccb85ece18..96de0ab226 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -59,16 +59,27 @@ static bool isLessThan(const std::vector& a, const std::vector& { std::vector a_minus_b; std::ranges::transform(a, b, std::back_inserter(a_minus_b), std::minus()); - return std::ranges::all_of(a_minus_b, [](double d) { return d <= 0.; }); + return std::ranges::all_of(a_minus_b, [](const double& e) { return e <= 0.; }); +} + +static bool isLessThan(const std::vector& v, const double c) +{ + return std::ranges::all_of(v, [&c](const double& e) { return e <= c; }); +} + +static bool isGreaterThan(const std::vector& v, const double c) +{ + return std::ranges::all_of(v, [&c](const double& e) { return e >= c; }); } static void checkInputCorrectness(const std::vector& G, const std::vector& H, const std::vector& D, + const std::vector& levels, const std::vector& P_max, const std::vector& P_min, double initial_level, - double capa, + double capacity, const std::vector& inflows, const std::vector& overflow, const std::vector& pump, @@ -78,7 +89,7 @@ static void checkInputCorrectness(const std::vector& G, std::string msg_prefix = "Remix hydro input : "; // Initial level smaller than capacity - if (initial_level > capa) + if (initial_level > capacity) { throw std::invalid_argument(msg_prefix + "initial level > reservoir capacity"); } @@ -86,6 +97,7 @@ static void checkInputCorrectness(const std::vector& G, std::vector sizes = {G.size(), H.size(), D.size(), + levels.size(), P_max.size(), P_min.size(), inflows.size(), @@ -115,6 +127,12 @@ static void checkInputCorrectness(const std::vector& G, { throw std::invalid_argument(msg_prefix + "H not greater than Pmin everywhere"); } + + if (!isLessThan(levels, capacity) || !isGreaterThan(levels, 0.)) + { + throw std::invalid_argument(msg_prefix + + "levels computed from input don't respect constraints"); + } } struct RemixHydroOutput @@ -137,9 +155,20 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, const std::vector& S, const std::vector& DTG_MRG) { + std::vector levels(G.size()); + if (levels.size()) + { + levels[0] = initial_level + inflows[0] - overflow[0] + pump[0] - H[0]; + for (size_t i = 1; i < levels.size(); ++i) + { + levels[i] = levels[i - 1] + inflows[i] - overflow[i] + pump[i] - H[i]; + } + } + checkInputCorrectness(G, H, D, + levels, P_max, P_min, initial_level, @@ -170,13 +199,6 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, std::vector G_plus_H(G.size()); std::transform(G.begin(), G.end(), new_H.begin(), G_plus_H.begin(), std::plus<>()); - std::vector levels(G.size()); - levels[0] = initial_level + inflows[0] - overflow[0] + pump[0] - new_H[0]; - for (size_t i = 1; i < levels.size(); ++i) - { - levels[i] = levels[i - 1] + inflows[i] - overflow[i] + pump[i] - new_H[i]; - } - while (loop-- > 0) { std::vector tried_creux(G.size(), 0); diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index c226fc5281..46ac1c1d1e 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -24,7 +24,7 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, const std::vector& D, const std::vector& P_max, const std::vector& P_min, - double initial_level, + double init_level, double capa, const std::vector& inflow, const std::vector& overflow, @@ -36,15 +36,15 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, using namespace Antares::Solver::Simulation; -BOOST_AUTO_TEST_CASE(input_arrays_of_different_sizes__exception_raised) +BOOST_AUTO_TEST_CASE(input_vectors_of_different_sizes__exception_raised) { std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; std::vector H = {0., 0.}; - double initial_level = 0.; + double init_level = 0.; double capa = 0.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : arrays of different sizes")); } @@ -53,11 +53,11 @@ BOOST_AUTO_TEST_CASE(input_init_level_exceeds_capacity__exception_raised) { std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; std::vector H = {0., 0.}; - double initial_level = 2.; + double init_level = 2.; double capa = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : initial level > reservoir capacity")); } @@ -65,11 +65,11 @@ BOOST_AUTO_TEST_CASE(input_init_level_exceeds_capacity__exception_raised) BOOST_AUTO_TEST_CASE(all_input_arrays_of_size_0__exception_raised) { std::vector G, H, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; - double initial_level = 0.; + double init_level = 0.; double capa = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : all arrays of sizes 0")); } @@ -80,11 +80,11 @@ BOOST_AUTO_TEST_CASE(H_not_smaller_than_pmax__exception_raised) std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); std::vector H = {1., 2., 3., 4., 5.}; std::vector P_max = {2., 2., 2., 4., 5.}; - double initial_level = 0.; + double init_level = 0.; double capa = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : H not smaller than Pmax everywhere")); } @@ -95,11 +95,11 @@ BOOST_AUTO_TEST_CASE(H_not_greater_than_pmin__exception_raised) std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); std::vector H = {1., 2., 3., 4., 5.}; std::vector P_min = {0., 0., 4., 0., 0.}; - double initial_level = 0.; + double init_level = 0.; double capa = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : H not greater than Pmin everywhere")); } @@ -108,11 +108,11 @@ BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) { std::vector G = {0.}, H = {0.}, D = {0.}, P_max = {0.}, P_min = {0.}, inflows = {0.}; std::vector ovf = {0.}, pump = {0.}, S = {0.}, DTG_MRG = {0.}; - double initial_level = 0.; + double init_level = 0.; double capa = 1.; BOOST_CHECK_NO_THROW( - new_remix_hydro(G, H, D, P_max, P_min, initial_level, capa, inflows, ovf, pump, S, DTG_MRG)); + new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG)); } BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh) @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 std::vector G(5, 100.); std::vector H = {0., 10., 20., 30., 40.}; // we have Pmin <= H <= Pmax std::vector D = {80.0, 60., 40., 20., 0.}; - double initial_level = 500.; + double init_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -132,7 +132,7 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -154,7 +154,7 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) std::vector G(5, 100.); std::vector H = {0., 10., 20., 30., 40.}; std::vector D = {80.0, 60., 40., 20., 0.}; - double initial_level = 500.; + double init_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 std::vector G(5, 100.); std::vector H = {40., 30., 20., 10., 0.}; std::vector D = {0., 20., 40., 60., 80.}; - double initial_level = 500.; + double init_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) // H is flat and must respect H <= Pmax everywhere std::vector H = {20., 20., 20., 20., 20.}; std::vector D = {50., 50., 50., 50., 50.}; - double initial_level = 500.; + double init_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -254,7 +254,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) // H is flat and must respect Pmin <= H <= Pmax everywhere std::vector H = {20., 20., 20., 20., 20.}; std::vector D = {50., 50., 50., 50., 50.}; - double initial_level = 500.; + double init_level = 500.; double capa = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -307,7 +307,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -323,13 +323,13 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed) { - // Not important + // Not important for testing levels std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); std::vector D(5, 10.); double capa = 1000.; // Used for level computations - double initial_level = 500.; + double init_level = 500.; std::vector ovf(5, 25.), H(5, 20.); // Cause levels to lower std::vector inflows(5, 15.), pump(5, 10.); // Cause levels to raise @@ -338,7 +338,7 @@ BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed D, P_max, P_min, - initial_level, + init_level, capa, inflows, ovf, @@ -350,36 +350,40 @@ BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed BOOST_TEST(levels == expected_levels, boost::test_tools::per_element()); } -BOOST_AUTO_TEST_CASE(what_if_levels_are_up_bounded_by_capacity) +BOOST_AUTO_TEST_CASE(input_leads_to_levels_over_capacity___exception_raised) { - // Not important + // Not important for testing levels std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); std::vector D(5, 10.); // Used for level computations - double initial_level = 500.; + double init_level = 500.; double capacity = 550.; std::vector ovf(5, 15.), H(5, 10.); // Cause levels to lower std::vector inflows(5, 25.), pump(5, 20.); // Cause levels to raise - auto [new_H, new_D, levels] = new_remix_hydro(G, - H, - D, - P_max, - P_min, - initial_level, - capacity, - inflows, - ovf, - pump, - S, - DTG_MRG); + BOOST_CHECK_EXCEPTION( + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), + std::invalid_argument, + checkMessage("Remix hydro input : levels computed from input don't respect constraints")); +} - // Bad ! Levels not limited by capacity. - std::vector expected_levels = {520., 540., 560., 580., 600.}; - BOOST_TEST(levels == expected_levels, boost::test_tools::per_element()); +BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) +{ + // Not important for testing levels + std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + std::vector D(5, 10.); + + // Used for level computations + double init_level = 50.; + double capacity = 100.; + std::vector ovf(5, 30.), H(5, 10.); // Cause levels to lower + std::vector inflows(5, 10.), pump(5, 10.); // Cause levels to raise - BOOST_CHECK(std::ranges::all_of(levels, [&capacity](double e) { return e <= capacity; })); + BOOST_CHECK_EXCEPTION( + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), + std::invalid_argument, + checkMessage("Remix hydro input : levels computed from input don't respect constraints")); } // Ideas for building further tests : From cd35bdb413e05813c9e6e1db5b57c594c7b42ee4 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 19 Dec 2024 13:24:19 +0100 Subject: [PATCH 15/27] New hydro remix : adding a failing test about of impact reservoir capacity on remix hydro algorithm --- src/solver/simulation/hydro-remix-new.cpp | 4 +- .../solver/simulation/test-hydro-remix.cpp | 116 +++++++++++++----- 2 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 96de0ab226..11b59b4585 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -131,7 +131,7 @@ static void checkInputCorrectness(const std::vector& G, if (!isLessThan(levels, capacity) || !isGreaterThan(levels, 0.)) { throw std::invalid_argument(msg_prefix - + "levels computed from input don't respect constraints"); + + "levels computed from input don't respect reservoir bounds"); } } @@ -183,7 +183,7 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, std::vector new_D = D; int loop = 1000; - double eps = 1e-2; + double eps = 1e-3; double top = *std::max_element(G.begin(), G.end()) + *std::max_element(H.begin(), H.end()) + *std::max_element(D.begin(), D.end()) + 1; diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 46ac1c1d1e..2273e59f26 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -25,7 +25,7 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, const std::vector& P_max, const std::vector& P_min, double init_level, - double capa, + double capacity, const std::vector& inflow, const std::vector& overflow, const std::vector& pump, @@ -41,10 +41,10 @@ BOOST_AUTO_TEST_CASE(input_vectors_of_different_sizes__exception_raised) std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; std::vector H = {0., 0.}; double init_level = 0.; - double capa = 0.; + double capacity = 0.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : arrays of different sizes")); } @@ -54,10 +54,10 @@ BOOST_AUTO_TEST_CASE(input_init_level_exceeds_capacity__exception_raised) std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; std::vector H = {0., 0.}; double init_level = 2.; - double capa = 1.; + double capacity = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : initial level > reservoir capacity")); } @@ -66,10 +66,10 @@ BOOST_AUTO_TEST_CASE(all_input_arrays_of_size_0__exception_raised) { std::vector G, H, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; double init_level = 0.; - double capa = 1.; + double capacity = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : all arrays of sizes 0")); } @@ -81,10 +81,10 @@ BOOST_AUTO_TEST_CASE(H_not_smaller_than_pmax__exception_raised) std::vector H = {1., 2., 3., 4., 5.}; std::vector P_max = {2., 2., 2., 4., 5.}; double init_level = 0.; - double capa = 1.; + double capacity = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : H not smaller than Pmax everywhere")); } @@ -96,10 +96,10 @@ BOOST_AUTO_TEST_CASE(H_not_greater_than_pmin__exception_raised) std::vector H = {1., 2., 3., 4., 5.}; std::vector P_min = {0., 0., 4., 0., 0.}; double init_level = 0.; - double capa = 1.; + double capacity = 1.; BOOST_CHECK_EXCEPTION( - new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG), + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, checkMessage("Remix hydro input : H not greater than Pmin everywhere")); } @@ -109,10 +109,10 @@ BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) std::vector G = {0.}, H = {0.}, D = {0.}, P_max = {0.}, P_min = {0.}, inflows = {0.}; std::vector ovf = {0.}, pump = {0.}, S = {0.}, DTG_MRG = {0.}; double init_level = 0.; - double capa = 1.; + double capacity = 1.; BOOST_CHECK_NO_THROW( - new_remix_hydro(G, H, D, P_max, P_min, init_level, capa, inflows, ovf, pump, S, DTG_MRG)); + new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG)); } BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh) @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 std::vector H = {0., 10., 20., 30., 40.}; // we have Pmin <= H <= Pmax std::vector D = {80.0, 60., 40., 20., 0.}; double init_level = 500.; - double capa = 1000.; + double capacity = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) std::vector H = {0., 10., 20., 30., 40.}; std::vector D = {80.0, 60., 40., 20., 0.}; double init_level = 500.; - double capa = 1000.; + double capacity = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 std::vector H = {40., 30., 20., 10., 0.}; std::vector D = {0., 20., 40., 60., 80.}; double init_level = 500.; - double capa = 1000.; + double capacity = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -222,7 +222,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) std::vector H = {20., 20., 20., 20., 20.}; std::vector D = {50., 50., 50., 50., 50.}; double init_level = 500.; - double capa = 1000.; + double capacity = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) std::vector H = {20., 20., 20., 20., 20.}; std::vector D = {50., 50., 50., 50., 50.}; double init_level = 500.; - double capa = 1000.; + double capacity = 1000.; std::vector inflows(5, 0.); std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); @@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -326,7 +326,7 @@ BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed // Not important for testing levels std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); std::vector D(5, 10.); - double capa = 1000.; + double capacity = 1000.; // Used for level computations double init_level = 500.; @@ -339,7 +339,7 @@ BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed P_max, P_min, init_level, - capa, + capacity, inflows, ovf, pump, @@ -383,7 +383,66 @@ BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, - checkMessage("Remix hydro input : levels computed from input don't respect constraints")); + checkMessage( + "Remix hydro input : levels computed from input don't respect reservoir bounds")); +} + +BOOST_AUTO_TEST_CASE(influence_of_reservoir_capacity_on_hydro_production, + *boost::unit_test::tolerance(0.001)) +{ + // Not important for this test + std::vector P_max(10, std::numeric_limits::max()); + std::vector P_min(10, 0.); + std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); + std::vector D(10, 20.); + + // H oscillates between 10 and 20 (new H will be flattened to 15 everywhere) + std::vector H = {10., 20., 10., 20., 10., 20., 10., 20., 10., 20.}; + // First inflows > H, then inflows < H. Consequence : levels first increase, then decrease. + std::vector inflows = {25., 25., 25., 25., 25., 5., 5., 5., 5., 5.}; + double init_level = 100.; + // H and inflows result in : input_levels = {115, 120, 135, 140, 155, 140, 135, 120, 115, 100} + // Note sup(input_levels) = 155 + + // Case 1 : capacity unlimited + double capacity = std::numeric_limits::max(); + auto [new_H, new_D, L] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + std::vector expected_H(10, 15.); // H is flat and is 15. (means of initial H) + // Levels associated to new H are such as sup(L) = 150. < sup(input_levels) = 155 + std::vector expected_L = {110., 120., 130., 140., 150., 140., 130., 120., 110., 100.}; + BOOST_TEST(new_H == expected_H, boost::test_tools::per_element()); + BOOST_TEST(L == expected_L, boost::test_tools::per_element()); + + // Now, if we limit to capacity to sup(input_levels) = 155, we should have same H and L + // as previously + capacity = 155.; + auto [new_H2, new_D2, L2] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + BOOST_TEST(new_H2 == expected_H, boost::test_tools::per_element()); + BOOST_TEST(L2 == expected_L, boost::test_tools::per_element()); } // Ideas for building further tests : @@ -392,6 +451,7 @@ BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) // vectors, run the algo on reversed vectors gives reversed output result vectors) // - After running remix hydro algo, sum(H), sum(H + D) must remain the same. // - influence of D : low values of G + H are searched where D > 0 (not where D == 0) +// - // Possible simplifications / clarifications of the algorithm itself : // - remove french from variable names From f7757b1013c3aceae300b9350811cfe867503cfd Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 19 Dec 2024 14:17:24 +0100 Subject: [PATCH 16/27] New hydro remix : change algorithm to try to fix a test failure --- src/solver/simulation/hydro-remix-new.cpp | 30 ++++++++++++------- .../solver/simulation/test-hydro-remix.cpp | 2 +- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 11b59b4585..789a0ff988 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -237,18 +237,28 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, + std::min(idx_creux, idx_pic), levels.begin() + std::max(idx_creux, idx_pic)); + double max_pic, max_creux; + if (idx_creux < idx_creux) + { + max_pic = capa; + max_creux = *std::min_element(intermediate_level.begin(), + intermediate_level.end()); + } + else + { + max_pic = capa + - *std::max_element(intermediate_level.begin(), + intermediate_level.end()); + max_creux = capa; + } + + max_pic = std::min(new_H[idx_pic] - P_min[idx_pic], max_pic); + max_creux = std::min( + {P_max[idx_creux] - new_H[idx_creux], new_D[idx_creux], max_creux}); - double max_pic = std::min(new_H[idx_pic] - P_min[idx_pic], - capa - - *std::max_element(intermediate_level.begin(), - intermediate_level.end())); - double max_creux = std::min( - {P_max[idx_creux] - new_H[idx_creux], - new_D[idx_creux], - *std::min_element(intermediate_level.begin(), intermediate_level.end())}); - double dif_pic_creux = std::max(G_plus_H[idx_pic] - G_plus_H[idx_creux], 0.0); + double dif_pic_creux = std::max(G_plus_H[idx_pic] - G_plus_H[idx_creux], 0.); - delta = std::max(std::min({max_pic, max_creux, dif_pic_creux / 2.0}), 0.0); + delta = std::max(std::min({max_pic, max_creux, dif_pic_creux / 2.}), 0.); if (delta > 0) { diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 2273e59f26..cbff0f49b9 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -456,7 +456,7 @@ BOOST_AUTO_TEST_CASE(influence_of_reservoir_capacity_on_hydro_production, // Possible simplifications / clarifications of the algorithm itself : // - remove french from variable names // - the algo is flat, it's C (not C++), it should be divided in a small number of steps -// - max_pic is a up hydro production margin (H_up_mrg) +// - max_pic is an up hydro production margin (H_up_mrg) // - max_creux is a down hydro production margin (H_down_mrg) // - an iter updates new_H : it's its main job. So new_D could be updated from new_H at the // end of an iteration, separately. From ea7e23f047623cab059e0782f837b17b47e684cd Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 19 Dec 2024 14:22:32 +0100 Subject: [PATCH 17/27] New hydro remix : oops, tiny fix --- src/tests/src/solver/simulation/test-hydro-remix.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index cbff0f49b9..4fbf8cef27 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -365,7 +365,8 @@ BOOST_AUTO_TEST_CASE(input_leads_to_levels_over_capacity___exception_raised) BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), std::invalid_argument, - checkMessage("Remix hydro input : levels computed from input don't respect constraints")); + checkMessage( + "Remix hydro input : levels computed from input don't respect reservoir bounds")); } BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) From 6ed6f62e3050035888c483a47db43600bc26d59d Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 19 Dec 2024 14:38:41 +0100 Subject: [PATCH 18/27] New hydro remix : fix the previous fix --- src/solver/simulation/hydro-remix-new.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 789a0ff988..12a5c4d626 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -238,7 +238,7 @@ RemixHydroOutput new_remix_hydro(const std::vector& G, levels.begin() + std::max(idx_creux, idx_pic)); double max_pic, max_creux; - if (idx_creux < idx_creux) + if (idx_creux < idx_pic) { max_pic = capa; max_creux = *std::min_element(intermediate_level.begin(), From adb89a75c068019a146d07c35e24d6008a2797bf Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 19 Dec 2024 16:09:08 +0100 Subject: [PATCH 19/27] New hydro remix : adding a unit test on getting a sub optimal solution for H --- .../solver/simulation/test-hydro-remix.cpp | 69 ++++++++++++++++++- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 4fbf8cef27..2ed37a3799 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -388,7 +388,7 @@ BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) "Remix hydro input : levels computed from input don't respect reservoir bounds")); } -BOOST_AUTO_TEST_CASE(influence_of_reservoir_capacity_on_hydro_production, +BOOST_AUTO_TEST_CASE(influence_of_capacity_on_hydro_prod___case_where_no_influence, *boost::unit_test::tolerance(0.001)) { // Not important for this test @@ -426,8 +426,9 @@ BOOST_AUTO_TEST_CASE(influence_of_reservoir_capacity_on_hydro_production, BOOST_TEST(new_H == expected_H, boost::test_tools::per_element()); BOOST_TEST(L == expected_L, boost::test_tools::per_element()); - // Now, if we limit to capacity to sup(input_levels) = 155, we should have same H and L - // as previously + // Case 2 : now, if we lower capacity to sup(input_levels) = 155, we should + // have same computed H and L as previously : this value of capacity should + // not have an influence on H and levels as results of the algorithm. capacity = 155.; auto [new_H2, new_D2, L2] = new_remix_hydro(G, H, @@ -446,6 +447,68 @@ BOOST_AUTO_TEST_CASE(influence_of_reservoir_capacity_on_hydro_production, BOOST_TEST(L2 == expected_L, boost::test_tools::per_element()); } +BOOST_AUTO_TEST_CASE(lowering_capacity_too_much_leads_to_suboptimal_solution_for_GplusH, + *boost::unit_test::tolerance(0.001)) +{ + // Not important for this test + std::vector P_max(10, std::numeric_limits::max()); + std::vector P_min(10, 0.); + std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); + std::vector D(10, 20.); + + // H oscillates between 10 and 20 (new H will be flattened to 15 everywhere) + std::vector H = {20., 10., 20., 10., 20., 10., 20., 10., 20., 10.}; + // First inflows > H, then inflows < H. Consequence : levels first increase, then decrease. + std::vector inflows = {25., 25., 25., 25., 25., 5., 5., 5., 5., 5.}; + double init_level = 100.; + // H and inflows result in : input_levels = {105, 120, 125, 140, 145, 140, 125, 120, 105, 100} + // Note sup(input_levels) = 145 + + // Case 1 : capacity unlimited + double capacity = std::numeric_limits::max(); + auto [new_H, new_D, L] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + std::vector expected_H(10, 15.); // H is flat and is 15. (means of initial H) + // Levels associated to new H are such as sup(L) = 150. > sup(input_levels) = 145 + std::vector expected_L = {110., 120., 130., 140., 150., 140., 130., 120., 110., 100.}; + BOOST_TEST(new_H == expected_H, boost::test_tools::per_element()); + BOOST_TEST(L == expected_L, boost::test_tools::per_element()); + + // Case 2 : now we lower capacity to sup(input_levels) = 145. + // This makes input acceptable for algo : levels computed from input have an + // up bound <= capacity + // But this time levels can not increase up to sup(L) = 150., as it would if capacity + // was infinite. So we expect to get an output H flat by interval, not on the whole domain. + capacity = 145.; + auto [new_H2, new_D2, L2] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + // new_H2 is flat by interval + std::vector expected_H2 = {16., 16., 16., 16., 16., 14., 14., 14., 14., 14.}; + BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); +} + // Ideas for building further tests : // ================================ // - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output From 72f75e45bb746ab9c051a766b20b9b363ac62a97 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 19 Dec 2024 17:39:14 +0100 Subject: [PATCH 20/27] New hydro remix : adding a unit test about the effect of lowering initial level too low --- .../solver/simulation/test-hydro-remix.cpp | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 2ed37a3799..af6110dac4 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -447,7 +447,7 @@ BOOST_AUTO_TEST_CASE(influence_of_capacity_on_hydro_prod___case_where_no_influen BOOST_TEST(L2 == expected_L, boost::test_tools::per_element()); } -BOOST_AUTO_TEST_CASE(lowering_capacity_too_much_leads_to_suboptimal_solution_for_GplusH, +BOOST_AUTO_TEST_CASE(lowering_capacity_too_low_leads_to_suboptimal_solution_for_GplusH, *boost::unit_test::tolerance(0.001)) { // Not important for this test @@ -509,6 +509,69 @@ BOOST_AUTO_TEST_CASE(lowering_capacity_too_much_leads_to_suboptimal_solution_for BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); } +BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution_for_GplusH, + *boost::unit_test::tolerance(0.001)) +{ + // Not important for this test + std::vector P_max(10, std::numeric_limits::max()); + std::vector P_min(10, 0.); + std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); + std::vector D(10, 20.); + + // H oscillates between 20 and 30 (new H will be flattened to 15 everywhere) + std::vector H = {20., 30., 20., 30., 20., 30., 20., 30., 20., 30.}; + // First inflows < H, then inflows > H. Consequence : levels first decrease, then increase. + std::vector inflows = {5., 5., 5., 5., 5., 45., 45., 45., 45., 45.}; + double capacity = std::numeric_limits::max(); + double init_level = 100.; + // H and inflows result in : input_levels = {85, 60, 45, 20, 5, 20, 45, 60, 85, 100} + // Note : inf(input_levels) = 5 + + // Case 1 : init level (== 100) is high enough so that input levels (computed from input data) + // are acceptable by algorithm, and levels computed by algorithm (output) are optimal, that is + // computed from a optimal (that is flat) new_H. + auto [new_H, new_D, L] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + std::vector expected_H(10, 25.); // H is flat and is 25. (means of initial H) + // Levels associated to new H are such as inf(L) = 0. > inf(input_levels) = 5 + std::vector expected_L = {80., 60., 40., 20., 0., 20., 40., 60., 80., 100.}; + BOOST_TEST(new_H == expected_H, boost::test_tools::per_element()); + BOOST_TEST(L == expected_L, boost::test_tools::per_element()); + + // Case 2 : now we lower initial level. We know that input data are still acceptable + // for algorithm, and that algorithm will have take the levels lower bound (0.) + // into account. As the level change, the solution new_H will be suboptimal, that + // is flat by interval. + init_level = 95.; + auto [new_H2, new_D2, L2] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + // new_H2 is flat by interval + std::vector expected_H2 = {24., 24., 24., 24., 24., 26., 26., 26., 26., 26.}; + BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); +} + // Ideas for building further tests : // ================================ // - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output From 08bbd702dbc8f570348d4aeef67ea3cbcd920fc6 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Thu, 19 Dec 2024 18:15:51 +0100 Subject: [PATCH 21/27] New hydro remix : adding test that case where initial level has no influence on algorithm --- .../solver/simulation/test-hydro-remix.cpp | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index af6110dac4..4c8f1e42bc 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -388,7 +388,7 @@ BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) "Remix hydro input : levels computed from input don't respect reservoir bounds")); } -BOOST_AUTO_TEST_CASE(influence_of_capacity_on_hydro_prod___case_where_no_influence, +BOOST_AUTO_TEST_CASE(influence_of_capacity_on_algorithm___case_where_no_influence, *boost::unit_test::tolerance(0.001)) { // Not important for this test @@ -518,7 +518,7 @@ BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); std::vector D(10, 20.); - // H oscillates between 20 and 30 (new H will be flattened to 15 everywhere) + // H oscillates between 20 and 30 (new H will be flattened to 25 everywhere) std::vector H = {20., 30., 20., 30., 20., 30., 20., 30., 20., 30.}; // First inflows < H, then inflows > H. Consequence : levels first decrease, then increase. std::vector inflows = {5., 5., 5., 5., 5., 45., 45., 45., 45., 45.}; @@ -550,7 +550,7 @@ BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution BOOST_TEST(L == expected_L, boost::test_tools::per_element()); // Case 2 : now we lower initial level. We know that input data are still acceptable - // for algorithm, and that algorithm will have take the levels lower bound (0.) + // for algorithm, and that algorithm will have to take the levels lower bound (0.) // into account. As the level change, the solution new_H will be suboptimal, that // is flat by interval. init_level = 95.; @@ -572,6 +572,68 @@ BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); } +BOOST_AUTO_TEST_CASE(influence_of_initial_level_on_algorithm___case_where_no_influence, + *boost::unit_test::tolerance(0.001)) +{ + // Not important for this test + std::vector P_max(10, std::numeric_limits::max()); + std::vector P_min(10, 0.); + std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); + std::vector D(10, 20.); + + // H oscillates between 10 and 20 (new H will be flattened to 15 everywhere) + std::vector H = {20., 10., 20., 10., 20., 10., 20., 10., 20., 10.}; + // First inflows < H, then inflows > H. Consequence : levels first decrease, then increase. + std::vector inflows = {5., 5., 5., 5., 5., 25., 25., 25., 25., 25.}; + double capacity = std::numeric_limits::max(); + double init_level = 100.; + // H and inflows are such as inf(input_levels) = 45 + + // Case 1 : init level (== 100) is high enough so that input levels (computed from input data) + // are acceptable by algorithm, and levels computed by algorithm (output) are optimal, that + // is computed from a optimal (that is flat) new_H. + auto [new_H, new_D, L] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + std::vector expected_H(10, 15.); // H is flat and is 15. (means of initial H) + // Levels associated to new H are such as inf(L) = 50 > inf(input_levels) = 45 + std::vector expected_L = {90., 80., 70., 60., 50., 60., 70., 80., 90., 100.}; + BOOST_TEST(new_H == expected_H, boost::test_tools::per_element()); + BOOST_TEST(L == expected_L, boost::test_tools::per_element()); + + // Case 2 : now we lower initial level down to 55. + // In this way, input data is still acceptable for algorithm + // and algorithm won't have to take the levels lower bound (0.) into account. + // The solution new_H will be optimal, that is flat by interval. + init_level = 55.; + auto [new_H2, new_D2, L2] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + // new_H2 is flat (and optimal) + std::vector expected_H2(10, 15.); + BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); +} + // Ideas for building further tests : // ================================ // - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output From f0854dd2a742dd206196230b69281bc76edf54e3 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 20 Dec 2024 10:38:41 +0100 Subject: [PATCH 22/27] New hydro remix : creating a header for remix hydro algorithm --- src/solver/simulation/hydro-remix-new.cpp | 9 ++---- .../solver/simulation/hydro-remix-new.h | 29 +++++++++++++++++++ .../src/solver/simulation/CMakeLists.txt | 5 ++++ .../solver/simulation/test-hydro-remix.cpp | 26 +---------------- 4 files changed, 37 insertions(+), 32 deletions(-) create mode 100644 src/solver/simulation/include/antares/solver/simulation/hydro-remix-new.h diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 12a5c4d626..141f3bcf4f 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -1,3 +1,5 @@ +#include "include/antares/solver/simulation/hydro-remix-new.h" + #include #include #include @@ -135,13 +137,6 @@ static void checkInputCorrectness(const std::vector& G, } } -struct RemixHydroOutput -{ - std::vector new_H; - std::vector new_D; - std::vector levels; -}; - RemixHydroOutput new_remix_hydro(const std::vector& G, const std::vector& H, const std::vector& D, diff --git a/src/solver/simulation/include/antares/solver/simulation/hydro-remix-new.h b/src/solver/simulation/include/antares/solver/simulation/hydro-remix-new.h new file mode 100644 index 0000000000..f621d90628 --- /dev/null +++ b/src/solver/simulation/include/antares/solver/simulation/hydro-remix-new.h @@ -0,0 +1,29 @@ + +#pragma once + +#include + +namespace Antares::Solver::Simulation +{ + +struct RemixHydroOutput +{ + std::vector new_H; + std::vector new_D; + std::vector levels; +}; + +RemixHydroOutput new_remix_hydro(const std::vector& G, + const std::vector& H, + const std::vector& D, + const std::vector& P_max, + const std::vector& P_min, + double init_level, + double capacity, + const std::vector& inflow, + const std::vector& overflow, + const std::vector& pump, + const std::vector& S, + const std::vector& DTG_MRG); + +} // namespace Antares::Solver::Simulation diff --git a/src/tests/src/solver/simulation/CMakeLists.txt b/src/tests/src/solver/simulation/CMakeLists.txt index be2285170d..e62fbcd2fa 100644 --- a/src/tests/src/solver/simulation/CMakeLists.txt +++ b/src/tests/src/solver/simulation/CMakeLists.txt @@ -130,6 +130,11 @@ add_executable(test-hydro-remix ${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 diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 4c8f1e42bc..640a62c1c8 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -8,31 +8,7 @@ #include -namespace Antares::Solver::Simulation -{ -// gp : instead of this, we should make a header asociated to hydro-remix-new.cpp -// ==> hydro-remix-new.h -struct RemixHydroOutput -{ - std::vector new_H; - std::vector new_D; - std::vector levels; -}; - -RemixHydroOutput new_remix_hydro(const std::vector& G, - const std::vector& H, - const std::vector& D, - const std::vector& P_max, - const std::vector& P_min, - double init_level, - double capacity, - const std::vector& inflow, - const std::vector& overflow, - const std::vector& pump, - const std::vector& S, - const std::vector& DTG_MRG); - -} // namespace Antares::Solver::Simulation +#include "include/antares/solver/simulation/hydro-remix-new.h" using namespace Antares::Solver::Simulation; From 4685abb8268bb506dcc932401d33dd1df5743ff2 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 20 Dec 2024 11:24:31 +0100 Subject: [PATCH 23/27] New hydro remix : define and use proper comparison operator when checking input data --- src/solver/simulation/hydro-remix-new.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 141f3bcf4f..4b6f8a97a0 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -57,19 +57,19 @@ int find_max_index(const std::vector& G_plus_H, return max_idx; } -static bool isLessThan(const std::vector& a, const std::vector& b) +static bool operator<=(const std::vector& a, const std::vector& b) { std::vector a_minus_b; std::ranges::transform(a, b, std::back_inserter(a_minus_b), std::minus()); return std::ranges::all_of(a_minus_b, [](const double& e) { return e <= 0.; }); } -static bool isLessThan(const std::vector& v, const double c) +static bool operator<=(const std::vector& v, const double c) { return std::ranges::all_of(v, [&c](const double& e) { return e <= c; }); } -static bool isGreaterThan(const std::vector& v, const double c) +static bool operator>=(const std::vector& v, const double c) { return std::ranges::all_of(v, [&c](const double& e) { return e >= c; }); } @@ -120,17 +120,18 @@ static void checkInputCorrectness(const std::vector& G, } // Hydro production < Pmax - if (!isLessThan(H, P_max)) + if (!(H <= P_max)) { throw std::invalid_argument(msg_prefix + "H not smaller than Pmax everywhere"); } + // Hydro production > Pmin - if (!isLessThan(P_min, H)) + if (!(P_min <= H)) { throw std::invalid_argument(msg_prefix + "H not greater than Pmin everywhere"); } - if (!isLessThan(levels, capacity) || !isGreaterThan(levels, 0.)) + if (!(levels <= capacity) || !(levels >= 0.)) { throw std::invalid_argument(msg_prefix + "levels computed from input don't respect reservoir bounds"); From 957cd5b8f23df88154cabfc3fd21effe74bf14ad Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 20 Dec 2024 11:36:20 +0100 Subject: [PATCH 24/27] [skip ci] New hydro remix : replace use of std::adjacent_find with use of std::all_of instead --- src/solver/simulation/hydro-remix-new.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/simulation/hydro-remix-new.cpp b/src/solver/simulation/hydro-remix-new.cpp index 4b6f8a97a0..cf76ccedbc 100644 --- a/src/solver/simulation/hydro-remix-new.cpp +++ b/src/solver/simulation/hydro-remix-new.cpp @@ -108,7 +108,7 @@ static void checkInputCorrectness(const std::vector& G, S.size(), DTG_MRG.size()}; - if (std::ranges::adjacent_find(sizes, std::not_equal_to()) != sizes.end()) + if (!std::ranges::all_of(sizes, [&sizes](const size_t s) { return s == sizes.front(); })) { throw std::invalid_argument(msg_prefix + "arrays of different sizes"); } From 6157f341d50c9f9ba00e891a3a8315714a4730cd Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 20 Dec 2024 12:26:58 +0100 Subject: [PATCH 25/27] [skip ci] New hydro remix : make code comment more clear --- .../solver/simulation/test-hydro-remix.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index 640a62c1c8..cb78db4dd5 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(influence_of_capacity_on_algorithm___case_where_no_influenc // H and inflows result in : input_levels = {115, 120, 135, 140, 155, 140, 135, 120, 115, 100} // Note sup(input_levels) = 155 - // Case 1 : capacity unlimited + // Case 1 : capacity unlimited (relaxed) ==> leads to optimal solution (H is flat) double capacity = std::numeric_limits::max(); auto [new_H, new_D, L] = new_remix_hydro(G, H, @@ -403,7 +403,7 @@ BOOST_AUTO_TEST_CASE(influence_of_capacity_on_algorithm___case_where_no_influenc BOOST_TEST(L == expected_L, boost::test_tools::per_element()); // Case 2 : now, if we lower capacity to sup(input_levels) = 155, we should - // have same computed H and L as previously : this value of capacity should + // have H and L identical to previously : this value of capacity should // not have an influence on H and levels as results of the algorithm. capacity = 155.; auto [new_H2, new_D2, L2] = new_remix_hydro(G, @@ -437,10 +437,11 @@ BOOST_AUTO_TEST_CASE(lowering_capacity_too_low_leads_to_suboptimal_solution_for_ // First inflows > H, then inflows < H. Consequence : levels first increase, then decrease. std::vector inflows = {25., 25., 25., 25., 25., 5., 5., 5., 5., 5.}; double init_level = 100.; - // H and inflows result in : input_levels = {105, 120, 125, 140, 145, 140, 125, 120, 105, 100} + // H and inflows lead to have : + // input_levels = {105, 120, 125, 140, 145, 140, 125, 120, 105,100} // Note sup(input_levels) = 145 - // Case 1 : capacity unlimited + // Case 1 : capacity unlimited (relaxed) ==> leads to optimal solution (H is flat) double capacity = std::numeric_limits::max(); auto [new_H, new_D, L] = new_remix_hydro(G, H, @@ -461,11 +462,12 @@ BOOST_AUTO_TEST_CASE(lowering_capacity_too_low_leads_to_suboptimal_solution_for_ BOOST_TEST(new_H == expected_H, boost::test_tools::per_element()); BOOST_TEST(L == expected_L, boost::test_tools::per_element()); - // Case 2 : now we lower capacity to sup(input_levels) = 145. + // Case 2 : we lower capacity to sup(input_levels) = 145. // This makes input acceptable for algo : levels computed from input have an // up bound <= capacity // But this time levels can not increase up to sup(L) = 150., as it would if capacity - // was infinite. So we expect to get an output H flat by interval, not on the whole domain. + // was infinite. Therefore, solution found is suboptimal : we expect to get an + // output H flat by interval, not flat on the whole domain. capacity = 145.; auto [new_H2, new_D2, L2] = new_remix_hydro(G, H, @@ -504,8 +506,8 @@ BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution // Note : inf(input_levels) = 5 // Case 1 : init level (== 100) is high enough so that input levels (computed from input data) - // are acceptable by algorithm, and levels computed by algorithm (output) are optimal, that is - // computed from a optimal (that is flat) new_H. + // are acceptable for algorithm (input levels >= 0.), and running algorithm leads to optimal + // solution (new_H is flat) auto [new_H, new_D, L] = new_remix_hydro(G, H, D, @@ -525,10 +527,10 @@ BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution BOOST_TEST(new_H == expected_H, boost::test_tools::per_element()); BOOST_TEST(L == expected_L, boost::test_tools::per_element()); - // Case 2 : now we lower initial level. We know that input data are still acceptable - // for algorithm, and that algorithm will have to take the levels lower bound (0.) - // into account. As the level change, the solution new_H will be suboptimal, that - // is flat by interval. + // Case 2 : we lower initial level. Input data are still acceptable + // for algorithm (despite the new init level), algorithm will have to take the levels lower + // bound (0.) into account. As the levels change, the solution new_H will be suboptimal, that is + // flat by interval (not flat on the whole domain). init_level = 95.; auto [new_H2, new_D2, L2] = new_remix_hydro(G, H, From 889b982f7cec39982f44a99599ffcb4ef9acf190 Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 20 Dec 2024 15:35:25 +0100 Subject: [PATCH 26/27] New hydro remix : improve unit tests by use of a fixture --- .../solver/simulation/test-hydro-remix.cpp | 292 ++++++++---------- 1 file changed, 130 insertions(+), 162 deletions(-) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index cb78db4dd5..c8568ebbf5 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -12,12 +12,31 @@ using namespace Antares::Solver::Simulation; -BOOST_AUTO_TEST_CASE(input_vectors_of_different_sizes__exception_raised) +template +struct InputFixture { - std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; - std::vector H = {0., 0.}; + InputFixture() + { + G.assign(size, 0.); + H.assign(size, 0.); + D.assign(size, 0.); + P_max.assign(size, std::numeric_limits::max()); + P_min.assign(size, 0.); + inflows.assign(size, 0.); + ovf.assign(size, 0.); + pump.assign(size, 0.); + S.assign(size, 0.); + DTG_MRG.assign(size, 0.); + } + + std::vector G, H, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; double init_level = 0.; - double capacity = 0.; + double capacity = std::numeric_limits::max(); +}; + +BOOST_FIXTURE_TEST_CASE(input_vectors_of_different_sizes__exception_raised, InputFixture<0>) +{ + H = {0., 0.}; BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), @@ -25,12 +44,11 @@ BOOST_AUTO_TEST_CASE(input_vectors_of_different_sizes__exception_raised) checkMessage("Remix hydro input : arrays of different sizes")); } -BOOST_AUTO_TEST_CASE(input_init_level_exceeds_capacity__exception_raised) +BOOST_FIXTURE_TEST_CASE(input_init_level_exceeds_capacity__exception_raised, InputFixture<0>) { - std::vector G, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; - std::vector H = {0., 0.}; - double init_level = 2.; - double capacity = 1.; + H = {0., 0.}; + init_level = 2.; + capacity = 1.; BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), @@ -38,11 +56,10 @@ BOOST_AUTO_TEST_CASE(input_init_level_exceeds_capacity__exception_raised) checkMessage("Remix hydro input : initial level > reservoir capacity")); } -BOOST_AUTO_TEST_CASE(all_input_arrays_of_size_0__exception_raised) +BOOST_FIXTURE_TEST_CASE(all_input_arrays_of_size_0__exception_raised, InputFixture<0>) { - std::vector G, H, D, P_max, P_min, inflows, ovf, pump, S, DTG_MRG; - double init_level = 0.; - double capacity = 1.; + init_level = 0.; + capacity = 1.; BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), @@ -50,14 +67,12 @@ BOOST_AUTO_TEST_CASE(all_input_arrays_of_size_0__exception_raised) checkMessage("Remix hydro input : all arrays of sizes 0")); } -BOOST_AUTO_TEST_CASE(H_not_smaller_than_pmax__exception_raised) +BOOST_FIXTURE_TEST_CASE(H_not_smaller_than_pmax__exception_raised, InputFixture<5>) { - std::vector G(5, 0.), D(5, 0.), P_min(5, 0.), inflows(5, 0.); - std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); - std::vector H = {1., 2., 3., 4., 5.}; - std::vector P_max = {2., 2., 2., 4., 5.}; - double init_level = 0.; - double capacity = 1.; + H = {1., 2., 3., 4., 5.}; + P_max = {2., 2., 2., 4., 5.}; + init_level = 0.; + capacity = 1.; BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), @@ -65,14 +80,12 @@ BOOST_AUTO_TEST_CASE(H_not_smaller_than_pmax__exception_raised) checkMessage("Remix hydro input : H not smaller than Pmax everywhere")); } -BOOST_AUTO_TEST_CASE(H_not_greater_than_pmin__exception_raised) +BOOST_FIXTURE_TEST_CASE(H_not_greater_than_pmin__exception_raised, InputFixture<5>) { - std::vector G(5, 0.), D(5, 0.), P_max(5, 1000.), inflows(5, 0.); - std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); - std::vector H = {1., 2., 3., 4., 5.}; - std::vector P_min = {0., 0., 4., 0., 0.}; - double init_level = 0.; - double capacity = 1.; + H = {1., 2., 3., 4., 5.}; + P_min = {0., 0., 4., 0., 0.}; + init_level = 0.; + capacity = 1.; BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), @@ -80,28 +93,24 @@ BOOST_AUTO_TEST_CASE(H_not_greater_than_pmin__exception_raised) checkMessage("Remix hydro input : H not greater than Pmin everywhere")); } -BOOST_AUTO_TEST_CASE(input_is_acceptable__no_exception_raised) +BOOST_FIXTURE_TEST_CASE(input_is_acceptable__no_exception_raised, InputFixture<1>) { - std::vector G = {0.}, H = {0.}, D = {0.}, P_max = {0.}, P_min = {0.}, inflows = {0.}; - std::vector ovf = {0.}, pump = {0.}, S = {0.}, DTG_MRG = {0.}; - double init_level = 0.; - double capacity = 1.; + init_level = 0.; + capacity = 1.; BOOST_CHECK_NO_THROW( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG)); } -BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh) +BOOST_FIXTURE_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh, + InputFixture<5>) { - std::vector P_max(5, 40.); - std::vector P_min(5, 0.); - std::vector G(5, 100.); - std::vector H = {0., 10., 20., 30., 40.}; // we have Pmin <= H <= Pmax - std::vector D = {80.0, 60., 40., 20., 0.}; - double init_level = 500.; - double capacity = 1000.; - std::vector inflows(5, 0.); - std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + std::ranges::fill(P_max, 40.); + std::ranges::fill(G, 100.); + H = {0., 10., 20., 30., 40.}; // we have Pmin <= H <= Pmax + D = {80.0, 60., 40., 20., 0.}; + init_level = 500.; + capacity = 1000.; auto [new_H, new_D, _] = new_remix_hydro(G, H, @@ -117,23 +126,20 @@ BOOST_AUTO_TEST_CASE(hydro_increases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 DTG_MRG); std::vector expected_H = {20., 20., 20., 20., 20.}; - // D such as G + H + D remains constant at each hour + // D such as G + H + D remains flat std::vector expected_D = {60., 50., 40., 30., 20.}; BOOST_CHECK(new_H == expected_H); BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) +BOOST_FIXTURE_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh, InputFixture<5>) { - std::vector P_max(5, 50.); - std::vector P_min(5, 0.); - std::vector G(5, 100.); - std::vector H = {0., 10., 20., 30., 40.}; - std::vector D = {80.0, 60., 40., 20., 0.}; - double init_level = 500.; - double capacity = 1000.; - std::vector inflows(5, 0.); - std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + std::ranges::fill(P_max, 50.); + std::ranges::fill(G, 100.); + H = {0., 10., 20., 30., 40.}; + D = {80.0, 60., 40., 20., 0.}; + init_level = 500.; + capacity = 1000.; auto [new_H, new_D, _] = new_remix_hydro(G, H, @@ -155,17 +161,15 @@ BOOST_AUTO_TEST_CASE(Pmax_does_not_impact_results_when_greater_than_40mwh) BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh) +BOOST_FIXTURE_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_20mwh, + InputFixture<5>) { - std::vector P_max(5, 40.); - std::vector P_min(5, 0.); - std::vector G(5, 100.); - std::vector H = {40., 30., 20., 10., 0.}; - std::vector D = {0., 20., 40., 60., 80.}; - double init_level = 500.; - double capacity = 1000.; - std::vector inflows(5, 0.); - std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + std::ranges::fill(P_max, 40.); + std::ranges::fill(G, 100.); + H = {40., 30., 20., 10., 0.}; + D = {0., 20., 40., 60., 80.}; + init_level = 500.; + capacity = 1000.; auto [new_H, new_D, _] = new_remix_hydro(G, H, @@ -187,25 +191,19 @@ BOOST_AUTO_TEST_CASE(hydro_decreases_and_pmax_40mwh___H_is_flattened_to_mean_H_2 BOOST_CHECK(new_D == expected_D); } -BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) +BOOST_FIXTURE_TEST_CASE(influence_of_pmax, InputFixture<5>, *boost::unit_test::tolerance(0.01)) { - std::vector P_min(5, 0.); - // G decreases - std::vector G = {100., 80., 60., 40., 20.}; + G = {100., 80., 60., 40., 20.}; // H is flat and must respect H <= Pmax everywhere - std::vector H = {20., 20., 20., 20., 20.}; - std::vector D = {50., 50., 50., 50., 50.}; - double init_level = 500.; - double capacity = 1000.; - std::vector inflows(5, 0.); - std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + H = {20., 20., 20., 20., 20.}; + D = {50., 50., 50., 50., 50.}; + init_level = 500.; + capacity = 1000.; // 1. Algorithm tends to flatten G + H, so it would require H to increase. // Proof : - std::vector P_max(5., std::numeric_limits::max()); - auto [new_H1, new_D1, L] = new_remix_hydro(G, H, D, @@ -244,23 +242,18 @@ BOOST_AUTO_TEST_CASE(influence_of_pmax, *boost::unit_test::tolerance(0.01)) BOOST_CHECK(new_D2 == expected_D2); } -BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) +BOOST_FIXTURE_TEST_CASE(influence_of_pmin, InputFixture<5>, *boost::unit_test::tolerance(0.01)) { - std::vector P_max(5, std::numeric_limits::max()); - // G decreases - std::vector G = {100., 80., 60., 40., 20.}; + G = {100., 80., 60., 40., 20.}; // H is flat and must respect Pmin <= H <= Pmax everywhere - std::vector H = {20., 20., 20., 20., 20.}; - std::vector D = {50., 50., 50., 50., 50.}; - double init_level = 500.; - double capacity = 1000.; - std::vector inflows(5, 0.); - std::vector ovf(5, 0.), pump(5, 0.), S(5, 0.), DTG_MRG(5, 0.); + H = {20., 20., 20., 20., 20.}; + D = {50., 50., 50., 50., 50.}; + init_level = 500.; + capacity = 1000.; // 1. Algorithm tends to flatten G + H, so it would require H to increase. - std::vector P_min(5, 0.); auto [new_H1, new_D1, L] = new_remix_hydro(G, H, D, @@ -297,17 +290,14 @@ BOOST_AUTO_TEST_CASE(influence_of_pmin, *boost::unit_test::tolerance(0.01)) BOOST_CHECK(new_D2 == expected_D2); } -BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed) +BOOST_FIXTURE_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed, + InputFixture<5>) { - // Not important for testing levels - std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); - std::vector D(5, 10.); - double capacity = 1000.; - - // Used for level computations - double init_level = 500.; - std::vector ovf(5, 25.), H(5, 20.); // Cause levels to lower - std::vector inflows(5, 15.), pump(5, 10.); // Cause levels to raise + init_level = 500.; + std::ranges::fill(ovf, 25.); // Cause levels to decrease + std::ranges::fill(H, 20.); // Cause levels to decrease + std::ranges::fill(inflows, 15.); // Cause levels to increase + std::ranges::fill(pump, 10.); // Cause levels to increase auto [new_H, new_D, levels] = new_remix_hydro(G, H, @@ -326,17 +316,14 @@ BOOST_AUTO_TEST_CASE(H_is_already_flat___remix_is_useless__level_easily_computed BOOST_TEST(levels == expected_levels, boost::test_tools::per_element()); } -BOOST_AUTO_TEST_CASE(input_leads_to_levels_over_capacity___exception_raised) +BOOST_FIXTURE_TEST_CASE(input_leads_to_levels_over_capacity___exception_raised, InputFixture<5>) { - // Not important for testing levels - std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); - std::vector D(5, 10.); - - // Used for level computations - double init_level = 500.; - double capacity = 550.; - std::vector ovf(5, 15.), H(5, 10.); // Cause levels to lower - std::vector inflows(5, 25.), pump(5, 20.); // Cause levels to raise + init_level = 500.; + capacity = 550.; + std::ranges::fill(ovf, 15); // Cause levels to decrease + std::ranges::fill(H, 10); // Cause levels to decrease + std::ranges::fill(inflows, 25); // Cause levels to increase + std::ranges::fill(pump, 20); // Cause levels to increase BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), @@ -345,17 +332,13 @@ BOOST_AUTO_TEST_CASE(input_leads_to_levels_over_capacity___exception_raised) "Remix hydro input : levels computed from input don't respect reservoir bounds")); } -BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) +BOOST_FIXTURE_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised, InputFixture<5>) { - // Not important for testing levels - std::vector P_max(5, 25.), P_min(5, 0.), G(5, 0.), S(5, 0.), DTG_MRG(5, 0.); - std::vector D(5, 10.); - - // Used for level computations - double init_level = 50.; - double capacity = 100.; - std::vector ovf(5, 30.), H(5, 10.); // Cause levels to lower - std::vector inflows(5, 10.), pump(5, 10.); // Cause levels to raise + init_level = 50.; + std::ranges::fill(ovf, 30); // Cause levels to decrease + std::ranges::fill(H, 10); // Cause levels to decrease + std::ranges::fill(inflows, 5); // Cause levels to increase + std::ranges::fill(pump, 10); // Cause levels to increase BOOST_CHECK_EXCEPTION( new_remix_hydro(G, H, D, P_max, P_min, init_level, capacity, inflows, ovf, pump, S, DTG_MRG), @@ -364,25 +347,21 @@ BOOST_AUTO_TEST_CASE(input_leads_to_levels_less_than_zero___exception_raised) "Remix hydro input : levels computed from input don't respect reservoir bounds")); } -BOOST_AUTO_TEST_CASE(influence_of_capacity_on_algorithm___case_where_no_influence, - *boost::unit_test::tolerance(0.001)) +BOOST_FIXTURE_TEST_CASE(influence_of_capacity_on_algorithm___case_where_no_influence, + InputFixture<10>, + *boost::unit_test::tolerance(0.001)) { - // Not important for this test - std::vector P_max(10, std::numeric_limits::max()); - std::vector P_min(10, 0.); - std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); - std::vector D(10, 20.); + std::ranges::fill(D, 20); // Not important for this test // H oscillates between 10 and 20 (new H will be flattened to 15 everywhere) - std::vector H = {10., 20., 10., 20., 10., 20., 10., 20., 10., 20.}; + H = {10., 20., 10., 20., 10., 20., 10., 20., 10., 20.}; // First inflows > H, then inflows < H. Consequence : levels first increase, then decrease. - std::vector inflows = {25., 25., 25., 25., 25., 5., 5., 5., 5., 5.}; - double init_level = 100.; + inflows = {25., 25., 25., 25., 25., 5., 5., 5., 5., 5.}; + init_level = 100.; // H and inflows result in : input_levels = {115, 120, 135, 140, 155, 140, 135, 120, 115, 100} // Note sup(input_levels) = 155 - // Case 1 : capacity unlimited (relaxed) ==> leads to optimal solution (H is flat) - double capacity = std::numeric_limits::max(); + // Case 1 : capacity relaxed (infinite by default) ==> leads to optimal solution (H is flat) auto [new_H, new_D, L] = new_remix_hydro(G, H, D, @@ -423,26 +402,22 @@ BOOST_AUTO_TEST_CASE(influence_of_capacity_on_algorithm___case_where_no_influenc BOOST_TEST(L2 == expected_L, boost::test_tools::per_element()); } -BOOST_AUTO_TEST_CASE(lowering_capacity_too_low_leads_to_suboptimal_solution_for_GplusH, - *boost::unit_test::tolerance(0.001)) +BOOST_FIXTURE_TEST_CASE(lowering_capacity_too_low_leads_to_suboptimal_solution_for_GplusH, + InputFixture<10>, + *boost::unit_test::tolerance(0.001)) { - // Not important for this test - std::vector P_max(10, std::numeric_limits::max()); - std::vector P_min(10, 0.); - std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); - std::vector D(10, 20.); + std::ranges::fill(D, 20); // Not important for this test // H oscillates between 10 and 20 (new H will be flattened to 15 everywhere) - std::vector H = {20., 10., 20., 10., 20., 10., 20., 10., 20., 10.}; + H = {20., 10., 20., 10., 20., 10., 20., 10., 20., 10.}; // First inflows > H, then inflows < H. Consequence : levels first increase, then decrease. - std::vector inflows = {25., 25., 25., 25., 25., 5., 5., 5., 5., 5.}; - double init_level = 100.; + inflows = {25., 25., 25., 25., 25., 5., 5., 5., 5., 5.}; + init_level = 100.; // H and inflows lead to have : // input_levels = {105, 120, 125, 140, 145, 140, 125, 120, 105,100} // Note sup(input_levels) = 145 - // Case 1 : capacity unlimited (relaxed) ==> leads to optimal solution (H is flat) - double capacity = std::numeric_limits::max(); + // Case 1 : capacity relaxed (infinite by default) ==> leads to optimal solution (H is flat) auto [new_H, new_D, L] = new_remix_hydro(G, H, D, @@ -487,21 +462,18 @@ BOOST_AUTO_TEST_CASE(lowering_capacity_too_low_leads_to_suboptimal_solution_for_ BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); } -BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution_for_GplusH, - *boost::unit_test::tolerance(0.001)) +BOOST_FIXTURE_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution_for_GplusH, + InputFixture<10>, + *boost::unit_test::tolerance(0.001)) { - // Not important for this test - std::vector P_max(10, std::numeric_limits::max()); - std::vector P_min(10, 0.); - std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); - std::vector D(10, 20.); + std::ranges::fill(D, 20); // Not important for this test // H oscillates between 20 and 30 (new H will be flattened to 25 everywhere) - std::vector H = {20., 30., 20., 30., 20., 30., 20., 30., 20., 30.}; + H = {20., 30., 20., 30., 20., 30., 20., 30., 20., 30.}; // First inflows < H, then inflows > H. Consequence : levels first decrease, then increase. - std::vector inflows = {5., 5., 5., 5., 5., 45., 45., 45., 45., 45.}; - double capacity = std::numeric_limits::max(); - double init_level = 100.; + inflows = {5., 5., 5., 5., 5., 45., 45., 45., 45., 45.}; + capacity = std::numeric_limits::max(); + init_level = 100.; // H and inflows result in : input_levels = {85, 60, 45, 20, 5, 20, 45, 60, 85, 100} // Note : inf(input_levels) = 5 @@ -550,21 +522,17 @@ BOOST_AUTO_TEST_CASE(lowering_initial_level_too_low_leads_to_suboptimal_solution BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); } -BOOST_AUTO_TEST_CASE(influence_of_initial_level_on_algorithm___case_where_no_influence, - *boost::unit_test::tolerance(0.001)) +BOOST_FIXTURE_TEST_CASE(influence_of_initial_level_on_algorithm___case_where_no_influence, + InputFixture<10>, + *boost::unit_test::tolerance(0.001)) { - // Not important for this test - std::vector P_max(10, std::numeric_limits::max()); - std::vector P_min(10, 0.); - std::vector G(10, 0.), pump(10, 0.), ovf(10, 0.), S(10, 0.), DTG_MRG(10, 0.); - std::vector D(10, 20.); + std::ranges::fill(D, 20); // Not important for this test // H oscillates between 10 and 20 (new H will be flattened to 15 everywhere) - std::vector H = {20., 10., 20., 10., 20., 10., 20., 10., 20., 10.}; + H = {20., 10., 20., 10., 20., 10., 20., 10., 20., 10.}; // First inflows < H, then inflows > H. Consequence : levels first decrease, then increase. - std::vector inflows = {5., 5., 5., 5., 5., 25., 25., 25., 25., 25.}; - double capacity = std::numeric_limits::max(); - double init_level = 100.; + inflows = {5., 5., 5., 5., 5., 25., 25., 25., 25., 25.}; + init_level = 100.; // H and inflows are such as inf(input_levels) = 45 // Case 1 : init level (== 100) is high enough so that input levels (computed from input data) From da27cb9497937359d07ea1c6a1de45caaff0408f Mon Sep 17 00:00:00 2001 From: Guillaume PIERRE Date: Fri, 27 Dec 2024 17:10:16 +0100 Subject: [PATCH 27/27] New hydro remix : adding test on invariance where S + DTG_MRG > 0 --- .../solver/simulation/test-hydro-remix.cpp | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/tests/src/solver/simulation/test-hydro-remix.cpp b/src/tests/src/solver/simulation/test-hydro-remix.cpp index c8568ebbf5..5b60bc53a3 100644 --- a/src/tests/src/solver/simulation/test-hydro-remix.cpp +++ b/src/tests/src/solver/simulation/test-hydro-remix.cpp @@ -580,6 +580,64 @@ BOOST_FIXTURE_TEST_CASE(influence_of_initial_level_on_algorithm___case_where_no_ BOOST_TEST(new_H2 == expected_H2, boost::test_tools::per_element()); } +BOOST_FIXTURE_TEST_CASE(spillage_positive_at_hour_0___no_change_at_this_hour, InputFixture<5>) +{ + std::ranges::fill(G, 100.); + H = {40., 30., 20., 10., 0.}; + D = {0., 20., 40., 60., 80.}; + init_level = 500.; + capacity = 1000.; + // At this stage, DTG_MRG is filled with zeros. Running the algorithm would flatten + // H to 20 everywhere : H = {20, 20, 20, 20, 20} + // But : + S[0] = 1.; + // Now, we expect no change for H at hour 0 + auto [new_H, __, _] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + std::vector expected_H = {40., 15., 15., 15., 15.}; + BOOST_CHECK(new_H == expected_H); +} + +BOOST_FIXTURE_TEST_CASE(DTG_MRG_positive_on_hour_4___no_change_at_this_hour, InputFixture<5>) +{ + std::ranges::fill(G, 100.); + H = {40., 30., 20., 10., 0.}; + D = {0., 20., 40., 60., 80.}; + init_level = 500.; + capacity = 1000.; + // At this stage, DTG_MRG is filled with zeros. Running the algorithm would flatten + // H to 20 everywhere : H = {20, 20, 20, 20, 20} + // But : + DTG_MRG[4] = 1.; + // Now, we expect no change for H at hour 4 + auto [new_H, new_D, L] = new_remix_hydro(G, + H, + D, + P_max, + P_min, + init_level, + capacity, + inflows, + ovf, + pump, + S, + DTG_MRG); + + std::vector expected_H = {25., 25., 25., 25., 0.}; + BOOST_CHECK(new_H == expected_H); +} + // Ideas for building further tests : // ================================ // - Remix hydro algorithm seems symmetrical (if we have input vectors and corresponding output