diff --git a/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java index a67787bba7..dc72c9f6f6 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java @@ -192,28 +192,33 @@ public ContingencyContext getContingencyContext() { } @Override + @SuppressWarnings("unchecked") public EquationTerm getFunctionEquationTerm() { LfBranch branch; - switch (functionType) { - case BRANCH_ACTIVE_POWER: - case BRANCH_ACTIVE_POWER_1: - case BRANCH_ACTIVE_POWER_3: - return (EquationTerm) ((LfBranch) functionElement).getP1(); - case BRANCH_ACTIVE_POWER_2: + return (EquationTerm) switch (functionType) { + case BRANCH_ACTIVE_POWER, BRANCH_ACTIVE_POWER_1, BRANCH_ACTIVE_POWER_3 + -> ((LfBranch) functionElement).getP1(); + case BRANCH_ACTIVE_POWER_2 -> { branch = (LfBranch) functionElement; - return branch instanceof LfLegBranch ? (EquationTerm) ((LfBranch) functionElement).getP1() : (EquationTerm) ((LfBranch) functionElement).getP2(); - case BRANCH_CURRENT: - case BRANCH_CURRENT_1: - case BRANCH_CURRENT_3: - return (EquationTerm) ((LfBranch) functionElement).getI1(); - case BRANCH_CURRENT_2: + yield branch instanceof LfLegBranch ? ((LfBranch) functionElement).getP1() + : ((LfBranch) functionElement).getP2(); + } + case BRANCH_REACTIVE_POWER_1, BRANCH_REACTIVE_POWER_3 + -> ((LfBranch) functionElement).getQ1(); + case BRANCH_REACTIVE_POWER_2 -> { branch = (LfBranch) functionElement; - return branch instanceof LfLegBranch ? (EquationTerm) ((LfBranch) functionElement).getI1() : (EquationTerm) ((LfBranch) functionElement).getI2(); - case BUS_VOLTAGE: - return (EquationTerm) ((LfBus) functionElement).getCalculatedV(); - default: - throw createFunctionTypeNotSupportedException(functionType); - } + yield branch instanceof LfLegBranch ? ((LfBranch) functionElement).getQ1() + : ((LfBranch) functionElement).getQ2(); + } + case BRANCH_CURRENT, BRANCH_CURRENT_1, BRANCH_CURRENT_3 + -> ((LfBranch) functionElement).getI1(); + case BRANCH_CURRENT_2 -> { + branch = (LfBranch) functionElement; + yield branch instanceof LfLegBranch ? ((LfBranch) functionElement).getI1() + : ((LfBranch) functionElement).getI2(); + } + case BUS_VOLTAGE -> ((LfBus) functionElement).getCalculatedV(); + }; } @Override @@ -1056,7 +1061,7 @@ protected SensitivityFactorHolder readAndCheckFactors(Network network, Map functionId, functionElement, functionType, injectionLfBuses, variableType, contingencyContext, originalVariableSetIds)); } else { LfElement functionElement; - LfElement variableElement = null; + LfElement variableElement; if (isActivePowerFunctionType(functionType) || isCurrentFunctionType(functionType)) { LfBranch branch = checkAndGetBranchOrLeg(network, functionId, functionType, lfNetwork); functionElement = branch != null && branch.getBus1() != null && branch.getBus2() != null ? branch : null; @@ -1077,6 +1082,9 @@ protected SensitivityFactorHolder readAndCheckFactors(Network network, Map LfBranch leg = lfNetwork.getBranchById(LfLegBranch.getId(variableId, getLegNumber(variableType))); variableElement = leg != null && leg.getBus1() != null && leg.getBus2() != null ? leg : null; break; + case BUS_TARGET_VOLTAGE: + variableElement = findBusTargetVoltageVariableElement(network, variableId, breakers, lfNetwork); + break; default: throw createVariableTypeNotSupportedWithFunctionTypeException(variableType, functionType); } @@ -1084,12 +1092,15 @@ protected SensitivityFactorHolder readAndCheckFactors(Network network, Map checkBus(network, functionId, busCache, breakers); functionElement = lfNetwork.getBusById(functionId); if (variableType == SensitivityVariableType.BUS_TARGET_VOLTAGE) { - checkRegulatingTerminal(network, variableId); - Optional regulatingTerminal = Networks.getEquipmentRegulatingTerminal(network, variableId); - if (regulatingTerminal.isPresent()) { // this cannot fail because it is checked in checkRegulatingTerminal - Bus regulatedBus = breakers ? regulatingTerminal.get().getBusBreakerView().getBus() : regulatingTerminal.get().getBusView().getBus(); - variableElement = regulatedBus != null ? lfNetwork.getBusById(regulatedBus.getId()) : null; - } + variableElement = findBusTargetVoltageVariableElement(network, variableId, breakers, lfNetwork); + } else { + throw createVariableTypeNotSupportedWithFunctionTypeException(variableType, functionType); + } + } else if (isReactivePowerFunctionType(functionType)) { + LfBranch branch = checkAndGetBranchOrLeg(network, functionId, functionType, lfNetwork); + functionElement = branch != null && branch.getBus1() != null && branch.getBus2() != null ? branch : null; + if (variableType == SensitivityVariableType.BUS_TARGET_VOLTAGE) { + variableElement = findBusTargetVoltageVariableElement(network, variableId, breakers, lfNetwork); } else { throw createVariableTypeNotSupportedWithFunctionTypeException(variableType, functionType); } @@ -1105,6 +1116,14 @@ protected SensitivityFactorHolder readAndCheckFactors(Network network, Map return factorHolder; } + protected static LfElement findBusTargetVoltageVariableElement(Network network, String variableId, boolean breakers, + LfNetwork lfNetwork) { + checkRegulatingTerminal(network, variableId); + Terminal regulatingTerminal = Networks.getEquipmentRegulatingTerminal(network, variableId).orElseThrow(); // this cannot fail because it is checked in checkRegulatingTerminal + Bus regulatedBus = breakers ? regulatingTerminal.getBusBreakerView().getBus() : regulatingTerminal.getBusView().getBus(); + return regulatedBus != null ? lfNetwork.getBusById(regulatedBus.getId()) : null; + } + private static boolean isActivePowerFunctionType(SensitivityFunctionType functionType) { return functionType == SensitivityFunctionType.BRANCH_ACTIVE_POWER || functionType == SensitivityFunctionType.BRANCH_ACTIVE_POWER_1 @@ -1112,6 +1131,12 @@ private static boolean isActivePowerFunctionType(SensitivityFunctionType functio || functionType == SensitivityFunctionType.BRANCH_ACTIVE_POWER_3; } + private static boolean isReactivePowerFunctionType(SensitivityFunctionType functionType) { + return functionType == SensitivityFunctionType.BRANCH_REACTIVE_POWER_1 + || functionType == SensitivityFunctionType.BRANCH_REACTIVE_POWER_2 + || functionType == SensitivityFunctionType.BRANCH_REACTIVE_POWER_3; + } + private static boolean isCurrentFunctionType(SensitivityFunctionType functionType) { return functionType == SensitivityFunctionType.BRANCH_CURRENT || functionType == SensitivityFunctionType.BRANCH_CURRENT_1 @@ -1151,26 +1176,21 @@ protected static boolean isDistributedSlackOnLoads(DcLoadFlowParameters lfParame * Base value for per-uniting, depending on the function type */ private static & Quantity, E extends Enum & Quantity> double getFunctionBaseValue(LfSensitivityFactor factor) { - switch (factor.getFunctionType()) { - case BRANCH_ACTIVE_POWER: - case BRANCH_ACTIVE_POWER_1: - case BRANCH_ACTIVE_POWER_2: - case BRANCH_ACTIVE_POWER_3: - return PerUnit.SB; - case BRANCH_CURRENT: - case BRANCH_CURRENT_1: - case BRANCH_CURRENT_3: + return switch (factor.getFunctionType()) { + case BRANCH_ACTIVE_POWER, BRANCH_ACTIVE_POWER_1, BRANCH_ACTIVE_POWER_2, BRANCH_ACTIVE_POWER_3, + BRANCH_REACTIVE_POWER_1, BRANCH_REACTIVE_POWER_2, BRANCH_REACTIVE_POWER_3 + -> PerUnit.SB; + case BRANCH_CURRENT, BRANCH_CURRENT_1, BRANCH_CURRENT_3 -> { LfBranch branch = (LfBranch) factor.getFunctionElement(); - return PerUnit.ib(branch.getBus1().getNominalV()); - case BRANCH_CURRENT_2: + yield PerUnit.ib(branch.getBus1().getNominalV()); + } + case BRANCH_CURRENT_2 -> { LfBranch branch2 = (LfBranch) factor.getFunctionElement(); - return branch2 instanceof LfLegBranch ? PerUnit.ib(branch2.getBus1().getNominalV()) : PerUnit.ib(branch2.getBus2().getNominalV()); - case BUS_VOLTAGE: - LfBus bus = (LfBus) factor.getFunctionElement(); - return bus.getNominalV(); - default: - throw new IllegalArgumentException("Unknown function type " + factor.getFunctionType()); - } + yield branch2 instanceof LfLegBranch ? PerUnit.ib(branch2.getBus1().getNominalV()) : + PerUnit.ib(branch2.getBus2().getNominalV()); + } + case BUS_VOLTAGE -> ((LfBus) factor.getFunctionElement()).getNominalV(); + }; } /** diff --git a/src/test/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysisTest.java index 9c94135e8a..f568daa3e0 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysisTest.java @@ -29,6 +29,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static com.powsybl.openloadflow.util.LoadFlowAssert.assertCurrentEquals; +import static com.powsybl.openloadflow.util.LoadFlowAssert.assertReactivePowerEquals; import static org.junit.jupiter.api.Assertions.*; /** @@ -1295,4 +1297,104 @@ void testWithTieLines() { assertEquals(1, result.getValues().size()); assertEquals(35.000, result.getBranchFlow1FunctionReferenceValue("t12"), LoadFlowAssert.DELTA_POWER); } + + @Test + void testReactivePowerAndCurrentPerTargetVSensi() { + Network network = EurostagTutorialExample1Factory.create(); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "NLOAD"); + + List factors = List.of( + new SensitivityFactor(SensitivityFunctionType.BRANCH_REACTIVE_POWER_1, + "NHV2_NLOAD", + SensitivityVariableType.BUS_TARGET_VOLTAGE, + "GEN", + false, + ContingencyContext.all()), + new SensitivityFactor(SensitivityFunctionType.BRANCH_REACTIVE_POWER_2, + "NHV2_NLOAD", + SensitivityVariableType.BUS_TARGET_VOLTAGE, + "GEN", + false, + ContingencyContext.all()), + new SensitivityFactor(SensitivityFunctionType.BRANCH_CURRENT_1, + "NHV2_NLOAD", + SensitivityVariableType.BUS_TARGET_VOLTAGE, + "GEN", + false, + ContingencyContext.all()), + new SensitivityFactor(SensitivityFunctionType.BRANCH_CURRENT_2, + "NHV2_NLOAD", + SensitivityVariableType.BUS_TARGET_VOLTAGE, + "GEN", + false, + ContingencyContext.all()) + ); + + SensitivityAnalysisResult result = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); + + assertEquals(4, result.getValues().size()); + assertEquals(-7.959, result.getSensitivityValue("GEN", "NHV2_NLOAD", SensitivityFunctionType.BRANCH_REACTIVE_POWER_1, SensitivityVariableType.BUS_TARGET_VOLTAGE), LoadFlowAssert.DELTA_POWER); + assertEquals(0.0, result.getSensitivityValue("GEN", "NHV2_NLOAD", SensitivityFunctionType.BRANCH_REACTIVE_POWER_2, SensitivityVariableType.BUS_TARGET_VOLTAGE), LoadFlowAssert.DELTA_POWER); + assertEquals(-52.329, result.getSensitivityValue("GEN", "NHV2_NLOAD", SensitivityFunctionType.BRANCH_CURRENT_1, SensitivityVariableType.BUS_TARGET_VOLTAGE), LoadFlowAssert.DELTA_POWER); + assertEquals(-132.392, result.getSensitivityValue("GEN", "NHV2_NLOAD", SensitivityFunctionType.BRANCH_CURRENT_2, SensitivityVariableType.BUS_TARGET_VOLTAGE), LoadFlowAssert.DELTA_POWER); + + runAcLf(network); + + // check reference flows are consistents with LF ones + var twt = network.getTwoWindingsTransformer("NHV2_NLOAD"); + assertReactivePowerEquals(result.getFunctionReferenceValue("NHV2_NLOAD", SensitivityFunctionType.BRANCH_REACTIVE_POWER_1), + twt.getTerminal1()); + assertReactivePowerEquals(result.getFunctionReferenceValue("NHV2_NLOAD", SensitivityFunctionType.BRANCH_REACTIVE_POWER_2), + twt.getTerminal2()); + assertCurrentEquals(result.getFunctionReferenceValue("NHV2_NLOAD", SensitivityFunctionType.BRANCH_CURRENT_1), + twt.getTerminal1()); + assertCurrentEquals(result.getFunctionReferenceValue("NHV2_NLOAD", SensitivityFunctionType.BRANCH_CURRENT_2), + twt.getTerminal2()); + + // check sensi values looks consistent with 2 LF diff + double q1Before = twt.getTerminal1().getQ(); + double q2Before = twt.getTerminal2().getQ(); + double i1Before = twt.getTerminal1().getI(); + double i2Before = twt.getTerminal2().getI(); + + Generator gen = network.getGenerator("GEN"); + gen.setTargetV(gen.getTargetV() + 1); + + runAcLf(network); + + assertEquals(-7.2817, twt.getTerminal1().getQ() - q1Before, LoadFlowAssert.DELTA_SENSITIVITY_VALUE); // looks ok vs -7.959 + assertEquals(0.0007, twt.getTerminal2().getQ() - q2Before, LoadFlowAssert.DELTA_SENSITIVITY_VALUE); // looks ok vs 0 + assertEquals(-49.1009, twt.getTerminal1().getI() - i1Before, LoadFlowAssert.DELTA_SENSITIVITY_VALUE); // looks ok vs -52.329 + assertEquals(-124.2233, twt.getTerminal2().getI() - i2Before, LoadFlowAssert.DELTA_SENSITIVITY_VALUE); // looks ok vs -132.392 + } + + @Test + void testUnsupportedVariablesSensiV() { + Network network = EurostagTutorialExample1Factory.create(); + + SensitivityAnalysisParameters sensiParameters = createParameters(false, "NLOAD"); + List contingencies = Collections.emptyList(); + List variableSets = Collections.emptyList(); + + List factors = List.of(new SensitivityFactor(SensitivityFunctionType.BRANCH_REACTIVE_POWER_1, + "NHV2_NLOAD", + SensitivityVariableType.INJECTION_ACTIVE_POWER, + "GEN", + false, + ContingencyContext.all())); + + CompletionException e = assertThrows(CompletionException.class, () -> sensiRunner.run(network, factors, contingencies, variableSets, sensiParameters)); + assertEquals("Variable type INJECTION_ACTIVE_POWER not supported with function type BRANCH_REACTIVE_POWER_1", e.getCause().getMessage()); + + List factors2 = List.of(new SensitivityFactor(SensitivityFunctionType.BUS_VOLTAGE, + "NLOAD", + SensitivityVariableType.INJECTION_ACTIVE_POWER, + "GEN", + false, + ContingencyContext.all())); + + e = assertThrows(CompletionException.class, () -> sensiRunner.run(network, factors2, contingencies, variableSets, sensiParameters)); + assertEquals("Variable type INJECTION_ACTIVE_POWER not supported with function type BUS_VOLTAGE", e.getCause().getMessage()); + } }