CommonTestData.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.glsk.commons.ZonalData;
import com.powsybl.iidm.modification.scalable.Scalable;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.serde.NetworkSerDe;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.CracCreationContext;
import com.powsybl.openrao.data.crac.api.parameters.CracCreationParameters;
import com.powsybl.openrao.data.crac.api.parameters.JsonCracCreationParameters;
import com.powsybl.openrao.data.crac.io.cim.parameters.CimCracCreationParameters;
import com.powsybl.openrao.data.crac.io.fbconstraint.parameters.FbConstraintCracCreationParameters;
import com.powsybl.openrao.data.crac.io.nc.parameters.NcCracCreationParameters;
import com.powsybl.openrao.data.glsk.virtual.hubs.GlskVirtualHubs;
import com.powsybl.openrao.data.raoresult.api.RaoResult;
import com.powsybl.openrao.data.refprog.referenceprogram.ReferenceProgram;
import com.powsybl.openrao.monitoring.results.MonitoringResult;
import com.powsybl.openrao.monitoring.results.RaoResultWithAngleMonitoring;
import com.powsybl.openrao.raoapi.json.JsonRaoParameters;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.OpenRaoSearchTreeParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters;
import com.powsybl.openrao.tests.utils.CoreCcPreprocessor;
import com.powsybl.openrao.tests.utils.Helpers;
import com.powsybl.openrao.virtualhubs.VirtualHubsConfiguration;
import com.powsybl.openrao.virtualhubs.xml.XmlVirtualHubsConfiguration;
import com.powsybl.sensitivity.SensitivityVariableSet;
import io.cucumber.java.Before;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import org.apache.commons.lang3.tuple.Pair;

import java.io.*;
import java.time.OffsetDateTime;

import static com.powsybl.openrao.tests.utils.Helpers.*;
import static com.powsybl.openrao.tests.utils.Helpers.getOffsetDateTimeFromBrusselsTimestamp;

public final class CommonTestData {

    private static final String DEFAULT_CRAC_CREATION_PARAMETERS_PATH = "cracCreationParameters/common/CracCreationParameters_default.json";
    private static final String DEFAULT_RAO_PARAMETERS_PATH = "configurations/common/RaoParameters_default.json";

    private static String dataPrefix = "src/test/resources/files/";

    private static String overrideLinearSolver = null;

    private static String networkPath;
    private static Boolean coreCcNetworkPreprocessing = false;
    private static Network network;

    private static String cracPath;
    private static String cracCreationParametersPath;
    private static CracCreationContext cracCreationContext;
    private static Crac crac;

    private static String raoParametersPath;
    private static RaoParameters raoParameters;

    private static String loopflowGlskPath;
    private static String monitoringGlskPath;
    private static ZonalData<Scalable> monitoringGlsks;
    private static ZonalData<SensitivityVariableSet> loopflowGlsks;

    private static String raoResultPath;
    private static RaoResult raoResult;

    private static String refProgPath;
    private static ReferenceProgram referenceProgram;

    private static String virtualHubsConfigPath;

    private static MonitoringResult monitoringResult;

    private static String timestamp;

    private CommonTestData() {
        // should not be instantiated
    }

    public static void setRaoResult(RaoResult raoResult) {
        if (CommonTestData.monitoringResult != null) {
            // update RAO result with angle values
            CommonTestData.raoResult = new RaoResultWithAngleMonitoring(raoResult, CommonTestData.monitoringResult);
        } else {
            CommonTestData.raoResult = raoResult;
        }
    }

    public static void setMonitoringResult(MonitoringResult result) {
        CommonTestData.monitoringResult = result;
        if (CommonTestData.raoResult != null) {
            // update RAO result with angle values
            CommonTestData.raoResult = new RaoResultWithAngleMonitoring(CommonTestData.raoResult, CommonTestData.monitoringResult);
        }
    }

    public static void setDataLocation(String location) {
        dataPrefix = location.concat("/");
    }

    public static void resetDataLocation() {
        setDataLocation("src/test/resources/files/");
    }

    public static String getResourcesPath() {
        return dataPrefix;
    }

    public static void setLinearSolver(String solver) {
        overrideLinearSolver = solver;
    }

    public static void resetLinearSolver() {
        overrideLinearSolver = null;
    }

    @Before
    // Reset data to null before every scenario
    public static void reset() {
        networkPath = null;
        raoParametersPath = null;
        cracCreationParametersPath = null;
        loopflowGlskPath = null;
        monitoringGlskPath = null;
        refProgPath = null;
        cracPath = null;
        raoResultPath = null;
        crac = null;
        cracCreationContext = null;
        network = null;
        virtualHubsConfigPath = null;
        raoParameters = null;
        loopflowGlsks = null;
        monitoringGlsks = null;
        referenceProgram = null;
        raoResult = null;
        monitoringResult = null;
    }

    @Given("crac file is {string}")
    public static void cracFileIs(String path) {
        cracPath = getResourcesPath().concat("crac/").concat(path);
    }

    @Given("crac creation parameters file is {string}")
    public static void cracCreationParametersFileIs(String path) {
        cracCreationParametersPath = getResourcesPath().concat("cracCreationParameters/").concat(path);
    }

    @Given("network file is {string}")
    public static void networkFileIs(String path) {
        setNetworkInput(path, false);
    }

    @Given("network file is {string} for CORE CC")
    public static void networkFileIsForCoreCC(String path) {
        // We do this in the code, because replacing UCTE files with XIIDM files multiplies their size by ~10
        setNetworkInput(path, true);
    }

    private static void setNetworkInput(String path, Boolean coreCcPreprocessing) {
        networkPath = getResourcesPath().concat("cases/").concat(path);
        coreCcNetworkPreprocessing = coreCcPreprocessing;
    }

    @Given("configuration file is {string}")
    public static void configurationFileIs(String path) {
        raoParametersPath = getResourcesPath().concat("configurations/").concat(path);
    }

    @Given("loopflow glsk file is {string}")
    public static void loopflowGlskFileIs(String path) {
        loopflowGlskPath = getResourcesPath().concat("glsks/").concat(path);
    }

    @Given("monitoring glsk file is {string}")
    public static void monitoringGlskFileIs(String path) {
        monitoringGlskPath = getResourcesPath().concat("glsks/").concat(path);
    }

    @Given("RefProg file is {string}")
    public static void refProgFileIs(String path) {
        refProgPath = getResourcesPath().concat("refprogs/").concat(path);
    }

    @Given("Virtual hubs configuration file is {string}")
    public static void virtualHubsConfigurationFileIs(String path) {
        virtualHubsConfigPath = getResourcesPath().concat("virtualhubs/").concat(path);
    }

    @Given("RaoResult file is {string}")
    public static void raoResultIs(String path) {
        raoResultPath = getResourcesPath().concat("raoresults/").concat(path);
    }

    @When("I import data")
    public static void iImportData() throws IOException {
        loadData(null);
    }

    @When("I import data at {string}")
    public static void iImportDataAt(String timestamp) throws IOException {
        loadData(timestamp);
    }

    public static Network getNetwork() {
        return network;
    }

    public static Network getNetworkClone() {
        return NetworkSerDe.copy(network);
    }

    public static Crac getCrac() {
        return crac;
    }

    public static CracCreationContext getCracCreationContext() {
        return cracCreationContext;
    }

    public static RaoParameters getRaoParameters() {
        return raoParameters;
    }

    public static ZonalData<SensitivityVariableSet> getLoopflowGlsks() {
        return loopflowGlsks;
    }

    public static ZonalData<Scalable> getMonitoringGlsks() {
        return monitoringGlsks;
    }

    public static ReferenceProgram getReferenceProgram() {
        return referenceProgram;
    }

    public static RaoResult getRaoResult() {
        return raoResult;
    }

    public static MonitoringResult getMonitoringResult() {
        return monitoringResult;
    }

    public static String getTimestamp() {
        return timestamp;
    }

    public static void loadData(String timestamp) throws IOException {
        // Detect the CRAC format first. If CIM, we have to import the network using RDF IDs as identifiable IDs
        String cracFormat = null;
        if (cracPath != null) {
            cracFormat = Helpers.getCracFormat(getFile(cracPath));
        } else {
            throw new OpenRaoException("You have not defined a CRAC file. All tests need a CRAC file.");
        }

        // Network
        if (networkPath != null) {
            network = importNetwork(getFile(networkPath), "CimCrac".equals(cracFormat));
        } else {
            throw new OpenRaoException("You have not defined a network file. All tests need a network file.");
        }
        if (coreCcNetworkPreprocessing) {
            CoreCcPreprocessor.applyCoreCcNetworkPreprocessing(network);
        }

        // CracCreationParameters
        CracCreationParameters cracCreationParameters = null;
        String ccpToImport = (cracCreationParametersPath == null) ? getResourcesPath().concat(DEFAULT_CRAC_CREATION_PARAMETERS_PATH) : cracCreationParametersPath;
        InputStream cracCreationParametersInputStream;
        try {
            cracCreationParametersInputStream = new BufferedInputStream(new FileInputStream(getFile(ccpToImport)));
            cracCreationParameters = JsonCracCreationParameters.read(cracCreationParametersInputStream);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        OffsetDateTime offsetDateTime = null;
        // Add timestamp manually if no crac creation parameters file were given explicitly but a timestamp is still needed
        if (timestamp != null && cracCreationParametersPath == null) {
            offsetDateTime = getOffsetDateTimeFromBrusselsTimestamp(timestamp);
            addTimestampToCracCreationParameters(cracFormat, offsetDateTime, cracCreationParameters);
        } else if (cracCreationParametersPath != null) { //
            offsetDateTime = importTimestampFromCracCreationParameters(cracFormat, cracCreationParameters);
        }

        // Crac
        Pair<Crac, CracCreationContext> cracImportResult = importCrac(getFile(cracPath), network, cracCreationParameters);
        crac = cracImportResult.getLeft();
        cracCreationContext = cracImportResult.getRight();

        // RAO parameters
        if (raoParametersPath != null) {
            raoParameters = buildConfig(getFile(raoParametersPath));
        } else {
            raoParameters = buildDefaultConfig();
        }
        if (overrideLinearSolver != null) {
            raoParameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver()
                .setSolver(SearchTreeRaoRangeActionsOptimizationParameters.Solver.valueOf(overrideLinearSolver.toUpperCase()));
        }

        // Loopflow GLSK
        // only work with UCTE GLSK files
        if (loopflowGlskPath != null) {
            loopflowGlsks = importUcteGlskFile(getFile(loopflowGlskPath), offsetDateTime, network);
        }

        // Monitoring GLSK
        if (monitoringGlskPath != null) {
            monitoringGlsks = importMonitoringGlskFile(getFile(monitoringGlskPath), offsetDateTime, network);
        }

        // Reference program
        if (refProgPath != null) {
            referenceProgram = importRefProg(getFile(refProgPath), offsetDateTime);
        }

        // RaoResult
        if (raoResultPath != null) {
            raoResult = importRaoResult(getFile(raoResultPath));
        }

        // Virtual hubs configuration
        if (virtualHubsConfigPath != null) {
            if (referenceProgram != null && loopflowGlsks != null) {
                VirtualHubsConfiguration virtualHubsConfiguration = XmlVirtualHubsConfiguration.importConfiguration(new FileInputStream(getFile(virtualHubsConfigPath)));
                ZonalData<SensitivityVariableSet> glskOfVirtualHubs = GlskVirtualHubs.getVirtualHubGlsks(virtualHubsConfiguration, network, referenceProgram);
                loopflowGlsks.addAll(glskOfVirtualHubs);
            } else {
                throw new OpenRaoException("In order to import a virtual hubs configuration file, you should define a reference program file and a GLSK file.");
            }
        }

    }

    private static OffsetDateTime importTimestampFromCracCreationParameters(String cracFormat, CracCreationParameters cracCreationParameters) {
        if (cracFormat.equals("CimCrac")) {
            return cracCreationParameters.getExtension(CimCracCreationParameters.class).getTimestamp();
        } else if (cracFormat.equals("FlowBasedConstraintDocument")) {
            return cracCreationParameters.getExtension(FbConstraintCracCreationParameters.class).getTimestamp();
        } else if (cracFormat.equals("NC")) {
            return cracCreationParameters.getExtension(NcCracCreationParameters.class).getTimestamp();
        } else {
            return null;
        }
    }

    private static void addTimestampToCracCreationParameters(String cracFormat, OffsetDateTime timestamp, CracCreationParameters cracCreationParameters) {

        if (cracFormat.equals("CimCrac")) {
            CimCracCreationParameters cimParams = new CimCracCreationParameters();
            cimParams.setTimestamp(timestamp);
            cracCreationParameters.addExtension(CimCracCreationParameters.class, cimParams);
        } else if (cracFormat.equals("FlowBasedConstraintDocument")) {
            FbConstraintCracCreationParameters fbConstraintParams = new FbConstraintCracCreationParameters();
            fbConstraintParams.setTimestamp(timestamp);
            cracCreationParameters.addExtension(FbConstraintCracCreationParameters.class, fbConstraintParams);
        } else if (cracFormat.equals("NC")) {
            NcCracCreationParameters csaParams = new NcCracCreationParameters();
            csaParams.setTimestamp(timestamp);
            cracCreationParameters.addExtension(NcCracCreationParameters.class, csaParams);
        }
    }

    private static RaoParameters buildDefaultConfig() {
        try (InputStream configStream = new FileInputStream(getFile(getResourcesPath().concat(DEFAULT_RAO_PARAMETERS_PATH)))) {
            return JsonRaoParameters.read(configStream);
        } catch (Exception e) {
            throw new IllegalArgumentException("Could not load default configuration file", e);
        }
    }

    private static RaoParameters buildConfig(File configFile) {
        RaoParameters config = buildDefaultConfig();
        try (InputStream configStream = new FileInputStream(configFile)) {
            JsonRaoParameters.update(config, configStream);
        } catch (IOException | UncheckedIOException e) {
            throw new IllegalArgumentException("Configuration file is not in expected JSON format", e);
        } catch (AssertionError e) {
            throw new IllegalArgumentException("Unknown parameter in configuration file", e);
        }
        return config;
    }
}