Skip to content

Commit 6fc7736

Browse files
Add the use of frequency reserve on maxP reduction (#255)
* Add the use of frequency reserve on maxP reduction Signed-off-by: Franck LECUYER <[email protected]>
1 parent 7c561c7 commit 6fc7736

File tree

9 files changed

+334
-12
lines changed

9 files changed

+334
-12
lines changed

src/main/java/org/gridsuite/modification/server/dto/GenerationDispatchInfos.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public class GenerationDispatchInfos extends ModificationInfos {
4444
@Schema(description = "generators with fixed supply")
4545
private List<GeneratorsFilterInfos> generatorsWithFixedSupply;
4646

47+
@Schema(description = "generators frequency reserve")
48+
private List<GeneratorsFrequencyReserveInfos> generatorsFrequencyReserve;
49+
4750
@Override
4851
public GenerationDispatchEntity toEntity() {
4952
return new GenerationDispatchEntity(this);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2023, 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+
*/
7+
package org.gridsuite.modification.server.dto;
8+
9+
import io.swagger.v3.oas.annotations.media.Schema;
10+
import lombok.AllArgsConstructor;
11+
import lombok.Getter;
12+
import lombok.NoArgsConstructor;
13+
import lombok.Setter;
14+
import lombok.experimental.SuperBuilder;
15+
16+
import java.util.List;
17+
18+
/**
19+
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
20+
*/
21+
@SuperBuilder
22+
@NoArgsConstructor
23+
@AllArgsConstructor
24+
@Getter
25+
@Setter
26+
@Schema(description = "Generators frequency reserve infos")
27+
public class GeneratorsFrequencyReserveInfos {
28+
@Schema(description = "generators filters")
29+
private List<GeneratorsFilterInfos> generatorsFilters;
30+
31+
@Schema(description = "frequency reserve")
32+
Double frequencyReserve;
33+
}

src/main/java/org/gridsuite/modification/server/entities/equipment/modification/GenerationDispatchEntity.java

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@
1212
import lombok.NonNull;
1313
import org.gridsuite.modification.server.dto.GenerationDispatchInfos;
1414
import org.gridsuite.modification.server.dto.GeneratorsFilterInfos;
15+
import org.gridsuite.modification.server.dto.GeneratorsFrequencyReserveInfos;
1516
import org.gridsuite.modification.server.dto.ModificationInfos;
1617
import org.gridsuite.modification.server.entities.ModificationEntity;
1718

19+
import javax.persistence.CascadeType;
1820
import javax.persistence.CollectionTable;
1921
import javax.persistence.Column;
2022
import javax.persistence.ElementCollection;
2123
import javax.persistence.Entity;
24+
import javax.persistence.FetchType;
25+
import javax.persistence.OneToMany;
2226
import javax.persistence.Table;
2327
import javax.validation.constraints.NotNull;
2428
import java.util.List;
@@ -47,6 +51,9 @@ public class GenerationDispatchEntity extends ModificationEntity {
4751
@CollectionTable(name = "generatorsWithFixedSupply")
4852
private List<GeneratorsFilterEmbeddable> generatorsWithFixedSupply;
4953

54+
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
55+
private List<GeneratorsFrequencyReserveEntity> generatorsFrequencyReserve;
56+
5057
public GenerationDispatchEntity(@NotNull GenerationDispatchInfos generationDispatchInfos) {
5158
super(generationDispatchInfos);
5259
assignAttributes(generationDispatchInfos);
@@ -61,23 +68,54 @@ public void update(@NonNull ModificationInfos modificationInfos) {
6168
private void assignAttributes(GenerationDispatchInfos generationDispatchInfos) {
6269
lossCoefficient = generationDispatchInfos.getLossCoefficient();
6370
defaultOutageRate = generationDispatchInfos.getDefaultOutageRate();
64-
generatorsWithoutOutage = toEmbeddable(generationDispatchInfos.getGeneratorsWithoutOutage());
65-
generatorsWithFixedSupply = toEmbeddable(generationDispatchInfos.getGeneratorsWithFixedSupply());
71+
generatorsWithoutOutage = toEmbeddableGeneratorsFilters(generationDispatchInfos.getGeneratorsWithoutOutage());
72+
generatorsWithFixedSupply = toEmbeddableGeneratorsFilters(generationDispatchInfos.getGeneratorsWithFixedSupply());
73+
if (generatorsFrequencyReserve == null) {
74+
generatorsFrequencyReserve = toEmbeddableGeneratorsFrequencyReserve(generationDispatchInfos.getGeneratorsFrequencyReserve());
75+
} else {
76+
generatorsFrequencyReserve.clear();
77+
generatorsFrequencyReserve.addAll(toEmbeddableGeneratorsFrequencyReserve(generationDispatchInfos.getGeneratorsFrequencyReserve()));
78+
}
6679
}
6780

68-
public static List<GeneratorsFilterEmbeddable> toEmbeddable(List<GeneratorsFilterInfos> generators) {
81+
public static List<GeneratorsFilterEmbeddable> toEmbeddableGeneratorsFilters(List<GeneratorsFilterInfos> generators) {
6982
return generators == null ? null : generators.stream()
7083
.map(generator -> new GeneratorsFilterEmbeddable(generator.getId(), generator.getName()))
7184
.collect(Collectors.toList());
7285
}
7386

87+
public static List<GeneratorsFrequencyReserveEntity> toEmbeddableGeneratorsFrequencyReserve(List<GeneratorsFrequencyReserveInfos> generators) {
88+
List<GeneratorsFrequencyReserveEntity> generatorsFrequencyReserveEntities = null;
89+
if (generators != null) {
90+
generatorsFrequencyReserveEntities = generators.stream().map(generator -> {
91+
List<GeneratorsFilterEmbeddable> generatorsFilterEmbeddables = generator.getGeneratorsFilters().stream().map(filter ->
92+
new GeneratorsFilterEmbeddable(filter.getId(), filter.getName())).collect(Collectors.toList());
93+
return new GeneratorsFrequencyReserveEntity(generatorsFilterEmbeddables, generator.getFrequencyReserve());
94+
}).collect(Collectors.toList());
95+
}
96+
return generatorsFrequencyReserveEntities;
97+
}
98+
7499
private List<GeneratorsFilterInfos> toGeneratorsFilters(List<GeneratorsFilterEmbeddable> generatorsFilters) {
75100
return generatorsFilters != null ? generatorsFilters
76101
.stream()
77102
.map(generator -> new GeneratorsFilterInfos(generator.getId(), generator.getName()))
78103
.collect(Collectors.toList()) : null;
79104
}
80105

106+
private List<GeneratorsFrequencyReserveInfos> toGeneratorsFrequencyReserve(List<GeneratorsFrequencyReserveEntity> generatorsFrequencyReserve) {
107+
List<GeneratorsFrequencyReserveInfos> generatorsFrequencyReserveInfos = null;
108+
if (generatorsFrequencyReserve != null) {
109+
generatorsFrequencyReserveInfos = generatorsFrequencyReserve.stream()
110+
.map(generator -> {
111+
List<GeneratorsFilterInfos> generatorsFilterInfos = generator.getGeneratorsFilters().stream().map(filter ->
112+
new GeneratorsFilterInfos(filter.getId(), filter.getName())).collect(Collectors.toList());
113+
return new GeneratorsFrequencyReserveInfos(generatorsFilterInfos, generator.getFrequencyReserve());
114+
}).collect(Collectors.toList());
115+
}
116+
return generatorsFrequencyReserveInfos;
117+
}
118+
81119
@Override
82120
public GenerationDispatchInfos toModificationInfos() {
83121
return GenerationDispatchInfos.builder()
@@ -87,6 +125,7 @@ public GenerationDispatchInfos toModificationInfos() {
87125
.defaultOutageRate(getDefaultOutageRate())
88126
.generatorsWithoutOutage(toGeneratorsFilters(generatorsWithoutOutage))
89127
.generatorsWithFixedSupply(toGeneratorsFilters(generatorsWithFixedSupply))
128+
.generatorsFrequencyReserve(toGeneratorsFrequencyReserve(generatorsFrequencyReserve))
90129
.build();
91130
}
92131
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2023, 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+
*/
7+
package org.gridsuite.modification.server.entities.equipment.modification;
8+
9+
import lombok.AllArgsConstructor;
10+
import lombok.Getter;
11+
import lombok.NoArgsConstructor;
12+
13+
import javax.persistence.CollectionTable;
14+
import javax.persistence.Column;
15+
import javax.persistence.ElementCollection;
16+
import javax.persistence.Entity;
17+
import javax.persistence.GeneratedValue;
18+
import javax.persistence.GenerationType;
19+
import javax.persistence.Id;
20+
import javax.persistence.Table;
21+
import java.util.List;
22+
import java.util.UUID;
23+
24+
/**
25+
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
26+
*/
27+
@NoArgsConstructor
28+
@AllArgsConstructor
29+
@Getter
30+
@Entity
31+
@Table(name = "GeneratorsFrequencyReserve")
32+
public class GeneratorsFrequencyReserveEntity {
33+
@Id
34+
@GeneratedValue(strategy = GenerationType.AUTO)
35+
@Column(name = "id")
36+
private UUID id;
37+
38+
@ElementCollection
39+
@CollectionTable(name = "generatorsFrequencyReserveFilters")
40+
private List<GeneratorsFilterEmbeddable> generatorsFilters;
41+
42+
@Column(name = "frequencyReserve")
43+
private double frequencyReserve;
44+
45+
public GeneratorsFrequencyReserveEntity(List<GeneratorsFilterEmbeddable> generatorsFilters, double frequencyReserve) {
46+
this.id = null;
47+
this.generatorsFilters = generatorsFilters;
48+
this.frequencyReserve = frequencyReserve;
49+
}
50+
}

src/main/java/org/gridsuite/modification/server/modifications/GenerationDispatch.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.powsybl.iidm.network.Network;
2525
import com.powsybl.iidm.network.extensions.GeneratorStartup;
2626
import com.powsybl.network.store.iidm.impl.NetworkImpl;
27+
import lombok.Builder;
28+
import lombok.Getter;
2729
import org.gridsuite.modification.server.NetworkModificationException;
2830
import org.gridsuite.modification.server.dto.FilterEquipments;
2931
import org.gridsuite.modification.server.dto.GenerationDispatchInfos;
@@ -42,6 +44,7 @@
4244
import java.util.Objects;
4345
import java.util.TreeSet;
4446
import java.util.UUID;
47+
import java.util.concurrent.atomic.AtomicReference;
4548
import java.util.function.Function;
4649
import java.util.stream.Collectors;
4750
import java.util.stream.Stream;
@@ -92,7 +95,7 @@ private static double computeTotalDemand(Component component, double lossCoeffic
9295
return totalLoad * (1. + lossCoefficient / 100.);
9396
}
9497

95-
private double computeTotalAmountFixedSupply(Network network, Component component, List<String> generatorsWithFixedSupply, Reporter reporter) {
98+
private static double computeTotalAmountFixedSupply(Network network, Component component, List<String> generatorsWithFixedSupply, Reporter reporter) {
9699
double totalAmountFixedSupply = 0.;
97100

98101
totalAmountFixedSupply += generatorsWithFixedSupply.stream().map(network::getGenerator)
@@ -116,7 +119,7 @@ private static Component getSynchronousComponentFrom(HvdcConverterStation<?> sta
116119
return station.getTerminal().getBusView().getBus().getSynchronousComponent();
117120
}
118121

119-
private double computeHvdcBalance(Component component) {
122+
private static double computeHvdcBalance(Component component) {
120123
AtomicDouble balance = new AtomicDouble(0.);
121124

122125
component.getBusStream().forEach(bus -> {
@@ -151,7 +154,7 @@ private double computeHvdcBalance(Component component) {
151154
return balance.get();
152155
}
153156

154-
private List<Generator> computeAdjustableGenerators(Component component, List<String> generatorsWithFixedSupply, Reporter reporter) {
157+
private static List<Generator> computeAdjustableGenerators(Component component, List<String> generatorsWithFixedSupply, Reporter reporter) {
155158
List<Generator> res;
156159

157160
// get all generators in the component
@@ -205,6 +208,13 @@ public void onUpdate(Identifiable identifiable, String attribute, String variant
205208
}
206209
}
207210

211+
@Builder
212+
@Getter
213+
private static class GeneratorsFrequencyReserve {
214+
private final List<String> generators;
215+
private final double frequencyReserve;
216+
}
217+
208218
@Override
209219
public void check(Network network) throws NetworkModificationException {
210220
double lossCoefficient = generationDispatchInfos.getLossCoefficient();
@@ -217,8 +227,8 @@ public void check(Network network) throws NetworkModificationException {
217227
}
218228
}
219229

220-
private List<String> exportFilters(List<GeneratorsFilterInfos> generatorsFilters,
221-
Network network, Reporter subReporter, ApplicationContext context) {
230+
private static List<String> exportFilters(List<GeneratorsFilterInfos> generatorsFilters,
231+
Network network, Reporter subReporter, ApplicationContext context) {
222232
if (CollectionUtils.isEmpty(generatorsFilters)) {
223233
return List.of();
224234
}
@@ -263,7 +273,27 @@ private List<String> collectGeneratorsWithFixedSupply(Network network, Reporter
263273
return exportFilters(generationDispatchInfos.getGeneratorsWithFixedSupply(), network, subReporter, context);
264274
}
265275

266-
private double reduceGeneratorMaxPValue(Generator generator, List<String> generatorsWithoutOutage) {
276+
private List<GeneratorsFrequencyReserve> collectGeneratorsWithFrequencyReserve(Network network, Reporter subReporter, ApplicationContext context) {
277+
return generationDispatchInfos.getGeneratorsFrequencyReserve().stream().map(g -> {
278+
List<String> generators = exportFilters(g.getGeneratorsFilters(), network, subReporter, context);
279+
return GeneratorsFrequencyReserve.builder().generators(generators).frequencyReserve(g.getFrequencyReserve()).build();
280+
}).collect(Collectors.toList());
281+
}
282+
283+
private static double computeGenFrequencyReserve(Generator generator,
284+
List<GeneratorsFrequencyReserve> generatorsFrequencyReserve) {
285+
AtomicReference<Double> freqReserve = new AtomicReference<>(0.);
286+
generatorsFrequencyReserve.forEach(g -> {
287+
if (g.getGenerators().contains(generator.getId())) {
288+
freqReserve.set(g.getFrequencyReserve());
289+
}
290+
});
291+
return freqReserve.get();
292+
}
293+
294+
private double reduceGeneratorMaxPValue(Generator generator,
295+
List<String> generatorsWithoutOutage,
296+
List<GeneratorsFrequencyReserve> generatorsFrequencyReserve) {
267297
double res = generator.getMaxP();
268298
if (!generatorsWithoutOutage.contains(generator.getId())) {
269299
GeneratorStartup startupExtension = generator.getExtension(GeneratorStartup.class);
@@ -275,7 +305,8 @@ private double reduceGeneratorMaxPValue(Generator generator, List<String> genera
275305
res *= 1. - generationDispatchInfos.getDefaultOutageRate() / 100.;
276306
}
277307
}
278-
return res;
308+
double genFrequencyReserve = computeGenFrequencyReserve(generator, generatorsFrequencyReserve);
309+
return res * (1. - genFrequencyReserve / 100.);
279310
}
280311

281312
@Override
@@ -291,6 +322,9 @@ public void apply(Network network, Reporter subReporter, ApplicationContext cont
291322
// get generators with fixed supply
292323
List<String> generatorsWithFixedSupply = collectGeneratorsWithFixedSupply(network, subReporter, context);
293324

325+
// get generators with frequency reserve
326+
List<GeneratorsFrequencyReserve> generatorsWithFrequencyReserve = collectGeneratorsWithFrequencyReserve(network, subReporter, context);
327+
294328
for (Component component : synchronousComponents) {
295329
int componentNum = component.getNum();
296330

@@ -331,7 +365,7 @@ public void apply(Network network, Reporter subReporter, ApplicationContext cont
331365
// stacking of adjustable generators to ensure the totalAmountSupplyToBeDispatched
332366
List<Scalable> generatorsScalable = adjustableGenerators.stream().map(generator -> {
333367
double minValue = generator.getMinP();
334-
double maxValue = reduceGeneratorMaxPValue(generator, generatorsWithoutOutage);
368+
double maxValue = reduceGeneratorMaxPValue(generator, generatorsWithoutOutage, generatorsWithFrequencyReserve);
335369
return (Scalable) Scalable.onGenerator(generator.getId(), minValue, maxValue);
336370
}).collect(Collectors.toList());
337371

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
2+
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
3+
<changeSet author="lecuyerfra (generated)" id="1684760502004-5">
4+
<createTable tableName="generation_dispatch_generators_frequency_reserve">
5+
<column name="generation_dispatch_entity_id" type="UUID">
6+
<constraints nullable="false"/>
7+
</column>
8+
<column name="generators_frequency_reserve_id" type="UUID">
9+
<constraints nullable="false"/>
10+
</column>
11+
</createTable>
12+
</changeSet>
13+
<changeSet author="lecuyerfra (generated)" id="1684760502004-6">
14+
<createTable tableName="generators_frequency_reserve">
15+
<column name="id" type="UUID">
16+
<constraints nullable="false" primaryKey="true" primaryKeyName="generators_frequency_reservePK"/>
17+
</column>
18+
<column name="frequency_reserve" type="FLOAT8"/>
19+
</createTable>
20+
</changeSet>
21+
<changeSet author="lecuyerfra (generated)" id="1684760502004-7">
22+
<createTable tableName="generators_frequency_reserve_filters">
23+
<column name="generators_frequency_reserve_entity_id" type="UUID">
24+
<constraints nullable="false"/>
25+
</column>
26+
<column name="filter_id" type="UUID"/>
27+
<column name="filter_name" type="VARCHAR(255)"/>
28+
</createTable>
29+
</changeSet>
30+
<changeSet author="lecuyerfra (generated)" id="1684760502004-8">
31+
<addUniqueConstraint columnNames="generators_frequency_reserve_id" constraintName="UK_qsdphctot5lvye9dxd908nxqe" tableName="generation_dispatch_generators_frequency_reserve"/>
32+
</changeSet>
33+
<changeSet author="lecuyerfra (generated)" id="1684760502004-9">
34+
<addForeignKeyConstraint baseColumnNames="generators_frequency_reserve_id" baseTableName="generation_dispatch_generators_frequency_reserve" constraintName="FK3knktj1ylaintk61vnhhkdl08" deferrable="false" initiallyDeferred="false" referencedColumnNames="id" referencedTableName="generators_frequency_reserve" validate="true"/>
35+
</changeSet>
36+
<changeSet author="lecuyerfra (generated)" id="1684760502004-10">
37+
<addForeignKeyConstraint baseColumnNames="generation_dispatch_entity_id" baseTableName="generation_dispatch_generators_frequency_reserve" constraintName="FK6b4as01ter3mb9m8ry5cl7gw8" deferrable="false" initiallyDeferred="false" referencedColumnNames="id" referencedTableName="generation_dispatch" validate="true"/>
38+
</changeSet>
39+
<changeSet author="lecuyerfra (generated)" id="1684760502004-11">
40+
<addForeignKeyConstraint baseColumnNames="generators_frequency_reserve_entity_id" baseTableName="generators_frequency_reserve_filters" constraintName="FKg01bd3thgs9i3pshw8v7igqx6" deferrable="false" initiallyDeferred="false" referencedColumnNames="id" referencedTableName="generators_frequency_reserve" validate="true"/>
41+
</changeSet>
42+
</databaseChangeLog>

src/main/resources/db/changelog/db.changelog-master.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ databaseChangeLog:
138138
- include:
139139
file: changesets/changelog_20230512T142024Z.xml
140140
relativeToChangelogFile: true
141+
- include:
142+
file: changesets/changelog_20230522T130120Z.xml
143+
relativeToChangelogFile: true
141144
- include:
142145
file: changesets/changelog_20230515T143336Z.xml
143146
relativeToChangelogFile: true

0 commit comments

Comments
 (0)