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); +}