diff --git a/src/solver/optimisation/adequacy_patch_local_matching/adequacy_patch_weekly_optimization.cpp b/src/solver/optimisation/adequacy_patch_local_matching/adequacy_patch_weekly_optimization.cpp index 9f0193912d..b1371c5255 100644 --- a/src/solver/optimisation/adequacy_patch_local_matching/adequacy_patch_weekly_optimization.cpp +++ b/src/solver/optimisation/adequacy_patch_local_matching/adequacy_patch_weekly_optimization.cpp @@ -49,7 +49,8 @@ AdequacyPatchOptimization::AdequacyPatchOptimization(const Antares::Data::Study& void AdequacyPatchOptimization::solve() { problemeHebdo_->adequacyPatchRuntimeData->AdequacyFirstStep = true; - OPT_OptimisationHebdomadaire(options_, problemeHebdo_, adqPatchParams_, writer_); + OPT_OptimisationHebdomadaire( + options_, problemeHebdo_, adqPatchParams_, writer_, thread_number_); problemeHebdo_->adequacyPatchRuntimeData->AdequacyFirstStep = false; for (uint32_t pays = 0; pays < problemeHebdo_->NombreDePays; ++pays) @@ -63,7 +64,8 @@ void AdequacyPatchOptimization::solve() problemeHebdo_->ResultatsHoraires[pays].ValeursHorairesDENS.end(), 0); } - OPT_OptimisationHebdomadaire(options_, problemeHebdo_, adqPatchParams_, writer_); + OPT_OptimisationHebdomadaire( + options_, problemeHebdo_, adqPatchParams_, writer_, thread_number_); } } // namespace Antares::Solver::Optimization diff --git a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp index ed361d6206..6b8e5e115f 100644 --- a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp +++ b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp @@ -36,7 +36,6 @@ extern "C" { #include "spx_definition_arguments.h" #include "spx_fonctions.h" - #include "srs_api.h" } @@ -96,16 +95,15 @@ struct SimplexResult long long solveTime = 0; mpsWriterFactory mps_writer_factory; }; - -static SimplexResult OPT_TryToCallSimplex( - const OptimizationOptions& options, - PROBLEME_HEBDO* problemeHebdo, - Optimization::PROBLEME_SIMPLEXE_NOMME& Probleme, - const int NumIntervalle, - const int optimizationNumber, - const OptPeriodStringGenerator& optPeriodStringGenerator, - bool PremierPassage, - IResultWriter& writer) +static SimplexResult OPT_TryToCallSimplex(const OptimizationOptions& options, + PROBLEME_HEBDO* problemeHebdo, + Optimization::PROBLEME_SIMPLEXE_NOMME& Probleme, + const int NumIntervalle, + const int optimizationNumber, + const OptPeriodStringGenerator& optPeriodStringGenerator, + bool PremierPassage, + IResultWriter& writer, + OrtoolsLogHandler& ortools_logger) { const auto& ProblemeAResoudre = problemeHebdo->ProblemeAResoudre; auto ProbSpx @@ -225,7 +223,8 @@ static SimplexResult OPT_TryToCallSimplex( { solver = ORTOOLS_ConvertIfNeeded(options.solverName, &Probleme, solver); } - const std::string filename = createMPSfilename(optPeriodStringGenerator, optimizationNumber); + + const std::string filename = createMPSfilename(optPeriodStringGenerator, optimizationNumber); mpsWriterFactory mps_writer_factory(problemeHebdo->ExportMPS, problemeHebdo->exportMPSOnError, @@ -241,6 +240,11 @@ static SimplexResult OPT_TryToCallSimplex( if (options.useOrtools) { const bool keepBasis = (optimizationNumber == PREMIERE_OPTIMISATION); + + if (Probleme.SolverLogs()) + { + solver->EnableOutput(&ortools_logger); + } solver = ORTOOLS_Simplexe(&Probleme, solver, keepBasis); if (solver != nullptr) { @@ -296,7 +300,8 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options, int NumIntervalle, const int optimizationNumber, const OptPeriodStringGenerator& optPeriodStringGenerator, - IResultWriter& writer) + IResultWriter& writer, + uint thread_number) { const auto& ProblemeAResoudre = problemeHebdo->ProblemeAResoudre; Optimization::PROBLEME_SIMPLEXE_NOMME Probleme(ProblemeAResoudre->NomDesVariables, @@ -308,16 +313,31 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options, problemeHebdo->solverLogs); bool PremierPassage = true; - - struct SimplexResult simplexResult = - OPT_TryToCallSimplex(options, problemeHebdo, Probleme, NumIntervalle, optimizationNumber, - optPeriodStringGenerator, PremierPassage, writer); + std::filesystem::path log_file = logs.logfile().c_str(); + auto log_directory = log_file.parent_path(); + OrtoolsLogHandler ortools_logger(options.solverName, log_directory, thread_number); + struct SimplexResult simplexResult = OPT_TryToCallSimplex(options, + problemeHebdo, + Probleme, + NumIntervalle, + optimizationNumber, + optPeriodStringGenerator, + PremierPassage, + writer, + ortools_logger); if (!simplexResult.success) { PremierPassage = false; - simplexResult = OPT_TryToCallSimplex(options, problemeHebdo, Probleme, NumIntervalle, optimizationNumber, - optPeriodStringGenerator, PremierPassage, writer); + simplexResult = OPT_TryToCallSimplex(options, + problemeHebdo, + Probleme, + NumIntervalle, + optimizationNumber, + optPeriodStringGenerator, + PremierPassage, + writer, + ortools_logger); } long long solveTime = simplexResult.solveTime; @@ -372,8 +392,13 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options, Probleme.SetUseNamedProblems(true); - auto MPproblem = std::shared_ptr(ProblemSimplexeNommeConverter(options.solverName, &Probleme).Convert()); + auto MPproblem = std::shared_ptr( + ProblemSimplexeNommeConverter(options.solverName, &Probleme).Convert()); + if (Probleme.SolverLogs()) + { + MPproblem->EnableOutput(&ortools_logger); + } auto analyzer = makeUnfeasiblePbAnalyzer(); analyzer->run(MPproblem.get()); analyzer->printReport(); @@ -384,6 +409,7 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options, return false; } - + // copy log file(s) + ortools_logger.copy_log(writer); return true; } diff --git a/src/solver/optimisation/opt_fonctions.h b/src/solver/optimisation/opt_fonctions.h index e2807f0c8d..620d04a05d 100644 --- a/src/solver/optimisation/opt_fonctions.h +++ b/src/solver/optimisation/opt_fonctions.h @@ -41,7 +41,8 @@ using OptimizationOptions = Antares::Solver::Optimization::OptimizationOptions; void OPT_OptimisationHebdomadaire(const OptimizationOptions& options, PROBLEME_HEBDO*, const AdqPatchParams&, - Antares::Solver::IResultWriter& writer); + Antares::Solver::IResultWriter& writer, + uint thread_number); void OPT_NumeroDeJourDuPasDeTemps(PROBLEME_HEBDO*); void OPT_NumeroDIntervalleOptimiseDuPasDeTemps(PROBLEME_HEBDO*); void OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeLineaire(PROBLEME_HEBDO*); @@ -75,7 +76,8 @@ bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE&, bool OPT_PilotageOptimisationLineaire(const OptimizationOptions& options, PROBLEME_HEBDO*, const AdqPatchParams&, - Antares::Solver::IResultWriter& writer); + Antares::Solver::IResultWriter& writer, + uint thread_number); void OPT_VerifierPresenceReserveJmoins1(PROBLEME_HEBDO*); bool OPT_PilotageOptimisationQuadratique(PROBLEME_HEBDO*); @@ -89,13 +91,15 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options, int, const int, const OptPeriodStringGenerator&, - Antares::Solver::IResultWriter& writer); + Antares::Solver::IResultWriter& writer, + uint thread_number); void OPT_LiberationProblemesSimplexe(const OptimizationOptions& options, const PROBLEME_HEBDO*); bool OPT_OptimisationLineaire(const OptimizationOptions& options, PROBLEME_HEBDO*, const AdqPatchParams&, - Antares::Solver::IResultWriter& writer); + Antares::Solver::IResultWriter& writer, + uint thread_number); void OPT_RestaurerLesDonnees(const PROBLEME_HEBDO*); /*------------------------------*/ diff --git a/src/solver/optimisation/opt_optimisation_hebdo.cpp b/src/solver/optimisation/opt_optimisation_hebdo.cpp index a84f334942..8755ed4e3b 100644 --- a/src/solver/optimisation/opt_optimisation_hebdo.cpp +++ b/src/solver/optimisation/opt_optimisation_hebdo.cpp @@ -48,11 +48,13 @@ using Antares::Solver::Optimization::OptimizationOptions; void OPT_OptimisationHebdomadaire(const OptimizationOptions& options, PROBLEME_HEBDO* pProblemeHebdo, const AdqPatchParams& adqPatchParams, - Solver::IResultWriter& writer) + Solver::IResultWriter& writer, + uint thread_number) { if (pProblemeHebdo->TypeDOptimisation == OPTIMISATION_LINEAIRE) { - if (!OPT_PilotageOptimisationLineaire(options, pProblemeHebdo, adqPatchParams, writer)) + if (!OPT_PilotageOptimisationLineaire( + options, pProblemeHebdo, adqPatchParams, writer, thread_number)) { logs.error() << "Linear optimization failed"; throw UnfeasibleProblemError("Linear optimization failed"); diff --git a/src/solver/optimisation/opt_optimisation_lineaire.cpp b/src/solver/optimisation/opt_optimisation_lineaire.cpp index b124098e98..d335c25dfc 100644 --- a/src/solver/optimisation/opt_optimisation_lineaire.cpp +++ b/src/solver/optimisation/opt_optimisation_lineaire.cpp @@ -66,7 +66,8 @@ bool runWeeklyOptimization(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo, const AdqPatchParams& adqPatchParams, Solver::IResultWriter& writer, - int optimizationNumber) + int optimizationNumber, + uint thread_number) { const int NombreDePasDeTempsPourUneOptimisation = problemeHebdo->NombreDePasDeTempsPourUneOptimisation; @@ -107,7 +108,8 @@ bool runWeeklyOptimization(const OptimizationOptions& options, numeroDeLIntervalle, optimizationNumber, *optPeriodStringGenerator, - writer)) + writer, + thread_number)) return false; if (problemeHebdo->ExportMPS != Data::mpsExportStatus::NO_EXPORT @@ -135,11 +137,11 @@ void runThermalHeuristic(PROBLEME_HEBDO* problemeHebdo) } } // namespace - bool OPT_OptimisationLineaire(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo, const AdqPatchParams& adqPatchParams, - Solver::IResultWriter& writer) + Solver::IResultWriter& writer, + uint thread_number) { if (!problemeHebdo->OptimisationAuPasHebdomadaire) { @@ -162,7 +164,7 @@ bool OPT_OptimisationLineaire(const OptimizationOptions& options, OPT_ConstruireLaMatriceDesContraintesDuProblemeLineaire(problemeHebdo, writer); bool ret = runWeeklyOptimization( - options, problemeHebdo, adqPatchParams, writer, PREMIERE_OPTIMISATION); + options, problemeHebdo, adqPatchParams, writer, PREMIERE_OPTIMISATION, thread_number); // We only need the 2nd optimization when NOT solving with integer variables // We also skip the 2nd optimization in the hidden 'Expansion' mode @@ -172,7 +174,7 @@ bool OPT_OptimisationLineaire(const OptimizationOptions& options, // We need to adjust some stuff before running the 2nd optimisation runThermalHeuristic(problemeHebdo); return runWeeklyOptimization( - options, problemeHebdo, adqPatchParams, writer, DEUXIEME_OPTIMISATION); + options, problemeHebdo, adqPatchParams, writer, DEUXIEME_OPTIMISATION, thread_number); } return ret; } diff --git a/src/solver/optimisation/opt_pilotage_optimisation_lineaire.cpp b/src/solver/optimisation/opt_pilotage_optimisation_lineaire.cpp index 49a3a489b6..f62139b7f4 100644 --- a/src/solver/optimisation/opt_pilotage_optimisation_lineaire.cpp +++ b/src/solver/optimisation/opt_pilotage_optimisation_lineaire.cpp @@ -37,7 +37,8 @@ using Antares::Solver::Optimization::OptimizationOptions; bool OPT_PilotageOptimisationLineaire(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo, const AdqPatchParams& adqPatchParams, - Solver::IResultWriter& writer) + Solver::IResultWriter& writer, + uint thread_number) { if (!problemeHebdo->LeProblemeADejaEteInstancie) { @@ -83,5 +84,5 @@ bool OPT_PilotageOptimisationLineaire(const OptimizationOptions& options, OPT_InitialiserNombreMinEtMaxDeGroupesCoutsDeDemarrage(problemeHebdo); } - return OPT_OptimisationLineaire(options, problemeHebdo, adqPatchParams, writer); + return OPT_OptimisationLineaire(options, problemeHebdo, adqPatchParams, writer, thread_number); } diff --git a/src/solver/optimisation/weekly_optimization.cpp b/src/solver/optimisation/weekly_optimization.cpp index 1734c5a0da..be834e48c5 100644 --- a/src/solver/optimisation/weekly_optimization.cpp +++ b/src/solver/optimisation/weekly_optimization.cpp @@ -41,7 +41,8 @@ DefaultWeeklyOptimization::DefaultWeeklyOptimization(const OptimizationOptions& void DefaultWeeklyOptimization::solve() { - OPT_OptimisationHebdomadaire(options_, problemeHebdo_, adqPatchParams_, writer_); + OPT_OptimisationHebdomadaire( + options_, problemeHebdo_, adqPatchParams_, writer_, thread_number_); } } // namespace Antares::Solver::Optimization diff --git a/src/solver/simulation/adequacy.cpp b/src/solver/simulation/adequacy.cpp index 0d311813a7..fe18732fef 100644 --- a/src/solver/simulation/adequacy.cpp +++ b/src/solver/simulation/adequacy.cpp @@ -199,7 +199,8 @@ bool Adequacy::year(Progression::Task& progression, OPT_OptimisationHebdomadaire(createOptimizationOptions(study), &pProblemesHebdo[numSpace], study.parameters.adqPatchParams, - resultWriter); + resultWriter, + numSpace); computingHydroLevels(study.areas, pProblemesHebdo[numSpace], false); @@ -382,7 +383,7 @@ void Adequacy::simulationEnd() if (!preproOnly && study.runtime->interconnectionsCount() > 0) { auto balance = retrieveBalance(study, variables); - ComputeFlowQuad(study, pProblemesHebdo[0], balance, pNbWeeks); + ComputeFlowQuad(study, pProblemesHebdo[0], balance, pNbWeeks, 0); } } diff --git a/src/solver/simulation/common-eco-adq.cpp b/src/solver/simulation/common-eco-adq.cpp index 4d8f4a1f11..88ee739648 100644 --- a/src/solver/simulation/common-eco-adq.cpp +++ b/src/solver/simulation/common-eco-adq.cpp @@ -44,7 +44,8 @@ namespace Antares::Solver::Simulation static void RecalculDesEchangesMoyens(Data::Study& study, PROBLEME_HEBDO& problem, const std::vector& balance, - int PasDeTempsDebut) + int PasDeTempsDebut, + uint thread_number) { for (uint i = 0; i < (uint)problem.NombreDePasDeTemps; i++) { @@ -95,7 +96,11 @@ static void RecalculDesEchangesMoyens(Data::Study& study, try { NullResultWriter resultWriter; - OPT_OptimisationHebdomadaire(createOptimizationOptions(study), &problem, study.parameters.adqPatchParams, resultWriter); + OPT_OptimisationHebdomadaire(createOptimizationOptions(study), + &problem, + study.parameters.adqPatchParams, + resultWriter, + thread_number); } catch (Data::UnfeasibleProblemError&) { @@ -197,7 +202,8 @@ bool ShouldUseQuadraticOptimisation(const Data::Study& study) void ComputeFlowQuad(Data::Study& study, PROBLEME_HEBDO& problem, const std::vector& balance, - uint nbWeeks) + uint nbWeeks, + uint thread_number) { uint startTime = study.calendar.days[study.parameters.simulationDays.first].hours.first; @@ -210,7 +216,7 @@ void ComputeFlowQuad(Data::Study& study, for (uint w = 0; w != nbWeeks; ++w) { int PasDeTempsDebut = startTime + (w * problem.NombreDePasDeTemps); - RecalculDesEchangesMoyens(study, problem, balance, PasDeTempsDebut); + RecalculDesEchangesMoyens(study, problem, balance, PasDeTempsDebut, thread_number); } } else diff --git a/src/solver/simulation/common-eco-adq.h b/src/solver/simulation/common-eco-adq.h index 3d1f47c3b0..c7b1c66181 100644 --- a/src/solver/simulation/common-eco-adq.h +++ b/src/solver/simulation/common-eco-adq.h @@ -89,7 +89,8 @@ bool ShouldUseQuadraticOptimisation(const Data::Study& study); void ComputeFlowQuad(Data::Study& study, PROBLEME_HEBDO& problem, const std::vector& balance, - unsigned int nbWeeks); + unsigned int nbWeeks, + uint thread_number); /*! ** \brief Hydro Remix diff --git a/src/solver/simulation/economy.cpp b/src/solver/simulation/economy.cpp index 85effa779a..3c1e93632e 100644 --- a/src/solver/simulation/economy.cpp +++ b/src/solver/simulation/economy.cpp @@ -260,7 +260,7 @@ void Economy::simulationEnd() if (!preproOnly && study.runtime->interconnectionsCount() > 0) { auto balance = retrieveBalance(study, variables); - ComputeFlowQuad(study, pProblemesHebdo[0], balance, pNbWeeks); + ComputeFlowQuad(study, pProblemesHebdo[0], balance, pNbWeeks, 0); } } diff --git a/src/solver/utils/ortools_utils.cpp b/src/solver/utils/ortools_utils.cpp index fc31bf5be7..b8fc5dfd37 100644 --- a/src/solver/utils/ortools_utils.cpp +++ b/src/solver/utils/ortools_utils.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace operations_research; @@ -31,10 +32,51 @@ namespace Antares { namespace Optimization { + +OrtoolsLogHandler::OrtoolsLogHandler(const std::string& solver_name, + const std::filesystem::path& log_directory, + uint thread_number) : + solver_name_(solver_name), log_directory_(log_directory), thread_number_(thread_number) +{ + init(); +} + +OrtoolsLogHandler& OrtoolsLogHandler::operator=(const OrtoolsLogHandler& other) +{ + if (this == &other) + { + return *this; + } + + solver_name_ = other.solver_name_; + log_directory_ = other.log_directory_; + thread_number_ = other.thread_number_; + init(); + return *this; +} +void OrtoolsLogHandler::init() +{ + // auto myid = std::this_thread::get_id(); + // std::stringstream ss; + // ss << myid; + std::ostringstream name; + name << "thread_" << thread_number_ << ".log"; + + log_file_per_thread_ = log_directory_ / name.str(); + + log_writer_.open(log_file_per_thread_, std::ofstream::out | std::ofstream::app); +} + +void OrtoolsLogHandler::copy_log(Solver::IResultWriter& writer) const +{ + writer.addEntryFromFile(log_file_per_thread_.filename().string().c_str(), + log_file_per_thread_.string().c_str()); +} + ProblemSimplexeNommeConverter::ProblemSimplexeNommeConverter( - const std::string& solverName, - const Antares::Optimization::PROBLEME_SIMPLEXE_NOMME* problemeSimplexe) - : solverName_(solverName), problemeSimplexe_(problemeSimplexe) + const std::string& solverName, + const Antares::Optimization::PROBLEME_SIMPLEXE_NOMME* problemeSimplexe) : + solverName_(solverName), problemeSimplexe_(problemeSimplexe) { if (problemeSimplexe_->UseNamedProblems()) { @@ -55,10 +97,6 @@ MPSolver* ProblemSimplexeNommeConverter::Convert() CopyRows(solver); CopyMatrix(solver); - if (problemeSimplexe_->SolverLogs()) - { - solver->EnableOutput(); - } return solver; } @@ -372,12 +410,11 @@ void ORTOOLS_LibererProbleme(MPSolver* solver) { delete solver; } - const std::map OrtoolsUtils::solverMap - = {{"xpress", {"xpress_lp", "xpress"}}, - {"sirius", {"sirius_lp", "sirius"}}, - {"coin", {"clp", "cbc"}}, - {"glpk", {"glpk_lp", "glpk"}}}; + = {{XPRESS, {XPRESS_LP, XPRESS}}, + {SIRIUS, {SIRIUS_LP, SIRIUS}}, + {COIN, {CLP, CBC}}, + {GLPK, {GLPK_LP, GLPK}}}; std::list getAvailableOrtoolsSolverName() { diff --git a/src/solver/utils/ortools_utils.h b/src/solver/utils/ortools_utils.h index 3ea49adce9..024587a94b 100644 --- a/src/solver/utils/ortools_utils.h +++ b/src/solver/utils/ortools_utils.h @@ -2,6 +2,7 @@ #include #include +#include #include @@ -11,6 +12,16 @@ using namespace operations_research; +const std::string XPRESS = "xpress"; +const std::string XPRESS_LP = "xpress_lp"; +const std::string SIRIUS = "sirius"; +const std::string SIRIUS_LP = "sirius_lp"; +const std::string GLPK = "glpk"; +const std::string GLPK_LP = "glpk_lp"; +const std::string COIN = "coin"; +const std::string CBC = "cbc"; +const std::string CLP = "clp"; + void ORTOOLS_EcrireJeuDeDonneesLineaireAuFormatMPS(MPSolver* solver, Antares::Solver::IResultWriter& writer, const std::string& filename); @@ -48,6 +59,38 @@ namespace Antares namespace Optimization { +class OrtoolsLogHandler : public LogHandlerInterface +{ +public: + // TODO won't work in ci, needs ortools update + // see https://github.com/rte-france/or-tools/pull/112 + explicit OrtoolsLogHandler(const std::string& solverName, + const std::filesystem::path& log_directory, + uint thread_number); + + explicit OrtoolsLogHandler(const OrtoolsLogHandler& other) : + OrtoolsLogHandler(other.solver_name_, other.log_directory_, other.thread_number_) + { + } + + OrtoolsLogHandler& operator=(const OrtoolsLogHandler& other); + void message(const char* msg, int nLen = 0) override + { + log_writer_ << std::setw(nLen) << msg << std::endl; + } + + void copy_log(Solver::IResultWriter& writer) const; + +private: + void init(); + + std::ofstream log_writer_; + std::string solver_name_; + std::filesystem::path log_directory_ = "."; + std::filesystem::path log_file_per_thread_ = ""; + uint thread_number_ = 0; +}; + class Nomenclature { public: