diff --git a/docs/user-guide/04-migration-guides.md b/docs/user-guide/04-migration-guides.md index c423bc5346..729818c28f 100644 --- a/docs/user-guide/04-migration-guides.md +++ b/docs/user-guide/04-migration-guides.md @@ -19,6 +19,9 @@ with XXX in - number of TS to generate => generaldata.ini/General/nbtimeserieslinks (unsigned int, default value 1) +### Input +#### Short term storage: efficiency for withdrawal +In input/st-storage/area/list.ini add property: `efficiencywithdrawal` [double] in range 0-1 ## v9.1.0 ### Input diff --git a/docs/user-guide/solver/02-inputs.md b/docs/user-guide/solver/02-inputs.md index 287be4b797..8f497e8b34 100644 --- a/docs/user-guide/solver/02-inputs.md +++ b/docs/user-guide/solver/02-inputs.md @@ -286,6 +286,7 @@ The user may pick any area appearing in the area list and is then given access t - Injection (MW): the maximum injection power for the storage - withdrawal refers to the flow from the power system to the storage - Stock (MWh): the capacity of the storage in MWh - Efficiency (%): the energy efficiency of the storage, i.e. the ratio for a given volume between the energy taken from the system to be injected into the storage and the energy returned to the system during its withdrawal. This efficiency factor is applied when injecting energy into the storage. + - Efficiency Withdrawal (%): Same behavior as the previous efficiency, this factor is applied when withdrawing energy from the storage. - Initial level (%): the imposed initial filling rate of the storage at the beginning of each optimisation period. - Initial level optimal: if the parameter is activated, the "Initial level" parameter is ignored and the initial storage level is optimized by Antares for each optimization period to minimize its objective function. _Note: setting this parameter to "True" implies that there is no guarantee that the initial storage level of week N is the same as the final storage level of week N-1. However, the final level of week N is always equal to the initial level of the same week N plus/minus the injections/withdrawals occuring at the last hour of week N._ diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h index 11b4808e0d..ac1d37e791 100644 --- a/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h @@ -41,12 +41,17 @@ class Properties std::optional withdrawalNominalCapacity; /// Not optional Reservoir capacity in MWh, >= 0 std::optional reservoirCapacity; + /// Initial level, <= 1 double initialLevel = initiallevelDefault; /// Bool to optimise or not initial level bool initialLevelOptim = false; - /// Efficiency factor between 0 and 1 - double efficiencyFactor = 1; + + /// Efficiency factor for injection between 0 and 1 + double injectionEfficiency = 1; + /// Efficiency factor for withdrawal between 0 and 1 + double withdrawalEfficiency = 1; + // Used to sort outputs std::string groupName = "OTHER1"; /// cluster name diff --git a/src/libs/antares/study/parts/short-term-storage/properties.cpp b/src/libs/antares/study/parts/short-term-storage/properties.cpp index 546717588e..b4592105fe 100644 --- a/src/libs/antares/study/parts/short-term-storage/properties.cpp +++ b/src/libs/antares/study/parts/short-term-storage/properties.cpp @@ -61,7 +61,12 @@ bool Properties::loadKey(const IniFile::Property* p) if (p->key == "efficiency") { - return p->value.to(this->efficiencyFactor); + return p->value.to(this->injectionEfficiency); + } + + if (p->key == "efficiencywithdrawal") + { + return p->value.to(this->withdrawalEfficiency); } if (p->key == "name") @@ -105,7 +110,8 @@ void Properties::save(IniFile& ini) const s->add("injectionnominalcapacity", this->injectionNominalCapacity); s->add("withdrawalnominalcapacity", this->withdrawalNominalCapacity); - s->add("efficiency", this->efficiencyFactor); + s->add("efficiency", this->injectionEfficiency); + s->add("efficiencyWithdrawal", this->withdrawalEfficiency); s->add("initialleveloptim", this->initialLevelOptim); s->add("enabled", this->enabled); } @@ -157,16 +163,30 @@ bool Properties::validate() return false; } - if (efficiencyFactor < 0) + if (injectionEfficiency < 0) { logs.warning() << "Property efficiency must be >= 0 " << "for short term storage " << name; - efficiencyFactor = 0; + injectionEfficiency = 0; } - if (efficiencyFactor > 1) + if (injectionEfficiency > 1) { logs.warning() << "Property efficiency must be <= 1 " << "for short term storage " << name; - efficiencyFactor = 1; + injectionEfficiency = 1; + } + + if (withdrawalEfficiency < 0) + { + logs.warning() << "Property efficiencyWithdrawal must be >= 0 " << "for short term storage " + << name; + withdrawalEfficiency = 0; + } + + if (withdrawalEfficiency > 1) + { + logs.warning() << "Property efficiencyWithdrawal must be <= 1 " << "for short term storage " + << name; + withdrawalEfficiency = 1; } if (initialLevel < 0) diff --git a/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp b/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp index 0faa565a8a..00a70e8dd9 100644 --- a/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp +++ b/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp @@ -42,8 +42,8 @@ void ShortTermStorageLevel::add(int pdt, int pays) -1.0, -1, builder.data.NombreDePasDeTempsPourUneOptimisation) - .ShortTermStorageInjection(index, -1.0 * storage.efficiency) - .ShortTermStorageWithdrawal(index, 1.0) + .ShortTermStorageInjection(index, -storage.injectionEfficiency) + .ShortTermStorageWithdrawal(index, storage.withdrawalEfficiency) .equalTo() .build(); } diff --git a/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp b/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp index c2d1859106..857561f616 100644 --- a/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp +++ b/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp @@ -61,7 +61,7 @@ static void shortTermStorageCost( pdtJour); varInjection >= 0) { - linearCost[varInjection] = cost; + linearCost[varInjection] = storage.withdrawalEfficiency * cost; } if (const int varWithdrawal = variableManager.ShortTermStorageWithdrawal( @@ -69,7 +69,7 @@ static void shortTermStorageCost( pdtJour); varWithdrawal >= 0) { - linearCost[varWithdrawal] = storage.efficiency * cost; + linearCost[varWithdrawal] = storage.injectionEfficiency * cost; } } } diff --git a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h index 66400ed678..7805218061 100644 --- a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h +++ b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h @@ -169,7 +169,8 @@ struct PROPERTIES double reservoirCapacity; double injectionNominalCapacity; double withdrawalNominalCapacity; - double efficiency; + double injectionEfficiency; + double withdrawalEfficiency; double initialLevel; bool initialLevelOptim; diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index 7fb3336599..f9f47eb65a 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -53,7 +53,8 @@ static void importShortTermStorages( // Properties toInsert.reservoirCapacity = st.properties.reservoirCapacity.value(); - toInsert.efficiency = st.properties.efficiencyFactor; + toInsert.injectionEfficiency = st.properties.injectionEfficiency; + toInsert.withdrawalEfficiency = st.properties.withdrawalEfficiency; toInsert.injectionNominalCapacity = st.properties.injectionNominalCapacity.value(); toInsert.withdrawalNominalCapacity = st.properties.withdrawalNominalCapacity.value(); toInsert.initialLevel = st.properties.initialLevel; diff --git a/src/tests/end-to-end/simple_study/simple-study.cpp b/src/tests/end-to-end/simple_study/simple-study.cpp index 7005057209..2664dc97d0 100644 --- a/src/tests/end-to-end/simple_study/simple-study.cpp +++ b/src/tests/end-to-end/simple_study/simple-study.cpp @@ -308,7 +308,8 @@ BOOST_FIXTURE_TEST_CASE(STS_initial_level_is_also_weekly_final_level, StudyFixtu props.injectionNominalCapacity = 10; props.withdrawalNominalCapacity = 10; props.reservoirCapacity = 100; - props.efficiencyFactor = .9; + props.injectionEfficiency = .9; + props.withdrawalEfficiency = .8; props.initialLevel = .443; props.groupName = std::string("Some STS group"); // Default values for series @@ -317,17 +318,16 @@ BOOST_FIXTURE_TEST_CASE(STS_initial_level_is_also_weekly_final_level, StudyFixtu storages.push_back(sts); // Fatal gen at h=1 + auto& windTS = area->wind.series.timeSeries; + TimeSeriesConfigurer(windTS).setColumnCount(1).fillColumnWith(0, 0.); + windTS[0][1] = 100; + + // Fatal load at h=2-10 + auto& loadTS = area->load.series.timeSeries; + TimeSeriesConfigurer(loadTS).setColumnCount(1).fillColumnWith(0, 0.); + for (int i = 2; i < 10; i++) { - auto& windTS = area->wind.series.timeSeries; - TimeSeriesConfigurer(windTS).setColumnCount(1).fillColumnWith(0, 0.); - windTS[0][1] = 100; - } - - // Fatal load at h=2 - { - auto& loadTS = area->load.series.timeSeries; - TimeSeriesConfigurer(loadTS).setColumnCount(1).fillColumnWith(0, 0.); - loadTS[0][2] = 100; + loadTS[0][i] = 100; } // Usual values, avoid spillage & unsupplied energy @@ -343,6 +343,51 @@ BOOST_FIXTURE_TEST_CASE(STS_initial_level_is_also_weekly_final_level, StudyFixtu == props.initialLevel * props.reservoirCapacity.value(), tt::tolerance(0.001)); } + +BOOST_FIXTURE_TEST_CASE(STS_efficiency_for_injection_and_withdrawal, StudyFixture) +{ + using namespace Antares::Data::ShortTermStorage; + setNumberMCyears(1); + auto& storages = area->shortTermStorage.storagesByIndex; + STStorageCluster sts; + auto& props = sts.properties; + props.name = "my-sts"; + props.injectionNominalCapacity = 10; + props.withdrawalNominalCapacity = 10; + props.reservoirCapacity = 100; + props.injectionEfficiency = .6; + props.withdrawalEfficiency = .8; + props.initialLevel = .5; + props.groupName = std::string("Some STS group"); + // Default values for series + sts.series->fillDefaultSeriesIfEmpty(); + + storages.push_back(sts); + + // Fatal gen at h=1 + auto& windTS = area->wind.series.timeSeries; + TimeSeriesConfigurer(windTS).setColumnCount(1).fillColumnWith(0, 0.); + windTS[0][1] = 100; + + // Fatal load at h=2 + auto& loadTS = area->load.series.timeSeries; + TimeSeriesConfigurer(loadTS).setColumnCount(1).fillColumnWith(0, 0.); + loadTS[0][2] = 100; + + // Usual values, avoid spillage & unsupplied energy + area->thermal.unsuppliedEnergyCost = 1.e3; + area->thermal.spilledEnergyCost = 1.; + + simulation->create(); + simulation->run(); + + unsigned int groupNb = 0; // Used to reach the first group of STS results + OutputRetriever output(simulation->rawSimu()); + + BOOST_CHECK_EQUAL(output.levelForSTSgroup(area, groupNb).hour(1), 56); // injection + BOOST_CHECK_EQUAL(output.levelForSTSgroup(area, groupNb).hour(2), 48); // withdrawal +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(HYDRO_MAX_POWER) diff --git a/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp b/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp index 83965049e6..99152e42d0 100644 --- a/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp +++ b/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp @@ -115,6 +115,7 @@ void createIniFile(bool enabled) outfile << "withdrawalnominalcapacity = 900.000000" << std::endl; outfile << "reservoircapacity = 31200.000000" << std::endl; outfile << "efficiency = 0.75" << std::endl; + outfile << "efficiencywithdrawal = 0.9" << std::endl; outfile << "initiallevel = 0.50000" << std::endl; outfile << "enabled = " << (enabled ? "true" : "false") << std::endl; outfile.close(); @@ -134,6 +135,7 @@ void createIniFileWrongValue() outfile << "withdrawalnominalcapacity = -900.000000" << std::endl; outfile << "reservoircapacity = -31200.000000" << std::endl; outfile << "efficiency = 4" << std::endl; + outfile << "efficiencywithdrawal = -2" << std::endl; outfile << "initiallevel = -0.50000" << std::endl; outfile.close();