SecurityAnalysisResultBuilderTest.java

package com.powsybl.security;

import com.powsybl.contingency.Contingency;
import com.powsybl.contingency.ContingencyContext;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.security.condition.TrueCondition;
import com.powsybl.security.interceptors.*;
import com.powsybl.security.results.*;
import com.powsybl.security.strategy.ConditionalActions;
import com.powsybl.security.strategy.OperatorStrategy;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author Sylvain Leclerc {@literal <sylvain.leclerc at rte-france.com>}
 */
class SecurityAnalysisResultBuilderTest {

    private final Network network = EurostagTutorialExample1Factory.createWithCurrentLimits();

    @Test
    void failedResult() {

        SecurityAnalysisResultBuilder builder = new SecurityAnalysisResultBuilder(new LimitViolationFilter(),
                new RunningContext(network, network.getVariantManager().getWorkingVariantId()));

        SecurityAnalysisResult res = builder.preContingency().setStatus(LoadFlowResult.ComponentResult.Status.FAILED).endPreContingency().build();

        assertSame(LoadFlowResult.ComponentResult.Status.FAILED, res.getPreContingencyResult().getStatus());
        assertTrue(res.getPreContingencyLimitViolationsResult().getLimitViolations().isEmpty());
        assertTrue(res.getPostContingencyResults().isEmpty());
    }

    @Test
    void completeResultWithCustomContext() {
        SecurityAnalysisInterceptor securityAnalysisInterceptorMock = new MockInterceptor();
        CustomContext preResultContext = new CustomContext(network, "pre");
        CustomContext baseContext = new CustomContext(network, "all");
        CustomContext preVioContext = new CustomContext(network, "pre-vio");

        SecurityAnalysisResultBuilder builder = new SecurityAnalysisResultBuilder(new LimitViolationFilter(),
                baseContext, Collections.singleton(securityAnalysisInterceptorMock));

        VoltageLevel vl = network.getVoltageLevel("VLHV1");
        vl.getBusView().getBusStream().forEach(b -> b.setV(410));

        SecurityAnalysisResultBuilder.PreContingencyResultBuilder preContingencyResultBuilder = builder.preContingency(preResultContext);
        preContingencyResultBuilder
                .setStatus(LoadFlowResult.ComponentResult.Status.CONVERGED)
                .addViolations(Security.checkLimits(network), preVioContext)
                .endPreContingency();
        assertEquals(Security.checkLimits(network).size(), preVioContext.getCalledCount());
        assertEquals(1, preResultContext.getCalledCount());

        vl.getBusView().getBusStream().forEach(b -> b.setV(380));

        CustomContext postResultContext = new CustomContext(network, "post");
        CustomContext postViolationContext = new CustomContext(network, "post-vio");
        builder.contingency(new Contingency("contingency1"), postResultContext)
                .setStatus(PostContingencyComputationStatus.CONVERGED)
                .addViolations(Security.checkLimits(network), postViolationContext)
                .setConnectivityResult(new ConnectivityResult(1, 1, 0.0, 0.0, Collections.emptySet()))
                .endContingency();
        assertEquals(Security.checkLimits(network).size(), postViolationContext.getCalledCount());
        assertEquals(1, postResultContext.getCalledCount());

        SecurityAnalysisResult result = builder.build();
        assertEquals(4, result.getPreContingencyLimitViolationsResult().getLimitViolations().size());
        assertEquals(1, baseContext.getCalledCount());
    }

    @Test
    void completeResult() {
        SecurityAnalysisResultBuilder builder = new SecurityAnalysisResultBuilder(new LimitViolationFilter(),
                new RunningContext(network, network.getVariantManager().getWorkingVariantId()));

        VoltageLevel vl = network.getVoltageLevel("VLHV1");
        vl.getBusView().getBusStream().forEach(b -> b.setV(410));

        builder.preContingency()
                .addViolations(Security.checkLimits(network))
                .endPreContingency();

        vl.getBusView().getBusStream().forEach(b -> b.setV(380));

        builder.contingency(new Contingency("contingency1"))
                .setStatus(PostContingencyComputationStatus.CONVERGED)
                .addBranchResult(new BranchResult("branchId", 0, 0, 0, 0, 0, 0, 0))
                .addBusResult(new BusResult("voltageLevelId", "busId", 400, 3.14))
                .addThreeWindingsTransformerResult(new ThreeWindingsTransformerResult("threeWindingsTransformerId",
                0, 0, 0, 0, 0, 0, 0, 0, 0))
                .addViolations(Security.checkLimits(network))
                .setConnectivityResult(new ConnectivityResult(1, 2, 10.0, 20.0, Set.of("branchId")))
                .endContingency();

        vl.getBusView().getBusStream().forEach(b -> b.setV(520));
        builder.contingency(new Contingency("contingency2"))
                .setStatus(PostContingencyComputationStatus.CONVERGED)
                .addViolations(Security.checkLimits(network))
                .setConnectivityResult(new ConnectivityResult(2, 4, 10.0, 15.0, Set.of("branchId", "branchId2")))
                .endContingency();

        SecurityAnalysisResult res = builder.build();

        assertSame(LoadFlowResult.ComponentResult.Status.CONVERGED, res.getPreContingencyResult().getStatus());
        assertEquals(4, res.getPreContingencyLimitViolationsResult().getLimitViolations().size());
        assertEquals(2, res.getPostContingencyResults().size());

        PostContingencyResult res1 = res.getPostContingencyResults().get(0);
        assertEquals("contingency1", res1.getContingency().getId());
        assertEquals(new BranchResult("branchId", 0, 0, 0, 0, 0, 0, 0), res1.getNetworkResult().getBranchResult("branchId"));
        assertEquals(new BusResult("voltageLevelId", "busId", 400, 3.14), res1.getNetworkResult().getBusResult("busId"));
        assertEquals(new ThreeWindingsTransformerResult("threeWindingsTransformerId",
            0, 0, 0, 0, 0, 0, 0, 0, 0), res1.getNetworkResult().getThreeWindingsTransformerResult("threeWindingsTransformerId"));
        assertEquals(1, res1.getConnectivityResult().getCreatedSynchronousComponentCount());
        assertEquals(2, res1.getConnectivityResult().getCreatedConnectedComponentCount());
        assertEquals(10.0, res1.getConnectivityResult().getDisconnectedLoadActivePower(), 1e-3);
        assertEquals(20.0, res1.getConnectivityResult().getDisconnectedGenerationActivePower(), 1e-3);
        assertEquals(Set.of("branchId"), res1.getConnectivityResult().getDisconnectedElements());
        assertEquals(2, res.getPostContingencyResults().size());

        List<LimitViolation> violations1 = res1.getLimitViolationsResult().getLimitViolations();
        assertEquals(4, violations1.stream().filter(l -> l.getLimitType() == LimitViolationType.CURRENT).count());
        assertEquals(1, violations1.stream().filter(l -> l.getLimitType() == LimitViolationType.LOW_VOLTAGE).count());
        assertEquals(0, violations1.stream().filter(l -> l.getLimitType() == LimitViolationType.HIGH_VOLTAGE).count());

        PostContingencyResult res2 = res.getPostContingencyResults().get(1);
        assertEquals("contingency2", res2.getContingency().getId());
        assertEquals(2, res2.getConnectivityResult().getCreatedSynchronousComponentCount());
        assertEquals(4, res2.getConnectivityResult().getCreatedConnectedComponentCount());
        assertEquals(10.0, res2.getConnectivityResult().getDisconnectedLoadActivePower(), 1e-3);
        assertEquals(15.0, res2.getConnectivityResult().getDisconnectedGenerationActivePower(), 1e-3);
        assertEquals(Set.of("branchId", "branchId2"), res2.getConnectivityResult().getDisconnectedElements());
        assertEquals(2, res.getPostContingencyResults().size());

        List<LimitViolation> violations2 = res2.getLimitViolationsResult().getLimitViolations();
        assertEquals(3, violations2.stream().filter(l -> l.getLimitType() == LimitViolationType.CURRENT).count());
        assertEquals(0, violations2.stream().filter(l -> l.getLimitType() == LimitViolationType.LOW_VOLTAGE).count());
        assertEquals(1, violations2.stream().filter(l -> l.getLimitType() == LimitViolationType.HIGH_VOLTAGE).count());
    }

    static class MockContext extends DefaultSecurityAnalysisResultContext {

        private int calledCount = 0;

        MockContext(Network network) {
            super(network);
        }

        final void foo() {
            calledCount++;
        }

        int getCalledCount() {
            return calledCount;
        }
    }

    static class CustomContext extends MockContext {

        private final String name;

        CustomContext(Network network, String name) {
            super(network);
            this.name = Objects.requireNonNull(name);
        }

        String getName() {
            return name;
        }
    }

    public static class MockInterceptor extends DefaultSecurityAnalysisInterceptor {

        @Override
        public void onPreContingencyResult(PreContingencyResult preContingencyResult, SecurityAnalysisResultContext context) {
            if (context instanceof CustomContext) {
                CustomContext customContext = (CustomContext) context;
                customContext.foo();
                assertEquals("pre", customContext.getName());
            }
        }

        @Override
        public void onPostContingencyResult(PostContingencyResult postContingencyResult, SecurityAnalysisResultContext context) {
            if (context instanceof CustomContext) {
                CustomContext customContext = (CustomContext) context;
                customContext.foo();
                assertEquals("post", customContext.getName());
            }
        }

        @Override
        public void onLimitViolation(LimitViolation limitViolation, SecurityAnalysisResultContext context) {
            if (context instanceof CustomContext) {
                CustomContext customContext = (CustomContext) context;
                assertEquals("pre-vio", customContext.getName());
                customContext.foo();
            }
        }

        @Override
        public void onLimitViolation(Contingency contingency, LimitViolation limitViolation, SecurityAnalysisResultContext context) {
            if (context instanceof CustomContext) {
                CustomContext customContext = (CustomContext) context;
                assertEquals("post-vio", customContext.getName());
                customContext.foo();
            }
        }

        @Override
        public void onSecurityAnalysisResult(SecurityAnalysisResult result, SecurityAnalysisResultContext context) {
            if (context instanceof CustomContext) {
                CustomContext customContext = (CustomContext) context;
                customContext.foo();
                assertEquals("all", customContext.getName());
            }
        }
    }

    @Test
    void operatorStrategyResultCreation() {
        //Build result with 1 operator strategy
        OperatorStrategy operatorStrategy = new OperatorStrategy("strat1", ContingencyContext.specificContingency("cont1"), new TrueCondition(),
                List.of("action1", "action2"));

        SecurityAnalysisResultBuilder builder = new SecurityAnalysisResultBuilder(new LimitViolationFilter(),
                new RunningContext(network, network.getVariantManager().getWorkingVariantId()));

        LimitViolation violation = LimitViolations.highVoltage().subject("VLHV1").value(425).limit(420).build();
        BusResult busResult = new BusResult("VLHV2", "VLHV2_0", 426.2, 0.12);
        builder.operatorStrategy(operatorStrategy)
                .newConditionalActionsResult("default")
                .addViolation(violation)
                .addBusResult(busResult)
                .setStatus(PostContingencyComputationStatus.CONVERGED)
                .endConditionalActions()
                .endOperatorStrategy();

        SecurityAnalysisResult result = builder.build();

        //Check content
        assertEquals(1, result.getOperatorStrategyResults().size());

        OperatorStrategyResult strategyResult = result.getOperatorStrategyResults().get(0);
        assertSame(operatorStrategy, strategyResult.getOperatorStrategy());

        LimitViolationsResult violationsResult = strategyResult.getLimitViolationsResult();
        assertSame(PostContingencyComputationStatus.CONVERGED, strategyResult.getStatus());
        assertEquals(1, violationsResult.getLimitViolations().size());
        assertSame(violation, violationsResult.getLimitViolations().get(0));
        NetworkResult networkResult = strategyResult.getNetworkResult();
        assertEquals(1, networkResult.getBusResults().size());
        assertEquals(busResult, networkResult.getBusResults().get(0));
        assertTrue(networkResult.getBranchResults().isEmpty());
        assertTrue(networkResult.getThreeWindingsTransformerResults().isEmpty());
    }

    @Test
    void operatorStrategyResultMultipleConditionalResults() {
        OperatorStrategy operatorStrategy = new OperatorStrategy("strat1", ContingencyContext.specificContingency("cont1"),
                List.of(new ConditionalActions("CRA1", new TrueCondition(), List.of("action1")), new ConditionalActions("CRA2", new TrueCondition(), List.of("action2"))));

        SecurityAnalysisResultBuilder builder = new SecurityAnalysisResultBuilder(new LimitViolationFilter(),
                new RunningContext(network, network.getVariantManager().getWorkingVariantId()));

        LimitViolation violation = LimitViolations.highVoltage().subject("VLHV1").value(425).limit(420).build();
        BusResult busResult = new BusResult("VLHV2", "VLHV2_0", 426.2, 0.12);

        LimitViolation violation2 = LimitViolations.highVoltage().subject("VLHV1").value(432).limit(444).build();
        BusResult busResult2 = new BusResult("VLHV2", "VLHV2_0", 34.2, 0.36);

        builder.operatorStrategy(operatorStrategy)
                .newConditionalActionsResult("CRA1")
                .addViolation(violation)
                .addBusResult(busResult)
                .setStatus(PostContingencyComputationStatus.CONVERGED)
                .endConditionalActions()
                .newConditionalActionsResult("CRA2")
                .addViolation(violation2)
                .addBusResult(busResult2)
                .setStatus(PostContingencyComputationStatus.FAILED)
                .endConditionalActions()
                .endOperatorStrategy();

        SecurityAnalysisResult result = builder.build();

        assertEquals(1, result.getOperatorStrategyResults().size());
        assertEquals(2, result.getOperatorStrategyResults().get(0).getConditionalActionsResults().size());

        OperatorStrategyResult strategyResult = result.getOperatorStrategyResults().get(0);
        OperatorStrategyResult.ConditionalActionsResult firstActionsRes = strategyResult.getConditionalActionsResults().get(0);
        OperatorStrategyResult.ConditionalActionsResult lastActionsRes = strategyResult.getConditionalActionsResults().get(1);

        // First conditional actions converged
        assertSame(PostContingencyComputationStatus.CONVERGED, firstActionsRes.getStatus());
        assertEquals("CRA1", firstActionsRes.getConditionalActionsId());

        // Last conditional actions fail
        assertSame(PostContingencyComputationStatus.FAILED, lastActionsRes.getStatus());
        assertEquals("CRA2", lastActionsRes.getConditionalActionsId());

        // Result available at strategy level are the last
        assertSame(PostContingencyComputationStatus.FAILED, strategyResult.getStatus());
    }
}