From 8dbba7ed47f16025fe90c2543a5a5303f421955b Mon Sep 17 00:00:00 2001 From: HugoKulesza <94374655+HugoKulesza@users.noreply.github.com> Date: Fri, 13 Oct 2023 11:38:54 +0200 Subject: [PATCH] Feature/optimization discrete variables (#670) * glop solver activated intVariable array added to store information on discrete variables * Adding discrete value array in problem creation Renaming intVar into VariablesEntieres Allocation + filling values during optimization variables creation * Giving discrete variables info to solvers * Making use of MakeVar(min, max, isInteger, name) * filling VariablesEntieres complete * Fix xpress identifier, use MIP solver if the problem is MIP * Deactivating heuristic and second optimization (by commenting) Only bringing back reduced costs and dual values when using MIP model * Remove commented second optimization ERRATUM for preceding commit : reduced costs and dual values when using *LP* model * Reactivated heuristics / second optimization Adding an epsilon to int vars to measure stability through heuristics * Use of round function for int vars out of XPRESS * Test : remove epsilon to output of XPRESS Check influence on heuristic * Going back without 2nd optimization Enabling solver output log * remove commented code * Renaming ucHeuristic and ucMILP (resp. to ucFast and ucAccurate) * also renaming selection methods * Adding "milp" parameter as option of optimisation conditionning "VariablesEntieres" to the use of milp parameter * activating 2nd optimization (if not milp) * No NODU smoothing needed for MILP resolution * changed memory required for input with MILP * (first draft) check of ortools if milp model * error messages changes * refactoring yearEndBuildFromThermalClusterIndex separating smoothing and costs computing not activating smoothing when MILP resolution * reactivating NODU smoothing for MILP solve * fix case * Cleaning, renaming after comments Moving isMIP to named_problem * adding methods to compute ON_Min and ON_Max * remove unused variable * Apply clang-format * remaking isMIP into a method * removing const typing * [TEMP] Test log xpress + 1 thread * remove xpress parameters * adding parameters --ortools-parameters="" for specific solver parameters string --ortools-verbose to activate verbosity * [TEMP] removing ortools presolve * Revert "[TEMP] removing ortools presolve" This reverts commit 9500cecaecce71451eb41cba86bf9a9f57291ce7. * changing parameters setting for ortools solvers * 2 solvers kept in memory for optim 1/2 for milp, 2nd optim with min/max fixed for M * [TEMP] individual year objectives * Revert "[TEMP] individual year objectives" This reverts commit 7d6164c2683e4514317f4f15ac17e76f4a7d0643. * Apply clang-format * fix : fixing NODU min and max for ALL timesteps in MILP mode * Fix build, formatting * Remove ortoolsVerbose & ortoolsParamsString * Simplify condition a bit * Fix variable type in MPS writer * [skip ci] Add TODO * Add forgotten data member * Revert state.* * One more fix * Fix build for GUI * Factorize UI code a bit * Move `checkOrtoolsUsage` * Add comment * Restore existing behavior for unitCommitmentMode = ucHeuristicAccurate * Small clean-up * Restore dual values & reduced costs * Renaming * Setup relaxation * Revert "Setup relaxation" This reverts commit 1c6411b1969c7813d3d9b5f83efb00d9e135aa97. * Remove relaxation, use 0 for reduced cost & marginal values * Format * Partial revert * Disable variables requiring dual values / marg costs when ucMode = milp * Add comments * Add comments in `enum UnitCommitmentMode` * Remove obsolete TODO note * Add OutputRetriever::thermalNbUnitsON * Add 2 unit tests with COIN * Remove obsolete TODO note * Fix formatting * Fix formatting * Skip 2nd optimization when solving with integer variables --------- Co-authored-by: Florian OMNES Co-authored-by: Florian Omnes --- docs/reference-guide/13-file-format.md | 11 ++- .../antares/checks/checkLoadedInputData.h | 4 + .../antares/checks/checkLoadedInputData.cpp | 19 +++++ src/libs/antares/exception/LoadingError.cpp | 10 +++ .../antares/exception/LoadingError.hpp | 12 +++ src/libs/antares/study/fwd.cpp | 12 ++- src/libs/antares/study/fwd.h | 12 ++- src/libs/antares/study/parameters.cpp | 26 ++++++- src/libs/antares/study/parameters.h | 10 ++- src/solver/application.cpp | 3 + .../opt_alloc_probleme_a_optimiser.cpp | 3 + .../opt_appel_solveur_lineaire.cpp | 4 +- ...onstruction_variables_couts_demarrages.cpp | 13 ++-- ...e_min_groupes_demarres_couts_demarrage.cpp | 77 +++++++++++-------- .../opt_optimisation_lineaire.cpp | 13 ++-- .../opt_structure_probleme_a_resoudre.h | 2 + .../simulation/sim_calcul_economique.cpp | 4 + .../sim_structure_probleme_economique.h | 1 + src/solver/utils/mps_utils.cpp | 5 +- src/solver/utils/named_problem.cpp | 8 +- src/solver/utils/named_problem.h | 2 + src/solver/utils/ortools_utils.cpp | 67 ++++++++++++---- src/solver/utils/ortools_utils.h | 2 +- src/solver/variable/state.cpp | 14 +++- .../end-to-end/simple_study/simple-study.cpp | 54 ++++++++++++- src/tests/end-to-end/utils/utils.cpp | 16 +++- src/tests/end-to-end/utils/utils.h | 1 + .../windows/options/advanced/advanced.cpp | 49 +++++++----- .../windows/options/advanced/advanced.h | 7 +- 29 files changed, 351 insertions(+), 110 deletions(-) diff --git a/docs/reference-guide/13-file-format.md b/docs/reference-guide/13-file-format.md index 24b4d64abb..5339a71c6b 100644 --- a/docs/reference-guide/13-file-format.md +++ b/docs/reference-guide/13-file-format.md @@ -1,10 +1,19 @@ # Study format changes This is a list of all recent changes that came with new Antares Simulator features. The main goal of this document is to lower the costs of changing existing interfaces, both GUI and scripts. -## v8.7.1 +## v8.8.0 ### Input ### Short-term storage If no value is specified for `initiallevel`, then a default value of 50% is used. Note that this value is used only if `initialleveloptim=false`, and that `false` is the default value for `initialleveloptim`. +### Experimental "MILP" mode +New value `milp` for existing property `other preferences/unit-commitment-mode` in file **settings/generaldata.ini**. + +Using this property requires OR-Tools and a MILP solver (XPRESS, COIN) + +``` +antares-8.8-solver --use-ortools --ortools-solver coin|xpress ... +``` + ## v8.7.0 ### Input #### Scenarized RHS for binding constraints diff --git a/src/libs/antares/checks/antares/checks/checkLoadedInputData.h b/src/libs/antares/checks/antares/checks/checkLoadedInputData.h index 8900cbfd62..ad036cafc0 100644 --- a/src/libs/antares/checks/antares/checks/checkLoadedInputData.h +++ b/src/libs/antares/checks/antares/checks/checkLoadedInputData.h @@ -26,6 +26,10 @@ */ namespace Antares::Check { +void checkOrtoolsUsage(Antares::Data::UnitCommitmentMode ucMode, + bool ortoolsUsed, + const std::string& solverName); + void checkStudyVersion(const AnyString& optStudyFolder); void checkSimplexRangeHydroPricing(Antares::Data::SimplexOptimization optRange, diff --git a/src/libs/antares/checks/checkLoadedInputData.cpp b/src/libs/antares/checks/checkLoadedInputData.cpp index 3fcad20e02..8317192de3 100644 --- a/src/libs/antares/checks/checkLoadedInputData.cpp +++ b/src/libs/antares/checks/checkLoadedInputData.cpp @@ -34,6 +34,25 @@ namespace Antares::Check { +void checkOrtoolsUsage(Antares::Data::UnitCommitmentMode ucMode, + bool ortoolsUsed, + const std::string& solverName) +{ + using namespace Antares::Data; + if (ucMode == UnitCommitmentMode::ucMILP) + { + if (!ortoolsUsed) + { + throw Error::IncompatibleMILPWithoutOrtools(); + } + + if (solverName == "sirius") + { + throw Error::IncompatibleMILPOrtoolsSolver(); + } + } +} + void checkStudyVersion(const AnyString& optStudyFolder) { using namespace Antares::Data; diff --git a/src/libs/antares/exception/LoadingError.cpp b/src/libs/antares/exception/LoadingError.cpp index 5334b0912d..92c6f26bfa 100644 --- a/src/libs/antares/exception/LoadingError.cpp +++ b/src/libs/antares/exception/LoadingError.cpp @@ -72,6 +72,16 @@ IncompatibleParallelOptions::IncompatibleParallelOptions() : { } +IncompatibleMILPWithoutOrtools::IncompatibleMILPWithoutOrtools() : + LoadingError("Unit Commitment mode 'milp' must be used with an OR-Tools solver ") +{ +} + +IncompatibleMILPOrtoolsSolver::IncompatibleMILPOrtoolsSolver() : + LoadingError("'milp' mode does not work with OR-Tools using Sirius solver") +{ +} + IncompatibleOptRangeHydroPricing::IncompatibleOptRangeHydroPricing() : LoadingError("Simplex optimization range and hydro pricing mode : values are not compatible ") { diff --git a/src/libs/antares/exception/antares/exception/LoadingError.hpp b/src/libs/antares/exception/antares/exception/LoadingError.hpp index a9b7b0fa30..d3b7e15b74 100644 --- a/src/libs/antares/exception/antares/exception/LoadingError.hpp +++ b/src/libs/antares/exception/antares/exception/LoadingError.hpp @@ -92,6 +92,18 @@ class IncompatibleParallelOptions : public LoadingError IncompatibleParallelOptions(); }; +class IncompatibleMILPWithoutOrtools : public LoadingError +{ +public: + IncompatibleMILPWithoutOrtools(); +}; + +class IncompatibleMILPOrtoolsSolver : public LoadingError +{ +public: + IncompatibleMILPOrtoolsSolver(); +}; + class IncompatibleOptRangeHydroPricing : public LoadingError { public: diff --git a/src/libs/antares/study/fwd.cpp b/src/libs/antares/study/fwd.cpp index 5988b26e7e..bfba0dc762 100644 --- a/src/libs/antares/study/fwd.cpp +++ b/src/libs/antares/study/fwd.cpp @@ -264,8 +264,10 @@ UnitCommitmentMode StringToUnitCommitmentMode(const AnyString& text) s.trim(); s.toLower(); if (s == "fast") - return ucHeuristic; - if (s == "accurate") // mixed integer linear problem + return ucHeuristicFast; + if (s == "accurate") + return ucHeuristicAccurate; + if (s == "milp") // mixed integer linear problem return ucMILP; return ucUnknown; @@ -275,10 +277,12 @@ const char* UnitCommitmentModeToCString(UnitCommitmentMode ucommitment) { switch (ucommitment) { - case ucHeuristic: + case ucHeuristicFast: return "fast"; - case ucMILP: + case ucHeuristicAccurate: return "accurate"; // (slow) + case ucMILP: + return "milp"; // (possibly very slow) case ucUnknown: return ""; } diff --git a/src/libs/antares/study/fwd.h b/src/libs/antares/study/fwd.h index 6db7c2ee57..8e3dca76d1 100644 --- a/src/libs/antares/study/fwd.h +++ b/src/libs/antares/study/fwd.h @@ -464,8 +464,16 @@ SheddingPolicy StringToSheddingPolicy(const AnyString& text); enum UnitCommitmentMode { - ucHeuristic = 0, - ucMILP, // mixed integer linear problem + //! Heuristic in which 2 LP problems are solved + //! No explicit modelling for the number of ON/OFF units + ucHeuristicFast = 0, + //! Heuristic in which 2 LP problems are solved + //! Explicit modelling for the number of ON/OFF units + ucHeuristicAccurate, + //! A single MILP problem is solved, with explicit modelling + //! for the number of ON/OFF units + ucMILP, + //! Unknown mode, mainly used for error handling ucUnknown, }; diff --git a/src/libs/antares/study/parameters.cpp b/src/libs/antares/study/parameters.cpp index c10519942f..ec128bda4e 100644 --- a/src/libs/antares/study/parameters.cpp +++ b/src/libs/antares/study/parameters.cpp @@ -302,7 +302,7 @@ void Parameters::reset() power.fluctuations = lssFreeModulations; shedding.policy = shpShavePeaks; - unitCommitment.ucMode = ucHeuristic; + unitCommitment.ucMode = ucHeuristicFast; nbCores.ncMode = ncAvg; renewableGeneration.rgModelling = rgAggregated; @@ -730,7 +730,7 @@ static bool SGDIntLoadFamily_OtherPreferences(Parameters& d, } logs.warning() << "parameters: invalid unit commitment mode. Got '" << value << "'. reset to fast mode"; - d.unitCommitment.ucMode = ucHeuristic; + d.unitCommitment.ucMode = ucHeuristicFast; return false; } // Renewable generation modelling @@ -1344,6 +1344,7 @@ void Parameters::prepareForSimulation(const StudyLoadOptions& options) std::vector excluded_vars; renewableGeneration.addExcludedVariables(excluded_vars); adqPatchParams.addExcludedVariables(excluded_vars); + unitCommitment.addExcludedVariables(excluded_vars); variablesPrintInfo.prepareForSimulation(thematicTrimming, excluded_vars); @@ -1792,4 +1793,25 @@ bool Parameters::RenewableGeneration::isClusters() const { return rgModelling == Antares::Data::rgClusters; } + +// Some variables rely on dual values & marginal costs +void Parameters::UCMode::addExcludedVariables(std::vector& out) const +{ + // These variables rely on dual values & marginal costs + // these don't really make sense for MILP problems + // TODO : solve a LP problem with fixed values for integer variables + // extract values for dual variables & marginal costs from LP problem + const static std::vector milpExclude = {{"MARG. COST"}, + {"BC. MARG. COST"}, + {"CONG. FEE (ALG.)"}, + {"CONG. FEE (ABS.)"}, + {"MRG. PRICE"}, + {"STS Cashflow By Cluster"}, + {"Profit by plant"}}; + + if (ucMode == ucMILP) + { + out.insert(out.end(), milpExclude.begin(), milpExclude.end()); + } +} } // namespace Antares::Data diff --git a/src/libs/antares/study/parameters.h b/src/libs/antares/study/parameters.h index ffb5f1d643..98357e4709 100644 --- a/src/libs/antares/study/parameters.h +++ b/src/libs/antares/study/parameters.h @@ -43,7 +43,6 @@ #include - namespace Antares::Data { /*! @@ -433,11 +432,15 @@ class Parameters final PowerFluctuations fluctuations; } power; - struct + struct UCMode { //! Unit Commitment Mode UnitCommitmentMode ucMode; - } unitCommitment; + + //! Some variables rely on dual values & marginal costs + void addExcludedVariables(std::vector&) const; + }; + UCMode unitCommitment; struct { @@ -488,7 +491,6 @@ class Parameters final SimplexOptimization simplexOptimizationRange; //@} - AdequacyPatch::AdqPatchParams adqPatchParams; //! \name Scenariio Builder - Rules diff --git a/src/solver/application.cpp b/src/solver/application.cpp index c9dc034d78..a2aa4e3dbd 100644 --- a/src/solver/application.cpp +++ b/src/solver/application.cpp @@ -140,6 +140,9 @@ void Application::prepare(int argc, char* argv[]) // Some more checks require the existence of pParameters, hence of a study. // Their execution is delayed up to this point. + checkOrtoolsUsage( + pParameters->unitCommitment.ucMode, pParameters->ortoolsUsed, pParameters->ortoolsSolver); + checkSimplexRangeHydroPricing(pParameters->simplexOptimizationRange, pParameters->hydroPricing.hpMode); diff --git a/src/solver/optimisation/opt_alloc_probleme_a_optimiser.cpp b/src/solver/optimisation/opt_alloc_probleme_a_optimiser.cpp index 4e9457c5a9..809ea0f5d5 100644 --- a/src/solver/optimisation/opt_alloc_probleme_a_optimiser.cpp +++ b/src/solver/optimisation/opt_alloc_probleme_a_optimiser.cpp @@ -82,8 +82,11 @@ void OPT_AllocateFromNumberOfVariableConstraints(PROBLEME_ANTARES_A_RESOUDRE* Pr ProblemeAResoudre->Pi.assign(nbVariables, 0.); ProblemeAResoudre->Colonne.assign(nbVariables, 0); + // Names ProblemeAResoudre->NomDesVariables.resize(nbVariables); ProblemeAResoudre->NomDesContraintes.resize(nbConstraints); + // Integer variables ? (MILP) + ProblemeAResoudre->VariablesEntieres.resize(nbVariables); } static void optimisationAllocateProblem(PROBLEME_HEBDO* problemeHebdo, const int mxPaliers) diff --git a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp index 5e9f8f45f8..060784fe36 100644 --- a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp +++ b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp @@ -104,8 +104,7 @@ static SimplexResult OPT_TryToCallSimplex( const int optimizationNumber, const OptPeriodStringGenerator& optPeriodStringGenerator, bool PremierPassage, - IResultWriter& writer - ) + IResultWriter& writer) { const auto& ProblemeAResoudre = problemeHebdo->ProblemeAResoudre; auto ProbSpx @@ -307,6 +306,7 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options, const auto& ProblemeAResoudre = problemeHebdo->ProblemeAResoudre; Optimization::PROBLEME_SIMPLEXE_NOMME Probleme(ProblemeAResoudre->NomDesVariables, ProblemeAResoudre->NomDesContraintes, + ProblemeAResoudre->VariablesEntieres, ProblemeAResoudre->StatutDesVariables, ProblemeAResoudre->StatutDesContraintes, problemeHebdo->NamedProblems); diff --git a/src/solver/optimisation/opt_construction_variables_couts_demarrages.cpp b/src/solver/optimisation/opt_construction_variables_couts_demarrages.cpp index b7b1658fec..ebb51aa5ae 100644 --- a/src/solver/optimisation/opt_construction_variables_couts_demarrages.cpp +++ b/src/solver/optimisation/opt_construction_variables_couts_demarrages.cpp @@ -42,8 +42,9 @@ void OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeLineaireCoutsDeDemarra int nombreDePasDeTempsPourUneOptimisation = problemeHebdo->NombreDePasDeTempsPourUneOptimisation; - int nombreDeVariables = ProblemeAResoudre->NombreDeVariables; + int& nombreDeVariables = ProblemeAResoudre->NombreDeVariables; VariableNamer variableNamer(ProblemeAResoudre->NomDesVariables); + const bool intVariables = problemeHebdo->OptimisationAvecVariablesEntieres; for (uint32_t pays = 0; pays < problemeHebdo->NombreDePays; pays++) { variableNamer.UpdateArea(problemeHebdo->NomsDesPays[pays]); @@ -71,6 +72,8 @@ void OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeLineaireCoutsDeDemarra = nombreDeVariables; ProblemeAResoudre->TypeDeVariable[nombreDeVariables] = VARIABLE_BORNEE_DES_DEUX_COTES; + + ProblemeAResoudre->VariablesEntieres[nombreDeVariables] = intVariables; variableNamer.NODU(nombreDeVariables, clusterName); nombreDeVariables++; @@ -80,6 +83,7 @@ void OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeLineaireCoutsDeDemarra ProblemeAResoudre->TypeDeVariable[nombreDeVariables] = VARIABLE_BORNEE_INFERIEUREMENT; + ProblemeAResoudre->VariablesEntieres[nombreDeVariables] = intVariables; variableNamer.NumberStartingDispatchableUnits(nombreDeVariables, clusterName); nombreDeVariables++; @@ -88,6 +92,7 @@ void OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeLineaireCoutsDeDemarra = nombreDeVariables; ProblemeAResoudre->TypeDeVariable[nombreDeVariables] = VARIABLE_BORNEE_INFERIEUREMENT; + ProblemeAResoudre->VariablesEntieres[nombreDeVariables] = intVariables; variableNamer.NumberStoppingDispatchableUnits(nombreDeVariables, clusterName); nombreDeVariables++; @@ -96,13 +101,11 @@ void OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeLineaireCoutsDeDemarra = nombreDeVariables; ProblemeAResoudre->TypeDeVariable[nombreDeVariables] = VARIABLE_BORNEE_DES_DEUX_COTES; + + ProblemeAResoudre->VariablesEntieres[nombreDeVariables] = intVariables; variableNamer.NumberBreakingDownDispatchableUnits(nombreDeVariables, clusterName); nombreDeVariables++; } } } - - ProblemeAResoudre->NombreDeVariables = nombreDeVariables; - - return; } diff --git a/src/solver/optimisation/opt_nombre_min_groupes_demarres_couts_demarrage.cpp b/src/solver/optimisation/opt_nombre_min_groupes_demarres_couts_demarrage.cpp index eb050f39d6..59742b94f6 100644 --- a/src/solver/optimisation/opt_nombre_min_groupes_demarres_couts_demarrage.cpp +++ b/src/solver/optimisation/opt_nombre_min_groupes_demarres_couts_demarrage.cpp @@ -176,35 +176,44 @@ void OPT_AjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage(PROBLEME_HEBDO* pro NombreMinDeGroupesEnMarcheDuPalierThermique[pdtHebdo] = (int)ceil(X); } - OPT_PbLineairePourAjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage( - problemeHebdo, NombreMinDeGroupesEnMarcheDuPalierThermique, pays, index); + if (!problemeHebdo->OptimisationAvecVariablesEntieres) + { + OPT_PbLineairePourAjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage( + problemeHebdo, NombreMinDeGroupesEnMarcheDuPalierThermique, pays, index); - for (int pdtHebdo = 0; pdtHebdo < NombreDePasDeTempsProblemeHebdo; pdtHebdo++) + for (int pdtHebdo = 0; pdtHebdo < NombreDePasDeTempsProblemeHebdo; pdtHebdo++) + { + if (NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo] + < NombreMinDeGroupesEnMarcheDuPalierThermique[pdtHebdo]) + NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo] + = NombreMinDeGroupesEnMarcheDuPalierThermique[pdtHebdo]; + + if (pminDUnGroupeDuPalierThermique + * NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo] + > PuissanceDisponibleDuPalierThermique[pdtHebdo]) + PuissanceDisponibleDuPalierThermique[pdtHebdo] + = pminDUnGroupeDuPalierThermique + * NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo]; + } + } + else { - if (NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo] - < NombreMinDeGroupesEnMarcheDuPalierThermique[pdtHebdo]) + for (int pdtHebdo = 0; pdtHebdo < NombreDePasDeTempsProblemeHebdo; pdtHebdo++) + { NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo] = NombreMinDeGroupesEnMarcheDuPalierThermique[pdtHebdo]; - - if (pminDUnGroupeDuPalierThermique - * NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo] - > PuissanceDisponibleDuPalierThermique[pdtHebdo]) - PuissanceDisponibleDuPalierThermique[pdtHebdo] - = pminDUnGroupeDuPalierThermique - * NombreMaxDeGroupesEnMarcheDuPalierThermique[pdtHebdo]; + } } } } } - - return; } void OPT_PbLineairePourAjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage( PROBLEME_HEBDO* problemeHebdo, std::vector& NbMinOptDeGroupesEnMarche, int Pays, - int Index) + int index) { int NombreDePasDeTemps = problemeHebdo->NombreDePasDeTemps; @@ -212,12 +221,12 @@ void OPT_PbLineairePourAjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage( = problemeHebdo->PaliersThermiquesDuPays[Pays]; const std::vector& NombreMaxDeGroupesEnMarcheDuPalierThermique - = PaliersThermiquesDuPays.PuissanceDisponibleEtCout[Index] + = PaliersThermiquesDuPays.PuissanceDisponibleEtCout[index] .NombreMaxDeGroupesEnMarcheDuPalierThermique; const int DureeMinimaleDeMarcheDUnGroupeDuPalierThermique - = PaliersThermiquesDuPays.DureeMinimaleDeMarcheDUnGroupeDuPalierThermique[Index]; + = PaliersThermiquesDuPays.DureeMinimaleDeMarcheDUnGroupeDuPalierThermique[index]; const int DureeMinimaleDArretDUnGroupeDuPalierThermique - = PaliersThermiquesDuPays.DureeMinimaleDArretDUnGroupeDuPalierThermique[Index]; + = PaliersThermiquesDuPays.DureeMinimaleDArretDUnGroupeDuPalierThermique[index]; std::vector& ProductionThermique = problemeHebdo->ResultatsHoraires[Pays].ProductionThermique; @@ -233,40 +242,40 @@ void OPT_PbLineairePourAjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage( if (NbMinOptDeGroupesEnMarche[t1] - NbMinOptDeGroupesEnMarche[t1moins1] < 0) { - ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[Index] = 0; - ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[Index] + ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[index] = 0; + ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[index] = NbMinOptDeGroupesEnMarche[t1moins1] - NbMinOptDeGroupesEnMarche[t1]; - ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[Index] = 0; + ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[index] = 0; if (NombreMaxDeGroupesEnMarcheDuPalierThermique[t1] < NombreMaxDeGroupesEnMarcheDuPalierThermique[t1moins1]) { if (NombreMaxDeGroupesEnMarcheDuPalierThermique[t1moins1] - NombreMaxDeGroupesEnMarcheDuPalierThermique[t1] - < ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[Index]) + < ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[index]) { - ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[Index] + ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[index] = NombreMaxDeGroupesEnMarcheDuPalierThermique[t1moins1] - NombreMaxDeGroupesEnMarcheDuPalierThermique[t1]; } else { - ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[Index] - = ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[Index]; + ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[index] + = ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[index]; } } } else if (NbMinOptDeGroupesEnMarche[t1] - NbMinOptDeGroupesEnMarche[t1moins1] > 0) { - ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[Index] + ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[index] = NbMinOptDeGroupesEnMarche[t1] - NbMinOptDeGroupesEnMarche[t1moins1]; - ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[Index] = 0; - ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[Index] = 0; + ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[index] = 0; + ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[index] = 0; } else { - ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[Index] = 0; - ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[Index] = 0; - ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[Index] = 0; + ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[index] = 0; + ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[index] = 0; + ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[index] = 0; } } @@ -283,8 +292,8 @@ void OPT_PbLineairePourAjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage( if (k < 0) t1 = NombreDePasDeTemps + k; SMarche - += ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[Index] - - ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[Index]; + += ProductionThermique[t1].NombreDeGroupesQuiDemarrentDuPalier[index] + - ProductionThermique[t1].NombreDeGroupesQuiTombentEnPanneDuPalier[index]; } if (NbMinOptDeGroupesEnMarche[t1] < SMarche) { @@ -315,7 +324,7 @@ void OPT_PbLineairePourAjusterLeNombreMinDeGroupesDemarresCoutsDeDemarrage( SArret += NombreMaxDeGroupesEnMarcheDuPalierThermique[t1] - NombreMaxDeGroupesEnMarcheDuPalierThermique[t1moins1]; } - SArret -= ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[Index]; + SArret -= ProductionThermique[t1].NombreDeGroupesQuiSArretentDuPalier[index]; } if (NbMinOptDeGroupesEnMarche[t1] > SArret) { diff --git a/src/solver/optimisation/opt_optimisation_lineaire.cpp b/src/solver/optimisation/opt_optimisation_lineaire.cpp index ea64560372..b124098e98 100644 --- a/src/solver/optimisation/opt_optimisation_lineaire.cpp +++ b/src/solver/optimisation/opt_optimisation_lineaire.cpp @@ -63,10 +63,10 @@ void OPT_EcrireResultatFonctionObjectiveAuFormatTXT( } bool runWeeklyOptimization(const OptimizationOptions& options, - PROBLEME_HEBDO* problemeHebdo, - const AdqPatchParams& adqPatchParams, - Solver::IResultWriter& writer, - int optimizationNumber) + PROBLEME_HEBDO* problemeHebdo, + const AdqPatchParams& adqPatchParams, + Solver::IResultWriter& writer, + int optimizationNumber) { const int NombreDePasDeTempsPourUneOptimisation = problemeHebdo->NombreDePasDeTempsPourUneOptimisation; @@ -164,7 +164,10 @@ bool OPT_OptimisationLineaire(const OptimizationOptions& options, bool ret = runWeeklyOptimization( options, problemeHebdo, adqPatchParams, writer, PREMIERE_OPTIMISATION); - if (ret && !problemeHebdo->Expansion) + // We only need the 2nd optimization when NOT solving with integer variables + // We also skip the 2nd optimization in the hidden 'Expansion' mode + // and if the 1st one failed. + if (ret && !problemeHebdo->Expansion && !problemeHebdo->OptimisationAvecVariablesEntieres) { // We need to adjust some stuff before running the 2nd optimisation runThermalHeuristic(problemeHebdo); diff --git a/src/solver/optimisation/opt_structure_probleme_a_resoudre.h b/src/solver/optimisation/opt_structure_probleme_a_resoudre.h index b46b659140..8743810af1 100644 --- a/src/solver/optimisation/opt_structure_probleme_a_resoudre.h +++ b/src/solver/optimisation/opt_structure_probleme_a_resoudre.h @@ -115,6 +115,8 @@ struct PROBLEME_ANTARES_A_RESOUDRE std::vector NomDesVariables; std::vector NomDesContraintes; + std::vector VariablesEntieres; // true = int, false = continuous + std::vector StatutDesVariables; std::vector StatutDesContraintes; diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index 60fe4aa9cd..c9a64ab22d 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -120,6 +120,10 @@ void SIM_InitialisationProblemeHebdo(Data::Study& study, problem.exportMPSOnError = Data::exportMPS(parameters.include.unfeasibleProblemBehavior); problem.OptimisationAvecCoutsDeDemarrage + = (study.parameters.unitCommitment.ucMode + != Antares::Data::UnitCommitmentMode::ucHeuristicFast); + + problem.OptimisationAvecVariablesEntieres = (study.parameters.unitCommitment.ucMode == Antares::Data::UnitCommitmentMode::ucMILP); problem.OptimisationAuPasHebdomadaire diff --git a/src/solver/simulation/sim_structure_probleme_economique.h b/src/solver/simulation/sim_structure_probleme_economique.h index 5a94273a9b..e935899904 100644 --- a/src/solver/simulation/sim_structure_probleme_economique.h +++ b/src/solver/simulation/sim_structure_probleme_economique.h @@ -487,6 +487,7 @@ struct PROBLEME_HEBDO char TypeDeLissageHydraulique = PAS_DE_LISSAGE_HYDRAULIQUE; bool WaterValueAccurate = false; bool OptimisationAvecCoutsDeDemarrage = false; + bool OptimisationAvecVariablesEntieres = false; uint32_t NombreDePays = 0; std::vector NomsDesPays; uint32_t NombreDePaliersThermiques = 0; diff --git a/src/solver/utils/mps_utils.cpp b/src/solver/utils/mps_utils.cpp index 3a40854431..fe702b2f65 100644 --- a/src/solver/utils/mps_utils.cpp +++ b/src/solver/utils/mps_utils.cpp @@ -64,8 +64,9 @@ class ProblemConverter dest->NbVar = src->NombreDeVariables; mVariableType.resize(src->NombreDeVariables); - // TODO[FOM] use actual variable types when MIP resolution is integrated - std::fill(mVariableType.begin(), mVariableType.end(), SRS_CONTINUOUS_VAR); + for (int var = 0; var < src->NombreDeVariables; var++) + mVariableType[var] = src->VariablesEntieres[var] ? SRS_INTEGER_VAR : SRS_CONTINUOUS_VAR; + dest->TypeDeVariable = mVariableType.data(); dest->TypeDeBorneDeLaVariable = src->TypeDeVariable; // VARIABLE_BORNEE_DES_DEUX_COTES, // VARIABLE_BORNEE_INFERIEUREMENT, etc. diff --git a/src/solver/utils/named_problem.cpp b/src/solver/utils/named_problem.cpp index 984e678bc6..7553a729cc 100644 --- a/src/solver/utils/named_problem.cpp +++ b/src/solver/utils/named_problem.cpp @@ -1,6 +1,5 @@ #include "named_problem.h" #include -#include namespace Antares { @@ -9,12 +8,14 @@ namespace Optimization PROBLEME_SIMPLEXE_NOMME::PROBLEME_SIMPLEXE_NOMME(const std::vector& NomDesVariables, const std::vector& NomDesContraintes, + const std::vector& VariablesEntieres, std::vector& StatutDesVariables, std::vector& StatutDesContraintes, bool UseNamedProblems) : NomDesVariables(NomDesVariables), NomDesContraintes(NomDesContraintes), + VariablesEntieres(VariablesEntieres), StatutDesVariables(StatutDesVariables), StatutDesContraintes(StatutDesContraintes), useNamedProblems_(UseNamedProblems) @@ -23,9 +24,8 @@ PROBLEME_SIMPLEXE_NOMME::PROBLEME_SIMPLEXE_NOMME(const std::vector& bool PROBLEME_SIMPLEXE_NOMME::isMIP() const { - // TODO replace implementation when MIP is introduced - // For now, no problem is MIP. - return false; + return std::any_of( + VariablesEntieres.cbegin(), VariablesEntieres.cend(), [](bool x) { return x; }); } bool PROBLEME_SIMPLEXE_NOMME::basisExists() const diff --git a/src/solver/utils/named_problem.h b/src/solver/utils/named_problem.h index 7e749e29ee..f8d68a1f10 100644 --- a/src/solver/utils/named_problem.h +++ b/src/solver/utils/named_problem.h @@ -17,6 +17,7 @@ struct PROBLEME_SIMPLEXE_NOMME : public PROBLEME_SIMPLEXE public: PROBLEME_SIMPLEXE_NOMME(const std::vector& NomDesVariables, const std::vector& NomDesContraintes, + const std::vector& VariablesEntieres, std::vector& StatutDesVariables, std::vector& StatutDesContraintes, bool UseNamedProblems); @@ -29,6 +30,7 @@ struct PROBLEME_SIMPLEXE_NOMME : public PROBLEME_SIMPLEXE public: std::vector& StatutDesVariables; std::vector& StatutDesContraintes; + const std::vector& VariablesEntieres; bool isMIP() const; bool basisExists() const; diff --git a/src/solver/utils/ortools_utils.cpp b/src/solver/utils/ortools_utils.cpp index dd2f60d693..cbe93d1971 100644 --- a/src/solver/utils/ortools_utils.cpp +++ b/src/solver/utils/ortools_utils.cpp @@ -49,7 +49,7 @@ MPSolver* ProblemSimplexeNommeConverter::Convert() TuneSolverSpecificOptions(solver); // Create the variables and set objective cost. - CopyVariables(solver); + CopyObjective(solver); // Create constraints and set coefs CopyRows(solver); @@ -100,7 +100,7 @@ void ProblemSimplexeNommeConverter::UpdateCoefficient(unsigned idxVar, MPObjective* const objective) { double min_l = 0.0; - if (problemeSimplexe_->Xmin != NULL) + if (problemeSimplexe_->Xmin != NULL) // TODO[FOM] Remove enclosing if ? { min_l = problemeSimplexe_->Xmin[idxVar]; } @@ -109,11 +109,10 @@ void ProblemSimplexeNommeConverter::UpdateCoefficient(unsigned idxVar, objective->SetCoefficient(var, problemeSimplexe_->CoutLineaire[idxVar]); } -void ProblemSimplexeNommeConverter::CopyVariables(MPSolver* solver) +void ProblemSimplexeNommeConverter::CopyObjective(MPSolver* solver) { MPObjective* const objective = solver->MutableObjective(); - for (int idxVar = 0; idxVar < problemeSimplexe_->NombreDeVariables; ++idxVar) { UpdateCoefficient(idxVar, solver, objective); @@ -150,26 +149,65 @@ void ProblemSimplexeNommeConverter::CopyRows(MPSolver* solver) } // namespace Optimization } // namespace Antares -static void extract_from_MPSolver(const MPSolver* solver, +static void extractSolutionValues(const std::vector& variables, Antares::Optimization::PROBLEME_SIMPLEXE_NOMME* problemeSimplexe) { - auto& variables = solver->variables(); int nbVar = problemeSimplexe->NombreDeVariables; - - // Extracting variable values and reduced costs for (int idxVar = 0; idxVar < nbVar; ++idxVar) { auto& var = variables[idxVar]; problemeSimplexe->X[idxVar] = var->solution_value(); + } +} + +static void extractReducedCosts(const std::vector& variables, + Antares::Optimization::PROBLEME_SIMPLEXE_NOMME* problemeSimplexe) +{ + int nbVar = problemeSimplexe->NombreDeVariables; + for (int idxVar = 0; idxVar < nbVar; ++idxVar) + { + auto& var = variables[idxVar]; problemeSimplexe->CoutsReduits[idxVar] = var->reduced_cost(); } +} - auto& constraints = solver->constraints(); - int nbRow = problemeSimplexe->NombreDeContraintes; - for (int idxRow = 0; idxRow < nbRow; ++idxRow) +static void extractDualValues(const std::vector& constraints, + Antares::Optimization::PROBLEME_SIMPLEXE_NOMME* problemeSimplexe) +{ + int nbRows = problemeSimplexe->NombreDeContraintes; + for (int idxRow = 0; idxRow < nbRows; ++idxRow) + { + auto& row = constraints[idxRow]; + problemeSimplexe->CoutsMarginauxDesContraintes[idxRow] = row->dual_value(); + } +} + +static void extract_from_MPSolver(MPSolver* solver, + Antares::Optimization::PROBLEME_SIMPLEXE_NOMME* problemeSimplexe) +{ + assert(solver); + assert(problemeSimplexe); + + const bool isMIP = problemeSimplexe->isMIP(); + + extractSolutionValues(solver->variables(), + problemeSimplexe); + + if (isMIP) + { + const int nbVar = problemeSimplexe->NombreDeVariables; + std::fill(problemeSimplexe->CoutsReduits, problemeSimplexe->CoutsReduits + nbVar, 0.); + + const int nbRows = problemeSimplexe->NombreDeContraintes; + std::fill(problemeSimplexe->CoutsMarginauxDesContraintes, + problemeSimplexe->CoutsMarginauxDesContraintes + nbRows, + 0.); + } + else { - auto& row = constraints[idxRow]; - problemeSimplexe->CoutsMarginauxDesContraintes[idxRow] = row->dual_value(); + extractReducedCosts(solver->variables(), problemeSimplexe); + + extractDualValues(solver->constraints(), problemeSimplexe); } } @@ -241,7 +279,8 @@ MPSolver* ORTOOLS_ConvertIfNeeded(const std::string& solverName, { if (solver == nullptr) { - return Antares::Optimization::ProblemSimplexeNommeConverter(solverName, Probleme).Convert(); + Antares::Optimization::ProblemSimplexeNommeConverter converter(solverName, Probleme); + return converter.Convert(); } else { diff --git a/src/solver/utils/ortools_utils.h b/src/solver/utils/ortools_utils.h index 3ec59ef13d..3ea49adce9 100644 --- a/src/solver/utils/ortools_utils.h +++ b/src/solver/utils/ortools_utils.h @@ -92,7 +92,7 @@ class ProblemSimplexeNommeConverter Nomenclature constraintNameManager_ = Nomenclature('c'); void UpdateCoefficient(unsigned idxVar, MPSolver* solver, MPObjective* const objective); - void CopyVariables(MPSolver* solver); + void CopyObjective(MPSolver* solver); void UpdateContraints(unsigned idxRow, MPSolver* solver); void CopyRows(MPSolver* solver); void TuneSolverSpecificOptions(MPSolver* solver) const; diff --git a/src/solver/variable/state.cpp b/src/solver/variable/state.cpp index 726cb1b478..cb9c6d1448 100644 --- a/src/solver/variable/state.cpp +++ b/src/solver/variable/state.cpp @@ -130,15 +130,20 @@ void State::initFromThermalClusterIndex(const uint clusterAreaWideIndex) = hourlyResults->ProductionThermique[hourInTheWeek] .ProductionThermiqueDuPalier[thermalCluster->index]; - if (unitCommitmentMode == Antares::Data::UnitCommitmentMode::ucMILP) // Economy accurate + switch (unitCommitmentMode) + { + using ucMode = Antares::Data::UnitCommitmentMode; + case ucMode::ucHeuristicAccurate: + case ucMode::ucMILP: thermal[area->index].numberOfUnitsONbyCluster[clusterAreaWideIndex] = static_cast(hourlyResults->ProductionThermique[hourInTheWeek] .NombreDeGroupesEnMarcheDuPalier[thermalCluster->index]); - else + break; + default: // Economy Fast or Adequacy -- will be calculated during the smoothing thermal[area->index].numberOfUnitsONbyCluster[clusterAreaWideIndex] = 0; + } } - initFromThermalClusterIndexProduction(clusterAreaWideIndex); if (studyMode != Data::stdmAdequacy) @@ -290,7 +295,7 @@ void State::yearEndBuildFromThermalClusterIndex(const uint clusterAreaWideIndex) switch (unitCommitmentMode) { - case Antares::Data::UnitCommitmentMode::ucHeuristic: + case Antares::Data::UnitCommitmentMode::ucHeuristicFast: { // ON_min[h] = static_cast(Math::Ceil(thermalClusterProduction / // currentCluster->nominalCapacityWithSpinning)); // code 5.0.3b<7 @@ -313,6 +318,7 @@ void State::yearEndBuildFromThermalClusterIndex(const uint clusterAreaWideIndex) break; } case Antares::Data::UnitCommitmentMode::ucMILP: + case Antares::Data::UnitCommitmentMode::ucHeuristicAccurate: { ON_min[h] = Math::Max( static_cast(Math::Ceil(thermalClusterProduction / currentCluster->nominalCapacityWithSpinning)), diff --git a/src/tests/end-to-end/simple_study/simple-study.cpp b/src/tests/end-to-end/simple_study/simple-study.cpp index d200c32bc8..dfd6b255a5 100644 --- a/src/tests/end-to-end/simple_study/simple-study.cpp +++ b/src/tests/end-to-end/simple_study/simple-study.cpp @@ -140,11 +140,62 @@ BOOST_AUTO_TEST_CASE(two_mc_years_with_different_weight__two_ts) BOOST_TEST(output.overallCost(area).hour(0) == averageLoad * clusterCost, tt::tolerance(0.001)); } +BOOST_AUTO_TEST_CASE(milp_two_mc_single_unit_single_scenario) +{ + setNumberMCyears(1); + + // Arbitrary large number, only characteristic is : larger than all + // other marginal costs + area->thermal.unsuppliedEnergyCost = 1000; + + // Use OR-Tools / COIN for MILP + auto& p = study->parameters; + p.unitCommitment.ucMode = ucMILP; + p.ortoolsUsed = true; + p.ortoolsSolver = "coin"; + + simulation->create(); + simulation->run(); + + OutputRetriever output(simulation->rawSimu()); + + BOOST_TEST(output.thermalGeneration(cluster.get()).hour(10) == loadInArea, + tt::tolerance(0.001)); + BOOST_TEST(output.thermalNbUnitsON(cluster.get()).hour(10) == 1, tt::tolerance(0.001)); + BOOST_TEST(output.overallCost(area).hour(0) == loadInArea * clusterCost, tt::tolerance(0.001)); +} + +BOOST_AUTO_TEST_CASE(milp_two_mc_two_unit_single_scenario) +{ + setNumberMCyears(1); + clusterConfig.setAvailablePower(0, 150.).setUnitCount(2); + + loadInArea = 150; + loadTSconfig.setColumnCount(1).fillColumnWith(0, loadInArea); + // Arbitrary large number, only characteristic is : larger than all + // other marginal costs + area->thermal.unsuppliedEnergyCost = 1000; + + // Use OR-Tools / COIN for MILP + auto& p = study->parameters; + p.unitCommitment.ucMode = ucMILP; + p.ortoolsUsed = true; + p.ortoolsSolver = "coin"; + + simulation->create(); + simulation->run(); + + OutputRetriever output(simulation->rawSimu()); + + BOOST_TEST(output.thermalGeneration(cluster.get()).hour(10) == loadInArea, + tt::tolerance(0.001)); + BOOST_TEST(output.thermalNbUnitsON(cluster.get()).hour(10) == 2, tt::tolerance(0.001)); +} BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE(error_cases) +BOOST_AUTO_TEST_SUITE(error_cases) BOOST_AUTO_TEST_CASE(error_on_wrong_hydro_data) { StudyBuilder builder; @@ -160,5 +211,4 @@ BOOST_AUTO_TEST_CASE(error_on_wrong_hydro_data) simulation->create(); BOOST_CHECK_THROW(simulation->run(), Antares::FatalError); } - BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/end-to-end/utils/utils.cpp b/src/tests/end-to-end/utils/utils.cpp index d4dc34ac54..38add40c60 100644 --- a/src/tests/end-to-end/utils/utils.cpp +++ b/src/tests/end-to-end/utils/utils.cpp @@ -41,31 +41,35 @@ void addScratchpadToEachArea(Study& study) } -ThermalClusterConfig::ThermalClusterConfig(ThermalCluster* cluster) : cluster_(cluster) +ThermalClusterConfig::ThermalClusterConfig(ThermalCluster* cluster) : cluster_(cluster), tsAvailablePowerConfig_(cluster_->series->timeSeries) { - tsAvailablePowerConfig_ = std::move(TimeSeriesConfigurer(cluster_->series->timeSeries)); } + ThermalClusterConfig& ThermalClusterConfig::setNominalCapacity(double nominalCapacity) { cluster_->nominalCapacity = nominalCapacity; return *this; } + ThermalClusterConfig& ThermalClusterConfig::setUnitCount(unsigned int unitCount) { cluster_->unitCount = unitCount; return *this; } + ThermalClusterConfig& ThermalClusterConfig::setCosts(double cost) { cluster_->marginalCost = cost; cluster_->marketBidCost = cost; // Must define market bid cost otherwise all production is used return *this; } + ThermalClusterConfig& ThermalClusterConfig::setAvailablePowerNumberOfTS(unsigned int columnCount) { tsAvailablePowerConfig_.setColumnCount(columnCount); return *this; -}; +} + ThermalClusterConfig& ThermalClusterConfig::setAvailablePower(unsigned int column, double value) { tsAvailablePowerConfig_.fillColumnWith(column, value); @@ -108,6 +112,12 @@ averageResults OutputRetriever::thermalGeneration(ThermalCluster* cluster) return averageResults((*result)[cluster->areaWideIndex].avgdata); } +averageResults OutputRetriever::thermalNbUnitsON(ThermalCluster* cluster) +{ + auto result = retrieveResultsForThermalCluster(cluster); + return averageResults((*result)[cluster->areaWideIndex].avgdata); +} + ScenarioBuilderRule::ScenarioBuilderRule(Study& study) { study.scenarioRulesCreate(); diff --git a/src/tests/end-to-end/utils/utils.h b/src/tests/end-to-end/utils/utils.h index 1781b2f54c..cf0d42d2ee 100644 --- a/src/tests/end-to-end/utils/utils.h +++ b/src/tests/end-to-end/utils/utils.h @@ -87,6 +87,7 @@ class OutputRetriever averageResults load(Area* area); averageResults flow(AreaLink* link); averageResults thermalGeneration(ThermalCluster* cluster); + averageResults thermalNbUnitsON(ThermalCluster* cluster); private: template diff --git a/src/ui/simulator/windows/options/advanced/advanced.cpp b/src/ui/simulator/windows/options/advanced/advanced.cpp index 3b925d47ef..fa670c5ce1 100644 --- a/src/ui/simulator/windows/options/advanced/advanced.cpp +++ b/src/ui/simulator/windows/options/advanced/advanced.cpp @@ -338,7 +338,7 @@ void AdvancedParameters::onResetToDefault(void*) parameters.hydroPricing.hpMode = Data::hpHeuristic; parameters.power.fluctuations = Data::lssFreeModulations; parameters.shedding.policy = Data::shpShavePeaks; - parameters.unitCommitment.ucMode = Data::ucHeuristic; + parameters.unitCommitment.ucMode = Data::ucHeuristicFast; parameters.nbCores.ncMode = Data::ncAvg; parameters.renewableGeneration.rgModelling = Data::rgAggregated; @@ -806,52 +806,63 @@ void AdvancedParameters::onUnitCommitmentMode(Component::Button&, wxMenu& menu, wxMenuItem* it; wxString text; - text = wxStringFromUTF8(UnitCommitmentModeToCString(Data::ucHeuristic)); // Fast + text = wxStringFromUTF8(UnitCommitmentModeToCString(Data::ucHeuristicFast)); // Fast text << wxT(" [default]"); it = Menu::CreateItem(&menu, wxID_ANY, text, "images/16x16/tag.png"); menu.Connect(it->GetId(), wxEVT_COMMAND_MENU_SELECTED, - wxCommandEventHandler(AdvancedParameters::onSelectUCHeuristic), + wxCommandEventHandler(AdvancedParameters::onSelectUCHeuristicFast), nullptr, this); text.clear(); - text = wxStringFromUTF8(UnitCommitmentModeToCString(Data::ucMILP)); // Accurate + text = wxStringFromUTF8(UnitCommitmentModeToCString(Data::ucHeuristicAccurate)); // Accurate text << wxT(" (slow)"); it = Menu::CreateItem(&menu, wxID_ANY, text, "images/16x16/tag.png"); menu.Connect(it->GetId(), wxEVT_COMMAND_MENU_SELECTED, - wxCommandEventHandler(AdvancedParameters::onSelectUCMixedIntegerLinearProblem), + wxCommandEventHandler(AdvancedParameters::onSelectUCHeuristicAccurate), + nullptr, + this); + + text.clear(); + text = wxStringFromUTF8(UnitCommitmentModeToCString(Data::ucMILP)); // Accurate + text << wxT(" "); + it = Menu::CreateItem(&menu, wxID_ANY, text, "images/16x16/tag.png"); + menu.Connect(it->GetId(), + wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(AdvancedParameters::onSelectUCMILP), nullptr, this); } -void AdvancedParameters::onSelectUCHeuristic(wxCommandEvent& /* evt */) +void AdvancedParameters::onSelectUCMode(Antares::Data::UnitCommitmentMode mode) { - auto& study = *GetCurrentStudy(); if (not CurrentStudyIsValid()) return; + auto study = GetCurrentStudy(); - if (study.parameters.unitCommitment.ucMode != Data::ucHeuristic) + if (study->parameters.unitCommitment.ucMode != mode) { - study.parameters.unitCommitment.ucMode = Data::ucHeuristic; + study->parameters.unitCommitment.ucMode = mode; MarkTheStudyAsModified(); refresh(); } } -void AdvancedParameters::onSelectUCMixedIntegerLinearProblem(wxCommandEvent& /* evt */) +void AdvancedParameters::onSelectUCHeuristicFast(wxCommandEvent& /* evt */) { - if (not CurrentStudyIsValid()) - return; - auto& study = *GetCurrentStudy(); + onSelectUCMode(Data::ucHeuristicFast); +} - if (study.parameters.unitCommitment.ucMode != Data::ucMILP) - { - study.parameters.unitCommitment.ucMode = Data::ucMILP; - MarkTheStudyAsModified(); - refresh(); - } +void AdvancedParameters::onSelectUCHeuristicAccurate(wxCommandEvent& /* evt */) +{ + onSelectUCMode(Data::ucHeuristicAccurate); +} + +void AdvancedParameters::onSelectUCMILP(wxCommandEvent& /* evt */) +{ + onSelectUCMode(Data::ucMILP); } void AdvancedParameters::onNumberOfCores(Component::Button&, wxMenu& menu, void*) diff --git a/src/ui/simulator/windows/options/advanced/advanced.h b/src/ui/simulator/windows/options/advanced/advanced.h index 15e274fae5..d888492400 100644 --- a/src/ui/simulator/windows/options/advanced/advanced.h +++ b/src/ui/simulator/windows/options/advanced/advanced.h @@ -98,8 +98,11 @@ class AdvancedParameters final : public wxDialog void onSelectSHPMinimizeDuration(wxCommandEvent& evt); void onUnitCommitmentMode(Component::Button&, wxMenu& menu, void*); - void onSelectUCHeuristic(wxCommandEvent& evt); - void onSelectUCMixedIntegerLinearProblem(wxCommandEvent& evt); + + void onSelectUCMode(Antares::Data::UnitCommitmentMode mode); + void onSelectUCHeuristicFast(wxCommandEvent& evt); + void onSelectUCHeuristicAccurate(wxCommandEvent& evt); + void onSelectUCMILP(wxCommandEvent& evt); void onNumberOfCores(Component::Button&, wxMenu& menu, void*); template