diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 2c73097adb..daceabc0ff 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -170,6 +170,7 @@ jobs: run: | cd _build ctest -C Release --output-on-failure -L "unit|end-to-end" + - name: Upload logs for failed tests if: ${{ failure() }} @@ -177,7 +178,7 @@ jobs: with: name: test-log path: ${{ github.workspace }}/_build/Testing/Temporary/LastTest.log - + - name: Run tests about infinity on BCs RHS if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} uses: ./.github/workflows/run-tests @@ -185,7 +186,7 @@ jobs: simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v830 os: ${{ env.os }} - + - name: Run MILP with CBC if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} uses: ./.github/workflows/run-tests @@ -195,6 +196,23 @@ jobs: variant: "milp-cbc" os: ${{ env.os }} + - name: Run tests on adequacy patch (CSR) + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{steps.simtest-version.outputs.prop}} + batch-name: adequacy-patch-CSR + os: ${{ env.os }} + + - name: Run parallel tests + if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{steps.simtest-version.outputs.prop}} + batch-name: valid-parallel + os: ${{ env.os }} + variant: "parallel" + - name: Run tests introduced in 8.6.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} uses: ./.github/workflows/run-tests @@ -248,14 +266,6 @@ jobs: batch-name: valid-mps os: ${{ env.os }} - - name: Run tests for adequacy patch (CSR) - if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} - uses: ./.github/workflows/run-tests - with: - simtest-tag: ${{ env.SIMTEST }} - batch-name: adequacy-patch-CSR - os: ${{ env.os }} - - name: Run parallel tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' && !cancelled() }} uses: ./.github/workflows/run-tests diff --git a/.github/workflows/windows-vcpkg.yml b/.github/workflows/windows-vcpkg.yml index 5fb4b19603..37d47eaadf 100644 --- a/.github/workflows/windows-vcpkg.yml +++ b/.github/workflows/windows-vcpkg.yml @@ -188,6 +188,14 @@ jobs: batch-name: adequacy-patch-CSR os: ${{ env.os }} + - name: Run tests about infinity on BCs RHS + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{steps.simtest-version.outputs.prop}} + batch-name: valid-v830 + os: ${{ env.test-platform }} + - name: Run tests about infinity on BCs RHS if: ${{ env.RUN_SIMPLE_TESTS == 'true' && !cancelled() }} uses: ./.github/workflows/run-tests diff --git a/sonar-project.properties b/sonar-project.properties index 0c74bd252a..d4ba8cce37 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,3 +1,24 @@ +# +# Copyright 2007-2024, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Antares-Simulator, +# Adequacy and Performance assessment for interconnected energy networks. +# +# Antares_Simulator is free software: you can redistribute it and/or modify +# it under the terms of the Mozilla Public Licence 2.0 as published by +# the Mozilla Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Antares_Simulator is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Mozilla Public Licence 2.0 for more details. +# +# You should have received a copy of the Mozilla Public Licence 2.0 +# along with Antares_Simulator. If not, see . +# + sonar.projectName=Antares_Simulator sonar.projectKey=AntaresSimulatorTeam_Antares_Simulator sonar.organization=antaressimulatorteam diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56dc5e2bf8..32beee7a3a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,6 @@ set(ANTARES_VERSION_HI 9) set(ANTARES_VERSION_LO 2) set(ANTARES_VERSION_REVISION 0) - # Beta release set(ANTARES_BETA 0) set(ANTARES_RC 7) @@ -192,6 +191,7 @@ endif() find_package(Boost REQUIRED) find_package(antlr4-runtime REQUIRED) +find_package(yaml-cpp REQUIRED) #Sirius solver if(POLICY CMP0074) diff --git a/src/libs/antares/exception/LoadingError.cpp b/src/libs/antares/exception/LoadingError.cpp index cea447153e..32f4b3b1c1 100644 --- a/src/libs/antares/exception/LoadingError.cpp +++ b/src/libs/antares/exception/LoadingError.cpp @@ -96,9 +96,8 @@ InvalidSolverSpecificParameters::InvalidSolverSpecificParameters(const std::stri { } -InvalidStudy::InvalidStudy(const Yuni::String& study): - LoadingError(std::string("The folder `") + study.c_str() - + "` does not seem to be a valid study") +InvalidStudy::InvalidStudy(const std::string& study): + LoadingError(std::string("The folder `") + study + "` does not seem to be a valid study") { } diff --git a/src/libs/antares/exception/include/antares/exception/LoadingError.hpp b/src/libs/antares/exception/include/antares/exception/LoadingError.hpp index d70820e5e9..9a5ff14002 100644 --- a/src/libs/antares/exception/include/antares/exception/LoadingError.hpp +++ b/src/libs/antares/exception/include/antares/exception/LoadingError.hpp @@ -140,7 +140,7 @@ class InvalidSolverSpecificParameters: public LoadingError class InvalidStudy: public LoadingError { public: - explicit InvalidStudy(const Yuni::String& study); + explicit InvalidStudy(const std::string& study); }; class NoStudyProvided: public LoadingError diff --git a/src/libs/antares/io/CMakeLists.txt b/src/libs/antares/io/CMakeLists.txt index f2f1eae17d..dc62a95aea 100644 --- a/src/libs/antares/io/CMakeLists.txt +++ b/src/libs/antares/io/CMakeLists.txt @@ -14,7 +14,6 @@ add_library(io ) add_library(Antares::io ALIAS io) - target_link_libraries(io PRIVATE yuni-static-core diff --git a/src/libs/antares/io/include/antares/io/file.h b/src/libs/antares/io/include/antares/io/file.h index b401b1cece..a9aed8c850 100644 --- a/src/libs/antares/io/include/antares/io/file.h +++ b/src/libs/antares/io/include/antares/io/file.h @@ -22,8 +22,7 @@ #define __LIBS_ANTARES_IO_FILE_H__ #include - -#include +#include namespace Antares::IO { diff --git a/src/libs/antares/study/CMakeLists.txt b/src/libs/antares/study/CMakeLists.txt index 29a89fc631..e7336d020c 100644 --- a/src/libs/antares/study/CMakeLists.txt +++ b/src/libs/antares/study/CMakeLists.txt @@ -74,8 +74,8 @@ set(SRC_STUDY_PART_THERMAL include/antares/study/parts/thermal/cost_provider.h include/antares/study/parts/thermal/cluster.hxx parts/thermal/cluster.cpp - parts/thermal/scenarized_cost_provider.cpp - parts/thermal/constant_cost_provider.cpp + parts/thermal/scenarized_cost_provider.cpp + parts/thermal/constant_cost_provider.cpp include/antares/study/parts/thermal/cluster_list.h parts/thermal/cluster_list.cpp include/antares/study/parts/thermal/pollutant.h @@ -102,7 +102,9 @@ set(SRC_STUDY_PART_SHORT_TERM_STORAGE parts/short-term-storage/series.cpp include/antares/study/parts/short-term-storage/series.h include/antares/study/parts/short-term-storage/cluster.h + include/antares/study/parts/short-term-storage/AdditionalConstraint.h parts/short-term-storage/cluster.cpp + parts/short-term-storage/AdditionalConstraint.cpp ) source_group("study\\part\\short-term-storage" FILES ${SRC_STUDY_PART_SHORT_TERM_SOTRAGE}) @@ -306,12 +308,12 @@ target_link_libraries(study ) target_include_directories(study - PUBLIC + PUBLIC $ # Make more than just study visible but it's the lesser evil for now ) -install(DIRECTORY include/antares +install(DIRECTORY include/antares DESTINATION "include" ) diff --git a/src/libs/antares/study/area/list.cpp b/src/libs/antares/study/area/list.cpp index cf2624515f..7af3a6a5a7 100644 --- a/src/libs/antares/study/area/list.cpp +++ b/src/libs/antares/study/area/list.cpp @@ -1199,6 +1199,7 @@ bool AreaList::loadFromFolder(const StudyLoadOptions& options) fs::path folder = stsFolder / "clusters" / area->id.c_str(); ret = area->shortTermStorage.createSTStorageClustersFromIniFile(folder) && ret; + ret = area->shortTermStorage.LoadConstraintsFromIniFile(folder) && ret; } } else diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/AdditionalConstraint.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/AdditionalConstraint.h new file mode 100644 index 0000000000..e16b991a05 --- /dev/null +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/AdditionalConstraint.h @@ -0,0 +1,53 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Antares_Simulator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once +#include +#include + +namespace Antares::Data::ShortTermStorage +{ + +struct AdditionalConstraint +{ + std::string name; + std::string cluster_id; + std::string variable; + std::string operatorType; + std::set hours; + double rhs; + + unsigned int globalIndex = 0; + + struct ValidateResult + { + bool ok; + std::string error_msg; + }; + + ValidateResult validate() const; + +private: + bool isValidVariable() const; + bool isValidOperatorType() const; + bool isValidHoursRange() const; +}; +} // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h index b4074a28c8..df74a350b0 100644 --- a/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/cluster.h @@ -26,6 +26,7 @@ #include +#include "AdditionalConstraint.h" #include "properties.h" #include "series.h" @@ -35,17 +36,21 @@ class STStorageCluster { public: bool enabled() const; + bool validate() const; bool loadFromSection(const IniFile::Section& section); + bool loadSeries(const std::filesystem::path& folder) const; void saveProperties(IniFile& ini) const; + bool saveSeries(const std::string& path) const; std::string id; std::shared_ptr series = std::make_shared(); mutable Properties properties; + std::vector additional_constraints; }; } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h index 90f8fafbce..d4e0233b5c 100644 --- a/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/container.h @@ -21,9 +21,9 @@ #pragma once #include -#include #include +#include "AdditionalConstraint.h" #include "cluster.h" namespace Antares::Data::ShortTermStorage @@ -32,18 +32,28 @@ class STStorageInput { public: bool validate() const; + /// 1. Read list.ini bool createSTStorageClustersFromIniFile(const std::filesystem::path& path); + /// 2. Read ALL series bool loadSeriesFromFolder(const std::filesystem::path& folder) const; + /// Number of enabled ST storages, ignoring disabled ST storages std::size_t count() const; + + bool LoadConstraintsFromIniFile(const std::filesystem::path& filePath); + /// erase disabled cluster from the vector uint removeDisabledClusters(); bool saveToFolder(const std::string& folder) const; + bool saveDataSeriesToFolder(const std::string& folder) const; std::vector storagesByIndex; + + /// Number cumulative - constraint + std::size_t cumulativeConstraintCount() const; }; } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/include/antares/study/runtime/runtime.h b/src/libs/antares/study/include/antares/study/runtime/runtime.h index 858719ab27..fe394199df 100644 --- a/src/libs/antares/study/include/antares/study/runtime/runtime.h +++ b/src/libs/antares/study/include/antares/study/runtime/runtime.h @@ -110,6 +110,7 @@ class StudyRuntimeInfos uint thermalPlantTotalCountMustRun; uint shortTermStorageCount = 0; + uint shortTermStorageCumulativeConstraintCount = 0; //! Override enable/disable TS generation per cluster bool thermalTSRefresh = false; diff --git a/src/libs/antares/study/parameters/adq-patch-params.cpp b/src/libs/antares/study/parameters/adq-patch-params.cpp index ebf6de321e..800f2d19f8 100644 --- a/src/libs/antares/study/parameters/adq-patch-params.cpp +++ b/src/libs/antares/study/parameters/adq-patch-params.cpp @@ -146,6 +146,10 @@ void AdqPatchParams::addExcludedVariables(std::vector& out) const out.emplace_back("LMR VIOL."); out.emplace_back("UNSP. ENRG CSR"); out.emplace_back("DTG MRG CSR"); + out.emplace_back("LOLD CSR"); + out.emplace_back("LOLP CSR"); + out.emplace_back("MAX MRG CSR"); + out.emplace_back("OV. COST CSR"); } } diff --git a/src/libs/antares/study/parts/short-term-storage/AdditionalConstraint.cpp b/src/libs/antares/study/parts/short-term-storage/AdditionalConstraint.cpp new file mode 100644 index 0000000000..2ca904041c --- /dev/null +++ b/src/libs/antares/study/parts/short-term-storage/AdditionalConstraint.cpp @@ -0,0 +1,65 @@ +/* +** Copyright 2007-2024, RTE (https://www.rte-france.com) +** See AUTHORS.txt +** SPDX-License-Identifier: MPL-2.0 +** This file is part of Antares-Simulator, +** Adequacy and Performance assessment for interconnected energy networks. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the Mozilla Public Licence 2.0 as published by +** the Mozilla Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** Mozilla Public Licence 2.0 for more details. +** +** You should have received a copy of the Mozilla Public Licence 2.0 +** along with Antares_Simulator. If not, see . +*/ +#include "antares/study/parts/short-term-storage/AdditionalConstraint.h" + +namespace Antares::Data::ShortTermStorage +{ +AdditionalConstraint::ValidateResult AdditionalConstraint::validate() const +{ + if (cluster_id.empty()) + { + return {false, "Cluster ID is empty."}; + } + + if (!isValidVariable()) + { + return {false, "Invalid variable type. Must be 'injection', 'withdrawal', or 'netting'."}; + } + + if (!isValidOperatorType()) + { + return {false, "Invalid operator type. Must be 'less', 'equal', or 'greater'."}; + } + + if (!isValidHoursRange()) + { + return {false, "Hours set contains invalid values. Must be between 1 and 168."}; + } + + return {true, ""}; +} + +bool AdditionalConstraint::isValidHoursRange() const +{ + // `hours` is a sorted set; begin() gives the smallest and prev(end()) gives the largest. + return !hours.empty() && *hours.begin() >= 1 && *std::prev(hours.end()) <= 168; +} + +bool AdditionalConstraint::isValidVariable() const +{ + return variable == "injection" || variable == "withdrawal" || variable == "netting"; +} + +bool AdditionalConstraint::isValidOperatorType() const +{ + return operatorType == "less" || operatorType == "equal" || operatorType == "greater"; +} +} // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/parts/short-term-storage/cluster.cpp b/src/libs/antares/study/parts/short-term-storage/cluster.cpp index 787d1c85e6..cbfb7e679f 100644 --- a/src/libs/antares/study/parts/short-term-storage/cluster.cpp +++ b/src/libs/antares/study/parts/short-term-storage/cluster.cpp @@ -28,7 +28,6 @@ namespace Antares::Data::ShortTermStorage { - bool STStorageCluster::loadFromSection(const IniFile::Section& section) { if (!section.firstProperty) @@ -92,5 +91,4 @@ bool STStorageCluster::saveSeries(const std::string& path) const { return series->saveToFolder(path); } - } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/parts/short-term-storage/container.cpp b/src/libs/antares/study/parts/short-term-storage/container.cpp index bee1546ec4..39a958c5c7 100644 --- a/src/libs/antares/study/parts/short-term-storage/container.cpp +++ b/src/libs/antares/study/parts/short-term-storage/container.cpp @@ -22,11 +22,13 @@ #include "antares/study/parts/short-term-storage/container.h" #include +#include #include #include #include +#include #define SEP Yuni::IO::Separator @@ -73,6 +75,83 @@ bool STStorageInput::createSTStorageClustersFromIniFile(const fs::path& path) return true; } +bool STStorageInput::LoadConstraintsFromIniFile(const fs::path& parent_path) +{ + IniFile ini; + const auto pathIni = parent_path / "additional-constraints.ini"; + if (!ini.open(pathIni, false)) + { + logs.info() << "There is no: " << pathIni; + return true; + } + + for (auto* section = ini.firstSection; section; section = section->next) + { + AdditionalConstraint constraint; + constraint.name = section->name.c_str(); + for (auto* property = section->firstProperty; property; property = property->next) + { + const std::string key = property->key; + const auto value = property->value; + + if (key == "cluster") + { + // TODO do i have to transform the name to id? TransformNameIntoID + std::string clusterName; + value.to(clusterName); + constraint.cluster_id = transformNameIntoID(clusterName); + } + else if (key == "variable") + { + value.to(constraint.variable); + } + else if (key == "operator") + { + value.to(constraint.operatorType); + } + else if (key == "hours") + { + std::stringstream ss(value.c_str()); + std::string hour; + while (std::getline(ss, hour, ',')) + { + int hourVal = std::stoi(hour); + constraint.hours.insert(hourVal); + } + } + else if (key == "rhs") + { + property->value.to(constraint.rhs); + } + } + + if (auto ret = constraint.validate(); !ret.ok) + { + logs.error() << "Invalid constraint in section: " << section->name; + logs.error() << ret.error_msg; + return false; + } + + auto it = std::find_if(storagesByIndex.begin(), + storagesByIndex.end(), + [&constraint](const STStorageCluster& cluster) + { return cluster.id == constraint.cluster_id; }); + if (it == storagesByIndex.end()) + { + logs.warning() << " from file " << pathIni; + logs.warning() << "Constraint " << section->name + << " does not reference an existing cluster"; + return false; + } + else + { + it->additional_constraints.push_back(constraint); + } + } + + return true; +} + bool STStorageInput::loadSeriesFromFolder(const fs::path& folder) const { if (folder.empty()) @@ -113,6 +192,15 @@ bool STStorageInput::saveDataSeriesToFolder(const std::string& folder) const { return storage.saveSeries(folder + SEP + storage.id); }); } +std::size_t STStorageInput::cumulativeConstraintCount() const +{ + return std::accumulate(storagesByIndex.begin(), + storagesByIndex.end(), + 0, + [](int acc, const auto& cluster) + { return acc + cluster.additional_constraints.size(); }); +} + std::size_t STStorageInput::count() const { return std::ranges::count_if(storagesByIndex, @@ -123,5 +211,4 @@ uint STStorageInput::removeDisabledClusters() { return std::erase_if(storagesByIndex, [](const auto& c) { return !c.enabled(); }); } - } // namespace Antares::Data::ShortTermStorage diff --git a/src/libs/antares/study/parts/thermal/cluster.cpp b/src/libs/antares/study/parts/thermal/cluster.cpp index 96b2d87826..cfd16f521e 100644 --- a/src/libs/antares/study/parts/thermal/cluster.cpp +++ b/src/libs/antares/study/parts/thermal/cluster.cpp @@ -298,6 +298,11 @@ void Data::ThermalCluster::calculationOfSpinning() void Data::ThermalCluster::reverseCalculationOfSpinning() { + if (tsGenBehavior == LocalTSGenerationBehavior::forceNoGen) + { + return; + } + // Nothing to do if the spinning is equal to zero // because it will the same multiply all entries of the matrix by 1. if (Utils::isZero(spinning)) diff --git a/src/libs/antares/study/runtime/runtime.cpp b/src/libs/antares/study/runtime/runtime.cpp index 3bca4404a5..32bfb0f28e 100644 --- a/src/libs/antares/study/runtime/runtime.cpp +++ b/src/libs/antares/study/runtime/runtime.cpp @@ -94,6 +94,8 @@ static void StudyRuntimeInfosInitializeAllAreas(Study& study, StudyRuntimeInfos& r.thermalPlantTotalCountMustRun += area.thermal.list.enabledAndMustRunCount(); r.shortTermStorageCount += area.shortTermStorage.count(); + r.shortTermStorageCumulativeConstraintCount += area.shortTermStorage + .cumulativeConstraintCount(); } } @@ -363,6 +365,8 @@ bool StudyRuntimeInfos::loadFromStudy(Study& study) logs.info() << " thermal clusters: " << thermalPlantTotalCount; logs.info() << " thermal clusters (must-run): " << thermalPlantTotalCountMustRun; logs.info() << " short-term storages: " << shortTermStorageCount; + logs.info() << " short-term storage cumulative constraints count: " + << shortTermStorageCumulativeConstraintCount; logs.info() << " binding constraints: " << study.bindingConstraints.activeConstraints().size(); logs.info() << " geographic trimming:" << (gd.geographicTrimming ? "true" : "false"); diff --git a/src/solver/misc/options.cpp b/src/solver/misc/options.cpp index d72bd4c692..00572039a9 100644 --- a/src/solver/misc/options.cpp +++ b/src/solver/misc/options.cpp @@ -82,14 +82,14 @@ std::unique_ptr CreateParser(Settings& settings, StudyLoad "Solver used for simulation\nAvailable solver list : " + availableOrToolsSolversString()); - //--xpress-parameters + //--solver-parameters parser->add( options.optOptions.solverParameters, ' ', "solver-parameters", - "Set xpress solver specific parameters. The specified string must be wrapped into quotes: " - "--solver-parameters=\"param1 value1 param2 value2\". The syntax of parameters is solver " - "specfic, examples are given in Antares-Simulator online documentation."); + "Set solver-specific parameters, for instance --solver-parameters=\"THREADS 1 PRESOLVE 1\"" + "for XPRESS or --solver-parameters=\"parallel/maxnthreads 1, lp/presolving TRUE\" for SCIP." + "Syntax is solver-dependent, and only supported for SCIP & XPRESS."); parser->addParagraph("\nParameters"); // --name diff --git a/src/solver/modelParser/CMakeLists.txt b/src/solver/modelParser/CMakeLists.txt index 1881ba78fa..8e5ac13726 100644 --- a/src/solver/modelParser/CMakeLists.txt +++ b/src/solver/modelParser/CMakeLists.txt @@ -1,5 +1,3 @@ -find_package(yaml-cpp REQUIRED) - set(SOURCES parser.cpp encoders.hxx @@ -24,4 +22,4 @@ target_link_libraries(modelParser install(DIRECTORY include/antares DESTINATION "include" -) \ No newline at end of file +) diff --git a/src/solver/modeler/CMakeLists.txt b/src/solver/modeler/CMakeLists.txt index 2ad5b9a49d..334e4c6c60 100644 --- a/src/solver/modeler/CMakeLists.txt +++ b/src/solver/modeler/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(api) add_subdirectory(ortoolsImpl) add_subdirectory(loadFiles) +add_subdirectory(parameters) diff --git a/src/solver/modeler/parameters/CMakeLists.txt b/src/solver/modeler/parameters/CMakeLists.txt new file mode 100644 index 0000000000..33cfda091e --- /dev/null +++ b/src/solver/modeler/parameters/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(modeler-parameters + include/antares/solver/modeler/parameters/modelerParameters.h + include/antares/solver/modeler/parameters/parseModelerParameters.h + parseModelerParameters.cpp + encoder.hxx) + +target_link_libraries(modeler-parameters + PRIVATE + yaml-cpp + Antares::io) + +target_include_directories(modeler-parameters + PUBLIC + $) diff --git a/src/solver/modeler/parameters/encoder.hxx b/src/solver/modeler/parameters/encoder.hxx new file mode 100644 index 0000000000..32de3e0d83 --- /dev/null +++ b/src/solver/modeler/parameters/encoder.hxx @@ -0,0 +1,23 @@ +#include + +#include "yaml-cpp/yaml.h" + +namespace YAML +{ +template<> +struct convert +{ + static bool decode(const Node& node, Antares::Solver::ModelerParameters& rhs) + { + if (!node.IsMap()) + { + return false; + } + rhs.solver = node["solver"].as(); + rhs.solverLogs = node["solver-logs"].as(false); + rhs.solverParameters = node["solver-parameters"].as(); + rhs.noOutput = node["no-output"].as(false); + return true; + } +}; +} // namespace YAML diff --git a/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/modelerParameters.h b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/modelerParameters.h new file mode 100644 index 0000000000..c6977a748b --- /dev/null +++ b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/modelerParameters.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace Antares::Solver +{ +struct ModelerParameters +{ + // OR-Tools solver to be used for the simulation + std::string solver; + // Display solver logs ON/OFF + bool solverLogs = false; + // Specific solver parameters + std::string solverParameters; + // Write output results + bool noOutput = false; +}; +} // namespace Antares::Solver diff --git a/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/parseModelerParameters.h b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/parseModelerParameters.h new file mode 100644 index 0000000000..2409fcddf1 --- /dev/null +++ b/src/solver/modeler/parameters/include/antares/solver/modeler/parameters/parseModelerParameters.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +namespace Antares::Solver +{ + +ModelerParameters parseModelerParameters(const std::filesystem::path& path); +} // namespace Antares::Solver diff --git a/src/solver/modeler/parameters/parseModelerParameters.cpp b/src/solver/modeler/parameters/parseModelerParameters.cpp new file mode 100644 index 0000000000..39935db38c --- /dev/null +++ b/src/solver/modeler/parameters/parseModelerParameters.cpp @@ -0,0 +1,16 @@ +#include +#include + +#include + +#include "encoder.hxx" + +namespace Antares::Solver +{ +ModelerParameters parseModelerParameters(const std::filesystem::path& path) +{ + const auto contents = Antares::IO::readFile(path); + YAML::Node root = YAML::Load(contents); + return root.as(); +} +} // namespace Antares::Solver diff --git a/src/solver/optimisation/CMakeLists.txt b/src/solver/optimisation/CMakeLists.txt index 57194b388a..0d64d42e44 100644 --- a/src/solver/optimisation/CMakeLists.txt +++ b/src/solver/optimisation/CMakeLists.txt @@ -47,9 +47,7 @@ set(RTESOLVER_OPT post_process_commands.cpp include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h include/antares/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.h - include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h adequacy_patch_csr/adq_patch_post_process_list.cpp - adequacy_patch_csr/post_processing.cpp include/antares/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.h adequacy_patch_csr/adq_patch_curtailment_sharing.cpp adequacy_patch_csr/solve_problem.cpp @@ -82,7 +80,9 @@ set(RTESOLVER_OPT include/antares/solver/optimisation/constraints/ShortTermStorageLevel.h constraints/ShortTermStorageLevel.cpp include/antares/solver/optimisation/constraints/ShortTermStorageCostVariation.h + include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h constraints/ShortTermStorageCostVariation.cpp + constraints/ShortTermStorageCumulation.cpp constraints/ShortTermStorageCostVariationInjectionForward.cpp constraints/ShortTermStorageCostVariationInjectionBackward.cpp constraints/ShortTermStorageCostVariationWithdrawalForward.cpp diff --git a/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp b/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp index b480108379..731aaac79c 100644 --- a/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp +++ b/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp @@ -25,9 +25,10 @@ #include "antares/solver/optimisation/adequacy_patch_csr/count_constraints_variables.h" #include "antares/solver/optimisation/adequacy_patch_csr/csr_quadratic_problem.h" -#include "antares/solver/optimisation/opt_fonctions.h" #include "antares/solver/simulation/adequacy_patch_runtime_data.h" +#include "solve_problem.h" + using namespace Yuni; namespace Antares::Data::AdequacyPatch diff --git a/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp b/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp index 1d54cb0da0..f9f8aa389b 100644 --- a/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp +++ b/src/solver/optimisation/adequacy_patch_csr/adq_patch_post_process_list.cpp @@ -36,7 +36,7 @@ AdqPatchPostProcessList::AdqPatchPostProcessList(const AdqPatchParams& adqPatchP { post_process_list.push_back( std::make_unique(problemeHebdo_, numSpace_, areas)); - + // Here a post process particular to adq patch post_process_list.push_back( std::make_unique(problemeHebdo_, areas, false, false)); post_process_list.push_back(std::make_unique(problemeHebdo_, @@ -44,15 +44,14 @@ AdqPatchPostProcessList::AdqPatchPostProcessList(const AdqPatchParams& adqPatchP sheddingPolicy, splxOptimization, numSpace)); - - // Here a post process particular to adq patch post_process_list.push_back(std::make_unique(adqPatchParams, problemeHebdo_, areas, numSpace_)); - // Here a post process particular to adq patch post_process_list.push_back( - std::make_unique(problemeHebdo_, areas, numSpace)); + std::make_unique(problemeHebdo_, areas, numSpace)); + post_process_list.push_back( + std::make_unique(problemeHebdo_, areas, numSpace)); post_process_list.push_back( std::make_unique(problemeHebdo_, areas, true, false)); post_process_list.push_back( diff --git a/src/solver/optimisation/adequacy_patch_csr/post_processing.cpp b/src/solver/optimisation/adequacy_patch_csr/post_processing.cpp deleted file mode 100644 index bc946a3c57..0000000000 --- a/src/solver/optimisation/adequacy_patch_csr/post_processing.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* -** Copyright 2007-2024, RTE (https://www.rte-france.com) -** See AUTHORS.txt -** SPDX-License-Identifier: MPL-2.0 -** This file is part of Antares-Simulator, -** Adequacy and Performance assessment for interconnected energy networks. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the Mozilla Public Licence 2.0 as published by -** the Mozilla Foundation, either version 2 of the License, or -** (at your option) any later version. -** -** Antares_Simulator is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** Mozilla Public Licence 2.0 for more details. -** -** You should have received a copy of the Mozilla Public Licence 2.0 -** along with Antares_Simulator. If not, see . -*/ -#include "antares/solver/optimisation/adequacy_patch_csr/post_processing.h" - -#include - -namespace Antares::Data::AdequacyPatch -{ -double recomputeDTG_MRG(bool triggered, double dtgMrg, double ens) -{ - if (triggered) - { - return std::max(0.0, dtgMrg - ens); - } - else - { - return dtgMrg; - } -} - -double recomputeENS_MRG(bool triggered, double dtgMrg, double ens) -{ - if (triggered) - { - return std::max(0.0, ens - dtgMrg); - } - else - { - return ens; - } -} - -double recomputeMRGPrice(double ensCsr, double originalCost, double unsuppliedEnergyCost) -{ - if (ensCsr > 0.5) - { - return -unsuppliedEnergyCost; - } - else - { - return originalCost; - } -} -} // namespace Antares::Data::AdequacyPatch diff --git a/src/solver/optimisation/adequacy_patch_csr/solve_problem.h b/src/solver/optimisation/adequacy_patch_csr/solve_problem.h new file mode 100644 index 0000000000..59b0619ca2 --- /dev/null +++ b/src/solver/optimisation/adequacy_patch_csr/solve_problem.h @@ -0,0 +1,14 @@ + +#pragma once + +#include "antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h" +#include "antares/solver/optimisation/opt_structure_probleme_a_resoudre.h" +#include "antares/study/parameters/adq-patch-params.h" + +using namespace Antares::Data::AdequacyPatch; + +bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE&, + HourlyCSRProblem&, + const AdqPatchParams&, + unsigned int week, + int year); diff --git a/src/solver/optimisation/constraints/Group1.cpp b/src/solver/optimisation/constraints/Group1.cpp index 810b957aee..61de7cf2b3 100644 --- a/src/solver/optimisation/constraints/Group1.cpp +++ b/src/solver/optimisation/constraints/Group1.cpp @@ -22,6 +22,7 @@ #include "antares/solver/optimisation/constraints/Group1.h" #include "antares/solver/optimisation/constraints/ShortTermStorageCostVariation.h" +#include "antares/solver/optimisation/constraints/ShortTermStorageCumulation.h" AreaBalanceData Group1::GetAreaBalanceData() { @@ -49,6 +50,13 @@ ShortTermStorageData Group1::GetShortTermStorageData() }; } +ShortTermStorageCumulativeConstraintData Group1::GetShortTermStorageCumulativeConstraintData() +{ + return {problemeHebdo_->CorrespondanceCntNativesCntOptim, + problemeHebdo_->ShortTermStorage, + problemeHebdo_->CorrespondanceCntNativesCntOptimHebdomadaires}; +} + FlowDissociationData Group1::GetFlowDissociationData() { return {.CorrespondanceCntNativesCntOptim = problemeHebdo_->CorrespondanceCntNativesCntOptim, @@ -89,6 +97,11 @@ void Group1::BuildConstraints() ShortTermStorageCostVariationWithdrawalForward shortTermStorageCostVariationWithdrawalForward( builder_, shortTermStorageData); + + auto shortTermStorageCumulativeConstraintData = GetShortTermStorageCumulativeConstraintData(); + ShortTermStorageCumulation shortTermStorageCumulation(builder_, + shortTermStorageCumulativeConstraintData); + auto flowDissociationData = GetFlowDissociationData(); FlowDissociation flowDissociation(builder_, flowDissociationData); @@ -123,4 +136,9 @@ void Group1::BuildConstraints() bindingConstraintHour.add(pdt, cntCouplante); } } + + for (uint32_t pays = 0; pays < problemeHebdo_->NombreDePays; ++pays) + { + shortTermStorageCumulation.add(pays); + } } diff --git a/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp b/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp index a1a73ef2fa..bb298a3f8d 100644 --- a/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp +++ b/src/solver/optimisation/constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp @@ -54,7 +54,7 @@ void NbDispUnitsMinBoundSinceMinUpTime::add(int pays, int index, int pdt) builder.greaterThan(); if (builder.NumberOfVariables() > 1) { - data.CorrespondanceCntNativesCntOptim[pays] + data.CorrespondanceCntNativesCntOptim[pdt] .NumeroDeContrainteDesContraintesDeDureeMinDeMarche[cluster] = builder.data.nombreDeContraintes; diff --git a/src/solver/optimisation/constraints/ShortTermStorageCumulation.cpp b/src/solver/optimisation/constraints/ShortTermStorageCumulation.cpp new file mode 100644 index 0000000000..f6e684db83 --- /dev/null +++ b/src/solver/optimisation/constraints/ShortTermStorageCumulation.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Antares_Simulator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#include "antares/solver/optimisation/constraints/ShortTermStorageCumulation.h" + +#include +#include + +class CumulationConstraint +{ +public: + virtual void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES& input) const + = 0; + virtual std::string name() const = 0; + virtual ~CumulationConstraint() = default; +}; + +class WithdrawalCumulationConstraint: public CumulationConstraint +{ +public: + void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES&) const override + { + builder.ShortTermStorageWithdrawal(index, 1.0); + } + + std::string name() const override + { + return "WithdrawalSum"; + } + + ~WithdrawalCumulationConstraint() override = default; +}; + +class InjectionCumulationConstraint: public CumulationConstraint +{ +public: + void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES&) const override + { + builder.ShortTermStorageInjection(index, 1.0); + } + + std::string name() const override + { + return "InjectionSum"; + } + + ~InjectionCumulationConstraint() override = default; +}; + +class NettingCumulationConstraint: public CumulationConstraint +{ +public: + void build(ConstraintBuilder& builder, + unsigned int index, + const ::ShortTermStorage::PROPERTIES& input) const override + { + builder.ShortTermStorageInjection(index, input.injectionEfficiency) + .ShortTermStorageWithdrawal(index, -input.withdrawalEfficiency); + } + + std::string name() const override + { + return "NettingSum"; + } + + ~NettingCumulationConstraint() override = default; +}; + +std::unique_ptr cumulationConstraintFromVariable(const std::string& variable) +{ + if (variable == "withdrawal") + { + return std::make_unique(); + } + else if (variable == "injection") + { + return std::make_unique(); + } + else if (variable == "netting") + { + return std::make_unique(); + } + throw std::invalid_argument("Invalid cumulation constraint type"); +} + +char ConvertSense(const std::string& sense) +{ + if (sense == "greater") + { + return '>'; + } + else if (sense == "less") + { + return '<'; + } + else + { + return '='; + } +} + +void ShortTermStorageCumulation::add(int pays) +{ + ConstraintNamer namer(builder.data.NomDesContraintes); + namer.UpdateArea(builder.data.NomsDesPays[pays]); + + for (const auto& storage: data.ShortTermStorage[pays]) + { + for (const auto& constraint: storage.additional_constraints) + { + // sum (var[h]) sign rhs, h in list provided by user where: + // var = injection for InjectionCumulationConstraint + // var = withdrawal for WithdrawalCumulationConstraint + // var = injectionEfficiency * injection - withdrawalEfficiency * withdrawal for Netting + auto constraintHelper = cumulationConstraintFromVariable(constraint.variable); + namer.ShortTermStorageCumulation(constraintHelper->name(), + builder.data.nombreDeContraintes, + storage.name, + constraint.name); + const auto index = storage.clusterGlobalIndex; + data.CorrespondanceCntNativesCntOptimHebdomadaires + .ShortTermStorageCumulation[constraint.globalIndex] + = builder.data.nombreDeContraintes; + + for (const auto& hour: constraint.hours) + { + builder.updateHourWithinWeek(hour - 1); + constraintHelper->build(builder, index, storage); + } + builder.SetOperator(ConvertSense(constraint.operatorType)).build(); + } + } +} diff --git a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h index c7fb554c22..6ebb9734e4 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h @@ -31,10 +31,69 @@ #include "../variables/VariableManagerUtils.h" +struct LinkVariable +{ + LinkVariable(): + directVar(-1), + indirectVar(-1) + { + } + + LinkVariable(int direct, int indirect): + directVar(direct), + indirectVar(indirect) + { + } + + inline bool check() const + { + if (directVar < 0) + { + Antares::logs.warning() << "directVar < 0 detected, this should not happen"; + } + if (indirectVar < 0) + { + Antares::logs.warning() << "indirectVar < 0 detected, this should not happen"; + } + + return (directVar >= 0) && (indirectVar >= 0); + } + + int directVar; + int indirectVar; +}; + struct PROBLEME_HEBDO; class HourlyCSRProblem { + using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; + +public: + explicit HourlyCSRProblem(const AdqPatchParams& adqPatchParams, PROBLEME_HEBDO* p): + adqPatchParams_(adqPatchParams), + variableManager_(p->CorrespondanceVarNativesVarOptim, + p->NumeroDeVariableStockFinal, + p->NumeroDeVariableDeTrancheDeStock, + p->NombreDePasDeTempsPourUneOptimisation), + problemeHebdo_(p) + { + double temp = pow(10, -adqPatchParams.curtailmentSharing.thresholdVarBoundsRelaxation); + belowThisThresholdSetToZero = std::min(temp, 0.1); + + allocateProblem(); + } + + HourlyCSRProblem(const HourlyCSRProblem&) = delete; + HourlyCSRProblem& operator=(const HourlyCSRProblem&) = delete; + + inline void setHour(int hour) + { + triggeredHour = hour; + } + + void run(uint week, uint year); + private: void calculateCsrParameters(); @@ -65,85 +124,26 @@ class HourlyCSRProblem void setQuadraticCost(); void setLinearCost(); -private: - using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; +public: + // TODO [gp] : try to make these members private + double belowThisThresholdSetToZero; + std::map numberOfConstraintCsrAreaBalance; + std::set ensVariablesInsideAdqPatch; // place inside only ENS inside adq-patch + std::set varToBeSetToZeroIfBelowThreshold; // place inside only ENS and Spillage variable + int triggeredHour; + const AdqPatchParams& adqPatchParams_; VariableManagement::VariableManager variableManager_; -public: - void run(uint week, uint year); - - // TODO[FOM] Make these members private - int triggeredHour; - double belowThisThresholdSetToZero; PROBLEME_HEBDO* problemeHebdo_; PROBLEME_ANTARES_A_RESOUDRE problemeAResoudre_; - explicit HourlyCSRProblem(const AdqPatchParams& adqPatchParams, PROBLEME_HEBDO* p): - adqPatchParams_(adqPatchParams), - variableManager_(p->CorrespondanceVarNativesVarOptim, - p->NumeroDeVariableStockFinal, - p->NumeroDeVariableDeTrancheDeStock, - p->NombreDePasDeTempsPourUneOptimisation), - problemeHebdo_(p) - { - double temp = pow(10, -adqPatchParams.curtailmentSharing.thresholdVarBoundsRelaxation); - belowThisThresholdSetToZero = std::min(temp, 0.1); - - allocateProblem(); - } - - ~HourlyCSRProblem() = default; - - HourlyCSRProblem(const HourlyCSRProblem&) = delete; - HourlyCSRProblem& operator=(const HourlyCSRProblem&) = delete; - - inline void setHour(int hour) - { - triggeredHour = hour; - } - std::map numberOfConstraintCsrEns; - std::map numberOfConstraintCsrAreaBalance; std::map numberOfConstraintCsrFlowDissociation; std::map numberOfConstraintCsrHourlyBinding; // length is number of binding constraint // contains interco 2-2 std::map rhsAreaBalanceValues; - std::set varToBeSetToZeroIfBelowThreshold; // place inside only ENS and Spillage variable - std::set ensVariablesInsideAdqPatch; // place inside only ENS inside adq-patch - - struct LinkVariable - { - LinkVariable(): - directVar(-1), - indirectVar(-1) - { - } - - LinkVariable(int direct, int indirect): - directVar(direct), - indirectVar(indirect) - { - } - - inline bool check() const - { - if (directVar < 0) - { - Antares::logs.warning() << "directVar < 0 detected, this should not happen"; - } - if (indirectVar < 0) - { - Antares::logs.warning() << "indirectVar < 0 detected, this should not happen"; - } - - return (directVar >= 0) && (indirectVar >= 0); - } - - int directVar; - int indirectVar; - }; // links between two areas inside the adq-patch domain std::map linkInsideAdqPatch; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h deleted file mode 100644 index 3ae5ee2d91..0000000000 --- a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/post_processing.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -** Copyright 2007-2024, RTE (https://www.rte-france.com) -** See AUTHORS.txt -** SPDX-License-Identifier: MPL-2.0 -** This file is part of Antares-Simulator, -** Adequacy and Performance assessment for interconnected energy networks. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the Mozilla Public Licence 2.0 as published by -** the Mozilla Foundation, either version 2 of the License, or -** (at your option) any later version. -** -** Antares_Simulator is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** Mozilla Public Licence 2.0 for more details. -** -** You should have received a copy of the Mozilla Public Licence 2.0 -** along with Antares_Simulator. If not, see . -*/ - -#pragma once - -namespace Antares::Data::AdequacyPatch -{ -double recomputeDTG_MRG(bool triggered, double dtgMrg, double ens); -double recomputeENS_MRG(bool triggered, double dtgMrg, double ens); -double recomputeMRGPrice(double ensCsr, double originalCost, double unsuppliedEnergyCost); -} // namespace Antares::Data::AdequacyPatch diff --git a/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h index b06af4614d..31019332d3 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ConstraintBuilder.h @@ -305,3 +305,8 @@ struct ShortTermStorageData const std::vector<::ShortTermStorage::AREA_INPUT>& ShortTermStorage; }; + +struct ShortTermStorageCumulativeConstraintData: ShortTermStorageData +{ + CORRESPONDANCES_DES_CONTRAINTES_HEBDOMADAIRES& CorrespondanceCntNativesCntOptimHebdomadaires; +}; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h b/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h index e5134edb6e..c74567716e 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/constraints/Group1.h @@ -38,6 +38,9 @@ class Group1: public ConstraintGroup AreaBalanceData GetAreaBalanceData(); FictitiousLoadData GetFictitiousLoadData(); ShortTermStorageData GetShortTermStorageData(); + + ShortTermStorageCumulativeConstraintData GetShortTermStorageCumulativeConstraintData(); + FlowDissociationData GetFlowDissociationData(); BindingConstraintHourData GetBindingConstraintHourData(); }; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h new file mode 100644 index 0000000000..2c4c540158 --- /dev/null +++ b/src/solver/optimisation/include/antares/solver/optimisation/constraints/ShortTermStorageCumulation.h @@ -0,0 +1,39 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Antares_Simulator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#pragma once +#include "ConstraintBuilder.h" + +class ShortTermStorageCumulation: ConstraintFactory +{ +public: + ShortTermStorageCumulation(ConstraintBuilder& builder, + ShortTermStorageCumulativeConstraintData& data): + ConstraintFactory(builder), + data(data) + { + } + + void add(int pays); + +private: + ShortTermStorageCumulativeConstraintData& data; +}; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h index 55cccc4e64..4d761bd558 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h @@ -56,13 +56,6 @@ void OPT_InitialiserLesCoutsLineaire(PROBLEME_HEBDO*, const int, const int); void OPT_InitialiserLesCoutsQuadratiques(PROBLEME_HEBDO*, int); bool OPT_AppelDuSolveurQuadratique(PROBLEME_ANTARES_A_RESOUDRE*, const int); -using namespace Antares::Data::AdequacyPatch; -bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE&, - HourlyCSRProblem&, - const AdqPatchParams&, - uint week, - int year); - bool OPT_PilotageOptimisationLineaire(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo, Solver::IResultWriter& writer, diff --git a/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h b/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h index f05b6eb15a..c7c3dff284 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/opt_rename_problem.h @@ -166,6 +166,11 @@ class ConstraintNamer: public Namer unsigned int constraint, const std::string& short_term_name); + void ShortTermStorageCumulation(const std::string& constraint_type, + unsigned int constraint, + const std::string& short_term_name, + const std::string& constraint_name); + private: void nameWithTimeGranularity(unsigned int constraint, const std::string& name, @@ -177,6 +182,11 @@ inline std::string TimeIdentifier(unsigned int timeStep, const std::string& time return timeStepType + "<" + std::to_string(timeStep) + ">"; } +inline std::string ShortTermStorageCumulationIdentifier(const std::string& name) +{ + return "Constraint<" + name + ">"; +} + inline std::string LocationIdentifier(const std::string& location, const std::string& locationType) { return locationType + "<" + location + ">"; diff --git a/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h b/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h index 4c3fd49bd1..b7e125ad03 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/post_process_commands.h @@ -69,16 +69,24 @@ class RemixHydroPostProcessCmd: public basePostProcessCommand SimplexOptimization splx_optimization_; }; -class DTGmarginForAdqPatchPostProcessCmd: public basePostProcessCommand +class UpdateMrgPriceAfterCSRcmd: public basePostProcessCommand { - using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; - public: - DTGmarginForAdqPatchPostProcessCmd(PROBLEME_HEBDO* problemeHebdo, - AreaList& areas, - unsigned int numSpace); + UpdateMrgPriceAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, + AreaList& areas, + unsigned int numSpace); + void execute(const optRuntimeData&) override; - void execute(const optRuntimeData& opt_runtime_data) override; +private: + const AreaList& area_list_; + unsigned int numSpace_ = 0; +}; + +class DTGnettingAfterCSRcmd: public basePostProcessCommand +{ +public: + DTGnettingAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, AreaList& areas, unsigned int numSpace); + void execute(const optRuntimeData&) override; private: const AreaList& area_list_; @@ -112,8 +120,6 @@ class HydroLevelsFinalUpdatePostProcessCmd: public basePostProcessCommand class CurtailmentSharingPostProcessCmd: public basePostProcessCommand { - using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; - public: CurtailmentSharingPostProcessCmd(const AdqPatchParams& adqPatchParams, PROBLEME_HEBDO* problemeHebdo, @@ -128,6 +134,7 @@ class CurtailmentSharingPostProcessCmd: public basePostProcessCommand std::set identifyHoursForCurtailmentSharing(const std::vector& sumENS) const; std::set getHoursRequiringCurtailmentSharing() const; + using AdqPatchParams = Antares::Data::AdequacyPatch::AdqPatchParams; const AreaList& area_list_; const AdqPatchParams& adqPatchParams_; unsigned int numSpace_ = 0; diff --git a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp index 2f6f907c5a..304aab13df 100644 --- a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp +++ b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp @@ -214,10 +214,9 @@ static SimplexResult OPT_TryToCallSimplex(const OptimizationOptions& options, mps_writer->runIfNeeded(writer, filename); TimeMeasurement measure; - const bool keepBasis = (optimizationNumber == PREMIERE_OPTIMISATION); solver = ORTOOLS_Simplexe(&Probleme, solver, keepBasis, options); - if (solver) + if (solver != nullptr) { ProblemeAResoudre->ProblemesSpx[NumIntervalle] = (void*)solver; } diff --git a/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp b/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp index d1a0f00ea2..79c29c58ab 100644 --- a/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp +++ b/src/solver/optimisation/opt_decompte_variables_et_contraintes.cpp @@ -144,12 +144,12 @@ int OPT_DecompteDesVariablesEtDesContraintesDuProblemeAOptimiser(PROBLEME_HEBDO* ProblemeAResoudre->NombreDeContraintes += 2; /* 2 constraints bounding the overall energy generated over the period (10a in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint setting the level variation over the period - (10b in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint bounding the overall energy pumped over the - period (10c in the reference document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint setting the level variation over the period + (10b in the reference document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint bounding the overall energy pumped over the + period (10c in the reference document) */ } if (!Pump && TurbEntreBornes && !MonitorHourlyLev) @@ -192,16 +192,16 @@ int OPT_DecompteDesVariablesEtDesContraintesDuProblemeAOptimiser(PROBLEME_HEBDO* ProblemeAResoudre->NombreDeContraintes += 2; /* 2 constraints bounding the overall energy generated over the period (10a in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint setting the level variation over the period - (10b in the reference document) */ - ProblemeAResoudre - ->NombreDeContraintes++; /* 1 constraint bounding the overall energy pumped over the - period (10c in the reference document) */ - ProblemeAResoudre->NombreDeContraintes - += nombreDePasDeTempsPourUneOptimisation; /* T constraints expressing the level hourly - variations (14a in the reference - document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint setting the level variation over the period + (10b in the reference document) */ + ProblemeAResoudre->NombreDeContraintes++; + /* 1 constraint bounding the overall energy pumped over the + period (10c in the reference document) */ + ProblemeAResoudre->NombreDeContraintes += nombreDePasDeTempsPourUneOptimisation; + /* T constraints expressing the level hourly + variations (14a in the reference + document) */ } if (!Pump && !TurbEntreBornes && MonitorHourlyLev) { @@ -245,6 +245,10 @@ int OPT_DecompteDesVariablesEtDesContraintesDuProblemeAOptimiser(PROBLEME_HEBDO* ProblemeAResoudre->NombreDeContraintes += 2 * nombreDePasDeTempsPourUneOptimisation; } + if (!storage.additional_constraints.empty()) + { + ProblemeAResoudre->NombreDeContraintes += storage.additional_constraints.size(); + } } } } diff --git a/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp b/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp index 36bb303964..c04d416f00 100644 --- a/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp +++ b/src/solver/optimisation/opt_gestion_second_membre_cas_lineaire.cpp @@ -43,6 +43,26 @@ static void shortTermStorageLevelsRHS( } } +static void shortTermStorageCumulationRHS( + const std::vector<::ShortTermStorage::AREA_INPUT>& shortTermStorageInput, + int numberOfAreas, + std::vector& SecondMembre, + const CORRESPONDANCES_DES_CONTRAINTES_HEBDOMADAIRES& CorrespondancesDesContraintesHebdomadaires) +{ + for (int areaIndex = 0; areaIndex < numberOfAreas; areaIndex++) + { + for (auto& storage: shortTermStorageInput[areaIndex]) + { + for (const auto& constraint: storage.additional_constraints) + { + int cnt = CorrespondancesDesContraintesHebdomadaires + .ShortTermStorageCumulation[constraint.globalIndex]; + SecondMembre[cnt] = constraint.rhs; + } + } + } +} + void OPT_InitialiserLeSecondMembreDuProblemeLineaire(PROBLEME_HEBDO* problemeHebdo, int PremierPdtDeLIntervalle, int DernierPdtDeLIntervalle, @@ -145,7 +165,6 @@ void OPT_InitialiserLeSecondMembreDuProblemeLineaire(PROBLEME_HEBDO* problemeHeb ProblemeAResoudre->SecondMembre, CorrespondanceCntNativesCntOptim, hourInTheYear); - for (uint32_t interco = 0; interco < problemeHebdo->NombreDInterconnexions; interco++) { if (const COUTS_DE_TRANSPORT& CoutDeTransport = problemeHebdo->CoutDeTransport[interco]; @@ -377,6 +396,10 @@ void OPT_InitialiserLeSecondMembreDuProblemeLineaire(PROBLEME_HEBDO* problemeHeb } } + shortTermStorageCumulationRHS(problemeHebdo->ShortTermStorage, + problemeHebdo->NombreDePays, + ProblemeAResoudre->SecondMembre, + problemeHebdo->CorrespondanceCntNativesCntOptimHebdomadaires); if (problemeHebdo->OptimisationAvecCoutsDeDemarrage) { OPT_InitialiserLeSecondMembreDuProblemeLineaireCoutsDeDemarrage(problemeHebdo, diff --git a/src/solver/optimisation/opt_rename_problem.cpp b/src/solver/optimisation/opt_rename_problem.cpp index a92553e5e6..b385c0f8f5 100644 --- a/src/solver/optimisation/opt_rename_problem.cpp +++ b/src/solver/optimisation/opt_rename_problem.cpp @@ -35,9 +35,9 @@ const std::string AREA("area"); std::string BuildName(const std::string& name, const std::string& location, - const std::string& timeIdentifier) + const std::string& additional_identifier) { - std::string result = name + SEPARATOR + location + SEPARATOR + timeIdentifier; + std::string result = name + SEPARATOR + location + SEPARATOR + additional_identifier; std::replace(result.begin(), result.end(), ' ', '*'); return result; } @@ -414,3 +414,16 @@ void ConstraintNamer::ShortTermStorageCostVariation(const std::string& constrain TimeIdentifier(timeStep_, HOUR)), constraint); } + +void ConstraintNamer::ShortTermStorageCumulation(const std::string& constraint_type, + unsigned int constraint, + const std::string& short_term_name, + const std::string& constraint_name) +{ + targetUpdater_.UpdateTargetAtIndex( + BuildName(constraint_type, + LocationIdentifier(area_, AREA) + SEPARATOR + "ShortTermStorage" + "<" + + short_term_name + ">", + ShortTermStorageCumulationIdentifier(constraint_name)), + constraint); +} diff --git a/src/solver/optimisation/post_process_commands.cpp b/src/solver/optimisation/post_process_commands.cpp index b069050960..3ddc06ddde 100644 --- a/src/solver/optimisation/post_process_commands.cpp +++ b/src/solver/optimisation/post_process_commands.cpp @@ -22,7 +22,6 @@ #include "antares/solver/optimisation/post_process_commands.h" #include "antares/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.h" -#include "antares/solver/optimisation/adequacy_patch_csr/post_processing.h" #include "antares/solver/simulation/adequacy_patch_runtime_data.h" #include "antares/solver/simulation/common-eco-adq.h" @@ -116,53 +115,84 @@ void RemixHydroPostProcessCmd::execute(const optRuntimeData& opt_runtime_data) hourInYear); } -// ----------------------------- -// DTG margin for adq patch -// ----------------------------- +// ---------------------------------- +// Update marginal price after CSR +// ---------------------------------- using namespace Antares::Data::AdequacyPatch; -DTGmarginForAdqPatchPostProcessCmd::DTGmarginForAdqPatchPostProcessCmd( - PROBLEME_HEBDO* problemeHebdo, - AreaList& areas, - unsigned int numSpace): +UpdateMrgPriceAfterCSRcmd::UpdateMrgPriceAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, + AreaList& areas, + unsigned int numSpace): basePostProcessCommand(problemeHebdo), area_list_(areas), numSpace_(numSpace) { } -/*! -** Calculate Dispatchable margin for all areas after CSR optimization and adjust ENS -** values if neccessary. If LOLD=1, Sets MRG COST to the max value (unsupplied energy cost) -** */ -void DTGmarginForAdqPatchPostProcessCmd::execute(const optRuntimeData&) +void UpdateMrgPriceAfterCSRcmd::execute(const optRuntimeData&) { for (uint32_t Area = 0; Area < problemeHebdo_->NombreDePays; Area++) { - if (problemeHebdo_->adequacyPatchRuntimeData->areaMode[Area] != physicalAreaInsideAdqPatch) + auto& hourlyResults = problemeHebdo_->ResultatsHoraires[Area]; + const auto& scratchpad = area_list_[Area]->scratchpad[numSpace_]; + const double unsuppliedEnergyCost = area_list_[Area]->thermal.unsuppliedEnergyCost; + const bool areaInside = problemeHebdo_->adequacyPatchRuntimeData->areaMode[Area] + == physicalAreaInsideAdqPatch; + for (uint hour = 0; hour < nbHoursInWeek; hour++) { - continue; + const bool isHourTriggeredByCsr = problemeHebdo_->adequacyPatchRuntimeData + ->wasCSRTriggeredAtAreaHour(Area, hour); + + if (isHourTriggeredByCsr + && hourlyResults.ValeursHorairesDeDefaillancePositive[hour] > 0.5 && areaInside) + { + hourlyResults.CoutsMarginauxHoraires[hour] = -unsuppliedEnergyCost; + } } + } +} + +// ----------------------------- +// DTG margin for adq patch +// ----------------------------- +DTGnettingAfterCSRcmd::DTGnettingAfterCSRcmd(PROBLEME_HEBDO* problemeHebdo, + AreaList& areas, + unsigned int numSpace): + basePostProcessCommand(problemeHebdo), + area_list_(areas), + numSpace_(numSpace) +{ +} + +void DTGnettingAfterCSRcmd::execute(const optRuntimeData&) +{ + for (uint32_t Area = 0; Area < problemeHebdo_->NombreDePays; Area++) + { + auto& hourlyResults = problemeHebdo_->ResultatsHoraires[Area]; + const auto& scratchpad = area_list_[Area]->scratchpad[numSpace_]; for (uint hour = 0; hour < nbHoursInWeek; hour++) { - auto& hourlyResults = problemeHebdo_->ResultatsHoraires[Area]; - const auto& scratchpad = area_list_[Area]->scratchpad[numSpace_]; + const bool isHourTriggeredByCsr = problemeHebdo_->adequacyPatchRuntimeData + ->wasCSRTriggeredAtAreaHour(Area, hour); + const double dtgMrg = scratchpad.dispatchableGenerationMargin[hour]; const double ens = hourlyResults.ValeursHorairesDeDefaillancePositive[hour]; - const bool triggered = problemeHebdo_->adequacyPatchRuntimeData - ->wasCSRTriggeredAtAreaHour(Area, hour); - hourlyResults.ValeursHorairesDtgMrgCsr[hour] = recomputeDTG_MRG(triggered, dtgMrg, ens); - hourlyResults.ValeursHorairesDeDefaillancePositiveCSR[hour] = recomputeENS_MRG( - triggered, - dtgMrg, - ens); - - const double unsuppliedEnergyCost = area_list_[Area]->thermal.unsuppliedEnergyCost; - hourlyResults.CoutsMarginauxHoraires[hour] = recomputeMRGPrice( - hourlyResults.ValeursHorairesDtgMrgCsr[hour], - hourlyResults.CoutsMarginauxHoraires[hour], - unsuppliedEnergyCost); + const bool areaInside = problemeHebdo_->adequacyPatchRuntimeData->areaMode[Area] + == physicalAreaInsideAdqPatch; + if (isHourTriggeredByCsr && areaInside) + { + hourlyResults.ValeursHorairesDtgMrgCsr[hour] = std::max(0.0, dtgMrg - ens); + hourlyResults.ValeursHorairesDeDefaillancePositiveCSR[hour] = std::max(0.0, + ens + - dtgMrg); + } + else + { + // Default value (when the hour is not triggered by CSR) + hourlyResults.ValeursHorairesDtgMrgCsr[hour] = dtgMrg; + hourlyResults.ValeursHorairesDeDefaillancePositiveCSR[hour] = ens; + } } } } diff --git a/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h b/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h index f1ed9bca0c..a4517115a8 100644 --- a/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h +++ b/src/solver/simulation/include/antares/solver/simulation/sim_alloc_probleme_hebdo.h @@ -39,6 +39,8 @@ void SIM_AllocationLinks(PROBLEME_HEBDO& problem, void SIM_AllocationConstraints(PROBLEME_HEBDO& problem, const Antares::Data::Study& study, unsigned NombreDePasDeTemps); +void SIM_AllocationShortermStorageCumulation(PROBLEME_HEBDO& problem, + const Antares::Data::Study& study); void SIM_AllocateAreas(PROBLEME_HEBDO& problem, const Antares::Data::Study& study, diff --git a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h index d24fb25bc4..35051ba2d5 100644 --- a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h +++ b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h @@ -111,6 +111,7 @@ struct CORRESPONDANCES_DES_CONTRAINTES_JOURNALIERES struct CORRESPONDANCES_DES_CONTRAINTES_HEBDOMADAIRES { std::vector NumeroDeContrainteDesContraintesCouplantes; + std::vector ShortTermStorageCumulation; }; struct VALEURS_DE_NTC_ET_RESISTANCES @@ -182,7 +183,7 @@ struct PROPERTIES bool penalizeVariationInjection; std::shared_ptr series; - + std::vector additional_constraints; int clusterGlobalIndex; std::string name; }; diff --git a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp index d735191ff7..dd7480671c 100644 --- a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp +++ b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp @@ -38,6 +38,7 @@ void SIM_AllocationProblemeHebdo(const Data::Study& study, SIM_AllocationProblemePasDeTemps(problem, study, NombreDePasDeTemps); SIM_AllocationLinks(problem, study.runtime.interconnectionsCount(), NombreDePasDeTemps); SIM_AllocationConstraints(problem, study, NombreDePasDeTemps); + SIM_AllocationShortermStorageCumulation(problem, study); SIM_AllocateAreas(problem, study, NombreDePasDeTemps); } catch (const std::bad_alloc& e) @@ -245,6 +246,13 @@ void SIM_AllocationLinks(PROBLEME_HEBDO& problem, const uint linkCount, unsigned } } +void SIM_AllocationShortermStorageCumulation(PROBLEME_HEBDO& problem, + const Antares::Data::Study& study) +{ + problem.CorrespondanceCntNativesCntOptimHebdomadaires.ShortTermStorageCumulation + .assign(study.runtime.shortTermStorageCumulativeConstraintCount, 0); +} + void SIM_AllocationConstraints(PROBLEME_HEBDO& problem, const Antares::Data::Study& study, unsigned NombreDePasDeTemps) @@ -386,6 +394,7 @@ void SIM_AllocateAreas(PROBLEME_HEBDO& problem, problem.ResultatsHoraires[k].ValeursHorairesDeDefaillanceNegative.assign(NombreDePasDeTemps, 0.); + problem.ResultatsHoraires[k].TurbinageHoraire.assign(NombreDePasDeTemps, 0.); problem.ResultatsHoraires[k].PompageHoraire.assign(NombreDePasDeTemps, 0.); problem.ResultatsHoraires[k].CoutsMarginauxHoraires.assign(NombreDePasDeTemps, 0.); diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index 3d29d3ee42..786b1b5aa6 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -39,6 +39,7 @@ static void importShortTermStorages( std::vector<::ShortTermStorage::AREA_INPUT>& ShortTermStorageOut) { int clusterGlobalIndex = 0; + int clusterCumulativeConstraintGlobalIndex = 0; for (uint areaIndex = 0; areaIndex != areas.size(); areaIndex++) { ShortTermStorageOut[areaIndex].resize(areas[areaIndex]->shortTermStorage.count()); @@ -59,7 +60,12 @@ static void importShortTermStorages( toInsert.penalizeVariationInjection = st.properties.penalizeVariationInjection; toInsert.penalizeVariationWithdrawal = st.properties.penalizeVariationWithdrawal; toInsert.name = st.properties.name; - + toInsert.additional_constraints = st.additional_constraints; + for (auto& constraint: toInsert.additional_constraints) + { + constraint.globalIndex = clusterCumulativeConstraintGlobalIndex; + ++clusterCumulativeConstraintGlobalIndex; + } toInsert.series = st.series; // TODO add missing properties, or use the same struct diff --git a/src/solver/systemParser/CMakeLists.txt b/src/solver/systemParser/CMakeLists.txt index 79e1262644..37e09a616a 100644 --- a/src/solver/systemParser/CMakeLists.txt +++ b/src/solver/systemParser/CMakeLists.txt @@ -1,5 +1,3 @@ -find_package(yaml-cpp REQUIRED) - set(SOURCES parser.cpp converter.cpp diff --git a/src/solver/variable/CMakeLists.txt b/src/solver/variable/CMakeLists.txt index 5022d5b542..ed7c017f5b 100644 --- a/src/solver/variable/CMakeLists.txt +++ b/src/solver/variable/CMakeLists.txt @@ -78,8 +78,9 @@ set(SRC_VARIABLE_ECONOMY include/antares/solver/variable/economy/links.h # Variables for Economy + include/antares/solver/variable/economy/max-mrg-utils.h + max-mrg-utils.cpp include/antares/solver/variable/economy/max-mrg.h - economy/max-mrg.cpp include/antares/solver/variable/economy/price.h include/antares/solver/variable/economy/balance.h include/antares/solver/variable/economy/operatingCost.h @@ -127,6 +128,7 @@ set(SRC_VARIABLE_ECONOMY include/antares/solver/variable/economy/links/congestionFeeAbs.h include/antares/solver/variable/economy/links/marginalCost.h include/antares/solver/variable/economy/links/congestionProbability.h + include/antares/solver/variable/economy/overallCostCsr.h # Binding constraints include/antares/solver/variable/economy/bindingConstraints/bindingConstraintsMarginalCost.h diff --git a/src/solver/variable/economy/max-mrg.cpp b/src/solver/variable/economy/max-mrg.cpp deleted file mode 100644 index 7cb565037f..0000000000 --- a/src/solver/variable/economy/max-mrg.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* -** Copyright 2007-2024, RTE (https://www.rte-france.com) -** See AUTHORS.txt -** SPDX-License-Identifier: MPL-2.0 -** This file is part of Antares-Simulator, -** Adequacy and Performance assessment for interconnected energy networks. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the Mozilla Public Licence 2.0 as published by -** the Mozilla Foundation, either version 2 of the License, or -** (at your option) any later version. -** -** Antares_Simulator is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** Mozilla Public Licence 2.0 for more details. -** -** You should have received a copy of the Mozilla Public Licence 2.0 -** along with Antares_Simulator. If not, see . -*/ - -#include "antares/solver/variable/economy/max-mrg.h" - -#include - -#include -#include - -using namespace Yuni; - -namespace Antares::Solver::Variable::Economy -{ -template -struct SpillageSelector -{ - template - static auto Value(const State&, - const U& weeklyResults, - uint) -> decltype(weeklyResults.ValeursHorairesDeDefaillanceNegative) - { - return weeklyResults.ValeursHorairesDeDefaillanceNegative; - } -}; - -template<> -struct SpillageSelector -{ - template - static auto Value(const State& state, const U&, uint index) -> decltype(state.resSpilled[index]) - { - return state.resSpilled[index]; - } -}; - -template -inline void PrepareMaxMRGFor(const State& state, double* opmrg, uint numSpace) -{ - assert(168 + state.hourInTheYear <= HOURS_PER_YEAR); - assert(opmrg && "Invalid OP.MRG target"); - - enum - { - offset = 0, - endHour = 168, - }; - - // current area - auto& area = *state.area; - // index of the current area - auto index = area.index; - assert(area.index < 50000 && "seems invalid"); - - // current problem - auto& problem = *state.problemeHebdo; - // Weekly results from solver for the current area - auto& weeklyResults = problem.ResultatsHoraires[index]; - // Unsupplied enery for the current area - auto& D = weeklyResults.ValeursHorairesDeDefaillancePositive; - // Spillage - auto S = SpillageSelector::Value(state, weeklyResults, area.index); - - double OI[168]; - - // H.STOR - std::vector& H = weeklyResults.TurbinageHoraire; - - // energie turbinee de la semaine - { - // DTG MRG - const double* M = area.scratchpad[numSpace].dispatchableGenerationMargin; - - double WH = 0.; - { - // H.STOR - for (uint i = offset; i != endHour; ++i) - { - WH += H[i]; - } - } - - if (Utils::isZero(WH)) // no hydro - { - for (uint i = offset; i != endHour; ++i) - { - opmrg[i] = +S[i] + M[i] - D[i]; - } - return; - } - - // initialisation - for (uint i = offset; i != endHour; ++i) - { - OI[i] = +S[i] + M[i] - D[i]; - } - } - - double bottom = +std::numeric_limits::max(); - double top = 0; - - for (uint i = offset; i != endHour; ++i) - { - double oii = OI[i]; - if (oii > top) - { - top = oii; - } - if (oii < bottom) - { - bottom = oii; - } - } - - double ecart = 1.; - uint loop = 100; // arbitrary - maximum number of iterations - - // Pmax - const uint y = problem.year; - const auto& P = area.hydro.series->maxHourlyGenPower; - - do - { - double niveau = (top + bottom) * 0.5; - double SP = 0; // S+ - double SM = 0; // S- - - for (uint i = offset; i != endHour; ++i) - { - assert(i < HOURS_PER_YEAR && "calendar overflow"); - if (niveau > OI[i]) - { - opmrg[i] = std::min(niveau, - OI[i] + P.getCoefficient(y, i + state.hourInTheYear) - H[i]); - SM += opmrg[i] - OI[i]; - } - else - { - opmrg[i] = std::max(niveau, OI[i] - H[i]); - SP += OI[i] - opmrg[i]; - } - } - - ecart = SP - SM; - if (ecart > 0) - { - bottom = niveau; - } - else - { - top = niveau; - } - - if (!--loop) - { - logs.error() << "OP.MRG: " << area.name - << ": infinite loop detected. please check input data"; - return; - } - } while (ecart * ecart > 0.25); -} - -void PrepareMaxMRG(const State& state, double* opmrg, uint numSpace) -{ - if (state.simplexRunNeeded) - { - PrepareMaxMRGFor(state, opmrg, numSpace); - } - else - { - PrepareMaxMRGFor(state, opmrg, numSpace); - } -} - -} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/all.h b/src/solver/variable/include/antares/solver/variable/economy/all.h index 8e1c2ed307..17207c22db 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/all.h +++ b/src/solver/variable/include/antares/solver/variable/economy/all.h @@ -49,12 +49,16 @@ #include "inflow.h" #include "localMatchingRuleViolations.h" #include "lold.h" +#include "loldCsr.h" #include "lolp.h" +#include "lolpCsr.h" +#include "max-mrg-csr.h" #include "max-mrg.h" #include "nbOfDispatchedUnits.h" #include "nonProportionalCost.h" #include "operatingCost.h" #include "overallCost.h" +#include "overallCostCsr.h" #include "overflow.h" #include "pumping.h" #include "renewableGeneration.h" @@ -91,59 +95,62 @@ namespace Antares::Solver::Variable::Economy /*! ** \brief All variables for a single area (economy) */ -typedef // Prices - OverallCost // Overall Cost (Op. Cost + Unsupplied Eng.) - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +typedef // Prices + OverallCost // Overall Cost (Op. Cost + Unsupplied Eng.) + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> VariablesPerArea; /*! diff --git a/src/solver/variable/include/antares/solver/variable/economy/loldCsr.h b/src/solver/variable/include/antares/solver/variable/economy/loldCsr.h new file mode 100644 index 0000000000..299e83b94f --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/loldCsr.h @@ -0,0 +1,242 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#pragma once + +#include "../variable.h" + +namespace Antares::Solver::Variable::Economy +{ + +struct VCardLOLD_CSR +{ + //! Caption + static std::string Caption() + { + return "LOLD CSR"; + } + + //! Unit + static std::string Unit() + { + return "Hours"; + } + + //! The short description of the variable + static std::string Description() + { + return "LOLD for CSR"; + } + + //! The expecte results + typedef Results>>>> + ResultsType; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 2; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief +*/ +template +class LOLD_CSR: public Variable::IVariable, NextT, VCardLOLD_CSR> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardLOLD_CSR VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~LOLD_CSR() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + // Next + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void simulationBegin() + { + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].reset(); + } + // Next + NextType::simulationBegin(); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + + // Next variable + NextType::yearBegin(year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily,weekly,monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); + + // Next variable + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + // Next variable + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void hourForEachArea(State& state, unsigned int numSpace) + { + if (state.hourlyResults->ValeursHorairesDeDefaillancePositiveCSR[state.hourInTheWeek] > 0.5) + { + pValuesForTheCurrentYear[numSpace][state.hourInTheYear] = 1.; + } + + // Next variable + NextType::hourForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class LOLD_CSR + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/lolpCsr.h b/src/solver/variable/include/antares/solver/variable/economy/lolpCsr.h new file mode 100644 index 0000000000..45d9ed61ac --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/lolpCsr.h @@ -0,0 +1,242 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#pragma once + +#include "../variable.h" + +namespace Antares::Solver::Variable::Economy +{ +struct VCardLOLP_CSR +{ + //! Caption + static std::string Caption() + { + return "LOLP CSR"; + } + + //! Unit + static std::string Unit() + { + return "%"; + } + + //! The short description of the variable + static std::string Description() + { + return "LOLP for CSR"; + } + + //! The expecte results + typedef Results> + ResultsType; + + //! The VCard to look for for calculating spatial aggregates + typedef VCardLOLP_CSR VCardForSpatialAggregate; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 2; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief +*/ +template +class LOLP_CSR: public Variable::IVariable, NextT, VCardLOLP_CSR> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardLOLP_CSR VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~LOLP_CSR() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + + // Next + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void simulationBegin() + { + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].reset(); + } + // Next + NextType::simulationBegin(); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + + // Next variable + NextType::yearBegin(year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily,weekly,monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsOrForTheCurrentYear(); + + // Next variable + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + // Next variable + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void hourForEachArea(State& state, unsigned int numSpace) + { + if (state.hourlyResults->ValeursHorairesDeDefaillancePositiveCSR[state.hourInTheWeek] > 0.5) + { + pValuesForTheCurrentYear[numSpace][state.hourInTheYear] = 100; + } + + // Next variable + NextType::hourForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class LOLP_CSR + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/max-mrg-csr.h b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-csr.h new file mode 100644 index 0000000000..2ff8edc89d --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-csr.h @@ -0,0 +1,243 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#pragma once + +#include "../variable.h" +#include "max-mrg-utils.h" + +namespace Antares::Solver::Variable::Economy +{ +struct VCardMAX_MRG_CSR +{ + //! Caption + static std::string Caption() + { + return "MAX MRG CSR"; + } + + //! Unit + static std::string Unit() + { + return "MWh"; + } + + //! The short description of the variable + static std::string Description() + { + return "Max margin for CSR"; + } + + //! The expecte results + typedef Results>>>> + ResultsType; + + //! The VCard to look for for calculating spatial aggregates + typedef VCardMAX_MRG_CSR VCardForSpatialAggregate; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 0; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief Prepare MAX.MRG results for a given week +*/ +template +void PrepareMaxMRGFor(const State& state, double* opmrg, uint numSpace); + +/*! +** \brief Max MRG +*/ +template +class MaxMrgCsr: public Variable::IVariable, NextT, VCardMAX_MRG_CSR> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardMAX_MRG_CSR VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~MaxMrgCsr() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + + // Next + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + // Next variable + NextType::yearBegin(year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily,weekly,monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); + + // Next variable + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + // Next variable + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void weekForEachArea(State& state, unsigned int numSpace) + { + double* rawhourly = Memory::RawPointer(pValuesForTheCurrentYear[numSpace].hour); + + // Getting data required to compute max margin + MaxMrgCSRdataFactory maxMRGcsrDataFactory(state, numSpace); + MaxMRGinput maxMRGinput = maxMRGcsrDataFactory.data(); + computeMaxMRG(rawhourly + state.hourInTheYear, maxMRGinput); + + // next + NextType::weekForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class MaxMrgCsr + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/max-mrg-utils.h b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-utils.h new file mode 100644 index 0000000000..2a5de71b74 --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/max-mrg-utils.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "../state.h" + +namespace Antares::Solver::Variable::Economy +{ + +struct MaxMRGinput +{ + double* spillage = nullptr; + double* dens = nullptr; + double* hydroGeneration = nullptr; + Antares::Data::TimeSeries* maxHourlyGenPower = nullptr; + double* dtgMargin = nullptr; + unsigned int hourInYear = 0; + unsigned int year = 0; + Date::Calendar* calendar = nullptr; + std::string areaName; +}; + +void computeMaxMRG(double* maxMrgOut, const MaxMRGinput& in); + +class MaxMrgDataFactory +{ +public: + MaxMrgDataFactory(const State& state, unsigned int numSpace); + virtual MaxMRGinput data() = 0; + +protected: + // in data + const State& state_; + const unsigned int numSpace_ = 0; + RESULTATS_HORAIRES& weeklyResults_; + // data to be built + MaxMRGinput maxMRGinput_; +}; + +class MaxMrgUsualDataFactory: public MaxMrgDataFactory +{ + using MaxMrgDataFactory::MaxMrgDataFactory; + +public: + virtual MaxMRGinput data() override; +}; + +class MaxMrgCSRdataFactory: public MaxMrgDataFactory +{ + using MaxMrgDataFactory::MaxMrgDataFactory; + +public: + virtual MaxMRGinput data() override; +}; + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h b/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h index 959d6f16e7..a53b893947 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h +++ b/src/solver/variable/include/antares/solver/variable/economy/max-mrg.h @@ -23,6 +23,8 @@ #include "antares/solver/variable/variable.h" +#include "max-mrg-utils.h" + namespace Antares { namespace Solver @@ -92,11 +94,6 @@ struct VCardMARGE }; // class VCard -/*! -** \brief Prepare MAX.MRG results for a given week -*/ -void PrepareMaxMRG(const State& state, double* opmrg, uint numSpace); - /*! ** \brief Max MRG */ @@ -206,8 +203,6 @@ class Marge: public Variable::IVariable, NextT, VCardMARGE> { // Compute all statistics for the current year (daily,weekly,monthly) pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); - // Merge all those values with the global results - // AncestorType::pResults.merge(year, pValuesForTheCurrentYear); // Next variable NextType::yearEnd(year, numSpace); @@ -242,7 +237,11 @@ class Marge: public Variable::IVariable, NextT, VCardMARGE> void weekForEachArea(State& state, unsigned int numSpace) { double* rawhourly = Memory::RawPointer(pValuesForTheCurrentYear[numSpace].hour); - PrepareMaxMRG(state, rawhourly + state.hourInTheYear, numSpace); + + // Getting data required to compute max margin + MaxMrgUsualDataFactory maxMRGdataFactory(state, numSpace); + MaxMRGinput maxMRGinput = maxMRGdataFactory.data(); + computeMaxMRG(rawhourly + state.hourInTheYear, maxMRGinput); // next NextType::weekForEachArea(state, numSpace); diff --git a/src/solver/variable/include/antares/solver/variable/economy/overallCostCsr.h b/src/solver/variable/include/antares/solver/variable/economy/overallCostCsr.h new file mode 100644 index 0000000000..63739b686b --- /dev/null +++ b/src/solver/variable/include/antares/solver/variable/economy/overallCostCsr.h @@ -0,0 +1,253 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#pragma once + +#include "../variable.h" + +namespace Antares::Solver::Variable::Economy +{ +struct VCardOverallCostCsr +{ + //! Caption + static std::string Caption() + { + return "OV. COST CSR"; + } + + //! Unit + static std::string Unit() + { + return "Euro"; + } + + //! The short description of the variable + static std::string Description() + { + return "Overall Cost throughout all MC years"; + } + + //! The expecte results + typedef Results, + R::AllYears::Average // Use these values for spatial cluster + > + ResultsType; + + //! The VCard to look for for calculating spatial aggregates + typedef VCardOverallCostCsr VCardForSpatialAggregate; + + static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; + //! File level (provided by the type of the results) + static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile + & (Category::FileLevel::id + | Category::FileLevel::va); + //! Precision (views) + static constexpr uint8_t precision = Category::all; + //! Indentation (GUI) + static constexpr uint8_t nodeDepthForGUI = +0; + //! Decimal precision + static constexpr uint8_t decimal = 0; + //! Number of columns used by the variable (One ResultsType per column) + static constexpr int columnCount = 1; + //! The Spatial aggregation + static constexpr uint8_t spatialAggregate = Category::spatialAggregateSum; + static constexpr uint8_t spatialAggregateMode = Category::spatialAggregateEachYear; + static constexpr uint8_t spatialAggregatePostProcessing = 0; + //! Intermediate values + static constexpr uint8_t hasIntermediateValues = 1; + //! Can this variable be non applicable (0 : no, 1 : yes) + static constexpr uint8_t isPossiblyNonApplicable = 0; + + typedef IntermediateValues IntermediateValuesBaseType; + typedef IntermediateValues* IntermediateValuesType; + + typedef IntermediateValuesBaseType* IntermediateValuesTypeForSpatialAg; + +}; // class VCard + +/*! +** \brief C02 Average value of the overall OverallCostCsr emissions expected from all +** the thermal dispatchable clusters +*/ +template +class OverallCostCsr: public Variable::IVariable, NextT, VCardOverallCostCsr> +{ +public: + //! Type of the next static variable + typedef NextT NextType; + //! VCard + typedef VCardOverallCostCsr VCardType; + //! Ancestor + typedef Variable::IVariable, NextT, VCardType> AncestorType; + + //! List of expected results + typedef typename VCardType::ResultsType ResultsType; + + typedef VariableAccessor VariableAccessorType; + + enum + { + //! How many items have we got + count = 1 + NextT::count, + }; + + template + struct Statistics + { + enum + { + count = ((VCardType::categoryDataLevel & CDataLevel + && VCardType::categoryFileLevel & CFile) + ? (NextType::template Statistics::count + + VCardType::columnCount * ResultsType::count) + : NextType::template Statistics::count), + }; + }; + +public: + ~OverallCostCsr() + { + delete[] pValuesForTheCurrentYear; + } + + void initializeFromStudy(Data::Study& study) + { + pNbYearsParallel = study.maxNbYearsInParallel; + + // Intermediate values + InitializeResultsFromStudy(AncestorType::pResults, study); + + // Intermediate values + pValuesForTheCurrentYear = new VCardType::IntermediateValuesBaseType[pNbYearsParallel]; + for (unsigned int numSpace = 0; numSpace < pNbYearsParallel; numSpace++) + { + pValuesForTheCurrentYear[numSpace].initializeFromStudy(study); + } + + NextType::initializeFromStudy(study); + } + + template + static void InitializeResultsFromStudy(R& results, Data::Study& study) + { + VariableAccessorType::InitializeAndReset(results, study); + } + + void yearBegin(unsigned int year, unsigned int numSpace) + { + // Reset the values for the current year + pValuesForTheCurrentYear[numSpace].reset(); + + NextType::yearBegin(year, numSpace); + } + + void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) + { + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; + ++i) + { + pValuesForTheCurrentYear[numSpace][i] += state.thermalClusterOperatingCostForYear[i]; + } + + NextType::yearEndBuildForEachThermalCluster(state, year, numSpace); + } + + void yearEnd(unsigned int year, unsigned int numSpace) + { + // Compute all statistics for the current year (daily, weekly, monthly) + pValuesForTheCurrentYear[numSpace].computeStatisticsForTheCurrentYear(); + + NextType::yearEnd(year, numSpace); + } + + void computeSummary(std::map& numSpaceToYear, + unsigned int nbYearsForCurrentSummary) + { + for (unsigned int numSpace = 0; numSpace < nbYearsForCurrentSummary; ++numSpace) + { + // Merge all those values with the global results + AncestorType::pResults.merge(numSpaceToYear[numSpace] /*year*/, + pValuesForTheCurrentYear[numSpace]); + } + + NextType::computeSummary(numSpaceToYear, nbYearsForCurrentSummary); + } + + void hourForEachArea(State& state, unsigned int numSpace) + { + const double costForSpilledOrUnsuppliedEnergyCSR = + // Total UnsupliedEnergy emissions + (state.hourlyResults->ValeursHorairesDeDefaillancePositiveCSR[state.hourInTheWeek] + * state.area->thermal.unsuppliedEnergyCost) + + (state.hourlyResults->ValeursHorairesDeDefaillanceNegative[state.hourInTheWeek] + * state.area->thermal.spilledEnergyCost) + // Current hydro storage and pumping generation costs + + (state.hourlyResults->valeurH2oHoraire[state.hourInTheWeek] + * (state.hourlyResults->TurbinageHoraire[state.hourInTheWeek] + - state.area->hydro.pumpingEfficiency + * state.hourlyResults->PompageHoraire[state.hourInTheWeek])); + + pValuesForTheCurrentYear[numSpace][state.hourInTheYear] + += costForSpilledOrUnsuppliedEnergyCSR; + + NextType::hourForEachArea(state, numSpace); + } + + Antares::Memory::Stored::ConstReturnType retrieveRawHourlyValuesForCurrentYear( + unsigned int, + unsigned int numSpace) const + { + return pValuesForTheCurrentYear[numSpace].hour; + } + + void localBuildAnnualSurveyReport(SurveyResults& results, + int fileLevel, + int precision, + unsigned int numSpace) const + { + // Initializing external pointer on current variable non applicable status + results.isCurrentVarNA = AncestorType::isNonApplicable; + + if (AncestorType::isPrinted[0]) + { + // Write the data for the current year + results.variableCaption = VCardType::Caption(); + results.variableUnit = VCardType::Unit(); + pValuesForTheCurrentYear[numSpace] + .template buildAnnualSurveyReport(results, fileLevel, precision); + } + } + +private: + //! Intermediate values for each year + typename VCardType::IntermediateValuesType pValuesForTheCurrentYear; + unsigned int pNbYearsParallel; + +}; // class OverallCostCsr + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h b/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h index e4a989de2c..a83ccb2e03 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h +++ b/src/solver/variable/include/antares/solver/variable/economy/unsupliedEnergyCsr.h @@ -60,8 +60,6 @@ struct VCardUnsupliedEnergyCSR //! The VCard to look for for calculating spatial aggregates typedef VCardUnsupliedEnergyCSR VCardForSpatialAggregate; - - //! Data Level static constexpr uint8_t categoryDataLevel = Category::DataLevel::area; //! File level (provided by the type of the results) static constexpr uint8_t categoryFileLevel = ResultsType::categoryFile diff --git a/src/solver/variable/max-mrg-utils.cpp b/src/solver/variable/max-mrg-utils.cpp new file mode 100644 index 0000000000..bfa93c1567 --- /dev/null +++ b/src/solver/variable/max-mrg-utils.cpp @@ -0,0 +1,171 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ + +#include + +#include +#include +#include + +using namespace Yuni; + +const unsigned int nbHoursInWeek = 168; + +namespace Antares::Solver::Variable::Economy +{ + +MaxMrgDataFactory::MaxMrgDataFactory(const State& state, unsigned int numSpace): + state_(state), + numSpace_(numSpace), + weeklyResults_(state.problemeHebdo->ResultatsHoraires[state.area->index]) +{ + maxMRGinput_.hydroGeneration = weeklyResults_.TurbinageHoraire.data(); + maxMRGinput_.maxHourlyGenPower = &state_.area->hydro.series->maxHourlyGenPower; + maxMRGinput_.dtgMargin = state_.area->scratchpad[numSpace].dispatchableGenerationMargin; + maxMRGinput_.hourInYear = state.hourInTheYear; + maxMRGinput_.year = state.problemeHebdo->year; + maxMRGinput_.calendar = &state.study.calendar; + maxMRGinput_.areaName = state_.area->name.c_str(); +} + +MaxMRGinput MaxMrgUsualDataFactory::data() +{ + if (state_.simplexRunNeeded) + { + maxMRGinput_.spillage = weeklyResults_.ValeursHorairesDeDefaillanceNegative.data(); + } + else + { + maxMRGinput_.spillage = state_.resSpilled[state_.area->index]; + } + + maxMRGinput_.dens = weeklyResults_.ValeursHorairesDeDefaillancePositive.data(); + return maxMRGinput_; +} + +MaxMRGinput MaxMrgCSRdataFactory::data() +{ + maxMRGinput_.spillage = weeklyResults_.ValeursHorairesDeDefaillanceNegative.data(); + maxMRGinput_.dens = weeklyResults_.ValeursHorairesDeDefaillancePositiveCSR.data(); + return maxMRGinput_; +} + +void computeMaxMRG(double* maxMrgOut, const MaxMRGinput& in) +{ + assert(maxMrgOut && "Invalid OP.MRG target"); + + // Following block could be replaced with : + // double weekHydroGen = std::accumulate(in.hydroGeneration, in.hydroGeneration + nbHoursInWeek, + // 0.); + double weekHydroGen = 0.; + for (uint h = 0; h != nbHoursInWeek; ++h) + { + weekHydroGen += in.hydroGeneration[h]; + } + + if (Yuni::Math::Zero(weekHydroGen)) + { + for (uint h = 0; h != nbHoursInWeek; ++h) + { + maxMrgOut[h] = in.spillage[h] + in.dtgMargin[h] - in.dens[h]; + } + return; + } + + // Initialisation + std::vector OI(nbHoursInWeek); + for (uint h = 0; h != nbHoursInWeek; ++h) + { + OI[h] = in.spillage[h] + in.dtgMargin[h] - in.dens[h]; + } + + // Following block could be replaced with : + // double bottom = *std::min_element(OI.begin(), OI.end()); + // double top = *std::max_element(OI.begin(), OI.end()); + double bottom = +std::numeric_limits::max(); + double top = 0; + for (uint i = 0; i != nbHoursInWeek; ++i) + { + double oii = OI[i]; + if (oii > top) + { + top = oii; + } + if (oii < bottom) + { + bottom = oii; + } + } + + double ecart = 1.; + uint loop = 100; // arbitrary - maximum number of iterations + + do + { + double niveau = (top + bottom) * 0.5; + double SP = 0; // S+ + double SM = 0; // S- + + for (uint h = 0; h != nbHoursInWeek; ++h) + { + assert(h < HOURS_PER_YEAR && "calendar overflow"); + if (niveau > OI[h]) + { + maxMrgOut[h] = Math::Min(niveau, + OI[h] + + in.maxHourlyGenPower->getCoefficient(in.year, + h + in.hourInYear) + - in.hydroGeneration[h]); + SM += maxMrgOut[h] - OI[h]; + } + else + { + maxMrgOut[h] = Math::Max(niveau, OI[h] - in.hydroGeneration[h]); + SP += OI[h] - maxMrgOut[h]; + } + } + + ecart = SP - SM; + if (ecart > 0) + { + bottom = niveau; + } + else + { + top = niveau; + } + + if (!--loop) + { + logs.error() << "OP.MRG: " << in.areaName + << ": infinite loop detected. please check input data"; + return; + } + } while (ecart * ecart > 0.25); +} + +} // namespace Antares::Solver::Variable::Economy diff --git a/src/tests/src/libs/antares/writer/test_zip_writer.cpp b/src/tests/src/libs/antares/writer/test_zip_writer.cpp new file mode 100644 index 0000000000..ffa0d89fed --- /dev/null +++ b/src/tests/src/libs/antares/writer/test_zip_writer.cpp @@ -0,0 +1,127 @@ +/* +** Copyright 2007-2023 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#define BOOST_TEST_MODULE test - writer tests + +#include +#include + +#include "yuni/job/queue/service.h" + +#include "antares/benchmarking/DurationCollector.h" +#include "antares/writer/i_writer.h" +#include "antares/writer/writer_factory.h" + +#include "utils.h" + +extern "C" +{ +#include +#include +#include +#include +} + +using namespace Yuni::Job; +using Antares::Solver::IResultWriter; +using Benchmarking::IDurationCollector; +using Benchmarking::NullDurationCollector; + +// Handles lifetime of necessary objects +struct TestContext +{ + std::shared_ptr threadPool; + std::unique_ptr durationCollector; + std::shared_ptr writer; +}; + +std::shared_ptr createThreadPool(int size) +{ + auto threadPool = std::make_shared(); + threadPool->maximumThreadCount(size); + threadPool->start(); + return threadPool; +} + +std::string removeExtension(const std::string& name, const std::string& ext) +{ + int length = name.size(); + if (name.size() > ext.size() && name.substr(length - ext.size()) == ext) + { + return name.substr(0, length - ext.size()); + } + return name; +} + +TestContext createContext(const std::filesystem::path zipPath, int threadCount) +{ + auto threadPool = createThreadPool(threadCount); + std::unique_ptr + durationCollector = std::make_unique(); + std::string archiveName = zipPath.string(); + auto writer = Antares::Solver::resultWriterFactory(Antares::Data::zipArchive, + removeExtension(zipPath.string(), ".zip"), + threadPool, + *durationCollector); + return {threadPool, std::move(durationCollector), writer}; +} + +using ZipReaderHandle = void*; + +void checkZipContent(ZipReaderHandle handle, + const std::string& path, + const std::string& expectedContent) +{ + BOOST_CHECK(mz_zip_reader_locate_entry(handle, path.c_str(), 0) == MZ_OK); + BOOST_CHECK(mz_zip_reader_entry_open(handle) == MZ_OK); + char buffer[4096]; + int bytesRead = mz_zip_reader_entry_read(handle, buffer, sizeof(buffer)); + std::string stringRead(buffer, bytesRead); + BOOST_CHECK(stringRead == expectedContent); + mz_zip_reader_entry_close(handle); +} + +BOOST_AUTO_TEST_CASE(test_zip) +{ + // Writer some content to test.zip, possibly from 2 threads + auto working_tmp_dir = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + auto zipPath = working_tmp_dir / "test.zip"; + auto context = createContext(zipPath, 2); + std::string content1 = "test-content1"; + std::string content2 = "test-content2"; + context.writer->addEntryFromBuffer("test-path", content1); + context.writer->addEntryFromBuffer("test-second-path", content2); + context.writer->flush(); + context.writer->finalize(true); + + // Check content is correct + ZipReaderHandle readerHandle = mz_zip_reader_create(); + std::string zipPathStr = zipPath.string(); + BOOST_CHECK(mz_zip_reader_open_file(readerHandle, zipPathStr.c_str()) == MZ_OK); + checkZipContent(readerHandle, "test-path", "test-content1"); + checkZipContent(readerHandle, "test-second-path", "test-content2"); + mz_zip_reader_close(readerHandle); +} diff --git a/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt b/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt index 1633966826..dd9ef54891 100644 --- a/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt +++ b/src/tests/src/libs/antares/yaml-parser/CMakeLists.txt @@ -1,6 +1,4 @@ - -find_package(yaml-cpp CONFIG REQUIRED) -Set(SRCS test_yaml_parser.cpp +set(SRCS test_yaml_parser.cpp ) set(execname "yaml-parser-test") @@ -8,8 +6,7 @@ add_executable(${execname} ${SRCS}) target_link_libraries(${execname} PRIVATE yaml-cpp - Boost::unit_test_framework - ) + Boost::unit_test_framework) add_test(NAME yaml-parser COMMAND ${execname}) diff --git a/src/tests/src/solver/modeler/CMakeLists.txt b/src/tests/src/solver/modeler/CMakeLists.txt index d532a0a77d..b5d0efe1f8 100644 --- a/src/tests/src/solver/modeler/CMakeLists.txt +++ b/src/tests/src/solver/modeler/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(api) add_subdirectory(loadFiles) +add_subdirectory(parameters) diff --git a/src/tests/src/solver/modeler/parameters/CMakeLists.txt b/src/tests/src/solver/modeler/parameters/CMakeLists.txt new file mode 100644 index 0000000000..3e989c2b03 --- /dev/null +++ b/src/tests/src/solver/modeler/parameters/CMakeLists.txt @@ -0,0 +1,14 @@ +# Zip writer +add_executable(parse-parameters testParametersParsing.cpp) + +target_link_libraries(parse-parameters + PRIVATE + Boost::unit_test_framework + modeler-parameters + test_utils_unit +) + +set_target_properties(parse-parameters PROPERTIES FOLDER Unit-tests/test-writer) + +add_test(NAME parse-parameters COMMAND parse-parameters) +set_tests_properties(parse-parameters PROPERTIES LABELS unit) diff --git a/src/tests/src/solver/modeler/parameters/testParametersParsing.cpp b/src/tests/src/solver/modeler/parameters/testParametersParsing.cpp new file mode 100644 index 0000000000..aa2b14732b --- /dev/null +++ b/src/tests/src/solver/modeler/parameters/testParametersParsing.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Antares_Simulator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ +#define WIN32_LEAN_AND_MEAN + +#include +#define BOOST_TEST_MODULE parse modeler parameters + +#include + +#include + +#include "files-system.h" + +BOOST_AUTO_TEST_SUITE(read_modeler_parameters) + +BOOST_AUTO_TEST_CASE(all_properties_set) +{ + const auto working_tmp_dir = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + const auto fileP = working_tmp_dir / "parameters.yaml"; + { + std::ofstream param(fileP); + param << R"( +solver: sirius +solver-logs: false +solver-parameters: PRESOLVE 1 +no-output: true)"; + } + + auto params = Antares::Solver::parseModelerParameters(fileP); + BOOST_CHECK_EQUAL(params.solver, "sirius"); + BOOST_CHECK_EQUAL(params.solverLogs, false); + BOOST_CHECK_EQUAL(params.solverParameters, "PRESOLVE 1"); + BOOST_CHECK_EQUAL(params.noOutput, true); +} + +BOOST_AUTO_TEST_CASE(all_properties_set_out_of_order) +{ + const auto working_tmp_dir = CREATE_TMP_DIR_BASED_ON_TEST_NAME(); + const auto fileP = working_tmp_dir / "parameters.yaml"; + { + std::ofstream param(fileP); + param << R"( +solver-logs: false +solver: sirius +solver-parameters: PRESOLVE 1 +no-output: true)"; + } + + auto params = Antares::Solver::parseModelerParameters(fileP); + BOOST_CHECK_EQUAL(params.solver, "sirius"); + BOOST_CHECK_EQUAL(params.solverLogs, false); + BOOST_CHECK_EQUAL(params.solverParameters, "PRESOLVE 1"); + BOOST_CHECK_EQUAL(params.noOutput, true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/src/solver/optimisation/adequacy_patch.cpp b/src/tests/src/solver/optimisation/adequacy_patch.cpp new file mode 100644 index 0000000000..f6c5fddeb0 --- /dev/null +++ b/src/tests/src/solver/optimisation/adequacy_patch.cpp @@ -0,0 +1,386 @@ +#define BOOST_TEST_MODULE test adequacy patch functions + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include + +#include + +#include +#include "antares/study/parameters/adq-patch-params.h" + +#include "adequacy_patch_csr/adq_patch_curtailment_sharing.h" +#include "adequacy_patch_local_matching/adq_patch_local_matching.h" + +static double origineExtremite = -1; +static double extremiteOrigine = 5; + +using namespace Antares::Data::AdequacyPatch; +namespace tt = boost::test_tools; + +static const double flowArea0toArea1_positive = 10; +static const double flowArea0toArea1_negative = -10; +static const double flowArea2toArea0_positive = 30; +static const double flowArea2toArea0_negative = -30; +static const double positiveEnsInit = 50.0; + +std::pair calculateAreaFlowBalanceForOneTimeStep( + double ensInit, + bool includeFlowsOutsideAdqPatchToDensNew, + AdequacyPatchMode Area1Mode, + AdequacyPatchMode Area2Mode, + double flowToArea1, + double flowFromArea2) +{ + PROBLEME_HEBDO problem; + int Area = 0; + uint hour = 0; + + // allocate memory + problem.adequacyPatchRuntimeData = std::make_shared(); + problem.adequacyPatchRuntimeData->originAreaMode.resize(3); + problem.adequacyPatchRuntimeData->extremityAreaMode.resize(3); + + AdqPatchParams adqPatchParams; + + problem.ResultatsHoraires.resize(1); + problem.ResultatsHoraires[0].ValeursHorairesDeDefaillancePositive = std::vector(1); + problem.ValeursDeNTC = std::vector(1); + problem.ValeursDeNTC[0].ValeurDuFlux = std::vector(3); + problem.IndexSuivantIntercoOrigine = std::vector(3); + problem.IndexSuivantIntercoExtremite = std::vector(3); + problem.IndexDebutIntercoOrigine = std::vector(1); + problem.IndexDebutIntercoExtremite = std::vector(1); + + // input values + adqPatchParams.localMatching.setToZeroOutsideInsideLinks + = !includeFlowsOutsideAdqPatchToDensNew; + problem.ResultatsHoraires[Area].ValeursHorairesDeDefaillancePositive[hour] = ensInit; + int Interco = 1; + problem.IndexDebutIntercoOrigine[Area] = Interco; + problem.adequacyPatchRuntimeData->extremityAreaMode[Interco] = Area1Mode; + problem.ValeursDeNTC[hour].ValeurDuFlux[Interco] = flowToArea1; + problem.IndexSuivantIntercoOrigine[Interco] = -1; + + Interco = 2; + problem.IndexDebutIntercoExtremite[Area] = Interco; + problem.adequacyPatchRuntimeData->originAreaMode[Interco] = Area2Mode; + problem.ValeursDeNTC[hour].ValeurDuFlux[Interco] = flowFromArea2; + problem.IndexSuivantIntercoExtremite[Interco] = -1; + + // get results + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew, std::ignore) = calculateAreaFlowBalance( + &problem, + adqPatchParams.localMatching.setToZeroOutsideInsideLinks, + Area, + hour); + + return std::make_pair(netPositionInit, densNew); +} + +AdqPatchParams createParams() +{ + AdqPatchParams p; + p.enabled = true; + p.curtailmentSharing.includeHurdleCost = true; + p.curtailmentSharing.priceTakingOrder = AdqPatchPTO::isDens; + + return p; +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 virtual-area, and Area2-virtual area +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_virtual_virtual_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + virtualArea, + virtualArea, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, 0.0); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2-virtual area +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_virtual_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + physicalAreaInsideAdqPatch, + virtualArea, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2-virtual area +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +// ensInit = 50.0 +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_virtual_NotIncludeOut_positiveFlow_ensInitGraterThanZero) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + positiveEnsInit, + false, + physicalAreaInsideAdqPatch, + virtualArea, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, positiveEnsInit - flowArea0toArea1_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_outside_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + physicalAreaInsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter should NOT include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_inside_inside_NotIncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + false, + physicalAreaInsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_positive + flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_inside_outside_IncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaInsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_positive + flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_outside_IncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, 0.0); + BOOST_CHECK_EQUAL(densNew, flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positive +// flow from Area2 -> Area0 is positive +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_positiveFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(netPositionInit, +flowArea2toArea0_positive); + BOOST_CHECK_EQUAL(densNew, +flowArea2toArea0_positive); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is negative +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_inside_outside_IncludeOut_negativeFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaInsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_negative, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_negative); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_negative); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area outside adq-patch +// flow from Area0 -> Area1 is negative +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_outside_IncludeOut_negativeFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaOutsideAdqPatch, + flowArea0toArea1_negative, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, 0.0); + BOOST_CHECK_EQUAL(densNew, -flowArea0toArea1_negative); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area outside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is negative +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +BOOST_AUTO_TEST_CASE(calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_negativeFlow) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 0.0, + true, + physicalAreaOutsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_negative, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positiive +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +// ensInit = 50.0 +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_negativeFlow_initEnsEqualTo50) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + positiveEnsInit, + true, + physicalAreaInsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(densNew, positiveEnsInit + netPositionInit); +} + +// Area 0 is physical area inside adq-patch connected to two areas: +// Area1 physical area inside adq-patch, and Area2 physical area inside adq-patch +// flow from Area0 -> Area1 is positiive +// flow from Area2 -> Area0 is negative +// DensNew parameter SHOULD include flows from areas outside adq patch +// ensInit = 2.0 +BOOST_AUTO_TEST_CASE( + calculateAreaFlowBalanceForOneTimeStep_outside_inside_IncludeOut_negativeFlow_initEnsEqualTo0) +{ + double netPositionInit; + double densNew; + std::tie(netPositionInit, densNew) = calculateAreaFlowBalanceForOneTimeStep( + 2.0, + true, + physicalAreaInsideAdqPatch, + physicalAreaInsideAdqPatch, + flowArea0toArea1_positive, + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(netPositionInit, -flowArea0toArea1_positive + flowArea2toArea0_negative); + BOOST_CHECK_EQUAL(densNew, 0.0); +} + +BOOST_AUTO_TEST_CASE(check_valid_adq_param) +{ + auto p = createParams(); + BOOST_CHECK_NO_THROW( + p.checkAdqPatchSimulationModeEconomyOnly(Antares::Data::SimulationMode::Economy)); + BOOST_CHECK_NO_THROW(p.checkAdqPatchIncludeHurdleCost(true)); +} + +BOOST_AUTO_TEST_CASE(check_adq_param_wrong_mode) +{ + auto p = createParams(); + BOOST_CHECK_THROW(p.checkAdqPatchSimulationModeEconomyOnly( + Antares::Data::SimulationMode::Adequacy), + Error::IncompatibleSimulationModeForAdqPatch); +} + +BOOST_AUTO_TEST_CASE(check_adq_param_wrong_hurdle_cost) +{ + auto p = createParams(); + BOOST_CHECK_THROW(p.checkAdqPatchIncludeHurdleCost(false), Error::IncompatibleHurdleCostCSR); +} diff --git a/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp b/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp index ea9762838b..9f716ecb77 100644 --- a/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp +++ b/src/tests/src/solver/optimisation/adequacy_patch/adequacy_patch.cpp @@ -31,7 +31,6 @@ #include #include #include "antares/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.h" -#include "antares/solver/optimisation/adequacy_patch_csr/post_processing.h" #include "antares/study/parameters/adq-patch-params.h" static double origineExtremite = -1; @@ -402,75 +401,3 @@ BOOST_AUTO_TEST_CASE(check_adq_param_wrong_hurdle_cost) auto p = createParams(); BOOST_CHECK_THROW(p.checkAdqPatchIncludeHurdleCost(false), Error::IncompatibleHurdleCostCSR); } - -BOOST_AUTO_TEST_SUITE(adq_patch_post_processing) - -BOOST_AUTO_TEST_CASE(dtg_mrg_triggered_low_ens) -{ - const bool triggered = true; - const double dtgMrg = 32.; - const double ens = 21.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == 11., tt::tolerance(1.e-6)); - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == std::abs(dtgMrg - ens), - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(dtg_mrg_triggered_high_ens) -{ - const bool triggered = true; - const double dtgMrg = 32.; - const double ens = 42.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == 0., tt::tolerance(1.e-6)); - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == std::abs(dtgMrg - ens), - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(dtg_mrg_not_triggered_low_ens) -{ - const bool triggered = false; - const double dtgMrg = 32.; - const double ens = 21.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == dtgMrg, tt::tolerance(1.e-6)); - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == dtgMrg + ens, - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(dtg_mrg_not_triggered_high_ens) -{ - const bool triggered = false; - const double dtgMrg = 32.; - const double ens = 42.; - - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) == dtgMrg, tt::tolerance(1.e-6)); - BOOST_TEST(recomputeDTG_MRG(triggered, dtgMrg, ens) + recomputeENS_MRG(triggered, dtgMrg, ens) - == dtgMrg + ens, - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(mrgprice_high_enscsr) -{ - const double ensCsr = 21.; - const double originalCost = 3.; - const double unsuppliedEnergyCost = 1000.; - BOOST_TEST(recomputeMRGPrice(ensCsr, originalCost, unsuppliedEnergyCost) - == -unsuppliedEnergyCost, - tt::tolerance(1.e-6)); -} - -BOOST_AUTO_TEST_CASE(mrgprice_low_enscsr) -{ - const double ensCsr = 0.; - const double originalCost = 3.; - const double unsuppliedEnergyCost = 1000.; - BOOST_TEST(recomputeMRGPrice(ensCsr, originalCost, unsuppliedEnergyCost) == originalCost, - tt::tolerance(1.e-6)); -} -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/ui/simulator/application/main/create.cpp b/src/ui/simulator/application/main/create.cpp index 03d3f57a71..bb2182d148 100644 --- a/src/ui/simulator/application/main/create.cpp +++ b/src/ui/simulator/application/main/create.cpp @@ -293,7 +293,13 @@ void ApplWnd::internalInitialize() statusbar->SetStatusStyles(2, styles); statusbar->SetMinHeight(14); - statusbar->SetStatusText(wxT(""), 1); + statusbar->Connect(statusbar->GetId(), + wxEVT_CONTEXT_MENU, + wxContextMenuEventHandler(ApplWnd::evtOnContextMenuStatusBar), + nullptr, + this); + + statusbar->SetStatusText(wxT("| "), 1); wxFont f = statusbar->GetFont(); f.SetPointSize(f.GetPointSize() - 1); diff --git a/src/ui/simulator/application/main/internal-ids.h b/src/ui/simulator/application/main/internal-ids.h index 33d8f6f764..57893d5187 100644 --- a/src/ui/simulator/application/main/internal-ids.h +++ b/src/ui/simulator/application/main/internal-ids.h @@ -172,6 +172,16 @@ enum MenusID mnIDLaunchAnalyzer, mnIDLaunchConstraintsBuilder, + //! \name Popup Menu Operator for selected cells on any grid + //@{ + mnIDPopupOpNone, + mnIDPopupOpAverage, + mnIDPopupOpCellCount, + mnIDPopupOpMinimum, + mnIDPopupOpMaximum, + mnIDPopupOpSum, + //@} + //! \name Popup Menu Operator for selected nodes on any layer //@{ mnIDPopupSelectionHide, diff --git a/src/ui/simulator/application/main/main.cpp b/src/ui/simulator/application/main/main.cpp index 137b009c2b..38131d6552 100644 --- a/src/ui/simulator/application/main/main.cpp +++ b/src/ui/simulator/application/main/main.cpp @@ -179,6 +179,14 @@ EVT_MENU(mnInternalLogMessage, ApplWnd::onLogMessage) EVT_MENU(mnIDLaunchAnalyzer, ApplWnd::evtLaunchAnalyzer) EVT_MENU(mnIDLaunchConstraintsBuilder, ApplWnd::evtLaunchConstraintsBuilder) +// Context menu : Operator for selected cells (grid) +EVT_MENU(mnIDPopupOpNone, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpAverage, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpCellCount, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpMinimum, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpMaximum, ApplWnd::evtOnContextMenuChangeOperator) +EVT_MENU(mnIDPopupOpSum, ApplWnd::evtOnContextMenuChangeOperator) + EVT_MENU_OPEN(ApplWnd::evtOnMenuOpen) EVT_MENU_CLOSE(ApplWnd::evtOnMenuClose) @@ -245,6 +253,8 @@ ApplWnd::ApplWnd() : pageRenewableCommon(nullptr), pageNodalOptim(nullptr), pWndBindingConstraints(nullptr), + pGridSelectionOperator(new Component::Datagrid::Selection::CellCount()), + pGridSelectionAttachedGrid(nullptr), pMapContextMenu(nullptr), pUserNotes(nullptr), pMainNotebookAlreadyHasItsComponents(false), @@ -308,6 +318,13 @@ ApplWnd::~ApplWnd() OnStudyAreasChanged.clear(); OnStudyAreaDelete.clear(); + // Delete the grid operator + if (pGridSelectionOperator) + { + delete pGridSelectionOperator; + pGridSelectionOperator = nullptr; // May be needed in some cases + } + // Disconnect all events destroyBoundEvents(); // Unregister the global pointer to the instance @@ -346,6 +363,34 @@ void ApplWnd::selectSystem() pNotebook->select(wxT("sys"), true); } +void ApplWnd::evtOnContextMenuChangeOperator(wxCommandEvent& evt) +{ + switch (evt.GetId()) + { + case mnIDPopupOpNone: + gridOperatorSelectedCells(nullptr); + break; + case mnIDPopupOpAverage: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Average()); + break; + case mnIDPopupOpCellCount: + gridOperatorSelectedCells(new Component::Datagrid::Selection::CellCount()); + break; + case mnIDPopupOpMinimum: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Minimum()); + break; + case mnIDPopupOpMaximum: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Maximum()); + break; + case mnIDPopupOpSum: + gridOperatorSelectedCells(new Component::Datagrid::Selection::Sum()); + break; + default: + break; + } + evt.Skip(); +} + static inline void EnableItem(wxMenuBar* menu, int id, bool opened) { auto* item = menu->FindItem(id); @@ -494,6 +539,7 @@ void ApplWnd::evtOnUpdateGUIAfterStudyIO(bool opened) // Reset the status bar resetDefaultStatusBarText(); + gridOperatorSelectedCellsUpdateResult(pGridSelectionAttachedGrid); // reload the user notes and districts if (not aboutToQuit and study) @@ -519,6 +565,7 @@ void ApplWnd::evtOnUpdateGUIAfterStudyIO(bool opened) { GetSizer()->Clear(true); pUserNotes = nullptr; + pGridSelectionAttachedGrid = nullptr; pBigDaddy = nullptr; pMainSizer = nullptr; pData->wipPanel = nullptr; @@ -778,6 +825,24 @@ void ApplWnd::onRenewableGenerationModellingChanged(bool init) refreshInputMenuOnRenewableModellingChanged(aggregated); } +void ApplWnd::gridOperatorSelectedCells(Component::Datagrid::Selection::IOperator* v) +{ + delete pGridSelectionOperator; + pGridSelectionOperator = v; + gridOperatorSelectedCellsUpdateResult(pGridSelectionAttachedGrid); +} + +Component::Datagrid::Selection::IOperator* ApplWnd::gridOperatorSelectedCells() const +{ + return pGridSelectionOperator; +} + +void ApplWnd::disableGridOperatorIfGrid(wxGrid* grid) +{ + if (pGridSelectionAttachedGrid == grid) + gridOperatorSelectedCellsUpdateResult(nullptr); +} + void ApplWnd::title() { assert(wxIsMainThread() == true and "Must be ran from the main thread"); diff --git a/src/ui/simulator/application/main/main.h b/src/ui/simulator/application/main/main.h index 0b1f9dd8bb..7b7683805e 100644 --- a/src/ui/simulator/application/main/main.h +++ b/src/ui/simulator/application/main/main.h @@ -26,6 +26,7 @@ #include #include "../../toolbox/components/notebook/notebook.h" +#include "../../toolbox/components/datagrid/selectionoperation.h" #include "../../toolbox/components/map/settings.h" #include #include "fwd.h" @@ -105,6 +106,41 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent */ Map::Component* map() const; + /*! + ** \name Grid operator (for selected cells) + ** + ** A grid operator computes an operation (Sum, average...) on all selected + ** cells of the grid that currently has the focus. The result of this + ** computation is displayed in the status bar. + ** + ** \see Antares::Component::Datagrid::Component::onGridEnter() + ** \see Antares::Component::Datagrid::Component::onGridLeave() + */ + //@{ + /*! + ** \brief Get the current grid operator for selected cells + */ + Component::Datagrid::Selection::IOperator* gridOperatorSelectedCells() const; + + /*! + ** \brief Set the grid operator for selected cells + */ + void gridOperatorSelectedCells(Component::Datagrid::Selection::IOperator* v); + + /*! + ** \brief Update the GUI to display the result of the grid operator + ** + ** This method should be called each time the cells selection changes. + ** \param grid The `wxGrid` that has currently the focus (may be NULL) + */ + void gridOperatorSelectedCellsUpdateResult(wxGrid* grid); + + /*! + ** \brief Disable the grid operator + */ + void disableGridOperatorIfGrid(wxGrid* grid); + //@} + //! \name Title of the Window //@{ void title(); @@ -327,6 +363,8 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent //! Create a complete menu for the window wxMenuBar* createMenu(); + //! Create a popup menu for all available operators on selected cells (grid) + wxMenu* createPopupMenuOperatorsOnGrid(); //! Create menu: File wxMenu* createMenuFiles(); @@ -398,6 +436,9 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent //! \name Event: Context menu //@{ + //! Show the context menu associated to the status bar + void evtOnContextMenuStatusBar(wxContextMenuEvent& evt); + void evtOnContextMenuChangeOperator(wxCommandEvent& evt); void evtOnContextMenuMap(int x, int y); //@} @@ -696,6 +737,10 @@ class ApplWnd final : public Component::Frame::WxLocalFrame, public Yuni::IEvent Component::Notebook::Page* pageScBuilderHydroInitialLevels; Component::Notebook::Page* pageScBuilderHydroFinalLevels; + //! The current grid operator to use on selected cells + Component::Datagrid::Selection::IOperator* pGridSelectionOperator; + wxGrid* pGridSelectionAttachedGrid; + //! A context menu for the map wxMenu* pMapContextMenu; diff --git a/src/ui/simulator/application/main/menu.cpp b/src/ui/simulator/application/main/menu.cpp index 11121bce3c..619269b283 100644 --- a/src/ui/simulator/application/main/menu.cpp +++ b/src/ui/simulator/application/main/menu.cpp @@ -64,6 +64,21 @@ wxMenuBar* ApplWnd::createMenu() return ret; } +wxMenu* ApplWnd::createPopupMenuOperatorsOnGrid() +{ + auto* menu = new wxMenu(); + + // Wizard + Menu::CreateItem(menu, mnIDPopupOpNone, wxT("None"), "images/16x16/empty.png"); + menu->AppendSeparator(); + Menu::CreateItem(menu, mnIDPopupOpAverage, wxT("Average ")); + Menu::CreateItem(menu, mnIDPopupOpCellCount, wxT("Cell count ")); + Menu::CreateItem(menu, mnIDPopupOpMinimum, wxT("Minimum ")); + Menu::CreateItem(menu, mnIDPopupOpMaximum, wxT("Maximum ")); + Menu::CreateItem(menu, mnIDPopupOpSum, wxT("Sum ")); + return menu; +} + wxMenu* ApplWnd::createMenuFiles() { delete pMenuFile; diff --git a/src/ui/simulator/application/main/statusbar.cpp b/src/ui/simulator/application/main/statusbar.cpp index a12dd4a290..ed33372524 100644 --- a/src/ui/simulator/application/main/statusbar.cpp +++ b/src/ui/simulator/application/main/statusbar.cpp @@ -22,17 +22,16 @@ #include #include "main.h" #include "../../windows/version.h" -#include "antares/study/study.h" -// Datagrid -#include "../../toolbox/components/datagrid/component.h" +#include "../study.h" #include "../../toolbox/components/datagrid/gridhelper.h" #include #include -namespace Antares -{ -namespace Forms +using namespace Component::Datagrid; + +namespace Antares::Forms { + void ApplWnd::resetDefaultStatusBarText() { assert(wxIsMainThread() == true && "Must be ran from the main thread"); @@ -41,5 +40,216 @@ void ApplWnd::resetDefaultStatusBarText() #endif } -} // namespace Forms -} // namespace Antares + +bool oneCellSelected(wxGrid& grid) +{ + const wxGridCellCoordsArray& cells(grid.GetSelectedCells()); + return cells.size() > 0; +} + +size_t updateStatisticsOpForOneCell(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + const wxGridCellCoordsArray& cells(grid.GetSelectedCells()); + for (uint i = 0; i < (uint)cells.size(); ++i) + { + auto& cell = cells[i]; + op->appendValue(gridHelper->GetNumericValue(cell.GetRow(), cell.GetCol())); + ++totalCell; + } + return totalCell; +} + +bool rowsSelected(wxGrid& grid) +{ + const wxArrayInt& rows(grid.GetSelectedRows()); + return rows.size() > 0; +} + +size_t updateStatisticsOpForRows(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + int colCount = grid.GetNumberCols(); + const wxArrayInt& rows(grid.GetSelectedRows()); + for (uint i = 0; i < (uint)rows.size(); ++i) + { + for (int col = 0; col < colCount; ++col) + { + op->appendValue(gridHelper->GetNumericValue(rows[i], col)); + ++totalCell; + } + } + return totalCell; +} + +bool columnsSelected(wxGrid& grid) +{ + const wxArrayInt& cols(grid.GetSelectedCols()); + return cols.size() > 0; +} + +size_t updateStatisticsOpForColumns(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + int rowCount = grid.GetNumberRows(); + const wxArrayInt& cols(grid.GetSelectedCols()); + for (uint i = 0; i < (uint)cols.size(); ++i) + { + for (int row = 0; row < rowCount; ++row) + { + op->appendValue(gridHelper->GetNumericValue(row, cols[i])); + ++totalCell; + } + } + return totalCell; +} + +bool blockSelected(wxGrid& grid) +{ + // Blocks. We always expect blocks top left and bottom right to have the same size, since their + // entries are supposed to correspond. + const wxGridCellCoordsArray& blockTopLeft(grid.GetSelectionBlockTopLeft()); + const wxGridCellCoordsArray& blockBottomRight(grid.GetSelectionBlockBottomRight()); + return (blockTopLeft.size() == blockBottomRight.size()) && (blockTopLeft.size() > 0); +} + +size_t updateStatisticsOpForBlock(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + size_t totalCell = 0; + const wxGridCellCoordsArray& blockTopLeft(grid.GetSelectionBlockTopLeft()); + const wxGridCellCoordsArray& blockBottomRight(grid.GetSelectionBlockBottomRight()); + size_t blockSize = blockTopLeft.size(); + + for (uint i = 0; i < blockSize; ++i) + { + const wxGridCellCoords& topLeft = blockTopLeft[i]; + const wxGridCellCoords& bottomRight = blockBottomRight[i]; + for (int row = topLeft.GetRow(); row <= bottomRight.GetRow(); ++row) + { + for (int col = topLeft.GetCol(); col <= bottomRight.GetCol(); ++col) + { + op->appendValue(gridHelper->GetNumericValue(row, col)); + ++totalCell; + } + } + } + return totalCell; +} + +/* +** Applies a functor to all selected cells. Returns the number of selected +** cells. +*/ +static size_t applyOperatorOnSelectedCells(wxGrid& grid, + VGridHelper* gridHelper, + Selection::IOperator* op) +{ + assert(wxIsMainThread() == true and "Must be ran from the main thread"); + + if (oneCellSelected(grid)) + { + return updateStatisticsOpForOneCell(grid, gridHelper, op); + } + + if (rowsSelected(grid)) + { + return updateStatisticsOpForRows(grid, gridHelper, op); + } + + if (columnsSelected(grid)) + { + return updateStatisticsOpForColumns(grid, gridHelper, op); + } + + if (blockSelected(grid)) + { + return updateStatisticsOpForBlock(grid, gridHelper, op); + } + + return 0; +} + +void ApplWnd::gridOperatorSelectedCellsUpdateResult(wxGrid* grid) +{ + assert(wxIsMainThread() == true and "Must be ran from the main thread"); + enum + { + fieldIndex = 1, + }; + // The status bar + auto* statusBar = GetStatusBar(); + pGridSelectionAttachedGrid = grid; + if (statusBar) + { + if (not CurrentStudyIsValid()) + { + statusBar->SetStatusText(wxEmptyString, fieldIndex); + return; + } + if (not pGridSelectionOperator) + { + statusBar->SetStatusText(wxT("| (none)"), fieldIndex); + return; + } + + if (grid and grid->GetTable()) + { + auto* gridHelper = dynamic_cast(grid->GetTable()); + if (gridHelper) + { + // Reset of the operator + pGridSelectionOperator->reset(); + // Browse all selected cells + if (applyOperatorOnSelectedCells(*grid, gridHelper, pGridSelectionOperator)) + { + // Update the GUI + statusBar->SetStatusText(wxString() + << wxT("| ") << pGridSelectionOperator->caption() + << wxT(" = ") << pGridSelectionOperator->result(), + fieldIndex); + return; + } + } + } + // Empty + statusBar->SetStatusText( + wxString(wxT("| (")) << pGridSelectionOperator->caption() << wxT(')'), fieldIndex); + } +} + +void ApplWnd::evtOnContextMenuStatusBar(wxContextMenuEvent& evt) +{ + if (GUIIsLock()) + return; + + wxStatusBar* statusBar = GetStatusBar(); + if (statusBar) + { + wxRect rect; + if (statusBar->GetFieldRect(1, rect)) + { + const wxPoint pos = statusBar->ScreenToClient(evt.GetPosition()); + if (rect.Contains(pos)) + { + if (!pPopupMenuOperatorsGrid) + { + // Popup menu: Operators for selected cells on any grid + pPopupMenuOperatorsGrid = createPopupMenuOperatorsOnGrid(); + } + + statusBar->PopupMenu(pPopupMenuOperatorsGrid); + } + } + } + evt.Skip(); +} + +} // namespace Antares::Forms \ No newline at end of file diff --git a/src/ui/simulator/cmake/components.cmake b/src/ui/simulator/cmake/components.cmake index 0586928a48..df72d40b83 100644 --- a/src/ui/simulator/cmake/components.cmake +++ b/src/ui/simulator/cmake/components.cmake @@ -48,6 +48,7 @@ SET(SRC_TOOLBOX_COM_DBGRID_RENDERERS toolbox/components/datagrid/renderer.h toolbox/components/datagrid/renderer.hxx toolbox/components/datagrid/renderer.cpp + toolbox/components/datagrid/selectionoperation.h toolbox/components/datagrid/renderer/adequacy-patch-area-grid.h toolbox/components/datagrid/renderer/adequacy-patch-area-grid.cpp toolbox/components/datagrid/renderer/area.h diff --git a/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp b/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp index dc8624172e..84fe490800 100644 --- a/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp +++ b/src/ui/simulator/toolbox/components/datagrid/dbgrid.cpp @@ -120,6 +120,11 @@ DBGrid::DBGrid(Component* parent) : DBGrid::~DBGrid() { + // Remove any remaining reference + auto* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->disableGridOperatorIfGrid(this); + pParentComponent = nullptr; otherGrid_ = nullptr; } @@ -128,6 +133,10 @@ void DBGrid::onGridSelectCell(wxGridEvent& evt) { assert(GetParent() && "invalid parent"); + auto* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->gridOperatorSelectedCellsUpdateResult(this); + pCurrentPosition.x = evt.GetCol(); pCurrentPosition.y = evt.GetRow(); auto* r = ((Component*)GetParent())->renderer(); @@ -141,6 +150,9 @@ void DBGrid::onGridRangeSelect(wxGridRangeSelectEvent& evt) { assert(GetGridWindow() && "invalid grid window"); + Forms::ApplWnd* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->gridOperatorSelectedCellsUpdateResult(this); if (GetGridWindow()) GetGridWindow()->SetFocus(); evt.Skip(); @@ -148,6 +160,9 @@ void DBGrid::onGridRangeSelect(wxGridRangeSelectEvent& evt) void DBGrid::onGridLeave(wxFocusEvent& evt) { + auto* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->gridOperatorSelectedCellsUpdateResult(nullptr); evt.Skip(); } @@ -202,6 +217,10 @@ void DBGrid::ensureDataAreLoaded() r->invalidate = false; // Post an event to update the gui after the data are loaded + Forms::ApplWnd* mainFrm = Forms::ApplWnd::Instance(); + if (mainFrm) + mainFrm->disableGridOperatorIfGrid(this); + assert(pAllowRefresh == true); parent->forceRefresh(); if (GetTable()) diff --git a/src/ui/simulator/toolbox/components/datagrid/selectionoperation.h b/src/ui/simulator/toolbox/components/datagrid/selectionoperation.h new file mode 100644 index 0000000000..a7deebcf32 --- /dev/null +++ b/src/ui/simulator/toolbox/components/datagrid/selectionoperation.h @@ -0,0 +1,247 @@ +/* +** Copyright 2007-2018 RTE +** Authors: Antares_Simulator Team +** +** This file is part of Antares_Simulator. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** There are special exceptions to the terms and conditions of the +** license as they are applied to this software. View the full text of +** the exceptions in file COPYING.txt in the directory of this software +** distribution +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Antares_Simulator. If not, see . +** +** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions +*/ +#ifndef __ANTARES_TOOLBOX_COMPONENT_DATAGRID_SELECTION_OPERATION_H__ +#define __ANTARES_TOOLBOX_COMPONENT_DATAGRID_SELECTION_OPERATION_H__ + +#include "wx-wrapper.h" +#include +#include + +namespace Antares +{ +namespace Component +{ +namespace Datagrid +{ +namespace Selection +{ +class IOperator +{ +public: + IOperator() + { + } + virtual ~IOperator() + { + } + + /*! + ** \brief Caption of the operator + */ + virtual const wxChar* caption() const = 0; + + /*! + ** \brief Reset all internal values + */ + virtual void reset() = 0; + + /*! + ** \brief Manage a new value + */ + virtual void appendValue(const double v) = 0; + + /*! + ** \brief Get the result + */ + virtual double result() const = 0; + +}; // class IOperator + +class Average final : public IOperator +{ +public: + Average() : pValue(0.), pCount(0) + { + } + + virtual ~Average() + { + } + + virtual const wxChar* caption() const + { + return wxT("Average"); + } + + virtual void reset() + { + pValue = 0.; + pCount = 0; + } + + virtual void appendValue(const double v) + { + pValue += v; + ++pCount; + } + + virtual double result() const + { + return pValue / (double)pCount; + } + +private: + double pValue; + uint pCount; + +}; // class Average + +class Sum final : public IOperator +{ +public: + Sum() : pValue(0.) + { + } + + virtual const wxChar* caption() const + { + return wxT("Sum"); + } + + virtual void reset() + { + pValue = 0.; + } + + virtual void appendValue(const double v) + { + pValue += v; + } + + virtual double result() const + { + return pValue; + } + +private: + double pValue; + +}; // class Sum + +class CellCount final : public IOperator +{ +public: + CellCount() : pCount(0) + { + } + + virtual const wxChar* caption() const + { + return wxT("Cell Count"); + } + + virtual void reset() + { + pCount = 0; + } + + virtual void appendValue(const double) + { + ++pCount; + } + + virtual double result() const + { + return (double)pCount; + } + +private: + uint pCount; + +}; // class Average + +class Minimum final : public IOperator +{ +public: + Minimum() : pValue(std::numeric_limits::infinity()) + { + } + + virtual const wxChar* caption() const + { + return wxT("Minimum"); + } + + virtual void reset() + { + pValue = std::numeric_limits::infinity(); + } + + virtual void appendValue(const double v) + { + if (v < pValue) + pValue = v; + } + + virtual double result() const + { + return pValue; + } + +private: + double pValue; + +}; // class Sum + +class Maximum final : public IOperator +{ +public: + Maximum() : pValue(-std::numeric_limits::infinity()) + { + } + + virtual const wxChar* caption() const + { + return wxT("Maximum"); + } + virtual void reset() + { + pValue = -std::numeric_limits::infinity(); + } + + virtual void appendValue(const double v) + { + if (v > pValue) + pValue = v; + } + + virtual double result() const + { + return pValue; + } + +private: + double pValue; + +}; // class Sum + +} // namespace Selection +} // namespace Datagrid +} // namespace Component +} // namespace Antares + +#endif // __ANTARES_TOOLBOX_COMPONENT_DATAGRID_SELECTION_OPERATION_H__