Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Computing Hvdc line loss in AC emulation #1062

Merged
merged 24 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
67346d7
Added HVDC line loss in AC emulation
SylvestreSakti Jul 10, 2024
7b60f08
Added newline for checkstyle
SylvestreSakti Jul 10, 2024
a15c4e7
Added newline for checkstyle
SylvestreSakti Jul 10, 2024
f6c740b
Modified tests to keep same behaviour with HVDC line resistance in AC…
SylvestreSakti Jul 10, 2024
cf2eb5f
Keeps r in ohms and its getter returns value in per unit
SylvestreSakti Jul 10, 2024
fba1fc4
Modified implementation of line losses with the method getHvdcLineLosses
SylvestreSakti Jul 10, 2024
ba21575
Added FIXME comment to use HvdcUtils.getHvdcLineLosses() from powsybl…
SylvestreSakti Jul 10, 2024
86189c4
Modified one test to check the correct sign of the influence of R in …
SylvestreSakti Jul 10, 2024
a7e9dac
Checkstyle corrections
SylvestreSakti Jul 10, 2024
b6707f5
Added comments to explain test of difference between R=0 and R!=0
SylvestreSakti Jul 10, 2024
65069d7
Use of HvdcUtils.getHvdcLineLosses with new release of powsybl-core
SylvestreSakti Jul 12, 2024
90809f6
Fixed for checkstyle
SylvestreSakti Jul 12, 2024
35bdfbb
Merge branch 'main' into hvdc-line-loss-ac-emulation
annetill Jul 15, 2024
860ee50
Modified to have more common code in AbstractHvdcAcEmulationFlowEquat…
SylvestreSakti Jul 15, 2024
e9cb339
Fixed tests to take cable loss into account
SylvestreSakti Jul 15, 2024
a2fb18c
Changed signs to avoid confusion
SylvestreSakti Jul 15, 2024
74dface
Added assert to check sign of powers
SylvestreSakti Jul 15, 2024
50ecc4d
Rename methods and added comments
SylvestreSakti Jul 15, 2024
8c2af3f
Fixed error in checking sign of losses
SylvestreSakti Jul 15, 2024
423aeeb
Merge branch 'main' into hvdc-line-loss-ac-emulation
annetill Jul 16, 2024
b8dc6dd
Removed unused line
SylvestreSakti Jul 16, 2024
6fc2c5a
Renamed variables
SylvestreSakti Jul 16, 2024
47a137e
Renamed variable for better comprehension
SylvestreSakti Jul 16, 2024
e8fd904
Changed boundedP to rawP in checking controller station
SylvestreSakti Jul 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import com.powsybl.openloadflow.network.LfBus;
import com.powsybl.openloadflow.network.LfHvdc;

import com.powsybl.iidm.network.util.HvdcUtils;

import java.util.List;

/**
Expand All @@ -30,6 +32,8 @@ public abstract class AbstractHvdcAcEmulationFlowEquationTerm extends AbstractEl

protected final double p0;

protected final double r;

protected final double lossFactor1;

protected final double lossFactor2;
Expand All @@ -45,13 +49,14 @@ protected AbstractHvdcAcEmulationFlowEquationTerm(LfHvdc hvdc, LfBus bus1, LfBus
variables = List.of(ph1Var, ph2Var);
k = hvdc.getDroop() * 180 / Math.PI;
p0 = hvdc.getP0();
r = hvdc.getR();
lossFactor1 = hvdc.getConverterStation1().getLossFactor() / 100;
lossFactor2 = hvdc.getConverterStation2().getLossFactor() / 100;
pMaxFromCS1toCS2 = hvdc.getPMaxFromCS1toCS2();
pMaxFromCS2toCS1 = hvdc.getPMaxFromCS2toCS1();
}

protected double rawP(double p0, double k, double ph1, double ph2) {
protected double rawP(double ph1, double ph2) {
return p0 + k * (ph1 - ph2);
}

Expand All @@ -78,6 +83,15 @@ protected double getVscLossMultiplier() {
return (1 - lossFactor1) * (1 - lossFactor2);
}

protected double getAbsActivePowerWithLosses(double boundedP, double lossController, double lossNonController) {
double lineInputPower = (1 - lossController) * Math.abs(boundedP);
return (1 - lossNonController) * (lineInputPower - getHvdcLineLosses(lineInputPower, r));
}

protected static double getHvdcLineLosses(double lineInputPower, double r) {
return HvdcUtils.getHvdcLineLosses(lineInputPower, 1, r);
}

@Override
public List<Variable<AcVariableType>> getVariables() {
return variables;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public HvdcAcEmulationSide1ActiveFlowEquationTerm(LfHvdc hvdc, LfBus bus1, LfBus
}

private double p1(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
double boundedP = boundedP(rawP);
return (isController(rawP) ? 1 : getVscLossMultiplier()) * boundedP;
double boundedP = boundedP(rawP(ph1, ph2));
// if converterStation1 is controller, then p1 is positive, otherwise it is negative
return isController(boundedP) ? boundedP : -getAbsActivePowerWithLosses(boundedP, lossFactor1, lossFactor2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change the check isController from rawP to boundedP?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because isController() only checks the sign of the power transit. I thought there was no need to store rawP instead of directly checking the sign of boundedP.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is equivalent but as Didier is not here (these lines about boundedP are from him) maybe keep former lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is equivalent (unless bounds of P are negative which they shouldn't). But I modified in order to keep the former lines.

}

private static boolean isController(double rawP) {
Expand All @@ -38,9 +38,9 @@ private boolean isInOperatingRange(double rawP) {
}

protected double dp1dph1(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
double rawP = rawP(ph1, ph2);
if (isInOperatingRange(rawP)) {
return (isController(rawP) ? 1 : getVscLossMultiplier()) * k;
return (isController(rawP) ? 1 : getVscLossMultiplier()) * k; // derivative of cable loss is neglected
} else {
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public HvdcAcEmulationSide2ActiveFlowEquationTerm(LfHvdc hvdc, LfBus bus1, LfBus
}

private double p2(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
double boundedP = boundedP(rawP);
return -(isController(rawP) ? 1 : getVscLossMultiplier()) * boundedP;
double boundedP = boundedP(rawP(ph1, ph2));
// if converterStation2 is controller, then p2 is positive, otherwise it is negative
return isController(boundedP) ? -boundedP : -getAbsActivePowerWithLosses(boundedP, lossFactor2, lossFactor1);
}

private boolean isController(double rawP) {
Expand All @@ -38,9 +38,9 @@ private boolean isInOperatingRange(double rawP) {
}

private double dp2dph1(double ph1, double ph2) {
double rawP = rawP(p0, k, ph1, ph2);
double rawP = rawP(ph1, ph2);
if (isInOperatingRange(rawP)) {
return -(isController(rawP) ? 1 : getVscLossMultiplier()) * k;
return -(isController(rawP) ? 1 : getVscLossMultiplier()) * k; // derivative of cable loss is neglected
} else {
return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/powsybl/openloadflow/network/LfHvdc.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public interface LfHvdc extends LfElement {

double getDroop();

double getR();

double getP0();

boolean isAcEmulation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public class LfHvdcImpl extends AbstractElement implements LfHvdc {

private double p0 = Double.NaN;

private double r = Double.NaN;

private double nominalV = Double.NaN;

private LfVscConverterStation converterStation1;

private LfVscConverterStation converterStation2;
Expand All @@ -52,6 +56,8 @@ public LfHvdcImpl(String id, LfBus bus1, LfBus bus2, LfNetwork network, HvdcLine
this.id = Objects.requireNonNull(id);
this.bus1 = bus1;
this.bus2 = bus2;
this.nominalV = hvdcLine.getNominalV();
this.r = hvdcLine.getR();
HvdcAngleDroopActivePowerControl droopControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class);
this.acEmulation = acEmulation && droopControl != null && droopControl.isEnabled();
if (this.acEmulation) {
Expand Down Expand Up @@ -128,6 +134,11 @@ public double getDroop() {
return droop / PerUnit.SB;
}

@Override
public double getR() {
return r / PerUnit.zb(nominalV);
}

@Override
public double getP0() {
return p0 / PerUnit.SB;
Expand Down
1 change: 1 addition & 0 deletions src/test/java/com/powsybl/openloadflow/EquationsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ void hvdcTest() {
Mockito.doReturn(false).when(hvdc).isDisabled();
Mockito.doReturn(DROOP).when(hvdc).getDroop();
Mockito.doReturn(P_0).when(hvdc).getP0();
Mockito.doReturn(0d).when(hvdc).getR();
Mockito.doReturn(Double.MAX_VALUE).when(hvdc).getPMaxFromCS1toCS2();
Mockito.doReturn(Double.MAX_VALUE).when(hvdc).getPMaxFromCS2toCS1();
LfVscConverterStationImpl station1 = Mockito.mock(LfVscConverterStationImpl.class, new RuntimeExceptionAnswer());
Expand Down
49 changes: 43 additions & 6 deletions src/test/java/com/powsybl/openloadflow/ac/AcLoadFlowVscTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,20 @@ void testHvdcPowerAcEmulation() {
LoadFlow.Runner loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
LoadFlowResult result = loadFlowRunner.run(network, new LoadFlowParameters());
assertTrue(result.isFullyConverged());
// AC Emulation takes into account cable loss
assertActivePowerEquals(198.158, network.getHvdcConverterStation("cs2").getTerminal());
assertActivePowerEquals(-193.799, network.getHvdcConverterStation("cs3").getTerminal());
assertActivePowerEquals(-304.359, network.getGenerator("g1").getTerminal());
assertActivePowerEquals(300.0, network.getLoad("l4").getTerminal());
}

@Test
void testHvdcPowerAcEmulationWithoutR() {
Network network = HvdcNetworkFactory.createHvdcLinkedByTwoLinesAndSwitch();
network.getHvdcLine("hvdc23").setR(0d); //Removing resistance to ignore cable loss
LoadFlow.Runner loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
LoadFlowResult result = loadFlowRunner.run(network, new LoadFlowParameters());
assertTrue(result.isFullyConverged());
assertActivePowerEquals(198.158, network.getHvdcConverterStation("cs2").getTerminal());
assertActivePowerEquals(-193.822, network.getHvdcConverterStation("cs3").getTerminal());
assertActivePowerEquals(-304.335, network.getGenerator("g1").getTerminal());
Expand All @@ -276,8 +290,10 @@ void testHvdcPowerAcEmulation() {
@Test
void testHvdcDirectionChangeAcEmulation() {
Network network = HvdcNetworkFactory.createHvdcInAcEmulationInSymetricNetwork();
network.getHvdcLine("hvdc12").setR(0.1d);
LoadFlow.Runner loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
LoadFlowResult result = loadFlowRunner.run(network, new LoadFlowParameters());
LoadFlowParameters parameters = new LoadFlowParameters().setHvdcAcEmulation(true);
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());

double pg2 = network.getGenerator("g2").getTerminal().getP();
Expand All @@ -293,10 +309,21 @@ void testHvdcDirectionChangeAcEmulation() {
assertTrue(pcs2 < 0, "Power delivered by cs2");
assertTrue(Math.abs(pcs1) > Math.abs(pcs2), "Loss at HVDC output");

// Test if removing line resistance increases the power transit
network.getHvdcLine("hvdc12").setR(0d);
result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
double pcs1r0 = network.getVscConverterStation("cs1").getTerminal().getP();
double pcs2r0 = network.getVscConverterStation("cs2").getTerminal().getP();
assertTrue(pcs1r0 > 0, "Power enters at cs1");
assertTrue(pcs2r0 < 0, "Power delivered by cs2");
assertTrue(Math.abs(pcs2r0 + pcs1r0) < Math.abs(pcs2 - pcs1)); // Check that loss with R=0 is lower than loss with R!=0

// Reverse power flow direction
network.getHvdcLine("hvdc12").setR(0.1d);
network.getGenerator("g2").setTargetP(5);
network.getGenerator("g1").setTargetP(0);
result = loadFlowRunner.run(network, new LoadFlowParameters());
result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());

pg2 = network.getGenerator("g2").getTerminal().getP();
Expand All @@ -312,6 +339,16 @@ void testHvdcDirectionChangeAcEmulation() {
assertTrue(pcs2 > 0, "Power enters at cs2");
assertTrue(pcs1 < 0, "Power delivered by cs1");
assertTrue(Math.abs(pcs2) > Math.abs(pcs1), "Loss at HVDC output");

// Test if removing line resistance increases the power transit in symetric network
network.getHvdcLine("hvdc12").setR(0d);
result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
pcs1r0 = network.getVscConverterStation("cs1").getTerminal().getP();
pcs2r0 = network.getVscConverterStation("cs2").getTerminal().getP();
assertTrue(pcs2r0 > 0, "Power enters at cs2");
assertTrue(pcs1r0 < 0, "Power delivered by cs1");
assertTrue(Math.abs(pcs2r0 + pcs1r0) < Math.abs(pcs2 - pcs1)); // Check that loss with R=0 is lower than loss with R!=0
}

@Test
Expand Down Expand Up @@ -464,7 +501,7 @@ void testAcEmuWithOperationalLimits() {

// Active flow capped at limit. Output has losses (due to VSC stations)
assertEquals(170, network.getHvdcConverterStation("cs2").getTerminal().getP(), DELTA_POWER);
assertEquals(-166.280, network.getHvdcConverterStation("cs3").getTerminal().getP(), DELTA_POWER);
assertEquals(-166.263, network.getHvdcConverterStation("cs3").getTerminal().getP(), DELTA_POWER);

// now invert power direction
HvdcAngleDroopActivePowerControl activePowerControl = network.getHvdcLine("hvdc23").getExtension(HvdcAngleDroopActivePowerControl.class);
Expand All @@ -473,7 +510,7 @@ void testAcEmuWithOperationalLimits() {
assertTrue(result.isFullyConverged());

// Active flow capped at other direction's limit. Output has losses (due to VSC stations)
assertEquals(-176.062, network.getHvdcConverterStation("cs2").getTerminal().getP(), DELTA_POWER);
assertEquals(-176.042, network.getHvdcConverterStation("cs2").getTerminal().getP(), DELTA_POWER);
assertEquals(180, network.getHvdcConverterStation("cs3").getTerminal().getP(), DELTA_POWER);
}

Expand All @@ -493,15 +530,15 @@ void testAcEmuAndPMax() {

// Active flow capped at limit. Output has losses (due to VSC stations)
assertActivePowerEquals(170, network.getHvdcConverterStation("cs2").getTerminal());
assertActivePowerEquals(-166.280, network.getHvdcConverterStation("cs3").getTerminal());
assertActivePowerEquals(-166.263, network.getHvdcConverterStation("cs3").getTerminal());

// now invert power direction
HvdcAngleDroopActivePowerControl activePowerControl = network.getHvdcLine("hvdc23").getExtension(HvdcAngleDroopActivePowerControl.class);
activePowerControl.setP0(-activePowerControl.getP0());
result = loadFlowRunner.run(network, p);
assertTrue(result.isFullyConverged());

assertActivePowerEquals(-166.280, network.getHvdcConverterStation("cs2").getTerminal());
assertActivePowerEquals(-166.263, network.getHvdcConverterStation("cs2").getTerminal());
assertActivePowerEquals(170, network.getHvdcConverterStation("cs3").getTerminal());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1293,9 +1293,9 @@ void testVSCLossAcEmulation() {
List<StateMonitor> monitors = createNetworkMonitors(network);
SecurityAnalysisResult result = runSecurityAnalysis(network, contingencies, monitors, new SecurityAnalysisParameters(),
operatorStrategies, actions, ReportNode.NO_OP);
assertEquals(193.822, result.getPreContingencyResult().getNetworkResult().getBranchResult("l34").getP1(), LoadFlowAssert.DELTA_POWER);
assertEquals(193.799, result.getPreContingencyResult().getNetworkResult().getBranchResult("l34").getP1(), LoadFlowAssert.DELTA_POWER);
assertEquals(0.0, getPostContingencyResult(result, "contingency").getNetworkResult().getBranchResult("l34").getP1(), LoadFlowAssert.DELTA_POWER);
assertEquals(193.822, getOperatorStrategyResult(result, "strategy").getNetworkResult().getBranchResult("l34").getP1(), LoadFlowAssert.DELTA_POWER);
assertEquals(193.799, getOperatorStrategyResult(result, "strategy").getNetworkResult().getBranchResult("l34").getP1(), LoadFlowAssert.DELTA_POWER);
}

@Test
Expand Down Expand Up @@ -1343,8 +1343,8 @@ void testHvdcDisconnectedThenConnectedByStrategy() {

OperatorStrategyResult operatorStrategyResult = getOperatorStrategyResult(result, "strategyL1");
assertEquals(198.158, operatorStrategyResult.getNetworkResult().getBranchResult("l12Bis").getP1(), DELTA_POWER);
assertEquals(193.822, operatorStrategyResult.getNetworkResult().getBranchResult("l34").getP1(), DELTA_POWER);
assertEquals(106.177, operatorStrategyResult.getNetworkResult().getBranchResult("l14").getP1(), DELTA_POWER);
assertEquals(193.799, operatorStrategyResult.getNetworkResult().getBranchResult("l34").getP1(), DELTA_POWER);
assertEquals(106.201, operatorStrategyResult.getNetworkResult().getBranchResult("l14").getP1(), DELTA_POWER);

// without AC emulation
SecurityAnalysisParameters parameters = new SecurityAnalysisParameters();
Expand Down