AngleMonitoringSteps.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.tests.steps;

import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.openrao.commons.PhysicalParameter;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.crac.api.Instant;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.openrao.data.crac.api.cnec.AngleCnec;
import com.powsybl.openrao.data.crac.impl.AngleCnecValue;
import com.powsybl.openrao.data.raoresult.api.RaoResult;
import com.powsybl.openrao.monitoring.Monitoring;
import com.powsybl.openrao.monitoring.MonitoringInput;
import com.powsybl.openrao.monitoring.results.CnecResult;
import com.powsybl.openrao.monitoring.results.MonitoringResult;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

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

/**
 * @author Godelaine de Montmorillon {@literal <godelaine.demontmorillon at rte-france.com>}
 */
public class AngleMonitoringSteps {
    private static final double DOUBLE_TOLERANCE = 1e-1;

    @When("I launch angle monitoring at {string} on {int} threads")
    public void iLaunchAngleMonitoring(String cracTimestamp, int numberOfLoadFlowsInParallel) throws IOException {
        runAngleMonitoring(cracTimestamp, numberOfLoadFlowsInParallel);
    }

    @When("I launch angle monitoring on {int} threads")
    public void iLaunchAngleMonitoring(int numberOfLoadFlowsInParallel) throws IOException {
        runAngleMonitoring(null, numberOfLoadFlowsInParallel);
    }

    private void runAngleMonitoring(String cracTimestamp, int numberOfLoadFlowsInParallel) throws IOException {
        LoadFlowParameters loadFlowParameters = new LoadFlowParameters();
        loadFlowParameters.setDc(false);
        CommonTestData.loadData(cracTimestamp);
        Network network = CommonTestData.getNetwork();
        RaoResult raoResult = CommonTestData.getRaoResult();
        MonitoringInput angleMonitoringInput = MonitoringInput.buildWithAngle(network, CommonTestData.getCrac(), raoResult, CommonTestData.getMonitoringGlsks()).build();
        MonitoringResult angleMonitoringResult = new Monitoring("OpenLoadFlow", loadFlowParameters).runMonitoring(angleMonitoringInput, numberOfLoadFlowsInParallel);
        CommonTestData.setMonitoringResult(angleMonitoringResult);
    }

    @Then("the angle monitoring result is {string}")
    public void statusCheck(String expectedStatus) {
        assertEquals(CommonTestData.getMonitoringResult().getStatus().toString(), expectedStatus);
        assertEquals(expectedStatus.equalsIgnoreCase("secure"), CommonTestData.getRaoResult().isSecure(PhysicalParameter.ANGLE));
    }

    @Then("the applied remedial actions should be:")
    public void appliedCras(DataTable arg1) {
        List<Map<String, String>> expectedCras = arg1.asMaps(String.class, String.class);
        for (Map<String, String> expectedCra : expectedCras) {
            String craName = expectedCra.get("Name");
            String contingency = expectedCra.get("Contingency");
            int numberOfCras = Integer.parseInt(expectedCra.get("NumberOfCras"));
            Instant instant = CommonTestData.getCrac().getInstant(expectedCra.get("Instant").toLowerCase());
            State state;
            if (instant.isPreventive()) {
                state = CommonTestData.getCrac().getPreventiveState();
            } else {
                state = CommonTestData.getCrac().getState(contingency, instant);
            }
            assertTrue(CommonTestData.getMonitoringResult().getAppliedRas().containsKey(state));
            assertEquals(numberOfCras, CommonTestData.getMonitoringResult().getAppliedRas(state).size());
            assertTrue(CommonTestData.getMonitoringResult().getAppliedRas(state).stream().anyMatch(networkAction -> networkAction.getId().equals(craName)));
        }
    }

    @Then("the AngleCnecs should have the following angles:")
    public void angleCnecValues(DataTable arg1) {
        List<Map<String, String>> expectedCnecs = arg1.asMaps(String.class, String.class);
        assertEquals(expectedCnecs.size(), CommonTestData.getMonitoringResult().getCnecResults().size());
        for (Map<String, String> expectedCnec : expectedCnecs) {
            String cnecId = expectedCnec.get("AngleCnecId");
            String cnecName = expectedCnec.get("Name");
            String contingency = expectedCnec.get("Contingency");
            Instant instant = CommonTestData.getCrac().getInstant(expectedCnec.get("Instant").toLowerCase());
            Double expectedAngle = Double.parseDouble(expectedCnec.get("Angle"));

            State state;
            if (instant.isPreventive()) {
                state = CommonTestData.getCrac().getPreventiveState();
            } else {
                state = CommonTestData.getCrac().getState(contingency, instant);
            }

            Set<CnecResult> angleResults = CommonTestData.getMonitoringResult().getCnecResults().stream().filter(angleResult -> angleResult.getCnec().getId().equals(cnecId)
                    && angleResult.getCnec().getName().equals(cnecName)
                    && angleResult.getCnec().getState().equals(state))
                    .collect(Collectors.toSet());
            assertNotNull(angleResults);
            assertEquals(1, angleResults.size());
            AngleCnec angleCnec = (AngleCnec) angleResults.iterator().next().getCnec();
            AngleCnecValue angleValue = (AngleCnecValue) angleResults.iterator().next().getValue();

            assertEquals(expectedAngle, angleValue.value(), DOUBLE_TOLERANCE);

            if (expectedCnec.get("LowerBound") != null) {
                Optional<Double> lowerBound = angleCnec.getLowerBound(Unit.DEGREE);
                if (lowerBound.isPresent()) {
                    assertEquals(Double.parseDouble(expectedCnec.get("LowerBound")), lowerBound.get(), DOUBLE_TOLERANCE);
                } else {
                    assertEquals("null", expectedCnec.get("LowerBound"));
                }
            }
            if (expectedCnec.get("UpperBound") != null) {
                Optional<Double> upperBound = angleCnec.getUpperBound(Unit.DEGREE);
                if (upperBound.isPresent()) {
                    assertEquals(Double.parseDouble(expectedCnec.get("UpperBound")), upperBound.get(), DOUBLE_TOLERANCE);
                } else {
                    assertEquals("null", expectedCnec.get("UpperBound"));
                }
            }
        }
    }
}