diff --git a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java index 9b6bf588e8..dff70922ee 100644 --- a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java +++ b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java @@ -24,13 +24,17 @@ import com.powsybl.openloadflow.ac.solver.*; import com.powsybl.openloadflow.ac.outerloop.AcOuterLoop; import com.powsybl.openloadflow.ac.outerloop.ReactiveLimitsOuterLoop; +import com.powsybl.openloadflow.dc.DcAreaInterchangeControlOuterLoop; +import com.powsybl.openloadflow.dc.DcIncrementalPhaseControlOuterLoop; import com.powsybl.openloadflow.dc.DcLoadFlowParameters; +import com.powsybl.openloadflow.dc.DcOuterLoop; import com.powsybl.openloadflow.dc.DcValueVoltageInitializer; import com.powsybl.openloadflow.dc.equations.DcApproximationType; import com.powsybl.openloadflow.dc.equations.DcEquationSystemCreationParameters; import com.powsybl.openloadflow.graph.GraphConnectivityFactory; import com.powsybl.openloadflow.lf.AbstractLoadFlowParameters; import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.util.ActivePowerDistribution; import com.powsybl.openloadflow.network.util.PreviousValueVoltageInitializer; import com.powsybl.openloadflow.network.util.UniformValueVoltageInitializer; import com.powsybl.openloadflow.network.util.VoltageInitializer; @@ -1816,7 +1820,7 @@ public static AcLoadFlowParameters createAcParameters(Network network, LoadFlowP return acParameters; } - static List createOuterLoops(LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) { + static List createAcOuterLoops(LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) { AcOuterLoopConfig outerLoopConfig = AcOuterLoopConfig.findOuterLoopConfig() .orElseGet(() -> parametersExt.getOuterLoopNames() != null ? new ExplicitAcOuterLoopConfig() : new DefaultAcOuterLoopConfig()); @@ -1835,7 +1839,7 @@ public static AcLoadFlowParameters createAcParameters(LoadFlowParameters paramet VoltageInitializer voltageInitializer = getExtendedVoltageInitializer(parameters, parametersExt, networkParameters, matrixFactory); - List outerLoops = createOuterLoops(parameters, parametersExt); + List outerLoops = createAcOuterLoops(parameters, parametersExt); AcSolverFactory solverFactory = AcSolverFactory.find(parametersExt.getAcSolverType()); @@ -1851,6 +1855,20 @@ public static AcLoadFlowParameters createAcParameters(LoadFlowParameters paramet .setSolverFactory(solverFactory, parameters); } + static List createDcOuterLoops(LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt) { + List outerLoops = new ArrayList<>(); + if (parameters.isPhaseShifterRegulationOn()) { + DcIncrementalPhaseControlOuterLoop phaseShifterControlOuterLoop = new DcIncrementalPhaseControlOuterLoop(); + outerLoops.add(phaseShifterControlOuterLoop); + } + if (parametersExt.isAreaInterchangeControl()) { + ActivePowerDistribution activePowerDistribution = ActivePowerDistribution.create(parameters.getBalanceType(), false, parametersExt.isUseActiveLimits()); + DcAreaInterchangeControlOuterLoop areaInterchangeControlOuterLoop = new DcAreaInterchangeControlOuterLoop(activePowerDistribution, parametersExt.getSlackBusPMaxMismatch(), parametersExt.getAreaInterchangePMaxMismatch()); + outerLoops.add(areaInterchangeControlOuterLoop); + } + return outerLoops; + } + public static DcLoadFlowParameters createDcParameters(Network network, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt, MatrixFactory matrixFactory, GraphConnectivityFactory connectivityFactory, boolean forcePhaseControlOffAndAddAngle1Var) { @@ -1911,9 +1929,9 @@ public static DcLoadFlowParameters createDcParameters(LoadFlowParameters paramet .setSlackDistributionFailureBehavior(parametersExt.getSlackDistributionFailureBehavior()) .setMatrixFactory(matrixFactory) .setDistributedSlack(parameters.isDistributedSlack()) - .setAreaInterchangeControl(parametersExt.isAreaInterchangeControl()) .setBalanceType(parameters.getBalanceType()) .setSetVToNan(true) + .setOuterLoops(createDcOuterLoops(parameters, parametersExt)) .setMaxOuterLoopIterations(parametersExt.getMaxOuterLoopIterations()) .setSlackBusPMaxMismatch(parametersExt.getSlackBusPMaxMismatch()) .setAreaInterchangePMaxMismatch(parametersExt.getAreaInterchangePMaxMismatch()); diff --git a/src/main/java/com/powsybl/openloadflow/dc/DcAreaInterchangeControlControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/dc/DcAreaInterchangeControlControlOuterLoop.java deleted file mode 100644 index cbba0cfd8d..0000000000 --- a/src/main/java/com/powsybl/openloadflow/dc/DcAreaInterchangeControlControlOuterLoop.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) - * 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/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.openloadflow.dc; - -import com.powsybl.openloadflow.dc.equations.DcEquationType; -import com.powsybl.openloadflow.dc.equations.DcVariableType; -import com.powsybl.openloadflow.lf.outerloop.AbstractAreaInterchangeControlOuterLoop; -import com.powsybl.openloadflow.network.LfBus; -import com.powsybl.openloadflow.network.util.ActivePowerDistribution; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * @author Valentin Mouradian {@literal } - */ -public class DcAreaInterchangeControlControlOuterLoop extends AbstractAreaInterchangeControlOuterLoop implements DcOuterLoop { - - private static final Logger LOGGER = LoggerFactory.getLogger(DcAreaInterchangeControlControlOuterLoop.class); - - protected DcAreaInterchangeControlControlOuterLoop(ActivePowerDistribution activePowerDistribution, double slackBusPMaxMismatch, double areaInterchangePMaxMismatch) { - super(activePowerDistribution, null, slackBusPMaxMismatch, areaInterchangePMaxMismatch, LOGGER); - } - - @Override - public String getName() { - return "AreaInterchangeControl"; - } - - @Override - public double getSlackBusActivePowerMismatch(DcOuterLoopContext context) { - List buses = context.getNetwork().getBuses(); - return DcLoadFlowEngine.getActivePowerMismatch(buses); - } -} diff --git a/src/main/java/com/powsybl/openloadflow/dc/DcAreaInterchangeControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/dc/DcAreaInterchangeControlOuterLoop.java new file mode 100644 index 0000000000..c6ab5396a6 --- /dev/null +++ b/src/main/java/com/powsybl/openloadflow/dc/DcAreaInterchangeControlOuterLoop.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * 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/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.openloadflow.dc; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.openloadflow.dc.equations.DcEquationType; +import com.powsybl.openloadflow.dc.equations.DcVariableType; +import com.powsybl.openloadflow.lf.outerloop.AbstractAreaInterchangeControlOuterLoop; +import com.powsybl.openloadflow.lf.outerloop.OuterLoopResult; +import com.powsybl.openloadflow.lf.outerloop.OuterLoopStatus; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.util.ActivePowerDistribution; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * @author Valentin Mouradian {@literal } + */ +public class DcAreaInterchangeControlOuterLoop extends AbstractAreaInterchangeControlOuterLoop implements DcOuterLoop { + + private static final Logger LOGGER = LoggerFactory.getLogger(DcAreaInterchangeControlOuterLoop.class); + + public DcAreaInterchangeControlOuterLoop(ActivePowerDistribution activePowerDistribution, double slackBusPMaxMismatch, double areaInterchangePMaxMismatch) { + super(activePowerDistribution, new DcNoAreaOuterLoop(), slackBusPMaxMismatch, areaInterchangePMaxMismatch, LOGGER); + } + + @Override + public String getName() { + return "AreaInterchangeControl"; + } + + @Override + public double getSlackBusActivePowerMismatch(DcOuterLoopContext context) { + List buses = context.getNetwork().getBuses(); + return DcLoadFlowEngine.getActivePowerMismatch(buses); + } + + /** + * If the network has no area, the area interchange control is replaced by slack distribution. + * In DC mode, the slack distribution is handled directly by the load flow engine, without any outer loop. + * This class will be used as fallback outerloop in case the network has no area, and has no need to do anything. + */ + private static class DcNoAreaOuterLoop implements DcOuterLoop { + @Override + public String getName() { + return "DcNoAreaOuterLoop"; + } + + @Override + public OuterLoopResult check(DcOuterLoopContext context, ReportNode reportNode) { + return new OuterLoopResult(this, OuterLoopStatus.STABLE); + } + } +} diff --git a/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java b/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java index da3c9fc7b4..a97e2e508a 100644 --- a/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java +++ b/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java @@ -31,7 +31,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -184,37 +183,33 @@ public DcLoadFlowResult run() { DcLoadFlowParameters parameters = context.getParameters(); TargetVector targetVector = context.getTargetVector(); RunningContext runningContext = new RunningContext(); + List outerLoops = parameters.getOuterLoops(); - // outer loop initialization - List> outerLoopsAndContexts = new ArrayList<>(); - - if (parameters.getNetworkParameters().isPhaseControl()) { - DcIncrementalPhaseControlOuterLoop phaseShifterControlOuterLoop = new DcIncrementalPhaseControlOuterLoop(); - DcOuterLoopContext phaseShifterControlOuterLoopContext = new DcOuterLoopContext(network); - outerLoopsAndContexts.add(Pair.of(phaseShifterControlOuterLoop, phaseShifterControlOuterLoopContext)); - phaseShifterControlOuterLoop.initialize(phaseShifterControlOuterLoopContext); - } + List> outerLoopsAndContexts = outerLoops.stream() + .map(outerLoop -> Pair.of(outerLoop, new DcOuterLoopContext(network))) + .toList(); - if (parameters.isAreaInterchangeControl() && network.hasArea()) { - ActivePowerDistribution activePowerDistribution = ActivePowerDistribution.create(parameters.getBalanceType(), false, parameters.getNetworkParameters().isUseActiveLimits()); - DcAreaInterchangeControlControlOuterLoop areaInterchangeControlOuterLoop = new DcAreaInterchangeControlControlOuterLoop(activePowerDistribution, parameters.getSlackBusPMaxMismatch(), parameters.getAreaInterchangePMaxMismatch()); - DcOuterLoopContext areaInterchangeControlOuterLoopContext = new DcOuterLoopContext(network); - outerLoopsAndContexts.add(Pair.of(areaInterchangeControlOuterLoop, areaInterchangeControlOuterLoopContext)); - areaInterchangeControlOuterLoop.initialize(areaInterchangeControlOuterLoopContext); + // outer loops initialization + for (var outerLoopAndContext : outerLoopsAndContexts) { + var outerLoop = outerLoopAndContext.getLeft(); + var outerLoopContext = outerLoopAndContext.getRight(); + outerLoop.initialize(outerLoopContext); } initStateVector(network, equationSystem, new UniformValueVoltageInitializer()); double initialSlackBusActivePowerMismatch = getActivePowerMismatch(network.getBuses()); double distributedActivePower = 0.0; - if (parameters.isDistributedSlack() || parameters.isAreaInterchangeControl()) { + + boolean isAreaInterchangeControl = outerLoops.stream().anyMatch(DcAreaInterchangeControlOuterLoop.class::isInstance); + if (parameters.isDistributedSlack() || isAreaInterchangeControl) { LoadFlowParameters.BalanceType balanceType = parameters.getBalanceType(); boolean useActiveLimits = parameters.getNetworkParameters().isUseActiveLimits(); ActivePowerDistribution activePowerDistribution = ActivePowerDistribution.create(balanceType, false, useActiveLimits); var result = activePowerDistribution.run(network, initialSlackBusActivePowerMismatch); final LfGenerator referenceGenerator; final OpenLoadFlowParameters.SlackDistributionFailureBehavior behavior; - if (parameters.isAreaInterchangeControl()) { + if (isAreaInterchangeControl && network.hasArea()) { // actual behavior will be handled by the outerloop itself, just leave on slack bus here behavior = OpenLoadFlowParameters.SlackDistributionFailureBehavior.LEAVE_ON_SLACK_BUS; referenceGenerator = null; @@ -284,7 +279,7 @@ public DcLoadFlowResult run() { for (var outerLoopAndContext : Lists.reverse(outerLoopsAndContexts)) { var outerLoop = outerLoopAndContext.getLeft(); var outerLoopContext = outerLoopAndContext.getRight(); - if (outerLoop instanceof DcAreaInterchangeControlControlOuterLoop activePowerDistributionOuterLoop) { + if (outerLoop instanceof DcAreaInterchangeControlOuterLoop activePowerDistributionOuterLoop) { distributedActivePower += activePowerDistributionOuterLoop.getDistributedActivePower(outerLoopContext); } outerLoop.cleanup(outerLoopContext); diff --git a/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowParameters.java b/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowParameters.java index 550769c64d..9320521cf8 100644 --- a/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowParameters.java +++ b/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowParameters.java @@ -12,6 +12,8 @@ import com.powsybl.openloadflow.dc.equations.DcEquationSystemCreationParameters; import com.powsybl.openloadflow.lf.AbstractLoadFlowParameters; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -23,12 +25,12 @@ public class DcLoadFlowParameters extends AbstractLoadFlowParameters outerLoops = Collections.emptyList(); + private int maxOuterLoopIterations = DEFAULT_MAX_OUTER_LOOP_ITERATIONS; private double slackBusPMaxMismatch = OpenLoadFlowParameters.SLACK_BUS_P_MAX_MISMATCH_DEFAULT_VALUE; @@ -62,15 +64,6 @@ public DcLoadFlowParameters setDistributedSlack(boolean distributedSlack) { return this; } - public boolean isAreaInterchangeControl() { - return areaInterchangeControl; - } - - public DcLoadFlowParameters setAreaInterchangeControl(boolean areaInterchangeControl) { - this.areaInterchangeControl = areaInterchangeControl; - return this; - } - public LoadFlowParameters.BalanceType getBalanceType() { return balanceType; } @@ -89,6 +82,15 @@ public DcLoadFlowParameters setSetVToNan(boolean setVToNan) { return this; } + public List getOuterLoops() { + return outerLoops; + } + + public DcLoadFlowParameters setOuterLoops(List outerLoops) { + this.outerLoops = Objects.requireNonNull(outerLoops); + return this; + } + public double getSlackBusPMaxMismatch() { return slackBusPMaxMismatch; } diff --git a/src/main/java/com/powsybl/openloadflow/lf/outerloop/AbstractAreaInterchangeControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/lf/outerloop/AbstractAreaInterchangeControlOuterLoop.java index 83a65b9019..1615f00946 100644 --- a/src/main/java/com/powsybl/openloadflow/lf/outerloop/AbstractAreaInterchangeControlOuterLoop.java +++ b/src/main/java/com/powsybl/openloadflow/lf/outerloop/AbstractAreaInterchangeControlOuterLoop.java @@ -41,8 +41,7 @@ public abstract class AbstractAreaInterchangeControlOuterLoop< P extends AbstractLoadFlowParameters

, C extends LoadFlowContext, O extends AbstractOuterLoopContext> - extends AbstractActivePowerDistributionOuterLoop - implements OuterLoop, ActivePowerDistributionOuterLoop { + extends AbstractActivePowerDistributionOuterLoop { private final Logger logger; diff --git a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java index e0743a0126..326c6bbaf6 100644 --- a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java +++ b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java @@ -407,17 +407,17 @@ void testExplicitOuterLoopsParameter() { OpenLoadFlowParameters parametersExt = new OpenLoadFlowParameters() .setSecondaryVoltageControl(true); - assertEquals(List.of("DistributedSlack", "SecondaryVoltageControl", "VoltageMonitoring", "ReactiveLimits", "ShuntVoltageControl"), OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); + assertEquals(List.of("DistributedSlack", "SecondaryVoltageControl", "VoltageMonitoring", "ReactiveLimits", "ShuntVoltageControl"), OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); parametersExt.setOuterLoopNames(List.of("ReactiveLimits", "SecondaryVoltageControl")); - assertEquals(List.of("ReactiveLimits", "SecondaryVoltageControl"), OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); + assertEquals(List.of("ReactiveLimits", "SecondaryVoltageControl"), OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); parametersExt.setOuterLoopNames(ExplicitAcOuterLoopConfig.NAMES); - PowsyblException e = assertThrows(PowsyblException.class, () -> OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt)); + PowsyblException e = assertThrows(PowsyblException.class, () -> OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt)); assertEquals("Multiple (2) outer loops with same type: ShuntVoltageControl", e.getMessage()); parametersExt.setOuterLoopNames(List.of("ReactiveLimits", "Foo")); - e = assertThrows(PowsyblException.class, () -> OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt)); + e = assertThrows(PowsyblException.class, () -> OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt)); assertEquals("Unknown outer loop 'Foo'", e.getMessage()); assertEquals("Ordered explicit list of outer loop names, supported outer loops are IncrementalPhaseControl, DistributedSlack, IncrementalShuntVoltageControl, IncrementalTransformerVoltageControl, VoltageMonitoring, PhaseControl, ReactiveLimits, SecondaryVoltageControl, ShuntVoltageControl, SimpleTransformerVoltageControl, TransformerVoltageControl, AutomationSystem, IncrementalTransformerReactivePowerControl, AreaInterchangeControl", @@ -430,17 +430,16 @@ void testSlackDistributionOuterLoops() { .setDistributedSlack(true); OpenLoadFlowParameters parametersExt = new OpenLoadFlowParameters(); - assertEquals(List.of("DistributedSlack", "VoltageMonitoring", "ReactiveLimits"), OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); + assertEquals(List.of("DistributedSlack", "VoltageMonitoring", "ReactiveLimits"), OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); parametersExt.setAreaInterchangeControl(true); - assertEquals(List.of("AreaInterchangeControl", "VoltageMonitoring", "ReactiveLimits"), OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); + assertEquals(List.of("AreaInterchangeControl", "VoltageMonitoring", "ReactiveLimits"), OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); parametersExt.setOuterLoopNames(List.of("DistributedSlack", "AreaInterchangeControl")); - assertEquals(List.of("AreaInterchangeControl"), OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); + assertEquals(List.of("AreaInterchangeControl"), OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); parametersExt.setOuterLoopNames(List.of("DistributedSlack")); - assertEquals(List.of("DistributedSlack"), OpenLoadFlowParameters.createOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); - + assertEquals(List.of("DistributedSlack"), OpenLoadFlowParameters.createAcOuterLoops(parameters, parametersExt).stream().map(OuterLoop::getType).toList()); } @Test