From af9f5598752b03a7b00cfb27e8fa8a7db99d8ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jason=20Mar=C3=A9chal?= <45510813+JasonMarechal25@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:49:22 +0200 Subject: [PATCH] [ANT-2206] yaml to model (#2431) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Expose `Antares::Solver::ObjectModel::Library Antares::Solver::ModelConverter::convert(const Antares::Solver::ModelParser::Library& library)` # Implementation details Convert a Library resulting from yaml parsing to a library in the context ob ObjectModel Use builder pattern to construct complexes object such as library and models Declaring a lib ModelConverter as the same level as ObjectModel and ObjectParser prevent either from depending on both. Conceptually they're independent from each others Defines two ValueType enums in two different contexts. Allow for diverging evolution # Limitations - [ANT-2228](https://gopro-tickets.rte-france.com/browse/ANT-2228) Gérer les exception de champs superflus - [ANT-2230](https://gopro-tickets.rte-france.com/browse/ANT-2230) Gérer les champs optionnels - [ANT-2233](https://gopro-tickets.rte-france.com/browse/ANT-2233) Pour les valueType, gérer la capitalisation - [ANT-2233](https://gopro-tickets.rte-france.com/browse/ANT-2233) value_type pour les Parameters - [ANT-2235](https://gopro-tickets.rte-france.com/browse/ANT-2235) contraintes et binding_constraints - [ANT-2236](https://gopro-tickets.rte-france.com/browse/ANT-2236) Constraintes et expressions, inadéquation models/yaml - Ports are not handled in the scope of this PR/ANT-2206 --------- Co-authored-by: Vincent Payet Co-authored-by: payetvin <113102157+payetvin@users.noreply.github.com> Co-authored-by: guilpier-code <62292552+guilpier-code@users.noreply.github.com> Co-authored-by: Florian OMNES --- src/solver/CMakeLists.txt | 1 + src/solver/libModelObject/CMakeLists.txt | 2 + .../solver/libObjectModel/constraint.h | 19 +- .../solver/libObjectModel/expression.h | 16 +- .../antares/solver/libObjectModel/library.h | 50 +++- .../antares/solver/libObjectModel/model.h | 51 +++- .../antares/solver/libObjectModel/parameter.h | 55 +++- .../antares/solver/libObjectModel/port.h | 13 +- .../antares/solver/libObjectModel/portField.h | 13 +- .../antares/solver/libObjectModel/portType.h | 25 +- .../antares/solver/libObjectModel/variable.h | 31 +- src/solver/libModelObject/library.cpp | 101 +++++++ src/solver/libModelObject/model.cpp | 110 ++++++- src/solver/modelConverter/CMakeLists.txt | 25 ++ .../solver/modelConverter/modelConverter.h | 41 +++ src/solver/modelConverter/modelConverter.cpp | 211 ++++++++++++++ src/solver/modelParser/CMakeLists.txt | 2 +- src/solver/modelParser/encoders.hxx | 53 +++- .../solver/modelParser/{model.h => Library.h} | 58 +++- .../antares/solver/modelParser/parser.h | 2 +- src/solver/modelParser/parser.cpp | 2 +- .../src/solver/modelParser/CMakeLists.txt | 3 + .../solver/modelParser/testModelParser.cpp | 176 ++++++++---- .../modelParser/testModelTranslator.cpp | 270 ++++++++++++++++++ 24 files changed, 1227 insertions(+), 103 deletions(-) create mode 100644 src/solver/libModelObject/library.cpp create mode 100644 src/solver/modelConverter/CMakeLists.txt create mode 100644 src/solver/modelConverter/include/antares/solver/modelConverter/modelConverter.h create mode 100644 src/solver/modelConverter/modelConverter.cpp rename src/solver/modelParser/include/antares/solver/modelParser/{model.h => Library.h} (66%) create mode 100644 src/tests/src/solver/modelParser/testModelTranslator.cpp diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index d579606350..be643ebecb 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(infeasible-problem-analysis) add_subdirectory(libModelObject) add_subdirectory(lps) add_subdirectory(misc) +add_subdirectory(modelConverter) add_subdirectory(modelParser) add_subdirectory(modeler) add_subdirectory(optimisation) diff --git a/src/solver/libModelObject/CMakeLists.txt b/src/solver/libModelObject/CMakeLists.txt index ce2ae94d35..bb489afd7e 100644 --- a/src/solver/libModelObject/CMakeLists.txt +++ b/src/solver/libModelObject/CMakeLists.txt @@ -1,6 +1,7 @@ project(LibObjectModel) set(SRC_model + library.cpp model.cpp include/antares/solver/libObjectModel/library.h @@ -19,6 +20,7 @@ set(SRC_model source_group("libObjectModel" FILES ${SRC_model}) add_library(antares-solver-libObjectModel ${SRC_model}) +add_library(Antares::antares-solver-libObjectModel ALIAS antares-solver-libObjectModel) target_include_directories(antares-solver-libObjectModel PUBLIC diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/constraint.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/constraint.h index fbf28a3a89..5a66ee8571 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/constraint.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/constraint.h @@ -32,11 +32,24 @@ namespace Antares::Solver::ObjectModel class Constraint { public: - Constraint(); - ~Constraint() = default; + Constraint(std::string name, Expression expression): + id_(std::move(name)), + expression_(std::move(expression)) + { + } + + const std::string& Id() const + { + return id_; + } + + Expression expression() const + { + return expression_; + } private: - std::string name_; + std::string id_; Expression expression_; }; diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/expression.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/expression.h index c83165ee09..87fefbc8c5 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/expression.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/expression.h @@ -28,8 +28,20 @@ namespace Antares::Solver::ObjectModel class Expression { public: - Expression(); - ~Expression() = default; + Expression() = default; + + explicit Expression(std::string value): + value_(std::move(value)) + { + } + + const std::string& Value() const + { + return value_; + } + +private: + std::string value_; }; } // namespace Antares::Solver::ObjectModel diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/library.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/library.h index 9752aeb0f0..1be9d63815 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/library.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/library.h @@ -20,7 +20,8 @@ */ #pragma once -#include +#include +#include #include "model.h" #include "portType.h" @@ -32,15 +33,56 @@ namespace Antares::Solver::ObjectModel class Library { public: - Library(); + Library() = default; ~Library() = default; + const std::string& Id() const + { + return id_; + } + + const std::string& Description() const + { + return description_; + } + + const std::unordered_map& PortTypes() const + { + return portTypes_; + } + + const std::unordered_map& Models() const + { + return models_; + } + private: + friend class LibraryBuilder; + std::string id_; std::string description_; - std::map portTypes_; - std::map models_; + std::unordered_map portTypes_; + std::unordered_map models_; +}; + +/** + * @brief Builder for the Library class + * Follow builder pattern: + * builder.Library().withId("id").withDescription("description").withPortTypes(portList).withModels(modelList).build(); + */ +class LibraryBuilder +{ +public: + LibraryBuilder& withId(const std::string& id); + LibraryBuilder& withDescription(const std::string& description); + LibraryBuilder& withPortTypes(std::vector&& portTypes); + LibraryBuilder& withModels(std::vector&& models); + + Library build(); + +private: + Library library_; }; } // namespace Antares::Solver::ObjectModel diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/model.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/model.h index 0cdeb0bb92..c76b22cd35 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/model.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/model.h @@ -39,22 +39,61 @@ namespace Antares::Solver::ObjectModel class Model { public: - Model(); - ~Model() = default; + const std::string& Id() const + { + return id_; + } - std::vector getConstraints(); + Expression Objective() const + { + return objective_; + } + + const std::map& getConstraints() const + { + return constraints_; + } + + const std::map& Parameters() const + { + return parameters_; + } + + const std::map& Variables() const + { + return variables_; + } + + const std::map& Ports() const + { + return ports_; + } private: + friend class ModelBuilder; std::string id_; Expression objective_; std::map parameters_; std::map variables_; - std::map constraints_; - std::map bindingConstraints_; - std::map ports_; }; +class ModelBuilder +{ +public: + ModelBuilder& withId(std::string_view id); + ModelBuilder& withObjective(Expression objective); + ModelBuilder& withParameters(std::vector&& parameters); + ModelBuilder& withVariables(std::vector&& variables); + ModelBuilder& withPorts(std::vector&& ports); + Model build(); + + ModelBuilder& withConstraints(std::vector&& constraints); + +private: + Model model_; +}; + } // namespace Antares::Solver::ObjectModel diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/parameter.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/parameter.h index 55be8984e0..ec25ff632e 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/parameter.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/parameter.h @@ -35,14 +35,59 @@ namespace Antares::Solver::ObjectModel class Parameter { public: - Parameter(); - ~Parameter() = default; + /** Using enum class to avoid primitive obsession. Mainly prevent headhaches when reading + * Parameter("Param", ValueType::FLOAT, false, true) + * Avoid mixing wich value is which boolean parameter + */ + + enum class TimeDependent : bool + { + NO = false, + YES = true + }; + + enum class ScenarioDependent : bool + { + NO = false, + YES = true + }; + + explicit Parameter(std::string id, + ValueType type, + TimeDependent timeDependent, + ScenarioDependent scenarioDependent): + id_(std::move(id)), + type_(type), + timeDependent_(timeDependent), + scenarioDependent_(scenarioDependent) + { + } + + const std::string& Id() const + { + return id_; + } + + ValueType Type() const + { + return type_; + } + + bool isTimeDependent() const + { + return timeDependent_ == TimeDependent::YES; + } + + bool isScenarioDependent() const + { + return scenarioDependent_ == ScenarioDependent::YES; + } private: - std::string name_; + std::string id_; ValueType type_; - bool timeDependent_ = true; // optional at construction - bool scenarioDependent_ = true; // optional at construction + TimeDependent timeDependent_ = TimeDependent::YES; // optional at construction + ScenarioDependent scenarioDependent_ = ScenarioDependent::YES; // optional at construction }; } // namespace Antares::Solver::ObjectModel diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/port.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/port.h index ddf89277cd..da8dc3d99f 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/port.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/port.h @@ -30,11 +30,18 @@ namespace Antares::Solver::ObjectModel class Port { public: - Port(); - ~Port() = default; + const std::string& Id() const + { + return id_; + } + + PortType Type() const + { + return type_; + } private: - std::string name_; + std::string id_; PortType type_; }; diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/portField.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/portField.h index 4484f62230..78325db1af 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/portField.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/portField.h @@ -27,8 +27,19 @@ namespace Antares::Solver::ObjectModel class PortField { +public: + explicit PortField(const std::string& id): + id_(id) + { + } + + const std::string& Id() const + { + return id_; + } + private: - std::string name; + std::string id_; }; } // namespace Antares::Solver::ObjectModel diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/portType.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/portType.h index 662cbb7041..8fbfb15dca 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/portType.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/portType.h @@ -31,8 +31,29 @@ namespace Antares::Solver::ObjectModel class PortType { public: - PortType(); - ~PortType() = default; + PortType(const std::string& id, + const std::string& description, + std::vector&& fields): + id_(id), + description_(description), + fields_(std::move(fields)) + { + } + + const std::string& Id() const + { + return id_; + } + + const std::string& Description() const + { + return description_; + } + + const std::vector& Fields() const + { + return fields_; + } private: std::string id_; diff --git a/src/solver/libModelObject/include/antares/solver/libObjectModel/variable.h b/src/solver/libModelObject/include/antares/solver/libObjectModel/variable.h index 51665508ed..44d5762b1d 100644 --- a/src/solver/libModelObject/include/antares/solver/libObjectModel/variable.h +++ b/src/solver/libModelObject/include/antares/solver/libObjectModel/variable.h @@ -33,11 +33,36 @@ namespace Antares::Solver::ObjectModel class Variable { public: - Variable(); - ~Variable() = default; + Variable(std::string id, Expression lower_bound, Expression upper_bound, ValueType type): + id_(std::move(id)), + type_(type), + lowerBound_(lower_bound), + upperBound_(upper_bound) + { + } + + const std::string& Id() const + { + return id_; + } + + ValueType Type() const + { + return type_; + } + + Expression LowerBound() const + { + return lowerBound_; + } + + Expression UpperBound() const + { + return upperBound_; + } private: - std::string name_; + std::string id_; ValueType type_; Expression lowerBound_; Expression upperBound_; diff --git a/src/solver/libModelObject/library.cpp b/src/solver/libModelObject/library.cpp new file mode 100644 index 0000000000..c531cd223d --- /dev/null +++ b/src/solver/libModelObject/library.cpp @@ -0,0 +1,101 @@ +/* + * 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/libObjectModel/library.h" + +#include +#include +#include +#include + +namespace Antares::Solver::ObjectModel +{ + +/** + * \brief Sets the ID of the library. + * + * \param id The ID to set. + * \return Reference to the LibraryBuilder object. + */ +LibraryBuilder& LibraryBuilder::withId(const std::string& id) +{ + library_.id_ = id; + return *this; +} + +/** + * \brief Sets the description of the library. + * + * \param description The description to set. + * \return Reference to the LibraryBuilder object. + */ +LibraryBuilder& LibraryBuilder::withDescription(const std::string& description) +{ + library_.description_ = description; + return *this; +} + +/** + * \brief Sets the port types of the library. + * + * \param portTypes A vector of PortType objects to set. + * \return Reference to the LibraryBuilder object. + * + * inputs it not garanteed to be valid after the call + */ +LibraryBuilder& LibraryBuilder::withPortTypes(std::vector&& portTypes) +{ + std::transform(portTypes.begin(), + portTypes.end(), + std::inserter(library_.portTypes_, library_.portTypes_.end()), + [](/*Non const to prevent copy*/ PortType& portType) + { return std::make_pair(portType.Id(), std::move(portType)); }); + return *this; +} + +/** + * \brief Sets the models of the library. + * + * \param models A vector of Model objects to set. + * \return Reference to the LibraryBuilder object. + * + * inputs it not garanteed to be valid after the call + */ +LibraryBuilder& LibraryBuilder::withModels(std::vector&& models) +{ + std::transform(models.begin(), + models.end(), + std::inserter(library_.models_, library_.models_.end()), + [](/*Non const to prevent copy*/ Model& model) + { return std::make_pair(model.Id(), std::move(model)); }); + return *this; +} + +/** + * \brief Returns the Library object. + * + * \return The constructed Library object. + */ +Library LibraryBuilder::build() +{ + return library_; +} +} // namespace Antares::Solver::ObjectModel diff --git a/src/solver/libModelObject/model.cpp b/src/solver/libModelObject/model.cpp index 19378317f8..bfb79e6882 100644 --- a/src/solver/libModelObject/model.cpp +++ b/src/solver/libModelObject/model.cpp @@ -19,14 +19,120 @@ ** along with Antares_Simulator. If not, see . */ +#include +#include +#include +#include + #include namespace Antares::Solver::ObjectModel { -std::vector getConstraints() +/** + * \brief Builds and returns the Model object. + * + * \return The constructed Model object. + */ +Model ModelBuilder::build() +{ + return model_; +} + +/** + * \brief Sets the ID of the model. + * + * \param id The ID to set. + * \return Reference to the ModelBuilder object. + */ +ModelBuilder& ModelBuilder::withId(std::string_view id) +{ + model_.id_ = id; + return *this; +} + +/** + * \brief Sets the objective of the model. + * + * \param objective The Expression object representing the objective. + * \return Reference to the ModelBuilder object. + */ +ModelBuilder& ModelBuilder::withObjective(Expression objective) +{ + model_.objective_ = objective; + return *this; +} + +/** + * \brief Sets the parameters of the model. + * + * \param parameters A vector of Parameter objects to set. + * \return Reference to the ModelBuilder object. + * + * inputs it not garanteed to be valid after the call + */ +ModelBuilder& ModelBuilder::withParameters(std::vector&& parameters) +{ + std::transform(parameters.begin(), + parameters.end(), + std::inserter(model_.parameters_, model_.parameters_.end()), + [](/*Non const to prevent copy*/ Parameter& parameter) + { return std::make_pair(parameter.Id(), std::move(parameter)); }); + return *this; +} + +/** + * \brief Sets the variables of the model. + * + * \param variables A vector of Variable objects to set. + * \return Reference to the ModelBuilder object. + * + * inputs it not garanteed to be valid after the call + */ +ModelBuilder& ModelBuilder::withVariables(std::vector&& variables) +{ + std::transform(variables.begin(), + variables.end(), + std::inserter(model_.variables_, model_.variables_.end()), + [](/*Non const to prevent copy*/ Variable& variable) + { return std::make_pair(variable.Id(), std::move(variable)); }); + return *this; +} + +/** + * \brief Sets the ports of the model. + * + * \param ports A vector of Port objects to set. + * \return Reference to the ModelBuilder object. + * + * inputs it not garanteed to be valid after the call + */ +ModelBuilder& ModelBuilder::withPorts(std::vector&& ports) +{ + std::transform(ports.begin(), + ports.end(), + std::inserter(model_.ports_, model_.ports_.end()), + [](/*Non const to prevent copy*/ Port& port) + { return std::make_pair(port.Id(), std::move(port)); }); + return *this; +} + +/** + * \brief Sets the ID of the library. + * + * \param id The ID to set. + * \return Reference to the LibraryBuilder object. + * + * inputs it not garanteed to be valid after the call + */ +ModelBuilder& ModelBuilder::withConstraints(std::vector&& constraints) { - return std::vector(); + std::transform(constraints.begin(), + constraints.end(), + std::inserter(model_.constraints_, model_.constraints_.end()), + [](/*Non const to prevent copy*/ Constraint& constraint) + { return std::make_pair(constraint.Id(), std::move(constraint)); }); + return *this; } } // namespace Antares::Solver::ObjectModel diff --git a/src/solver/modelConverter/CMakeLists.txt b/src/solver/modelConverter/CMakeLists.txt new file mode 100644 index 0000000000..d679c3f7ff --- /dev/null +++ b/src/solver/modelConverter/CMakeLists.txt @@ -0,0 +1,25 @@ +set(SOURCES + modelConverter.cpp + include/antares/solver/modelConverter/modelConverter.h +) + +# Create the library +add_library(modelConverter STATIC ${SOURCES}) +add_library(Antares::modelConverter ALIAS modelConverter) + +# Specify include directories +target_include_directories(modelConverter + PUBLIC + $ +) + +# Link dependencies (if any) +target_link_libraries(modelConverter + PRIVATE + Antares::antares-solver-libObjectModel + Antares::modelParser +) + +install(DIRECTORY include/antares + DESTINATION "include" +) \ No newline at end of file diff --git a/src/solver/modelConverter/include/antares/solver/modelConverter/modelConverter.h b/src/solver/modelConverter/include/antares/solver/modelConverter/modelConverter.h new file mode 100644 index 0000000000..4d2212b749 --- /dev/null +++ b/src/solver/modelConverter/include/antares/solver/modelConverter/modelConverter.h @@ -0,0 +1,41 @@ + +/* + * 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::Solver +{ +namespace ObjectModel +{ +class Library; +} + +namespace ModelParser +{ +class Library; +} +} // namespace Antares::Solver + +namespace Antares::Solver::ModelConverter +{ +Antares::Solver::ObjectModel::Library convert(const Antares::Solver::ModelParser::Library& library); +} diff --git a/src/solver/modelConverter/modelConverter.cpp b/src/solver/modelConverter/modelConverter.cpp new file mode 100644 index 0000000000..82cab77b92 --- /dev/null +++ b/src/solver/modelConverter/modelConverter.cpp @@ -0,0 +1,211 @@ +/* + * 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/modelConverter/modelConverter.h" + +#include + +#include "antares/solver/libObjectModel/constraint.h" +#include "antares/solver/libObjectModel/library.h" +#include "antares/solver/libObjectModel/model.h" +#include "antares/solver/libObjectModel/parameter.h" +#include "antares/solver/libObjectModel/port.h" +#include "antares/solver/libObjectModel/portType.h" +#include "antares/solver/libObjectModel/variable.h" +#include "antares/solver/modelParser/Library.h" + +namespace Antares::Solver::ModelConverter +{ + +/** + * \brief Converts parameters from ModelParser::Model to ObjectModel::Parameter. + * + * \param model The ModelParser::Model object containing parameters. + * \return A vector of ObjectModel::Parameter objects. + */ +std::vector convertTypes( + const Antares::Solver::ModelParser::Library& library) +{ + // Convert portTypes to Antares::Solver::ObjectModel::PortType + std::vector out; + for (const auto& portType: library.port_types) + { + std::vector fields; + for (const auto& field: portType.fields) + { + fields.emplace_back(Antares::Solver::ObjectModel::PortField{field}); + } + Antares::Solver::ObjectModel::PortType portTypeModel(portType.id, + portType.description, + std::move(fields)); + out.emplace_back(std::move(portTypeModel)); + } + return out; +} + +/** + * \brief Converts a ModelParser::ValueType to an ObjectModel::ValueType. + * + * \param type The ModelParser::ValueType to convert. + * \return The corresponding ObjectModel::ValueType. + * \throws std::runtime_error if the type is unknown. + */ +std::vector convertParameters( + const Antares::Solver::ModelParser::Model& model) +{ + std::vector parameters; + for (const auto& parameter: model.parameters) + { + parameters.emplace_back(Antares::Solver::ObjectModel::Parameter{ + parameter.id, + Antares::Solver::ObjectModel::ValueType::FLOAT, // TODO: change to correct type + static_cast( + parameter.time_dependent), + static_cast( + parameter.scenario_dependent)}); + } + return parameters; +} + +/** + * \brief Converts variables from ModelParser::Model to ObjectModel::Variable. + * + * \param model The ModelParser::Model object containing variables. + * \return A vector of ObjectModel::Variable objects. + */ +Antares::Solver::ObjectModel::ValueType convertType(Antares::Solver::ModelParser::ValueType type) +{ + using namespace std::string_literals; + switch (type) + { + case Antares::Solver::ModelParser::ValueType::FLOAT: + return Antares::Solver::ObjectModel::ValueType::FLOAT; + case Antares::Solver::ModelParser::ValueType::INTEGER: + return Antares::Solver::ObjectModel::ValueType::INTEGER; + case Antares::Solver::ModelParser::ValueType::BOOL: + return Antares::Solver::ObjectModel::ValueType::BOOL; + default: + throw std::runtime_error("Unknown type: " + Antares::Solver::ModelParser::toString(type)); + } +} + +/** + * \brief Converts ports from ModelParser::Model to ObjectModel::Port. + * + * \param model The ModelParser::Model object containing ports. + * \return A vector of ObjectModel::Port objects. + */ +std::vector convertVariables( + const Antares::Solver::ModelParser::Model& model) +{ + std::vector variables; + for (const auto& variable: model.variables) + { + variables.emplace_back(Antares::Solver::ObjectModel::Variable{ + variable.id, + Antares::Solver::ObjectModel::Expression{variable.lower_bound}, + Antares::Solver::ObjectModel::Expression{variable.upper_bound}, + convertType(variable.variable_type)}); + } + return variables; +} + +/** + * \brief Converts constraints from ModelParser::Model to ObjectModel::Constraint. + * + * \param model The ModelParser::Model object containing constraints. + * \return A vector of ObjectModel::Constraint objects. + */ +std::vector convertPorts( + const Antares::Solver::ModelParser::Model& model) +{ + std::vector ports; + for (const auto& port: model.ports) + { + // ports.emplace_back(Antares::Solver::ObjectModel::Port{port.name, port.type}); + } + return ports; +} + +std::vector convertConstraints( + const Antares::Solver::ModelParser::Model& model) +{ + std::vector constraints; + for (const auto& constraint: model.constraints) + { + constraints.emplace_back(Antares::Solver::ObjectModel::Constraint{ + constraint.id, + Antares::Solver::ObjectModel::Expression{constraint.expression}}); + } + return constraints; +} + +/** + * \brief Converts models from ModelParser::Library to ObjectModel::Model. + * + * \param library The ModelParser::Library object containing models. + * \return A vector of ObjectModel::Model objects. + */ +std::vector convertModels( + const Antares::Solver::ModelParser::Library& library) +{ + std::vector models; + for (const auto& model: library.models) + { + Antares::Solver::ObjectModel::ModelBuilder modelBuilder; + std::vector parameters = convertParameters(model); + std::vector variables = convertVariables(model); + std::vector ports = convertPorts(model); + std::vector constraints = convertConstraints( + model); + + auto modelObj = modelBuilder.withId(model.id) + .withObjective(Antares::Solver::ObjectModel::Expression{model.objective}) + .withParameters(std::move(parameters)) + .withVariables(std::move(variables)) + .withPorts(std::move(ports)) + .withConstraints(std::move(constraints)) + .build(); + models.emplace_back(std::move(modelObj)); + } + return models; +} + +/** + * \brief Converts a ModelParser::Library object to an ObjectModel::Library object. + * + * \param library The ModelParser::Library object to convert. + * \return The corresponding ObjectModel::Library object. + */ +Antares::Solver::ObjectModel::Library convert(const Antares::Solver::ModelParser::Library& library) +{ + Antares::Solver::ObjectModel::LibraryBuilder builder; + std::vector portTypes = convertTypes(library); + std::vector models = convertModels(library); + Antares::Solver::ObjectModel::Library lib = builder.withId(library.id) + .withDescription(library.description) + .withPortTypes(std::move(portTypes)) + .withModels(std::move(models)) + .build(); + return lib; +} + +} // namespace Antares::Solver::ModelConverter diff --git a/src/solver/modelParser/CMakeLists.txt b/src/solver/modelParser/CMakeLists.txt index efc5baca38..1881ba78fa 100644 --- a/src/solver/modelParser/CMakeLists.txt +++ b/src/solver/modelParser/CMakeLists.txt @@ -2,7 +2,7 @@ find_package(yaml-cpp REQUIRED) set(SOURCES parser.cpp - #encoders.hxx + encoders.hxx include/antares/solver/modelParser/parser.h ) diff --git a/src/solver/modelParser/encoders.hxx b/src/solver/modelParser/encoders.hxx index 61e22e9bc9..940cc9ac88 100644 --- a/src/solver/modelParser/encoders.hxx +++ b/src/solver/modelParser/encoders.hxx @@ -22,7 +22,7 @@ #pragma once -#include "antares/solver/modelParser/model.h" +#include "antares/solver/modelParser/Library.h" #include "yaml-cpp/yaml.h" @@ -38,13 +38,42 @@ struct convert { return false; } - rhs.name = node["name"].as(); + rhs.id = node["id"].as(); rhs.time_dependent = node["time-dependent"].as(); rhs.scenario_dependent = node["scenario-dependent"].as(); return true; } }; +template<> +struct convert +{ + static bool decode(const Node& node, Antares::Solver::ModelParser::ValueType& rhs) + { + if (!node.IsScalar()) + { + return false; + } + if (node.as() == "FLOAT") + { + rhs = Antares::Solver::ModelParser::ValueType::FLOAT; + } + else if (node.as() == "INTEGER") + { + rhs = Antares::Solver::ModelParser::ValueType::INTEGER; + } + else if (node.as() == "BOOL") + { + rhs = Antares::Solver::ModelParser::ValueType::BOOL; + } + else + { + return false; + } + return true; + } +}; + template<> struct convert { @@ -54,9 +83,11 @@ struct convert { return false; } - rhs.name = node["name"].as(); - rhs.lower_bound = node["lower-bound"].as(); - rhs.upper_bound = node["upper-bound"].as(); + rhs.id = node["id"].as(); + rhs.lower_bound = node["lower-bound"].as(); + rhs.upper_bound = node["upper-bound"].as(); + rhs.variable_type = node["variable-type"].as( + Antares::Solver::ModelParser::ValueType::FLOAT); return true; } }; @@ -70,7 +101,7 @@ struct convert { return false; } - rhs.name = node["name"].as(); + rhs.id = node["id"].as(); rhs.type = node["type"].as(); return true; } @@ -101,7 +132,7 @@ struct convert { return false; } - rhs.name = node["name"].as(); + rhs.id = node["id"].as(); rhs.expression = node["expression"].as(); return true; } @@ -117,7 +148,7 @@ struct convert return false; } rhs.id = node["id"].as(); - rhs.description = node["description"].as(); + rhs.description = node["description"].as(""); rhs.parameters = node["parameters"] .as>(); rhs.variables = node["variables"].as>(); @@ -142,10 +173,10 @@ struct convert return false; } rhs.id = node["id"].as(); - rhs.description = node["description"].as(); + rhs.description = node["description"].as(""); for (const auto& field: node["fields"]) { - rhs.fields.push_back(field["name"].as()); + rhs.fields.push_back(field["id"].as()); } return true; } @@ -157,7 +188,7 @@ struct convert static bool decode(const Node& node, Antares::Solver::ModelParser::Library& rhs) { rhs.id = node["id"].as(); - rhs.description = node["description"].as(); + rhs.description = node["description"].as(""); rhs.port_types = node["port-types"] .as>(); rhs.models = node["models"].as>(); diff --git a/src/solver/modelParser/include/antares/solver/modelParser/model.h b/src/solver/modelParser/include/antares/solver/modelParser/Library.h similarity index 66% rename from src/solver/modelParser/include/antares/solver/modelParser/model.h rename to src/solver/modelParser/include/antares/solver/modelParser/Library.h index 44b15584f5..d806cb1c3f 100644 --- a/src/solver/modelParser/include/antares/solver/modelParser/model.h +++ b/src/solver/modelParser/include/antares/solver/modelParser/Library.h @@ -21,6 +21,7 @@ */ #pragma once +#include #include #include @@ -29,21 +30,66 @@ namespace Antares::Solver::ModelParser // Define structures struct Parameter { - std::string name; + std::string id; bool time_dependent; bool scenario_dependent; }; +enum class ValueType +{ + FLOAT, + INTEGER, + BOOL +}; + +inline std::string toString(const ValueType& value_type) +{ + using namespace std::string_literals; + switch (value_type) + { + case ValueType::FLOAT: + return "FLOAT"s; + case ValueType::INTEGER: + return "INTEGER"s; + case ValueType::BOOL: + return "BOOL"s; + default: + return "UNKNOWN"s; + } +} + +inline std::ostream& operator<<(std::ostream& os, const ValueType& value_type) +{ + using namespace std::string_literals; + switch (value_type) + { + case ValueType::FLOAT: + os << "FLOAT"s; + break; + case ValueType::INTEGER: + os << "INTEGER"s; + break; + case ValueType::BOOL: + os << "BOOL"s; + break; + default: + os << "UNKNOWN"s; + break; + } + return os; +} + struct Variable { - std::string name; - double lower_bound; - double upper_bound; + std::string id; + std::string lower_bound; + std::string upper_bound; + ValueType variable_type; }; struct Port { - std::string name; + std::string id; std::string type; }; @@ -56,7 +102,7 @@ struct PortFieldDefinition struct Constraint { - std::string name; + std::string id; std::string expression; }; diff --git a/src/solver/modelParser/include/antares/solver/modelParser/parser.h b/src/solver/modelParser/include/antares/solver/modelParser/parser.h index 1447af9f2b..0c042ce027 100644 --- a/src/solver/modelParser/include/antares/solver/modelParser/parser.h +++ b/src/solver/modelParser/include/antares/solver/modelParser/parser.h @@ -21,7 +21,7 @@ */ #pragma once -#include "antares/solver/modelParser/model.h" +#include "antares/solver/modelParser/Library.h" namespace Antares::Solver::ModelParser { diff --git a/src/solver/modelParser/parser.cpp b/src/solver/modelParser/parser.cpp index f4c1f9e515..13d1b120eb 100644 --- a/src/solver/modelParser/parser.cpp +++ b/src/solver/modelParser/parser.cpp @@ -21,7 +21,7 @@ #include "antares/solver/modelParser/parser.h" -#include "antares/solver/modelParser/model.h" +#include "antares/solver/modelParser/Library.h" #include "encoders.hxx" diff --git a/src/tests/src/solver/modelParser/CMakeLists.txt b/src/tests/src/solver/modelParser/CMakeLists.txt index 11123853b0..fc65d70e93 100644 --- a/src/tests/src/solver/modelParser/CMakeLists.txt +++ b/src/tests/src/solver/modelParser/CMakeLists.txt @@ -1,6 +1,7 @@ # Add source files set(SOURCE_FILES testModelParser.cpp + testModelTranslator.cpp ) # Add executable @@ -10,7 +11,9 @@ add_executable(TestModelParser ${SOURCE_FILES}) target_link_libraries(TestModelParser PRIVATE Boost::unit_test_framework + Antares::modelConverter Antares::modelParser + Antares::antares-solver-libObjectModel ) # Storing test-toybox under the folder Unit-tests in the IDE diff --git a/src/tests/src/solver/modelParser/testModelParser.cpp b/src/tests/src/solver/modelParser/testModelParser.cpp index dff40144fb..9e94dd2d52 100644 --- a/src/tests/src/solver/modelParser/testModelParser.cpp +++ b/src/tests/src/solver/modelParser/testModelParser.cpp @@ -30,7 +30,7 @@ using namespace std::string_literals; // Test empty library -BOOST_AUTO_TEST_CASE(test_empty_library) +BOOST_AUTO_TEST_CASE(EmpyLibrary_is_valid) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(test_empty_library) } // Test library with id and description -BOOST_AUTO_TEST_CASE(test_library_id_description) +BOOST_AUTO_TEST_CASE(library_id_and_description_parsed_properly) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -61,12 +61,10 @@ BOOST_AUTO_TEST_CASE(test_library_id_description) Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_CHECK_EQUAL(libraryObj.id, "test_id"); BOOST_CHECK_EQUAL(libraryObj.description, "test_description"); - BOOST_CHECK(libraryObj.port_types.empty()); - BOOST_CHECK(libraryObj.models.empty()); } // Test library with port types -BOOST_AUTO_TEST_CASE(test_library_port_types) +BOOST_AUTO_TEST_CASE(port_types_properly_parsed) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -77,7 +75,7 @@ BOOST_AUTO_TEST_CASE(test_library_port_types) - id: "porttype_id" description: "porttype_description" fields: - - name: "port_name" + - id: "port_name" models: [] )"s; Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); @@ -89,7 +87,7 @@ BOOST_AUTO_TEST_CASE(test_library_port_types) } // Test library with multiple port types -BOOST_AUTO_TEST_CASE(test_library_multiple_port_types) +BOOST_AUTO_TEST_CASE(library_can_contain_multiple_port_types) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -100,11 +98,11 @@ BOOST_AUTO_TEST_CASE(test_library_multiple_port_types) - id: "porttype_id1" description: "porttype_description1" fields: - - name: "port_name1" + - id: "port_name1" - id: "porttype_id2" description: "porttype_description2" fields: - - name: "port_name2" + - id: "port_name2" models: [] )"s; Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); @@ -120,7 +118,7 @@ BOOST_AUTO_TEST_CASE(test_library_multiple_port_types) } // Test library with models -BOOST_AUTO_TEST_CASE(test_library_models) +BOOST_AUTO_TEST_CASE(models_properly_parsed) { Antares::Solver::ModelParser::Parser parser; const std::string library = R"( @@ -152,7 +150,7 @@ BOOST_AUTO_TEST_CASE(test_library_models) } // Test library with multiple models -BOOST_AUTO_TEST_CASE(test_library_multiple_models) +BOOST_AUTO_TEST_CASE(library_can_contain_multiple_models) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -189,7 +187,7 @@ BOOST_AUTO_TEST_CASE(test_library_multiple_models) } // Test library with one model containing parameters -BOOST_AUTO_TEST_CASE(test_library_model_parameters) +BOOST_AUTO_TEST_CASE(parameters_properly_parsed) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -201,7 +199,7 @@ BOOST_AUTO_TEST_CASE(test_library_model_parameters) - id: "model_id" description: "model_description" parameters: - - name: "param_name" + - id: "param_name" time-dependent: false scenario-dependent: false variables: [] @@ -213,13 +211,13 @@ BOOST_AUTO_TEST_CASE(test_library_model_parameters) Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].parameters.size(), 1); - BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].name, "param_name"); + BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].id, "param_name"); BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].time_dependent, false); BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].scenario_dependent, false); } // Test library with one model containing multiple parameters -BOOST_AUTO_TEST_CASE(test_library_model_multiple_parameters) +BOOST_AUTO_TEST_CASE(model_can_contain_multiple_parameters) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -231,10 +229,10 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_parameters) - id: "model_id" description: "model_description" parameters: - - name: "param_name1" + - id: "param_name1" time-dependent: false scenario-dependent: false - - name: "param_name2" + - id: "param_name2" time-dependent: true scenario-dependent: true variables: [] @@ -246,16 +244,16 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_parameters) Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].parameters.size(), 2); - BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].name, "param_name1"); + BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].id, "param_name1"); BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].time_dependent, false); BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[0].scenario_dependent, false); - BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[1].name, "param_name2"); + BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[1].id, "param_name2"); BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[1].time_dependent, true); BOOST_CHECK_EQUAL(libraryObj.models[0].parameters[1].scenario_dependent, true); } // Test library with one model containing variables -BOOST_AUTO_TEST_CASE(test_library_model_variables) +BOOST_AUTO_TEST_CASE(variables_properly_parsed) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -268,7 +266,7 @@ BOOST_AUTO_TEST_CASE(test_library_model_variables) description: "model_description" parameters: [] variables: - - name: "var_name" + - id: "var_name" lower-bound: 0 upper-bound: 1 ports: [] @@ -279,13 +277,13 @@ BOOST_AUTO_TEST_CASE(test_library_model_variables) Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].variables.size(), 1); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].name, "var_name"); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].lower_bound, 0); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].upper_bound, 1); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].id, "var_name"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].lower_bound, "0"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].upper_bound, "1"); } // Test library with one model containing multiple variables -BOOST_AUTO_TEST_CASE(test_library_model_multiple_variables) +BOOST_AUTO_TEST_CASE(model_can_contain_multiple_variables) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -298,10 +296,10 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_variables) description: "model_description" parameters: [] variables: - - name: "var_name1" + - id: "var_name1" lower-bound: 0 upper-bound: 1 - - name: "var_name2" + - id: "var_name2" lower-bound: -1 upper-bound: 2 ports: [] @@ -312,16 +310,90 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_variables) Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].variables.size(), 2); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].name, "var_name1"); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].lower_bound, 0); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].upper_bound, 1); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[1].name, "var_name2"); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[1].lower_bound, -1); - BOOST_CHECK_EQUAL(libraryObj.models[0].variables[1].upper_bound, 2); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].id, "var_name1"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].lower_bound, "0"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].upper_bound, "1"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[1].id, "var_name2"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[1].lower_bound, "-1"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[1].upper_bound, "2"); +} + +// variable bounds are strings expressions +BOOST_AUTO_TEST_CASE(variables_bounds_are_literals) +{ + Antares::Solver::ModelParser::Parser parser; + const auto library = R"( + library: + id: "lib_id" + description: "lib_description" + port-types: [] + models: + - id: "model_id" + description: "model_description" + parameters: [] + variables: + - id: "var_name" + lower-bound: "near-zero" + upper-bound: "pmax" + ports: [] + port-field-definitions: [] + constraints: [] + objective: "objective" + )"s; + Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].id, "var_name"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].lower_bound, "near-zero"); + BOOST_CHECK_EQUAL(libraryObj.models[0].variables[0].upper_bound, "pmax"); +} + +// variable variable-type +BOOST_AUTO_TEST_CASE(variable_types_can_be_integer_bool_float_default_to_float) +{ + Antares::Solver::ModelParser::Parser parser; + const auto library = R"( + library: + id: "lib_id" + description: "lib_description" + port-types: [] + models: + - id: "model_id" + description: "model_description" + parameters: [] + variables: + - id: "var1" + lower-bound: 0 + upper-bound: 1 + variable-type: "BOOL" + - id: "var2" + lower-bound: 0 + upper-bound: 1 + variable-type: "INTEGER" + - id: "var3" + lower-bound: 0 + upper-bound: 1 + variable-type: "FLOAT" + - id: "var4" + lower-bound: 0 + upper-bound: 1 + ports: [] + port-field-definitions: [] + constraints: [] + objective: "objective" + )"s; + Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); + auto& model = libraryObj.models[0]; + auto& var1 = model.variables[0]; + auto& var2 = model.variables[1]; + auto& var3 = model.variables[2]; + auto& var4 = model.variables[3]; + BOOST_CHECK_EQUAL(var1.variable_type, Antares::Solver::ModelParser::ValueType::BOOL); + BOOST_CHECK_EQUAL(var2.variable_type, Antares::Solver::ModelParser::ValueType::INTEGER); + BOOST_CHECK_EQUAL(var3.variable_type, Antares::Solver::ModelParser::ValueType::FLOAT); + BOOST_CHECK_EQUAL(var4.variable_type, Antares::Solver::ModelParser::ValueType::FLOAT); } // Test library with one model containing ports -BOOST_AUTO_TEST_CASE(test_library_model_ports) +BOOST_AUTO_TEST_CASE(ports_are_properly_parsed) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -335,7 +407,7 @@ BOOST_AUTO_TEST_CASE(test_library_model_ports) parameters: [] variables: [] ports: - - name: "port_name" + - id: "port_name" type: "port_type" port-field-definitions: [] constraints: [] @@ -344,12 +416,12 @@ BOOST_AUTO_TEST_CASE(test_library_model_ports) Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].ports.size(), 1); - BOOST_CHECK_EQUAL(libraryObj.models[0].ports[0].name, "port_name"); + BOOST_CHECK_EQUAL(libraryObj.models[0].ports[0].id, "port_name"); BOOST_CHECK_EQUAL(libraryObj.models[0].ports[0].type, "port_type"); } // Test library with one model containing multiple ports -BOOST_AUTO_TEST_CASE(test_library_model_multiple_ports) +BOOST_AUTO_TEST_CASE(model_can_conatin_multiple_ports) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -363,9 +435,9 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_ports) parameters: [] variables: [] ports: - - name: "port_name1" + - id: "port_name1" type: "port_type1" - - name: "port_name2" + - id: "port_name2" type: "port_type2" port-field-definitions: [] constraints: [] @@ -374,14 +446,14 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_ports) Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].ports.size(), 2); - BOOST_CHECK_EQUAL(libraryObj.models[0].ports[0].name, "port_name1"); + BOOST_CHECK_EQUAL(libraryObj.models[0].ports[0].id, "port_name1"); BOOST_CHECK_EQUAL(libraryObj.models[0].ports[0].type, "port_type1"); - BOOST_CHECK_EQUAL(libraryObj.models[0].ports[1].name, "port_name2"); + BOOST_CHECK_EQUAL(libraryObj.models[0].ports[1].id, "port_name2"); BOOST_CHECK_EQUAL(libraryObj.models[0].ports[1].type, "port_type2"); } // Test library with one model containing port field definitions -BOOST_AUTO_TEST_CASE(test_library_model_port_field_definitions) +BOOST_AUTO_TEST_CASE(model_port_fileds_properly_parsed) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -411,7 +483,7 @@ BOOST_AUTO_TEST_CASE(test_library_model_port_field_definitions) } // Test library with one model containing multiple port field definitions -BOOST_AUTO_TEST_CASE(test_library_model_multiple_port_field_definitions) +BOOST_AUTO_TEST_CASE(model_can_contain_multiple_portfields) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -447,7 +519,7 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_port_field_definitions) } // Test library with one model containing constraints -BOOST_AUTO_TEST_CASE(test_library_model_constraints) +BOOST_AUTO_TEST_CASE(constraints_properly_parsed) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -463,19 +535,19 @@ BOOST_AUTO_TEST_CASE(test_library_model_constraints) ports: [] port-field-definitions: [] constraints: - - name: "constraint_name" + - id: "constraint_name" expression: "expression" objective: "objective" )"s; Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].constraints.size(), 1); - BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[0].name, "constraint_name"); + BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[0].id, "constraint_name"); BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[0].expression, "expression"); } // Test library with one model containing multiple constraints -BOOST_AUTO_TEST_CASE(test_library_model_multiple_constraints) +BOOST_AUTO_TEST_CASE(model_can_contain_multiple_constraints) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( @@ -491,23 +563,23 @@ BOOST_AUTO_TEST_CASE(test_library_model_multiple_constraints) ports: [] port-field-definitions: [] constraints: - - name: "constraint_name1" + - id: "constraint_name1" expression: "expression1" - - name: "constraint_name2" + - id: "constraint_name2" expression: "expression2" objective: "objective" )"s; Antares::Solver::ModelParser::Library libraryObj = parser.parse(library); BOOST_REQUIRE_EQUAL(libraryObj.models.size(), 1); BOOST_REQUIRE_EQUAL(libraryObj.models[0].constraints.size(), 2); - BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[0].name, "constraint_name1"); + BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[0].id, "constraint_name1"); BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[0].expression, "expression1"); - BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[1].name, "constraint_name2"); + BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[1].id, "constraint_name2"); BOOST_CHECK_EQUAL(libraryObj.models[0].constraints[1].expression, "expression2"); } // Test error when model is not a map -BOOST_AUTO_TEST_CASE(test_error_model_not_map) +BOOST_AUTO_TEST_CASE(model_is_not_scalar) { Antares::Solver::ModelParser::Parser parser; const auto library = R"( diff --git a/src/tests/src/solver/modelParser/testModelTranslator.cpp b/src/tests/src/solver/modelParser/testModelTranslator.cpp new file mode 100644 index 0000000000..b8a3149997 --- /dev/null +++ b/src/tests/src/solver/modelParser/testModelTranslator.cpp @@ -0,0 +1,270 @@ +/* + * Copyright 2007-2024, RTE (https://www.rte-france.com) + * See AUTHORS.txt + * SPDX-License-Identifier: MPL-2.0 + * This file is part of Antares-Simulator, + * Adequacy and Performance assessment for interconnected energy networks. + * + * Antares_Simulator is free software: you can redistribute it and/or modify + * it under the terms of the Mozilla Public Licence 2.0 as published by + * the Mozilla Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Antares_Simulator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Mozilla Public Licence 2.0 for more details. + * + * You should have received a copy of the Mozilla Public Licence 2.0 + * along with Antares_Simulator. If not, see . + */ + +#define WIN32_LEAN_AND_MEAN + +#include + +#include + +#include "antares/solver/libObjectModel/library.h" +#include "antares/solver/modelConverter/modelConverter.h" +#include "antares/solver/modelParser/Library.h" + +using namespace Antares::Solver; + +namespace Antares::Solver::ObjectModel +{ +inline std::ostream& operator<<(std::ostream& os, const ValueType& value_type) +{ + using namespace std::string_literals; + switch (value_type) + { + case ValueType::FLOAT: + os << "FLOAT"s; + break; + case ValueType::INTEGER: + os << "INTEGER"s; + break; + case ValueType::BOOL: + os << "BOOL"s; + break; + default: + os << "UNKNOWN"s; + break; + } + return os; +} +} // namespace Antares::Solver::ObjectModel + +// Test empty library +BOOST_AUTO_TEST_CASE(Empty_library_is_valid) +{ + ModelParser::Library library; + ObjectModel::Library lib = ModelConverter::convert(library); + BOOST_CHECK(lib.Id().empty()); + BOOST_CHECK(lib.Description().empty()); + BOOST_CHECK(lib.PortTypes().empty()); + BOOST_CHECK(lib.Models().empty()); +} + +// Test library with id and description +BOOST_AUTO_TEST_CASE(library_id_description_properly_translated) +{ + ModelParser::Library library; + library.id = "test_id"; + library.description = "test_description"; + ObjectModel::Library lib = ModelConverter::convert(library); + BOOST_CHECK_EQUAL(lib.Id(), "test_id"); + BOOST_CHECK_EQUAL(lib.Description(), "test_description"); +} + +// Test library with port types +BOOST_AUTO_TEST_CASE(port_type_with_empty_fileds_properly_translated) +{ + ModelParser::Library library; + ModelParser::PortType portType1{"port1", "flow port", {}}; + ModelParser::PortType portType2{"port2", "impedance port", {}}; + library.port_types = {portType1, portType2}; + ObjectModel::Library lib = ModelConverter::convert(library); + BOOST_REQUIRE_EQUAL(lib.PortTypes().size(), 2); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port1").Id(), "port1"); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port1").Description(), "flow port"); + BOOST_CHECK(lib.PortTypes().at("port1").Fields().empty()); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port2").Id(), "port2"); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port2").Description(), "impedance port"); + BOOST_CHECK(lib.PortTypes().at("port2").Fields().empty()); +} + +// Test library with port types and fields +BOOST_AUTO_TEST_CASE(portType_with_fields_properly_translated) +{ + ModelParser::Library library; + ModelParser::PortType portType1{"port1", "flow port", {"field1", "field2"}}; + ModelParser::PortType portType2{"port2", "impedance port", {"field3", "field4"}}; + library.port_types = {portType1, portType2}; + ObjectModel::Library lib = ModelConverter::convert(library); + BOOST_REQUIRE_EQUAL(lib.PortTypes().at("port1").Fields().size(), 2); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port1").Fields()[0].Id(), "field1"); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port1").Fields()[1].Id(), "field2"); + BOOST_REQUIRE_EQUAL(lib.PortTypes().at("port2").Fields().size(), 2); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port2").Fields()[0].Id(), "field3"); + BOOST_CHECK_EQUAL(lib.PortTypes().at("port2").Fields()[1].Id(), "field4"); +} + +// Test library with models +BOOST_AUTO_TEST_CASE(empty_model_properly_translated) +{ + ModelParser::Library library; + ModelParser::Model model1{.id = "model1", + .description = "description", + .parameters = {}, + .variables = {}, + .ports = {}, + .port_field_definitions = {}, + .constraints = {}, + .objective = "objectives"}; + library.models = {model1}; + ObjectModel::Library lib = ModelConverter::convert(library); + BOOST_REQUIRE_EQUAL(lib.Models().size(), 1); + BOOST_CHECK_EQUAL(lib.Models().at("model1").Id(), "model1"); + BOOST_CHECK_EQUAL(lib.Models().at("model1").Objective().Value(), "objectives"); +} + +// Test library with models and parameters +BOOST_AUTO_TEST_CASE(model_parameters_properly_translated) +{ + ModelParser::Library library; + ModelParser::Model model1{.id = "model1", + .description = "description", + .parameters = {{"param1", true, false}, {"param2", false, false}}, + .variables = {}, + .ports = {}, + .port_field_definitions{}, + .constraints{}, + .objective = "objectives"}; + library.models = {model1}; + ObjectModel::Library lib = ModelConverter::convert(library); + auto& model = lib.Models().at("model1"); + BOOST_REQUIRE_EQUAL(model.Parameters().size(), 2); + auto& parameter1 = model.Parameters().at("param1"); + auto& parameter2 = model.Parameters().at("param2"); + BOOST_CHECK_EQUAL(parameter1.Id(), "param1"); + BOOST_CHECK(parameter1.isTimeDependent()); + BOOST_CHECK(!parameter1.isScenarioDependent()); + BOOST_CHECK_EQUAL(parameter1.Type(), ObjectModel::ValueType::FLOAT); + BOOST_CHECK_EQUAL(parameter2.Id(), "param2"); + BOOST_CHECK(!parameter2.isTimeDependent()); + BOOST_CHECK(!parameter2.isScenarioDependent()); + BOOST_CHECK_EQUAL(parameter2.Type(), ObjectModel::ValueType::FLOAT); +} + +// Test library with models and variables +BOOST_AUTO_TEST_CASE(model_variables_properly_translated) +{ + ModelParser::Library library; + ModelParser::Model model1{ + .id = "model1", + .description = "description", + .parameters = {}, + .variables = {{"var1", "7", "pmax", ModelParser::ValueType::BOOL}, + {"var2", "99999999.9999999", "vcost", ModelParser::ValueType::INTEGER}}, + .ports = {}, + .port_field_definitions = {}, + .constraints = {}, + .objective = "objectives"}; + library.models = {model1}; + ObjectModel::Library lib = ModelConverter::convert(library); + auto& model = lib.Models().at("model1"); + BOOST_REQUIRE_EQUAL(model.Variables().size(), 2); + auto& variable1 = model.Variables().at("var1"); + auto& variable2 = model.Variables().at("var2"); + BOOST_CHECK_EQUAL(variable1.Id(), "var1"); + BOOST_CHECK_EQUAL(variable1.LowerBound().Value(), "7"); + BOOST_CHECK_EQUAL(variable1.UpperBound().Value(), "pmax"); + BOOST_CHECK_EQUAL(variable1.Type(), ObjectModel::ValueType::BOOL); + BOOST_CHECK_EQUAL(variable2.Id(), "var2"); + BOOST_CHECK_EQUAL(variable2.LowerBound().Value(), "99999999.9999999"); + BOOST_CHECK_EQUAL(variable2.UpperBound().Value(), "vcost"); + BOOST_CHECK_EQUAL(variable2.Type(), ObjectModel::ValueType::INTEGER); +} + +// Test library with models and ports +BOOST_AUTO_TEST_CASE(model_ports_properly_translated, *boost::unit_test::disabled()) +{ + ModelParser::Library library; + ModelParser::Model model1{.id = "model1", + .description = "description", + .parameters = {}, + .variables = {}, + .ports = {{"port1", "flow"}, {"port2", "impedance"}}, + .port_field_definitions = {}, + .constraints = {}, + .objective = "objectives"}; + library.models = {model1}; + ObjectModel::Library lib = ModelConverter::convert(library); + auto& model = lib.Models().at("model1"); + // BOOST_REQUIRE_EQUAL(model.Ports().size(), 2); + // auto& port1 = model.Ports().at("port1"); + // auto& port2 = model.Ports().at("port2"); + // BOOST_CHECK_EQUAL(port1.Name(), "port1"); + // BOOST_CHECK_EQUALS port1.Type() + // BOOST_CHECK_EQUAL(port2.Name(), "port2"); + // BOOST_CHECK_EQUALS port2.Type() +} + +// Test library with models and constraints +BOOST_AUTO_TEST_CASE(model_constraints_properly_translated) +{ + ModelParser::Library library; + ModelParser::Model model1{.id = "model1", + .description = "description", + .parameters = {}, + .variables = {}, + .ports = {}, + .port_field_definitions = {}, + .constraints = {{"constraint1", "expression1"}, + {"constraint2", "expression2"}}, + .objective = "objectives"}; + library.models = {model1}; + ObjectModel::Library lib = ModelConverter::convert(library); + auto& model = lib.Models().at("model1"); + BOOST_REQUIRE_EQUAL(model.getConstraints().size(), 2); + auto& constraint1 = model.getConstraints().at("constraint1"); + auto& constraint2 = model.getConstraints().at("constraint2"); + BOOST_CHECK_EQUAL(constraint1.Id(), "constraint1"); + BOOST_CHECK_EQUAL(constraint1.expression().Value(), "expression1"); + BOOST_CHECK_EQUAL(constraint2.Id(), "constraint2"); + BOOST_CHECK_EQUAL(constraint2.expression().Value(), "expression2"); +} + +// Test with 2 models +BOOST_AUTO_TEST_CASE(multiple_models_properly_translated) +{ + ModelParser::Library library; + ModelParser::Model model1{.id = "model1", + .description = "description", + .parameters = {{"param1", true, false}, {"param2", false, false}}, + .variables = {{"varP", "7", "pmin", ModelParser::ValueType::FLOAT}}, + .ports = {}, + .port_field_definitions = {}, + .constraints = {}, + .objective = "objectives"}; + ModelParser::Model model2{ + .id = "model2", + .description = "description", + .parameters = {}, + .variables = {{"var1", "7", "pmax", ModelParser::ValueType::BOOL}, + {"var2", "99999999.9999999", "vcost", ModelParser::ValueType::INTEGER}}, + .ports = {}, + .port_field_definitions = {}, + .constraints = {}, + .objective = "objectives"}; + library.models = {model1, model2}; + ObjectModel::Library lib = ModelConverter::convert(library); + BOOST_REQUIRE_EQUAL(lib.Models().size(), 2); + auto& modelo1 = lib.Models().at("model1"); + BOOST_REQUIRE_EQUAL(modelo1.Parameters().size(), 2); + BOOST_REQUIRE_EQUAL(modelo1.Variables().size(), 1); + auto& modelo2 = lib.Models().at("model2"); + BOOST_REQUIRE_EQUAL(modelo2.Parameters().size(), 0); + BOOST_REQUIRE_EQUAL(modelo2.Variables().size(), 2); +}