ContingencyResultsUtils.java

/**
 * Copyright (c) 2023, 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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.dynawo.contingency;

import com.powsybl.commons.report.ReportNode;
import com.powsybl.contingency.Contingency;
import com.powsybl.dynawo.commons.CommonReports;
import com.powsybl.dynawo.commons.timeline.TimelineEntry;
import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser;
import com.powsybl.dynawo.contingency.results.ResultsUtil;
import com.powsybl.dynawo.contingency.results.Status;
import com.powsybl.dynawo.contingency.xml.XmlScenarioResultParser;
import com.powsybl.dynawo.contingency.xml.ConstraintsReader;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.security.*;
import com.powsybl.security.results.NetworkResult;
import com.powsybl.security.results.PostContingencyResult;
import com.powsybl.security.results.PreContingencyResult;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.powsybl.dynawo.contingency.ContingencyReports.createContingenciesTimelineReportNode;
import static com.powsybl.dynawo.contingency.ContingencyConstants.AGGREGATED_RESULTS;
import static com.powsybl.dynawo.contingency.ContingencyConstants.CONSTRAINTS_FOLDER;

/**
 * @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
 */
public final class ContingencyResultsUtils {

    private ContingencyResultsUtils() {
    }

    public static SecurityAnalysisResult createSecurityAnalysisResult(Network network, LimitViolationFilter violationFilter,
                                                                      Path workingDir, List<Contingency> contingencies) {
        Map<String, Status> aggregatedResults = getAggregatedResults(workingDir);
        Path constraintsDir = workingDir.resolve(CONSTRAINTS_FOLDER);
        return new SecurityAnalysisResult(
                ContingencyResultsUtils.getPreContingencyResult(network, violationFilter),
                ContingencyResultsUtils.getPostContingencyResults(network, violationFilter, constraintsDir, aggregatedResults, contingencies),
                Collections.emptyList());
    }

    /**
     * Build the pre-contingency results from the constraints file written by dynawo or directly form the network if the results are not found
     */
    private static PreContingencyResult getPreContingencyResult(Network network, LimitViolationFilter violationFilter) {
        NetworkResult networkResult = new NetworkResult(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
        List<LimitViolation> limitViolations = Security.checkLimits(network);
        List<LimitViolation> filteredViolations = violationFilter.apply(limitViolations, network);
        // Pre contingency always set to CONVERGED (see issue #174 & #414)
        return new PreContingencyResult(LoadFlowResult.ComponentResult.Status.CONVERGED, new LimitViolationsResult(filteredViolations), networkResult);
    }

    /**
     * Build the post-contingency results from the constraints files written by dynawo
     */
    private static List<PostContingencyResult> getPostContingencyResults(Network network, LimitViolationFilter violationFilter,
                                                                        Path constraintsDir, Map<String, Status> scenarioResults,
                                                                        List<Contingency> contingencies) {
        return contingencies.stream()
                .map(c -> new PostContingencyResult(c,
                        ResultsUtil.convertToPostStatus(scenarioResults.getOrDefault(c.getId(), Status.EXECUTION_PROBLEM)),
                        getLimitViolationsResult(network, violationFilter, constraintsDir, c.getId())))
                .toList();
    }

    private static Map<String, Status> getAggregatedResults(Path workingDir) {
        Path results = workingDir.resolve(AGGREGATED_RESULTS);
        Map<String, Status> scenarioResults = new HashMap<>();
        if (Files.exists(results)) {
            new XmlScenarioResultParser().parse(results, s -> scenarioResults.put(s.id(), s.status()));
        }
        return scenarioResults;
    }

    private static LimitViolationsResult getLimitViolationsResult(Network network, LimitViolationFilter violationFilter,
                                            Path constraintsDir, String contingencyName) {
        Path constraintsFile = constraintsDir.resolve("constraints_" + contingencyName + ".xml");
        if (Files.exists(constraintsFile)) {
            List<LimitViolation> limitViolationsRead = ConstraintsReader.read(network, constraintsFile);
            List<LimitViolation> limitViolationsFiltered = violationFilter.apply(limitViolationsRead, network);
            return new LimitViolationsResult(limitViolationsFiltered);
        }
        return LimitViolationsResult.empty();
    }

    // Report the timeline events from the timeline files written by dynawo
    public static void reportContingenciesTimelines(List<Contingency> contingencies, Path timelineDir, ReportNode reportNode) {
        contingencies.forEach(c -> {
            ReportNode contingencyReporter = createContingenciesTimelineReportNode(reportNode, c.getId());
            getTimeline(timelineDir, c).forEach(e -> CommonReports.reportTimelineEntry(contingencyReporter, e));
        });
    }

    private static List<TimelineEntry> getTimeline(Path timelineDir, Contingency c) {
        Path timelineFile = timelineDir.resolve("timeline_" + c.getId() + ".xml");
        return new XmlTimeLineParser().parse(timelineFile);
    }
}