Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

For short-term storage cumulative constraints [ANT-1855] #2546

Open
wants to merge 33 commits into
base: develop
Choose a base branch
from

Conversation

a-zakir
Copy link
Contributor

@a-zakir a-zakir commented Dec 20, 2024

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


namespace Antares::Data::ShortTermStorage
{
AdditionalConstraint::ValidateResult AdditionalConstraint::validate() const
AdditionalConstraints::ValidateResult AdditionalConstraints::validate() const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to define a struct ValidateResult : it can be replaced with a std::pair<bool, std::string>.

That said, instead of returning something, we could throw exception (std::runtime_error ?) inside validate().
This would simplify the function a bit :

  • it would return void
  • it would avoid the strange default return {true, ""}

Calling it would be a bit simpler and conventional :

try
{
    additional_constraints.validate();
}
catch (const std::runtime_error& ex)
{
    logs.error() << "Invalid constraint in section: " << section->name;
    logs.error() << ex.what();
    return false;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pair done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't thinh that std::pair<bool, std::string> is more meaningful than struct other { bool ok; string msg;} come on!
pair<bool, string> a; a.first = true;

other o; o.ok = true;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to agree with @a-zakir on this one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggestion of using a std::pair<bool, std::string> instead of a struct was a part of this remark.
What about the other part (with the code sample) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't necessarily want to throw exception, the return boolean is used by the caller
bool AreaList::loadFromFolder --> bool STStorageInput::loadAdditionalConstraints --> AdditionalConstraints::ValidateResult AdditionalConstraints::validate()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in your suggestion:

try
{
    additional_constraints.validate();
}
catch (const std::runtime_error& ex)
{
    logs.error() << "Invalid constraint in section: " << section->name;
    logs.error() << ex.what();
    return false;
}

i am doubtful with throwing an exception to catch it the line after.
can you give an example of code in the project that use this technique?

src/solver/simulation/sim_calcul_economique.cpp Outdated Show resolved Hide resolved
@@ -130,28 +130,33 @@ void ShortTermStorageCumulation::add(int pays)

for (const auto& storage: data.ShortTermStorage[pays])
{
for (const auto& constraint: storage.additional_constraints)
for (const auto& additional_constraints: storage.additional_constraints)
Copy link
Contributor

@guilpier-code guilpier-code Dec 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I find this code painful to understand.
I find it difficult to make the correspondance between the code and the content of the specifications, whereas it should be quite immediate.

I'll try to explain why :

  • Function cumulationConstraintFromVariable : this function creates a constraint. Indeed, it returns a child of class CumulationConstraint. So I would rename it into cumulationConstraintFactory(...)

  • constraintHelper : why do we give this variable that name ? This local variable is the result of a function that returns a CumulationConstraint. So let's name this local variable cumulationConstraint. I personally never append or prefix a name (variable, class) with helper or manager, because it's not meaningful enough, it's too vague.

  • class CumulationConstraint : there is design problem
    This class is an interface and requires its children to define the build(...) function.
    build(...) takes 3 arguments. The 3rd argument is used only by one child, and useless to the other 2.
    To fix this, we could pass the builder and the storage to the constraint factory (function cumulationConstraintFromVariable), and pass the storage internally to the constructor of the cumulation constraint that needs it.
    Doing this, we simplify the call form :
    constraintHelper->build(builder, index, storage);
    to :
    constraintHelper->build(index);

    We could even go further and replace :

    builder.updateHourWithinWeek(hour - 1);
    constraintHelper->build(builder, index, storage);

    with only :

    constraintHelper->build(index, hour); // Notice the new 'hour' as arg
  • namer : useless to build a constraint itself, but used to build the constraint name to print in a MPS file. So we mix the construction of the constraint itself and its MPS name in the same function. We could do both things separately (in different functions), so that each piece is simpler and more understandable, but it's already done in one piece in many places. So is it worth it to separate things here and not elsewhere ?

  • index : when we use it, we don't know which objet this index is related to, so we have to navigate to its definition to realize it's a storage index. Let's name it storageIndex.

  • About ConvertSense(...) :

    • this conversion should have already been made at that (for example when reading input data or right after), it's not the place to do it. Doing it some place else would lighten the code a little bit more.
    • renaming : we talk about operator type on one hand, and about sense on the other hand. We have to choose which unique term to use. I ask to @JMJ-rte, he recommends operator.
      For more clarity, this function should be called convertOperatorToChar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok for the first 3 points, about namer, ths object is instantiated once outside the for loops and one of its method is called inside those loops, i don't see what i can do about it unless to recreate it for every ite of the inner loop via a function.

on concertSense grammar, you'll notice that less, greater and equal are not striclty operator, they are words and their global defintion is Sense

Copy link
Contributor

@guilpier-code guilpier-code Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acceptable like this for me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok


BOOST_AUTO_TEST_SUITE(AdditionalConstraintsTests)

BOOST_AUTO_TEST_CASE(Validate_ClusterIdEmpty)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not hesitate to make test title look like a sentence :
Validate_ClusterIdEmpty ==> cluster_id_is_empty___we_get_validation_error

Comment on lines +501 to +504
ShortTermStorage::AdditionalConstraints constraints;
constraints.cluster_id = "ClusterA";
constraints.variable = "injection";
constraints.operatorType = "less";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For many tests, we :

  • create an additional constraint
  • give the same value to its fields

We should use a fixture creating an additional constraint and giving a default value to its fields

@flomnes flomnes changed the title For short-term storage cumulative constraints For short-term storage cumulative constraints [ANT-1855] Dec 31, 2024
double rhs;
bool enabled = true;
// TODO a lot unused entries
// std::array<double, HOURS_PER_YEAR> rhs = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// std::array<double, HOURS_PER_YEAR> rhs = {};

comment to be removed

std::set<int> hours;
double rhs;
bool enabled = true;
// TODO a lot unused entries
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// TODO a lot unused entries

What do you mean ?
If the comment is meant to remain there, it should be cristal clear or we will forget what it refers to

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rhs values corresponding to the hours set are used. (rhs is a vector 8760 lines)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants