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..13c67fe7a0 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) 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/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/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/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/optimisation/CMakeLists.txt b/src/solver/optimisation/CMakeLists.txt index ec98f298e8..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 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/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/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/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/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/sim_alloc_probleme_hebdo.cpp b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp index 78785f0fd3..dd7480671c 100644 --- a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp +++ b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp @@ -394,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/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/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__