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 29109d05d4..c6cceb1ac4 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBus.java @@ -211,36 +211,10 @@ void addLoad(Load load, boolean distributedOnConformLoad) { void addLccConverterStation(LccConverterStation lccCs) { // note that LCC converter station are out of the slack distribution. lccCss.add(lccCs); - HvdcLine line = lccCs.getHvdcLine(); - double targetP = PerUnit.SB * getLccConverterStationLoadTargetP(lccCs, line); + double targetP = HvdcConverterStations.getConverterStationTargetP(lccCs); loadTargetP += targetP; initialLoadTargetP += targetP; - loadTargetQ += PerUnit.SB * getLccConverterStationLoadTargetQ(lccCs, line); - } - - /** - * Gets active power for an LCC station in per-unit. - */ - public static double getLccConverterStationLoadTargetP(LccConverterStation lccCs, HvdcLine line) { - // The active power setpoint is always positive. - // If the converter station is at side 1 and is rectifier, p should be positive. - // If the converter station is at side 1 and is inverter, p should be negative. - // If the converter station is at side 2 and is rectifier, p should be positive. - // If the converter station is at side 2 and is inverter, p should be negative. - return line.getActivePowerSetpoint() / PerUnit.SB * HvdcConverterStations.getActivePowerSetpointMultiplier(lccCs); // A LCC station has active losses. - } - - /** - * Gets reactive power for an LCC station in per-unit. - */ - public static double getLccConverterStationLoadTargetQ(LccConverterStation lccCs, HvdcLine line) { - // The active power setpoint is always positive. - // If the converter station is at side 1 and is rectifier, p should be positive. - // If the converter station is at side 1 and is inverter, p should be negative. - // If the converter station is at side 2 and is rectifier, p should be positive. - // If the converter station is at side 2 and is inverter, p should be negative. - double pCs = getLccConverterStationLoadTargetP(lccCs, line); - return Math.abs(pCs * Math.tan(Math.acos(lccCs.getPowerFactor()))); // A LCC station always consumes reactive power. + loadTargetQ += HvdcConverterStations.getLccConverterStationLoadTargetQ(lccCs); } protected void add(LfGenerator generator) { @@ -497,9 +471,8 @@ public void updateState(boolean reactiveLimits, boolean writeSlackBus, boolean d // update lcc converter station power for (LccConverterStation lccCs : lccCss) { - HvdcLine line = lccCs.getHvdcLine(); - double pCs = line.getActivePowerSetpoint() * HvdcConverterStations.getActivePowerSetpointMultiplier(lccCs); // A LCC station has active losses. - double qCs = Math.abs(pCs * Math.tan(Math.acos(lccCs.getPowerFactor()))); // A LCC station always consumes reactive power. + double pCs = HvdcConverterStations.getConverterStationTargetP(lccCs); // A LCC station has active losses. + double qCs = HvdcConverterStations.getLccConverterStationLoadTargetQ(lccCs); // A LCC station always consumes reactive power. lccCs.getTerminal() .setP(pCs) .setQ(qCs); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/HvdcConverterStations.java b/src/main/java/com/powsybl/openloadflow/network/impl/HvdcConverterStations.java index 26610e0df5..5777307f51 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/HvdcConverterStations.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/HvdcConverterStations.java @@ -29,7 +29,8 @@ public static boolean isRectifier(HvdcConverterStation station) { || (line.getConverterStation2() == station && line.getConvertersMode() == HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER); } - public static double getActivePowerSetpointMultiplier(HvdcConverterStation station) { + public static double getSign(HvdcConverterStation station) { + // This method gives the sign of PAc. boolean isConverterStationRectifier = isRectifier(station); double sign; if (station instanceof LccConverterStation) { // load convention. @@ -39,6 +40,86 @@ public static double getActivePowerSetpointMultiplier(HvdcConverterStation st } else { throw new PowsyblException("Unknown HVDC converter station type: " + station.getClass().getSimpleName()); } - return sign * (1 + (isConverterStationRectifier ? 1 : -1) * station.getLossFactor() / 100); + return sign; + } + + /** + * Gets targetP of an VSC converter station or load target P for a LCC converter station. + */ + public static double getConverterStationTargetP(HvdcConverterStation station) { + // For a VSC converter station, we are in generator convention. + // If the converter station is at side 1 and is rectifier, targetP should be negative. + // If the converter station is at side 1 and is inverter, targetP should be positive. + // If the converter station is at side 2 and is rectifier, targetP should be negative. + // If the converter station is at side 2 and is inverter, targetP should be positive. + // for a LCC converter station, we are in load convention. + // If the converter station is at side 1 and is rectifier, p should be positive. + // If the converter station is at side 1 and is inverter, p should be negative. + // If the converter station is at side 2 and is rectifier, p should be positive. + // If the converter station is at side 2 and is inverter, p should be negative. + return getSign(station) * getAbsoluteValuePAc(station); + } + + /** + * Gets reactive power for an LCC converter station. + */ + public static double getLccConverterStationLoadTargetQ(LccConverterStation lccCs) { + // Load convention. + // If the converter station is at side 1 and is rectifier, p should be positive. + // If the converter station is at side 1 and is inverter, p should be negative. + // If the converter station is at side 2 and is rectifier, p should be positive. + // If the converter station is at side 2 and is inverter, p should be negative. + double pCs = getConverterStationTargetP(lccCs); + return Math.abs(pCs * Math.tan(Math.acos(lccCs.getPowerFactor()))); // A LCC station always consumes reactive power. + } + + private static double getAbsoluteValuePAc(HvdcConverterStation station) { + boolean isConverterStationRectifier = isRectifier(station); + if (isConverterStationRectifier) { + return station.getHvdcLine().getActivePowerSetpoint(); + } else { + // the converter station is inverter. + HvdcConverterStation otherStation = getOtherConversionStation(station); + return getAbsoluteValueInverterPAc(otherStation.getLossFactor(), station.getLossFactor(), station.getHvdcLine()); + } + } + + private static double getHvdcLineLosses(double rectifierPDc, double nominalV, double r) { + // This method computes the losses due to the HVDC line. + // The active power value on rectifier DC side is known as the HVDC active power set point minus the losses related + // to AC/DC conversion (rectifier conversion), the voltage is approximated to the nominal voltage as attribute of the HVDC line. + // In an HVDC, as a branch with two sides, the difference between pDc1 and pDc2 can be computed with the assumptions: + // I = (V1 - V2) / R and pDc1 = I * V1 and pDc2 = I * V2 and V1 = nominalV + // we simply obtain that the absolute value of the difference is equal to R * pDc1 * pDc1 / (V1 * V1) if side 1 is rectifier side. + return r * rectifierPDc * rectifierPDc / (nominalV * nominalV); + } + + private static double getAbsoluteValueInverterPAc(double rectifierLossFactor, double inverterLossFactor, + HvdcLine hvdcLine) { + // On inverter side, absolute value of PAc of a VSC converter station should be computed in three step: + // 1) compute the losses related to the rectifier conversion. + // 2) compute the losses related to the HVDC line itself (R i^2). + // 3) compute the losses related to the inverter conversion. + double rectifierPDc = hvdcLine.getActivePowerSetpoint() * (1 - rectifierLossFactor / 100); // rectifierPDc positive. + double inverterPDc = rectifierPDc - getHvdcLineLosses(rectifierPDc, hvdcLine.getNominalV(), hvdcLine.getR()); + return inverterPDc * (1 - inverterLossFactor / 100); // always positive. + } + + private static HvdcConverterStation getOtherConversionStation(HvdcConverterStation station) { + HvdcLine line = station.getHvdcLine(); + return line.getConverterStation1() == station ? line.getConverterStation2() : line.getConverterStation1(); + } + + public static double getActivePowerSetpointMultiplier(HvdcConverterStation station) { + // For sensitivity analysis, we need the multiplier by converter station for an increase of 1MW + // of the HVDC active power setpoint. + // As a first approximation, we don't take into account the losses due to HVDC line itself. + boolean isConverterStationRectifier = isRectifier(station); + double sign = getSign(station); + if (isConverterStationRectifier) { + return sign; + } else { + return sign * (1 - (station.getLossFactor() + getOtherConversionStation(station).getLossFactor()) / 100); + } } } diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java index 0dc10c9370..40269d8722 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java @@ -20,7 +20,7 @@ public final class LfVscConverterStationImpl extends AbstractLfGenerator { private final VscConverterStation station; private LfVscConverterStationImpl(VscConverterStation station, boolean breakers, boolean reactiveLimits, LfNetworkLoadingReport report) { - super(getHvdcLineTargetP(station)); + super(HvdcConverterStations.getConverterStationTargetP(station)); this.station = station; // local control only @@ -34,16 +34,6 @@ public static LfVscConverterStationImpl create(VscConverterStation station, bool return new LfVscConverterStationImpl(station, breakers, reactiveLimits, report); } - private static double getHvdcLineTargetP(VscConverterStation vscCs) { - // The active power setpoint is always positive. - // If the converter station is at side 1 and is rectifier, targetP should be negative. - // If the converter station is at side 1 and is inverter, targetP should be positive. - // If the converter station is at side 2 and is rectifier, targetP should be negative. - // If the converter station is at side 2 and is inverter, targetP should be positive. - HvdcLine line = vscCs.getHvdcLine(); - return line.getActivePowerSetpoint() * HvdcConverterStations.getActivePowerSetpointMultiplier(vscCs); - } - @Override public String getId() { return station.getId(); diff --git a/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java index 0220a29a50..8bf66f5c82 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java @@ -607,8 +607,7 @@ private void applyInjectionContingencies(Network network, LfNetwork lfNetwork, P for (Pair busAndlcc : lccs) { LfBus bus = busAndlcc.getKey(); LccConverterStation lcc = busAndlcc.getValue(); - HvdcLine line = lcc.getHvdcLine(); - bus.setLoadTargetP(bus.getLoadTargetP() - AbstractLfBus.getLccConverterStationLoadTargetP(lcc, line)); + bus.setLoadTargetP(bus.getLoadTargetP() - HvdcConverterStations.getConverterStationTargetP(lcc) / PerUnit.SB); } for (LfVscConverterStationImpl vsc : vscs) { diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowLccTest.java b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowLccTest.java index e29be0453f..b1d752de47 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowLccTest.java +++ b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowLccTest.java @@ -39,18 +39,18 @@ void test() { Bus bus2 = network.getBusView().getBus("vl2_0"); assertVoltageEquals(389.3763, bus2); - assertAngleEquals(-0.095311, bus2); + assertAngleEquals(-0.095268, bus2); Bus bus3 = network.getBusView().getBus("vl3_0"); assertVoltageEquals(380, bus3); assertAngleEquals(0, bus3); LccConverterStation cs2 = network.getLccConverterStation("cs2"); - assertActivePowerEquals(50.05, cs2.getTerminal()); - assertReactivePowerEquals(37.538, cs2.getTerminal()); + assertActivePowerEquals(50.00, cs2.getTerminal()); + assertReactivePowerEquals(37.499, cs2.getTerminal()); LccConverterStation cs3 = network.getLccConverterStation("cs3"); - assertActivePowerEquals(-49.45, cs3.getTerminal()); - assertReactivePowerEquals(37.087, cs3.getTerminal()); + assertActivePowerEquals(-49.399, cs3.getTerminal()); + assertReactivePowerEquals(37.049, cs3.getTerminal()); } } diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowPhaseShifterTest.java b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowPhaseShifterTest.java index 6ed4b449a6..3e2490c4e1 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowPhaseShifterTest.java +++ b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowPhaseShifterTest.java @@ -410,7 +410,7 @@ void nonSupportedPhaseControl() { .setRegulating(true); LoadFlowResult result = loadFlowRunner.run(network, parameters); assertTrue(result.isOk()); - assertActivePowerEquals(100.1307, network.getLine("l12").getTerminal1()); + assertActivePowerEquals(100.0805, network.getLine("l12").getTerminal1()); } @Test diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowVscTest.java b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowVscTest.java index 8d83267ed9..956dce27e9 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowVscTest.java +++ b/src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowVscTest.java @@ -40,7 +40,7 @@ void test() { Bus bus2 = network.getBusView().getBus("vl2_0"); assertVoltageEquals(385, bus2); - assertAngleEquals(0.116917, bus2); + assertAngleEquals(0.117616, bus2); Bus bus3 = network.getBusView().getBus("vl3_0"); assertVoltageEquals(383, bus3); @@ -48,21 +48,21 @@ void test() { Generator g1 = network.getGenerator("g1"); assertActivePowerEquals(-102.56, g1.getTerminal()); - assertReactivePowerEquals(-615.733, g1.getTerminal()); + assertReactivePowerEquals(-615.918, g1.getTerminal()); VscConverterStation cs2 = network.getVscConverterStation("cs2"); - assertActivePowerEquals(50.55, cs2.getTerminal()); - assertReactivePowerEquals(598.046, cs2.getTerminal()); + assertActivePowerEquals(50.00, cs2.getTerminal()); + assertReactivePowerEquals(598.228, cs2.getTerminal()); VscConverterStation cs3 = network.getVscConverterStation("cs3"); - assertActivePowerEquals(-49.90, cs3.getTerminal()); + assertActivePowerEquals(-49.35, cs3.getTerminal()); assertReactivePowerEquals(-10.0, cs3.getTerminal()); Line l12 = network.getLine("l12"); - assertActivePowerEquals(103.112, l12.getTerminal1()); - assertReactivePowerEquals(615.733, l12.getTerminal1()); - assertActivePowerEquals(-100.55, l12.getTerminal2()); - assertReactivePowerEquals(-608.046, l12.getTerminal2()); + assertActivePowerEquals(102.563, l12.getTerminal1()); + assertReactivePowerEquals(615.918, l12.getTerminal1()); + assertActivePowerEquals(-99.999, l12.getTerminal2()); + assertReactivePowerEquals(-608.228, l12.getTerminal2()); } @Test diff --git a/src/test/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysisTest.java index b94ab8b85c..49a3678ce6 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysisTest.java @@ -318,9 +318,9 @@ protected void testPhaseShifterOutsideMainComponent(boolean dc) { assertEquals(1, result.getValues().size()); assertEquals(0d, result.getSensitivityValue("l45", "l12"), LoadFlowAssert.DELTA_POWER); if (dc) { - assertEquals(100.050, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.00, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); } else { - assertEquals(100.131, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.08, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); } } @@ -339,9 +339,9 @@ protected void testGlskOutsideMainComponent(boolean dc) { assertEquals(1, result.getValues().size()); assertEquals(0, result.getSensitivityValue("glsk", "l12"), LoadFlowAssert.DELTA_POWER); if (dc) { - assertEquals(100.050, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.000, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); } else { - assertEquals(100.131, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.080, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); } } diff --git a/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java b/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java index f4ad0465fb..85b682a4c2 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisContingenciesTest.java @@ -750,7 +750,7 @@ void testGlskOutsideMainComponentWithContingency() { assertEquals(2, result.getValues().size()); assertEquals(0, result.getSensitivityValue("glsk", "l12"), LoadFlowAssert.DELTA_POWER); - assertEquals(100.131, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); - assertEquals(100.131, result.getFunctionReferenceValue("additionnalline_0", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.080, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.080, result.getFunctionReferenceValue("additionnalline_0", "l12"), LoadFlowAssert.DELTA_POWER); } } diff --git a/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisTest.java index 4d18547376..f44b0779ad 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/ac/AcSensitivityAnalysisTest.java @@ -644,9 +644,9 @@ void testHvdcSensiWithLCCs() { SensitivityAnalysisResult result = sensiRunner.run(network, factors, Collections.emptyList(), Collections.emptyList(), sensiParameters); - assertEquals(-0.346002, result.getSensitivityValue("hvdc34", "l12"), LoadFlowAssert.DELTA_POWER); - assertEquals(0.346002, result.getSensitivityValue("hvdc34", "l13"), LoadFlowAssert.DELTA_POWER); - assertEquals(0.642998, result.getSensitivityValue("hvdc34", "l23"), LoadFlowAssert.DELTA_POWER); + assertEquals(-0.341889, result.getSensitivityValue("hvdc34", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(0.341889, result.getSensitivityValue("hvdc34", "l13"), LoadFlowAssert.DELTA_POWER); + assertEquals(0.63611, result.getSensitivityValue("hvdc34", "l23"), LoadFlowAssert.DELTA_POWER); } @Test diff --git a/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java b/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java index e20fcb4548..6cbbc78657 100644 --- a/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java +++ b/src/test/java/com/powsybl/openloadflow/sensi/dc/DcSensitivityAnalysisContingenciesTest.java @@ -1718,8 +1718,8 @@ void testGlskOutsideMainComponentWithContingency() { assertEquals(2, result.getValues().size()); assertEquals(0, result.getSensitivityValue("glsk", "l12"), LoadFlowAssert.DELTA_POWER); - assertEquals(100.050, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); - assertEquals(100.050, result.getFunctionReferenceValue("additionnalline_0", "l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.000, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.000, result.getFunctionReferenceValue("additionnalline_0", "l12"), LoadFlowAssert.DELTA_POWER); } @Test @@ -1740,7 +1740,7 @@ void testGlskOutsideMainComponentWithContingencyOnWatchedLine() { assertEquals(2, result.getValues().size()); assertEquals(0, result.getSensitivityValue("glsk", "l12"), LoadFlowAssert.DELTA_POWER); - assertEquals(100.050, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); + assertEquals(100.0, result.getFunctionReferenceValue("l12"), LoadFlowAssert.DELTA_POWER); assertEquals(0, result.getFunctionReferenceValue("l12", "l12"), LoadFlowAssert.DELTA_POWER); }