ExporterTest.java
/**
* Copyright (c) 2017, RTE (http://www.rte-france.com)
* 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.security.converter;
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.commons.PowsyblException;
import com.powsybl.contingency.*;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.security.*;
import com.powsybl.security.condition.AtLeastOneViolationCondition;
import com.powsybl.security.extensions.ActivePowerExtension;
import com.powsybl.security.extensions.CurrentExtension;
import com.powsybl.security.extensions.VoltageExtension;
import com.powsybl.security.json.SecurityAnalysisResultDeserializer;
import com.powsybl.security.strategy.OperatorStrategy;
import com.powsybl.security.results.*;
import com.powsybl.security.strategy.ConditionalActions;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Mathieu Bague {@literal <mathieu.bague at rte-france.com>}
*/
class ExporterTest extends AbstractSerDeTest {
private static final Network NETWORK = EurostagTutorialExample1Factory.createWithFixedCurrentLimits();
private static SecurityAnalysisResult create() {
// Create a LimitViolation(CURRENT) to ensure backward compatibility works
LimitViolation violation1 = new LimitViolation("NHV1_NHV2_1", LimitViolationType.CURRENT, null, Integer.MAX_VALUE, 100, 0.95f, 110.0, TwoSides.ONE);
violation1.addExtension(ActivePowerExtension.class, new ActivePowerExtension(220.0));
LimitViolation violation2 = new LimitViolation("NHV1_NHV2_2", LimitViolationType.CURRENT, "20'", 1200, 100, 1.0f, 110.0, TwoSides.TWO);
violation2.addExtension(ActivePowerExtension.class, new ActivePowerExtension(220.0, 230.0));
violation2.addExtension(CurrentExtension.class, new CurrentExtension(95.0));
LimitViolation violation3 = new LimitViolation("GEN", LimitViolationType.HIGH_VOLTAGE, 100, 0.9f, 110, new BusBreakerViolationLocation(List.of("BUSID")));
LimitViolation violation4 = new LimitViolation("GEN2", LimitViolationType.LOW_VOLTAGE, 100, 0.7f, 115, new NodeBreakerViolationLocation("VL", Arrays.asList(0, 1)));
violation4.addExtension(VoltageExtension.class, new VoltageExtension(400.0));
LimitViolation violation5 = new LimitViolation("NHV1_NHV2_2", LimitViolationType.ACTIVE_POWER, "20'", 1200, 100, 1.0f, 110.0, TwoSides.ONE);
LimitViolation violation6 = new LimitViolation("NHV1_NHV2_2", LimitViolationType.APPARENT_POWER, "20'", 1200, 100, 1.0f, 110.0, TwoSides.TWO);
Contingency contingency = Contingency.builder("contingency")
.addBranch("NHV1_NHV2_2", "VLNHV1")
.addBranch("NHV1_NHV2_1")
.addGenerator("GEN")
.addBusbarSection("BBS1")
.build();
LimitViolationsResult preContingencyResult = new LimitViolationsResult(Collections.singletonList(violation1));
PostContingencyResult postContingencyResult = new PostContingencyResult(contingency, PostContingencyComputationStatus.CONVERGED, Arrays.asList(violation2, violation3, violation4, violation5, violation6), Arrays.asList("action1", "action2"));
List<BranchResult> preContingencyBranchResults = List.of(new BranchResult("branch1", 1, 2, 3, 1.1, 2.2, 3.3),
new BranchResult("branch2", 0, 0, 0, 0, 0, 0, 10));
List<BusResult> preContingencyBusResults = List.of(new BusResult("voltageLevelId", "busId", 400, 3.14));
List<ThreeWindingsTransformerResult> threeWindingsTransformerResults = List.of(new ThreeWindingsTransformerResult("threeWindingsTransformerId", 1, 2, 3, 1.1, 2.1, 3.1, 1.2, 2.2, 3.2));
List<OperatorStrategyResult> operatorStrategyResults = new ArrayList<>();
operatorStrategyResults.add(
new OperatorStrategyResult(
new OperatorStrategy("strategyId", ContingencyContext.specificContingency("contingency1"),
List.of(new ConditionalActions("stage1", new AtLeastOneViolationCondition(Collections.singletonList("violationId1")), Collections.singletonList("actionId1")))),
PostContingencyComputationStatus.CONVERGED,
new LimitViolationsResult(Collections.emptyList()),
new NetworkResult(Collections.emptyList(), Collections.emptyList(), Collections.emptyList())));
SecurityAnalysisResult result = new SecurityAnalysisResult(preContingencyResult, LoadFlowResult.ComponentResult.Status.CONVERGED,
Collections.singletonList(postContingencyResult),
preContingencyBranchResults, preContingencyBusResults, threeWindingsTransformerResults, operatorStrategyResults);
result.setNetworkMetadata(new NetworkMetadata(NETWORK));
return result;
}
@Test
void testCompatibilityV1Deserialization() {
LimitViolation violation1 = new LimitViolation("NHV1_NHV2_1", LimitViolationType.CURRENT, null, Integer.MAX_VALUE, 100, 0.95f, 110.0, TwoSides.ONE);
violation1.addExtension(ActivePowerExtension.class, new ActivePowerExtension(220.0));
SecurityAnalysisResult result = SecurityAnalysisResultDeserializer.read(getClass().getResourceAsStream("/SecurityAnalysisResultV1.json"));
Assertions.assertThat(result.getPreContingencyLimitViolationsResult().getLimitViolations()).hasSize(1);
assertEquals(0, LimitViolations.comparator().compare(violation1, result.getPreContingencyLimitViolationsResult().getLimitViolations().get(0)));
}
@Test
void testCompatibilityV12DeserializationFail() {
InputStream inputStream = getClass().getResourceAsStream("/SecurityAnalysisResultV1.2fail.json");
assertNotNull(inputStream);
Assertions.assertThatThrownBy(() -> SecurityAnalysisResultDeserializer.read(inputStream))
.isInstanceOf(PowsyblException.class)
.hasMessageContaining("PreContingencyResult. Tag: branchResults is not valid for version 1.2. Version should be <= 1.1");
}
@Test
void testCompatibilityV11Deserialization() {
SecurityAnalysisResult result = SecurityAnalysisResultDeserializer.read(getClass().getResourceAsStream("/SecurityAnalysisResultV1.1.json"));
assertEquals(0, result.getOperatorStrategyResults().size());
assertEquals(3.3, result.getPreContingencyResult().getNetworkResult().getBranchResult("branch1").getI2(), 0.01);
}
private static Stream<Arguments> provideArguments() {
return Stream.of(
Arguments.of("/SecurityAnalysisResultV1.2.json"),
Arguments.of("/SecurityAnalysisResultV1.3.json"),
Arguments.of("/SecurityAnalysisResultV1.4.json")
);
}
@ParameterizedTest
@MethodSource("provideArguments")
void testCompatibilityV12To14Deserialization(String jsonFileName) {
SecurityAnalysisResult result = SecurityAnalysisResultDeserializer.read(getClass().getResourceAsStream(jsonFileName));
assertEquals(PostContingencyComputationStatus.CONVERGED, result.getPostContingencyResults().get(0).getStatus());
}
private static Stream<Arguments> provideArguments2() {
return Stream.of(
Arguments.of("/SecurityAnalysisResultV1.5.json"),
Arguments.of("/SecurityAnalysisResultV1.6.json")
);
}
@ParameterizedTest
@MethodSource("provideArguments2")
void testCompatibilityV15Deserialization(String jsonFileName) {
SecurityAnalysisResult result = SecurityAnalysisResultDeserializer.read(getClass().getResourceAsStream(jsonFileName));
assertEquals(PostContingencyComputationStatus.CONVERGED, result.getPostContingencyResults().get(0).getStatus());
assertEquals(PostContingencyComputationStatus.CONVERGED, result.getOperatorStrategyResults().get(0).getConditionalActionsResults().get(0).getStatus());
}
@Test
void roundTripJson() throws IOException {
SecurityAnalysisResult result = create();
roundTripTest(result, ExporterTest::writeJson, SecurityAnalysisResultDeserializer::read, "/SecurityAnalysisResult.json");
BiConsumer<SecurityAnalysisResult, Path> exporter = (res, path) -> SecurityAnalysisResultExporters.export(res, path, "JSON");
roundTripTest(result, exporter, SecurityAnalysisResultDeserializer::read, "/SecurityAnalysisResult.json");
// Check invalid path
Path path = Paths.get("");
assertThrows(UncheckedIOException.class, () -> SecurityAnalysisResultExporters.export(result, path, "JSON"));
// Check invalid format
Path pathInvalidFormat = tmpDir.resolve("data");
assertThrows(PowsyblException.class, () -> SecurityAnalysisResultExporters.export(result, pathInvalidFormat, "XXX"));
}
@Test
void roundTripJsonWithProperties() throws IOException {
SecurityAnalysisResult result = create();
roundTripTest(result, ExporterTest::writeJsonWithProperties, SecurityAnalysisResultDeserializer::read, "/SecurityAnalysisResult.json");
BiConsumer<SecurityAnalysisResult, Path> exporter = (res, path) -> SecurityAnalysisResultExporters.export(res, null, path, "JSON");
roundTripTest(result, exporter, SecurityAnalysisResultDeserializer::read, "/SecurityAnalysisResult.json");
// Check invalid path
Path emptyPath = Paths.get("");
assertThrows(UncheckedIOException.class, () -> SecurityAnalysisResultExporters.export(result, null, emptyPath, "JSON"));
// Check invalid format
Path pathInvalidFormat = tmpDir.resolve("data");
assertThrows(PowsyblException.class, () -> SecurityAnalysisResultExporters.export(result, null, pathInvalidFormat, "XXX"));
}
private static void writeJson(SecurityAnalysisResult result, Path path) {
SecurityAnalysisResultExporter exporter = SecurityAnalysisResultExporters.getExporter("JSON");
assertNotNull(exporter);
assertEquals("JSON", exporter.getFormat());
try (Writer writer = Files.newBufferedWriter(path)) {
exporter.export(result, writer);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static void writeJsonWithProperties(SecurityAnalysisResult result, Path path) {
SecurityAnalysisResultExporter exporter = SecurityAnalysisResultExporters.getExporter("JSON");
assertNotNull(exporter);
assertEquals("JSON", exporter.getFormat());
try (Writer writer = Files.newBufferedWriter(path)) {
exporter.export(result, new Properties(), writer);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}