Skip to content

Commit

Permalink
Improve docs and tests for PTDF calculation in Flow Decomposition (#159)
Browse files Browse the repository at this point in the history
Improve docs and tests about PTDF calculation respecting the flow sign convention.

Signed-off-by: Caio Luke <caioluke97@gmail.com>
  • Loading branch information
caioluke authored Oct 10, 2024
1 parent c24ca22 commit 9b05615
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ Observers are notified of the following events:

Note that these observers are meant to be used for testing purposes only.
Using observers impacts calculation performance and therefore are not suitable in production environment.

Note also that PTDFs and PSDFs respect the [flow sign convention](../flow_decomposition/flow-decomposition-outputs.md#flow-sign-conventions).
2 changes: 1 addition & 1 deletion docs/flow_decomposition/flow-decomposition-outputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ For each network element of interest, flow decomposition outputs contain the fol

On one hand, the reference flows are oriented from side 1 to side 2 of the associated IIDM branch. A positive reference flow implies
a flow from side 1 to side 2, while a negative one means a flow from side 2 to side 1.
For coherence and simplicity purposes, the entire algorithm (except [proportional rescaling](../flow_decomposition/algorithm-description.md#proportional-rescaling)) is based on the side 1.
For coherence and simplicity purposes, the entire algorithm (except some [rescaling methods](../flow_decomposition/algorithm-description.md#flow-parts-rescaling)) is based on the side 1.

On the other hand, all flow parts (allocated flow, internal flow, loop flows and PST flow) are oriented in the branch
flow convention. A positive flow part tends to increase the absolute flow on the branch (i.e. a burdening flow), while a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,16 @@ private void fillSensitivityAnalysisResult(List<Pair<String, String>> factors, S
runSensitivityAnalysis(network, factorReader, valueWriter, EMPTY_SENSITIVITY_VARIABLE_SETS);
}

public static double respectFlowSignConvention(double ptdfValue, double referenceFlow) {
return referenceFlow < 0 ? -ptdfValue : ptdfValue;
}

private static SensitivityResultWriter getSensitivityResultWriter(List<Pair<String, String>> factors, SparseMatrixWithIndexesTriplet sensitivityMatrixTriplet) {
return new SensitivityResultWriter() {
@Override
public void writeSensitivityValue(int factorIndex, int contingencyIndex, double value, double functionReference) {
Pair<String, String> factor = factors.get(factorIndex);
double referenceOrientedSensitivity = functionReference < 0 ? -value : value;
sensitivityMatrixTriplet.addItem(factor.getFirst(), factor.getSecond(), referenceOrientedSensitivity);
sensitivityMatrixTriplet.addItem(factor.getFirst(), factor.getSecond(), respectFlowSignConvention(value, functionReference));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
package com.powsybl.flow_decomposition;

import com.powsybl.contingency.Contingency;
import com.powsybl.flow_decomposition.xnec_provider.XnecProviderAllBranches;
import com.powsybl.flow_decomposition.xnec_provider.XnecProviderByIds;
import com.powsybl.iidm.network.Country;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.LoadFlowParameters;
import org.junit.jupiter.api.Test;

import java.util.*;

import static com.powsybl.flow_decomposition.SensitivityAnalyser.respectFlowSignConvention;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
Expand Down Expand Up @@ -447,6 +450,50 @@ void testObserverWithEnableLossesCompensation() {
report.acNodalInjections.forBaseCase().forEach((inj, p) -> assertFalse(inj.startsWith(LossesCompensator.LOSSES_ID_PREFIX)));
}

@Test
void testPtdfsStayTheSameWithLossCompensationAndSlackDistributionOnLoads() {
String networkFileName = "ptdf_instability.xiidm";
Network network = TestUtils.importNetwork(networkFileName);
XnecProvider xnecProvider = new XnecProviderAllBranches();

LoadFlowParameters loadFlowParameters = new LoadFlowParameters();
loadFlowParameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD).setDistributedSlack(true);
FlowDecompositionParameters flowDecompositionParameters = new FlowDecompositionParameters().setEnableLossesCompensation(false);

// Without loss compensation
FlowDecompositionComputer flowDecompositionComputer1 = new FlowDecompositionComputer(flowDecompositionParameters, loadFlowParameters);
ObserverReport report1 = new ObserverReport();
flowDecompositionComputer1.addObserver(report1);
flowDecompositionComputer1.run(xnecProvider, network);

// With loss compensation
flowDecompositionParameters.setEnableLossesCompensation(true);
FlowDecompositionComputer flowDecompositionComputer2 = new FlowDecompositionComputer(flowDecompositionParameters, loadFlowParameters);
ObserverReport report2 = new ObserverReport();
flowDecompositionComputer2.addObserver(report2);
flowDecompositionComputer2.run(xnecProvider, network);

// Intermediate results
// With loss compensation
var dcFlows1 = report1.dcFlows.forBaseCase();
var ptdfs1 = report1.ptdfs.forBaseCase();

// Without loss compensation
var dcFlows2 = report2.dcFlows.forBaseCase();
var ptdfs2 = report2.ptdfs.forBaseCase();

// Ensure that ptdfs are the same with or without loss compensation
ptdfs1.forEach((branchId, ptdfInjections1) -> {
var ptdfInjections2 = ptdfs2.get(branchId);
ptdfInjections1.forEach((injectionId, ptdfValue1) -> {
Double ptdfValue2 = ptdfInjections2.get(injectionId);
double ptdfSignConvention1 = respectFlowSignConvention(ptdfValue1, dcFlows1.get(branchId));
double ptdfSignConvention2 = respectFlowSignConvention(ptdfValue2, dcFlows2.get(branchId));
assertEquals(ptdfSignConvention1, ptdfSignConvention2, 1E-2);
});
});
}

private static void assertEventsFired(Collection<Event> firedEvents, Event... expectedEvents) {
var missing = new HashSet<Event>();
Collections.addAll(missing, expectedEvents);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Map;
import java.util.stream.Collectors;

import static com.powsybl.flow_decomposition.SensitivityAnalyser.respectFlowSignConvention;
import static com.powsybl.flow_decomposition.TestUtils.importNetwork;
import static org.junit.jupiter.api.Assertions.assertEquals;

Expand Down Expand Up @@ -121,4 +122,18 @@ void testThatPsdfAreWellComputed() {
assertEquals(-420.042573, psdf.get(x1).get(pst), EPSILON);
assertEquals(420.042573, psdf.get(x2).get(pst), EPSILON);
}

@Test
void testRespectFlowSignConventionIsAnInvolution() {
assertEquals(0.5, applyRespectFlowSignConventionTwice(0.5, 1.0));
assertEquals(0.5, applyRespectFlowSignConventionTwice(0.5, 0.0));
assertEquals(0.5, applyRespectFlowSignConventionTwice(0.5, -1.0));
assertEquals(-0.5, applyRespectFlowSignConventionTwice(-0.5, 1.0));
assertEquals(-0.5, applyRespectFlowSignConventionTwice(-0.5, 0.0));
assertEquals(-0.5, applyRespectFlowSignConventionTwice(-0.5, -1.0));
}

private double applyRespectFlowSignConventionTwice(double ptdfValue, double referenceFlow) {
return respectFlowSignConvention(respectFlowSignConvention(ptdfValue, referenceFlow), referenceFlow);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_13" xmlns:ld="http://www.itesla_project.eu/schema/iidm/ext/load_detail/1_0" xmlns:refpri="http://www.powsybl.org/schema/iidm/ext/reference_priorities/1_0" xmlns:apc="http://www.powsybl.org/schema/iidm/ext/active_power_control/1_2" id="ptdf-instability" caseDate="2024-09-30T12:53:22.809Z" forecastDistance="0" sourceFormat="code" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="b1_UY_s" country="UY">
<iidm:voltageLevel id="b1_UY_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b1_UY" name="Bus 1 UY"/>
</iidm:busBreakerTopology>
<iidm:danglingLine id="l_UYAR_UY" name="Halfline UY - AR" p0="0.0" q0="0.0" r="1.0" x="5.0" g="0.0" b="1.0E-4" bus="b1_UY" connectableBus="b1_UY" pairingKey="t_UY_AR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b1_PY_s" country="PY">
<iidm:voltageLevel id="b1_PY_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b1_PY" name="Bus 1 PY"/>
</iidm:busBreakerTopology>
<iidm:generator id="gen_PY" name="Gen PY" energySource="OTHER" minP="-1.0" maxP="1.0" voltageRegulatorOn="true" targetP="0.0" targetV="220.0" targetQ="0.0" bus="b1_PY" connectableBus="b1_PY"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b2_PY_s" country="PY">
<iidm:voltageLevel id="b2_PY_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b2_PY" name="Bus 2 PY"/>
</iidm:busBreakerTopology>
<iidm:danglingLine id="l_PYBR_PY" name="Halfline PY - BR" p0="0.0" q0="0.0" r="1.0" x="5.0" g="0.0" b="1.0E-4" bus="b2_PY" connectableBus="b2_PY" pairingKey="t_PY_BR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b1_AR_s" country="AR">
<iidm:voltageLevel id="b1_AR_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b1_AR" name="Bus 1 AR"/>
</iidm:busBreakerTopology>
<iidm:generator id="gen_AR" name="Gen AR" energySource="OTHER" minP="0.0" maxP="105.0" voltageRegulatorOn="true" targetP="104.5" targetV="220.0" targetQ="0.0" bus="b1_AR" connectableBus="b1_AR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b2_AR_s" country="AR">
<iidm:voltageLevel id="b2_AR_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b2_AR" name="Bus 2 AR"/>
</iidm:busBreakerTopology>
<iidm:load id="load_AR" name="Load AR" loadType="UNDEFINED" p0="100.0" q0="0.0" bus="b2_AR" connectableBus="b2_AR"/>
<iidm:danglingLine id="l_UYAR_AR" name="Halfline AR - UY" p0="0.0" q0="0.0" r="1.0" x="5.0" g="0.0" b="1.0E-4" bus="b2_AR" connectableBus="b2_AR" pairingKey="t_UY_AR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b3_AR_s" country="AR">
<iidm:voltageLevel id="b3_AR_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b3_AR" name="Bus 3 AR"/>
</iidm:busBreakerTopology>
<iidm:danglingLine id="l_ARBR_AR" name="Halfline AR - BR" p0="0.0" q0="0.0" r="1.0" x="5.0" g="0.0" b="1.0E-4" bus="b3_AR" connectableBus="b3_AR" pairingKey="t_AR_BR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b1_BR_s" country="BR">
<iidm:voltageLevel id="b1_BR_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b1_BR" name="Bus 1 BR"/>
</iidm:busBreakerTopology>
<iidm:danglingLine id="l_PYBR_BR" name="Halfline BR - PY" p0="0.0" q0="0.0" r="1.0" x="5.0" g="0.0" b="1.0E-4" bus="b1_BR" connectableBus="b1_BR" pairingKey="t_PY_BR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b2_BR_s" country="BR">
<iidm:voltageLevel id="b2_BR_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b2_BR" name="Bus 2 BR"/>
</iidm:busBreakerTopology>
<iidm:load id="load_BR" name="Load BR" loadType="UNDEFINED" p0="100.0" q0="0.0" bus="b2_BR" connectableBus="b2_BR"/>
<iidm:danglingLine id="l_ARBR_BR" name="Halfline BR - AR" p0="0.0" q0="0.0" r="1.0" x="5.0" g="0.0" b="1.0E-4" bus="b2_BR" connectableBus="b2_BR" pairingKey="t_AR_BR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="b3_BR_s" country="BR">
<iidm:voltageLevel id="b3_BR_vl" nominalV="220.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="b3_BR" name="Bus 3 BR"/>
</iidm:busBreakerTopology>
<iidm:generator id="gen_BR" name="Gen BR" energySource="OTHER" minP="0.0" maxP="105.0" voltageRegulatorOn="true" targetP="100.0" targetV="220.0" targetQ="0.0" bus="b3_BR" connectableBus="b3_BR"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:line id="l_1PY_2PY" name="Line PY 1 - PY 2" r="2.0" x="10.0" g1="0.0" b1="1.0E-4" g2="0.0" b2="1.0E-4" voltageLevelId1="b1_PY_vl" bus1="b1_PY" connectableBus1="b1_PY" voltageLevelId2="b2_PY_vl" bus2="b2_PY" connectableBus2="b2_PY"/>
<iidm:line id="l_2AR_1AR" name="Line AR 2 - AR 1" r="2.0" x="10.0" g1="0.0" b1="1.0E-4" g2="0.0" b2="1.0E-4" voltageLevelId1="b2_AR_vl" bus1="b2_AR" connectableBus1="b2_AR" voltageLevelId2="b1_AR_vl" bus2="b1_AR" connectableBus2="b1_AR"/>
<iidm:line id="l_3AR_2AR" name="Line AR 3 - AR 2" r="2.0" x="10.0" g1="0.0" b1="1.0E-4" g2="0.0" b2="1.0E-4" voltageLevelId1="b3_AR_vl" bus1="b3_AR" connectableBus1="b3_AR" voltageLevelId2="b2_AR_vl" bus2="b2_AR" connectableBus2="b2_AR"/>
<iidm:line id="l_2BR_3BR" name="Line BR 2 - BR 3" r="2.0" x="10.0" g1="0.0" b1="1.0E-4" g2="0.0" b2="1.0E-4" voltageLevelId1="b2_BR_vl" bus1="b2_BR" connectableBus1="b2_BR" voltageLevelId2="b3_BR_vl" bus2="b3_BR" connectableBus2="b3_BR"/>
<iidm:line id="l_1BR_2BR" name="Line BR 1 - BR 2" r="2.0" x="10.0" g1="0.0" b1="1.0E-4" g2="0.0" b2="1.0E-4" voltageLevelId1="b1_BR_vl" bus1="b1_BR" connectableBus1="b1_BR" voltageLevelId2="b2_BR_vl" bus2="b2_BR" connectableBus2="b2_BR"/>
<iidm:tieLine id="t_PY_BR" name="Tieline PY - BR" danglingLineId1="l_PYBR_PY" danglingLineId2="l_PYBR_BR"/>
<iidm:tieLine id="t_UY_AR" name="Tieline UY - AR" danglingLineId1="l_UYAR_UY" danglingLineId2="l_UYAR_AR"/>
<iidm:tieLine id="t_AR_BR" name="Tieline AR - BR" danglingLineId1="l_ARBR_AR" danglingLineId2="l_ARBR_BR"/>
</iidm:network>

0 comments on commit 9b05615

Please sign in to comment.