Skip to content

Commit

Permalink
Add optional additionnal constraints to short-term storage objects [A…
Browse files Browse the repository at this point in the history
…NT-1855] (#2546)

add week-dependent rhs
New syntax to declare multiple contraints that shares same rhs vector
and type

---------

Co-authored-by: payetvin <[email protected]>
Co-authored-by: Vincent Payet <[email protected]>
Co-authored-by: Florian Omnès <[email protected]>
  • Loading branch information
4 people authored Jan 6, 2025
1 parent 4a30c44 commit c6a693e
Show file tree
Hide file tree
Showing 15 changed files with 856 additions and 111 deletions.
4 changes: 2 additions & 2 deletions src/libs/antares/study/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ set(SRC_STUDY_PART_SHORT_TERM_STORAGE
parts/short-term-storage/series.cpp
include/antares/study/parts/short-term-storage/series.h
include/antares/study/parts/short-term-storage/cluster.h
include/antares/study/parts/short-term-storage/AdditionalConstraint.h
include/antares/study/parts/short-term-storage/additionalConstraints.h
parts/short-term-storage/cluster.cpp
parts/short-term-storage/AdditionalConstraint.cpp
parts/short-term-storage/additionalConstraints.cpp
)
source_group("study\\part\\short-term-storage" FILES ${SRC_STUDY_PART_SHORT_TERM_SOTRAGE})

Expand Down
10 changes: 6 additions & 4 deletions src/libs/antares/study/area/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,12 +1194,14 @@ bool AreaList::loadFromFolder(const StudyLoadOptions& options)

if (fs::exists(stsFolder))
{
for (const auto& [id, area]: areas)
for (const auto& area: areas | std::views::values)
{
fs::path folder = stsFolder / "clusters" / area->id.c_str();
fs::path cluster_folder = stsFolder / "clusters" / area->id.c_str();
ret = area->shortTermStorage.createSTStorageClustersFromIniFile(cluster_folder)
&& ret;

ret = area->shortTermStorage.createSTStorageClustersFromIniFile(folder) && ret;
ret = area->shortTermStorage.LoadConstraintsFromIniFile(folder) && ret;
const auto constraints_folder = stsFolder / "constraints" / area->id.c_str();
ret = area->shortTermStorage.loadAdditionalConstraints(constraints_folder) && ret;
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,46 @@
#pragma once
#include <set>
#include <string>
#include <vector>

namespace Antares::Data::ShortTermStorage
{

struct AdditionalConstraint
class SingleAdditionalConstraint
{
public:
std::set<int> hours;
unsigned int globalIndex = 0;
unsigned int localIndex = 0;
bool isValidHoursRange() const;
};

struct AdditionalConstraints
{
std::string name;
std::string cluster_id;
std::string variable;
std::string operatorType;
std::set<int> hours;
double rhs;
bool enabled = true;
std::vector<double> rhs;

unsigned int globalIndex = 0;
std::vector<SingleAdditionalConstraint> constraints;

struct ValidateResult
{
bool ok;
std::string error_msg;
};

// Number of enabled constraints
std::size_t enabledConstraints() const;

ValidateResult validate() const;

private:
bool isValidVariable() const;
bool isValidOperatorType() const;
bool isValidHoursRange() const;

bool isValidHours() const;
};
} // namespace Antares::Data::ShortTermStorage
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

#include <antares/inifile/inifile.h>

#include "AdditionalConstraint.h"
#include "additionalConstraints.h"
#include "properties.h"
#include "series.h"

Expand All @@ -51,6 +51,6 @@ class STStorageCluster

std::shared_ptr<Series> series = std::make_shared<Series>();
mutable Properties properties;
std::vector<AdditionalConstraint> additional_constraints;
std::vector<AdditionalConstraints> additionalConstraints;
};
} // namespace Antares::Data::ShortTermStorage
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include <filesystem>
#include <string>

#include "AdditionalConstraint.h"
#include "cluster.h"

namespace Antares::Data::ShortTermStorage
Expand All @@ -42,7 +41,7 @@ class STStorageInput
/// Number of enabled ST storages, ignoring disabled ST storages
std::size_t count() const;

bool LoadConstraintsFromIniFile(const std::filesystem::path& filePath);
bool loadAdditionalConstraints(const std::filesystem::path& filePath);

/// erase disabled cluster from the vector
uint removeDisabledClusters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,5 @@ class Series

bool loadFile(const std::filesystem::path& folder, std::vector<double>& vect);
bool writeVectorToFile(const std::string& path, const std::vector<double>& vect);

void fillIfEmpty(std::vector<double>& v, double value);
} // namespace Antares::Data::ShortTermStorage
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@
** You should have received a copy of the Mozilla Public Licence 2.0
** along with Antares_Simulator. If not, see <https://opensource.org/license/mpl-2-0/>.
*/
#include "antares/study/parts/short-term-storage/AdditionalConstraint.h"

#include "antares/study/parts/short-term-storage/additionalConstraints.h"

#include <algorithm>

namespace Antares::Data::ShortTermStorage
{
AdditionalConstraint::ValidateResult AdditionalConstraint::validate() const
AdditionalConstraints::ValidateResult AdditionalConstraints::validate() const
{
if (cluster_id.empty())
{
Expand All @@ -39,27 +42,39 @@ AdditionalConstraint::ValidateResult AdditionalConstraint::validate() const
return {false, "Invalid operator type. Must be 'less', 'equal', or 'greater'."};
}

if (!isValidHoursRange())
if (!isValidHours())
{
return {false, "Hours set contains invalid values. Must be between 1 and 168."};
return {false, "Hours sets contains invalid values. Must be between 1 and 168."};
}

return {true, ""};
}

bool AdditionalConstraint::isValidHoursRange() const
bool SingleAdditionalConstraint::isValidHoursRange() const
{
// `hours` is a sorted set; begin() gives the smallest and prev(end()) gives the largest.
return !hours.empty() && *hours.begin() >= 1 && *std::prev(hours.end()) <= 168;
}

bool AdditionalConstraint::isValidVariable() const
bool AdditionalConstraints::isValidHours() const
{
return std::ranges::all_of(constraints,
[](const auto& constraint)
{ return constraint.isValidHoursRange(); });
}

bool AdditionalConstraints::isValidVariable() const
{
return variable == "injection" || variable == "withdrawal" || variable == "netting";
}

bool AdditionalConstraint::isValidOperatorType() const
bool AdditionalConstraints::isValidOperatorType() const
{
return operatorType == "less" || operatorType == "equal" || operatorType == "greater";
}

std::size_t AdditionalConstraints::enabledConstraints() const
{
return enabled ? constraints.size() : 0;
}
} // namespace Antares::Data::ShortTermStorage
149 changes: 117 additions & 32 deletions src/libs/antares/study/parts/short-term-storage/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include <algorithm>
#include <numeric>
#include <regex>
#include <string>

#include <yuni/io/file.h>
Expand Down Expand Up @@ -75,10 +76,80 @@ bool STStorageInput::createSTStorageClustersFromIniFile(const fs::path& path)
return true;
}

bool STStorageInput::LoadConstraintsFromIniFile(const fs::path& parent_path)
static bool loadHours(std::string hoursStr, AdditionalConstraints& additionalConstraints)
{
std::erase_if(hoursStr, ::isspace);
// Validate the entire string format
if (std::regex fullFormatRegex(R"(^(\[\d+(,\d+)*\])(,(\[\d+(,\d+)*\]))*$)");
!std::regex_match(hoursStr, fullFormatRegex))
{
logs.error() << "In constraint " << additionalConstraints.name
<< ": Input string does not match the required format: " << hoursStr << '\n';
return false;
}
// Split the `hours` field into multiple groups
std::regex groupRegex(R"(\[(.*?)\])");
// Match each group enclosed in square brackets
auto groupsBegin = std::sregex_iterator(hoursStr.begin(), hoursStr.end(), groupRegex);
auto groupsEnd = std::sregex_iterator();
unsigned int localIndex = 0;
for (auto it = groupsBegin; it != groupsEnd; ++it)
{
// Extract the contents of the square brackets
std::string group = (*it)[1].str();
std::stringstream ss(group);
std::string hour;
std::set<int> hourSet;
int hourVal;
while (std::getline(ss, hour, ','))
{
try
{
hourVal = std::stoi(hour);
hourSet.insert(hourVal);
}

catch (const std::invalid_argument& ex)
{
logs.error() << "In constraint " << additionalConstraints.name
<< " Hours sets contains invalid values: " << hour
<< "\n exception thrown: " << ex.what() << '\n';

return false;
}
catch (const std::out_of_range& ex)
{
logs.error() << "In constraint " << additionalConstraints.name
<< " Hours sets contains out of range values: " << hour
<< "\n exception thrown: " << ex.what() << '\n';
return false;
}
}
if (!hourSet.empty())
{
// Add this group to the `hours` vec
additionalConstraints.constraints.push_back(
{.hours = hourSet, .localIndex = localIndex});
++localIndex;
}
}
return true;
}

static bool readRHS(AdditionalConstraints& additionalConstraints, const fs::path& rhsPath)
{
const auto ret = loadFile(rhsPath, additionalConstraints.rhs);
if (ret)
{
fillIfEmpty(additionalConstraints.rhs, 0.0);
}
return ret;
}

bool STStorageInput::loadAdditionalConstraints(const fs::path& parentPath)
{
IniFile ini;
const auto pathIni = parent_path / "additional-constraints.ini";
const auto pathIni = parentPath / "additional-constraints.ini";
if (!ini.open(pathIni, false))
{
logs.info() << "There is no: " << pathIni;
Expand All @@ -87,55 +158,60 @@ bool STStorageInput::LoadConstraintsFromIniFile(const fs::path& parent_path)

for (auto* section = ini.firstSection; section; section = section->next)
{
AdditionalConstraint constraint;
constraint.name = section->name.c_str();
AdditionalConstraints additionalConstraints;
additionalConstraints.name = section->name.c_str();
for (auto* property = section->firstProperty; property; property = property->next)
{
const std::string key = property->key;
const auto value = property->value;

if (key == "cluster")
{
// TODO do i have to transform the name to id? TransformNameIntoID
std::string clusterName;
value.to<std::string>(clusterName);
constraint.cluster_id = transformNameIntoID(clusterName);
additionalConstraints.cluster_id = transformNameIntoID(clusterName);
}
else if (key == "variable")
else if (key == "enabled")
{
value.to<std::string>(constraint.variable);
value.to<bool>(additionalConstraints.enabled);
}
else if (key == "operator")
else if (key == "variable")
{
value.to<std::string>(constraint.operatorType);
value.to<std::string>(additionalConstraints.variable);
}
else if (key == "hours")
else if (key == "operator")
{
std::stringstream ss(value.c_str());
std::string hour;
while (std::getline(ss, hour, ','))
{
int hourVal = std::stoi(hour);
constraint.hours.insert(hourVal);
}
value.to<std::string>(additionalConstraints.operatorType);
}
else if (key == "rhs")
else if (key == "hours" && !loadHours(value.c_str(), additionalConstraints))
{
property->value.to<double>(constraint.rhs);
return false;
}
}

if (auto ret = constraint.validate(); !ret.ok)
// We don't want load RHS and link the STS time if the constraint is disabled
if (!additionalConstraints.enabled)
{
return true;
}

if (const auto rhsPath = parentPath / ("rhs_" + additionalConstraints.name + ".txt");
!readRHS(additionalConstraints, rhsPath))
{
logs.error() << "Error while reading rhs file: " << rhsPath;
return false;
}

if (auto [ok, error_msg] = additionalConstraints.validate(); !ok)
{
logs.error() << "Invalid constraint in section: " << section->name;
logs.error() << ret.error_msg;
logs.error() << error_msg;
return false;
}

auto it = std::find_if(storagesByIndex.begin(),
storagesByIndex.end(),
[&constraint](const STStorageCluster& cluster)
{ return cluster.id == constraint.cluster_id; });
auto it = std::ranges::find_if(storagesByIndex,
[&additionalConstraints](const STStorageCluster& cluster)
{ return cluster.id == additionalConstraints.cluster_id; });
if (it == storagesByIndex.end())
{
logs.warning() << " from file " << pathIni;
Expand All @@ -145,7 +221,7 @@ bool STStorageInput::LoadConstraintsFromIniFile(const fs::path& parent_path)
}
else
{
it->additional_constraints.push_back(constraint);
it->additionalConstraints.push_back(additionalConstraints);
}
}

Expand Down Expand Up @@ -194,11 +270,20 @@ bool STStorageInput::saveDataSeriesToFolder(const std::string& folder) const

std::size_t STStorageInput::cumulativeConstraintCount() const
{
return std::accumulate(storagesByIndex.begin(),
storagesByIndex.end(),
0,
[](int acc, const auto& cluster)
{ return acc + cluster.additional_constraints.size(); });
return std::accumulate(
storagesByIndex.begin(),
storagesByIndex.end(),
0,
[](size_t outer_constraint_count, const auto& cluster)
{
return outer_constraint_count
+ std::accumulate(
cluster.additionalConstraints.begin(),
cluster.additionalConstraints.end(),
0,
[](size_t inner_constraint_count, const auto& additionalConstraints)
{ return inner_constraint_count + additionalConstraints.enabledConstraints(); });
});
}

std::size_t STStorageInput::count() const
Expand Down
Loading

0 comments on commit c6a693e

Please sign in to comment.