From 001e6d534e802167dd71398dbfc94ae92059ccd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Omn=C3=A8s?= Date: Tue, 10 Dec 2024 13:34:21 +0100 Subject: [PATCH] Add interface for Thermal costs, fix wrong values in GUI (#2526) # Advantages - Better separation of responsabilities, remove a lot of `if..else` - Make sure that if costgeneration = setManually (legacy behavior), no new code is executed # Cons - The caller needs to call function `ThermalCluster::getCostProvider` first # GUI Previously, the formula used for modulation columns was "netCost = modulation * modulation * originalCost" for market bid & marginal costs. We fix this formula to "netCost = modulation * originalCost" --- src/libs/antares/study/CMakeLists.txt | 3 + .../antares/study/parts/thermal/cluster.h | 42 ++--- .../study/parts/thermal/cost_provider.h | 82 +++++++++ .../antares/study/parts/thermal/defines.h | 7 +- .../antares/study/parts/thermal/cluster.cpp | 163 ++---------------- .../study/parts/thermal/cluster_list.cpp | 1 - .../parts/thermal/constant_cost_provider.cpp | 47 +++++ .../thermal/scenarized_cost_provider.cpp | 131 ++++++++++++++ src/solver/simulation/common-eco-adq.cpp | 2 +- .../solver/variable/economy/profitByPlant.h | 2 +- src/solver/variable/state.cpp | 5 +- .../thermal-price-definition.cpp | 40 ++--- .../renderer/area/thermalmodulation.cpp | 17 +- .../windows/inspector/property.update.cpp | 5 - 14 files changed, 322 insertions(+), 225 deletions(-) create mode 100644 src/libs/antares/study/include/antares/study/parts/thermal/cost_provider.h create mode 100644 src/libs/antares/study/parts/thermal/constant_cost_provider.cpp create mode 100644 src/libs/antares/study/parts/thermal/scenarized_cost_provider.cpp diff --git a/src/libs/antares/study/CMakeLists.txt b/src/libs/antares/study/CMakeLists.txt index a354de3058..29a89fc631 100644 --- a/src/libs/antares/study/CMakeLists.txt +++ b/src/libs/antares/study/CMakeLists.txt @@ -71,8 +71,11 @@ set(SRC_STUDY_PART_THERMAL include/antares/study/parts/thermal/ecoInput.h parts/thermal/ecoInput.cpp include/antares/study/parts/thermal/cluster.h + include/antares/study/parts/thermal/cost_provider.h include/antares/study/parts/thermal/cluster.hxx parts/thermal/cluster.cpp + parts/thermal/scenarized_cost_provider.cpp + parts/thermal/constant_cost_provider.cpp include/antares/study/parts/thermal/cluster_list.h parts/thermal/cluster_list.cpp include/antares/study/parts/thermal/pollutant.h diff --git a/src/libs/antares/study/include/antares/study/parts/thermal/cluster.h b/src/libs/antares/study/include/antares/study/parts/thermal/cluster.h index 09ada6b8c1..6b6523673c 100644 --- a/src/libs/antares/study/include/antares/study/parts/thermal/cluster.h +++ b/src/libs/antares/study/include/antares/study/parts/thermal/cluster.h @@ -34,6 +34,7 @@ #include "../../fwd.h" #include "../common/cluster.h" +#include "cost_provider.h" #include "defines.h" #include "ecoInput.h" #include "pollutant.h" @@ -64,6 +65,12 @@ enum class LocalTSGenerationBehavior forceNoGen }; +double computeMarketBidCost(double fuelCost, + double fuelEfficiency, + double co2EmissionFactor, + double co2cost, + double variableomcost); + /*! ** \brief A single thermal cluster */ @@ -147,13 +154,6 @@ class ThermalCluster final: public Cluster, public std::enable_shared_from_this< */ void calculationOfSpinning(); - //! \name MarketBid and Marginal Costs - //@{ - /*! - ** \brief Calculation of market bid and marginals costs per hour - */ - void ComputeCostTimeSeries(); - /*! ** \brief Calculation of spinning (reverse) ** @@ -207,10 +207,6 @@ class ThermalCluster final: public Cluster, public std::enable_shared_from_this< bool doWeGenerateTS(bool globalTSgeneration) const; - double getOperatingCost(uint tsIndex, uint hourInTheYear) const; - double getMarginalCost(uint tsIndex, uint hourInTheYear) const; - double getMarketBidCost(uint hourInTheYear, uint year) const; - // Check & correct availability timeseries for thermal availability // Only applies if time-series are ready-made void checkAndCorrectAvailability(); @@ -349,29 +345,16 @@ class ThermalCluster final: public Cluster, public std::enable_shared_from_this< //! Data for the preprocessor std::unique_ptr prepro; - /*! - ** \brief Production Cost, Market Bid Cost and Marginal Cost Matrixes - Per Hour and per Time - *Series - */ - struct CostsTimeSeries - { - std::array productionCostTs; - std::array marketBidCostTS; - std::array marginalCostTS; - }; - - std::vector costsTimeSeries; - EconomicInputData ecoInput; LocalTSGenerationBehavior tsGenBehavior = LocalTSGenerationBehavior::useGlobalParameter; friend class ThermalClusterList; - double computeMarketBidCost(double fuelCost, double co2EmissionFactor, double co2cost); - unsigned int precision() const override; + CostProvider& getCostProvider(); + private: // Calculation of marketBid and marginal costs hourly time series // @@ -383,12 +366,7 @@ class ThermalCluster final: public Cluster, public std::enable_shared_from_this< // Marginal_Cost[€/MWh] = Market_Bid_Cost[€/MWh] = (Fuel_Cost[€/GJ] * 3.6 * 100 / Efficiency[%]) // CO2_emission_factor[tons/MWh] * C02_cost[€/tons] + Variable_O&M_cost[€/MWh] - void fillMarketBidCostTS(); - void fillMarginalCostTS(); - void resizeCostTS(); - void ComputeMarketBidTS(); - void MarginalCostEqualsMarketBid(); - void ComputeProductionCostTS(); + std::unique_ptr costProvider; }; // class ThermalCluster } // namespace Data diff --git a/src/libs/antares/study/include/antares/study/parts/thermal/cost_provider.h b/src/libs/antares/study/include/antares/study/parts/thermal/cost_provider.h new file mode 100644 index 0000000000..2a3b1b1d54 --- /dev/null +++ b/src/libs/antares/study/include/antares/study/parts/thermal/cost_provider.h @@ -0,0 +1,82 @@ +/* + * 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 + +#include +#include + +namespace Antares::Data +{ +class CostProvider +{ +public: + virtual ~CostProvider() = default; + virtual double getOperatingCost(uint serieIndex, uint hourInTheYear) const = 0; + virtual double getMarginalCost(uint serieIndex, uint hourInTheYear) const = 0; + virtual double getMarketBidCost(uint hourInTheYear, uint year) const = 0; +}; + +class ThermalCluster; + +class ConstantCostProvider: public CostProvider +{ +public: + explicit ConstantCostProvider(const ThermalCluster& cluster); + virtual ~ConstantCostProvider() = default; + double getOperatingCost(uint serieIndex, uint hourInTheYear) const override; + double getMarginalCost(uint serieIndex, uint hourInTheYear) const override; + double getMarketBidCost(uint hourInTheYear, uint year) const override; + +private: + const ThermalCluster& cluster; +}; + +class ScenarizedCostProvider: public CostProvider +{ +public: + explicit ScenarizedCostProvider(const ThermalCluster& cluster); + virtual ~ScenarizedCostProvider() = default; + double getOperatingCost(uint serieIndex, uint hourInTheYear) const override; + double getMarginalCost(uint serieIndex, uint hourInTheYear) const override; + double getMarketBidCost(uint hourInTheYear, uint year) const override; + +private: + /*! + ** \brief Production Cost, Market Bid Cost and Marginal Cost Matrixes - Per Hour and per Time + *Series + */ + struct CostsTimeSeries + { + std::array productionCostTs; + std::array marketBidCostTS; + std::array marginalCostTS; + }; + + void resizeCostTS(); + void ComputeMarketBidTS(); + void MarginalCostEqualsMarketBid(); + void ComputeProductionCostTS(); + + std::vector costsTimeSeries; + const ThermalCluster& cluster; +}; +} // namespace Antares::Data diff --git a/src/libs/antares/study/include/antares/study/parts/thermal/defines.h b/src/libs/antares/study/include/antares/study/parts/thermal/defines.h index 5d74c795ff..52d7cd9798 100644 --- a/src/libs/antares/study/include/antares/study/parts/thermal/defines.h +++ b/src/libs/antares/study/include/antares/study/parts/thermal/defines.h @@ -21,16 +21,13 @@ #ifndef __ANTARES_LIBS_STUDY_PARTS_THERMAL_DEFINES_H__ #define __ANTARES_LIBS_STUDY_PARTS_THERMAL_DEFINES_H__ -namespace Antares -{ -namespace Data +namespace Antares::Data { // Forward declaration class ThermalCluster; class ThermalClusterList; class PreproAvailability; -} // namespace Data -} // namespace Antares +} // namespace Antares::Data #endif // __ANTARES_LIBS_STUDY_PARTS_THERMAL_DEFINES_H__ diff --git a/src/libs/antares/study/parts/thermal/cluster.cpp b/src/libs/antares/study/parts/thermal/cluster.cpp index eaab3cada8..96b2d87826 100644 --- a/src/libs/antares/study/parts/thermal/cluster.cpp +++ b/src/libs/antares/study/parts/thermal/cluster.cpp @@ -120,8 +120,7 @@ namespace Data { Data::ThermalCluster::ThermalCluster(Area* parent): Cluster(parent), - PthetaInf(HOURS_PER_YEAR, 0), - costsTimeSeries(1, CostsTimeSeries()) + PthetaInf(HOURS_PER_YEAR, 0) { // assert assert(parent && "A parent for a thermal dispatchable cluster can not be null"); @@ -184,8 +183,6 @@ void Data::ThermalCluster::copyFrom(const ThermalCluster& cluster) fixedCost = cluster.fixedCost; startupCost = cluster.startupCost; marketBidCost = cluster.marketBidCost; - // assignment “=” operator can be used to copy-paste vector by value - costsTimeSeries = cluster.costsTimeSeries; // modulation modulation = cluster.modulation; @@ -299,106 +296,6 @@ void Data::ThermalCluster::calculationOfSpinning() } } -void Data::ThermalCluster::ComputeCostTimeSeries() -{ - switch (costgeneration) - { - case Data::setManually: - fillMarketBidCostTS(); - fillMarginalCostTS(); - break; - case Data::useCostTimeseries: - resizeCostTS(); - ComputeMarketBidTS(); - MarginalCostEqualsMarketBid(); - ComputeProductionCostTS(); - break; - } -} - -void Data::ThermalCluster::fillMarketBidCostTS() -{ - std::fill(costsTimeSeries[0].marketBidCostTS.begin(), - costsTimeSeries[0].marketBidCostTS.end(), - marketBidCost); -} - -void Data::ThermalCluster::fillMarginalCostTS() -{ - std::fill(costsTimeSeries[0].marginalCostTS.begin(), - costsTimeSeries[0].marginalCostTS.end(), - marginalCost); -} - -void ThermalCluster::resizeCostTS() -{ - const uint fuelCostWidth = ecoInput.fuelcost.width; - const uint co2CostWidth = ecoInput.co2cost.width; - const uint tsCount = std::max(fuelCostWidth, co2CostWidth); - - costsTimeSeries.resize(tsCount, CostsTimeSeries()); -} - -void ThermalCluster::ComputeMarketBidTS() -{ - const uint fuelCostWidth = ecoInput.fuelcost.width; - const uint co2CostWidth = ecoInput.co2cost.width; - - double& co2EmissionFactor = emissions.factors[Pollutant::CO2]; - - for (uint tsIndex = 0; tsIndex < costsTimeSeries.size(); ++tsIndex) - { - uint tsIndexFuel = std::min(fuelCostWidth - 1, tsIndex); - uint tsIndexCo2 = std::min(co2CostWidth - 1, tsIndex); - for (uint hour = 0; hour < HOURS_PER_YEAR; ++hour) - { - double& marketBidCostTS = costsTimeSeries[tsIndex].marketBidCostTS[hour]; - - double& fuelcost = ecoInput.fuelcost[tsIndexFuel][hour]; - double& co2cost = ecoInput.co2cost[tsIndexCo2][hour]; - - marketBidCostTS = computeMarketBidCost(fuelcost, co2EmissionFactor, co2cost); - } - } -} - -void ThermalCluster::MarginalCostEqualsMarketBid() -{ - for (auto& timeSeries: costsTimeSeries) - { - auto& source = timeSeries.marketBidCostTS; - auto& destination = timeSeries.marginalCostTS; - std::copy(source.begin(), source.end(), destination.begin()); - } -} - -void ThermalCluster::ComputeProductionCostTS() -{ - if (modulation.width == 0) - { - return; - } - - for (auto& timeSeries: costsTimeSeries) - { - auto& productionCostTS = timeSeries.productionCostTs; - auto& marginalCostTS = timeSeries.marginalCostTS; - - for (uint hour = 0; hour < HOURS_PER_YEAR; ++hour) - { - double hourlyModulation = modulation[Data::thermalModulationCost][hour]; - productionCostTS[hour] = marginalCostTS[hour] * hourlyModulation; - } - } -} - -double Data::ThermalCluster::computeMarketBidCost(double fuelCost, - double co2EmissionFactor, - double co2cost) -{ - return fuelCost * 360.0 / fuelEfficiency + co2EmissionFactor * co2cost + variableomcost; -} - void Data::ThermalCluster::reverseCalculationOfSpinning() { // Nothing to do if the spinning is equal to zero @@ -464,7 +361,6 @@ void Data::ThermalCluster::reset() startupCost = 0.; marketBidCost = 0.; variableomcost = 0.; - costsTimeSeries.resize(1, CostsTimeSeries()); // modulation modulation.resize(thermalModulationMax, HOURS_PER_YEAR); @@ -714,52 +610,23 @@ unsigned int ThermalCluster::precision() const return 0; } -double ThermalCluster::getOperatingCost(uint serieIndex, uint hourInTheYear) const -{ - if (costgeneration == Data::setManually) - { - const auto* modCost = modulation[thermalModulationCost]; - return marginalCost * modCost[hourInTheYear]; - } - else - { - const uint tsIndex = std::min(serieIndex, (uint)costsTimeSeries.size() - 1); - return costsTimeSeries[tsIndex].productionCostTs[hourInTheYear]; - } -} - -double ThermalCluster::getMarginalCost(uint serieIndex, uint hourInTheYear) const +CostProvider& ThermalCluster::getCostProvider() { - const double mod = modulation[Data::thermalModulationCost][hourInTheYear]; - - if (costgeneration == Data::setManually) + if (!costProvider) { - return marginalCost * mod; - } - else - { - const uint tsIndex = std::min(serieIndex, (uint)costsTimeSeries.size() - 1); - return costsTimeSeries[tsIndex].marginalCostTS[hourInTheYear] * mod; - } - /* std::min is necessary in case Availability has e.g 10 TS and both FuelCost & Co2Cost have - only 1TS. Then - > In order to save memory marginalCostTS vector has only one array - inside -> that is used for all (e.g.10) TS*/ -} - -double ThermalCluster::getMarketBidCost(uint hourInTheYear, uint year) const -{ - const double mod = modulation[thermalModulationMarketBid][hourInTheYear]; - - if (costgeneration == Data::setManually) - { - return marketBidCost * mod; - } - else - { - const uint serieIndex = series.getSeriesIndex(year); - const uint tsIndex = std::min(serieIndex, (uint)costsTimeSeries.size() - 1); - return costsTimeSeries[tsIndex].marketBidCostTS[hourInTheYear] * mod; + switch (costgeneration) + { + case Data::setManually: + costProvider = std::make_unique(*this); + break; + case Data::useCostTimeseries: + costProvider = std::make_unique(*this); + break; + default: + throw std::runtime_error("Invalid costgeneration parameter"); + } } + return *costProvider; } void ThermalCluster::checkAndCorrectAvailability() diff --git a/src/libs/antares/study/parts/thermal/cluster_list.cpp b/src/libs/antares/study/parts/thermal/cluster_list.cpp index 99d7fc21ac..e9d7e785e9 100644 --- a/src/libs/antares/study/parts/thermal/cluster_list.cpp +++ b/src/libs/antares/study/parts/thermal/cluster_list.cpp @@ -622,7 +622,6 @@ bool ThermalClusterList::loadEconomicCosts(Study& study, const fs::path& folder) auto filePath = folder / c->parentArea->id.c_str() / c->id(); bool result = c->ecoInput.loadFromFolder(study, filePath); - c->ComputeCostTimeSeries(); return result; }); } diff --git a/src/libs/antares/study/parts/thermal/constant_cost_provider.cpp b/src/libs/antares/study/parts/thermal/constant_cost_provider.cpp new file mode 100644 index 0000000000..0f7133f19d --- /dev/null +++ b/src/libs/antares/study/parts/thermal/constant_cost_provider.cpp @@ -0,0 +1,47 @@ +/* + * 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/study/parts/thermal/cluster.h" + +namespace Antares::Data +{ +ConstantCostProvider::ConstantCostProvider(const ThermalCluster& cluster): + cluster(cluster) +{ +} + +double ConstantCostProvider::getOperatingCost(uint serieIndex, uint hourInTheYear) const +{ + const auto* modCost = cluster.modulation[thermalModulationCost]; + return cluster.marginalCost * modCost[hourInTheYear]; +} + +double ConstantCostProvider::getMarginalCost(uint serieIndex, uint hourInTheYear) const +{ + const double mod = cluster.modulation[Data::thermalModulationCost][hourInTheYear]; + return cluster.marginalCost * mod; +} + +double ConstantCostProvider::getMarketBidCost(uint hourInTheYear, uint year) const +{ + const double mod = cluster.modulation[thermalModulationMarketBid][hourInTheYear]; + return cluster.marketBidCost * mod; +} +} // namespace Antares::Data diff --git a/src/libs/antares/study/parts/thermal/scenarized_cost_provider.cpp b/src/libs/antares/study/parts/thermal/scenarized_cost_provider.cpp new file mode 100644 index 0000000000..7a3232db40 --- /dev/null +++ b/src/libs/antares/study/parts/thermal/scenarized_cost_provider.cpp @@ -0,0 +1,131 @@ +/* + * 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/study/parts/thermal/cluster.h" + +namespace Antares::Data +{ + +ScenarizedCostProvider::ScenarizedCostProvider(const ThermalCluster& cluster): + cluster(cluster) +{ + resizeCostTS(); + ComputeMarketBidTS(); + MarginalCostEqualsMarketBid(); + ComputeProductionCostTS(); +} + +void ScenarizedCostProvider::ComputeProductionCostTS() +{ + if (cluster.modulation.width == 0) + { + return; + } + + for (auto& timeSeries: costsTimeSeries) + { + auto& productionCostTS = timeSeries.productionCostTs; + auto& marginalCostTS = timeSeries.marginalCostTS; + + for (uint hour = 0; hour < HOURS_PER_YEAR; ++hour) + { + double hourlyModulation = cluster.modulation[Data::thermalModulationCost][hour]; + productionCostTS[hour] = marginalCostTS[hour] * hourlyModulation; + } + } +} + +void ScenarizedCostProvider::resizeCostTS() +{ + const uint fuelCostWidth = cluster.ecoInput.fuelcost.width; + const uint co2CostWidth = cluster.ecoInput.co2cost.width; + const uint tsCount = std::max(fuelCostWidth, co2CostWidth); + + costsTimeSeries.resize(tsCount, CostsTimeSeries()); +} + +void ScenarizedCostProvider::MarginalCostEqualsMarketBid() +{ + for (auto& timeSeries: costsTimeSeries) + { + auto& source = timeSeries.marketBidCostTS; + auto& destination = timeSeries.marginalCostTS; + std::copy(source.begin(), source.end(), destination.begin()); + } +} + +double computeMarketBidCost(double fuelCost, + double fuelEfficiency, + double co2EmissionFactor, + double co2cost, + double variableomcost) +{ + return fuelCost * 360.0 / fuelEfficiency + co2EmissionFactor * co2cost + variableomcost; +} + +void ScenarizedCostProvider::ComputeMarketBidTS() +{ + const uint fuelCostWidth = cluster.ecoInput.fuelcost.width; + const uint co2CostWidth = cluster.ecoInput.co2cost.width; + + double co2EmissionFactor = cluster.emissions.factors[Pollutant::CO2]; + + for (uint tsIndex = 0; tsIndex < costsTimeSeries.size(); ++tsIndex) + { + uint tsIndexFuel = std::min(fuelCostWidth - 1, tsIndex); + uint tsIndexCo2 = std::min(co2CostWidth - 1, tsIndex); + for (uint hour = 0; hour < HOURS_PER_YEAR; ++hour) + { + double fuelcost = cluster.ecoInput.fuelcost[tsIndexFuel][hour]; + double co2cost = cluster.ecoInput.co2cost[tsIndexCo2][hour]; + + costsTimeSeries[tsIndex].marketBidCostTS[hour] = computeMarketBidCost( + fuelcost, + cluster.fuelEfficiency, + co2EmissionFactor, + co2cost, + cluster.variableomcost); + } + } +} + +double ScenarizedCostProvider::getOperatingCost(uint serieIndex, uint hourInTheYear) const +{ + const uint tsIndex = std::min(serieIndex, static_cast(costsTimeSeries.size()) - 1); + return costsTimeSeries[tsIndex].productionCostTs[hourInTheYear]; +} + +double ScenarizedCostProvider::getMarginalCost(uint serieIndex, uint hourInTheYear) const +{ + const double mod = cluster.modulation[thermalModulationMarketBid][hourInTheYear]; + const uint tsIndex = std::min(serieIndex, static_cast(costsTimeSeries.size()) - 1); + return costsTimeSeries[tsIndex].marginalCostTS[hourInTheYear] * mod; +} + +double ScenarizedCostProvider::getMarketBidCost(uint hourInTheYear, uint year) const +{ + const double mod = cluster.modulation[thermalModulationMarketBid][hourInTheYear]; + const uint serieIndex = cluster.series.getSeriesIndex(year); + const uint tsIndex = std::min(serieIndex, static_cast(costsTimeSeries.size()) - 1); + return costsTimeSeries[tsIndex].marketBidCostTS[hourInTheYear] * mod; +} + +} // namespace Antares::Data diff --git a/src/solver/simulation/common-eco-adq.cpp b/src/solver/simulation/common-eco-adq.cpp index 5b46e35cce..59eb02e9b4 100644 --- a/src/solver/simulation/common-eco-adq.cpp +++ b/src/solver/simulation/common-eco-adq.cpp @@ -378,7 +378,7 @@ void BuildThermalPartOfWeeklyProblem(Data::Study& study, .PuissanceDisponibleEtCout[cluster->index]; Pt.CoutHoraireDeProductionDuPalierThermique[hourInWeek] - = cluster->getMarketBidCost(hourInYear, year) + = cluster->getCostProvider().getMarketBidCost(hourInYear, year) + thermalNoises[areaIdx][cluster->areaWideIndex]; Pt.PuissanceDisponibleDuPalierThermique[hourInWeek] = cluster->series diff --git a/src/solver/variable/include/antares/solver/variable/economy/profitByPlant.h b/src/solver/variable/include/antares/solver/variable/economy/profitByPlant.h index 2dd9378067..20e1d6e228 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/profitByPlant.h +++ b/src/solver/variable/include/antares/solver/variable/economy/profitByPlant.h @@ -288,7 +288,7 @@ class ProfitByPlant: public Variable::IVariable, NextT, VCa pValuesForTheCurrentYear[numSpace][cluster->areaWideIndex].hour[hourInTheYear] = std::max((hourlyClusterProduction - cluster->PthetaInf[hourInTheYear]), 0.) * (-areaMarginalCosts[hourInTheWeek] - - cluster->getMarginalCost(tsIndex, hourInTheYear)); + - cluster->getCostProvider().getMarginalCost(tsIndex, hourInTheYear)); } // Next variable diff --git a/src/solver/variable/state.cpp b/src/solver/variable/state.cpp index 6657a5a23b..0e9f0eca8e 100644 --- a/src/solver/variable/state.cpp +++ b/src/solver/variable/state.cpp @@ -199,7 +199,7 @@ void State::initFromThermalClusterIndexProduction(const uint clusterAreaWideInde // calculating the operating cost for the current hour // O(h) = MA * P(h) * Modulation thermal[area->index].thermalClustersOperatingCost[clusterAreaWideIndex] - = (p * thermalCluster->getOperatingCost(serieIndex, hourInTheYear)); + = (p * thermalCluster->getCostProvider().getOperatingCost(serieIndex, hourInTheYear)); // Startup cost if (newUnitCount > previousUnitCount && hourInTheSimulation != 0u) @@ -297,7 +297,8 @@ void State::yearEndBuildFromThermalClusterIndex(const uint clusterAreaWideIndex) uint serieIndex = currentCluster->series.timeseriesNumbers[this->year]; thermalClusterOperatingCostForYear[h] = thermalClusterProduction - * currentCluster->getOperatingCost(serieIndex, h); + * currentCluster->getCostProvider() + .getOperatingCost(serieIndex, h); switch (unitCommitmentMode) { diff --git a/src/tests/src/libs/antares/study/thermal-price-definition/thermal-price-definition.cpp b/src/tests/src/libs/antares/study/thermal-price-definition/thermal-price-definition.cpp index 3aad7c0cec..b8eeb41d51 100644 --- a/src/tests/src/libs/antares/study/thermal-price-definition/thermal-price-definition.cpp +++ b/src/tests/src/libs/antares/study/thermal-price-definition/thermal-price-definition.cpp @@ -101,13 +101,6 @@ struct TimeSeriesFile const std::string name_; }; -void fillThermalEconomicTimeSeries(ThermalCluster* c) -{ - c->costsTimeSeries[0].productionCostTs.fill(1); - c->costsTimeSeries[0].marketBidCostTS.fill(1); - c->costsTimeSeries[0].marginalCostTS.fill(1); -} - // ================= // The fixture // ================= @@ -231,11 +224,14 @@ BOOST_FIXTURE_TEST_CASE(ThermalCluster_costGenManualCalculationOfMarketBidAndMar clusterList.loadFromFolder(*study, folder, area); auto cluster = clusterList.findInAll("some cluster"); + cluster->modulation.resize(thermalModulationMax, HOURS_PER_YEAR); + cluster->modulation.fill(1.); + cluster->costgeneration = Data::setManually; - cluster->ComputeCostTimeSeries(); - BOOST_CHECK_EQUAL(cluster->costsTimeSeries[0].marketBidCostTS[2637], 35); - BOOST_CHECK_EQUAL(cluster->costsTimeSeries[0].marginalCostTS[6737], 23); + auto& cp = cluster->getCostProvider(); + BOOST_CHECK_EQUAL(cp.getMarketBidCost(2637, 0), 35); + BOOST_CHECK_EQUAL(cp.getMarginalCost(6737, 0), 23); } BOOST_FIXTURE_TEST_CASE( @@ -248,22 +244,26 @@ BOOST_FIXTURE_TEST_CASE( clusterList.loadFromFolder(*study, folder, area); auto cluster = clusterList.findInAll("some cluster"); - cluster->modulation.reset(1, 8760); + cluster->modulation.resize(thermalModulationMax, HOURS_PER_YEAR); + cluster->modulation.fill(1.); + cluster->ecoInput.loadFromFolder(*study, folder); - fillThermalEconomicTimeSeries(cluster); - cluster->ComputeCostTimeSeries(); + cluster->tsNumbers.reset(1); - BOOST_CHECK_CLOSE(cluster->costsTimeSeries[0].marketBidCostTS[0], 24.12, 0.001); - BOOST_CHECK_CLOSE(cluster->costsTimeSeries[0].marketBidCostTS[2637], 24.12, 0.001); + auto& cp = cluster->getCostProvider(); + BOOST_CHECK_CLOSE(cp.getMarginalCost(0, 0), 24.12, 0.001); + BOOST_CHECK_CLOSE(cp.getMarketBidCost(2637, 0), 24.12, 0.001); } -BOOST_FIXTURE_TEST_CASE(computeMarketBidCost, FixtureFull) +BOOST_FIXTURE_TEST_CASE(computeMarketBidCost_useTimeSeries, FixtureFull) { clusterList.loadFromFolder(*study, folder, area); auto cluster = clusterList.findInAll("some cluster"); - - BOOST_CHECK_CLOSE(cluster->computeMarketBidCost(1, 2, 1), 24.12, 0.001); + BOOST_CHECK_CLOSE( + computeMarketBidCost(1, cluster->fuelEfficiency, 2, 1, cluster->variableomcost), + 24.12, + 0.001); } BOOST_AUTO_TEST_CASE(non_constant_marketbid_modulation) @@ -279,12 +279,12 @@ BOOST_AUTO_TEST_CASE(non_constant_marketbid_modulation) { mod[thermalModulationMarketBid][0] = .5; - BOOST_CHECK_EQUAL(cluster.getMarketBidCost(0, 0), .5 * 120); + BOOST_CHECK_EQUAL(cluster.getCostProvider().getMarketBidCost(0, 0), .5 * 120); } { mod[thermalModulationMarketBid][1] = .8; - BOOST_CHECK_EQUAL(cluster.getMarketBidCost(1, 0), .8 * 120); + BOOST_CHECK_EQUAL(cluster.getCostProvider().getMarketBidCost(1, 0), .8 * 120); } } diff --git a/src/ui/simulator/toolbox/components/datagrid/renderer/area/thermalmodulation.cpp b/src/ui/simulator/toolbox/components/datagrid/renderer/area/thermalmodulation.cpp index bf72ed3dc0..a53872ee64 100644 --- a/src/ui/simulator/toolbox/components/datagrid/renderer/area/thermalmodulation.cpp +++ b/src/ui/simulator/toolbox/components/datagrid/renderer/area/thermalmodulation.cpp @@ -95,17 +95,16 @@ wxString ThermalClusterCommonModulation::cellValue(int x, int y) const return DoubleToWxString(Math::Round((*pMatrix)[Data::thermalMinGenModulation][y], 3)); case (Data::thermalModulationCost + Data::thermalModulationMax): return DoubleToWxString( - Math::Round((*pMatrix)[Data::thermalModulationCost][y] - * pCluster->costsTimeSeries[0].marginalCostTS[y], + Math::Round(pCluster->getCostProvider().getMarginalCost(0, y), 3)); case (Data::thermalModulationMarketBid + Data::thermalModulationMax): return DoubleToWxString( - Math::Round((*pMatrix)[Data::thermalModulationMarketBid][y] - * pCluster->costsTimeSeries[0].marketBidCostTS[y], + Math::Round(pCluster->getCostProvider().getMarketBidCost(y, 0), 3)); case (Data::thermalModulationCapacity + Data::thermalModulationMax): - return DoubleToWxString(Math::Round( - (*pMatrix)[Data::thermalModulationCapacity][y] * pCluster->nominalCapacity, 2)); + return DoubleToWxString(Math::Round((*pMatrix)[Data::thermalModulationCapacity][y] + * pCluster->nominalCapacity, + 2)); case (Data::thermalMinGenModulation + Data::thermalModulationMax): return DoubleToWxString(Math::Round((*pMatrix)[Data::thermalMinGenModulation][y] * pCluster->unitCount * pCluster->nominalCapacity, @@ -131,11 +130,9 @@ double ThermalClusterCommonModulation::cellNumericValue(int x, int y) const case Data::thermalMinGenModulation: return (*pMatrix)[Data::thermalMinGenModulation][y]; case (Data::thermalModulationCost + Data::thermalModulationMax): - return (*pMatrix)[Data::thermalModulationCost][y] - * pCluster->costsTimeSeries[0].marginalCostTS[y]; + return pCluster->getCostProvider().getMarginalCost(0, y); case (Data::thermalModulationMarketBid + Data::thermalModulationMax): - return (*pMatrix)[Data::thermalModulationMarketBid][y] - * pCluster->costsTimeSeries[0].marketBidCostTS[y]; + return pCluster->getCostProvider().getMarketBidCost(y, 0); case (Data::thermalModulationCapacity + Data::thermalModulationMax): return (*pMatrix)[Data::thermalModulationCapacity][y] * pCluster->nominalCapacity; case (Data::thermalMinGenModulation + Data::thermalModulationMax): diff --git a/src/ui/simulator/windows/inspector/property.update.cpp b/src/ui/simulator/windows/inspector/property.update.cpp index 1c1879dfae..dc0e7eef1e 100644 --- a/src/ui/simulator/windows/inspector/property.update.cpp +++ b/src/ui/simulator/windows/inspector/property.update.cpp @@ -724,7 +724,6 @@ bool InspectorGrid::onPropertyChanging_ThermalCluster(wxPGProperty*, for (; i != end; ++i) { (*i)->fuelEfficiency = d; - (*i)->ComputeCostTimeSeries(); } // update } OnStudyThermalClusterCommonSettingsChanged(); @@ -750,7 +749,6 @@ bool InspectorGrid::onPropertyChanging_ThermalCluster(wxPGProperty*, for (; i != end; ++i) { (*i)->costgeneration = costgeneration; - (*i)->ComputeCostTimeSeries(); // update } AccumulatorCheck::ApplyGreyColor( pFrame.pPGThClusterMarginalCost, @@ -796,7 +794,6 @@ bool InspectorGrid::onPropertyChanging_ThermalCluster(wxPGProperty*, for (; i != end; ++i) { (*i)->marginalCost = d; - (*i)->ComputeCostTimeSeries(); // update } pFrame.delayApply(); @@ -879,7 +876,6 @@ bool InspectorGrid::onPropertyChanging_ThermalCluster(wxPGProperty*, for (; i != end; ++i) { (*i)->marketBidCost = d; - (*i)->ComputeCostTimeSeries(); } pFrame.delayApply(); @@ -922,7 +918,6 @@ bool InspectorGrid::onPropertyChanging_ThermalCluster(wxPGProperty*, for (; i != end; ++i) { (*i)->variableomcost = d; - (*i)->ComputeCostTimeSeries(); // update } } OnStudyThermalClusterCommonSettingsChanged();