Skip to content

Commit 6bdec1c

Browse files
committed
Add inconcistency management
Signed-off-by: Didier Vidal <[email protected]>
1 parent 3a0f4c0 commit 6bdec1c

File tree

10 files changed

+120
-25
lines changed

10 files changed

+120
-25
lines changed

src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,6 @@ protected boolean checkIfGeneratorIsInsideActivePowerLimitsForVoltageControl(LfN
258258

259259
@Override
260260
public double getLocalTargetV() {
261-
return generatorRef.get().getLocatTargetV() / getBus().getNominalV();
261+
return generatorRef.get().getLocalTargetV() / getBus().getNominalV();
262262
}
263263
}

src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkLoaderImpl.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ private static void createVoltageControls(List<LfBus> lfBuses, LfNetworkParamete
121121
}
122122
}
123123

124-
private static void checkAndCreateVoltageControl(LfBus controllerBus, List<GeneratorVoltageControl> voltageControls, List<LfGenerator> voltageControlGenerators, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
124+
private static void checkAndCreateVoltageControl(LfBus controllerBus,
125+
List<GeneratorVoltageControl> voltageControls,
126+
List<LfGenerator> voltageControlGenerators,
127+
LfNetworkParameters parameters,
128+
LfNetworkLoadingReport report) {
125129
LfGenerator lfGenerator0 = voltageControlGenerators.get(0);
126130
LfBus controlledBus = lfGenerator0.getControlledBus();
127131
double controllerTargetV = lfGenerator0.getTargetV();
@@ -147,30 +151,42 @@ private static void checkAndCreateVoltageControl(LfBus controllerBus, List<Gener
147151
controlledBus.getGeneratorVoltageControl().ifPresentOrElse(
148152
vc -> updateGeneratorVoltageControl(vc, controllerBus, controllerTargetV),
149153
() -> createGeneratorVoltageControl(controlledBus, controllerBus, controllerTargetV, voltageControls, parameters));
150-
} else {
151-
double localTargetV = getLocalVoltageTarget(controllerBus, controllerTargetV, controlledBus.getNominalV());
152-
// TODO handle the case where the controlledBus voltage control exists
153-
controlledBus.getGeneratorVoltageControl().ifPresentOrElse(
154-
vc -> updateGeneratorVoltageControl(vc, controllerBus, controllerTargetV), // updating only to check targetV uniqueness
155-
() -> createGeneratorVoltageControl(controllerBus, controllerBus, localTargetV, voltageControls, parameters));
154+
} else { // !parameters.isGeneratorVoltageRemoteControl() && controlledBus != controllerBus
155+
double localTargetV = getLocalVoltageTarget(controllerBus, controllerTargetV, controlledBus.getNominalV(),
156+
parameters.isDisableInconsistentVoltageControls(), report);
157+
if (!Double.isNaN(localTargetV)) {
158+
if (controlledBus.getGeneratorVoltageControl().isEmpty()) {
159+
createGeneratorVoltageControl(controllerBus, controllerBus, localTargetV, voltageControls, parameters);
160+
}
161+
}
156162
}
157163
}
158164

159-
private static double getLocalVoltageTarget(LfBus controllerBus, double remoteTargetV, double remoteNominal) {
165+
/**
166+
* Returns the local target from backup if available. Returns Double.NaN in case of inconsistent backup target for the bus.
167+
*/
168+
private static double getLocalVoltageTarget(LfBus controllerBus,
169+
double remoteTargetV,
170+
double remoteNominal,
171+
boolean disableInconsistentVoltageControls,
172+
LfNetworkLoadingReport report) {
160173
double localTargetV = Double.NaN;
161174
for (LfGenerator generator : controllerBus.getGenerators()) {
162175
double genLocalTargetV = generator.getLocalTargetV();
163176
if (!Double.isNaN(genLocalTargetV)) {
164177
if (Double.isNaN(localTargetV)) {
165178
localTargetV = genLocalTargetV;
179+
} else {
180+
if (disableInconsistentVoltageControls && Math.abs(localTargetV - genLocalTargetV) > TARGET_V_EPSILON) {
181+
return Double.NaN;
182+
}
183+
// else if !disableInconsistentVoltageControls keep the first voltage target found
166184
}
167-
// TODO - handle case where another group has an inconistent targetV
168185
}
169186
}
170187
if (Double.isNaN(localTargetV)) {
171-
// TODO: add method argument and LOG only if requested
172-
// TODO: add a report ?
173-
localTargetV = remoteTargetV ;
188+
report.rescaledRemoteVoltageControls += 1;
189+
localTargetV = remoteTargetV;
174190
LOGGER.warn("Remote voltage control is not activated and no local terget is defined. The voltage target of {} with remote control is rescaled from {} to {}",
175191
controllerBus.getId(), remoteTargetV * remoteNominal, localTargetV * controllerBus.getNominalV());
176192
}
@@ -1088,6 +1104,12 @@ private LfNetwork create(int numCC, int numSC, Network network, List<Bus> buses,
10881104
lfNetwork, report.shuntsWithInconsistentTargetVoltage);
10891105
}
10901106

1107+
if (report.rescaledRemoteVoltageControls > 0) {
1108+
Reports.reportRescaledRemoteVoltageControls(reportNode, report.rescaledRemoteVoltageControls);
1109+
LOGGER.warn("Network {}: {} remote voltage controls have no backup local targetV and have been rescaled",
1110+
lfNetwork, report.rescaledRemoteVoltageControls);
1111+
}
1112+
10911113
if (parameters.getDebugDir() != null) {
10921114
Path debugDir = DebugUtil.getDebugDir(parameters.getDebugDir());
10931115
String dateStr = ZonedDateTime.now().format(DATE_TIME_FORMAT);

src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkLoadingReport.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/**
2-
* Copyright (c) 2019, RTE (http://www.rte-france.com)
1+
/*
2+
* Copyright (c) 2019-2025, RTE (http://www.rte-france.com)
33
* This Source Code Form is subject to the terms of the Mozilla Public
44
* License, v. 2.0. If a copy of the MPL was not distributed with this
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@@ -47,4 +47,6 @@ public class LfNetworkLoadingReport {
4747
int transformersWithInconsistentTargetVoltage = 0;
4848

4949
int shuntsWithInconsistentTargetVoltage = 0;
50+
51+
int rescaledRemoteVoltageControls = 0;
5052
}

src/main/java/com/powsybl/openloadflow/util/Reports.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,14 @@ public static void reportShuntsDiscardedFromVoltageControlBecauseTargetVIsIncons
643643
.add();
644644
}
645645

646+
public static void reportRescaledRemoteVoltageControls(ReportNode reportNode, int rescaledRemoteVoltageControls) {
647+
reportNode.newReportNode()
648+
.withMessageTemplate("olf.rescaledRemoteVoltageControls")
649+
.withUntypedValue("count", rescaledRemoteVoltageControls)
650+
.withSeverity(TypedValue.WARN_SEVERITY)
651+
.add();
652+
}
653+
646654
public static void reportAcLfCompleteWithSuccess(ReportNode reportNode, String solverStatus, String outerloopStatus) {
647655
reportNode.newReportNode()
648656
.withMessageTemplate("olf.acLfCompleteWithSuccess")

src/main/resources/com/powsybl/openloadflow/reports.properties

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
#
2+
# Copyright (c) 2025, 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+
19
olf.acEmulationDisabledInWoodburyDcSecurityAnalysis = AC emulation of HVDC lines is disabled with Woodbury DC Security Analysis. HVDC active power setpoint will be used instead.
210
olf.acLfCompleteWithError = AC load flow completed with error (solverStatus=${solverStatus}, outerloopStatus=${outerloopStatus})
311
olf.acLfCompleteWithSuccess = AC load flow completed successfully (solverStatus=${solverStatus}, outerloopStatus=${outerloopStatus})
@@ -71,6 +79,7 @@ olf.PvToPqMinRealisticV = Switch bus '${busId}' PV -> PQ, q set to ${targetQ} =
7179
olf.reactiveControllerBusesToPqBuses = ${remoteReactivePowerControllerBusToPqCount} bus(es) with remote reactive power controller switched PQ
7280
olf.reactiveControllerBusesToPqMaxQ = Remote reactive power controller bus '${busId}' -> PQ, q=${busQ} > maxQ=${maxQ}
7381
olf.reactiveControllerBusesToPqMinQ = Remote reactive power controller bus '${busId}' -> PQ, q=${busQ} < minQ=${minQ}
82+
olf.rescaledRemoteVoltageControls = ${count} remote voltage controls have no backup local targetV and have been rescaled
7483
olf.residualDistributionMismatch = Remaining residual slack bus active power mismatch after active power distribution, ${mismatch} MW remains
7584
olf.sensitivityAnalysis = Sensitivity analysis on network '${networkId}'
7685
olf.shuntsDiscardedFromVoltageControlBecauseTargetVIsInconsistent = ${impactedShuntCount} shunt compensators have been discarded from voltage control because targetV is inconsistent

src/main/resources/com/powsybl/openloadflow/reports_fr.properties

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
#
2+
# Copyright (c) 2025, 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+
19
olf.acEmulationDisabledInWoodburyDcSecurityAnalysis = L'émulation AC des lignes HVDC est désactivée avec l'analyse de sécurité en actif seul de Woodbury. La consigne de puissance active de la ligne HVDC sera utilisée à la place.
210
olf.acLfCompleteWithError = Calcul de répartition AC terminé avec erreur (solverStatus=${solverStatus}, outerloopStatus=${outerloopStatus})
311
olf.acLfCompleteWithSuccess = Calcul de répartition AC terminé avec succès (solverStatus=${solverStatus}, outerloopStatus=${outerloopStatus})
@@ -71,6 +79,7 @@ olf.PvToPqMinRealisticV = Passage PV-PQ du noeud
7179
olf.reactiveControllerBusesToPqBuses = ${remoteReactivePowerControllerBusToPqCount} noeuds électriques avec régleur de puissance réactive à distance passé(s) en PQ
7280
olf.reactiveControllerBusesToPqMaxQ = Réglage de la puissance réactive distante du noeud électrique '${busId}' -> PQ, q=${busQ} > maxQ=${maxQ}
7381
olf.reactiveControllerBusesToPqMinQ = Noeud électrique ${busId} régleur de puissance réactive à distance -> PQ, q=${busQ} < minQ=${minQ}
82+
olf.rescaledRemoteVoltageControls = ${count} contrôles de tension distants n'ont pas de consigne équivalente locale et passent en réglage local par mise à l'échelle de la tension nominale locals.
7483
olf.residualDistributionMismatch = Puissance active résiduelle au noeud bilan après compensation, ${mismatch} MW restants
7584
olf.sensitivityAnalysis = Analyse de sensibilité sur le réseau '${networkId}'
7685
olf.shuntsDiscardedFromVoltageControlBecauseTargetVIsInconsistent = ${impactedShuntCount} moyens de compensation statique ont été mis hors réglage de tension car leur tension de consigne (targetV) est incohérente.

src/test/java/com/powsybl/openloadflow/ac/GeneratorRemoteControlLocalRescaleTest.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@
77
*/
88
package com.powsybl.openloadflow.ac;
99

10+
import com.powsybl.commons.report.ReportNode;
11+
import com.powsybl.commons.test.PowsyblTestReportResourceBundle;
1012
import com.powsybl.iidm.network.*;
1113
import com.powsybl.loadflow.LoadFlow;
1214
import com.powsybl.loadflow.LoadFlowParameters;
1315
import com.powsybl.loadflow.LoadFlowResult;
16+
import com.powsybl.loadflow.LoadFlowRunParameters;
1417
import com.powsybl.math.matrix.DenseMatrixFactory;
1518
import com.powsybl.openloadflow.OpenLoadFlowParameters;
1619
import com.powsybl.openloadflow.OpenLoadFlowProvider;
1720
import com.powsybl.openloadflow.util.LoadFlowAssert;
21+
import com.powsybl.openloadflow.util.report.PowsyblOpenLoadFlowReportResourceBundle;
1822
import org.junit.jupiter.api.BeforeEach;
1923
import org.junit.jupiter.api.Test;
2024

25+
import java.io.IOException;
26+
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
2128
import static org.junit.jupiter.api.Assertions.assertTrue;
2229

2330
/**
@@ -92,11 +99,19 @@ void setUp() {
9299
}
93100

94101
@Test
95-
void test() {
96-
LoadFlowResult result = loadFlowRunner.run(network, parameters);
102+
void test() throws IOException {
103+
ReportNode reportNode = ReportNode.newRootReportNode()
104+
.withResourceBundles(PowsyblOpenLoadFlowReportResourceBundle.BASE_NAME, PowsyblTestReportResourceBundle.TEST_BASE_NAME)
105+
.withMessageTemplate("testReport")
106+
.build();
107+
LoadFlowRunParameters rp = new LoadFlowRunParameters()
108+
.setReportNode(reportNode)
109+
.setParameters(parameters);
110+
LoadFlowResult result = loadFlowRunner.run(network, rp);
97111
assertTrue(result.isFullyConverged());
98112
LoadFlowAssert.assertVoltageEquals(20.67, b1); // check local targetV has been correctly rescaled 20.67=413.4/400*20
99113
LoadFlowAssert.assertVoltageEquals(395.927, b2);
114+
LoadFlowAssert.assertReportEquals("/targetVRescaleReport.txt", reportNode);
100115
}
101116

102117
@Test
@@ -114,7 +129,7 @@ void testLocalTargetV() {
114129
network.getGenerator("g1").setTargetV(413.4, 21.5535);
115130
parameters.getExtension(OpenLoadFlowParameters.class).setVoltageRemoteControl(false);
116131

117-
result = loadFlowRunner.run(network, parameters);
132+
result = loadFlowRunner.run(network, parameters);
118133

119134
assertTrue(result.isFullyConverged());
120135
LoadFlowAssert.assertVoltageEquals(413.4, b2);
@@ -165,7 +180,25 @@ void testInconsistentLocalTargetV() {
165180

166181
assertTrue(result.isFullyConverged());
167182
LoadFlowAssert.assertVoltageEquals(400.48, b2);
168-
LoadFlowAssert.assertVoltageEquals(20.09, b1); // The local target V is maintained
183+
LoadFlowAssert.assertVoltageEquals(20.9, b1); // The local target V of first generatour found is maintained
184+
185+
parameters.getExtension(OpenLoadFlowParameters.class).setDisableInconsistentVoltageControls(true);
186+
network.getGenerator("g1").setTargetQ(10);
187+
g2.setTargetQ(10);
188+
result = loadFlowRunner.run(network, parameters);
189+
190+
// The groups have been disabled from voltage control
191+
assertTrue(result.getComponentResults().getFirst().getStatus() == LoadFlowResult.ComponentResult.Status.NO_CALCULATION);
192+
assertEquals("Network has no generator with voltage control enabled", result.getComponentResults().getFirst().getStatusText());
169193

194+
// set consistent targets and run with disableInconsistentVOltaeg mode
195+
network.getGenerator("g1").setTargetV(413.4, 21);
196+
g2.setTargetV(413.4, 21);
197+
198+
result = loadFlowRunner.run(network, parameters);
199+
200+
assertTrue(result.isFullyConverged());
201+
LoadFlowAssert.assertVoltageEquals(402.45, b2);
202+
LoadFlowAssert.assertVoltageEquals(21.0, b1); // The local target V is maintained
170203
}
171204
}

src/test/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysisContingenciesTest.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/**
2-
* Copyright (c) 2021, RTE (http://www.rte-france.com)
1+
/*
2+
* Copyright (c) 2021-2025, RTE (http://www.rte-france.com)
33
* This Source Code Form is subject to the terms of the Mozilla Public
44
* License, v. 2.0. If a copy of the MPL was not distributed with this
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@@ -27,8 +27,7 @@
2727
import org.junit.jupiter.params.ParameterizedTest;
2828
import org.junit.jupiter.params.provider.ValueSource;
2929

30-
import java.io.IOException;
31-
import java.io.InputStream;
30+
import java.io.*;
3231
import java.nio.file.FileSystem;
3332
import java.nio.file.Files;
3433
import java.nio.file.Path;

src/test/resources/debug-network.xiidm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_14" xmlns:reft="http://www.powsybl.org/schema/iidm/ext/reference_terminals/1_0" id="test" caseDate="2021-04-25T13:47:34.697+02:00" forecastDistance="0" sourceFormat="code" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
2+
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_15" xmlns:reft="http://www.powsybl.org/schema/iidm/ext/reference_terminals/1_0" id="test" caseDate="2021-04-25T13:47:34.697+02:00" forecastDistance="0" sourceFormat="code" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
33
<iidm:substation id="b1_s" country="FR">
44
<iidm:voltageLevel id="b1_vl" nominalV="1.0" topologyKind="BUS_BREAKER">
55
<iidm:busBreakerTopology>
@@ -64,4 +64,4 @@
6464
<reft:referenceTerminal id="l13" side="TWO"/>
6565
</reft:referenceTerminals>
6666
</iidm:extension>
67-
</iidm:network>
67+
</iidm:network>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
+ Test Report
2+
+ Load flow on network 'GeneratorRemoteControlLocalRescaleTest'
3+
+ Network CC0 SC0
4+
1 remote voltage controls have no backup local targetV and have been rescaled
5+
+ Network info
6+
Network has 2 buses and 1 branches
7+
Network balance: active generation=100 MW, active load=99.9 MW, reactive generation=0 MVar, reactive load=80 MVar
8+
Angle reference bus: vl2_0
9+
Slack bus: vl2_0
10+
Outer loop DistributedSlack
11+
Outer loop VoltageMonitoring
12+
Outer loop ReactiveLimits
13+
AC load flow completed successfully (solverStatus=CONVERGED, outerloopStatus=STABLE)

0 commit comments

Comments
 (0)