diff --git a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java index 72d3d6019b..321700d039 100644 --- a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java +++ b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java @@ -76,6 +76,13 @@ public enum VoltageInitModeOverride { public static final VoltageInitModeOverride VOLTAGE_INIT_MODE_OVERRIDE_DEFAULT_VALUE = VoltageInitModeOverride.NONE; + public enum TransformerVoltageControlMode { + WITH_GENERATOR_VOLTAGE_CONTROL, + AFTER_GENERATOR_VOLTAGE_CONTROL + } + + public static final TransformerVoltageControlMode TRANSFORMER_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE = TransformerVoltageControlMode.WITH_GENERATOR_VOLTAGE_CONTROL; + private SlackBusSelectionMode slackBusSelectionMode = SLACK_BUS_SELECTION_DEFAULT_VALUE; private List slackBusesIds = Collections.emptyList(); @@ -109,6 +116,8 @@ public enum LowImpedanceBranchMode { private VoltageInitModeOverride voltageInitModeOverride = VOLTAGE_INIT_MODE_OVERRIDE_DEFAULT_VALUE; + private TransformerVoltageControlMode transformerVoltageControlMode = TRANSFORMER_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE; + @Override public String getName() { return "open-load-flow-parameters"; @@ -248,6 +257,15 @@ public OpenLoadFlowParameters setVoltageInitModeOverride(VoltageInitModeOverride return this; } + public TransformerVoltageControlMode getTransformerVoltageControlMode() { + return transformerVoltageControlMode; + } + + public OpenLoadFlowParameters setTransformerVoltageControlMode(TransformerVoltageControlMode transformerVoltageControlMode) { + this.transformerVoltageControlMode = Objects.requireNonNull(transformerVoltageControlMode); + return this; + } + public static OpenLoadFlowParameters load() { return new OpenLoadFlowConfigLoader().load(PlatformConfig.defaultConfig()); } @@ -269,6 +287,7 @@ public String toString() { ", maxIteration=" + maxIteration + ", newtonRaphsonConvEpsPerEq=" + newtonRaphsonConvEpsPerEq + ", voltageInitModeOverride=" + voltageInitModeOverride + + ", transformerVoltageControlMode=" + transformerVoltageControlMode + ')'; } @@ -303,6 +322,8 @@ public static class OpenLoadFlowConfigLoader implements LoadFlowParameters.Confi public static final String VOLTAGE_INIT_MODE_OVERRIDE_NAME = "voltageInitModeOverride"; + public static final String TRANSFORMER_VOLTAGE_CONTROL_MODE_NAME = "transformerVoltageControlMode"; + @Override public OpenLoadFlowParameters load(PlatformConfig platformConfig) { OpenLoadFlowParameters parameters = new OpenLoadFlowParameters(); @@ -325,6 +346,7 @@ public OpenLoadFlowParameters load(PlatformConfig platformConfig) { .setMaxIteration(config.getIntProperty(MAX_ITERATION_NAME, NewtonRaphsonParameters.DEFAULT_MAX_ITERATION)) .setNewtonRaphsonConvEpsPerEq(config.getDoubleProperty(NEWTON_RAPHSON_CONV_EPS_PER_EQ_NAME, DefaultNewtonRaphsonStoppingCriteria.DEFAULT_CONV_EPS_PER_EQ)) .setVoltageInitModeOverride(config.getEnumProperty(VOLTAGE_INIT_MODE_OVERRIDE_NAME, VoltageInitModeOverride.class, VOLTAGE_INIT_MODE_OVERRIDE_DEFAULT_VALUE)) + .setTransformerVoltageControlMode(config.getEnumProperty(TRANSFORMER_VOLTAGE_CONTROL_MODE_NAME, TransformerVoltageControlMode.class, TRANSFORMER_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE)) ); return parameters; } diff --git a/src/main/java/com/powsybl/openloadflow/ac/AbstractTransformerVoltageControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/AbstractTransformerVoltageControlOuterLoop.java new file mode 100644 index 0000000000..b3faa273ba --- /dev/null +++ b/src/main/java/com/powsybl/openloadflow/ac/AbstractTransformerVoltageControlOuterLoop.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2020, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.openloadflow.ac; + +import com.powsybl.openloadflow.ac.outerloop.OuterLoop; +import com.powsybl.openloadflow.ac.outerloop.OuterLoopStatus; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.PiModel; +import com.powsybl.openloadflow.network.TransformerVoltageControl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Anne Tilloy + */ +public abstract class AbstractTransformerVoltageControlOuterLoop implements OuterLoop { + + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTransformerVoltageControlOuterLoop.class); + + protected OuterLoopStatus roundVoltageRatios(LfNetwork network) { + OuterLoopStatus status = OuterLoopStatus.STABLE; + for (LfBranch branch : network.getBranches()) { + TransformerVoltageControl voltageControl = branch.getVoltageControl().orElse(null); + if (voltageControl != null) { + branch.setVoltageControlEnabled(false); + + // round the rho shift to the closest tap + PiModel piModel = branch.getPiModel(); + double r1Value = piModel.getR1(); + piModel.roundR1ToClosestTap(); + double roundedR1Value = piModel.getR1(); + LOGGER.trace("Round voltage ratio of '{}': {} -> {}", branch.getId(), r1Value, roundedR1Value); + + status = OuterLoopStatus.UNSTABLE; + } + } + return status; + } + + @Override + public void cleanup(LfNetwork network) { + for (LfBranch branch : network.getBranches()) { + branch.getVoltageControl().ifPresent(voltageControl -> branch.setVoltageControlEnabled(true)); + } + } +} diff --git a/src/main/java/com/powsybl/openloadflow/ac/DefaultOuterLoopConfig.java b/src/main/java/com/powsybl/openloadflow/ac/DefaultOuterLoopConfig.java index 8f2ab2bfba..4c65a9360b 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/DefaultOuterLoopConfig.java +++ b/src/main/java/com/powsybl/openloadflow/ac/DefaultOuterLoopConfig.java @@ -34,7 +34,13 @@ public List configure(LoadFlowParameters parameters, OpenLoadFlowPara outerLoops.add(new ReactiveLimitsOuterLoop()); } if (parameters.isTransformerVoltageControlOn()) { - outerLoops.add(new TransformerVoltageControlOuterLoop()); + if (parametersExt.getTransformerVoltageControlMode() == OpenLoadFlowParameters.TransformerVoltageControlMode.WITH_GENERATOR_VOLTAGE_CONTROL) { + outerLoops.add(new SimpleTransformerVoltageControlOuterLoop()); + } else if (parametersExt.getTransformerVoltageControlMode() == OpenLoadFlowParameters.TransformerVoltageControlMode.AFTER_GENERATOR_VOLTAGE_CONTROL) { + outerLoops.add(new TransformerVoltageControlOuterLoop()); + } else { + throw new IllegalStateException("Unknown transformer voltage control mode: " + parametersExt.getTransformerVoltageControlMode()); + } } if (parameters.isSimulShunt()) { outerLoops.add(new ShuntVoltageControlOuterLoop()); diff --git a/src/main/java/com/powsybl/openloadflow/ac/SimpleTransformerVoltageControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/SimpleTransformerVoltageControlOuterLoop.java new file mode 100644 index 0000000000..b497c0c679 --- /dev/null +++ b/src/main/java/com/powsybl/openloadflow/ac/SimpleTransformerVoltageControlOuterLoop.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2020, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.openloadflow.ac; + +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.openloadflow.ac.outerloop.OuterLoopContext; +import com.powsybl.openloadflow.ac.outerloop.OuterLoopStatus; + +/** + * @author Anne Tilloy + */ +public class SimpleTransformerVoltageControlOuterLoop extends AbstractTransformerVoltageControlOuterLoop { + + @Override + public String getType() { + return "Simple transformer voltage control"; + } + + @Override + public OuterLoopStatus check(OuterLoopContext context, Reporter reporter) { + OuterLoopStatus status = OuterLoopStatus.STABLE; + if (context.getIteration() == 0) { + status = roundVoltageRatios(context.getNetwork()); + } + return status; + } +} diff --git a/src/main/java/com/powsybl/openloadflow/ac/TransformerVoltageControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/TransformerVoltageControlOuterLoop.java index e74e9f223f..8be1b2f961 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/TransformerVoltageControlOuterLoop.java +++ b/src/main/java/com/powsybl/openloadflow/ac/TransformerVoltageControlOuterLoop.java @@ -7,22 +7,30 @@ package com.powsybl.openloadflow.ac; import com.powsybl.commons.reporter.Reporter; -import com.powsybl.openloadflow.ac.outerloop.OuterLoop; import com.powsybl.openloadflow.ac.outerloop.OuterLoopContext; import com.powsybl.openloadflow.ac.outerloop.OuterLoopStatus; import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; import com.powsybl.openloadflow.network.LfNetwork; -import com.powsybl.openloadflow.network.PiModel; import com.powsybl.openloadflow.network.TransformerVoltageControl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * @author Anne Tilloy */ -public class TransformerVoltageControlOuterLoop implements OuterLoop { +public class TransformerVoltageControlOuterLoop extends AbstractTransformerVoltageControlOuterLoop { - private static final Logger LOGGER = LoggerFactory.getLogger(TransformerVoltageControlOuterLoop.class); + private double maxControlledNominalVoltage = Double.MIN_VALUE; + + @Override + public void initialize(LfNetwork network) { + // All transformer voltage control are disabled for the first equation system resolution. + for (LfBranch branch : network.getBranches()) { + branch.getVoltageControl().ifPresent(voltageControl -> { + branch.setVoltageControlEnabled(false); + maxControlledNominalVoltage = Math.max(maxControlledNominalVoltage, voltageControl.getControlled().getNominalV()); + }); + } + } @Override public String getType() { @@ -32,30 +40,44 @@ public String getType() { @Override public OuterLoopStatus check(OuterLoopContext context, Reporter reporter) { OuterLoopStatus status = OuterLoopStatus.STABLE; + + // At first outer loop iteration, the voltage control of generators that controlled at nominal voltage of + // the set controlledNominalVoltages are disabled. + // The transformer voltage controls are enabled. if (context.getIteration() == 0) { + for (LfBus bus : context.getNetwork().getBuses()) { + if (bus.isVoltageControlled() && bus.getNominalV() <= maxControlledNominalVoltage) { + bus.getVoltageControl().ifPresent(voltageControl -> { + voltageControl.getControllerBuses().forEach(controllerBus -> { + controllerBus.setGenerationTargetQ(controllerBus.getQ().eval()); + controllerBus.setVoltageControlEnabled(false); + }); + }); + status = OuterLoopStatus.UNSTABLE; + } + } for (LfBranch branch : context.getNetwork().getBranches()) { TransformerVoltageControl voltageControl = branch.getVoltageControl().orElse(null); if (voltageControl != null) { - branch.setVoltageControlEnabled(false); - - // round the rho shift to the closest tap - PiModel piModel = branch.getPiModel(); - double r1Value = piModel.getR1(); - piModel.roundR1ToClosestTap(); - double roundedR1Value = piModel.getR1(); - LOGGER.trace("Round voltage ratio of '{}': {} -> {}", branch.getId(), r1Value, roundedR1Value); - + branch.setVoltageControlEnabled(true); status = OuterLoopStatus.UNSTABLE; } } } - return status; - } - @Override - public void cleanup(LfNetwork network) { - for (LfBranch branch : network.getBranches()) { - branch.getVoltageControl().ifPresent(voltageControl -> branch.setVoltageControlEnabled(true)); + // At second outer loop iteration, the transformers are rounded. The generator voltage controls that were + // disabled previously are enabled. + if (context.getIteration() == 1) { + status = roundVoltageRatios(context.getNetwork()); + for (LfBus bus : context.getNetwork().getBuses()) { + if (bus.hasVoltageControllerCapability() && bus.getNominalV() <= maxControlledNominalVoltage) { + bus.setGenerationTargetQ(0); + bus.setVoltageControlEnabled(true); + status = OuterLoopStatus.UNSTABLE; + } + } } + + return status; } } diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java index 08ba79844f..ab00c52503 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/AcloadFlowEngine.java @@ -104,6 +104,11 @@ public AcLoadFlowResult run(Reporter reporter) { NewtonRaphson newtonRaphson = new NewtonRaphson(context.getNetwork(), context.getParameters().getNewtonRaphsonParameters(), context.getEquationSystem(), context.getJacobianMatrix(), context.getTargetVector()); + // outer loops initialization + for (OuterLoop outerLoop : context.getParameters().getOuterLoops()) { + outerLoop.initialize(context.getNetwork()); + } + // run initial Newton-Raphson runningContext.lastNrResult = newtonRaphson.run(reporter); @@ -111,11 +116,6 @@ public AcLoadFlowResult run(Reporter reporter) { if (runningContext.lastNrResult.getStatus() == NewtonRaphsonStatus.CONVERGED) { updatePvBusesReactivePower(runningContext.lastNrResult, context.getNetwork(), context.getEquationSystem()); - // outer loops initialization - for (OuterLoop outerLoop : context.getParameters().getOuterLoops()) { - outerLoop.initialize(context.getNetwork()); - } - // re-run all outer loops until Newton-Raphson failed or no more Newton-Raphson iterations are needed int oldIterationCount; do { diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowTransformerControlTest.java b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowTransformerControlTest.java index 97296b2d32..6a2c16f3e1 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowTransformerControlTest.java +++ b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowTransformerControlTest.java @@ -33,15 +33,13 @@ class AcLoadFlowTransformerControlTest { private Bus bus2; private Bus bus3; private Bus bus4; - private Line line12; - private Line line24; private TwoWindingsTransformer t2wt; private TwoWindingsTransformer t2wt2; private ThreeWindingsTransformer t3wt; - private Load load3; private LoadFlow.Runner loadFlowRunner; private LoadFlowParameters parameters; + private OpenLoadFlowParameters parametersExt; @BeforeEach void setUp() { @@ -49,7 +47,7 @@ void setUp() { parameters = new LoadFlowParameters(); parameters.setTransformerVoltageControlOn(false); parameters.setDistributedSlack(false); - OpenLoadFlowParameters.create(parameters) + parametersExt = OpenLoadFlowParameters.create(parameters) .setSlackBusSelectionMode(SlackBusSelectionMode.FIRST); } @@ -97,6 +95,50 @@ void voltageControlT2wtTest() { assertEquals(3, t2wt.getRatioTapChanger().getTapPosition()); } + @Test + void voltageControlT2wtTest2() { + selectNetwork(VoltageControlNetworkFactory.createNetworkWithT2wt()); + + parameters.setTransformerVoltageControlOn(true); + parametersExt.setTransformerVoltageControlMode(OpenLoadFlowParameters.TransformerVoltageControlMode.AFTER_GENERATOR_VOLTAGE_CONTROL); + t2wt.getRatioTapChanger() + .setTargetDeadband(0) + .setRegulating(true) + .setTapPosition(0) + .setRegulationTerminal(t2wt.getTerminal2()) + .setTargetV(34.0); + + LoadFlowResult result = loadFlowRunner.run(network, parameters); + assertTrue(result.isOk()); + assertVoltageEquals(134.281, bus2); + assertVoltageEquals(34.433, t2wt.getTerminal2().getBusView().getBus()); //FIXME: should be 34.427 + assertEquals(3, t2wt.getRatioTapChanger().getTapPosition()); + } + + @Test + void voltageControlT2wtTest3() { + selectNetwork(VoltageControlNetworkFactory.createNetworkWithT2wt()); + + parameters.setTransformerVoltageControlOn(true); + t2wt.getRatioTapChanger() + .setTargetDeadband(0) + .setRegulating(true) + .setTapPosition(0) + .setRegulationTerminal(t2wt.getTerminal1()) + .setTargetV(135.0); + + LoadFlowResult result = loadFlowRunner.run(network, parameters); + assertFalse(result.isOk()); + + parametersExt.setTransformerVoltageControlMode(OpenLoadFlowParameters.TransformerVoltageControlMode.AFTER_GENERATOR_VOLTAGE_CONTROL); + LoadFlowResult result2 = loadFlowRunner.run(network, parameters); + assertTrue(result2.isOk()); + + assertVoltageEquals(134.281, bus2); + assertVoltageEquals(27.0, t2wt.getTerminal2().getBusView().getBus()); + assertEquals(0, t2wt.getRatioTapChanger().getTapPosition()); + } + @Test void remoteVoltageControlT2wtTest() { selectNetwork(VoltageControlNetworkFactory.createNetworkWithT2wt()); @@ -615,13 +657,12 @@ private Network createNetworkWithSharedControl() { private void selectNetwork(Network network) { this.network = network; + bus1 = network.getBusBreakerView().getBus("BUS_1"); bus2 = network.getBusBreakerView().getBus("BUS_2"); bus3 = network.getBusBreakerView().getBus("BUS_3"); bus4 = network.getBusBreakerView().getBus("BUS_4"); - line12 = network.getLine("LINE_12"); - t2wt = network.getTwoWindingsTransformer("T2wT"); t3wt = network.getThreeWindingsTransformer("T3wT"); } diff --git a/src/test/resources/debug-parameters.json b/src/test/resources/debug-parameters.json index 1c2606cb90..a44bad15a7 100644 --- a/src/test/resources/debug-parameters.json +++ b/src/test/resources/debug-parameters.json @@ -29,7 +29,8 @@ "voltagePerReactivePowerControl" : false, "maxIteration" : 30, "newtonRaphsonConvEpsPerEq" : 1.0E-4, - "voltageInitModeOverride" : "NONE" + "voltageInitModeOverride" : "NONE", + "transformerVoltageControlMode" : "WITH_GENERATOR_VOLTAGE_CONTROL" } } },