FlowDecompositionObserverTest.java
/*
* Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.flow_decomposition;
import com.powsybl.contingency.Contingency;
import com.powsybl.flow_decomposition.partitioners.ReferenceNodalInjectionComputer;
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.iidm.network.TwoSides;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.loadflow.LoadFlowResult;
import org.junit.jupiter.api.Test;
import java.util.*;
import static com.powsybl.flow_decomposition.partitioners.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;
/**
* @author Guillaume Verger {@literal <guillaume.verger at artelys.com>}
* @author Caio Luke {@literal <caio.luke at artelys.com>}
*/
class FlowDecompositionObserverTest {
private enum Event {
RUN_START,
RUN_DONE,
COMPUTING_BASE_CASE,
COMPUTING_CONTINGENCY,
COMPUTED_GLSK,
COMPUTED_NET_POSITIONS,
COMPUTED_NODAL_INJECTIONS_MATRIX,
COMPUTED_PTDF_MATRIX,
COMPUTED_PSDF_MATRIX,
COMPUTED_AC_NODAL_INJECTIONS,
COMPUTED_DC_NODAL_INJECTIONS,
COMPUTED_AC_FLOWS,
COMPUTED_DC_FLOWS,
COMPUTED_AC_CURRENTS,
COMPUTED_PRE_RESCALING_DECOMPOSED_FLOWS
}
private static final String BASE_CASE = "base-case";
/**
* ObserverReport gathers all observed events from the flow decomposition. It keeps the events occuring, and the
* matrices
*/
private static final class ObserverReport implements FlowDecompositionObserver {
private final List<Event> events = new LinkedList<>();
private String currentContingency = null;
private final ContingencyValue<List<Event>> eventsPerContingency = new ContingencyValue<>();
private Map<Country, Map<String, Double>> glsks;
private Map<Country, Double> netPositions;
private final ContingencyValue<LoadFlowResult> acLoadFlowResult = new ContingencyValue<>();
private final ContingencyValue<Boolean> acLoadFlowFallbackHasBeenActivated = new ContingencyValue<>();
private final ContingencyValue<LoadFlowResult> dcLoadFlowResult = new ContingencyValue<>();
private final ContingencyValue<Map<String, Map<String, Double>>> nodalInjections = new ContingencyValue<>();
private final ContingencyValue<Map<String, Map<String, Double>>> ptdfs = new ContingencyValue<>();
private final ContingencyValue<Map<String, Map<String, Double>>> psdfs = new ContingencyValue<>();
private final ContingencyValue<Map<String, Double>> acNodalInjections = new ContingencyValue<>();
private final ContingencyValue<Map<String, Double>> dcNodalInjections = new ContingencyValue<>();
private final ContingencyValue<Map<String, Double>> acFlowsTerminal1 = new ContingencyValue<>();
private final ContingencyValue<Map<String, Double>> acFlowsTerminal2 = new ContingencyValue<>();
private final ContingencyValue<Map<String, Double>> dcFlows = new ContingencyValue<>();
private final ContingencyValue<Map<String, Double>> acCurrentsTerminal1 = new ContingencyValue<>();
private final ContingencyValue<Map<String, Double>> acCurrentsTerminal2 = new ContingencyValue<>();
private final ContingencyValue<Map<String, DecomposedFlow>> preRescalingDecomposedFlows = new ContingencyValue<>();
public List<Event> allEvents() {
return events;
}
public List<Event> eventsForBaseCase() {
return eventsPerContingency.forBaseCase();
}
public List<Event> eventsForContingency(String contingencyId) {
return eventsPerContingency.forContingency(contingencyId);
}
@Override
public void runStart() {
addEvent(Event.RUN_START);
}
@Override
public void runDone() {
addEvent(Event.RUN_DONE);
}
@Override
public void computingBaseCase() {
currentContingency = BASE_CASE;
addEvent(Event.COMPUTING_BASE_CASE);
}
@Override
public void computingContingency(String contingencyId) {
currentContingency = contingencyId;
addEvent(Event.COMPUTING_CONTINGENCY);
}
@Override
public void computedGlsk(Map<Country, Map<String, Double>> glsks) {
addEvent(Event.COMPUTED_GLSK);
this.glsks = glsks;
}
@Override
public void computedNetPositions(Map<Country, Double> netPositions) {
addEvent(Event.COMPUTED_NET_POSITIONS);
this.netPositions = netPositions;
}
@Override
public void computedNodalInjectionsMatrix(Map<String, Map<String, Double>> nodalInjections) {
addEvent(Event.COMPUTED_NODAL_INJECTIONS_MATRIX);
this.nodalInjections.put(currentContingency, nodalInjections);
}
@Override
public void computedPtdfMatrix(Map<String, Map<String, Double>> ptdfMatrix) {
addEvent(Event.COMPUTED_PTDF_MATRIX);
this.ptdfs.put(currentContingency, ptdfMatrix);
}
@Override
public void computedPsdfMatrix(Map<String, Map<String, Double>> psdfMatrix) {
addEvent(Event.COMPUTED_PSDF_MATRIX);
this.psdfs.put(currentContingency, psdfMatrix);
}
@Override
public void computedAcLoadFlowResults(Network network, LoadFlowResult loadFlowResult, boolean fallbackHasBeenActivated) {
this.acLoadFlowResult.put(currentContingency, loadFlowResult);
this.acLoadFlowFallbackHasBeenActivated.put(currentContingency, fallbackHasBeenActivated);
computedAcNodalInjections(network);
computedAcFlowsTerminal1(network, fallbackHasBeenActivated);
computedAcFlowsTerminal2(network, fallbackHasBeenActivated);
computedAcCurrentsTerminal1(network, fallbackHasBeenActivated);
computedAcCurrentsTerminal2(network, fallbackHasBeenActivated);
}
@Override
public void computedDcLoadFlowResults(Network network, LoadFlowResult loadFlowResult) {
this.dcLoadFlowResult.put(currentContingency, loadFlowResult);
computedDcNodalInjections(network);
computedDcFlows(network);
}
private void computedAcNodalInjections(Network network) {
addEvent(Event.COMPUTED_AC_NODAL_INJECTIONS);
Map<String, Double> injections = new ReferenceNodalInjectionComputer().run(NetworkUtil.getNodeList(network));
this.acNodalInjections.put(currentContingency, injections);
}
private void computedDcNodalInjections(Network network) {
addEvent(Event.COMPUTED_DC_NODAL_INJECTIONS);
Map<String, Double> injections = new ReferenceNodalInjectionComputer().run(NetworkUtil.getNodeList(network));
this.dcNodalInjections.put(currentContingency, injections);
}
private void computedAcFlowsTerminal1(Network network, boolean fallbackHasBeenActivated) {
addEvent(Event.COMPUTED_AC_FLOWS);
Map<String, Double> flows = FlowComputerUtils.calculateAcTerminalReferenceFlows(network.getBranchStream().toList(), fallbackHasBeenActivated, TwoSides.ONE);
this.acFlowsTerminal1.put(currentContingency, flows);
}
private void computedAcFlowsTerminal2(Network network, boolean fallbackHasBeenActivated) {
addEvent(Event.COMPUTED_AC_FLOWS);
Map<String, Double> flows = FlowComputerUtils.calculateAcTerminalReferenceFlows(network.getBranchStream().toList(), fallbackHasBeenActivated, TwoSides.TWO);
this.acFlowsTerminal2.put(currentContingency, flows);
}
private void computedDcFlows(Network network) {
addEvent(Event.COMPUTED_DC_FLOWS);
Map<String, Double> flows = FlowComputerUtils.getTerminalReferenceFlow(network.getBranchStream().toList(), TwoSides.ONE);
this.dcFlows.put(currentContingency, flows);
}
private void computedAcCurrentsTerminal1(Network network, boolean fallbackHasBeenActivated) {
addEvent(Event.COMPUTED_AC_CURRENTS);
Map<String, Double> currents = FlowComputerUtils.calculateAcTerminalCurrents(network.getBranchStream().toList(), fallbackHasBeenActivated, TwoSides.ONE);
this.acCurrentsTerminal1.put(currentContingency, currents);
}
private void computedAcCurrentsTerminal2(Network network, boolean fallbackHasBeenActivated) {
addEvent(Event.COMPUTED_AC_CURRENTS);
Map<String, Double> currents = FlowComputerUtils.calculateAcTerminalCurrents(network.getBranchStream().toList(), fallbackHasBeenActivated, TwoSides.TWO);
this.acCurrentsTerminal2.put(currentContingency, currents);
}
@Override
public void computedPreRescalingDecomposedFlows(DecomposedFlow decomposedFlow) {
addEvent(Event.COMPUTED_PRE_RESCALING_DECOMPOSED_FLOWS);
this.preRescalingDecomposedFlows.put(currentContingency, Map.of(decomposedFlow.getBranchId(), decomposedFlow));
}
private void addEvent(Event e) {
if (currentContingency != null) {
eventsPerContingency.putIfAbsent(currentContingency, new LinkedList<>());
eventsPerContingency.forContingency(currentContingency).add(e);
}
events.add(e);
}
}
@Test
void testNStateN1AndN2PostContingencyState() {
String networkFileName = "19700101_0000_FO4_UX1.uct";
String branchId = "DB000011 DF000011 1";
String contingencyElementId1 = "FB000011 FD000011 1";
String contingencyElementId2 = "FB000021 FD000021 1";
String contingencyId1 = "DD000011 DF000011 1";
String contingencyId2 = "FB000011 FD000011 1_FB000021 FD000021 1";
Network network = TestUtils.importNetwork(networkFileName);
Map<String, Set<String>> contingencies = Map.ofEntries(
Map.entry(contingencyId1, Set.of(contingencyId1)),
Map.entry(contingencyId2, Set.of(contingencyElementId1, contingencyElementId2)));
XnecProvider xnecProvider = XnecProviderByIds.builder()
.addContingencies(contingencies)
.addNetworkElementsAfterContingencies(
Set.of(branchId),
Set.of(contingencyId1, contingencyId2))
.addNetworkElementsOnBasecase(Set.of(branchId))
.build();
var flowDecompositionParameters = FlowDecompositionParameters.load();
FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters);
var report = new ObserverReport();
flowComputer.addObserver(report);
flowComputer.run(xnecProvider, network);
validateObserverReportLoadFlowResult(report, List.of(BASE_CASE, contingencyId1, contingencyId2));
validateObserverReportEvents(report, List.of(BASE_CASE, contingencyId1, contingencyId2), Boolean.TRUE);
validateObserverReportGlsk(report, Set.of("DB000011_generator", "DF000011_generator"));
validateObserverReportNetPositions(report, Set.of(Country.BE, Country.DE, Country.FR));
Set<String> xnecNodes = Set.of(
"BB000021_load",
"BF000011_generator",
"BF000021_load",
"DB000011_generator",
"DD000011_load",
"DF000011_generator",
"FB000021_generator",
"FB000022_load",
"FD000011_load",
"FF000011_generator",
"XES00011 FD000011 1",
"XNL00011 BB000011 1");
validateObserverReportNodalInjections(
report,
List.of(BASE_CASE, contingencyId1, contingencyId2),
xnecNodes,
"BB000021_load",
Set.of("Allocated Flow", "Loop Flow from BE"));
validateObserverReportPtdfs(report, List.of(BASE_CASE, contingencyId1, contingencyId2), xnecNodes, branchId);
validateObserverReportPsdfs(report, List.of(BASE_CASE, contingencyId1, contingencyId2), branchId, Set.of(
"BF000011 BF000012 1"));
var allBranches = Set.of(
"BB000011 BB000021 1",
"BB000011 BD000011 1",
"BB000011 BF000012 1",
"BB000021 BD000021 1",
"BB000021 BF000021 1",
"BD000011 BD000021 1",
"BD000011 BF000011 1",
"BD000021 BF000021 1",
"BF000011 BF000012 1",
"BF000011 BF000021 1",
"DB000011 DD000011 1",
"DB000011 DF000011 1",
"DD000011 DF000011 1",
"FB000011 FB000022 1",
"FB000011 FD000011 1",
"FB000011 FF000011 1",
"FB000021 FD000021 1",
"FD000011 FD000021 1",
"FD000011 FF000011 1",
"FD000011 FF000011 2",
"XBD00011 BD000011 1 + XBD00011 DB000011 1",
"XBD00012 BD000011 1 + XBD00012 DB000011 1",
"XBF00011 BF000011 1 + XBF00011 FB000011 1",
"XBF00021 BF000021 1 + XBF00021 FB000021 1",
"XBF00022 BF000021 1 + XBF00022 FB000022 1",
"XDF00011 DF000011 1 + XDF00011 FD000011 1");
validateObserverReportFlows(report, List.of(BASE_CASE, contingencyId1, contingencyId2), allBranches);
var decomposedFlowBranches = Set.of("DB000011 DF000011 1");
validateObserverReportDecomposedFlows(report, List.of(contingencyId1), decomposedFlowBranches);
}
@Test
void testNStateN1LoadContingencyState() {
String networkFileName = "19700101_0000_FO4_UX1.uct";
String branchId = "DB000011 DF000011 1";
String contingencyId1 = "FD000011_load";
Network network = TestUtils.importNetwork(networkFileName);
Contingency contingency = Contingency.builder(contingencyId1).addLoad(contingencyId1).build();
XnecProvider xnecProvider = XnecProviderByIds.builder()
.addContingency(contingency)
.addNetworkElementsAfterContingencies(Set.of(branchId), Set.of(contingencyId1))
.build();
var flowDecompositionParameters = FlowDecompositionParameters.load();
FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters);
var report = new ObserverReport();
flowComputer.addObserver(report);
flowComputer.run(xnecProvider, network);
validateObserverReportLoadFlowResult(report, List.of(contingencyId1));
validateObserverReportEvents(report, List.of(contingencyId1), Boolean.FALSE);
validateObserverReportGlsk(report, Set.of("DB000011_generator", "DF000011_generator"));
validateObserverReportNetPositions(report, Set.of(Country.BE, Country.DE, Country.FR));
Set<String> xnecNodes = Set.of(
"BB000021_load",
"BF000011_generator",
"BF000021_load",
"DB000011_generator",
"DD000011_load",
"DF000011_generator",
"FB000021_generator",
"FB000022_load",
"FF000011_generator",
"XES00011 FD000011 1",
"XNL00011 BB000011 1");
validateObserverReportNodalInjections(
report,
List.of(contingencyId1),
xnecNodes,
"BF000021_load",
Set.of("Allocated Flow", "Loop Flow from BE"));
validateObserverReportPtdfs(report, List.of(contingencyId1), xnecNodes, branchId);
validateObserverReportPsdfs(report, List.of(contingencyId1), branchId, Set.of("BF000011 BF000012 1"));
var allBranches = Set.of(
"BB000011 BB000021 1",
"BB000011 BD000011 1",
"BB000011 BF000012 1",
"BB000021 BD000021 1",
"BB000021 BF000021 1",
"BD000011 BD000021 1",
"BD000011 BF000011 1",
"BD000021 BF000021 1",
"BF000011 BF000012 1",
"BF000011 BF000021 1",
"DB000011 DD000011 1",
"DB000011 DF000011 1",
"DD000011 DF000011 1",
"FB000011 FB000022 1",
"FB000011 FD000011 1",
"FB000011 FF000011 1",
"FB000021 FD000021 1",
"FD000011 FD000021 1",
"FD000011 FF000011 1",
"FD000011 FF000011 2",
"XBD00011 BD000011 1 + XBD00011 DB000011 1",
"XBD00012 BD000011 1 + XBD00012 DB000011 1",
"XBF00011 BF000011 1 + XBF00011 FB000011 1",
"XBF00021 BF000021 1 + XBF00021 FB000021 1",
"XBF00022 BF000021 1 + XBF00022 FB000022 1",
"XDF00011 DF000011 1 + XDF00011 FD000011 1");
validateObserverReportFlows(report, List.of(contingencyId1), allBranches);
var decomposedFlowBranches = Set.of("DB000011 DF000011 1");
validateObserverReportDecomposedFlows(report, List.of(contingencyId1), decomposedFlowBranches);
}
private static void validateObserverReportLoadFlowResult(ObserverReport report, List<String> contingencyIds) {
for (var contingencyId : contingencyIds) {
LoadFlowResult contingencyAcLoadFlowResult = report.acLoadFlowResult.forContingency(contingencyId);
boolean contingencyAcLoadFlowFallbackHasBeenActivated = report.acLoadFlowFallbackHasBeenActivated.forContingency(contingencyId);
LoadFlowResult contingencyDcLoadFlowResult = report.dcLoadFlowResult.forContingency(contingencyId);
assertTrue(contingencyAcLoadFlowResult.isFullyConverged());
assertFalse(contingencyAcLoadFlowFallbackHasBeenActivated);
assertTrue(contingencyDcLoadFlowResult.isFullyConverged());
}
}
private static void validateObserverReportEvents(ObserverReport report, List<String> contingencyIds, Boolean isBaseCaseExecuted) {
assertEventsFired(report.allEvents(), Event.COMPUTED_GLSK, Event.COMPUTED_NET_POSITIONS);
if (isBaseCaseExecuted) {
assertEventsFired(report.eventsForBaseCase(),
Event.COMPUTED_AC_FLOWS,
Event.COMPUTED_AC_NODAL_INJECTIONS,
Event.COMPUTED_DC_FLOWS,
Event.COMPUTED_DC_NODAL_INJECTIONS,
Event.COMPUTED_NODAL_INJECTIONS_MATRIX,
Event.COMPUTED_PTDF_MATRIX,
Event.COMPUTED_PSDF_MATRIX,
Event.COMPUTED_PRE_RESCALING_DECOMPOSED_FLOWS);
}
for (var contingencyId : contingencyIds) {
assertEventsFired(report.eventsForContingency(contingencyId),
Event.COMPUTED_AC_FLOWS,
Event.COMPUTED_AC_NODAL_INJECTIONS,
Event.COMPUTED_DC_FLOWS,
Event.COMPUTED_DC_NODAL_INJECTIONS,
Event.COMPUTED_NODAL_INJECTIONS_MATRIX,
Event.COMPUTED_PTDF_MATRIX,
Event.COMPUTED_PSDF_MATRIX,
Event.COMPUTED_PRE_RESCALING_DECOMPOSED_FLOWS);
}
}
private static void validateObserverReportGlsk(ObserverReport report, Set<String> expectedResources) {
assertEquals(Set.of(Country.BE, Country.DE, Country.FR), report.glsks.keySet());
assertEquals(expectedResources, report.glsks.get(Country.DE).keySet());
}
private static void validateObserverReportNetPositions(ObserverReport report, Set<Country> expectedCountries) {
assertEquals(expectedCountries, report.netPositions.keySet());
}
private static void validateObserverReportNodalInjections(ObserverReport report, List<String> caseIds,
Set<String> xnecNodes, String nodeId, Set<String> expectedFields) {
for (var contingencyId : caseIds) {
assertEquals(xnecNodes, report.nodalInjections.forContingency(contingencyId).keySet());
assertEquals(xnecNodes, report.acNodalInjections.forContingency(contingencyId).keySet());
assertEquals(xnecNodes, report.dcNodalInjections.forContingency(contingencyId).keySet());
assertEquals(expectedFields, report.nodalInjections.forContingency(contingencyId).get(nodeId).keySet());
}
}
private static void validateObserverReportPtdfs(ObserverReport report, List<String> caseIds, Set<String> xnecNodes, String branchId) {
for (var contingencyId : caseIds) {
var branches = Set.of(branchId);
assertEquals(branches, report.ptdfs.forContingency(contingencyId).keySet());
assertEquals(xnecNodes, report.ptdfs.forContingency(contingencyId).get(branchId).keySet());
}
}
private static void validateObserverReportPsdfs(ObserverReport report, List<String> caseIds, String branchId, Set<String> pstNodes) {
for (var contingencyId : caseIds) {
var branches = Set.of(branchId);
assertEquals(branches, report.psdfs.forContingency(contingencyId).keySet());
assertEquals(pstNodes, report.psdfs.forContingency(contingencyId).get(branchId).keySet(), "contingency = " + contingencyId);
}
}
private static void validateObserverReportFlows(ObserverReport report, List<String> caseIds, Set<String> allBranches) {
for (var contingencyId : caseIds) {
assertEquals(allBranches, report.acFlowsTerminal1.forContingency(contingencyId).keySet());
assertEquals(allBranches, report.dcFlows.forContingency(contingencyId).keySet());
}
}
private static void validateObserverReportDecomposedFlows(ObserverReport report, List<String> caseIds, Set<String> allBranches) {
for (var contingencyId : caseIds) {
assertEquals(allBranches, report.preRescalingDecomposedFlows.forContingency(contingencyId).keySet());
}
}
@Test
void testRemoveObserver() {
String networkFileName = "19700101_0000_FO4_UX1.uct";
String branchId = "DB000011 DF000011 1";
Network network = TestUtils.importNetwork(networkFileName);
XnecProvider xnecProvider = XnecProviderByIds.builder()
.addNetworkElementsOnBasecase(Set.of(branchId))
.build();
var flowDecompositionParameters = FlowDecompositionParameters.load();
FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters);
var reportInserted = new ObserverReport();
flowComputer.addObserver(reportInserted);
var reportRemoved = new ObserverReport();
flowComputer.addObserver(reportRemoved);
flowComputer.removeObserver(reportRemoved);
flowComputer.run(xnecProvider, network);
assertFalse(reportInserted.allEvents().isEmpty());
assertTrue(reportRemoved.allEvents().isEmpty());
}
@Test
void testObserverWithEnableLossesCompensation() {
String networkFileName = "19700101_0000_FO4_UX1.uct";
String branchId = "DB000011 DF000011 1";
Network network = TestUtils.importNetwork(networkFileName);
XnecProvider xnecProvider = XnecProviderByIds.builder()
.addNetworkElementsOnBasecase(Set.of(branchId))
.build();
var flowDecompositionParameters = FlowDecompositionParameters.load().setEnableLossesCompensation(true);
FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters);
var report = new ObserverReport();
flowComputer.addObserver(report);
flowComputer.run(xnecProvider, network);
// there are no losses in acNodalInjection
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);
missing.removeAll(firedEvents);
assertTrue(missing.isEmpty(), () -> "Missing events: " + missing);
}
private static final class ContingencyValue<T> {
private final Map<String, T> values = new HashMap<>();
public void put(String contingencyId, T value) {
values.put(contingencyId, value);
}
public void putIfAbsent(String contingencyId, T value) {
values.putIfAbsent(contingencyId, value);
}
public T forContingency(String contingencyId) {
return values.get(contingencyId);
}
public T forBaseCase() {
return values.get(BASE_CASE);
}
}
}