DynaFlowSecurityAnalysisHandler.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.dynaflow;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.powsybl.commons.json.JsonUtil;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.AbstractExecutionHandler;
import com.powsybl.computation.Command;
import com.powsybl.computation.CommandExecution;
import com.powsybl.computation.ExecutionReport;
import com.powsybl.contingency.Contingency;
import com.powsybl.contingency.SidedContingencyElement;
import com.powsybl.contingency.contingency.list.ContingencyList;
import com.powsybl.contingency.contingency.list.DefaultContingencyList;
import com.powsybl.contingency.json.ContingencyJsonModule;
import com.powsybl.dynaflow.json.DynaFlowConfigSerializer;
import com.powsybl.dynawo.commons.DynawoUtil;
import com.powsybl.dynawo.contingency.ContingencyResultsUtils;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.security.LimitViolationFilter;
import com.powsybl.security.SecurityAnalysisParameters;
import com.powsybl.security.SecurityAnalysisReport;
import com.powsybl.security.interceptors.SecurityAnalysisInterceptor;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Predicate;

import static com.powsybl.dynaflow.DynaFlowConstants.CONFIG_FILENAME;
import static com.powsybl.dynaflow.DynaflowReports.createSidedContingencyReportNode;
import static com.powsybl.dynaflow.SecurityAnalysisConstants.CONTINGENCIES_FILENAME;
import static com.powsybl.dynawo.commons.DynawoConstants.NETWORK_FILENAME;
import static com.powsybl.dynawo.commons.DynawoConstants.TIMELINE_FOLDER;
import static com.powsybl.dynawo.commons.DynawoUtil.getCommandExecutions;
import static com.powsybl.dynawo.contingency.ContingencyResultsUtils.createSecurityAnalysisResult;

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

    private final Command command;
    private final Network network;
    private final String workingVariantId;
    private final SecurityAnalysisParameters securityAnalysisParameters;
    private final List<Contingency> contingencies;
    private final LimitViolationFilter violationFilter;
    private final List<SecurityAnalysisInterceptor> interceptors;
    private final ReportNode reportNode;

    public DynaFlowSecurityAnalysisHandler(Network network, String workingVariantId, Command command,
                                           SecurityAnalysisParameters securityAnalysisParameters, List<Contingency> contingencies,
                                           LimitViolationFilter violationFilter, List<SecurityAnalysisInterceptor> interceptors,
                                           ReportNode reportNode) {
        this.network = network;
        this.workingVariantId = workingVariantId;
        this.command = command;
        this.securityAnalysisParameters = securityAnalysisParameters;
        this.contingencies = contingencies;
        this.violationFilter = violationFilter;
        this.interceptors = interceptors;
        this.reportNode = reportNode;
    }

    @Override
    public List<CommandExecution> before(Path workingDir) throws IOException {
        network.getVariantManager().setWorkingVariant(workingVariantId);

        DynawoUtil.writeIidm(network, workingDir.resolve(NETWORK_FILENAME));
        writeParameters(securityAnalysisParameters, workingDir);
        writeContingencies(contingencies, workingDir);
        return getCommandExecutions(command);
    }

    @Override
    public SecurityAnalysisReport after(Path workingDir, ExecutionReport report) throws IOException {
        super.after(workingDir, report);
        network.getVariantManager().setWorkingVariant(workingVariantId);
        ContingencyResultsUtils.reportContingenciesTimelines(contingencies, workingDir.resolve(TIMELINE_FOLDER), reportNode);
        return new SecurityAnalysisReport(createSecurityAnalysisResult(network, violationFilter, workingDir, contingencies));
    }

    private void writeContingencies(List<Contingency> contingencies, Path workingDir) throws IOException {
        try (OutputStream os = Files.newOutputStream(workingDir.resolve(CONTINGENCIES_FILENAME))) {
            ObjectMapper mapper = JsonUtil.createObjectMapper();
            ContingencyJsonModule module = new ContingencyJsonModule();
            mapper.registerModule(module);
            ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();
            writer.writeValue(os, buildContingencyList(contingencies));
        }
    }

    private ContingencyList buildContingencyList(List<Contingency> contingencies) {
        return new DefaultContingencyList("", contingencies.stream().filter(nonSidedContingency()).toList());
    }

    private Predicate<Contingency> nonSidedContingency() {
        return c -> {
            if (c instanceof SidedContingencyElement sidedC && sidedC.getVoltageLevelId() != null) {
                createSidedContingencyReportNode(reportNode, c.getId());
                return false;
            }
            return true;
        };
    }

    private static void writeParameters(SecurityAnalysisParameters securityAnalysisParameters, Path workingDir) throws IOException {
        // TODO(Luma) Take into account also Security Analysis parameters
        LoadFlowParameters loadFlowParameters = securityAnalysisParameters.getLoadFlowParameters();
        DynaFlowParameters dynaFlowParameters = DynaFlowProvider.getParametersExt(loadFlowParameters);
        DynaFlowSecurityAnalysisParameters dynaFlowSecurityAnalysisParameters = DynaFlowSecurityAnalysisProvider.getParametersExt(securityAnalysisParameters);
        DynaFlowConfigSerializer.serialize(loadFlowParameters, dynaFlowParameters, dynaFlowSecurityAnalysisParameters,
                Path.of("."), workingDir.resolve(CONFIG_FILENAME));
    }
}