diff --git a/src/main/java/com/powsybl/openloadflow/AcLoadFlowFromCache.java b/src/main/java/com/powsybl/openloadflow/AcLoadFlowFromCache.java index ac95f2942a..7a79919590 100644 --- a/src/main/java/com/powsybl/openloadflow/AcLoadFlowFromCache.java +++ b/src/main/java/com/powsybl/openloadflow/AcLoadFlowFromCache.java @@ -14,6 +14,8 @@ import com.powsybl.openloadflow.ac.AcLoadFlowParameters; import com.powsybl.openloadflow.ac.AcLoadFlowResult; import com.powsybl.openloadflow.ac.AcloadFlowEngine; +import com.powsybl.openloadflow.ac.nr.NewtonRaphsonStatus; +import com.powsybl.openloadflow.lf.outerloop.OuterLoopStatus; import com.powsybl.openloadflow.network.impl.LfNetworkList; import com.powsybl.openloadflow.network.impl.Networks; import org.slf4j.Logger; @@ -93,13 +95,16 @@ private List initContexts(NetworkCache.Entry entry) { } private static AcLoadFlowResult run(AcLoadFlowContext context) { - if (context.getNetwork().isValid() && context.isNetworkUpdated()) { + if (!context.getNetwork().isValid()) { + return AcLoadFlowResult.createNoCalculationResult(context.getNetwork()); + } + if (context.isNetworkUpdated()) { AcLoadFlowResult result = new AcloadFlowEngine(context) .run(); context.setNetworkUpdated(false); return result; } - return AcLoadFlowResult.createNoCalculationResult(context.getNetwork()); + return new AcLoadFlowResult(context.getNetwork(), 0, 0, NewtonRaphsonStatus.CONVERGED, OuterLoopStatus.STABLE, 0d, 0d); } public List run() { diff --git a/src/main/java/com/powsybl/openloadflow/NetworkCache.java b/src/main/java/com/powsybl/openloadflow/NetworkCache.java index d3cc5479f4..874122ce68 100644 --- a/src/main/java/com/powsybl/openloadflow/NetworkCache.java +++ b/src/main/java/com/powsybl/openloadflow/NetworkCache.java @@ -15,6 +15,7 @@ import com.powsybl.openloadflow.network.LfShunt; import com.powsybl.openloadflow.network.GeneratorVoltageControl; import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.impl.AbstractLfGenerator; import com.powsybl.openloadflow.network.util.PreviousValueVoltageInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -134,8 +135,18 @@ private boolean onGeneratorUpdate(Generator generator, String attribute, Object if (attribute.equals("targetV")) { double valueShift = (double) newValue - (double) oldValue; GeneratorVoltageControl voltageControl = lfBus.getGeneratorVoltageControl().orElseThrow(); - double newTargetV = voltageControl.getTargetValue() + valueShift / lfBus.getNominalV(); - voltageControl.setTargetValue(newTargetV); + double nominalV = voltageControl.getControlledBus().getNominalV(); + double newTargetV = voltageControl.getTargetValue() + valueShift / nominalV; + LfNetworkParameters networkParameters = context.getParameters().getNetworkParameters(); + if (AbstractLfGenerator.checkTargetV(generator.getId(), newTargetV, nominalV, networkParameters, null)) { + voltageControl.setTargetValue(newTargetV); + } else { + context.getNetwork().getGeneratorById(generator.getId()).setGeneratorControlType(LfGenerator.GeneratorControlType.OFF); + if (lfBus.getGenerators().stream().noneMatch(gen -> gen.getGeneratorControlType() == LfGenerator.GeneratorControlType.VOLTAGE)) { + lfBus.setGeneratorVoltageControlEnabled(false); + } + } + context.getNetwork().validate(LoadFlowModel.AC, null); return true; } return false; diff --git a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java index b9b8027b90..9393d811fe 100644 --- a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java +++ b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java @@ -130,7 +130,7 @@ private LoadFlowResult runAc(Network network, LoadFlowParameters parameters, Ope List componentResults = new ArrayList<>(results.size()); for (AcLoadFlowResult result : results) { // update network state - if (result.getNewtonRaphsonStatus() == NewtonRaphsonStatus.CONVERGED || parametersExt.isAlwaysUpdateNetwork()) { + if ((result.getNewtonRaphsonStatus() == NewtonRaphsonStatus.CONVERGED && result.getNewtonRaphsonIterations() > 0) || parametersExt.isAlwaysUpdateNetwork()) { var updateParameters = new LfNetworkStateUpdateParameters(parameters.isUseReactiveLimits(), parameters.isWriteSlackBus(), parameters.isPhaseShifterRegulationOn(), diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java index 8a00f8d4be..f64eb6f07a 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java @@ -490,16 +490,18 @@ public void fix(boolean minImpedance, double lowImpedanceThreshold) { private void validateBuses(LoadFlowModel loadFlowModel, Reporter reporter) { if (loadFlowModel == LoadFlowModel.AC) { - boolean hasAtLeastOneBusVoltageControlled = false; + boolean hasAtLeastOneBusGeneratorVoltageControlEnabled = false; for (LfBus bus : busesByIndex) { - if (bus.isGeneratorVoltageControlled()) { - hasAtLeastOneBusVoltageControlled = true; + if (bus.isGeneratorVoltageControlEnabled()) { + hasAtLeastOneBusGeneratorVoltageControlEnabled = true; break; } } - if (!hasAtLeastOneBusVoltageControlled) { - LOGGER.error("Network {} must have at least one bus voltage controlled", this); - Reports.reportNetworkMustHaveAtLeastOneBusVoltageControlled(reporter); + if (!hasAtLeastOneBusGeneratorVoltageControlEnabled) { + LOGGER.error("Network {} must have at least one bus with generator voltage control enabled", this); + if (reporter != null) { + Reports.reportNetworkMustHaveAtLeastOneBusGeneratorVoltageControlEnabled(reporter); + } valid = false; } } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java index e3990ec255..b3cacb0344 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java @@ -212,7 +212,7 @@ protected void setVoltageControl(double targetV, Terminal terminal, Terminal reg LOGGER.warn("Regulating terminal of LfGenerator {} is not in the same synchronous component: voltage control discarded", getId()); return; } - if (!checkTargetV(targetV / regulatingTerminal.getVoltageLevel().getNominalV(), regulatingTerminal.getVoltageLevel().getNominalV(), parameters, report)) { + if (!checkTargetV(getId(), targetV / regulatingTerminal.getVoltageLevel().getNominalV(), regulatingTerminal.getVoltageLevel().getNominalV(), parameters, report)) { return; } this.controlledBusId = controlledBus.getId(); @@ -262,13 +262,15 @@ protected boolean checkVoltageControlConsistency(LfNetworkParameters parameters, return consistency; } - protected boolean checkTargetV(double targetV, double nominalV, LfNetworkParameters parameters, LfNetworkLoadingReport report) { + public static boolean checkTargetV(String generatorId, double targetV, double nominalV, LfNetworkParameters parameters, LfNetworkLoadingReport report) { // check that targetV has a plausible value (wrong nominal voltage issue) if (nominalV > parameters.getMinNominalVoltageTargetVoltageCheck() && (targetV < parameters.getMinPlausibleTargetVoltage() || targetV > parameters.getMaxPlausibleTargetVoltage())) { LOGGER.trace("Generator '{}' has an inconsistent target voltage: {} pu: generator voltage control discarded", - getId(), targetV); - report.generatorsWithInconsistentTargetVoltage++; + generatorId, targetV); + if (report != null) { + report.generatorsWithInconsistentTargetVoltage++; + } return false; } return true; diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java index a2e7891d3f..6ba23e70ea 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java @@ -31,7 +31,7 @@ private LfDanglingLineGenerator(DanglingLine danglingLine, LfNetwork network, St // local control only if (danglingLine.getGeneration().isVoltageRegulationOn() && checkVoltageControlConsistency(parameters, report)) { // The controlled bus cannot be reached from the DanglingLine parameters (there is no terminal in DanglingLine.Generation) - if (checkTargetV(danglingLine.getGeneration().getTargetV() / danglingLine.getTerminal().getVoltageLevel().getNominalV(), + if (checkTargetV(getId(), danglingLine.getGeneration().getTargetV() / danglingLine.getTerminal().getVoltageLevel().getNominalV(), danglingLine.getTerminal().getVoltageLevel().getNominalV(), parameters, report)) { this.controlledBusId = Objects.requireNonNull(controlledLfBusId); this.targetV = danglingLine.getGeneration().getTargetV() / danglingLine.getTerminal().getVoltageLevel().getNominalV(); diff --git a/src/main/java/com/powsybl/openloadflow/util/Reports.java b/src/main/java/com/powsybl/openloadflow/util/Reports.java index 72e24f63ed..98ab266b24 100644 --- a/src/main/java/com/powsybl/openloadflow/util/Reports.java +++ b/src/main/java/com/powsybl/openloadflow/util/Reports.java @@ -47,10 +47,10 @@ public static void reportNetworkBalance(Reporter reporter, double activeGenerati .build()); } - public static void reportNetworkMustHaveAtLeastOneBusVoltageControlled(Reporter reporter) { + public static void reportNetworkMustHaveAtLeastOneBusGeneratorVoltageControlEnabled(Reporter reporter) { reporter.report(Report.builder() - .withKey("networkMustHaveAtLeastOneBusVoltageControlled") - .withDefaultMessage("Network must have at least one bus voltage controlled") + .withKey("networkMustHaveAtLeastOneBusGeneratorVoltageControlEnabled") + .withDefaultMessage("Network must have at least one bus with generator voltage control enabled") .build()); } diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowEurostagTutorialExample1Test.java b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowEurostagTutorialExample1Test.java index db43731d0d..3602d8840f 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowEurostagTutorialExample1Test.java +++ b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowEurostagTutorialExample1Test.java @@ -288,7 +288,7 @@ void noGeneratorTest() { ReporterModel postLoadingReporter = createNetworkReporter.getSubReporters().get(0); assertEquals("postLoadingProcessing", postLoadingReporter.getTaskKey()); assertEquals(1, postLoadingReporter.getReports().size()); - assertEquals("Network must have at least one bus voltage controlled", + assertEquals("Network must have at least one bus with generator voltage control enabled", postLoadingReporter.getReports().iterator().next().getDefaultMessage()); } diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowWithCachingTest.java b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowWithCachingTest.java index 0fbcf1b2af..07c07a41c1 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowWithCachingTest.java +++ b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowWithCachingTest.java @@ -74,8 +74,7 @@ void testTargetV() { result = loadFlowRunner.run(network, parameters); assertEquals(1, NetworkCache.INSTANCE.getEntryCount()); - // FIXME NO_CALCULATION should be added to API - assertEquals(LoadFlowResult.ComponentResult.Status.FAILED, result.getComponentResults().get(0).getStatus()); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus()); assertEquals(0, result.getComponentResults().get(0).getIterationCount()); } @@ -333,4 +332,33 @@ void testSwitchClose() { assertActivePowerEquals(301.884, l1.getTerminal1()); assertActivePowerEquals(301.884, l2.getTerminal1()); } + + @Test + void testInvalidNetwork() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + var result = loadFlowRunner.run(network, parameters); + + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + var gen = network.getGenerator("GEN"); + gen.setTargetV(1000); + result = loadFlowRunner.run(network, parameters); + assertEquals(LoadFlowResult.ComponentResult.Status.FAILED, result.getComponentResults().get(0).getStatus()); + } + + @Test + @Disabled("To support later") + void testInitiallyInvalidNetwork() { + var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create()); + var gen = network.getGenerator("GEN"); + gen.setTargetV(1000); + var result = loadFlowRunner.run(network, parameters); + assertEquals(LoadFlowResult.ComponentResult.Status.FAILED, result.getComponentResults().get(0).getStatus()); + + assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); + + gen.setTargetV(24); + result = loadFlowRunner.run(network, parameters); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus()); + } }