NcSsiTest.java

/*
 * Copyright (c) 2024, 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.data.crac.io.nc.craccreator;

import com.powsybl.action.*;
import com.powsybl.contingency.Contingency;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.cnec.AngleCnec;
import com.powsybl.openrao.data.crac.api.networkaction.NetworkAction;
import com.powsybl.openrao.data.crac.api.rangeaction.PstRangeAction;
import com.powsybl.openrao.data.crac.api.usagerule.UsageMethod;
import com.powsybl.openrao.data.crac.io.commons.api.ImportStatus;
import org.junit.jupiter.api.Test;

import java.util.Comparator;
import java.util.List;
import java.util.Set;

import static com.powsybl.openrao.data.crac.io.nc.craccreator.NcCracCreationTestUtil.*;
import static com.powsybl.openrao.data.crac.io.nc.craccreator.NcCracCreationTestUtil.assertNetworkActionImported;
import static org.junit.jupiter.api.Assertions.*;

/**
 * @author Thomas Bouquet {@literal <thomas.bouquet at rte-france.com>}
 */
class NcSsiTest {

    private NcCracCreationContext cracCreationContext;
    private Crac crac;

    @Test
    void activateDeactivateContingency() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-1_Contingency.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();
        assertEquals(1, crac.getContingencies().size());
        assertContingencyEquality(crac.getContingencies().iterator().next(), "contingency-1", "RTE_CO1", Set.of("FFR1AA1  FFR2AA1  1"));
        assertContingencyNotImported(cracCreationContext, "contingency-2", ImportStatus.NOT_FOR_RAO, "Contingency contingency-2 will not be imported because its field mustStudy is set to false");

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-1_Contingency.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();
        assertEquals(1, crac.getContingencies().size());
        assertContingencyEquality(crac.getContingencies().iterator().next(), "contingency-2", "RTE_CO2", Set.of("FFR1AA1  FFR3AA1  1"));
        assertContingencyNotImported(cracCreationContext, "contingency-1", ImportStatus.NOT_FOR_RAO, "Contingency contingency-1 will not be imported because its field mustStudy is set to false");
    }

    @Test
    void activateDeactivateRemedialAction() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-2_RemedialAction.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        NetworkAction remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action-1", remedialAction.getId());
        assertEquals("RTE_RA1", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        Action elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof SwitchAction);
        assertEquals("BBE1AA1  BBE4AA1  1", ((SwitchAction) elementaryAction).getSwitchId());
        assertTrue(((SwitchAction) elementaryAction).isOpen());

        assertEquals(1, remedialAction.getUsageRules().size());
        assertEquals(crac.getInstant(PREVENTIVE_INSTANT_ID), remedialAction.getUsageRules().iterator().next().getInstant());
        assertEquals(UsageMethod.AVAILABLE, remedialAction.getUsageRules().iterator().next().getUsageMethod());

        assertRaNotImported(cracCreationContext, "remedial-action-2", ImportStatus.NOT_FOR_RAO, "Remedial action remedial-action-2 will not be imported because it is set as unavailable");

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-2_RemedialAction.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action-2", remedialAction.getId());
        assertEquals("RTE_RA2", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof SwitchAction);
        assertEquals("DDE3AA1  DDE4AA1  1", ((SwitchAction) elementaryAction).getSwitchId());
        assertTrue(((SwitchAction) elementaryAction).isOpen());

        assertEquals(1, remedialAction.getUsageRules().size());
        assertEquals(crac.getInstant(PREVENTIVE_INSTANT_ID), remedialAction.getUsageRules().iterator().next().getInstant());
        assertEquals(UsageMethod.AVAILABLE, remedialAction.getUsageRules().iterator().next().getUsageMethod());

        assertRaNotImported(cracCreationContext, "remedial-action-1", ImportStatus.NOT_FOR_RAO, "Remedial action remedial-action-1 will not be imported because it is set as unavailable");
    }

    @Test
    void changeTopologyActionType() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-3_ChangeTopologicalActionType.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        NetworkAction remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action-1", remedialAction.getId());
        assertEquals("RTE_RA1", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        Action elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof SwitchAction);
        assertEquals("BBE1AA1  BBE4AA1  1", ((SwitchAction) elementaryAction).getSwitchId());
        assertTrue(((SwitchAction) elementaryAction).isOpen());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-3_ChangeTopologicalActionType.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action-1", remedialAction.getId());
        assertEquals("RTE_RA1", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof SwitchAction);
        assertEquals("BBE1AA1  BBE4AA1  1", ((SwitchAction) elementaryAction).getSwitchId());
        assertFalse(((SwitchAction) elementaryAction).isOpen());
    }

    @Test
    void restrictPstActionRange() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-4_RestrictPSTActionRange.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getPstRangeActions().size());
        PstRangeAction remedialAction = crac.getPstRangeActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());
        assertEquals("BBE2AA1  BBE3AA1  1", remedialAction.getNetworkElement().getId());
        assertEquals(1, remedialAction.getRanges().size());
        assertEquals(-16, remedialAction.getRanges().iterator().next().getMinTap());
        assertEquals(16, remedialAction.getRanges().iterator().next().getMaxTap());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-4_RestrictPSTActionRange.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getPstRangeActions().size());
        remedialAction = crac.getPstRangeActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());
        assertEquals("BBE2AA1  BBE3AA1  1", remedialAction.getNetworkElement().getId());
        assertEquals(1, remedialAction.getRanges().size());
        assertEquals(-5, remedialAction.getRanges().iterator().next().getMinTap());
        assertEquals(10, remedialAction.getRanges().iterator().next().getMaxTap());
    }

    @Test
    void changeRotatingMachineactionSetpoint() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-5_ChangeRotatingMachineActionSetpoint.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        NetworkAction remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        Action elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof GeneratorAction);
        assertEquals("FFR1AA1 _generator", ((GeneratorAction) elementaryAction).getGeneratorId());
        assertEquals(75d, ((GeneratorAction) elementaryAction).getActivePowerValue().getAsDouble());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-5_ChangeRotatingMachineActionSetpoint.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof GeneratorAction);
        assertEquals("FFR1AA1 _generator", ((GeneratorAction) elementaryAction).getGeneratorId());
        assertEquals(100d, ((GeneratorAction) elementaryAction).getActivePowerValue().getAsDouble());
    }

    @Test
    void activateDeactivateTopologyAction() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-6_TopologyAction.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        NetworkAction remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        Action elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof SwitchAction);
        assertEquals("BBE1AA1  BBE4AA1  1", ((SwitchAction) elementaryAction).getSwitchId());
        assertTrue(((SwitchAction) elementaryAction).isOpen());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-6_TopologyAction.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof SwitchAction);
        assertEquals("DDE3AA1  DDE4AA1  1", ((SwitchAction) elementaryAction).getSwitchId());
        assertFalse(((SwitchAction) elementaryAction).isOpen());
    }

    @Test
    void activateDeactivateRotatingMachineAction() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-7_RotatingMachineAction.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        NetworkAction remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        Action elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof GeneratorAction);
        assertEquals("FFR1AA1 _generator", ((GeneratorAction) elementaryAction).getGeneratorId());
        assertEquals(75d, ((GeneratorAction) elementaryAction).getActivePowerValue().getAsDouble());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-7_RotatingMachineAction.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof GeneratorAction);
        assertEquals("FFR2AA1 _generator", ((GeneratorAction) elementaryAction).getGeneratorId());
        assertEquals(100d, ((GeneratorAction) elementaryAction).getActivePowerValue().getAsDouble());
    }

    @Test
    void activateTapPositionAction() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-8_ActivateTapPositionAction.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(0, crac.getPstRangeActions().size());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-8_ActivateTapPositionAction.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getPstRangeActions().size());
        PstRangeAction remedialAction = crac.getPstRangeActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());
        assertEquals("BBE2AA1  BBE3AA1  1", remedialAction.getNetworkElement().getId());
        assertEquals(1, remedialAction.getRanges().size());
        assertEquals(-4, remedialAction.getRanges().iterator().next().getMinTap());
        assertEquals(8, remedialAction.getRanges().iterator().next().getMaxTap());
    }

    @Test
    void deactivateTapPositionAction() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-9_DeactivateTapPositionAction.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getPstRangeActions().size());
        PstRangeAction remedialAction = crac.getPstRangeActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());
        assertEquals("BBE2AA1  BBE3AA1  1", remedialAction.getNetworkElement().getId());
        assertEquals(1, remedialAction.getRanges().size());
        assertEquals(-4, remedialAction.getRanges().iterator().next().getMinTap());
        assertEquals(8, remedialAction.getRanges().iterator().next().getMaxTap());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-9_DeactivateTapPositionAction.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(0, crac.getPstRangeActions().size());
    }

    @Test
    void activateDeactivateShuntCompensatorModification() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-10_ShuntCompensatorModification.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        NetworkAction remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        Action elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof ShuntCompensatorPositionAction);
        assertEquals("shunt-compensator", ((ShuntCompensatorPositionAction) elementaryAction).getShuntCompensatorId());
        assertEquals(1, ((ShuntCompensatorPositionAction) elementaryAction).getSectionCount());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-10_ShuntCompensatorModification.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action", remedialAction.getId());
        assertEquals("RTE_RA", remedialAction.getName());

        assertEquals(1, remedialAction.getElementaryActions().size());
        elementaryAction = remedialAction.getElementaryActions().iterator().next();
        assertTrue(elementaryAction instanceof ShuntCompensatorPositionAction);
        assertEquals("shunt-compensator", ((ShuntCompensatorPositionAction) elementaryAction).getShuntCompensatorId());
        assertEquals(3, ((ShuntCompensatorPositionAction) elementaryAction).getSectionCount());
    }

    @Test
    void activateDeactivateAllElementaryActions() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-11_ElementaryActions.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        NetworkAction remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action-1", remedialAction.getId());
        assertEquals("RTE_RA1", remedialAction.getName());

        List<Action> elementaryActions = remedialAction.getElementaryActions().stream().sorted(Comparator.comparing(Action::toString)).toList();
        assertEquals(3, elementaryActions.size());

        assertTrue(elementaryActions.get(0) instanceof GeneratorAction);
        assertEquals("FFR1AA1 _generator", ((GeneratorAction) elementaryActions.get(0)).getGeneratorId());
        assertEquals(75d, ((GeneratorAction) elementaryActions.get(0)).getActivePowerValue().getAsDouble());

        assertTrue(elementaryActions.get(1) instanceof ShuntCompensatorPositionAction);
        assertEquals("shunt-compensator", ((ShuntCompensatorPositionAction) elementaryActions.get(1)).getShuntCompensatorId());
        assertEquals(1, ((ShuntCompensatorPositionAction) elementaryActions.get(1)).getSectionCount());

        assertTrue(elementaryActions.get(2) instanceof SwitchAction);
        assertEquals("BBE1AA1  BBE4AA1  1", ((SwitchAction) elementaryActions.get(2)).getSwitchId());
        assertTrue(((SwitchAction) elementaryActions.get(2)).isOpen());

        assertRaNotImported(cracCreationContext, "remedial-action-2", ImportStatus.NOT_FOR_RAO, "Remedial action remedial-action-2 will not be imported because it has no elementary action");

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-11_ElementaryActions.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getNetworkActions().size());
        remedialAction = crac.getNetworkActions().iterator().next();
        assertEquals("remedial-action-2", remedialAction.getId());
        assertEquals("RTE_RA2", remedialAction.getName());

        elementaryActions = remedialAction.getElementaryActions().stream().sorted(Comparator.comparing(Action::toString)).toList();
        assertEquals(3, elementaryActions.size());

        assertTrue(elementaryActions.get(0) instanceof GeneratorAction);
        assertEquals("FFR2AA1 _generator", ((GeneratorAction) elementaryActions.get(0)).getGeneratorId());
        assertEquals(100d, ((GeneratorAction) elementaryActions.get(0)).getActivePowerValue().getAsDouble());

        assertTrue(elementaryActions.get(1) instanceof ShuntCompensatorPositionAction);
        assertEquals("shunt-compensator", ((ShuntCompensatorPositionAction) elementaryActions.get(1)).getShuntCompensatorId());
        assertEquals(4, ((ShuntCompensatorPositionAction) elementaryActions.get(1)).getSectionCount());

        assertTrue(elementaryActions.get(2) instanceof SwitchAction);
        assertEquals("DDE3AA1  DDE4AA1  1", ((SwitchAction) elementaryActions.get(2)).getSwitchId());
        assertFalse(((SwitchAction) elementaryActions.get(2)).isOpen());

        assertRaNotImported(cracCreationContext, "remedial-action-1", ImportStatus.NOT_FOR_RAO, "Remedial action remedial-action-1 will not be imported because it has no elementary action");
    }

    @Test
    void activateDeactivateContingencyWithRemedialAction() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-12_ContingencyWithRemedialAction.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        List<Contingency> contingencies = crac.getContingencies().stream().sorted(Comparator.comparing(Contingency::getId)).toList();
        assertEquals(3, contingencies.size());
        assertContingencyEquality(contingencies.get(0), "contingency-1", "RTE_CO1", Set.of("FFR1AA1  FFR2AA1  1"));
        assertContingencyEquality(contingencies.get(1), "contingency-2", "RTE_CO2", Set.of("FFR1AA1  FFR3AA1  1"));
        assertContingencyEquality(contingencies.get(2), "contingency-3", "RTE_CO3", Set.of("FFR1AA1  FFR4AA1  1"));

        List<NetworkAction> networkActions = crac.getNetworkActions().stream().sorted(Comparator.comparing(NetworkAction::getId)).toList();
        assertEquals(2, networkActions.size());

        assertEquals("remedial-action-1", networkActions.get(0).getId());
        assertEquals("RTE_RA1", networkActions.get(0).getName());
        assertEquals(3, networkActions.get(0).getUsageRules().size());
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-1", "contingency-1", CURATIVE_1_INSTANT_ID, UsageMethod.AVAILABLE);
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-1", "contingency-1", CURATIVE_2_INSTANT_ID, UsageMethod.AVAILABLE);
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-1", "contingency-1", CURATIVE_3_INSTANT_ID, UsageMethod.AVAILABLE);

        assertEquals("remedial-action-2", networkActions.get(1).getId());
        assertEquals("RTE_RA2", networkActions.get(1).getName());
        assertEquals(3, networkActions.get(1).getUsageRules().size());
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-2", "contingency-3", CURATIVE_1_INSTANT_ID, UsageMethod.AVAILABLE);
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-2", "contingency-3", CURATIVE_2_INSTANT_ID, UsageMethod.AVAILABLE);
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-2", "contingency-3", CURATIVE_3_INSTANT_ID, UsageMethod.AVAILABLE);

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-12_ContingencyWithRemedialAction.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        contingencies = crac.getContingencies().stream().sorted(Comparator.comparing(Contingency::getId)).toList();
        assertEquals(2, contingencies.size());
        assertContingencyEquality(contingencies.get(0), "contingency-1", "RTE_CO1", Set.of("FFR1AA1  FFR2AA1  1"));
        assertContingencyEquality(contingencies.get(1), "contingency-2", "RTE_CO2", Set.of("FFR1AA1  FFR3AA1  1"));
        assertContingencyNotImported(cracCreationContext, "contingency-3", ImportStatus.NOT_FOR_RAO, "Contingency contingency-3 will not be imported because its field mustStudy is set to false");

        networkActions = crac.getNetworkActions().stream().sorted(Comparator.comparing(NetworkAction::getId)).toList();
        assertEquals(2, networkActions.size());

        assertEquals("remedial-action-1", networkActions.get(0).getId());
        assertEquals("RTE_RA1", networkActions.get(0).getName());
        assertEquals(3, networkActions.get(0).getUsageRules().size());
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-1", "contingency-2", CURATIVE_1_INSTANT_ID, UsageMethod.AVAILABLE);
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-1", "contingency-2", CURATIVE_2_INSTANT_ID, UsageMethod.AVAILABLE);
        assertHasOnContingencyStateUsageRule(cracCreationContext, "remedial-action-1", "contingency-2", CURATIVE_3_INSTANT_ID, UsageMethod.AVAILABLE);

        assertEquals("remedial-action-2", networkActions.get(1).getId());
        assertEquals("RTE_RA2", networkActions.get(1).getName());
        assertEquals(0, networkActions.get(1).getUsageRules().size());
    }

    @Test
    void activateDeactivateAngleCnec() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-13_AngleCNEC.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - preventive"), "RTE_AE1 (assessed-element-1) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 30d, -30d, "RTE", "ES-FR");
        assertCnecNotImported(cracCreationContext, "assessed-element-2", ImportStatus.NOT_FOR_RAO, "AssessedElement assessed-element-2 ignored because it is not enabled");

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-13_AngleCNEC.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - preventive"), "RTE_AE2 (assessed-element-2) - preventive", "BBE4AA1 ", "BBE1AA1 ", PREVENTIVE_INSTANT_ID, null, 45d, -45d, "RTE", "ES-FR");
        assertCnecNotImported(cracCreationContext, "assessed-element-1", ImportStatus.NOT_FOR_RAO, "AssessedElement assessed-element-1 ignored because it is not enabled");
    }

    @Test
    void changeAngleCnecThreshold() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-14_VoltageAngleLimit.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE (assessed-element) - preventive"), "RTE_AE (assessed-element) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 30d, -30d, "RTE", "ES-FR");

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-14_VoltageAngleLimit.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE (assessed-element) - preventive"), "RTE_AE (assessed-element) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 45d, -45d, "RTE", "ES-FR");
    }

    @Test
    void activateDeactivateAssessedElementWithContingency() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-15_AssessedElementWithContingency.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(4, crac.getAngleCnecs().size());
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - preventive"), "RTE_AE1 (assessed-element-1) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - RTE_CO1 - curative 3"), "RTE_AE1 (assessed-element-1) - RTE_CO1 - curative 3", "BBE1AA1 ", "BBE4AA1 ", CURATIVE_3_INSTANT_ID, "contingency-1", 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - preventive"), "RTE_AE2 (assessed-element-2) - preventive", "BBE4AA1 ", "BBE1AA1 ", PREVENTIVE_INSTANT_ID, null, 45d, -45d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - RTE_CO3 - curative 3"), "RTE_AE2 (assessed-element-2) - RTE_CO3 - curative 3", "BBE4AA1 ", "BBE1AA1 ", CURATIVE_3_INSTANT_ID, "contingency-3", 45d, -45d, "RTE", "ES-FR");
        assertCnecNotImported(cracCreationContext, "assessed-element-1", ImportStatus.NOT_FOR_RAO, "The link between contingency contingency-2 and the assessed element is disabled");

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-15_AssessedElementWithContingency.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(3, crac.getAngleCnecs().size());
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - preventive"), "RTE_AE1 (assessed-element-1) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - RTE_CO2 - curative 3"), "RTE_AE1 (assessed-element-1) - RTE_CO2 - curative 3", "BBE1AA1 ", "BBE4AA1 ", CURATIVE_3_INSTANT_ID, "contingency-2", 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - preventive"), "RTE_AE2 (assessed-element-2) - preventive", "BBE4AA1 ", "BBE1AA1 ", PREVENTIVE_INSTANT_ID, null, 45d, -45d, "RTE", "ES-FR");
        assertCnecNotImported(cracCreationContext, "assessed-element-1", ImportStatus.NOT_FOR_RAO, "The link between contingency contingency-1 and the assessed element is disabled");
        assertCnecNotImported(cracCreationContext, "assessed-element-2", ImportStatus.INCONSISTENCY_IN_DATA, "The contingency contingency-3 linked to the assessed element does not exist in the CRAC");
    }

    @Test
    void activateDeactivateAssessedElementWithRemedialAction() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-16_AssessedElementWithRemedialAction.zip", NETWORK, "2023-01-01T22:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getContingencies().size());
        assertContingencyEquality(crac.getContingencies().iterator().next(), "contingency", "RTE_CO", Set.of("FFR1AA1  FFR2AA1  1"));

        assertEquals(6, crac.getAngleCnecs().size());
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - preventive"), "RTE_AE1 (assessed-element-1) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - RTE_CO - curative 3"), "RTE_AE1 (assessed-element-1) - RTE_CO - curative 3", "BBE1AA1 ", "BBE4AA1 ", CURATIVE_3_INSTANT_ID, "contingency", 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - preventive"), "RTE_AE2 (assessed-element-2) - preventive", "BBE4AA1 ", "BBE1AA1 ", PREVENTIVE_INSTANT_ID, null, 45d, -45d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - RTE_CO - curative 3"), "RTE_AE2 (assessed-element-2) - RTE_CO - curative 3", "BBE4AA1 ", "BBE1AA1 ", CURATIVE_3_INSTANT_ID, "contingency", 45d, -45d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE3 (assessed-element-3) - preventive"), "RTE_AE3 (assessed-element-3) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 15d, -15d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE3 (assessed-element-3) - RTE_CO - curative 3"), "RTE_AE3 (assessed-element-3) - RTE_CO - curative 3", "BBE1AA1 ", "BBE4AA1 ", CURATIVE_3_INSTANT_ID, "contingency", 15d, -15d, "RTE", "ES-FR");

        List<NetworkAction> remedialActions = crac.getNetworkActions().stream().sorted(Comparator.comparing(NetworkAction::getId)).toList();
        assertEquals(2, remedialActions.size());

        assertEquals("remedial-action-1", remedialActions.get(0).getId());
        assertEquals("RTE_RA1", remedialActions.get(0).getName());
        assertEquals(3, remedialActions.get(0).getUsageRules().size());
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO - curative 3", crac.getInstant(CURATIVE_1_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO - curative 3", crac.getInstant(CURATIVE_2_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE1 (assessed-element-1) - RTE_CO - curative 3", crac.getInstant(CURATIVE_3_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);

        assertEquals("remedial-action-2", remedialActions.get(1).getId());
        assertEquals("RTE_RA2", remedialActions.get(1).getName());
        assertEquals(3, remedialActions.get(1).getUsageRules().size());
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE3 (assessed-element-3) - RTE_CO - curative 3", crac.getInstant(CURATIVE_1_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE3 (assessed-element-3) - RTE_CO - curative 3", crac.getInstant(CURATIVE_2_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-2", "RTE_AE3 (assessed-element-3) - RTE_CO - curative 3", crac.getInstant(CURATIVE_3_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-16_AssessedElementWithRemedialAction.zip", NETWORK, "2024-01-31T12:30Z");
        crac = cracCreationContext.getCrac();

        assertEquals(1, crac.getContingencies().size());
        assertContingencyEquality(crac.getContingencies().iterator().next(), "contingency", "RTE_CO", Set.of("FFR1AA1  FFR2AA1  1"));

        assertEquals(4, crac.getAngleCnecs().size());
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - preventive"), "RTE_AE1 (assessed-element-1) - preventive", "BBE1AA1 ", "BBE4AA1 ", PREVENTIVE_INSTANT_ID, null, 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE1 (assessed-element-1) - RTE_CO - curative 3"), "RTE_AE1 (assessed-element-1) - RTE_CO - curative 3", "BBE1AA1 ", "BBE4AA1 ", CURATIVE_3_INSTANT_ID, "contingency", 30d, -30d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - preventive"), "RTE_AE2 (assessed-element-2) - preventive", "BBE4AA1 ", "BBE1AA1 ", PREVENTIVE_INSTANT_ID, null, 45d, -45d, "RTE", "ES-FR");
        assertAngleCnecEquality(crac.getAngleCnec("RTE_AE2 (assessed-element-2) - RTE_CO - curative 3"), "RTE_AE2 (assessed-element-2) - RTE_CO - curative 3", "BBE4AA1 ", "BBE1AA1 ", CURATIVE_3_INSTANT_ID, "contingency", 45d, -45d, "RTE", "ES-FR");

        remedialActions = crac.getNetworkActions().stream().sorted(Comparator.comparing(NetworkAction::getId)).toList();
        assertEquals(2, remedialActions.size());

        assertEquals("remedial-action-1", remedialActions.get(0).getId());
        assertEquals("RTE_RA1", remedialActions.get(0).getName());
        assertEquals(3, remedialActions.get(0).getUsageRules().size());
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE2 (assessed-element-2) - RTE_CO - curative 3", crac.getInstant(CURATIVE_1_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE2 (assessed-element-2) - RTE_CO - curative 3", crac.getInstant(CURATIVE_2_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);
        assertHasOnConstraintUsageRule(cracCreationContext, "remedial-action-1", "RTE_AE2 (assessed-element-2) - RTE_CO - curative 3", crac.getInstant(CURATIVE_3_INSTANT_ID), UsageMethod.AVAILABLE, AngleCnec.class);

        assertEquals("remedial-action-2", remedialActions.get(1).getId());
        assertEquals("RTE_RA2", remedialActions.get(1).getName());
        assertEquals(0, remedialActions.get(1).getUsageRules().size());
    }

    @Test
    void activateDeactivateRemedialActionDependency() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-17_RemedialActionDependency.zip", NETWORK, "2023-01-01T22:30Z");
        assertNetworkActionImported(cracCreationContext, "remedial-action-group", Set.of("FFR1AA1 _generator", "BBE1AA1  BBE4AA1  1"), true, 1, "RTE");
        assertEquals("Remedial Action Group", cracCreationContext.getCrac().getRemedialAction("remedial-action-group").getName());
        assertNetworkActionImported(cracCreationContext, "redispatching-action-fr2", Set.of("FFR2AA1 _generator"), false, 1, "RTE");
        assertEquals("RTE_Redispatch -70 MW FR2", cracCreationContext.getCrac().getRemedialAction("redispatching-action-fr2").getName());
        assertEquals("The RemedialActionGroup with mRID remedial-action-group was turned into a remedial action from the following remedial actions: redispatching-action-fr1, topological-action",
            cracCreationContext.getRemedialActionCreationContext("remedial-action-group").getImportStatusDetail());

        // With SSI
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-17_RemedialActionDependency.zip", NETWORK, "2024-01-31T12:30Z");
        assertNetworkActionImported(cracCreationContext, "remedial-action-group", Set.of("FFR2AA1 _generator", "BBE1AA1  BBE4AA1  1"), true, 1, "RTE");
        assertEquals("Remedial Action Group", cracCreationContext.getCrac().getRemedialAction("remedial-action-group").getName());
        assertNetworkActionImported(cracCreationContext, "redispatching-action-fr1", Set.of("FFR1AA1 _generator"), false, 1, "RTE");
        assertEquals("RTE_Redispatch 70 MW FR1", cracCreationContext.getCrac().getRemedialAction("redispatching-action-fr1").getName());
        assertEquals("The RemedialActionGroup with mRID remedial-action-group was turned into a remedial action from the following remedial actions: redispatching-action-fr2, topological-action",
            cracCreationContext.getRemedialActionCreationContext("remedial-action-group").getImportStatusDetail());
    }

    @Test
    void overrideRemedialActionGroup() {
        // General case
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-18_RemedialActionGroup.zip", NETWORK, "2023-01-01T22:30Z");
        assertNetworkActionImported(cracCreationContext, "remedial-action-group", Set.of("BBE1AA1  BBE4AA1  1", "DDE3AA1  DDE4AA1  1"), true, 1, "RTE");
        assertEquals("The RemedialActionGroup with mRID remedial-action-group was turned into a remedial action from the following remedial actions: open-be1-be4, open-de3-de4",
            cracCreationContext.getRemedialActionCreationContext("remedial-action-group").getImportStatusDetail());

        // With SSI 1
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-18_RemedialActionGroup.zip", NETWORK, "2024-01-31T12:30Z");
        assertEquals(0, cracCreationContext.getCrac().getRemedialActions().size());
        assertEquals("Remedial action group remedial-action-group will not be imported because the remedial action open-be1-be4 does not exist or not imported. All RA's depending in that group will be ignored: open-be1-be4, open-de3-de4",
            cracCreationContext.getRemedialActionCreationContext("remedial-action-group").getImportStatusDetail());
        // With SSI 2
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-18_RemedialActionGroup.zip", NETWORK, "2024-02-01T12:30Z");
        assertEquals(2, cracCreationContext.getCrac().getRemedialActions().size());
        assertEquals("The RemedialActionGroup with mRID remedial-action-group was turned into a remedial action from the following remedial actions: open-de3-de4",
            cracCreationContext.getRemedialActionCreationContext("remedial-action-group").getImportStatusDetail());

        // With SSI 3
        cracCreationContext = getNcCracCreationContext("/profiles/ssi/SSI-18_RemedialActionGroup.zip", NETWORK, "2024-02-02T12:30Z");
        assertEquals(1, cracCreationContext.getCrac().getRemedialActions().size());
        assertEquals("The RemedialActionGroup with mRID remedial-action-group was turned into a remedial action from the following remedial actions: open-de3-de4",
            cracCreationContext.getRemedialActionCreationContext("remedial-action-group").getImportStatusDetail());
    }
}