Skip to content

Commit

Permalink
Fix: Hvdc active power setpoint is interpreted on rectifier AC side (#…
Browse files Browse the repository at this point in the history
…471)

Signed-off-by: Anne Tilloy <[email protected]>
  • Loading branch information
annetill authored Mar 11, 2022
1 parent 2a60fac commit f6359e6
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,8 +607,7 @@ private void applyInjectionContingencies(Network network, LfNetwork lfNetwork, P
for (Pair<LfBus, LccConverterStation> 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) {
Expand Down
10 changes: 5 additions & 5 deletions src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowLccTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 9 additions & 9 deletions src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowVscTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,29 @@ 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);
assertAngleEquals(0, bus3);

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand All @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}

Expand Down

0 comments on commit f6359e6

Please sign in to comment.