diff --git a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java index ab366762c5..6db08bde9d 100644 --- a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java +++ b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowProvider.java @@ -28,17 +28,12 @@ import com.powsybl.openloadflow.dc.DcLoadFlowResult; import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; import com.powsybl.openloadflow.graph.GraphConnectivityFactory; -import com.powsybl.openloadflow.network.LfBranch; -import com.powsybl.openloadflow.network.LfBus; -import com.powsybl.openloadflow.network.LfNetwork; -import com.powsybl.openloadflow.network.LfNetworkStateUpdateParameters; +import com.powsybl.openloadflow.network.*; import com.powsybl.openloadflow.network.impl.LfNetworkLoaderImpl; import com.powsybl.openloadflow.network.impl.Networks; import com.powsybl.openloadflow.network.util.ZeroImpedanceFlows; import com.powsybl.openloadflow.util.*; import com.powsybl.tools.PowsyblCoreVersion; -import org.jgrapht.Graph; -import org.jgrapht.alg.interfaces.SpanningTreeAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -161,9 +156,10 @@ private LoadFlowResult runAc(Network network, LoadFlowParameters parameters, Ope } private void computeZeroImpedanceFlows(LfNetwork network, boolean dc) { - Graph zeroImpedanceSubGraph = network.getZeroImpedanceNetwork(dc).getSubGraph(); - SpanningTreeAlgorithm.SpanningTree spanningTree = network.getZeroImpedanceNetwork(dc).getSpanningTree(); - new ZeroImpedanceFlows(zeroImpedanceSubGraph, spanningTree, dc).compute(); + for (LfZeroImpedanceNetwork zeroImpedanceNetwork : network.getZeroImpedanceNetworks(dc)) { + new ZeroImpedanceFlows(zeroImpedanceNetwork.getGraph(), zeroImpedanceNetwork.getSpanningTree(), dc) + .compute(); + } } private LoadFlowResult runDc(Network network, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt, Reporter reporter) { diff --git a/src/main/java/com/powsybl/openloadflow/ac/AcTargetVector.java b/src/main/java/com/powsybl/openloadflow/ac/AcTargetVector.java index 7e94a5c16a..f6c07769f4 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/AcTargetVector.java +++ b/src/main/java/com/powsybl/openloadflow/ac/AcTargetVector.java @@ -15,7 +15,6 @@ import com.powsybl.openloadflow.network.*; import java.util.Objects; -import java.util.Optional; /** * @author Geoffroy Jamgotchian @@ -24,11 +23,9 @@ public class AcTargetVector extends TargetVector private static double getBusTargetV(LfBus bus) { Objects.requireNonNull(bus); - double targetV = bus.getShuntVoltageControl().filter(dvc -> bus.isShuntVoltageControlled()) - .map(ShuntVoltageControl::getTargetValue) - .orElse(bus.getTransformerVoltageControl().filter(dvc -> bus.isTransformerVoltageControlled()) - .map(TransformerVoltageControl::getTargetValue) - .orElse(getVoltageControlledTargetValue(bus).orElse(Double.NaN))); + double targetV = bus.getHighestPriorityVoltageControl() + .map(Control::getTargetValue) + .orElseThrow(() -> new IllegalStateException("No active voltage control has been found for bus '" + bus.getId() + "'")); if (bus.hasGeneratorsWithSlope()) { // take first generator with slope: network loading ensures that there's only one generator with slope double slope = bus.getGeneratorsControllingVoltageWithSlope().get(0).getSlope(); @@ -37,19 +34,10 @@ private static double getBusTargetV(LfBus bus) { return targetV; } - private static Optional getVoltageControlledTargetValue(LfBus bus) { - return bus.getGeneratorVoltageControl().filter(vc -> bus.isGeneratorVoltageControlled()).map(vc -> { - if (vc.getControllerElements().stream().noneMatch(LfBus::isGeneratorVoltageControlEnabled)) { - throw new IllegalStateException("None of the controller buses of bus '" + bus.getId() + "'has voltage control on"); - } - return vc.getTargetValue(); - }); - } - private static double getReactivePowerDistributionTarget(LfNetwork network, int busNum) { LfBus controllerBus = network.getBus(busNum); double target = (controllerBus.getRemoteVoltageControlReactivePercent() - 1) * controllerBus.getTargetQ(); - for (LfBus otherControllerBus : controllerBus.getGeneratorVoltageControl().orElseThrow().getControllerElements()) { + for (LfBus otherControllerBus : controllerBus.getGeneratorVoltageControl().orElseThrow().getMergedControllerElements()) { if (otherControllerBus != controllerBus) { target += controllerBus.getRemoteVoltageControlReactivePercent() * otherControllerBus.getTargetQ(); } diff --git a/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemCreator.java b/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemCreator.java index 27d9aa4bdc..c04748e7cb 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemCreator.java +++ b/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemCreator.java @@ -58,13 +58,14 @@ private void createBusEquation(LfBus bus, bus.setCalculatedV(vTerm); createShuntEquations(bus, equationSystem); + } - // voltage control equations - createGeneratorVoltageControlEquations(bus, equationSystem); - - createTransformerVoltageControlEquations(bus, equationSystem); - - createShuntVoltageControlEquations(bus, equationSystem); + private void createVoltageControlEquations(EquationSystem equationSystem) { + for (LfBus bus : network.getBuses()) { + createGeneratorVoltageControlEquations(bus, equationSystem); + createTransformerVoltageControlEquations(bus, equationSystem); + createShuntVoltageControlEquations(bus, equationSystem); + } } private void createBusesEquations(EquationSystem equationSystem) { @@ -76,10 +77,11 @@ private void createBusesEquations(EquationSystem private void createGeneratorVoltageControlEquations(LfBus bus, EquationSystem equationSystem) { bus.getGeneratorVoltageControl() + .filter(voltageControl -> voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) .ifPresent(voltageControl -> { if (bus.isGeneratorVoltageControlled()) { if (voltageControl.isLocalControl()) { - createLocalVoltageControlEquation(bus, equationSystem); + createGeneratorLocalVoltageControlEquation(bus, equationSystem); } else { createGeneratorRemoteVoltageControlEquations(voltageControl, equationSystem); } @@ -88,8 +90,8 @@ private void createGeneratorVoltageControlEquations(LfBus bus, }); } - private void createLocalVoltageControlEquation(LfBus bus, - EquationSystem equationSystem) { + private void createGeneratorLocalVoltageControlEquation(LfBus bus, + EquationSystem equationSystem) { if (bus.hasGeneratorsWithSlope()) { // take first generator with slope: network loading ensures that there's only one generator with slope double slope = bus.getGeneratorsControllingVoltageWithSlope().get(0).getSlope(); @@ -98,7 +100,7 @@ private void createLocalVoltageControlEquation(LfBus bus, // equation is: V + slope * qSVC = targetV // which is modeled here with: V + slope * (sum_branch qBranch) = TargetV - slope * qLoads + slope * qGenerators equationSystem.getEquation(bus.getNum(), AcEquationType.BUS_TARGET_V).orElseThrow() - .addTerms(createReactiveTerms(bus, equationSystem.getVariableSet()) + .addTerms(createReactiveTerms(bus, equationSystem.getVariableSet(), creationParameters) .stream() .map(term -> term.multiply(slope)) .collect(Collectors.toList())); @@ -149,25 +151,22 @@ private static void createShuntEquations(LfBus bus, EquationSystem createShuntEquation(shunt, bus, equationSystem, false)); } - private void createGeneratorRemoteVoltageControlEquations(GeneratorVoltageControl voltageControl, - EquationSystem equationSystem) { - for (LfBus controllerBus : voltageControl.getControllerElements()) { - equationSystem.createEquation(controllerBus, AcEquationType.BUS_TARGET_Q); - - // create reactive power distribution equations at voltage controller buses - + private static void createReactivePowerDistributionEquations(GeneratorVoltageControl voltageControl, EquationSystem equationSystem, + AcEquationSystemCreationParameters creationParameters) { + List controllerBuses = voltageControl.getMergedControllerElements(); + for (LfBus controllerBus : controllerBuses) { // reactive power at controller bus i (supposing this voltage control is enabled) // q_i = qPercent_i * sum_j(q_j) where j are all the voltage controller buses // 0 = qPercent_i * sum_j(q_j) - q_i // which can be rewritten in a more simple way // 0 = (qPercent_i - 1) * q_i + qPercent_i * sum_j(q_j) where j are all the voltage controller buses except i Equation zero = equationSystem.createEquation(controllerBus, AcEquationType.DISTR_Q) - .addTerms(createReactiveTerms(controllerBus, equationSystem.getVariableSet()).stream() + .addTerms(createReactiveTerms(controllerBus, equationSystem.getVariableSet(), creationParameters).stream() .map(term -> term.multiply(() -> controllerBus.getRemoteVoltageControlReactivePercent() - 1)) .collect(Collectors.toList())); - for (LfBus otherControllerBus : voltageControl.getControllerElements()) { + for (LfBus otherControllerBus : controllerBuses) { if (otherControllerBus != controllerBus) { - zero.addTerms(createReactiveTerms(otherControllerBus, equationSystem.getVariableSet()).stream() + zero.addTerms(createReactiveTerms(otherControllerBus, equationSystem.getVariableSet(), creationParameters).stream() .map(term -> term.multiply(controllerBus::getRemoteVoltageControlReactivePercent)) .collect(Collectors.toList())); } @@ -175,12 +174,36 @@ private void createGeneratorRemoteVoltageControlEquations(GeneratorVoltageContro } } + public static void recreateReactivePowerDistributionEquations(GeneratorVoltageControl voltageControl, + EquationSystem equationSystem, + AcEquationSystemCreationParameters parameters) { + for (LfBus controllerBus : voltageControl.getMergedControllerElements()) { + equationSystem.removeEquation(controllerBus.getNum(), AcEquationType.DISTR_Q); + } + if (!voltageControl.isLocalControl()) { + createReactivePowerDistributionEquations(voltageControl, equationSystem, parameters); + } + updateGeneratorVoltageControl(voltageControl, equationSystem); + } + + private void createGeneratorRemoteVoltageControlEquations(GeneratorVoltageControl voltageControl, + EquationSystem equationSystem) { + for (LfBus controllerBus : voltageControl.getMergedControllerElements()) { + equationSystem.createEquation(controllerBus, AcEquationType.BUS_TARGET_Q); + } + + // create reactive power distribution equations at voltage controller buses + createReactivePowerDistributionEquations(voltageControl, equationSystem, creationParameters); + } + static void updateRemoteVoltageControlEquations(VoltageControl voltageControl, EquationSystem equationSystem, AcEquationType distrEqType, AcEquationType ctrlEqType) { + checkNotDependentVoltageControl(voltageControl); + LfBus controlledBus = voltageControl.getControlledBus(); - List controllerElements = voltageControl.getControllerElements() + List controllerElements = voltageControl.getMergedControllerElements() .stream() .filter(b -> !b.isDisabled()) // discard disabled controller elements .collect(Collectors.toList()); @@ -188,8 +211,12 @@ static void updateRemoteVoltageControlEquations(VoltageCon Equation vEq = equationSystem.getEquation(controlledBus.getNum(), AcEquationType.BUS_TARGET_V) .orElseThrow(); - if (controlledBus.isDisabled()) { - // if controlled bus is disabled, we disable all voltage control equations + List> vEqMergedList = voltageControl.getMergedDependentVoltageControls().stream() + .map(vc -> equationSystem.getEquation(vc.getControlledBus().getNum(), AcEquationType.BUS_TARGET_V).orElseThrow()) + .collect(Collectors.toList()); + + if (voltageControl.isDisabled()) { + // we disable all voltage control equations vEq.setActive(false); for (T controllerElement : controllerElements) { equationSystem.getEquation(controllerElement.getNum(), distrEqType) @@ -197,37 +224,53 @@ static void updateRemoteVoltageControlEquations(VoltageCon .setActive(false); equationSystem.getEquation(controllerElement.getNum(), ctrlEqType) .orElseThrow() - .setActive(true); + .setActive(false); } } else { - List enabledControllerElements = controllerElements.stream() - .filter(voltageControl::isControllerEnabled).collect(Collectors.toList()); - List disabledControllerElements = controllerElements.stream() - .filter(Predicate.not(voltageControl::isControllerEnabled)).collect(Collectors.toList()); + if (voltageControl.isHidden()) { + for (T controllerElement : controllerElements) { + equationSystem.getEquation(controllerElement.getNum(), distrEqType) + .orElseThrow() + .setActive(false); + equationSystem.getEquation(controllerElement.getNum(), ctrlEqType) + .orElseThrow() + .setActive(true); + } + } else { + List enabledControllerElements = controllerElements.stream() + .filter(voltageControl::isControllerEnabled).collect(Collectors.toList()); + List disabledControllerElements = controllerElements.stream() + .filter(Predicate.not(voltageControl::isControllerEnabled)).collect(Collectors.toList()); - // activate voltage control at controlled bus only if at least one controller element is enabled - vEq.setActive(!enabledControllerElements.isEmpty()); + // activate voltage control at controlled bus only if at least one controller element is enabled + vEq.setActive(!enabledControllerElements.isEmpty()); - // deactivate distribution equations and reactivate control equations - for (T controllerElement : disabledControllerElements) { - equationSystem.getEquation(controllerElement.getNum(), distrEqType) - .orElseThrow() - .setActive(false); - equationSystem.getEquation(controllerElement.getNum(), ctrlEqType) - .orElseThrow() - .setActive(true); - } + // deactivate voltage control for merged controlled buses + for (var vEqMerged : vEqMergedList) { + vEqMerged.setActive(false); + } - // activate distribution equation and deactivate control equation at all enabled controller buses except one (first) - for (int i = 0; i < enabledControllerElements.size(); i++) { - boolean active = i != 0; - T controllerElement = enabledControllerElements.get(i); - equationSystem.getEquation(controllerElement.getNum(), distrEqType) - .orElseThrow() - .setActive(active); - equationSystem.getEquation(controllerElement.getNum(), ctrlEqType) - .orElseThrow() - .setActive(false); + // deactivate distribution equations and reactivate control equations + for (T controllerElement : disabledControllerElements) { + equationSystem.getEquation(controllerElement.getNum(), distrEqType) + .orElseThrow() + .setActive(false); + equationSystem.getEquation(controllerElement.getNum(), ctrlEqType) + .orElseThrow() + .setActive(true); + } + + // activate distribution equation and deactivate control equation at all enabled controller buses except one (first) + for (int i = 0; i < enabledControllerElements.size(); i++) { + boolean active = i != 0; + T controllerElement = enabledControllerElements.get(i); + equationSystem.getEquation(controllerElement.getNum(), distrEqType) + .orElseThrow() + .setActive(active); + equationSystem.getEquation(controllerElement.getNum(), ctrlEqType) + .orElseThrow() + .setActive(false); + } } } } @@ -239,8 +282,9 @@ static void updateRemoteGeneratorVoltageControlEquations(GeneratorVoltageControl updateRemoteVoltageControlEquations(voltageControl, equationSystem, AcEquationType.DISTR_Q, AcEquationType.BUS_TARGET_Q); } - private List> createReactiveTerms(LfBus controllerBus, - VariableSet variableSet) { + private static List> createReactiveTerms(LfBus controllerBus, + VariableSet variableSet, + AcEquationSystemCreationParameters creationParameters) { List> terms = new ArrayList<>(); for (LfBranch branch : controllerBus.getBranches()) { EquationTerm q; @@ -255,7 +299,7 @@ private List> createReactiveTerms(L .minus(); } } else { - boolean deriveA1 = isDeriveA1(branch); + boolean deriveA1 = isDeriveA1(branch, creationParameters); boolean deriveR1 = isDeriveR1(branch); if (branch.getBus1() == controllerBus) { LfBus otherSideBus = branch.getBus2(); @@ -281,88 +325,111 @@ private List> createReactiveTerms(L } public static void updateGeneratorVoltageControl(GeneratorVoltageControl voltageControl, EquationSystem equationSystem) { + checkNotDependentVoltageControl(voltageControl); LfBus controlledBus = voltageControl.getControlledBus(); if (voltageControl.isLocalControl()) { - equationSystem.getEquation(controlledBus.getNum(), AcEquationType.BUS_TARGET_V) - .orElseThrow() - .setActive(!controlledBus.isDisabled() && controlledBus.isGeneratorVoltageControlEnabled()); - equationSystem.getEquation(controlledBus.getNum(), AcEquationType.BUS_TARGET_Q) - .orElseThrow() - .setActive(!controlledBus.isDisabled() && !controlledBus.isGeneratorVoltageControlEnabled()); + if (voltageControl.isHidden()) { + equationSystem.getEquation(controlledBus.getNum(), AcEquationType.BUS_TARGET_V) + .orElseThrow() + .setActive(false); + equationSystem.getEquation(controlledBus.getNum(), AcEquationType.BUS_TARGET_Q) + .orElseThrow() + .setActive(false); + } else { + equationSystem.getEquation(controlledBus.getNum(), AcEquationType.BUS_TARGET_V) + .orElseThrow() + .setActive(controlledBus.isGeneratorVoltageControlEnabled()); + equationSystem.getEquation(controlledBus.getNum(), AcEquationType.BUS_TARGET_Q) + .orElseThrow() + .setActive(!controlledBus.isGeneratorVoltageControlEnabled()); + } } else { updateRemoteGeneratorVoltageControlEquations(voltageControl, equationSystem); } } + private static void checkNotDependentVoltageControl(VoltageControl voltageControl) { + if (voltageControl.getMergeStatus() == VoltageControl.MergeStatus.DEPENDENT) { + throw new IllegalArgumentException("Cannot update a merged dependent voltage control"); + } + } + private static void createNonImpedantBranch(LfBranch branch, LfBus bus1, LfBus bus2, - EquationSystem equationSystem) { - Optional> v1 = equationSystem.getEquation(bus1.getNum(), AcEquationType.BUS_TARGET_V); - Optional> v2 = equationSystem.getEquation(bus2.getNum(), AcEquationType.BUS_TARGET_V); - boolean hasV1 = v1.isPresent() && v1.get().isActive(); // may be inactive if the equation has been created for sensitivity - boolean hasV2 = v2.isPresent() && v2.get().isActive(); // may be inactive if the equation has been created for sensitivity - if (!(hasV1 && hasV2)) { - // create voltage magnitude coupling equation - // 0 = v1 - v2 * rho - PiModel piModel = branch.getPiModel(); - double rho = PiModel.R2 / piModel.getR1(); - EquationTerm vTerm = equationSystem.getVariable(bus1.getNum(), AcVariableType.BUS_V) - .createTerm(); - EquationTerm bus2vTerm = equationSystem.getVariable(bus2.getNum(), AcVariableType.BUS_V) - .createTerm(); - equationSystem.createEquation(branch, AcEquationType.ZERO_V) - .addTerm(vTerm) - .addTerm(bus2vTerm.multiply(-rho)); - // add a dummy reactive power variable to both sides of the non impedant branch and with an opposite sign - // to ensure we have the same number of equation and variables - var dummyQ = equationSystem.getVariable(branch.getNum(), AcVariableType.DUMMY_Q); - equationSystem.getEquation(bus1.getNum(), AcEquationType.BUS_TARGET_Q) - .orElseThrow() - .addTerm(dummyQ.createTerm()); + EquationSystem equationSystem, + boolean spanningTreeEdge) { + if (bus1 != null && bus2 != null) { + Optional> v1 = equationSystem.getEquation(bus1.getNum(), AcEquationType.BUS_TARGET_V); + Optional> v2 = equationSystem.getEquation(bus2.getNum(), AcEquationType.BUS_TARGET_V); + boolean hasV1 = v1.isPresent() && v1.get().isActive(); // may be inactive if the equation has been created for sensitivity + boolean hasV2 = v2.isPresent() && v2.get().isActive(); // may be inactive if the equation has been created for sensitivity + boolean enabled = !branch.isDisabled() && spanningTreeEdge; + if (!(hasV1 && hasV2)) { + // create voltage magnitude coupling equation + // 0 = v1 - v2 * rho + PiModel piModel = branch.getPiModel(); + double rho = PiModel.R2 / piModel.getR1(); + EquationTerm vTerm = equationSystem.getVariable(bus1.getNum(), AcVariableType.BUS_V) + .createTerm(); + EquationTerm bus2vTerm = equationSystem.getVariable(bus2.getNum(), AcVariableType.BUS_V) + .createTerm(); + equationSystem.createEquation(branch, AcEquationType.ZERO_V) + .addTerm(vTerm) + .addTerm(bus2vTerm.multiply(-rho)) + .setActive(enabled); + + // add a dummy reactive power variable to both sides of the non impedant branch and with an opposite sign + // to ensure we have the same number of equation and variables + var dummyQ = equationSystem.getVariable(branch.getNum(), AcVariableType.DUMMY_Q); + equationSystem.getEquation(bus1.getNum(), AcEquationType.BUS_TARGET_Q) + .orElseThrow() + .addTerm(dummyQ.createTerm()); - equationSystem.getEquation(bus2.getNum(), AcEquationType.BUS_TARGET_Q) - .orElseThrow() - .addTerm(dummyQ.createTerm() - .minus()); - - // create an inactive dummy reactive power target equation set to zero that could be activated - // on case of switch opening - equationSystem.createEquation(branch, AcEquationType.DUMMY_TARGET_Q) - .addTerm(dummyQ.createTerm()) - .setActive(branch.isDisabled()); // inverted logic - } else { - // nothing to do in case of v1 and v2 are found, we just have to ensure - // target v are equals. - } - - boolean hasPhi1 = equationSystem.hasEquation(bus1.getNum(), AcEquationType.BUS_TARGET_PHI); - boolean hasPhi2 = equationSystem.hasEquation(bus2.getNum(), AcEquationType.BUS_TARGET_PHI); - if (!(hasPhi1 && hasPhi2)) { - // create voltage angle coupling equation - // alpha = phi1 - phi2 - equationSystem.createEquation(branch, AcEquationType.ZERO_PHI) - .addTerm(equationSystem.getVariable(bus1.getNum(), AcVariableType.BUS_PHI).createTerm()) - .addTerm(equationSystem.getVariable(bus2.getNum(), AcVariableType.BUS_PHI).createTerm() - .minus()); - - // add a dummy active power variable to both sides of the non impedant branch and with an opposite sign - // to ensure we have the same number of equation and variables - var dummyP = equationSystem.getVariable(branch.getNum(), AcVariableType.DUMMY_P); - equationSystem.getEquation(bus1.getNum(), AcEquationType.BUS_TARGET_P) - .orElseThrow() - .addTerm(dummyP.createTerm()); + equationSystem.getEquation(bus2.getNum(), AcEquationType.BUS_TARGET_Q) + .orElseThrow() + .addTerm(dummyQ.createTerm() + .minus()); + + // create an inactive dummy reactive power target equation set to zero that could be activated + // on case of switch opening + equationSystem.createEquation(branch, AcEquationType.DUMMY_TARGET_Q) + .addTerm(dummyQ.createTerm()) + .setActive(!enabled); // inverted logic + } else { + // nothing to do in case of v1 and v2 are found, we just have to ensure + // target v are equals. + } - equationSystem.getEquation(bus2.getNum(), AcEquationType.BUS_TARGET_P) - .orElseThrow() - .addTerm(dummyP.createTerm() - .minus()); - - // create an inactive dummy active power target equation set to zero that could be activated - // on case of switch opening - equationSystem.createEquation(branch, AcEquationType.DUMMY_TARGET_P) - .addTerm(dummyP.createTerm()) - .setActive(branch.isDisabled()); // inverted logic - } else { - throw new IllegalStateException("Cannot happen because only there is one slack bus per model"); + boolean hasPhi1 = equationSystem.hasEquation(bus1.getNum(), AcEquationType.BUS_TARGET_PHI); + boolean hasPhi2 = equationSystem.hasEquation(bus2.getNum(), AcEquationType.BUS_TARGET_PHI); + if (!(hasPhi1 && hasPhi2)) { + // create voltage angle coupling equation + // alpha = phi1 - phi2 + equationSystem.createEquation(branch, AcEquationType.ZERO_PHI) + .addTerm(equationSystem.getVariable(bus1.getNum(), AcVariableType.BUS_PHI).createTerm()) + .addTerm(equationSystem.getVariable(bus2.getNum(), AcVariableType.BUS_PHI).createTerm() + .minus()) + .setActive(enabled); + + // add a dummy active power variable to both sides of the non impedant branch and with an opposite sign + // to ensure we have the same number of equation and variables + var dummyP = equationSystem.getVariable(branch.getNum(), AcVariableType.DUMMY_P); + equationSystem.getEquation(bus1.getNum(), AcEquationType.BUS_TARGET_P) + .orElseThrow() + .addTerm(dummyP.createTerm()); + + equationSystem.getEquation(bus2.getNum(), AcEquationType.BUS_TARGET_P) + .orElseThrow() + .addTerm(dummyP.createTerm() + .minus()); + + // create an inactive dummy active power target equation set to zero that could be activated + // on case of switch opening + equationSystem.createEquation(branch, AcEquationType.DUMMY_TARGET_P) + .addTerm(dummyP.createTerm()) + .setActive(!enabled); // inverted logic + } else { + throw new IllegalStateException("Cannot happen because only there is one slack bus per model"); + } } } @@ -418,12 +485,13 @@ public static void updateTransformerPhaseControlEquations(TransformerPhaseContro private static void createTransformerVoltageControlEquations(LfBus bus, EquationSystem equationSystem) { bus.getTransformerVoltageControl() + .filter(voltageControl -> voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) .ifPresent(voltageControl -> { // add transformer ratio distribution equations - createR1DistributionEquations(voltageControl.getControllerElements(), equationSystem); + createR1DistributionEquations(voltageControl, equationSystem); // we also create an equation per controller that will be used later to maintain R1 variable constant - for (LfBranch controllerBranch : voltageControl.getControllerElements()) { + for (LfBranch controllerBranch : voltageControl.getMergedControllerElements()) { equationSystem.createEquation(controllerBranch, AcEquationType.BRANCH_TARGET_RHO1) .addTerm(equationSystem.getVariable(controllerBranch.getNum(), AcVariableType.BRANCH_RHO1).createTerm()); } @@ -432,8 +500,9 @@ private static void createTransformerVoltageControlEquations(LfBus bus, Equation }); } - public static void createR1DistributionEquations(List controllerBranches, + public static void createR1DistributionEquations(TransformerVoltageControl voltageControl, EquationSystem equationSystem) { + var controllerBranches = voltageControl.getMergedControllerElements(); for (int i = 0; i < controllerBranches.size(); i++) { LfBranch controllerBranch = controllerBranches.get(i); // r1 at controller branch i @@ -459,13 +528,23 @@ static void updateTransformerVoltageControlEquations(TransformerVoltageControl v updateRemoteVoltageControlEquations(voltageControl, equationSystem, AcEquationType.DISTR_RHO, AcEquationType.BRANCH_TARGET_RHO1); } + public static void recreateR1DistributionEquations(TransformerVoltageControl voltageControl, + EquationSystem equationSystem) { + for (LfBranch controllerBranch : voltageControl.getMergedControllerElements()) { + equationSystem.removeEquation(controllerBranch.getNum(), AcEquationType.DISTR_RHO); + } + createR1DistributionEquations(voltageControl, equationSystem); + updateTransformerVoltageControlEquations(voltageControl, equationSystem); + } + private static void createShuntVoltageControlEquations(LfBus bus, EquationSystem equationSystem) { bus.getShuntVoltageControl() + .filter(voltageControl -> voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) .ifPresent(voltageControl -> { // add shunt distribution equations - createShuntSusceptanceDistributionEquations(voltageControl.getControllerElements(), equationSystem); + createShuntSusceptanceDistributionEquations(voltageControl, equationSystem); - for (LfShunt controllerShunt : voltageControl.getControllerElements()) { + for (LfShunt controllerShunt : voltageControl.getMergedControllerElements()) { // we also create an equation that will be used later to maintain B variable constant // this equation is now inactive equationSystem.createEquation(controllerShunt, AcEquationType.SHUNT_TARGET_B) @@ -476,8 +555,9 @@ private static void createShuntVoltageControlEquations(LfBus bus, EquationSystem }); } - public static void createShuntSusceptanceDistributionEquations(List controllerShunts, + public static void createShuntSusceptanceDistributionEquations(ShuntVoltageControl voltageControl, EquationSystem equationSystem) { + var controllerShunts = voltageControl.getMergedControllerElements(); for (LfShunt controllerShunt : controllerShunts) { // shunt b at controller bus i // b_i = sum_j(b_j) / controller_count where j are all the controller buses @@ -502,7 +582,16 @@ static void updateShuntVoltageControlEquations(ShuntVoltageControl voltageContro updateRemoteVoltageControlEquations(voltageControl, equationSystem, AcEquationType.DISTR_SHUNT_B, AcEquationType.SHUNT_TARGET_B); } - private boolean isDeriveA1(LfBranch branch) { + public static void recreateShuntSusceptanceDistributionEquations(ShuntVoltageControl voltageControl, + EquationSystem equationSystem) { + for (LfShunt controllerShunt : voltageControl.getMergedControllerElements()) { + equationSystem.removeEquation(controllerShunt.getNum(), AcEquationType.DISTR_SHUNT_B); + } + createShuntSusceptanceDistributionEquations(voltageControl, equationSystem); + updateShuntVoltageControlEquations(voltageControl, equationSystem); + } + + private static boolean isDeriveA1(LfBranch branch, AcEquationSystemCreationParameters creationParameters) { return branch.isPhaseController() || (creationParameters.isForceA1Var() && branch.hasPhaseControllerCapability() && branch.isConnectedAtBothSides()); } @@ -519,7 +608,7 @@ private void createImpedantBranch(LfBranch branch, LfBus bus1, LfBus bus2, EquationTerm q2 = null; EquationTerm i1 = null; EquationTerm i2 = null; - boolean deriveA1 = isDeriveA1(branch); + boolean deriveA1 = isDeriveA1(branch, creationParameters); boolean deriveR1 = isDeriveR1(branch); if (bus1 != null && bus2 != null) { p1 = new ClosedBranchSide1ActiveFlowEquationTerm(branch, bus1, bus2, equationSystem.getVariableSet(), deriveA1, deriveR1); @@ -606,9 +695,7 @@ private void createBranchEquations(LfBranch branch, EquationSystem equationSystem) { // create zero and non zero impedance branch equations if (branch.isZeroImpedance(false)) { - if (branch.isSpanningTreeEdge(false)) { - createNonImpedantBranch(branch, branch.getBus1(), branch.getBus2(), equationSystem); - } + createNonImpedantBranch(branch, branch.getBus1(), branch.getBus2(), equationSystem, branch.isSpanningTreeEdge(false)); } else { createImpedantBranch(branch, branch.getBus1(), branch.getBus2(), equationSystem); } @@ -633,7 +720,7 @@ private List> createActiveInjection terms.add(p); } } else { - boolean deriveA1 = isDeriveA1(branch); + boolean deriveA1 = isDeriveA1(branch, creationParameters); boolean deriveR1 = isDeriveR1(branch); if (branch.getBus1() == bus) { LfBus otherSideBus = branch.getBus2(); @@ -681,9 +768,11 @@ public EquationSystem create() { createHvdcAcEmulationEquations(hvdc, equationSystem); } + createVoltageControlEquations(equationSystem); + EquationSystemPostProcessor.findAll().forEach(pp -> pp.onCreate(equationSystem)); - network.addListener(LfNetworkListenerTracer.trace(new AcEquationSystemUpdater(equationSystem))); + network.addListener(LfNetworkListenerTracer.trace(new AcEquationSystemUpdater(equationSystem, creationParameters))); return equationSystem; } diff --git a/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java b/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java index f2dddd6d1b..f54cc86938 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java +++ b/src/main/java/com/powsybl/openloadflow/ac/equations/AcEquationSystemUpdater.java @@ -10,18 +10,31 @@ import com.powsybl.openloadflow.lf.AbstractEquationSystemUpdater; import com.powsybl.openloadflow.network.*; +import java.util.List; +import java.util.Objects; + /** * @author Geoffroy Jamgotchian */ public class AcEquationSystemUpdater extends AbstractEquationSystemUpdater { - public AcEquationSystemUpdater(EquationSystem equationSystem) { + private final AcEquationSystemCreationParameters parameters; + + public AcEquationSystemUpdater(EquationSystem equationSystem, + AcEquationSystemCreationParameters parameters) { super(equationSystem, false); + this.parameters = Objects.requireNonNull(parameters); + } + + private void updateVoltageControls(LfBus bus) { + bus.getGeneratorVoltageControl().ifPresent(voltageControl -> AcEquationSystemCreator.updateGeneratorVoltageControl(voltageControl.getMainVoltageControl(), equationSystem)); + bus.getTransformerVoltageControl().ifPresent(voltageControl -> AcEquationSystemCreator.updateTransformerVoltageControlEquations(voltageControl.getMainVoltageControl(), equationSystem)); + bus.getShuntVoltageControl().ifPresent(voltageControl -> AcEquationSystemCreator.updateShuntVoltageControlEquations(voltageControl.getMainVoltageControl(), equationSystem)); } @Override public void onGeneratorVoltageControlChange(LfBus controllerBus, boolean newVoltageControllerEnabled) { - controllerBus.getGeneratorVoltageControl().ifPresent(voltageControl -> AcEquationSystemCreator.updateGeneratorVoltageControl(voltageControl, equationSystem)); + updateVoltageControls(controllerBus.getGeneratorVoltageControl().orElseThrow().getControlledBus()); controllerBus.getReactivePowerControl().ifPresent(reactivePowerControl -> AcEquationSystemCreator.updateReactivePowerControlBranchEquations(reactivePowerControl, equationSystem)); } @@ -32,12 +45,12 @@ public void onTransformerPhaseControlChange(LfBranch controllerBranch, boolean n @Override public void onTransformerVoltageControlChange(LfBranch controllerBranch, boolean newVoltageControllerEnabled) { - AcEquationSystemCreator.updateTransformerVoltageControlEquations(controllerBranch.getVoltageControl().orElseThrow(), equationSystem); + updateVoltageControls(controllerBranch.getVoltageControl().orElseThrow().getControlledBus()); } @Override public void onShuntVoltageControlChange(LfShunt controllerShunt, boolean newVoltageControllerEnabled) { - AcEquationSystemCreator.updateShuntVoltageControlEquations(controllerShunt.getVoltageControl().orElseThrow(), equationSystem); + updateVoltageControls(controllerShunt.getVoltageControl().orElseThrow().getControlledBus()); } @Override @@ -74,9 +87,7 @@ public void onDisableChange(LfElement element, boolean disabled) { equationSystem.getEquation(bus.getNum(), AcEquationType.BUS_TARGET_V) .orElseThrow() .setActive(false); - bus.getGeneratorVoltageControl().ifPresent(voltageControl -> AcEquationSystemCreator.updateGeneratorVoltageControl(voltageControl, equationSystem)); - bus.getTransformerVoltageControl().ifPresent(voltageControl -> AcEquationSystemCreator.updateTransformerVoltageControlEquations(voltageControl, equationSystem)); - bus.getShuntVoltageControl().ifPresent(voltageControl -> AcEquationSystemCreator.updateShuntVoltageControlEquations(voltageControl, equationSystem)); + updateVoltageControls(bus); bus.getReactivePowerControl().ifPresent(reactivePowerControl -> AcEquationSystemCreator.updateReactivePowerControlBranchEquations(reactivePowerControl, equationSystem)); break; case BRANCH: @@ -96,4 +107,41 @@ public void onDisableChange(LfElement element, boolean disabled) { throw new IllegalStateException("Unknown element type: " + element.getType()); } } + + private void recreateDistributionEquations(LfZeroImpedanceNetwork network) { + for (LfBus bus : network.getGraph().vertexSet()) { + bus.getGeneratorVoltageControl() + .filter(voltageControl -> voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) + .ifPresent(voltageControl -> AcEquationSystemCreator.recreateReactivePowerDistributionEquations(voltageControl, equationSystem, parameters)); + bus.getTransformerVoltageControl() + .filter(voltageControl -> voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) + .ifPresent(voltageControl -> AcEquationSystemCreator.recreateR1DistributionEquations(voltageControl, equationSystem)); + bus.getShuntVoltageControl() + .filter(voltageControl -> voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) + .ifPresent(voltageControl -> AcEquationSystemCreator.recreateShuntSusceptanceDistributionEquations(voltageControl, equationSystem)); + } + } + + @Override + public void onZeroImpedanceNetworkSplit(LfZeroImpedanceNetwork initialNetwork, List splitNetworks, boolean dc) { + if (!dc) { + // TODO + // only recreate distribution equations if controllers buses are redistributed on the different + // split networks (should be a rare case) and not only ate the end on only one of the split network + for (LfZeroImpedanceNetwork splitNetwork : splitNetworks) { + recreateDistributionEquations(splitNetwork); + } + } + } + + @Override + public void onZeroImpedanceNetworkMerge(LfZeroImpedanceNetwork network1, LfZeroImpedanceNetwork network2, LfZeroImpedanceNetwork mergedNetwork, boolean dc) { + if (!dc) { + // TODO + // only recreate distribution equations if controllers buses are merged (should be a rare case) + // so we have to check here that controllers were spread over network1 and network2 and were not + // already only on network1 or network2 + recreateDistributionEquations(mergedNetwork); + } + } } diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalShuntVoltageControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalShuntVoltageControlOuterLoop.java index 448ec74873..7195b89d23 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalShuntVoltageControlOuterLoop.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalShuntVoltageControlOuterLoop.java @@ -161,12 +161,14 @@ public OuterLoopStatus check(OuterLoopContext context, Reporter reporter) { .filter(LfBus::isShuntVoltageControlled) .forEach(controlledBus -> { ShuntVoltageControl voltageControl = controlledBus.getShuntVoltageControl().orElseThrow(); - double diffV = voltageControl.getTargetValue() - voltageControl.getControlledBus().getV(); - List sortedControllers = voltageControl.getControllerElements().stream() - .filter(shunt -> !shunt.isDisabled() && shunt.hasVoltageControlCapability()) - .sorted(Comparator.comparingDouble(LfShunt::getBMagnitude).reversed()) - .collect(Collectors.toList()); - adjustB(voltageControl, sortedControllers, controlledBus, contextData, sensitivityContext, diffV, status); + if (voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) { + double diffV = voltageControl.getTargetValue() - voltageControl.getControlledBus().getV(); + List sortedControllers = voltageControl.getMergedControllerElements().stream() + .filter(shunt -> !shunt.isDisabled() && shunt.hasVoltageControlCapability()) + .sorted(Comparator.comparingDouble(LfShunt::getBMagnitude).reversed()) + .collect(Collectors.toList()); + adjustB(voltageControl, sortedControllers, controlledBus, contextData, sensitivityContext, diffV, status); + } }); return status.getValue(); } diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalTransformerVoltageControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalTransformerVoltageControlOuterLoop.java index 9fb65412f4..cd58454f4c 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalTransformerVoltageControlOuterLoop.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/IncrementalTransformerVoltageControlOuterLoop.java @@ -208,22 +208,24 @@ public OuterLoopStatus check(OuterLoopContext context, Reporter reporter) { controlledBuses.forEach(controlledBus -> { TransformerVoltageControl voltageControl = controlledBus.getTransformerVoltageControl().orElseThrow(); - double diffV = getDiffV(voltageControl); - double halfTargetDeadband = getHalfTargetDeadband(voltageControl); - if (Math.abs(diffV) > halfTargetDeadband) { - controlledBusesOutsideOfDeadband.add(controlledBus.getId()); - List controllers = voltageControl.getControllerElements(); - LOGGER.trace("Controlled bus '{}' ({} controllers) is outside of its deadband (half is {} kV) and could need a voltage adjustment of {} kV", - controlledBus.getId(), controllers.size(), halfTargetDeadband * controlledBus.getNominalV(), diffV * controlledBus.getNominalV()); - boolean adjusted; - if (controllers.size() == 1) { - adjusted = adjustWithOneController(controllers.get(0), controlledBus, contextData, sensitivityContext, diffV, controlledBusesWithAllItsControllersToLimit); - } else { - adjusted = adjustWithSeveralControllers(controllers, controlledBus, contextData, sensitivityContext, diffV, halfTargetDeadband, controlledBusesWithAllItsControllersToLimit); - } - if (adjusted) { - controlledBusesAdjusted.add(controlledBus.getId()); - status.setValue(OuterLoopStatus.UNSTABLE); + if (voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) { + double diffV = getDiffV(voltageControl); + double halfTargetDeadband = getHalfTargetDeadband(voltageControl); + if (Math.abs(diffV) > halfTargetDeadband) { + controlledBusesOutsideOfDeadband.add(controlledBus.getId()); + List controllers = voltageControl.getMergedControllerElements(); + LOGGER.trace("Controlled bus '{}' ({} controllers) is outside of its deadband (half is {} kV) and could need a voltage adjustment of {} kV", + controlledBus.getId(), controllers.size(), halfTargetDeadband * controlledBus.getNominalV(), diffV * controlledBus.getNominalV()); + boolean adjusted; + if (controllers.size() == 1) { + adjusted = adjustWithOneController(controllers.get(0), controlledBus, contextData, sensitivityContext, diffV, controlledBusesWithAllItsControllersToLimit); + } else { + adjusted = adjustWithSeveralControllers(controllers, controlledBus, contextData, sensitivityContext, diffV, halfTargetDeadband, controlledBusesWithAllItsControllersToLimit); + } + if (adjusted) { + controlledBusesAdjusted.add(controlledBus.getId()); + status.setValue(OuterLoopStatus.UNSTABLE); + } } } }); diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/ReactiveLimitsOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/ReactiveLimitsOuterLoop.java index 970e24578b..0983a180a9 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/ReactiveLimitsOuterLoop.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/ReactiveLimitsOuterLoop.java @@ -10,8 +10,8 @@ import com.powsybl.openloadflow.ac.OuterLoop; import com.powsybl.openloadflow.ac.OuterLoopContext; import com.powsybl.openloadflow.ac.OuterLoopStatus; -import com.powsybl.openloadflow.network.LfBus; import com.powsybl.openloadflow.network.GeneratorVoltageControl; +import com.powsybl.openloadflow.network.LfBus; import com.powsybl.openloadflow.util.PerUnit; import com.powsybl.openloadflow.util.Reports; import org.apache.commons.lang3.mutable.MutableInt; diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/SecondaryVoltageControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/SecondaryVoltageControlOuterLoop.java index 53969c95a9..967b10d970 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/SecondaryVoltageControlOuterLoop.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/SecondaryVoltageControlOuterLoop.java @@ -47,8 +47,9 @@ private static boolean isValid(LfBus bus) { private static List getControllerBuses(LfBus controlledBus) { return controlledBus.getGeneratorVoltageControl() + .filter(voltageControl -> voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) .orElseThrow() - .getControllerElements() + .getMergedControllerElements() .stream().filter(SecondaryVoltageControlOuterLoop::isValid) .collect(Collectors.toList()); } diff --git a/src/main/java/com/powsybl/openloadflow/ac/outerloop/TransformerVoltageControlOuterLoop.java b/src/main/java/com/powsybl/openloadflow/ac/outerloop/TransformerVoltageControlOuterLoop.java index 8716615645..ae4fa11312 100644 --- a/src/main/java/com/powsybl/openloadflow/ac/outerloop/TransformerVoltageControlOuterLoop.java +++ b/src/main/java/com/powsybl/openloadflow/ac/outerloop/TransformerVoltageControlOuterLoop.java @@ -11,6 +11,7 @@ import com.powsybl.openloadflow.ac.OuterLoopStatus; import com.powsybl.openloadflow.network.LfBranch; import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.VoltageControl; import org.apache.commons.lang3.mutable.MutableObject; import java.util.ArrayList; @@ -79,14 +80,16 @@ public OuterLoopStatus check(OuterLoopContext context, Reporter reporter) { for (LfBus bus : context.getNetwork().getBuses()) { if (!bus.isDisabled() && bus.isGeneratorVoltageControlled() && bus.getNominalV() <= maxControlledNominalVoltage) { var voltageControl = bus.getGeneratorVoltageControl().orElseThrow(); - voltageControl.getControllerElements().forEach(controllerBus -> { - if (controllerBus.isGeneratorVoltageControlEnabled()) { - controllerBus.setGenerationTargetQ(controllerBus.getQ().eval()); - controllerBus.setGeneratorVoltageControlEnabled(false); - contextData.getBusesWithVoltageControlDisabled().add(controllerBus); - } - }); - status.setValue(OuterLoopStatus.UNSTABLE); + if (voltageControl.getMergeStatus() == VoltageControl.MergeStatus.MAIN) { + voltageControl.getMergedControllerElements().forEach(controllerBus -> { + if (controllerBus.isGeneratorVoltageControlEnabled()) { + controllerBus.setGenerationTargetQ(controllerBus.getQ().eval()); + controllerBus.setGeneratorVoltageControlEnabled(false); + contextData.getBusesWithVoltageControlDisabled().add(controllerBus); + } + }); + status.setValue(OuterLoopStatus.UNSTABLE); + } } } for (LfBranch branch : getControllerBranches(context.getNetwork())) { diff --git a/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemCreator.java b/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemCreator.java index 4ddc03a7e9..c17cc5dc53 100644 --- a/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemCreator.java +++ b/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemCreator.java @@ -44,35 +44,38 @@ private void createBuses(EquationSystem equation } public static void createNonImpedantBranch(EquationSystem equationSystem, - LfBranch branch, LfBus bus1, LfBus bus2) { - boolean hasPhi1 = equationSystem.hasEquation(bus1.getNum(), DcEquationType.BUS_TARGET_PHI); - boolean hasPhi2 = equationSystem.hasEquation(bus2.getNum(), DcEquationType.BUS_TARGET_PHI); - if (!(hasPhi1 && hasPhi2)) { - // create voltage angle coupling equation - // alpha = phi1 - phi2 - equationSystem.createEquation(branch, DcEquationType.ZERO_PHI) - .addTerm(equationSystem.getVariable(bus1.getNum(), DcVariableType.BUS_PHI).createTerm()) - .addTerm(equationSystem.getVariable(bus2.getNum(), DcVariableType.BUS_PHI).createTerm() - .minus()); - - // add a dummy active power variable to both sides of the non impedant branch and with an opposite sign - // to ensure we have the same number of equation and variables - var dummyP = equationSystem.getVariable(branch.getNum(), DcVariableType.DUMMY_P); - equationSystem.getEquation(bus1.getNum(), DcEquationType.BUS_TARGET_P) - .orElseThrow() - .addTerm(dummyP.createTerm()); - - equationSystem.getEquation(bus2.getNum(), DcEquationType.BUS_TARGET_P) - .orElseThrow() - .addTerm(dummyP.createTerm().minus()); - - // create an inactive dummy active power target equation set to zero that could be activated - // on case of switch opening - equationSystem.createEquation(branch, DcEquationType.DUMMY_TARGET_P) - .addTerm(dummyP.createTerm()) - .setActive(branch.isDisabled()); // inverted logic - } else { - throw new IllegalStateException("Cannot happen because only there is one slack bus per model"); + LfBranch branch, LfBus bus1, LfBus bus2, boolean spanningTree) { + if (bus1 != null && bus2 != null) { + boolean hasPhi1 = equationSystem.hasEquation(bus1.getNum(), DcEquationType.BUS_TARGET_PHI); + boolean hasPhi2 = equationSystem.hasEquation(bus2.getNum(), DcEquationType.BUS_TARGET_PHI); + if (!(hasPhi1 && hasPhi2)) { + // create voltage angle coupling equation + // alpha = phi1 - phi2 + equationSystem.createEquation(branch, DcEquationType.ZERO_PHI) + .addTerm(equationSystem.getVariable(bus1.getNum(), DcVariableType.BUS_PHI).createTerm()) + .addTerm(equationSystem.getVariable(bus2.getNum(), DcVariableType.BUS_PHI).createTerm() + .minus()) + .setActive(!branch.isDisabled() && spanningTree); + + // add a dummy active power variable to both sides of the non impedant branch and with an opposite sign + // to ensure we have the same number of equation and variables + var dummyP = equationSystem.getVariable(branch.getNum(), DcVariableType.DUMMY_P); + equationSystem.getEquation(bus1.getNum(), DcEquationType.BUS_TARGET_P) + .orElseThrow() + .addTerm(dummyP.createTerm()); + + equationSystem.getEquation(bus2.getNum(), DcEquationType.BUS_TARGET_P) + .orElseThrow() + .addTerm(dummyP.createTerm().minus()); + + // create an inactive dummy active power target equation set to zero that could be activated + // on case of switch opening + equationSystem.createEquation(branch, DcEquationType.DUMMY_TARGET_P) + .addTerm(dummyP.createTerm()) + .setActive(branch.isDisabled() || !spanningTree); // inverted logic + } else { + throw new IllegalStateException("Cannot happen because only there is one slack bus per model"); + } } } @@ -117,9 +120,7 @@ private void createBranches(EquationSystem equat LfBus bus1 = branch.getBus1(); LfBus bus2 = branch.getBus2(); if (branch.isZeroImpedance(true)) { - if (branch.isSpanningTreeEdge(true)) { - createNonImpedantBranch(equationSystem, branch, bus1, bus2); - } + createNonImpedantBranch(equationSystem, branch, bus1, bus2, branch.isSpanningTreeEdge(true)); } else { createImpedantBranch(equationSystem, creationParameters, branch, bus1, bus2); } diff --git a/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemUpdater.java b/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemUpdater.java index ea44144213..5d978447cd 100644 --- a/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemUpdater.java +++ b/src/main/java/com/powsybl/openloadflow/dc/equations/DcEquationSystemUpdater.java @@ -8,7 +8,9 @@ import com.powsybl.openloadflow.equations.EquationSystem; import com.powsybl.openloadflow.lf.AbstractEquationSystemUpdater; -import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.LfElement; /** * @author Anne Tilloy diff --git a/src/main/java/com/powsybl/openloadflow/lf/AbstractEquationSystemUpdater.java b/src/main/java/com/powsybl/openloadflow/lf/AbstractEquationSystemUpdater.java index 11357a29d7..f06b3967bb 100644 --- a/src/main/java/com/powsybl/openloadflow/lf/AbstractEquationSystemUpdater.java +++ b/src/main/java/com/powsybl/openloadflow/lf/AbstractEquationSystemUpdater.java @@ -37,9 +37,16 @@ protected static void checkSlackBus(LfBus bus, boolean disabled) { protected abstract void updateNonImpedantBranchEquations(LfBranch branch, boolean enable); + @Override + public void onZeroImpedanceNetworkSpanningTreeChange(LfBranch branch, boolean dc, boolean spanningTree) { + if (dc == this.dc) { + updateNonImpedantBranchEquations(branch, !branch.isDisabled() && spanningTree); + } + } + protected void updateElementEquations(LfElement element, boolean enable) { - if (element instanceof LfBranch && ((LfBranch) element).isZeroImpedance(dc) && ((LfBranch) element).isSpanningTreeEdge(dc)) { - updateNonImpedantBranchEquations((LfBranch) element, enable); + if (element instanceof LfBranch && ((LfBranch) element).isZeroImpedance(dc)) { + updateNonImpedantBranchEquations((LfBranch) element, enable && ((LfBranch) element).isSpanningTreeEdge(dc)); } else { // update all equations related to the element for (var equation : equationSystem.getEquations(element.getType(), element.getNum())) { diff --git a/src/main/java/com/powsybl/openloadflow/network/AbstractElement.java b/src/main/java/com/powsybl/openloadflow/network/AbstractElement.java index 7da6a81ec3..ec479f1319 100644 --- a/src/main/java/com/powsybl/openloadflow/network/AbstractElement.java +++ b/src/main/java/com/powsybl/openloadflow/network/AbstractElement.java @@ -44,9 +44,13 @@ public boolean isDisabled() { public void setDisabled(boolean disabled) { if (disabled != this.disabled) { this.disabled = disabled; - for (LfNetworkListener listener : network.getListeners()) { - listener.onDisableChange(this, disabled); - } + notifyDisable(); + } + } + + protected void notifyDisable() { + for (LfNetworkListener listener : network.getListeners()) { + listener.onDisableChange(this, disabled); } } diff --git a/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java b/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java index 97876be14d..a8a6e86a78 100644 --- a/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java +++ b/src/main/java/com/powsybl/openloadflow/network/AbstractLfNetworkListener.java @@ -6,6 +6,8 @@ */ package com.powsybl.openloadflow.network; +import java.util.List; + /** * @author Geoffroy Jamgotchian */ @@ -70,4 +72,19 @@ public void onTapPositionChange(LfBranch branch, int oldPosition, int newPositio public void onShuntSusceptanceChange(LfShunt shunt, double b) { // empty } + + @Override + public void onZeroImpedanceNetworkSpanningTreeChange(LfBranch branch, boolean dc, boolean spanningTree) { + // empty + } + + @Override + public void onZeroImpedanceNetworkSplit(LfZeroImpedanceNetwork initialNetwork, List splitNetworks, boolean dc) { + // empty + } + + @Override + public void onZeroImpedanceNetworkMerge(LfZeroImpedanceNetwork network1, LfZeroImpedanceNetwork network2, LfZeroImpedanceNetwork mergedNetwork, boolean dc) { + // empty + } } diff --git a/src/main/java/com/powsybl/openloadflow/network/DiscreteVoltageControl.java b/src/main/java/com/powsybl/openloadflow/network/DiscreteVoltageControl.java index 53a6c655d8..ea688e45f3 100644 --- a/src/main/java/com/powsybl/openloadflow/network/DiscreteVoltageControl.java +++ b/src/main/java/com/powsybl/openloadflow/network/DiscreteVoltageControl.java @@ -16,8 +16,8 @@ public class DiscreteVoltageControl extends VoltageControl< private Double targetDeadband; - protected DiscreteVoltageControl(LfBus controlled, double targetValue, Double targetDeadband) { - super(targetValue, controlled); + protected DiscreteVoltageControl(LfBus controlled, Type type, int priority, double targetValue, Double targetDeadband) { + super(targetValue, type, priority, controlled); this.targetDeadband = targetDeadband; } diff --git a/src/main/java/com/powsybl/openloadflow/network/GeneratorVoltageControl.java b/src/main/java/com/powsybl/openloadflow/network/GeneratorVoltageControl.java index b6c94a3a4f..b16bbfd174 100644 --- a/src/main/java/com/powsybl/openloadflow/network/GeneratorVoltageControl.java +++ b/src/main/java/com/powsybl/openloadflow/network/GeneratorVoltageControl.java @@ -20,8 +20,10 @@ public class GeneratorVoltageControl extends VoltageControl { private static final Logger LOGGER = LoggerFactory.getLogger(GeneratorVoltageControl.class); + private static final int PRIORITY = 0; + public GeneratorVoltageControl(LfBus controlledBus, double targetValue) { - super(targetValue, controlledBus); + super(targetValue, Type.GENERATOR, PRIORITY, controlledBus); } @Override @@ -48,6 +50,10 @@ public void addControllerElement(LfBus controllerBus) { * @return true if the voltage control is ONLY local, false otherwise */ public boolean isLocalControl() { + return isLocalControl(getMergedControllerElements()); + } + + private boolean isLocalControl(List controllerElements) { return controllerElements.size() == 1 && controllerElements.contains(controlledBus); } @@ -61,11 +67,15 @@ public boolean isSharedControl() { } public void updateReactiveKeys() { - double[] reactiveKeys = createReactiveKeys(controllerElements); + updateReactiveKeys(getMergedControllerElements()); + } + + public static void updateReactiveKeys(List controllerBuses) { + double[] reactiveKeys = createReactiveKeys(controllerBuses); // no reactive dispatch on PQ buses, so we set the key to 0 - for (int i = 0; i < controllerElements.size(); i++) { - LfBus controllerBus = controllerElements.get(i); + for (int i = 0; i < controllerBuses.size(); i++) { + LfBus controllerBus = controllerBuses.get(i); if (controllerBus.isDisabled() || !controllerBus.isGeneratorVoltageControlEnabled()) { reactiveKeys[i] = 0d; } @@ -73,8 +83,8 @@ public void updateReactiveKeys() { // update bus reactive keys double reactiveKeysSum = Arrays.stream(reactiveKeys).sum(); - for (int i = 0; i < controllerElements.size(); i++) { - LfBus controllerBus = controllerElements.get(i); + for (int i = 0; i < controllerBuses.size(); i++) { + LfBus controllerBus = controllerBuses.get(i); controllerBus.setRemoteVoltageControlReactivePercent(reactiveKeysSum == 0 ? 0 : reactiveKeys[i] / reactiveKeysSum); } } diff --git a/src/main/java/com/powsybl/openloadflow/network/LfBus.java b/src/main/java/com/powsybl/openloadflow/network/LfBus.java index c3b1c6f427..6831532572 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfBus.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfBus.java @@ -29,6 +29,22 @@ public interface LfBus extends LfElement { void setReference(boolean reference); + /** + * Get list of all voltage controls (generator + transformer + shunt) linked to this bus. + */ + List> getVoltageControls(); + + /** + * Check if this bus is voltage controlled so either by a generator, a transformer or a shunt. + */ + boolean isVoltageControlled(); + + /** + * Get the highest priority voltage control connected to a bus of the zero impedance subgraph to which this bus + * belong. + */ + Optional> getHighestPriorityVoltageControl(); + // generator voltage control boolean hasGeneratorVoltageControllerCapability(); @@ -37,8 +53,6 @@ public interface LfBus extends LfElement { void setGeneratorVoltageControl(GeneratorVoltageControl generatorVoltageControl); - void removeGeneratorVoltageControl(); - boolean isGeneratorVoltageControlled(); boolean isGeneratorVoltageControlEnabled(); @@ -179,4 +193,8 @@ default List createBusResults() { default Optional getCountry() { return Optional.empty(); } + + void setZeroImpedanceNetwork(boolean dc, LfZeroImpedanceNetwork zeroImpedanceNetwork); + + LfZeroImpedanceNetwork getZeroImpedanceNetwork(boolean dc); } diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java index c05c454634..e4fe7fbffe 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java @@ -15,10 +15,6 @@ import com.powsybl.openloadflow.util.PerUnit; import com.powsybl.openloadflow.util.Reports; import org.anarres.graphviz.builder.GraphVizGraph; -import org.jgrapht.Graph; -import org.jgrapht.alg.interfaces.SpanningTreeAlgorithm; -import org.jgrapht.alg.spanning.KruskalMinimumSpanningTree; -import org.jgrapht.graph.Pseudograph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +26,6 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; import java.util.stream.Collectors; import static com.powsybl.openloadflow.util.Markers.PERFORMANCE_MARKER; @@ -80,45 +75,9 @@ public class LfNetwork extends AbstractPropertyBag implements PropertyBag { private GraphConnectivity connectivity; - public static class LfZeroImpedanceNetwork { + private Set dcLfZeroImpedanceNetworks; - private final Graph subGraph; - - private final SpanningTreeAlgorithm.SpanningTree spanningTree; - - public LfZeroImpedanceNetwork(LfNetwork network, boolean dc) { - subGraph = createZeroImpedanceSubGraph(network, dc); - spanningTree = createZeroImpedanceSpanningTree(subGraph, dc); - } - - private static Graph createZeroImpedanceSubGraph(LfNetwork network, boolean dc) { - return network.createSubGraph(branch -> branch.isZeroImpedance(dc) - && branch.getBus1() != null && branch.getBus2() != null); - } - - private static SpanningTreeAlgorithm.SpanningTree createZeroImpedanceSpanningTree(Graph zeroImpedanceSubGraph, boolean dc) { - if (!zeroImpedanceSubGraph.vertexSet().isEmpty()) { - SpanningTreeAlgorithm.SpanningTree spanningTree = new KruskalMinimumSpanningTree<>(zeroImpedanceSubGraph).getSpanningTree(); - for (LfBranch branch : spanningTree.getEdges()) { - branch.setSpanningTreeEdge(dc, true); - } - return spanningTree; - } - return null; - } - - public Graph getSubGraph() { - return subGraph; - } - - public SpanningTreeAlgorithm.SpanningTree getSpanningTree() { - return spanningTree; - } - } - - private LfZeroImpedanceNetwork dcLfZeroImpedanceNetwork; - - private LfZeroImpedanceNetwork acLfZeroImpedanceNetwork; + private Set acLfZeroImpedanceNetworks; private Reporter reporter; private final List secondaryVoltageControls = new ArrayList<>(); @@ -177,8 +136,8 @@ public void updateSlackBuses() { } private void invalidateZeroImpedanceNetworks() { - dcLfZeroImpedanceNetwork = null; - acLfZeroImpedanceNetwork = null; + dcLfZeroImpedanceNetworks = null; + acLfZeroImpedanceNetworks = null; } public void addBranch(LfBranch branch) { @@ -586,36 +545,19 @@ public static List load(T network, LfNetworkLoader networkLoad public void updateZeroImpedanceCache(boolean dc) { if (dc) { - if (dcLfZeroImpedanceNetwork == null) { - dcLfZeroImpedanceNetwork = new LfZeroImpedanceNetwork(this, true); + if (dcLfZeroImpedanceNetworks == null) { + dcLfZeroImpedanceNetworks = LfZeroImpedanceNetwork.create(this, true); } } else { - if (acLfZeroImpedanceNetwork == null) { - acLfZeroImpedanceNetwork = new LfZeroImpedanceNetwork(this, false); + if (acLfZeroImpedanceNetworks == null) { + acLfZeroImpedanceNetworks = LfZeroImpedanceNetwork.create(this, false); } } } - public LfZeroImpedanceNetwork getZeroImpedanceNetwork(boolean dc) { + public Set getZeroImpedanceNetworks(boolean dc) { updateZeroImpedanceCache(dc); - return dc ? dcLfZeroImpedanceNetwork : acLfZeroImpedanceNetwork; - } - - private Graph createSubGraph(Predicate branchFilter) { - Objects.requireNonNull(branchFilter); - - List filteredBranches = getBranches().stream() - .filter(branchFilter) - .collect(Collectors.toList()); - - Graph subGraph = new Pseudograph<>(LfBranch.class); - for (LfBranch branch : filteredBranches) { - subGraph.addVertex(branch.getBus1()); - subGraph.addVertex(branch.getBus2()); - subGraph.addEdge(branch.getBus1(), branch.getBus2(), branch); - } - - return subGraph; + return dc ? dcLfZeroImpedanceNetworks : acLfZeroImpedanceNetworks; } public GraphConnectivity getConnectivity() { diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java b/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java index af21163f94..d1a2ff8e0e 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfNetworkListener.java @@ -6,6 +6,8 @@ */ package com.powsybl.openloadflow.network; +import java.util.List; + /** * @author Geoffroy Jamgotchian */ @@ -34,4 +36,10 @@ public interface LfNetworkListener { void onTapPositionChange(LfBranch branch, int oldPosition, int newPosition); void onShuntSusceptanceChange(LfShunt shunt, double b); + + void onZeroImpedanceNetworkSpanningTreeChange(LfBranch branch, boolean dc, boolean spanningTree); + + void onZeroImpedanceNetworkSplit(LfZeroImpedanceNetwork initialNetwork, List splitNetworks, boolean dc); + + void onZeroImpedanceNetworkMerge(LfZeroImpedanceNetwork network1, LfZeroImpedanceNetwork network2, LfZeroImpedanceNetwork mergedNetwork, boolean dc); } diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetworkListenerTracer.java b/src/main/java/com/powsybl/openloadflow/network/LfNetworkListenerTracer.java index afbbbfbf8f..e87ce9ef25 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfNetworkListenerTracer.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfNetworkListenerTracer.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; import java.util.Objects; /** @@ -113,4 +114,23 @@ public void onShuntSusceptanceChange(LfShunt shunt, double b) { LOGGER.trace("onShuntSusceptanceChange(shuntId='{}', b={})", shunt.getId(), b); delegate.onShuntSusceptanceChange(shunt, b); } + + @Override + public void onZeroImpedanceNetworkSpanningTreeChange(LfBranch branch, boolean dc, boolean spanningTree) { + LOGGER.trace("onZeroImpedanceNetworkSpanningTreeChange(branchId='{}', dc={}, spanningTree={})", + branch, dc, spanningTree); + delegate.onZeroImpedanceNetworkSpanningTreeChange(branch, dc, spanningTree); + } + + @Override + public void onZeroImpedanceNetworkSplit(LfZeroImpedanceNetwork initialNetwork, List splitNetworks, boolean dc) { + LOGGER.trace("onZeroImpedanceNetworkSplit(initialNetwork={}, splitNetworks={}, dc={})", initialNetwork, splitNetworks, dc); + delegate.onZeroImpedanceNetworkSplit(initialNetwork, splitNetworks, dc); + } + + @Override + public void onZeroImpedanceNetworkMerge(LfZeroImpedanceNetwork network1, LfZeroImpedanceNetwork network2, LfZeroImpedanceNetwork mergedNetwork, boolean dc) { + LOGGER.trace("onZeroImpedanceNetworkMerge(network1={}, network2={}, mergedNetwork={}, dc={})", network1, network2, mergedNetwork, dc); + delegate.onZeroImpedanceNetworkMerge(network1, network2, mergedNetwork, dc); + } } diff --git a/src/main/java/com/powsybl/openloadflow/network/LfZeroImpedanceNetwork.java b/src/main/java/com/powsybl/openloadflow/network/LfZeroImpedanceNetwork.java new file mode 100644 index 0000000000..40cb85b223 --- /dev/null +++ b/src/main/java/com/powsybl/openloadflow/network/LfZeroImpedanceNetwork.java @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2023, 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.network; + +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.alg.connectivity.ConnectivityInspector; +import org.jgrapht.alg.interfaces.SpanningTreeAlgorithm; +import org.jgrapht.alg.spanning.KruskalMinimumSpanningTree; +import org.jgrapht.graph.AsSubgraph; +import org.jgrapht.graph.Pseudograph; + +import java.util.*; + +/** + * @author Geoffroy Jamgotchian + */ +public class LfZeroImpedanceNetwork { + + private final LfNetwork network; + + private final boolean dc; + + private final Graph graph; + + private SpanningTreeAlgorithm.SpanningTree spanningTree; + + public LfZeroImpedanceNetwork(LfNetwork network, boolean dc, Graph graph) { + this.network = Objects.requireNonNull(network); + this.dc = dc; + this.graph = Objects.requireNonNull(graph); + for (LfBus bus : graph.vertexSet()) { + bus.setZeroImpedanceNetwork(dc, this); + } + updateSpanningTree(); + if (!dc) { + updateVoltageControlMergeStatus(); + } + } + + private static Graph createSubgraph(Graph graph, Set vertexSubset) { + Graph subGraph = new Pseudograph<>(LfBranch.class); + Graphs.addGraph(subGraph, new AsSubgraph<>(graph, vertexSubset)); + return subGraph; + } + + public static Set create(LfNetwork network, boolean dc) { + Objects.requireNonNull(network); + Set zeroImpedanceNetworks = new LinkedHashSet<>(); + var graph = createZeroImpedanceSubGraph(network, dc); + List> connectedSets = new ConnectivityInspector<>(graph).connectedSets(); + for (Set connectedSet : connectedSets) { + var subGraph = createSubgraph(graph, connectedSet); + zeroImpedanceNetworks.add(new LfZeroImpedanceNetwork(network, dc, subGraph)); + } + return zeroImpedanceNetworks; + } + + private static Graph createZeroImpedanceSubGraph(LfNetwork network, boolean dc) { + Graph subGraph = new Pseudograph<>(LfBranch.class); + for (LfBranch branch : network.getBranches()) { + LfBus bus1 = branch.getBus1(); + LfBus bus2 = branch.getBus2(); + if (bus1 != null && bus2 != null && branch.isZeroImpedance(dc)) { + // add to zero impedance graph all buses that could be connected to a zero impedance branch + if (!subGraph.containsVertex(bus1)) { + subGraph.addVertex(bus1); + } + if (!subGraph.containsVertex(bus2)) { + subGraph.addVertex(bus2); + } + if (!branch.isDisabled()) { + subGraph.addEdge(bus1, bus2, branch); + } + } + } + return subGraph; + } + + public LfNetwork getNetwork() { + return network; + } + + public boolean isDc() { + return dc; + } + + public Graph getGraph() { + return graph; + } + + public SpanningTreeAlgorithm.SpanningTree getSpanningTree() { + return spanningTree; + } + + public void updateSpanningTree() { + spanningTree = new KruskalMinimumSpanningTree<>(graph).getSpanningTree(); + Set spanningTreeEdges = spanningTree.getEdges(); + for (LfBranch branch : graph.edgeSet()) { + branch.setSpanningTreeEdge(dc, spanningTreeEdges.contains(branch)); + } + } + + @SuppressWarnings("unchecked") + private static void linkVoltageControls(VoltageControl mainVc, VoltageControl vc) { + mainVc.mergedDependentVoltageControls.add((VoltageControl) vc); + vc.mainMergedVoltageControl = (VoltageControl) mainVc; + } + + private void updateVoltageControlMergeStatus() { + Map>> voltageControlsByType = new EnumMap<>(VoltageControl.Type.class); + for (LfBus zb : graph.vertexSet()) { // all enabled by design + for (VoltageControl vc : zb.getVoltageControls()) { + voltageControlsByType.computeIfAbsent(vc.getType(), k -> new ArrayList<>()) + .add(vc); + vc.getMergedDependentVoltageControls().clear(); + vc.mainMergedVoltageControl = null; + } + } + for (List> voltageControls : voltageControlsByType.values()) { + if (voltageControls.size() > 1) { + // we take the highest target voltage (why not...) and in case of equality the voltage control + // with the first controlled bus ID by alpha sort + voltageControls.sort(Comparator.>comparingDouble(VoltageControl::getTargetValue) + .reversed() + .thenComparing(o -> o.getControlledBus().getId())); + VoltageControl mainVc = voltageControls.get(0); + mainVc.mergeStatus = VoltageControl.MergeStatus.MAIN; + // first one is main, the other ones are dependents + for (int i = 1; i < voltageControls.size(); i++) { + VoltageControl vc = voltageControls.get(i); + vc.mergeStatus = VoltageControl.MergeStatus.DEPENDENT; + linkVoltageControls(mainVc, vc); + } + } else { + voltageControls.get(0).mergeStatus = VoltageControl.MergeStatus.MAIN; + } + } + } + + public void removeBranchAndTryToSplit(LfBranch disabledBranch) { + graph.removeEdge(disabledBranch); + + List> connectedSets = new ConnectivityInspector<>(graph).connectedSets(); + if (connectedSets.size() > 1) { // real split + disabledBranch.setSpanningTreeEdge(dc, false); + + Set zeroImpedanceNetworks = network.getZeroImpedanceNetworks(dc); + zeroImpedanceNetworks.remove(this); + List splitZns = new ArrayList<>(2); + for (Set connectedSet : connectedSets) { + var subGraph = createSubgraph(graph, connectedSet); + splitZns.add(new LfZeroImpedanceNetwork(network, dc, subGraph)); + } + zeroImpedanceNetworks.addAll(splitZns); + + for (LfNetworkListener listener : network.getListeners()) { + listener.onZeroImpedanceNetworkSplit(this, splitZns, dc); + } + } else { + if (disabledBranch.isSpanningTreeEdge(dc)) { + disabledBranch.setSpanningTreeEdge(dc, false); + + // just update the spanning + updateSpanningTree(); + } + } + } + + public static void addBranchAndMerge(LfZeroImpedanceNetwork zn1, LfZeroImpedanceNetwork zn2, LfBranch enabledBranch) { + Objects.requireNonNull(zn1); + Objects.requireNonNull(zn2); + Objects.requireNonNull(enabledBranch); + LfNetwork network = zn1.getNetwork(); + boolean dc = zn1.isDc(); + Set zeroImpedanceNetworks = network.getZeroImpedanceNetworks(dc); + Graph mergedGraph = new Pseudograph<>(LfBranch.class); + Graphs.addGraph(mergedGraph, zn1.getGraph()); + Graphs.addGraph(mergedGraph, zn2.getGraph()); + mergedGraph.addEdge(enabledBranch.getBus1(), enabledBranch.getBus2(), enabledBranch); + zeroImpedanceNetworks.remove(zn1); + zeroImpedanceNetworks.remove(zn2); + LfZeroImpedanceNetwork mergedZn = new LfZeroImpedanceNetwork(network, dc, mergedGraph); + zeroImpedanceNetworks.add(mergedZn); + + for (LfNetworkListener listener : network.getListeners()) { + listener.onZeroImpedanceNetworkMerge(zn1, zn2, mergedZn, dc); + } + } + + public void addBranch(LfBranch branch) { + graph.addEdge(branch.getBus1(), branch.getBus2(), branch); + } + + @Override + public String toString() { + return "LfZeroImpedanceNetwork(dc=" + dc + + ", buses=" + graph.vertexSet() + + ", branches=" + graph.edgeSet() + + ")"; + } +} diff --git a/src/main/java/com/powsybl/openloadflow/network/ShuntVoltageControl.java b/src/main/java/com/powsybl/openloadflow/network/ShuntVoltageControl.java index 5af2fa7b59..2405f0f215 100644 --- a/src/main/java/com/powsybl/openloadflow/network/ShuntVoltageControl.java +++ b/src/main/java/com/powsybl/openloadflow/network/ShuntVoltageControl.java @@ -12,8 +12,10 @@ */ public class ShuntVoltageControl extends DiscreteVoltageControl { + private static final int PRIORITY = 2; + public ShuntVoltageControl(LfBus controlledBus, double targetValue, Double targetDeadband) { - super(controlledBus, targetValue, targetDeadband); + super(controlledBus, Type.SHUNT, PRIORITY, targetValue, targetDeadband); } @Override diff --git a/src/main/java/com/powsybl/openloadflow/network/TransformerVoltageControl.java b/src/main/java/com/powsybl/openloadflow/network/TransformerVoltageControl.java index cd2d0fdbba..f390f08783 100644 --- a/src/main/java/com/powsybl/openloadflow/network/TransformerVoltageControl.java +++ b/src/main/java/com/powsybl/openloadflow/network/TransformerVoltageControl.java @@ -12,8 +12,10 @@ */ public class TransformerVoltageControl extends DiscreteVoltageControl { + private static final int PRIORITY = 1; + public TransformerVoltageControl(LfBus controlledBus, double targetValue, Double targetDeadband) { - super(controlledBus, targetValue, targetDeadband); + super(controlledBus, Type.TRANSFORMER, PRIORITY, targetValue, targetDeadband); } @Override diff --git a/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java b/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java index 9654c17a18..3ef87f4ce4 100644 --- a/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java +++ b/src/main/java/com/powsybl/openloadflow/network/VoltageControl.java @@ -6,21 +6,42 @@ */ package com.powsybl.openloadflow.network; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; /** * @author Geoffroy Jamgotchian */ public class VoltageControl extends Control { + public enum Type { + GENERATOR, + TRANSFORMER, + SHUNT + } + + public enum MergeStatus { + MAIN, + DEPENDENT + } + + protected final Type type; + + protected int priority; + protected final LfBus controlledBus; protected final List controllerElements = new ArrayList<>(); - protected VoltageControl(double targetValue, LfBus controlledBus) { + protected MergeStatus mergeStatus = MergeStatus.MAIN; + + protected final List> mergedDependentVoltageControls = new ArrayList<>(); + + protected VoltageControl mainMergedVoltageControl; + + protected VoltageControl(double targetValue, Type type, int priority, LfBus controlledBus) { super(targetValue); + this.type = Objects.requireNonNull(type); + this.priority = priority; this.controlledBus = Objects.requireNonNull(controlledBus); } @@ -39,4 +60,104 @@ public void addControllerElement(T controllerElement) { public boolean isControllerEnabled(T controllerElement) { throw new IllegalStateException(); } + + public List> getMergedDependentVoltageControls() { + return mergedDependentVoltageControls; + } + + protected int getPriority() { + return priority; + } + + public Type getType() { + return type; + } + + public boolean isDisabled() { + if (controlledBus.isDisabled()) { + return true; + } + return controllerElements.stream() + .allMatch(LfElement::isDisabled); + } + + public MergeStatus getMergeStatus() { + return mergeStatus; + } + + @SuppressWarnings("unchecked") + public > E getMainVoltageControl() { + switch (mergeStatus) { + case MAIN: + return (E) this; + case DEPENDENT: + return (E) mainMergedVoltageControl; + default: + throw new IllegalStateException("Unknown merge status: " + mergeStatus); + } + } + + public List getMergedControllerElements() { + if (mergedDependentVoltageControls.isEmpty()) { + return controllerElements; + } else { + List mergedControllerElements = new ArrayList<>(controllerElements); + for (var mvc : mergedDependentVoltageControls) { + mergedControllerElements.addAll(mvc.getControllerElements()); + } + return mergedControllerElements; + } + } + + private static void addVoltageControls(List> voltageControls, LfBus bus) { + if (bus.isVoltageControlled()) { + for (VoltageControl vc : bus.getVoltageControls()) { + if (vc.isDisabled() || vc.getMergeStatus() == MergeStatus.DEPENDENT) { + continue; + } + voltageControls.add(vc); + } + } + } + + public static List> findVoltageControlsSortedByPriority(LfBus bus) { + List> voltageControls = new ArrayList<>(); + LfZeroImpedanceNetwork zn = bus.getZeroImpedanceNetwork(false); + if (zn != null) { // bus is part of a zero impedance graph + for (LfBus zb : zn.getGraph().vertexSet()) { // all enabled by design + addVoltageControls(voltageControls, zb); + } + } else { + addVoltageControls(voltageControls, bus); + } + voltageControls.sort(Comparator.comparingInt(VoltageControl::getPriority)); + return voltageControls; + } + + /** + * FIXME: take into account controllers status + */ + public boolean isHidden() { + // collect all voltage controls with the same controlled bus as this one and also all voltage controls coming + // from merged ones + List> voltageControls = findVoltageControlsSortedByPriority(controlledBus); + if (voltageControls.isEmpty()) { + return true; // means all disabled + } + // we should normally have max 3 voltage controls (one of each type) because already merged + if (voltageControls.size() > 1) { + return voltageControls.get(0) != this; + } + return false; + } + + @Override + public String toString() { + return "VoltageControl(type=" + type + + ", controlledBus='" + controlledBus + + "', controllerElements=" + controllerElements + + ", mergeStatus=" + mergeStatus + + ", mergedDependentVoltageControlsSize=" + mergedDependentVoltageControls.size() + + ")"; + } } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBranch.java b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBranch.java index 8d91ec7d23..66bd877354 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBranch.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBranch.java @@ -230,9 +230,19 @@ public boolean isZeroImpedance(boolean dc) { @Override public void setSpanningTreeEdge(boolean dc, boolean spanningTreeEdge) { if (dc) { - dcSpanningTreeEdge = spanningTreeEdge; + if (spanningTreeEdge != dcSpanningTreeEdge) { + dcSpanningTreeEdge = spanningTreeEdge; + for (LfNetworkListener listener : network.getListeners()) { + listener.onZeroImpedanceNetworkSpanningTreeChange(this, dc, spanningTreeEdge); + } + } } else { - acSpanningTreeEdge = spanningTreeEdge; + if (spanningTreeEdge != acSpanningTreeEdge) { + acSpanningTreeEdge = spanningTreeEdge; + for (LfNetworkListener listener : network.getListeners()) { + listener.onZeroImpedanceNetworkSpanningTreeChange(this, dc, spanningTreeEdge); + } + } } } @@ -285,4 +295,42 @@ private static boolean isZeroImpedanceBranch(PiModel piModel, boolean dc, double return piModel.getZ() < lowImpedanceThreshold; } } + + private void updateZeroImpedanceNetworks(boolean disabled, boolean dc) { + if (isZeroImpedance(dc)) { + LfZeroImpedanceNetwork zn1 = bus1.getZeroImpedanceNetwork(dc); + LfZeroImpedanceNetwork zn2 = bus2.getZeroImpedanceNetwork(dc); + if (zn1 != null && zn2 != null) { + if (disabled) { + if (zn1 == zn2) { + // zero impedance network split (maybe) + zn1.removeBranchAndTryToSplit(this); + } else { + throw new IllegalStateException("Should not happen"); + } + } else { + if (zn1 != zn2) { + // zero impedance network merge + LfZeroImpedanceNetwork.addBranchAndMerge(zn1, zn2, this); + } else { + // we need to add the branch again to zero impedance graph and update spanning tree as + // this branch might become part of spanning tree (and was not before because disabled) + zn1.addBranch(this); + } + } + } + } + } + + @Override + public void setDisabled(boolean disabled) { + if (disabled != this.disabled) { + this.disabled = disabled; + notifyDisable(); + if (bus1 != null && bus2 != null) { + updateZeroImpedanceNetworks(disabled, false); + updateZeroImpedanceNetworks(disabled, true); + } + } + } } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java index 19838a6f97..798cf78aa1 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java @@ -85,6 +85,10 @@ public abstract class AbstractLfBus extends AbstractElement implements LfBus { protected double remoteVoltageControlReactivePercent = Double.NaN; + protected LfZeroImpedanceNetwork dcZeroImpedanceNetwork; + + protected LfZeroImpedanceNetwork acZeroImpedanceNetwork; + protected AbstractLfBus(LfNetwork network, double v, double angle, boolean distributedOnConformLoad) { super(network); lfAggregatedLoads = new LfAggregatedLoadsImpl(distributedOnConformLoad); @@ -129,6 +133,25 @@ public double getTargetQ() { return getGenerationTargetQ() - getLoadTargetQ(); } + @Override + public List> getVoltageControls() { + List> voltageControls = new ArrayList<>(3); + getGeneratorVoltageControl().ifPresent(voltageControls::add); + getTransformerVoltageControl().ifPresent(voltageControls::add); + getShuntVoltageControl().ifPresent(voltageControls::add); + return voltageControls; + } + + @Override + public boolean isVoltageControlled() { + return isGeneratorVoltageControlled() || isShuntVoltageControlled() || isTransformerVoltageControlled(); + } + + @Override + public Optional> getHighestPriorityVoltageControl() { + return VoltageControl.findVoltageControlsSortedByPriority(this).stream().findFirst(); + } + @Override public boolean hasGeneratorVoltageControllerCapability() { return generatorVoltageControl != null && generatorVoltageControl.getControllerElements().contains(this); @@ -139,10 +162,6 @@ public Optional getGeneratorVoltageControl() { return Optional.ofNullable(generatorVoltageControl); } - public void removeGeneratorVoltageControl() { - this.generatorVoltageControl = null; - } - @Override public void setGeneratorVoltageControl(GeneratorVoltageControl generatorVoltageControl) { this.generatorVoltageControl = Objects.requireNonNull(generatorVoltageControl); @@ -581,4 +600,19 @@ public void setRemoteVoltageControlReactivePercent(double remoteVoltageControlRe public double getMismatchP() { return p.eval() - getTargetP(); // slack bus can also have real injection connected } + + @Override + public void setZeroImpedanceNetwork(boolean dc, LfZeroImpedanceNetwork zeroImpedanceNetwork) { + Objects.requireNonNull(zeroImpedanceNetwork); + if (dc) { + dcZeroImpedanceNetwork = zeroImpedanceNetwork; + } else { + acZeroImpedanceNetwork = zeroImpedanceNetwork; + } + } + + @Override + public LfZeroImpedanceNetwork getZeroImpedanceNetwork(boolean dc) { + return dc ? dcZeroImpedanceNetwork : acZeroImpedanceNetwork; + } } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkLoaderImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkLoaderImpl.java index 32fdc6c717..687fb5a4d8 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkLoaderImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfNetworkLoaderImpl.java @@ -20,7 +20,6 @@ import com.powsybl.openloadflow.util.Reports; import net.jafama.FastMath; import org.apache.commons.lang3.tuple.Pair; -import org.jgrapht.alg.connectivity.ConnectivityInspector; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -467,105 +466,6 @@ private static void createSwitches(List switches, LfNetwork lfNetwork, L } } - private static void fixAllVoltageControls(LfNetwork lfNetwork, LfNetworkParameters parameters) { - // If min impedance is set, there is no zero-impedance branch - if (!parameters.isDc() && !parameters.isMinImpedance()) { - // Merge the discrete voltage control in each zero impedance connected set - List> connectedSets = new ConnectivityInspector<>(lfNetwork.getZeroImpedanceNetwork(false).getSubGraph()).connectedSets(); - connectedSets.forEach(connectedSet -> mergeVoltageControls(connectedSet, parameters)); - } - } - - private static void mergeVoltageControls(Set zeroImpedanceConnectedSet, LfNetworkParameters parameters) { - // Get the list of voltage controls from controlled buses in the zero impedance connected set - // Note that the list order is not deterministic as jgrapht connected set computation is using a basic HashSet - List generatorVoltageControls = zeroImpedanceConnectedSet.stream() - .filter(LfBus::isGeneratorVoltageControlled) - .flatMap(bus -> bus.getGeneratorVoltageControl().stream()) - .collect(Collectors.toList()); - - // Get the list of discrete voltage controls from controlled buses in the zero impedance connected set - // Note that the list order is not deterministic as jgrapht connected set computation is using a basic HashSet - List transformerVoltageControls = !parameters.isTransformerVoltageControl() ? Collections.emptyList() : - zeroImpedanceConnectedSet.stream() - .filter(LfBus::isTransformerVoltageControlled) - .flatMap(bus -> bus.getTransformerVoltageControl().stream()) - .collect(Collectors.toList()); - - if (generatorVoltageControls.isEmpty() && transformerVoltageControls.size() <= 1) { - return; - } - - if (!generatorVoltageControls.isEmpty()) { - - // First, resolve problem of voltage controls (generator, static var compensator, etc.) - // We have several controls whose controlled bus are in the same non-impedant connected set - // To solve that we keep only one voltage control (and its target value), the other ones are removed - // and the corresponding controllers are added to the control kept - if (generatorVoltageControls.size() > 1) { - LOGGER.info("Zero impedance connected set with several voltage controls: controls are merged"); - - // Sort voltage controls to have a merged voltage control with a deterministic controlled bus, - // a deterministic target value and controller buses in a deterministic order - generatorVoltageControls.sort(Comparator.comparing(GeneratorVoltageControl::getTargetValue).thenComparing(vc -> vc.getControlledBus().getId())); - checkVoltageControlUniqueTargetV("generator", generatorVoltageControls); - - // Merge the controllers into the kept voltage control - GeneratorVoltageControl keptVoltageControl = generatorVoltageControls.remove(generatorVoltageControls.size() - 1); - generatorVoltageControls.forEach(vc -> vc.getControlledBus().removeGeneratorVoltageControl()); - generatorVoltageControls.stream() - .flatMap(vc -> vc.getControllerElements().stream()) - .forEach(controller -> { - keptVoltageControl.addControllerElement(controller); - controller.setGeneratorVoltageControl(keptVoltageControl); - }); - } - - // Second, we have to remove all the discrete voltage controls if present. - if (!transformerVoltageControls.isEmpty()) { - LOGGER.info("Zero impedance connected set with several discrete voltage controls and a voltage control: discrete controls deleted"); - transformerVoltageControls.stream() - .flatMap(dvc -> dvc.getControllerElements().stream()) - .forEach(controller -> controller.setVoltageControl(null)); - transformerVoltageControls.forEach(dvc -> dvc.getControlledBus().setTransformerVoltageControl(null)); - } - } else { - // We have at least 2 discrete controls whose controlled bus are in the same non-impedant connected set - // To solve that we keep only one discrete voltage control, the other ones are removed - // and the corresponding controllers are added to the discrete control kept - LOGGER.info("Zero impedance connected set with several discrete voltage controls: discrete controls merged"); - - // Sort discrete voltage controls to have a merged discrete voltage control with a deterministic controlled bus, - // a deterministic target value and controller branches in a deterministic order - transformerVoltageControls.sort(Comparator.comparing(TransformerVoltageControl::getTargetValue).thenComparing(vc -> vc.getControlledBus().getId())); - checkVoltageControlUniqueTargetV("transformer", transformerVoltageControls); - - // Merge the controllers into the kept voltage control - TransformerVoltageControl keptTransformerVoltageControl = transformerVoltageControls.remove(transformerVoltageControls.size() - 1); - transformerVoltageControls.forEach(dvc -> dvc.getControlledBus().setTransformerVoltageControl(null)); - transformerVoltageControls.stream() - .flatMap(tvc -> tvc.getControllerElements().stream()) - .forEach(controller -> { - keptTransformerVoltageControl.addControllerElement(controller); - controller.setVoltageControl(keptTransformerVoltageControl); - }); - } - } - - private static > void checkVoltageControlUniqueTargetV(String voltageControlType, List voltageControls) { - // To check uniqueness we take the target value which will be kept as reference. - // The kept target value is the highest, it corresponds to the last voltage control in the ordered list. - V vcRef = voltageControls.get(voltageControls.size() - 1); - boolean uniqueTargetV = voltageControls.stream().noneMatch(vc -> FastMath.abs(vc.getTargetValue() - vcRef.getTargetValue()) > TARGET_V_EPSILON); - if (!uniqueTargetV) { - LOGGER.error("Inconsistent {} voltage controls: buses {} are in the same non-impedant connected set and are controlled with different target voltages ({}). Only target voltage {} is kept", - voltageControlType, - voltageControls.stream().map(VoltageControl::getControlledBus).collect(Collectors.toList()), - voltageControls.stream().map(VoltageControl::getTargetValue).collect(Collectors.toList()), - vcRef.getTargetValue()); - } - } - private static void createPhaseControl(LfNetwork lfNetwork, PhaseTapChanger ptc, String controllerBranchId, String legId, LfNetworkParameters parameters) { if (ptc != null && ptc.isRegulating() && ptc.getRegulationMode() != PhaseTapChanger.RegulationMode.FIXED_TAP) { @@ -636,10 +536,6 @@ private static void createTransformerVoltageControl(LfNetwork lfNetwork, RatioTa LOGGER.warn("Regulating terminal of voltage controller branch '{}' is out of voltage or in a different synchronous component: voltage control discarded", controllerBranch.getId()); return; } - if (controlledBus.isGeneratorVoltageControlled()) { - LOGGER.warn("Controlled bus '{}' has both generator and transformer voltage control on: only generator control is kept", controlledBus.getId()); - return; - } double regulatingTerminalNominalV = rtc.getRegulationTerminal().getVoltageLevel().getNominalV(); double targetValue = rtc.getTargetV() / regulatingTerminalNominalV; @@ -686,17 +582,6 @@ private static void createShuntVoltageControl(LfNetwork lfNetwork, ShuntCompensa controllerShunt.setVoltageControlCapability(false); return; } - if (controlledBus.isGeneratorVoltageControlled()) { - LOGGER.warn("Controlled bus {} has both generator and shunt voltage control on: only generator control is kept", controlledBus.getId()); - controllerShunt.setVoltageControlCapability(false); - return; - } - Optional tvc = controlledBus.getTransformerVoltageControl(); - if (tvc.isPresent()) { - LOGGER.error("Controlled bus {} has already a transformer voltage control: only transformer control is kept", controlledBus.getId()); - controllerShunt.setVoltageControlCapability(false); - return; - } if (controllerShunt.getVoltageControl().isPresent()) { // if a controller shunt is already in a shunt voltage control, the number of equations will not equal the // number of variables. We have only one B variable for more than one bus target V equations. @@ -711,7 +596,7 @@ private static void createShuntVoltageControl(LfNetwork lfNetwork, ShuntCompensa controlledBus.getShuntVoltageControl().ifPresentOrElse(voltageControl -> { LOGGER.trace("Controlled bus {} has already a shunt voltage control: a shared control is created", controlledBus.getId()); if (FastMath.abs(voltageControl.getTargetValue() - targetValue) > TARGET_V_EPSILON) { - LOGGER.warn("Controlled bus {} already has a transformer voltage control with a different target voltage: {} and {}", + LOGGER.warn("Controlled bus {} already has a shunt voltage control with a different target voltage: {} and {}", controlledBus.getId(), voltageControl.getTargetValue(), targetValue); } if (!voltageControl.getControllerElements().contains(controllerShunt)) { @@ -777,9 +662,6 @@ private LfNetwork create(int numCC, int numSC, Network network, List buses, createSwitches(switches, lfNetwork, postProcessors, parameters, report); } - // Fixing voltage controls need to be done after creating switches, as the zero-impedance graph is changed with switches - fixAllVoltageControls(lfNetwork, parameters); - // secondary voltage controls createSecondaryVoltageControls(network, parameters, lfNetwork); diff --git a/src/test/java/com/powsybl/openloadflow/NonImpedantBranchTest.java b/src/test/java/com/powsybl/openloadflow/NonImpedantBranchTest.java index 94f7a0adbf..ff66246de6 100644 --- a/src/test/java/com/powsybl/openloadflow/NonImpedantBranchTest.java +++ b/src/test/java/com/powsybl/openloadflow/NonImpedantBranchTest.java @@ -6,6 +6,11 @@ */ package com.powsybl.openloadflow; +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.computation.local.LocalComputationManager; +import com.powsybl.contingency.BranchContingency; +import com.powsybl.contingency.ContingenciesProvider; +import com.powsybl.contingency.Contingency; import com.powsybl.iidm.network.Bus; import com.powsybl.iidm.network.Line; import com.powsybl.iidm.network.Network; @@ -14,11 +19,18 @@ import com.powsybl.loadflow.LoadFlowParameters; import com.powsybl.loadflow.LoadFlowResult; import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; import com.powsybl.openloadflow.network.AbstractLoadFlowNetworkFactory; import com.powsybl.openloadflow.network.SlackBusSelectionMode; +import com.powsybl.openloadflow.sa.OpenSecurityAnalysisProvider; +import com.powsybl.security.*; +import com.powsybl.security.detectors.DefaultLimitViolationDetector; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Collections; +import java.util.List; + import static com.powsybl.openloadflow.util.LoadFlowAssert.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -155,6 +167,13 @@ void threeBusesAndNonImpTransfoTest() { assertAngleEquals(0, b3); } + /** + * g1 + * | + * b1 === b2 --- b3 --- b4 + * | | + * g2 l1 + */ @Test void inconsistentTargetVoltagesTest() { Network network = Network.create("FourBusesWithNonImpedantLineAndInconsistentTargetVoltages", "code"); @@ -249,8 +268,16 @@ void twoLinkedPVBusesTest() { assertReactivePowerEquals(0, l12.getTerminal2()); } + /** + * + * g1 (2MW) g3 (2 MW) + * | | + * b1 -- b2 -- b3 + * | + * l2 (4 MW, 2 MVar) + */ @Test - void nonImpedentNetworkWithTwoPVBusesTest() { + void nonImpedantNetworkWithTwoPVBusesTest() { Network network = Network.create("TwoPVBusesInNonImpNet", "code"); Bus b1 = createBus(network, "b1"); Bus b2 = createBus(network, "b2"); @@ -281,7 +308,7 @@ void nonImpedentNetworkWithTwoPVBusesTest() { } @Test - void nonImpedentNetworkWithCycleTest() { + void nonImpedantNetworkWithCycleTest() { Network network = Network.create("ThreeBusesNetworkWithCycle", "code"); Bus b1 = createBus(network, "b1"); Bus b2 = createBus(network, "b2"); @@ -303,4 +330,39 @@ void nonImpedentNetworkWithCycleTest() { assertAngleEquals(0, b2); assertAngleEquals(0, b3); } + + @Test + void securityAnalysisTest() { + Network network = Network.create("test", "code"); + Bus b0 = createBus(network, "b0"); + Bus b1 = createBus(network, "b1"); + Bus b2 = createBus(network, "b2"); + Bus b3 = createBus(network, "b3"); + Bus b5 = createBus(network, "b5"); + Bus b7 = createBus(network, "b7"); + Bus b9 = createBus(network, "b9"); + createGenerator(b1, "g1", 2); + createGenerator(b9, "g9", 1); + createLoad(b2, "d2", 1); + createLoad(b7, "d7", 4); + createLine(network, b0, b1, "l01", 0.0); + createLine(network, b0, b2, "l02", 0.0); + createLine(network, b1, b3, "l13", 0.0); + createLine(network, b2, b3, "l23", 0.0); + createLine(network, b2, b5, "l25", 0.0); + createLine(network, b5, b9, "l59", 0.1); + createLine(network, b7, b9, "l79", 0.1); + + List contingencies = List.of(new Contingency("contingency1", List.of(new BranchContingency("l01"), new BranchContingency("l02"))), + new Contingency("contingency2", List.of(new BranchContingency("l01"), new BranchContingency("l13")))); + + ContingenciesProvider provider = n -> contingencies; + SecurityAnalysisProvider securityAnalysisProvider = new OpenSecurityAnalysisProvider(new DenseMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>()); + SecurityAnalysisReport report = securityAnalysisProvider.run(network, network.getVariantManager().getWorkingVariantId(), new DefaultLimitViolationDetector(), + new LimitViolationFilter(), LocalComputationManager.getDefault(), new SecurityAnalysisParameters(), provider, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Reporter.NO_OP).join(); + assertEquals(PostContingencyComputationStatus.CONVERGED, report.getResult().getPostContingencyResults().get(0).getStatus()); + assertEquals(PostContingencyComputationStatus.CONVERGED, report.getResult().getPostContingencyResults().get(1).getStatus()); + } } diff --git a/src/test/java/com/powsybl/openloadflow/ZeroImpedanceFlowsTest.java b/src/test/java/com/powsybl/openloadflow/ZeroImpedanceFlowsTest.java index 77337175b9..56a37ac67d 100644 --- a/src/test/java/com/powsybl/openloadflow/ZeroImpedanceFlowsTest.java +++ b/src/test/java/com/powsybl/openloadflow/ZeroImpedanceFlowsTest.java @@ -88,6 +88,13 @@ void threeBusesZeroImpedanceParallelLinesTest() { checkFlows(0.0, 0.0, l23p.getTerminal1(), 0.0, 0.0, l23p.getTerminal2()); } + /** + * g1 --- b4 + * | / | + * b1 --- b2--- b3 | + * \ | + * --- b5 + */ @Test void fiveBusesZeroImpedanceLineTest() { Network network = Network.create("FiveBusesWithZeroImpedanceLine", "code"); diff --git a/src/test/java/com/powsybl/openloadflow/ac/NonImpedantBranchDisablingTest.java b/src/test/java/com/powsybl/openloadflow/ac/NonImpedantBranchDisablingTest.java index fbabed0661..6387c9210c 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/NonImpedantBranchDisablingTest.java +++ b/src/test/java/com/powsybl/openloadflow/ac/NonImpedantBranchDisablingTest.java @@ -11,8 +11,10 @@ import com.powsybl.iidm.network.Switch; import com.powsybl.loadflow.LoadFlow; import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; import com.powsybl.math.matrix.DenseMatrixFactory; import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.OpenLoadFlowProvider; import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; import com.powsybl.openloadflow.graph.NaiveGraphConnectivityFactory; import com.powsybl.openloadflow.network.*; @@ -21,6 +23,7 @@ import com.powsybl.openloadflow.network.impl.Networks; import com.powsybl.openloadflow.util.LoadFlowAssert; import com.powsybl.openloadflow.util.PerUnit; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; @@ -29,12 +32,20 @@ import static com.powsybl.openloadflow.util.LoadFlowAssert.assertActivePowerEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Geoffroy Jamgotchian */ class NonImpedantBranchDisablingTest { + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory())); + } + @Test void test() { Network network = NodeBreakerNetworkFactory.create(); @@ -72,14 +83,14 @@ void testOpenBranch() { Network network = NodeBreakerNetworkFactory.create3Bars(); network.getLine("L2").setR(0.0).setX(0.0); network.getLine("L1").getTerminal1().disconnect(); - LoadFlow.run(network); + loadFlowRunner.run(network); assertEquals(600.018, network.getLine("L2").getTerminal1().getP(), LoadFlowAssert.DELTA_POWER); assertEquals(-600.018, network.getLine("L2").getTerminal2().getP(), LoadFlowAssert.DELTA_POWER); assertEquals(Double.NaN, network.getLine("L1").getTerminal1().getP(), LoadFlowAssert.DELTA_POWER); network.getLine("L1").getTerminal1().connect(); network.getLine("L1").getTerminal2().disconnect(); - LoadFlow.run(network); + loadFlowRunner.run(network); assertEquals(600.0, network.getLine("L2").getTerminal1().getP(), LoadFlowAssert.DELTA_POWER); assertEquals(-600.0, network.getLine("L2").getTerminal2().getP(), LoadFlowAssert.DELTA_POWER); assertEquals(Double.NaN, network.getLine("L1").getTerminal2().getP(), LoadFlowAssert.DELTA_POWER); @@ -96,7 +107,7 @@ void testDisabledNonImpedantBranch() { .setSlackBusSelectionMode(SlackBusSelectionMode.NAME) .setSlackBusesIds(List.of("VL2_0")); - LoadFlow.run(network, parameters); + loadFlowRunner.run(network, parameters); assertActivePowerEquals(401.757, network.getLine("L1").getTerminal1()); assertActivePowerEquals(100.878, network.getLine("L2").getTerminal1()); @@ -116,9 +127,27 @@ void testDisabledNonImpedantBranch() { new AcloadFlowEngine(context).run(); } // should be the same as with previous LF - assertEquals(401.740, largestNetwork.getBranchById("L1").getP1().eval() * PerUnit.SB, LoadFlowAssert.DELTA_POWER); - assertEquals(100.870, largestNetwork.getBranchById("L2").getP1().eval() * PerUnit.SB, LoadFlowAssert.DELTA_POWER); - assertEquals(100.870, largestNetwork.getBranchById("L3").getP1().eval() * PerUnit.SB, LoadFlowAssert.DELTA_POWER); + assertEquals(401.757, largestNetwork.getBranchById("L1").getP1().eval() * PerUnit.SB, LoadFlowAssert.DELTA_POWER); + assertEquals(100.878, largestNetwork.getBranchById("L2").getP1().eval() * PerUnit.SB, LoadFlowAssert.DELTA_POWER); + assertEquals(100.878, largestNetwork.getBranchById("L3").getP1().eval() * PerUnit.SB, LoadFlowAssert.DELTA_POWER); } } + + @Test + void testOpenAtOneSideZeroImpedanceBranch() { + Network network = NodeBreakerNetworkFactory.create3Bars(); + network.getLine("L2").setR(0.0).setX(0.0); + network.getLine("L2").getTerminal2().disconnect(); + LoadFlowResult result = loadFlowRunner.run(network); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus()); + assertTrue(Double.isNaN(network.getLine("L2").getTerminal1().getP())); + assertTrue(Double.isNaN(network.getLine("L2").getTerminal2().getP())); + + LoadFlowParameters parameters = new LoadFlowParameters() + .setDc(true); + result = loadFlowRunner.run(network, parameters); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus()); + assertTrue(Double.isNaN(network.getLine("L2").getTerminal1().getP())); + assertTrue(Double.isNaN(network.getLine("L2").getTerminal2().getP())); + } } diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index e18d97040b..3605375e3f 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -1844,21 +1844,68 @@ void testSecurityAnalysisWithOperatorStrategy() { SecurityAnalysisResult result = runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters, operatorStrategies, actions, Reporter.NO_OP); - assertEquals(578.3, result.getPreContingencyResult().getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(578.740, result.getPreContingencyResult().getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); assertEquals(0.0, result.getPreContingencyResult().getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(292.0, result.getPreContingencyResult().getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(0.0, getPostContingencyResult(result, "L1").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(318.3, getPostContingencyResult(result, "L1").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(583.5, getOperatorStrategyResult(result, "strategyL1").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(302.0, getOperatorStrategyResult(result, "strategyL1").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(292.708, result.getPreContingencyResult().getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(0.002, getPostContingencyResult(result, "L1").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(318.294, getPostContingencyResult(result, "L1").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(583.624, getOperatorStrategyResult(result, "strategyL1").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(303.513, getOperatorStrategyResult(result, "strategyL1").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); assertEquals(0.0, getPostContingencyResult(result, "L3").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(431.0, getPostContingencyResult(result, "L3").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(302.2, getOperatorStrategyResult(result, "strategyL3").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(583.5, getOperatorStrategyResult(result, "strategyL3").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(583.5, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(302.2, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(441.5, getOperatorStrategyResult(result, "strategyL2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(441.5, getOperatorStrategyResult(result, "strategyL2").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(602.965, getPostContingencyResult(result, "L3").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(303.513, getOperatorStrategyResult(result, "strategyL3").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(583.624, getOperatorStrategyResult(result, "strategyL3").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(583.624, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(303.513, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(441.539, getOperatorStrategyResult(result, "strategyL2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(441.539, getOperatorStrategyResult(result, "strategyL2").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + + // re-run with loadflows + LoadFlow.run(network, parameters); + assertEquals(578.740, network.getLine("L1").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(0.0, network.getLine("L2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(292.708, network.getLine("L3").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + + network.getLine("L1").getTerminal1().disconnect(); + network.getLine("L1").getTerminal2().disconnect(); + LoadFlow.run(network, parameters); + assertEquals(0.002, network.getLine("L2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(318.284, network.getLine("L3").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + + network.getSwitch("C1").setOpen(false); + LoadFlow.run(network, parameters); + assertEquals(583.624, network.getLine("L2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(303.513, network.getLine("L3").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + + network.getLine("L1").getTerminal1().connect(); + network.getLine("L1").getTerminal2().connect(); + network.getLine("L3").getTerminal1().disconnect(); + network.getLine("L3").getTerminal2().disconnect(); + network.getSwitch("C1").setOpen(true); + LoadFlow.run(network, parameters); + assertEquals(602.965, network.getLine("L1").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(0.0, network.getLine("L2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + + network.getSwitch("C2").setOpen(false); + LoadFlow.run(network, parameters); + assertEquals(583.624, network.getLine("L1").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(303.513, network.getLine("L2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + + network.getLine("L3").getTerminal1().connect(); + network.getLine("L3").getTerminal2().connect(); + network.getLine("L2").getTerminal1().disconnect(); + network.getLine("L2").getTerminal2().disconnect(); + network.getSwitch("C2").setOpen(true); + LoadFlow.run(network, parameters); + assertEquals(583.624, network.getLine("L1").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(303.513, network.getLine("L3").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + + network.getSwitch("C1").setOpen(false); + network.getSwitch("C2").setOpen(false); + LoadFlow.run(network, parameters); + assertEquals(441.539, network.getLine("L1").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(89.429, network.getLine("L2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(441.539, network.getLine("L3").getTerminal1().getI(), LoadFlowAssert.DELTA_I); } @Test @@ -1896,24 +1943,24 @@ void testSecurityAnalysisWithOperatorStrategy2() { SecurityAnalysisResult result = runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters, operatorStrategies, actions, Reporter.NO_OP); - assertEquals(578.3, result.getPreContingencyResult().getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(578.740, result.getPreContingencyResult().getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); assertEquals(0.0, result.getPreContingencyResult().getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(292.0, result.getPreContingencyResult().getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(292.708, result.getPreContingencyResult().getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); // L1 contingency assertEquals(0.0, getPostContingencyResult(result, "L1").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(318.3, getPostContingencyResult(result, "L1").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(318.284, getPostContingencyResult(result, "L1").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); assertTrue(getPostContingencyResult(result, "L1").getLimitViolationsResult().getLimitViolations().isEmpty()); assertTrue(getOptionalOperatorStrategyResult(result, "strategyL1").isEmpty()); // L3 contingency assertEquals(0.0, getPostContingencyResult(result, "L3").getNetworkResult().getBranchResult("L2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(431.0, getPostContingencyResult(result, "L3").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(602.965, getPostContingencyResult(result, "L3").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); assertFalse(getPostContingencyResult(result, "L3").getLimitViolationsResult().getLimitViolations().isEmpty()); // HIGH_VOLTAGE assertFalse(getOptionalOperatorStrategyResult(result, "strategyL3").isEmpty()); // L2 contingency - assertEquals(583.5, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(302.2, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(441.5, getOperatorStrategyResult(result, "strategyL2_1").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(441.5, getOperatorStrategyResult(result, "strategyL2_2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(583.624, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(303.513, getPostContingencyResult(result, "L2").getNetworkResult().getBranchResult("L3").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(441.539, getOperatorStrategyResult(result, "strategyL2_1").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(441.539, getOperatorStrategyResult(result, "strategyL2_2").getNetworkResult().getBranchResult("L1").getI1(), LoadFlowAssert.DELTA_I); } @Test @@ -1922,6 +1969,8 @@ void testSecurityAnalysisWithOperatorStrategy3() { securityAnalysisProvider = new OpenSecurityAnalysisProvider(matrixFactory, connectivityFactory); Network network = NodeBreakerNetworkFactory.create3Bars(); + network.getVoltageLevel("VL1").setLowVoltageLimit(390.0); + network.getVoltageLevel("VL2").setLowVoltageLimit(390.0); network.getSwitch("C1").setOpen(true); network.getSwitch("C2").setOpen(true); network.getLine("L1").getCurrentLimits1().orElseThrow().setPermanentLimit(580.0); @@ -1934,12 +1983,12 @@ void testSecurityAnalysisWithOperatorStrategy3() { .collect(Collectors.toList()); List actions = List.of(new SwitchAction("action1", "C1", false), - new SwitchAction("action3", "C2", false)); + new SwitchAction("action3", "C2", false)); List operatorStrategies = List.of(new OperatorStrategy("strategyL3", ContingencyContext.specificContingency("L3"), new AllViolationCondition(List.of("VL1", "VL2")), List.of("action3")), - new OperatorStrategy("strategyL2", ContingencyContext.specificContingency("L2"), new AtLeastOneViolationCondition(List.of("L1", "L3")), List.of("action1", "action3"))); + new OperatorStrategy("strategyL2", ContingencyContext.specificContingency("L2"), new AtLeastOneViolationCondition(List.of("L1", "L3")), List.of("action1", "action3"))); - List monitors = createAllBranchesMonitors(network); + List monitors = createNetworkMonitors(network); LoadFlowParameters parameters = new LoadFlowParameters(); parameters.setDistributedSlack(false); @@ -1993,7 +2042,7 @@ void testMetrixTutorial() { securityAnalysisProvider = new OpenSecurityAnalysisProvider(matrixFactory, connectivityFactory); Network network = MetrixTutorialSixBusesFactory.create(); - network.getGenerator("SO_G2").setTargetP(628); + network.getGenerator("SO_G2").setTargetP(960); SecurityAnalysisParameters securityAnalysisParameters = new SecurityAnalysisParameters(); LoadFlowParameters parameters = new LoadFlowParameters(); @@ -2016,27 +2065,29 @@ void testMetrixTutorial() { SecurityAnalysisResult result = runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters, operatorStrategies, actions, Reporter.NO_OP); - assertEquals(271.99, result.getPreContingencyResult().getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(504.40, getPostContingencyResult(result, "S_SO_1").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(301.69, getOperatorStrategyResult(result, "strategy1").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(488.99, getOperatorStrategyResult(result, "strategy2").getNetworkResult().getBranchResult("SO_NO_1").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(499.984, getOperatorStrategyResult(result, "strategy3").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); - assertEquals(499.984, getOperatorStrategyResult(result, "strategy4").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); - - network.getGenerator("SO_G2").setTargetP(628); + assertEquals(346.296, result.getPreContingencyResult().getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(642.805, getPostContingencyResult(result, "S_SO_1").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(240.523, getOperatorStrategyResult(result, "strategy1").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(599.161, getOperatorStrategyResult(result, "strategy2").getNetworkResult().getBranchResult("SO_NO_1").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(639.268, getOperatorStrategyResult(result, "strategy3").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); + assertEquals(639.268, getOperatorStrategyResult(result, "strategy4").getNetworkResult().getBranchResult("S_SO_2").getI1(), LoadFlowAssert.DELTA_I); + + LoadFlow.run(network, parameters); + assertEquals(346.296, network.getLine("S_SO_2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + network.getLine("S_SO_1").getTerminal1().disconnect(); network.getLine("S_SO_1").getTerminal2().disconnect(); + LoadFlow.run(network, parameters); + assertEquals(642.805, network.getLine("S_SO_2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); - LoadFlowParameters parameters2 = new LoadFlowParameters(); - parameters2.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD); - parameters2.setHvdcAcEmulation(false); - + network.getSwitch("SOO1_SOO1_DJ_OMN").setOpen(true); LoadFlow.run(network, parameters); - assertEquals(504.40, network.getLine("S_SO_2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(240.523, network.getLine("S_SO_2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + network.getSwitch("SOO1_SOO1_DJ_OMN").setOpen(false); network.getTwoWindingsTransformer("NE_NO_1").getPhaseTapChanger().setTapPosition(1); LoadFlow.run(network, parameters); - assertEquals(499.989, network.getLine("S_SO_2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); + assertEquals(639.268, network.getLine("S_SO_2").getTerminal1().getI(), LoadFlowAssert.DELTA_I); } @Test