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

[ANT-992] Add ramping for thermal clusters #1817

Draft
wants to merge 26 commits into
base: develop
Choose a base branch
from
Draft
Changes from 9 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a64625a
core implementation of the ramping model
bencamus Dec 5, 2023
b9c4f2b
add export/import features for the thermal ramping attributes
bencamus Dec 7, 2023
624f2ce
make the ramping constraints cyclical as the evolution of the product…
bencamus Dec 18, 2023
915e0c7
remove reference to Yuni in the ramping model code
bencamus Dec 18, 2023
f2d469e
removing of a useless header file
bencamus Dec 18, 2023
e78c034
improve readability of ramping model methods by naming their paramete…
bencamus Dec 18, 2023
b9801fe
add subclass to store and check thermal cluster ramping attributes
bencamus Dec 18, 2023
09a2ca9
Small code improvement for ramping
flomnes Dec 18, 2023
5ce4531
Solve build errors with a friend
flomnes Dec 19, 2023
77daa36
add the possibility to enable/disable the ramping model for each cluster
bencamus Dec 20, 2023
8133150
improvement of the implementation of the ON/OFF ramping model features
bencamus Dec 20, 2023
134ad57
merge commit
bencamus Dec 20, 2023
54ef194
add ramping costs to the output operational costs of the cluster
bencamus Jan 15, 2024
d8ca6aa
add dedicated output log for ramping costs
bencamus Jan 16, 2024
d1f983c
implementation of a new version of the ramping equations
bencamus Feb 29, 2024
70ba583
fixing a bug in the definition of the ramping variables bounds + free…
bencamus Mar 6, 2024
7387013
Supression of clusterRampingVariablesIndex and standardisation of the…
sylvmara Mar 7, 2024
ab1b701
Harmonisation of ramp checking
sylvmara Mar 7, 2024
8b6ce0b
merge with Antares master
bencamus Apr 12, 2024
3b17545
merge last version of ramping model with last version of Antares
bencamus Apr 12, 2024
50f7a48
removing ramping files that are useless with the last version of Antares
bencamus Apr 12, 2024
554455d
changing constraint equation related to power output variation of the…
bencamus Apr 16, 2024
3625bd2
Temporarily enable build for CentOS7
flomnes May 3, 2024
44f722d
changing the variable caption from RAMP COST by plant to RAMP COST BY…
bencamus May 15, 2024
e41d0b8
update the ramping constraint 18ter to remove breaking down thermal u…
bencamus Aug 26, 2024
34f2531
Merge remote-tracking branch 'origin/feature/ramping_model_scalian' i…
bencamus Aug 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion src/libs/antares/study/parts/thermal/cluster.cpp
Original file line number Diff line number Diff line change
@@ -137,7 +137,8 @@ Data::ThermalCluster::ThermalCluster(Area* parent) :
forcedLaw(thermalLawUniform),
plannedLaw(thermalLawUniform),
PthetaInf(HOURS_PER_YEAR, 0),
costsTimeSeries(1, CostsTimeSeries())
costsTimeSeries(1, CostsTimeSeries()),
ramping()
Copy link
Member

Choose a reason for hiding this comment

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

This constructor is called by default, there is no need to call it explicitly here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed in commit 77daa36

{
// assert
assert(parent and "A parent for a thermal dispatchable cluster can not be null");
@@ -182,6 +183,9 @@ void Data::ThermalCluster::copyFrom(const ThermalCluster& cluster)
minUpTime = cluster.minUpTime;
minDownTime = cluster.minDownTime;

// ramping
ramping = cluster.ramping;

// spinning
spinning = cluster.spinning;

@@ -479,6 +483,9 @@ void Data::ThermalCluster::reset()
marketBidCost = 0.;
variableomcost = 0.;
costsTimeSeries.resize(1, CostsTimeSeries());

// ramping
ramping.reset();

// modulation
modulation.resize(thermalModulationMax, HOURS_PER_YEAR);
@@ -608,6 +615,9 @@ bool Data::ThermalCluster::integrityCheck()
ret = false;
}*/

// ramping
ret = ramping.checkValidity(parentArea, pName) && ret;

return ret;
}

@@ -822,5 +832,56 @@ bool ThermalCluster::isActive() const {
return enabled && !mustrun;
}

void ThermalCluster::Ramping::reset()
{
powerIncreaseCost = 0;
powerDecreaseCost = 0;
maxUpwardPowerRampingRate = 0;
maxDownwardPowerRampingRate = 0;
}

bool ThermalCluster::Ramping::checkValidity(Area* parentArea, Data::ClusterName clusterName)
{
bool ret = true;

if (maxUpwardPowerRampingRate <= 0)
{
logs.error() << "Thermal cluster: " << parentArea->name << '/' << clusterName
<< ": The maximum upward power ramping rate must greater than zero.";
maxUpwardPowerRampingRate = 1.;
ret = false;
}
if (maxDownwardPowerRampingRate <= 0)
{
logs.error() << "Thermal cluster: " << parentArea->name << '/' << clusterName
<< ": The maximum downward power ramping rate must greater than zero.";
maxDownwardPowerRampingRate = 1.;
ret = false;
}
if (powerIncreaseCost < 0)
{
logs.error() << "Thermal cluster: " << parentArea->name << '/' << clusterName
<< ": The ramping power increase cost must be positive or null.";
powerIncreaseCost = 0.;
ret = false;
}
if (powerDecreaseCost < 0)
{
logs.error() << "Thermal cluster: " << parentArea->name << '/' << clusterName
<< ": The ramping power decrease cost must be positive or null.";
powerDecreaseCost = 0.;
ret = false;
}
return ret;
}

std::ostream& operator<<(std::ostream& os, const ThermalCluster::Ramping& r)
{
return os << r.powerIncreaseCost << '\t'
<< r.powerDecreaseCost << '\t'
<< r.maxUpwardPowerRampingRate << '\t'
<< r.maxDownwardPowerRampingRate;
}

} // namespace Data
} // namespace Antares
26 changes: 26 additions & 0 deletions src/libs/antares/study/parts/thermal/cluster.h
Original file line number Diff line number Diff line change
@@ -340,6 +340,32 @@ class ThermalCluster final : public Cluster, public std::enable_shared_from_this
double marketBidCost = 0;
//! Variable O&M cost (euros/MWh)
double variableomcost = 0;

struct Ramping
{
//! Cost of power increase (euros/MW)
double powerIncreaseCost;
//! Cost of power decrease (euros/MW)
double powerDecreaseCost;
//! Maximum hourly upward power ramping rate (MW/hour)
double maxUpwardPowerRampingRate;
//! Maximum hourly downward power ramping rate (MW/hour)
double maxDownwardPowerRampingRate;

Ramping() :
powerIncreaseCost(0.),
powerDecreaseCost(0.),
maxUpwardPowerRampingRate(0.),
maxDownwardPowerRampingRate(0.)
{
}

void reset();
bool checkValidity(Area* area, Data::ClusterName clusterName);
friend std::ostream& operator<<(std::ostream&, const Ramping& ramping);
};
Ramping ramping;
Copy link
Member

@flomnes flomnes Dec 19, 2023

Choose a reason for hiding this comment

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

Suggested change
Ramping ramping;
std::optional<Ramping> ramping;

By default, the ramping constraints are not enabled for a given cluster. They are enabled only if an object exists within this std::optional.

Possible justification : there is no good default value for maxUpwardPowerRampingRate and maxDownwardPowerRampingRate, ie we can't keep the existing behavior by default, which is very important.

This induces a small difficulty in the variable/constraint counting process, in the sense that we must only take into account cluster for which ramping is enabled.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Commit 8133150 implements now is feature


//@}

/*!
20 changes: 20 additions & 0 deletions src/libs/antares/study/parts/thermal/cluster_list.cpp
Original file line number Diff line number Diff line change
@@ -222,6 +222,15 @@ static bool ThermalClusterLoadFromProperty(ThermalCluster& cluster, const IniFil
if (p->key == "startup-cost")
return p->value.to<double>(cluster.startupCost);

if (p->key == "power-increase-cost")
return p->value.to<double>(cluster.ramping.powerIncreaseCost);
if (p->key == "power-decrease-cost")
return p->value.to<double>(cluster.ramping.powerDecreaseCost);
if (p->key == "max-upward-power-ramping-rate")
return p->value.to<double>(cluster.ramping.maxUpwardPowerRampingRate);
if (p->key == "max-downward-power-ramping-rate")
return p->value.to<double>(cluster.ramping.maxDownwardPowerRampingRate);

if (p->key == "unitcount")
return p->value.to<uint>(cluster.unitCount);
if (p->key == "volatility.planned")
@@ -405,6 +414,17 @@ bool ThermalClusterList::saveToFolder(const AnyString& folder) const
if (!Math::Zero(c.variableomcost))
s->add("variableomcost", Math::Round(c.variableomcost,3));

// ramping
if (c.ramping.powerIncreaseCost != 0)
s->add("power-increase-cost", Math::Round(c.ramping.powerIncreaseCost, 3));
if (c.ramping.powerDecreaseCost != 0)
s->add("power-decrease-cost", Math::Round(c.ramping.powerDecreaseCost, 3));
if (c.ramping.maxUpwardPowerRampingRate != 0)
s->add("max-upward-power-ramping-rate",
Math::Round(c.ramping.maxUpwardPowerRampingRate, 3));
if (c.ramping.maxDownwardPowerRampingRate != 0)
s->add("max-downward-power-ramping-rate",
Math::Round(c.ramping.maxDownwardPowerRampingRate, 3));

//pollutant factor
for (auto const& [key, val] : Pollutant::namesToEnum)
12 changes: 11 additions & 1 deletion src/solver/optimisation/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -38,6 +38,11 @@ set(RTESOLVER_OPT
opt_decompte_variables_et_contraintes_couts_demarrage.cpp
opt_init_minmax_groupes_couts_demarrage.cpp
opt_nombre_min_groupes_demarres_couts_demarrage.cpp
opt_construction_contraintes_rampes_thermiques.cpp
opt_decompte_variables_et_contraintes_rampes_thermiques.cpp
opt_construction_variables_rampes_thermiques.cpp
opt_gestion_des_bornes_rampes_thermiques.cpp
opt_gestion_des_couts_rampes_thermiques.cpp
opt_export_structure.h
opt_export_structure.cpp
base_weekly_optimization.h
@@ -119,7 +124,12 @@ set(RTESOLVER_OPT
constraints/NbDispUnitsMinBoundSinceMinUpTime.cpp
constraints/MinDownTime.h
constraints/MinDownTime.cpp

constraints/RampingIncreaseRate.h
constraints/RampingIncreaseRate.cpp
constraints/RampingDecreaseRate.h
constraints/RampingDecreaseRate.cpp
constraints/PowerOutputVariation.h
constraints/PowerOutputVariation.cpp
)


18 changes: 18 additions & 0 deletions src/solver/optimisation/constraints/ConstraintBuilder.cpp
Original file line number Diff line number Diff line change
@@ -61,6 +61,24 @@ ConstraintBuilder& ConstraintBuilder::DispatchableProduction(unsigned int index,
return *this;
Copy link
Contributor

Choose a reason for hiding this comment

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

remove debug comments

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in commit 624f2ce

}

ConstraintBuilder& ConstraintBuilder::ProductionDecreaseAboveMin(unsigned int index,
double coeff,
int offset,
int delta)
{
AddVariable(GetVariableManager(offset, delta).ProductionDecreaseAboveMin(index), coeff);
return *this;
}

ConstraintBuilder& ConstraintBuilder::ProductionIncreaseAboveMin(unsigned int index,
double coeff,
int offset,
int delta)
{
AddVariable(GetVariableManager(offset, delta).ProductionIncreaseAboveMin(index), coeff);
return *this;
}

ConstraintBuilder& ConstraintBuilder::NumberOfDispatchableUnits(unsigned int index,
double coeff,
int offset,
20 changes: 20 additions & 0 deletions src/solver/optimisation/constraints/ConstraintBuilder.h
Original file line number Diff line number Diff line change
@@ -31,6 +31,16 @@ class VariableManager
return nativeOptimVar.NumeroDeVariableDuPalierThermique[index];
}

int ProductionIncreaseAboveMin(unsigned int index) const
{
return nativeOptimVar.powerRampingIncreaseIndex[index];
}

int ProductionDecreaseAboveMin(unsigned int index) const
{
return nativeOptimVar.powerRampingDecreaseIndex[index];
}

int NumberOfDispatchableUnits(unsigned int index) const
{
return nativeOptimVar.NumeroDeVariableDuNombreDeGroupesEnMarcheDuPalierThermique[index];
@@ -183,6 +193,16 @@ class ConstraintBuilder
int offset = 0,
int delta = 0);

ConstraintBuilder& ProductionDecreaseAboveMin(unsigned int index,
double coeff,
int offset = 0,
int delta = 0);

ConstraintBuilder& ProductionIncreaseAboveMin(unsigned int index,
double coeff,
int offset = 0,
int delta = 0);

ConstraintBuilder& NumberOfDispatchableUnits(unsigned int index,
double coeff,
int offset = 0,
35 changes: 35 additions & 0 deletions src/solver/optimisation/constraints/PowerOutputVariation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "PowerOutputVariation.h"

void PowerOutputVariation::add(int pays, int cluster, int clusterIndex, int pdt, bool Simulation)
{
if (!Simulation)
{
const PALIERS_THERMIQUES& PaliersThermiquesDuPays = problemeHebdo->PaliersThermiquesDuPays[pays];
double maxUpwardPowerRampingRate = PaliersThermiquesDuPays.maxUpwardPowerRampingRate[clusterIndex];
double pminDUnGroupeDuPalierThermique = PaliersThermiquesDuPays.pminDUnGroupeDuPalierThermique[clusterIndex];
// constraint : P(t) - P(t-1) - l * M^+(t) - P^+ + P^- = 0
builder.updateHourWithinWeek(pdt)
.DispatchableProduction(cluster, 1.0)
.DispatchableProduction(cluster, -1.0, -1, problemeHebdo->NombreDePasDeTempsPourUneOptimisation)
.NumberStartingDispatchableUnits(cluster, -pminDUnGroupeDuPalierThermique)
.ProductionIncreaseAboveMin(cluster, -1.0)
.ProductionDecreaseAboveMin(cluster, 1.0)
.equalTo();

if (builder.NumberOfVariables() > 0)
{
ConstraintNamer namer(problemeHebdo->ProblemeAResoudre->NomDesContraintes);

namer.UpdateTimeStep(problemeHebdo->weekInTheYear * 168 + pdt);
namer.UpdateArea(problemeHebdo->NomsDesPays[pays]);

namer.ProductionOutputVariation(problemeHebdo->ProblemeAResoudre->NombreDeContraintes, PaliersThermiquesDuPays.NomsDesPaliersThermiques[clusterIndex]);
}
builder.build();
}
else
{
problemeHebdo->NbTermesContraintesPourLesRampes += 5;
problemeHebdo->ProblemeAResoudre->NombreDeContraintes++;
}
}
20 changes: 20 additions & 0 deletions src/solver/optimisation/constraints/PowerOutputVariation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once
#include "ConstraintBuilder.h"

/*!
* represent 'RampingIncreaseRate' Constraint type
*/
class PowerOutputVariation : private ConstraintFactory
{
public:
using ConstraintFactory::ConstraintFactory;

/*!
* @brief Add variables to the constraint and update constraints Matrix
* @param pays : area
* @param cluster : global index of the cluster
* @param pdt : timestep
* @param Simulation : ---
*/
void add(int pays, int cluster, int clusterIndex, int pdt, bool Simulation);
};
36 changes: 36 additions & 0 deletions src/solver/optimisation/constraints/RampingDecreaseRate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "RampingDecreaseRate.h"

void RampingDecreaseRate::add(int pays, int cluster, int clusterIndex, int pdt, bool Simulation)
{
if (!Simulation)
{
const PALIERS_THERMIQUES& PaliersThermiquesDuPays = problemeHebdo->PaliersThermiquesDuPays[pays];
double maxDownwardPowerRampingRate = PaliersThermiquesDuPays.maxDownwardPowerRampingRate[clusterIndex];
double pmaxDUnGroupeDuPalierThermique = PaliersThermiquesDuPays.PmaxDUnGroupeDuPalierThermique[clusterIndex];
// constraint : P(t) - P(t-1) + R^- * M(t) + u * M^-(t) + u * M^--(t) > 0
builder.updateHourWithinWeek(pdt)
.DispatchableProduction(cluster, 1.0)
.DispatchableProduction(cluster, -1.0, -1, problemeHebdo->NombreDePasDeTempsPourUneOptimisation)
.NumberOfDispatchableUnits(cluster, maxDownwardPowerRampingRate)
.NumberStoppingDispatchableUnits(cluster, pmaxDUnGroupeDuPalierThermique)
.NumberBreakingDownDispatchableUnits(cluster, pmaxDUnGroupeDuPalierThermique)
.greaterThan();

if (builder.NumberOfVariables() > 0)
{
ConstraintNamer namer(problemeHebdo->ProblemeAResoudre->NomDesContraintes);

namer.UpdateTimeStep(problemeHebdo->weekInTheYear * 168 + pdt);
namer.UpdateArea(problemeHebdo->NomsDesPays[pays]);

namer.RampingDecreaseRate(problemeHebdo->ProblemeAResoudre->NombreDeContraintes,
PaliersThermiquesDuPays.NomsDesPaliersThermiques[clusterIndex]);
}
builder.build();
}
else
{
problemeHebdo->NbTermesContraintesPourLesRampes += 5;
problemeHebdo->ProblemeAResoudre->NombreDeContraintes++;
}
}
20 changes: 20 additions & 0 deletions src/solver/optimisation/constraints/RampingDecreaseRate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once
#include "ConstraintBuilder.h"

/*!
* represent 'RampingIncreaseRate' Constraint type
*/
class RampingDecreaseRate : private ConstraintFactory
{
public:
using ConstraintFactory::ConstraintFactory;

/*!
* @brief Add variables to the constraint and update constraints Matrix
* @param pays : area
* @param cluster : global index of the cluster
* @param pdt : timestep
* @param Simulation : ---
*/
void add(int pays, int cluster, int clusterIndex, int pdt, bool Simulation);
};
Loading