AbsolutePtdfSumsComputationTest.java

/*
 * Copyright (c) 2020, 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/.
 */
package com.powsybl.openrao.searchtreerao.commons;

import com.powsybl.openrao.commons.EICode;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openrao.data.crac.impl.utils.CommonCracCreation;
import com.powsybl.openrao.data.crac.impl.utils.NetworkImportsUtil;
import com.powsybl.openrao.raoapi.ZoneToZonePtdfDefinition;
import com.powsybl.openrao.sensitivityanalysis.SystematicSensitivityResult;
import com.powsybl.glsk.commons.ZonalData;
import com.powsybl.glsk.ucte.UcteGlskDocument;
import com.powsybl.iidm.network.Network;
import com.powsybl.sensitivity.SensitivityVariableSet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;

import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

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

/**
 * @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
 */
class AbsolutePtdfSumsComputationTest {
    private static final double DOUBLE_TOLERANCE = 0.001;

    private SystematicSensitivityResult systematicSensitivityResult;

    @BeforeEach
    public void setUp() {

        systematicSensitivityResult = Mockito.mock(SystematicSensitivityResult.class);
        Mockito.when(systematicSensitivityResult.getSensitivityOnFlow(Mockito.any(SensitivityVariableSet.class), Mockito.any(FlowCnec.class), Mockito.any(TwoSides.class)))
            .thenAnswer(
                (Answer<Double>) invocation -> {
                    SensitivityVariableSet linearGlsk = (SensitivityVariableSet) invocation.getArguments()[0];
                    FlowCnec branchCnec = (FlowCnec) invocation.getArguments()[1];
                    if (branchCnec.getId().contains("cnec1")) {
                        return switch (linearGlsk.getId().substring(0, EICode.EIC_LENGTH)) {
                            case "10YFR-RTE------C", "22Y201903144---9" -> 0.1;
                            case "10YBE----------2" -> 0.2;
                            case "10YCB-GERMANY--8" -> 0.3;
                            case "22Y201903145---4" -> 0.4;
                            default -> 0.;
                        };
                    } else if (branchCnec.getId().contains("cnec2")) {
                        return switch (linearGlsk.getId().substring(0, EICode.EIC_LENGTH)) {
                            case "10YFR-RTE------C", "10YBE----------2" -> 0.3;
                            case "10YCB-GERMANY--8" -> 0.2;
                            case "22Y201903145---4" -> 0.1;
                            case "22Y201903144---9" -> 0.9;
                            default -> 0.;
                        };
                    } else {
                        return 0.;
                    }
                });
    }

    @Test
    void testComputation() {

        // prepare data
        Network network = NetworkImportsUtil.import12NodesNetwork();
        ZonalData<SensitivityVariableSet> glskProvider = UcteGlskDocument.importGlsk(getClass().getResourceAsStream("/glsk/glsk_proportional_12nodes_with_alegro.xml"))
                .getZonalGlsks(network, Instant.parse("2016-07-28T22:30:00Z"));
        Crac crac = CommonCracCreation.create(Set.of(TwoSides.ONE, TwoSides.TWO));
        List<ZoneToZonePtdfDefinition> boundaries = Arrays.asList(
                new ZoneToZonePtdfDefinition("{FR}-{BE}"),
                new ZoneToZonePtdfDefinition("{FR}-{DE}"),
                new ZoneToZonePtdfDefinition("{DE}-{BE}"),
                new ZoneToZonePtdfDefinition("{BE}-{22Y201903144---9}-{DE}+{22Y201903145---4}"));

        // compute zToz PTDF sum
        AbsolutePtdfSumsComputation absolutePtdfSumsComputation = new AbsolutePtdfSumsComputation(glskProvider, boundaries);
        Map<FlowCnec, Map<TwoSides, Double>> ptdfSums = absolutePtdfSumsComputation.computeAbsolutePtdfSums(crac.getFlowCnecs(), systematicSensitivityResult);

        // test results
        assertEquals(0.6, ptdfSums.get(crac.getFlowCnec("cnec1basecase")).get(TwoSides.ONE), DOUBLE_TOLERANCE); // abs(0.1 - 0.2) + abs(0.1 - 0.3) + abs(0.3 - 0.2) + abs(0.2 - 0.1 - 0.3 + 0.4) = 0.1 + 0.2 + 0.1 + 0.2
        assertEquals(0.9, ptdfSums.get(crac.getFlowCnec("cnec2basecase")).get(TwoSides.TWO), DOUBLE_TOLERANCE); // abs(0.3 - 0.3) + abs(0.3 - 0.2) + abs(0.2 - 0.3) + abs(0.3 - 0.9 - 0.2 + 0.1) = 0 + 0.1 + 0.1 + 0.7
    }

    @Test
    void testIgnoreZtoZWithLessThan2ZtoS() {

        // prepare data
        Network network = NetworkImportsUtil.import12NodesNetwork();
        ZonalData<SensitivityVariableSet> glskProvider = UcteGlskDocument.importGlsk(getClass().getResourceAsStream("/glsk/glsk_proportional_12nodes_with_alegro.xml"))
                .getZonalGlsks(network, Instant.parse("2016-07-28T22:30:00Z"));
        Crac crac = CommonCracCreation.create(Set.of(TwoSides.ONE, TwoSides.TWO));
        List<ZoneToZonePtdfDefinition> boundaries = Arrays.asList(
                new ZoneToZonePtdfDefinition("{FR}-{BE}"),
                new ZoneToZonePtdfDefinition("{FR}-{DE}"),
                new ZoneToZonePtdfDefinition("{DE}-{BE}"),
                new ZoneToZonePtdfDefinition("{BE}-{22Y201903144---0}-{DE}+{22Y201903144---1}"), // wrong EIC for Alegro, only {BE}-{DE} will be taken into account
                new ZoneToZonePtdfDefinition("{FR}-{ES}"), // ES doesn't exist in GLSK map, must be filtered
                new ZoneToZonePtdfDefinition("{ES}-{DE}")); // ES doesn't exist in GLSK map, must be filtered

        // compute zToz PTDF sum
        AbsolutePtdfSumsComputation absolutePtdfSumsComputation = new AbsolutePtdfSumsComputation(glskProvider, boundaries);
        Map<FlowCnec, Map<TwoSides, Double>> ptdfSums = absolutePtdfSumsComputation.computeAbsolutePtdfSums(crac.getFlowCnecs(), systematicSensitivityResult);

        // Test that these 3 new boundaries are ignored (results should be the same as previous test)
        assertEquals(0.5, ptdfSums.get(crac.getFlowCnec("cnec1basecase")).get(TwoSides.TWO), DOUBLE_TOLERANCE); // abs(0.1 - 0.2) + abs(0.1 - 0.3) + abs(0.3 - 0.2) + abs(0.2 - 0.3) = 0.1 + 0.2 + 0.1 + 0.1
        assertEquals(0.3, ptdfSums.get(crac.getFlowCnec("cnec2basecase")).get(TwoSides.ONE), DOUBLE_TOLERANCE); // abs(0.3 - 0.3) + abs(0.3 - 0.2) + abs(0.2 - 0.3) + abs(0.3 - 0.2) = 0 + 0.1 + 0.1 + 0.1
    }
}