Skip to content

Commit 2c5c2e6

Browse files
committed
Transformer continuous voltage control outer loop
Signed-off-by: Geoffroy Jamgotchian <geoffroy.jamgotchian@rte-france.com>
1 parent 654b0db commit 2c5c2e6

12 files changed

Lines changed: 144 additions & 11 deletions

src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,8 @@ public enum VoltageInitModeOverride {
473473
public enum TransformerVoltageControlMode {
474474
WITH_GENERATOR_VOLTAGE_CONTROL,
475475
AFTER_GENERATOR_VOLTAGE_CONTROL,
476-
INCREMENTAL_VOLTAGE_CONTROL
476+
INCREMENTAL_VOLTAGE_CONTROL,
477+
CONTINUOUS_VOLTAGE_CONTROL
477478
}
478479

479480
public enum ShuntVoltageControlMode {

src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ private void updateAcState(Network network, LoadFlowParameters parameters, OpenL
105105
parameters.isWriteSlackBus(),
106106
parameters.isPhaseShifterRegulationOn(),
107107
parameters.isTransformerVoltageControlOn(),
108+
parametersExt.getTransformerVoltageControlMode(),
108109
parametersExt.isTransformerReactivePowerControl(),
109110
(parameters.isDistributedSlack() || parametersExt.isAreaInterchangeControl()) && (parameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD || parameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD) && parametersExt.isLoadPowerFactorConstant(),
110111
parameters.isDc(),
@@ -229,6 +230,7 @@ private LoadFlowResult.ComponentResult processResult(Network network, DcLoadFlow
229230
parameters.isWriteSlackBus(),
230231
parameters.isPhaseShifterRegulationOn(),
231232
parameters.isTransformerVoltageControlOn(),
233+
parametersExt.getTransformerVoltageControlMode(),
232234
false,
233235
false,
234236
true,

src/main/java/com/powsybl/openloadflow/ac/outerloop/AbstractTransformerVoltageControlOuterLoop.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*/
2222
public abstract class AbstractTransformerVoltageControlOuterLoop implements AcOuterLoop {
2323

24-
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTransformerVoltageControlOuterLoop.class);
24+
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractTransformerVoltageControlOuterLoop.class);
2525

2626
private static final double MIN_TARGET_DEADBAND_KV = 0.1; // kV
2727

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright (c) 2026, 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.openloadflow.ac.outerloop;
9+
10+
import com.powsybl.commons.report.ReportNode;
11+
import com.powsybl.openloadflow.ac.AcOuterLoopContext;
12+
import com.powsybl.openloadflow.lf.outerloop.OuterLoopResult;
13+
import com.powsybl.openloadflow.lf.outerloop.OuterLoopStatus;
14+
import com.powsybl.openloadflow.network.LfBranch;
15+
import com.powsybl.openloadflow.network.LfNetwork;
16+
import com.powsybl.openloadflow.network.PiModel;
17+
import com.powsybl.openloadflow.network.VoltageControl;
18+
19+
/**
20+
* @author Anne Tilloy {@literal <anne.tilloy at rte-france.com>}
21+
*/
22+
public class ContinuousTransformerVoltageControlOuterLoop extends AbstractTransformerVoltageControlOuterLoop {
23+
24+
public static final String NAME = "ContinuousTransformerVoltageControl";
25+
26+
@Override
27+
public String getName() {
28+
return NAME;
29+
}
30+
31+
@Override
32+
public void initialize(AcOuterLoopContext context) {
33+
for (LfBranch controllerBranch : context.getNetwork().<LfBranch>getControllerElements(VoltageControl.Type.TRANSFORMER)) {
34+
if (controllerBranch.isConnectedAtBothSides()) {
35+
controllerBranch.setVoltageControlEnabled(true);
36+
}
37+
}
38+
context.getNetwork().fixTransformerVoltageControls();
39+
}
40+
41+
@Override
42+
public OuterLoopResult check(AcOuterLoopContext context, ReportNode reportNode) {
43+
OuterLoopStatus status = OuterLoopStatus.STABLE;
44+
if (context.getIteration() == 0) {
45+
LfNetwork network = context.getNetwork();
46+
for (LfBranch controllerBranch : network.<LfBranch>getControllerElements(VoltageControl.Type.TRANSFORMER)) {
47+
controllerBranch.setVoltageControlEnabled(false);
48+
49+
// clip the rho shift to min or max if needed
50+
PiModel piModel = controllerBranch.getPiModel();
51+
double r1Value = piModel.getR1();
52+
if (piModel.clipR1()) {
53+
status = OuterLoopStatus.UNSTABLE;
54+
LOGGER.trace("Clip voltage ratio of '{}': {} -> {}", controllerBranch.getId(), r1Value, piModel.getContinuousR1());
55+
}
56+
}
57+
}
58+
return new OuterLoopResult(this, status);
59+
}
60+
}

src/main/java/com/powsybl/openloadflow/lf/outerloop/config/AbstractAcOuterLoopConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ protected static Optional<AcOuterLoop> createTransformerVoltageControlOuterLoop(
106106
case WITH_GENERATOR_VOLTAGE_CONTROL -> new SimpleTransformerVoltageControlOuterLoop();
107107
case AFTER_GENERATOR_VOLTAGE_CONTROL -> new TransformerVoltageControlOuterLoop(useInitialTapPosition, generatorVoltageControlMinNominalVoltage);
108108
case INCREMENTAL_VOLTAGE_CONTROL -> new IncrementalTransformerVoltageControlOuterLoop(incrementalTransformerVoltageControlOuterLoopMaxTapShift);
109+
case CONTINUOUS_VOLTAGE_CONTROL -> new ContinuousTransformerVoltageControlOuterLoop();
109110
};
110111
return Optional.of(outerLoop);
111112
}

src/main/java/com/powsybl/openloadflow/network/LfNetworkStateUpdateParameters.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
*/
88
package com.powsybl.openloadflow.network;
99

10+
import com.powsybl.openloadflow.OpenLoadFlowParameters;
11+
1012
import java.util.Objects;
1113

1214
/**
@@ -22,6 +24,8 @@ public class LfNetworkStateUpdateParameters {
2224

2325
private final boolean transformerVoltageControlOn;
2426

27+
private final OpenLoadFlowParameters.TransformerVoltageControlMode transformerVoltageControlMode;
28+
2529
private final boolean transformerReactivePowerControlOn;
2630

2731
private final boolean loadPowerFactorConstant;
@@ -36,17 +40,19 @@ public class LfNetworkStateUpdateParameters {
3640

3741
private final ReferenceBusSelectionMode referenceBusSelectionMode;
3842

39-
private boolean simulateAutomationSystems;
43+
private final boolean simulateAutomationSystems;
4044

4145
public LfNetworkStateUpdateParameters(boolean reactiveLimits, boolean writeSlackBus, boolean phaseShifterRegulationOn,
42-
boolean transformerVoltageControlOn, boolean transformerReactivePowerControlOn, boolean loadPowerFactorConstant, boolean dc,
46+
boolean transformerVoltageControlOn, OpenLoadFlowParameters.TransformerVoltageControlMode transformerVoltageControlMode,
47+
boolean transformerReactivePowerControlOn, boolean loadPowerFactorConstant, boolean dc,
4348
boolean breakers, ReactivePowerDispatchMode reactivePowerDispatchMode,
4449
boolean writeReferenceTerminals, ReferenceBusSelectionMode referenceBusSelectionMode,
4550
boolean simulateAutomationSystems) {
4651
this.reactiveLimits = reactiveLimits;
4752
this.writeSlackBus = writeSlackBus;
4853
this.phaseShifterRegulationOn = phaseShifterRegulationOn;
4954
this.transformerVoltageControlOn = transformerVoltageControlOn;
55+
this.transformerVoltageControlMode = transformerVoltageControlMode;
5056
this.transformerReactivePowerControlOn = transformerReactivePowerControlOn;
5157
this.loadPowerFactorConstant = loadPowerFactorConstant;
5258
this.dc = dc;
@@ -73,6 +79,10 @@ public boolean isTransformerVoltageControlOn() {
7379
return transformerVoltageControlOn;
7480
}
7581

82+
public OpenLoadFlowParameters.TransformerVoltageControlMode getTransformerVoltageControlMode() {
83+
return transformerVoltageControlMode;
84+
}
85+
7686
public boolean isTransformerReactivePowerControlOn() {
7787
return transformerReactivePowerControlOn;
7888
}

src/main/java/com/powsybl/openloadflow/network/PiModel.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ public interface PiModel {
6161

6262
void roundR1ToClosestTap();
6363

64+
boolean clipR1();
65+
6466
boolean shiftOneTapPositionToChangeA1(Direction direction);
6567

6668
Optional<Direction> updateTapPositionToReachNewR1(double deltaR1, int maxTapShift, AllowedDirection allowedDirection);

src/main/java/com/powsybl/openloadflow/network/PiModelArray.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,23 @@ public void roundR1ToClosestTap() {
294294
r1 = Double.NaN;
295295
}
296296

297+
@Override
298+
public boolean clipR1() {
299+
if (Double.isNaN(r1)) {
300+
return false; // nothing to do because r1 has not been modified
301+
}
302+
if (r1 < minR1) {
303+
r1 = minR1;
304+
tapPositionIndex = lowTapPosition;
305+
return true;
306+
} else if (r1 > maxR1) {
307+
r1 = maxR1;
308+
tapPositionIndex = lowTapPosition + models.size() - 1;
309+
return true;
310+
}
311+
return false;
312+
}
313+
297314
@Override
298315
public boolean shiftOneTapPositionToChangeA1(Direction direction) {
299316
// an increase direction means that A1 should increase.

src/main/java/com/powsybl/openloadflow/network/SimplePiModel.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ public void roundR1ToClosestTap() {
153153
throw new IllegalStateException("R1 rounding is not supported in simple Pi model implementation");
154154
}
155155

156+
@Override
157+
public boolean clipR1() {
158+
throw new IllegalStateException("R1 clipping is not supported in simple Pi model implementation");
159+
}
160+
156161
@Override
157162
public boolean shiftOneTapPositionToChangeA1(Direction direction) {
158163
throw new IllegalStateException(NO_TAP_POSITION_ERROR);

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.powsybl.commons.PowsyblException;
1111
import com.powsybl.iidm.network.*;
1212
import com.powsybl.iidm.network.extensions.LineFortescue;
13+
import com.powsybl.openloadflow.OpenLoadFlowParameters;
1314
import com.powsybl.openloadflow.network.*;
1415
import com.powsybl.openloadflow.sa.LimitReductionManager;
1516
import com.powsybl.openloadflow.util.PerUnit;
@@ -29,6 +30,8 @@ public class LfBranchImpl extends AbstractImpedantLfBranch {
2930

3031
private static final Logger LOGGER = LoggerFactory.getLogger(LfBranchImpl.class);
3132

33+
public static final String RTC_CONTINUOUS_RATIO = "continuousRatio";
34+
3235
private final Ref<Branch<?>> branchRef;
3336

3437
protected LfBranchImpl(LfNetwork network, LfBus bus1, LfBus bus2, PiModel piModel, Branch<?> branch, LfNetworkParameters parameters) {
@@ -370,10 +373,15 @@ public void updateState(LfNetworkStateUpdateParameters parameters, LfNetworkUpda
370373
rtc.unsetSolvedTapPosition();
371374
} else if (parameters.isTransformerVoltageControlOn() && isVoltageController()
372375
|| parameters.isTransformerReactivePowerControlOn() && isTransformerReactivePowerController()) { // it means there is a regulating ratio tap changer
373-
double baseRatio = Transformers.getRatioPerUnitBase(twt);
374-
double rho = getPiModel().getR1() * twt.getRatedU1() / twt.getRatedU2() * baseRatio;
375-
double ptcRho = twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getCurrentStep().getRho() : 1;
376-
updateSolvedTapPosition(rtc, ptcRho, rho);
376+
if (parameters.getTransformerVoltageControlMode() == OpenLoadFlowParameters.TransformerVoltageControlMode.CONTINUOUS_VOLTAGE_CONTROL) {
377+
rtc.unsetSolvedTapPosition();
378+
rtc.setProperty(RTC_CONTINUOUS_RATIO, Double.toString(getPiModel().getR1()));
379+
} else {
380+
double baseRatio = Transformers.getRatioPerUnitBase(twt);
381+
double rho = getPiModel().getR1() * twt.getRatedU1() / twt.getRatedU2() * baseRatio;
382+
double ptcRho = twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getCurrentStep().getRho() : 1;
383+
updateSolvedTapPosition(rtc, ptcRho, rho);
384+
}
377385
} else {
378386
rtc.setSolvedTapPosition(rtc.getTapPosition());
379387
}

0 commit comments

Comments
 (0)