Skip to content

Commit 9e00ea2

Browse files
marifunfrolnico
andauthored
Adding doctrine costs configuration for redispatching, load shedding and losses (#191)
* [IMG-2475] Generator doctrine cost Signed-off-by: marifunf <[email protected]> * [IMG-2475] Load doctrine cost Signed-off-by: marifunf <[email protected]> * [IMG-2475] Losses doctrine cost Signed-off-by: marifunf <[email protected]> * [IMG-2475] Doctrine cost tests Signed-off-by: marifunf <[email protected]> * [IMG-2475] MetrixVariable addings Signed-off-by: marifunf <[email protected]> * test with no output modification Signed-off-by: Nicolas Rol <[email protected]> * add check for non-null object Signed-off-by: Nicolas Rol <[email protected]> * [IMG-2475] Add some doc Signed-off-by: marifunf <[email protected]> * [IMG-2475] Add some doc Signed-off-by: marifunf <[email protected]> * [IMG-2475] add comment on doctrine costs Signed-off-by: marifunf <[email protected]> --------- Signed-off-by: marifunf <[email protected]> Signed-off-by: Nicolas Rol <[email protected]> Co-authored-by: Nicolas Rol <[email protected]>
1 parent 8316082 commit 9e00ea2

File tree

7 files changed

+412
-33
lines changed

7 files changed

+412
-33
lines changed

Diff for: docs/metrix.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,18 @@ If no generator is configured to be managed by Metrix, then all generators are i
233233
Note that Metrix will take into account the Pmin and Pmax values of generators (which can be modified through the mapping script).
234234
In the same way than most parameters, the value can be a fixed integer/float or a time series name.
235235

236+
Doctrine costs can be defined for postprocessing purpose (up and down redispatching and costs timeseries)
237+
236238
The syntax to define a managed generator is:
237239

238240
```groovy
239241
generator(id) { // id of the generator which will be managed by Metrix
240242
adequacyDownCosts 'ts_cost_down' // Cost of ramping down for the adequacy phase (here a time series name is used)
241243
adequacyUpCosts 'ts_cost_up' // Cost of ramping up for the adequacy phase (here a time series name is used)
242-
redispatchingDownCosts 10 // Cost of ramping down (preventive) for the OPF simulation (here a fixed value is used)
243-
redispatchingUpCosts 100 // Cost of ramping up (preventive) for the OPF simulation (here a fixed value is used)
244+
redispatchingDownCosts 10 // Cost of ramping down for the OPF simulation (here a fixed value is used)
245+
redispatchingUpCosts 100 // Cost of ramping up for the OPF simulation (here a fixed value is used)
246+
redispatchingDownDoctrineCosts 10 // Doctrine cost of ramping down for the OPF simulation (fixed value or time series name)
247+
redispatchingUpDoctrineCosts 100 // Doctrine cost of ramping up for the OPF simulation (fixed value or time series name)
244248
onContingencies 'a','b' // list of contingencies where Metrix can use this generator in (curative) remedial actions
245249
}
246250
```
@@ -301,6 +305,8 @@ for (g in network.generators) {
301305

302306
Similarly to generators, loads can be adjusted in the OPF simulation (in preventive and curative mode), but only as a decrease. The cost in preventive action is fixed (default 13000 €/MWh) and can be modified in the global parameters with the keyword `lossOfLoadCost`. We can also override this value for specific loads. The specified cost will automatically be weighted with the contingency probability (default : 10^-3). We can also limit the maximum percentage of load shedding (in preventive and curative mode).
303307

308+
Doctrine costs can be defined for postprocessing purpose (load shedding costs timeseries)
309+
304310
Here is the corresponding syntax:
305311
```groovy
306312
load(load_id) {
@@ -309,6 +315,8 @@ load(load_id) {
309315
curativeSheddingPercentage 10 // optional shedding max percentage for curative actions
310316
curativeSheddingCost 40 // optional cost for curative shedding action
311317
onContingencies 'a', 'b' // optional contingency upon which curative action are operated
318+
preventiveSheddingDoctrineCost 10000 // doctrine cost for preventive shedding action (fixed value or time series name)
319+
curativeSheddingDoctrineCost 'ts_doctrine_cost' // doctrine cost for curative shedding action (fixed value or time series name)
312320
}
313321
```
314322

@@ -348,6 +356,16 @@ Control types:
348356
- `OPTIMIZED`: can optimize the p0 target in adequacy phase and preventive (and curative if contingencies were defined)
349357
- `FIXED`: the target p0 is fixed
350358

359+
### Losses
360+
361+
Doctrine costs can be defined for postprocessing purpose (global losses cost time series)
362+
363+
```groovy
364+
losses() {
365+
costs 'ts_losses_cost' // Losses cost (fixed value or time series name)
366+
}
367+
```
368+
351369
### Monitored sections
352370

353371
It is possible to monitor a constraint on a weighted sum of a set of branch flows:

Diff for: metrix-integration/src/main/groovy/com/powsybl/metrix/integration/GeneratorData.groovy

+70-21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class GeneratorData {
2222
Object adequacyDownCosts
2323
Object redispatchingUpCosts
2424
Object redispatchingDownCosts
25+
Object redispatchingUpDoctrineCosts
26+
Object redispatchingDownDoctrineCosts
2527

2628
void adequacyUpCosts(Object timeSeriesNames) {
2729
this.adequacyUpCosts = timeSeriesNames
@@ -39,6 +41,14 @@ class GeneratorData {
3941
this.redispatchingDownCosts = timeSeriesNames
4042
}
4143

44+
void redispatchingUpDoctrineCosts(Object timeSeriesNames) {
45+
this.redispatchingUpDoctrineCosts = timeSeriesNames
46+
}
47+
48+
void redispatchingDownDoctrineCosts(Object timeSeriesNames) {
49+
this.redispatchingDownDoctrineCosts = timeSeriesNames
50+
}
51+
4252
void onContingencies(String[] contingencies) {
4353
this.onContingencies = contingencies
4454
}
@@ -47,6 +57,62 @@ class GeneratorData {
4757
this.onContingencies = onContingencies
4858
}
4959

60+
/**
61+
* Set the generator available for adequacy when up and down adequacy costs are defined
62+
*/
63+
private static generatorForAdequacy(String id, GeneratorData spec, TimeSeriesMappingConfigLoader configLoader, MetrixDslData data, LogDslLoader logDslLoader) {
64+
if (spec.adequacyUpCosts != null || spec.adequacyDownCosts != null) {
65+
if (spec.adequacyUpCosts != null && spec.adequacyDownCosts != null) {
66+
configLoader.addEquipmentTimeSeries(spec.adequacyDownCosts, MetrixVariable.OFF_GRID_COST_DOWN, id)
67+
configLoader.addEquipmentTimeSeries(spec.adequacyUpCosts, MetrixVariable.OFF_GRID_COST_UP, id)
68+
data.addGeneratorForAdequacy(id)
69+
} else if (spec.adequacyUpCosts == null) {
70+
logDslLoader.logDebug("generator %s is missing adequacy up-cost time-series to be properly configured", id)
71+
} else {
72+
logDslLoader.logDebug("generator %s is missing adequacy down-cost time-series to be properly configured", id)
73+
}
74+
}
75+
}
76+
77+
/**
78+
* Set the generator available for redispatching when up and down redispatching costs are defined
79+
*/
80+
private static boolean generatorForRedispatching(String id, GeneratorData spec, TimeSeriesMappingConfigLoader configLoader, MetrixDslData data, LogDslLoader logDslLoader) {
81+
if (spec.redispatchingUpCosts != null || spec.redispatchingDownCosts != null) {
82+
if (spec.redispatchingUpCosts != null && spec.redispatchingDownCosts != null) {
83+
configLoader.addEquipmentTimeSeries(spec.redispatchingDownCosts, MetrixVariable.ON_GRID_COST_DOWN, id)
84+
configLoader.addEquipmentTimeSeries(spec.redispatchingUpCosts, MetrixVariable.ON_GRID_COST_UP, id)
85+
data.addGeneratorForRedispatching(id, spec.onContingencies)
86+
return true
87+
} else if (spec.redispatchingUpCosts == null) {
88+
logDslLoader.logDebug("generator %s is missing redispatching up-cost time-series to be properly configured", id)
89+
} else {
90+
logDslLoader.logDebug("generator %s is missing redispatching down-cost time-series to be properly configured", id)
91+
}
92+
}
93+
return false
94+
}
95+
96+
/**
97+
* Define up and down redispatching doctrine costs when generator is available for redispatching
98+
*/
99+
private static generatorRedispatchingDoctrineCosts(String id, GeneratorData spec, TimeSeriesMappingConfigLoader configLoader, LogDslLoader logDslLoader) {
100+
if (spec.redispatchingUpDoctrineCosts == null && spec.redispatchingDownDoctrineCosts == null) {
101+
logDslLoader.logWarn("generator %s is missing redispatching doctrine cost to be properly configured", id)
102+
return
103+
}
104+
if (spec.redispatchingUpDoctrineCosts == null) {
105+
logDslLoader.logWarn("generator %s is missing redispatching doctrine up cost to be properly configured", id)
106+
return
107+
}
108+
if (spec.redispatchingDownDoctrineCosts == null) {
109+
logDslLoader.logWarn("generator %s is missing redispatching doctrine down cost to be properly configured", id)
110+
return
111+
}
112+
configLoader.addEquipmentTimeSeries(spec.redispatchingDownDoctrineCosts, MetrixVariable.ON_GRID_DOCTRINE_COST_DOWN, id)
113+
configLoader.addEquipmentTimeSeries(spec.redispatchingUpDoctrineCosts, MetrixVariable.ON_GRID_DOCTRINE_COST_UP, id)
114+
}
115+
50116
protected static generatorData(Closure closure, String id, Network network, TimeSeriesMappingConfigLoader configLoader, MetrixDslData data, LogDslLoader logDslLoader) {
51117
Identifiable identifiable = network.getGenerator(id)
52118
if (identifiable == null) {
@@ -57,27 +123,10 @@ class GeneratorData {
57123
GeneratorData spec = generatorData(closure)
58124

59125
if (spec) {
60-
if (spec.adequacyUpCosts != null || spec.adequacyDownCosts != null) {
61-
if (spec.adequacyUpCosts != null && spec.adequacyDownCosts != null) {
62-
configLoader.addEquipmentTimeSeries(spec.adequacyDownCosts, MetrixVariable.OFF_GRID_COST_DOWN, id)
63-
configLoader.addEquipmentTimeSeries(spec.adequacyUpCosts, MetrixVariable.OFF_GRID_COST_UP, id)
64-
data.addGeneratorForAdequacy(id)
65-
} else if (spec.adequacyUpCosts == null) {
66-
logDslLoader.logDebug("generator %s is missing adequacy up-cost time-series to be properly configured", id)
67-
} else {
68-
logDslLoader.logDebug("generator %s is missing adequacy down-cost time-series to be properly configured", id)
69-
}
70-
}
71-
if (spec.redispatchingUpCosts != null || spec.redispatchingDownCosts != null) {
72-
if (spec.redispatchingUpCosts != null && spec.redispatchingDownCosts != null) {
73-
configLoader.addEquipmentTimeSeries(spec.redispatchingDownCosts, MetrixVariable.ON_GRID_COST_DOWN, id)
74-
configLoader.addEquipmentTimeSeries(spec.redispatchingUpCosts, MetrixVariable.ON_GRID_COST_UP, id)
75-
data.addGeneratorForRedispatching(id, spec.onContingencies)
76-
} else if (spec.redispatchingUpCosts == null) {
77-
logDslLoader.logDebug("generator %s is missing redispatching up-cost time-series to be properly configured", id)
78-
} else {
79-
logDslLoader.logDebug("generator %s is missing redispatching down-cost time-series to be properly configured", id)
80-
}
126+
generatorForAdequacy(id, spec, configLoader, data, logDslLoader)
127+
boolean isGeneratorAvailableForRedispatching = generatorForRedispatching(id, spec, configLoader, data, logDslLoader)
128+
if (isGeneratorAvailableForRedispatching) {
129+
generatorRedispatchingDoctrineCosts(id, spec, configLoader, logDslLoader)
81130
}
82131
}
83132
}

Diff for: metrix-integration/src/main/groovy/com/powsybl/metrix/integration/LoadData.groovy

+53-9
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ class LoadData {
2020
List<String> onContingencies
2121
Integer preventiveSheddingPercentage
2222
Float preventiveSheddingCost
23+
Object preventiveSheddingDoctrineCost
2324
Integer curativeSheddingPercentage
2425
Object curativeSheddingCost
26+
Object curativeSheddingDoctrineCost
2527

2628
void preventiveSheddingPercentage(int percent) {
2729
this.preventiveSheddingPercentage = percent
@@ -31,6 +33,10 @@ class LoadData {
3133
this.preventiveSheddingCost = loadSheddingCost
3234
}
3335

36+
void preventiveSheddingDoctrineCost(Object timeSeriesNames) {
37+
this.preventiveSheddingDoctrineCost = timeSeriesNames
38+
}
39+
3440
void curativeSheddingPercentage(int percent) {
3541
this.curativeSheddingPercentage = percent
3642
}
@@ -39,6 +45,10 @@ class LoadData {
3945
this.curativeSheddingCost = timeSeriesName
4046
}
4147

48+
void curativeSheddingDoctrineCost(Object timeSeriesName) {
49+
this.curativeSheddingDoctrineCost = timeSeriesName
50+
}
51+
4252
void onContingencies(String[] onContingencies) {
4353
this.onContingencies = onContingencies
4454
}
@@ -47,15 +57,7 @@ class LoadData {
4757
this.onContingencies = onContingencies
4858
}
4959

50-
protected static loadData(Closure closure, String id, Network network, TimeSeriesMappingConfigLoader configLoader, MetrixDslData data, LogDslLoader logDslLoader) {
51-
Identifiable identifiable = network.getLoad(id)
52-
if (identifiable == null) {
53-
logDslLoader.logWarn("load id %s not found in the network", id)
54-
return
55-
}
56-
57-
LoadData loadSpec = loadData(closure)
58-
60+
private static boolean loadForPreventiveShedding(String id, LoadData loadSpec, MetrixDslData data, LogDslLoader logDslLoader) {
5961
if (loadSpec.preventiveSheddingPercentage != null) {
6062
if (loadSpec.preventiveSheddingPercentage > 100 || loadSpec.preventiveSheddingPercentage < 0) {
6163
logDslLoader.logWarn("preventive shedding percentage for load %s is not valid", id)
@@ -64,21 +66,63 @@ class LoadData {
6466
if (loadSpec.preventiveSheddingCost != null) {
6567
data.addPreventiveLoadCost(id, loadSpec.preventiveSheddingCost)
6668
}
69+
return true
6770
}
6871
}
72+
return false
73+
}
74+
75+
private static void loadPreventiveSheddingDoctrineCosts(String id, LoadData loadSpec, TimeSeriesMappingConfigLoader configLoader, LogDslLoader logDslLoader) {
76+
if (loadSpec.preventiveSheddingDoctrineCost == null) {
77+
logDslLoader.logWarn("load %s is missing preventive shedding doctrine cost to be properly configured", id)
78+
return
79+
}
80+
configLoader.addEquipmentTimeSeries(loadSpec.preventiveSheddingDoctrineCost, MetrixVariable.PREVENTIVE_DOCTRINE_COST_DOWN, id)
81+
}
6982

83+
private static boolean loadForCurativeShedding(String id, LoadData loadSpec, TimeSeriesMappingConfigLoader configLoader, MetrixDslData data, LogDslLoader logDslLoader) {
7084
if (loadSpec.curativeSheddingPercentage || loadSpec.curativeSheddingCost != null || loadSpec.onContingencies) {
7185
if (loadSpec.curativeSheddingPercentage && loadSpec.curativeSheddingCost != null && loadSpec.onContingencies) {
7286
if (loadSpec.curativeSheddingPercentage > 100 || loadSpec.curativeSheddingPercentage < 0) {
7387
logDslLoader.logWarn("curative shedding percentage for load %s is not valid", id)
7488
} else {
7589
configLoader.addEquipmentTimeSeries(loadSpec.curativeSheddingCost, MetrixVariable.CURATIVE_COST_DOWN, id)
7690
data.addCurativeLoad(id, loadSpec.curativeSheddingPercentage, loadSpec.onContingencies)
91+
return true
7792
}
7893
} else {
7994
logDslLoader.logWarn("configuration error for load %s : curative costs, percentage and contingencies list must be set altogether", id)
8095
}
8196
}
97+
return false
98+
}
99+
100+
private static void loadCurativeSheddingDoctrineCosts(String id, LoadData loadSpec, TimeSeriesMappingConfigLoader configLoader, LogDslLoader logDslLoader) {
101+
if (loadSpec.curativeSheddingDoctrineCost == null) {
102+
logDslLoader.logWarn("load %s is missing curative shedding doctrine cost to be properly configured", id)
103+
return
104+
}
105+
configLoader.addEquipmentTimeSeries(loadSpec.curativeSheddingDoctrineCost, MetrixVariable.CURATIVE_DOCTRINE_COST_DOWN, id)
106+
}
107+
108+
protected static loadData(Closure closure, String id, Network network, TimeSeriesMappingConfigLoader configLoader, MetrixDslData data, LogDslLoader logDslLoader) {
109+
Identifiable identifiable = network.getLoad(id)
110+
if (identifiable == null) {
111+
logDslLoader.logWarn("load id %s not found in the network", id)
112+
return
113+
}
114+
115+
LoadData loadSpec = loadData(closure)
116+
117+
boolean isLoadAvailableForPreventiveShedding = loadForPreventiveShedding(id, loadSpec, data, logDslLoader)
118+
if (isLoadAvailableForPreventiveShedding) {
119+
loadPreventiveSheddingDoctrineCosts(id, loadSpec, configLoader, logDslLoader)
120+
}
121+
122+
boolean isLoadAvailableForCurativeShedding = loadForCurativeShedding(id, loadSpec, configLoader, data, logDslLoader)
123+
if (isLoadAvailableForCurativeShedding) {
124+
loadCurativeSheddingDoctrineCosts(id, loadSpec, configLoader, logDslLoader)
125+
}
82126
}
83127

84128
protected static LoadData loadData(Closure closure) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2020, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
* SPDX-License-Identifier: MPL-2.0
7+
*/
8+
package com.powsybl.metrix.integration
9+
10+
import com.powsybl.iidm.network.Network
11+
import com.powsybl.metrix.mapping.LogDslLoader
12+
import com.powsybl.metrix.mapping.TimeSeriesMappingConfigLoader
13+
14+
/**
15+
* @author Marianne Funfrock {@literal <marianne.funfrock at rte-france.com>}
16+
*/
17+
class LossesData {
18+
19+
Object costs
20+
21+
void costs(Object timeSeriesNames) {
22+
this.costs = timeSeriesNames
23+
}
24+
25+
private static lossesDoctrineCosts(String id, LossesData spec, TimeSeriesMappingConfigLoader configLoader, LogDslLoader logDslLoader) {
26+
if (spec.costs != null) {
27+
configLoader.addEquipmentTimeSeries(spec.costs, MetrixVariable.LOSSES_DOCTRINE_COST, id)
28+
}
29+
}
30+
31+
protected static lossesData(Closure closure, Network network, TimeSeriesMappingConfigLoader configLoader, LogDslLoader logDslLoader) {
32+
LossesData spec = lossesData(closure)
33+
if (spec) {
34+
lossesDoctrineCosts(network.getId(), spec, configLoader, logDslLoader)
35+
}
36+
}
37+
38+
protected static LossesData lossesData(Closure closure) {
39+
def cloned = closure.clone()
40+
LossesData spec = new LossesData()
41+
cloned.delegate = spec
42+
cloned()
43+
spec
44+
}
45+
}

Diff for: metrix-integration/src/main/groovy/com/powsybl/metrix/integration/MetrixDslDataLoader.groovy

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import static LoadsBindingData.loadsBindingData
3434
import static ParametersData.parametersData
3535
import static PhaseShifterData.phaseShifterData
3636
import static SectionMonitoringData.sectionMonitoringData
37+
import static LossesData.lossesData
3738

3839
/**
3940
* @author Paul Bui-Quang {@literal <paul.buiquang at rte-france.com>}
@@ -176,6 +177,11 @@ class MetrixDslDataLoader {
176177
binding.contingencies = { Closure<Void> closure ->
177178
contingenciesData(closure, data, logDslLoader)
178179
}
180+
181+
// losses
182+
binding.losses = { Closure<Void> closure ->
183+
lossesData(closure, network, loader, logDslLoader)
184+
}
179185
}
180186

181187
static MetrixDslData load(Reader reader, Network network, MetrixParameters parameters, ReadOnlyTimeSeriesStore store,

Diff for: metrix-integration/src/main/java/com/powsybl/metrix/integration/MetrixVariable.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public enum MetrixVariable implements MappingVariable {
2525
OFF_GRID_COST_UP("offGridCostUp"),
2626
ON_GRID_COST_DOWN("onGridCostDown"),
2727
ON_GRID_COST_UP("onGridCostUp"),
28+
ON_GRID_DOCTRINE_COST_DOWN("onGridDoctrineCostDown"),
29+
ON_GRID_DOCTRINE_COST_UP("onGridDoctrineCostUp"),
2830
ANALYSIS_THRESHOLD_N("analysisThresholdN"),
2931
ANALYSIS_THRESHOLD_NK("analysisThresholdNk"),
3032
ANALYSIS_THRESHOLD_N_END_OR("analysisThresholdNEndOr"),
@@ -39,7 +41,10 @@ public enum MetrixVariable implements MappingVariable {
3941
THRESHOLD_NK_END_OR("thresholdNkEndOr"),
4042
THRESHOLD_ITAM_END_OR("thresholdITAMEndOr"),
4143
THRESHOLD_ITAM_NK_END_OR("thresholdITAMNkEndOr"),
42-
CURATIVE_COST_DOWN("curativeCostDown");
44+
CURATIVE_COST_DOWN("curativeCostDown"),
45+
PREVENTIVE_DOCTRINE_COST_DOWN("preventiveDoctrineCostDown"),
46+
CURATIVE_DOCTRINE_COST_DOWN("curativeDoctrineCostDown"),
47+
LOSSES_DOCTRINE_COST("lossesDoctrineCost");
4348

4449
protected static final String NAME = "metrix";
4550

0 commit comments

Comments
 (0)