MetrixBranchPostProcessingTimeSeriesTest.java
/*
* Copyright (c) 2021, 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.metrix.integration;
import com.google.common.collect.Sets;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.serde.NetworkSerDe;
import com.powsybl.metrix.integration.dataGenerator.MetrixOutputData;
import com.powsybl.metrix.mapping.DataTableStore;
import com.powsybl.metrix.mapping.MappingParameters;
import com.powsybl.metrix.mapping.TimeSeriesDslLoader;
import com.powsybl.metrix.mapping.TimeSeriesMappingConfig;
import com.powsybl.timeseries.ReadOnlyTimeSeriesStore;
import com.powsybl.timeseries.ReadOnlyTimeSeriesStoreCache;
import com.powsybl.timeseries.RegularTimeSeriesIndex;
import com.powsybl.timeseries.TimeSeries;
import com.powsybl.timeseries.TimeSeriesIndex;
import com.powsybl.timeseries.ast.BinaryOperation;
import com.powsybl.timeseries.ast.FloatNodeCalc;
import com.powsybl.timeseries.ast.IntegerNodeCalc;
import com.powsybl.timeseries.ast.NodeCalc;
import com.powsybl.timeseries.ast.TimeSeriesNameNodeCalc;
import com.powsybl.timeseries.ast.UnaryOperation;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.threeten.extra.Interval;
import java.time.Duration;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Paul Bui-Quang {@literal <paul.buiquang at rte-france.com>}
*/
class MetrixBranchPostProcessingTimeSeriesTest {
private Network network;
private final MetrixParameters parameters = new MetrixParameters();
private final String mappingScript = String.join(System.lineSeparator(),
"timeSeries['tsN'] = 1000",
"timeSeries['tsN1'] = 2000",
"timeSeries['tsITAM'] = 2500",
"timeSeries['tsAnalysisN'] = 3000",
"timeSeries['tsAnalysisNk'] = 4000",
"timeSeries['tsAnalysisITAM'] = 5000");
private final String metrixConfigurationScript = String.join(System.lineSeparator(),
"branch('FS.BIS1 FVALDI1 1') {",
"}",
"branch('FS.BIS1 FVALDI1 2') {",
" baseCaseFlowResults true",
" maxThreatFlowResults true",
"}",
"branch('FVALDI1 FTDPRA1 1') {",
" branchRatingsBaseCase 'tsN'",
" branchRatingsOnContingency 'tsN1'",
" branchRatingsBeforeCurative 'tsITAM'",
"}",
"branch('FP.AND1 FVERGE1 1') {", // this branch is opened in the network
" branchRatingsBaseCase 'tsN'",
" branchRatingsOnContingency 'tsN1'",
" branchRatingsBeforeCurative 'tsITAM'",
"}",
"branch('FVALDI1 FTDPRA1 2') {",
" branchRatingsBaseCase 'tsN'",
" branchRatingsBaseCaseEndOr 'tsNEndOr'",
" branchRatingsOnContingency 'tsN1'",
" branchRatingsOnContingencyEndOr 'tsN1EndOr'",
" branchRatingsBeforeCurative 'tsITAM'",
" branchRatingsBeforeCurativeEndOr 'tsITAMEndOr'",
"}",
"branch('FTDPRA1 FVERGE1 2') {",
" branchRatingsBaseCase 'tsN'",
"}",
"branch('FTDPRA1 FVERGE1 2') {",
" branchAnalysisRatingsBaseCase 'tsAnalysisN'",
"}",
"branch('FTDPRA1 FVERGE1 2') {",
" branchAnalysisRatingsOnContingency 'tsAnalysisNk'",
"}",
"branch('FTDPRA1 FVERGE1 2') {",
" branchRatingsOnContingency 'tsN1'",
" branchRatingsBeforeCurative 'tsITAM'",
"}"
);
Map<String, NodeCalc> postProcessingTimeSeries;
@BeforeEach
public void setUp() {
network = NetworkSerDe.read(Objects.requireNonNull(getClass().getResourceAsStream("/simpleNetwork.xml")));
}
private void verifyBasecaseLoad(String branchName, NodeCalc flow, NodeCalc ratingN) {
NodeCalc expectedBasecaseLoad = BinaryOperation.multiply(BinaryOperation.div(flow, ratingN), new FloatNodeCalc(100f));
assertEquals(expectedBasecaseLoad, postProcessingTimeSeries.get("basecaseLoad_" + branchName));
}
private void verifyLoad(String branchName, NodeCalc maxThreat, NodeCalc rating, String loadPrefix) {
NodeCalc outageLoad1 = BinaryOperation.multiply(BinaryOperation.div(maxThreat, rating), new FloatNodeCalc(100f));
assertEquals(outageLoad1, postProcessingTimeSeries.get(loadPrefix + branchName));
}
private void verifyOutageLoad(String branchName, NodeCalc maxThreat, NodeCalc ratingNk) {
verifyLoad(branchName, maxThreat, ratingNk, "outageLoad_");
}
private void verifyItamLoad(String branchName, NodeCalc maxThreat, NodeCalc ratingItam) {
verifyLoad(branchName, maxThreat, ratingItam, "itamLoad_");
}
private NodeCalc verifyBasecaseOverload(String branchName, NodeCalc flow, NodeCalc ratingN) {
NodeCalc basecaseOverload = BinaryOperation.plus(BinaryOperation.multiply(BinaryOperation.greaterThan(flow, ratingN),
BinaryOperation.minus(flow, ratingN)),
BinaryOperation.multiply(BinaryOperation.lessThan(flow, UnaryOperation.negative(ratingN)),
BinaryOperation.minus(flow, UnaryOperation.negative(ratingN))));
assertEquals(basecaseOverload, postProcessingTimeSeries.get("basecaseOverload_" + branchName));
return basecaseOverload;
}
private NodeCalc verifyOverload(String branchName, NodeCalc maxThreat, NodeCalc ratingNkExOr, NodeCalc ratingNkOrEx, String overloadPrefix) {
NodeCalc outageOverload = BinaryOperation.plus(BinaryOperation.multiply(BinaryOperation.greaterThan(maxThreat, ratingNkExOr),
BinaryOperation.minus(maxThreat, ratingNkExOr)),
BinaryOperation.multiply(BinaryOperation.lessThan(maxThreat, UnaryOperation.negative(ratingNkOrEx)),
BinaryOperation.minus(maxThreat, UnaryOperation.negative(ratingNkOrEx))));
assertEquals(outageOverload, postProcessingTimeSeries.get(overloadPrefix + branchName));
return outageOverload;
}
private NodeCalc verifyOutageOverload(String branchName, NodeCalc maxThreat, NodeCalc ratingNkOrEx, NodeCalc ratingNkExOr) {
return verifyOverload(branchName, maxThreat, ratingNkOrEx, ratingNkExOr, "outageOverload_");
}
private NodeCalc verifyItamOverload(String branchName, NodeCalc maxThreat, NodeCalc ratingItamOrEx, NodeCalc ratingItamExOr) {
return verifyOverload(branchName, maxThreat, ratingItamOrEx, ratingItamExOr, "itamOverload_");
}
private void verifyOverallOverload(String branchName, NodeCalc basecaseOverload, NodeCalc outageOverload, String overallOverloadPrefix) {
NodeCalc overallOverload = BinaryOperation.plus(UnaryOperation.abs(basecaseOverload), UnaryOperation.abs(outageOverload));
assertEquals(overallOverload, postProcessingTimeSeries.get(overallOverloadPrefix + branchName));
}
private void verifyOverallOverload(String branchName, NodeCalc basecaseOverload, NodeCalc outageOverload) {
verifyOverallOverload(branchName, basecaseOverload, outageOverload, "overallOverload_");
}
private void verifyOverallItamOverload(String branchName, NodeCalc basecaseOverload, NodeCalc outageOverload) {
verifyOverallOverload(branchName, basecaseOverload, outageOverload, "overallItamOverload_");
}
private void verifySimpleBranchPostProcessing() {
final String branchName = "FVALDI1 FTDPRA1 1";
NodeCalc flow = new TimeSeriesNameNodeCalc("FLOW_" + branchName);
NodeCalc maxThreat = new TimeSeriesNameNodeCalc("MAX_THREAT_1_FLOW_" + branchName);
NodeCalc maxTmpThreat = new TimeSeriesNameNodeCalc("MAX_TMP_THREAT_FLOW_" + branchName);
NodeCalc ratingN = new IntegerNodeCalc(1000);
NodeCalc ratingNk = new IntegerNodeCalc(2000);
NodeCalc ratingItam = new IntegerNodeCalc(2500);
verifyBasecaseLoad(branchName, flow, ratingN);
verifyOutageLoad(branchName, maxThreat, ratingNk);
verifyItamLoad(branchName, maxTmpThreat, ratingItam);
NodeCalc basecaseOverload = verifyBasecaseOverload(branchName, flow, ratingN);
NodeCalc outageOverload = verifyOutageOverload(branchName, maxThreat, ratingNk, ratingNk);
NodeCalc itamOverload = verifyItamOverload(branchName, maxTmpThreat, ratingItam, ratingItam);
verifyOverallOverload(branchName, basecaseOverload, outageOverload);
verifyOverallItamOverload(branchName, basecaseOverload, itamOverload);
}
private void verifyExOrBranchPostProcessing() {
final String branchName = "FVALDI1 FTDPRA1 2";
NodeCalc flow = new TimeSeriesNameNodeCalc("FLOW_" + branchName);
NodeCalc maxThreat = new TimeSeriesNameNodeCalc("MAX_THREAT_1_FLOW_" + branchName);
NodeCalc maxTmpThreat = new TimeSeriesNameNodeCalc("MAX_TMP_THREAT_FLOW_" + branchName);
NodeCalc ratingNOrEx = new IntegerNodeCalc(1000);
NodeCalc ratingNExOr = new TimeSeriesNameNodeCalc("tsNEndOr");
NodeCalc ratingN = BinaryOperation.plus(BinaryOperation.multiply(BinaryOperation.greaterThan(flow, new IntegerNodeCalc(0)), ratingNOrEx),
BinaryOperation.multiply(BinaryOperation.lessThan(flow, new IntegerNodeCalc(0)), ratingNExOr));
NodeCalc ratingNkOrEx = new IntegerNodeCalc(2000);
NodeCalc ratingNkExOr = new TimeSeriesNameNodeCalc("tsN1EndOr");
NodeCalc ratingItamOrEx = new IntegerNodeCalc(2500);
NodeCalc ratingItamExOr = new TimeSeriesNameNodeCalc("tsITAMEndOr");
verifyBasecaseLoad(branchName, flow, ratingN);
verifyOutageOverload(branchName, maxThreat, ratingNkOrEx, ratingNkExOr);
verifyItamOverload(branchName, maxTmpThreat, ratingItamOrEx, ratingItamExOr);
}
private void verifySeparatedConfigBranchPostProcessing() {
// For this branch, ratings are configured in separated branch() instructions
final String branchName = "FTDPRA1 FVERGE1 2";
NodeCalc flow = new TimeSeriesNameNodeCalc("FLOW_" + branchName);
NodeCalc maxThreat = new TimeSeriesNameNodeCalc("MAX_THREAT_1_FLOW_" + branchName);
NodeCalc maxTmpThreat = new TimeSeriesNameNodeCalc("MAX_TMP_THREAT_FLOW_" + branchName);
NodeCalc ratingN = new IntegerNodeCalc(3000);
NodeCalc ratingNk = new IntegerNodeCalc(2000);
NodeCalc ratingItam = new IntegerNodeCalc(2500);
verifyBasecaseLoad(branchName, flow, ratingN);
verifyOutageLoad(branchName, maxThreat, ratingNk);
verifyItamLoad(branchName, maxTmpThreat, ratingItam);
NodeCalc basecaseOverload = verifyBasecaseOverload(branchName, flow, ratingN);
NodeCalc outageOverload = verifyOutageOverload(branchName, maxThreat, ratingNk, ratingNk);
NodeCalc itamOverload = verifyItamOverload(branchName, maxTmpThreat, ratingItam, ratingItam);
verifyOverallOverload(branchName, basecaseOverload, outageOverload);
verifyOverallItamOverload(branchName, basecaseOverload, itamOverload);
}
@Test
void postProcessingTimeSeriesTest() {
TimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("1970-01-01T00:00:00Z/1970-01-02T00:00:00Z"), Duration.ofDays(1));
Set<String> branchNames = Sets.newHashSet(
"FVALDI1 FTDPRA1 1",
"FVALDI1 FTDPRA1 2",
"FS.BIS1 FVALDI1 2",
"FTDPRA1 FVERGE1 2");
Set<String> resultTimeSeriesNames = new HashSet<>();
branchNames.forEach(branchName -> resultTimeSeriesNames.add(MetrixOutputData.FLOW_NAME + branchName));
branchNames.forEach(branchName -> resultTimeSeriesNames.add(AbstractMetrix.MAX_THREAT_PREFIX + branchName));
branchNames.forEach(branchName -> resultTimeSeriesNames.add(MetrixOutputData.MAX_TMP_THREAT_FLOW + branchName));
ReadOnlyTimeSeriesStore metrixResultTimeSeries = mock(ReadOnlyTimeSeriesStore.class);
when(metrixResultTimeSeries.getTimeSeriesNames(Mockito.any())).thenReturn(resultTimeSeriesNames);
ReadOnlyTimeSeriesStore store = new ReadOnlyTimeSeriesStoreCache(
TimeSeries.createDouble("tsNEndOr", index, 1500d, 1500d),
TimeSeries.createDouble("tsN1EndOr", index, 2500d, 2500d),
TimeSeries.createDouble("tsITAM", index, 3500d, 3500d),
TimeSeries.createDouble("tsITAMEndOr", index, 4500d, 4500d)
);
MappingParameters mappingParameters = MappingParameters.load();
TimeSeriesDslLoader timeSeriesDslLoader = new TimeSeriesDslLoader(mappingScript);
TimeSeriesMappingConfig mappingConfig = timeSeriesDslLoader.load(network, mappingParameters, store, new DataTableStore(), null);
MetrixDslDataLoader metrixDslDataLoader = new MetrixDslDataLoader(metrixConfigurationScript);
MetrixDslData dslData = metrixDslDataLoader.load(network, parameters, store, new DataTableStore(), mappingConfig, null);
MetrixBranchPostProcessingTimeSeries branchProcessing = new MetrixBranchPostProcessingTimeSeries(dslData, mappingConfig, metrixResultTimeSeries.getTimeSeriesNames(null), null);
postProcessingTimeSeries = branchProcessing.createPostProcessingTimeSeries();
assertEquals(3 * 8, postProcessingTimeSeries.size());
verifySimpleBranchPostProcessing();
verifyExOrBranchPostProcessing();
verifySeparatedConfigBranchPostProcessing();
}
}