All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.powsybl.loadflow.validation.ValidationTool Maven / Gradle / Ivy

/**
 * Copyright (c) 2017-2018, 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.loadflow.validation;

import com.google.auto.service.AutoService;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.ImportConfig;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.VariantManagerConstants;
import com.powsybl.iidm.network.tools.ConversionToolUtils;
import com.powsybl.loadflow.LoadFlow;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.loadflow.validation.io.ValidationWriters;
import com.powsybl.tools.Command;
import com.powsybl.tools.Tool;
import com.powsybl.tools.ToolRunningContext;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import static com.powsybl.iidm.network.tools.ConversionToolUtils.*;

/**
 *
 * @author Massimo Ferraro {@literal }
 */
@AutoService(Tool.class)
public class ValidationTool implements Tool {

    private static final String CASE_FILE = "case-file";
    private static final String OUTPUT_FOLDER = "output-folder";
    private static final String LOAD_FLOW = "load-flow";
    private static final String VERBOSE = "verbose";
    private static final String OUTPUT_FORMAT = "output-format";
    private static final String TYPES = "types";
    private static final String COMPARE_RESULTS = "compare-results";
    private static final String RUN_COMPUTATION = "run-computation";
    private static final String COMPARE_CASE_FILE = "compare-case-file";

    private static final Command COMMAND = new Command() {

        @Override
        public String getName() {
            return "loadflow-validation";
        }

        @Override
        public String getTheme() {
            return "Computation";
        }

        @Override
        public String getDescription() {
            return "Validate load-flow results of a network";
        }

        @Override
        public Options getOptions() {
            Options options = new Options();
            options.addOption(Option.builder().longOpt(CASE_FILE)
                    .desc("case file path")
                    .hasArg()
                    .argName("FILE")
                    .required()
                    .build());
            options.addOption(Option.builder().longOpt(OUTPUT_FOLDER)
                    .desc("output folder path")
                    .hasArg()
                    .argName("FOLDER")
                    .required()
                    .build());
            options.addOption(Option.builder().longOpt(LOAD_FLOW)
                    .desc("run loadflow")
                    .build());
            options.addOption(Option.builder().longOpt(RUN_COMPUTATION)
                    .desc("run a computation on the network before validation, available computations are "
                            + Arrays.toString(CandidateComputations.getComputationsNames().toArray()))
                    .hasArg()
                    .argName("COMPUTATION")
                    .build());
            options.addOption(Option.builder().longOpt(VERBOSE)
                    .desc("verbose output")
                    .build());
            options.addOption(Option.builder().longOpt(OUTPUT_FORMAT)
                    .desc("output format " + Arrays.toString(ValidationOutputWriter.values()))
                    .hasArg()
                    .argName("VALIDATION_WRITER")
                    .build());
            options.addOption(Option.builder().longOpt(TYPES)
                    .desc("validation types " + Arrays.toString(ValidationType.values()) + " to run, all of them if the option if not specified")
                    .hasArg()
                    .argName("VALIDATION_TYPE,VALIDATION_TYPE,...")
                    .build());
            options.addOption(Option.builder().longOpt(COMPARE_RESULTS)
                    .desc("compare results of two validations, printing output files with results of both ones. Available comparisons are " + Arrays.toString(ComparisonType.values()))
                    .hasArg()
                    .argName("COMPARISON_TYPE")
                    .build());
            options.addOption(Option.builder().longOpt(COMPARE_CASE_FILE)
                    .desc("path to the case file to compare")
                    .hasArg()
                    .argName("FILE")
                    .build());
            options.addOption(createImportParametersFileOption());
            options.addOption(createImportParameterOption());
            return options;
        }

        @Override
        public String getUsageFooter() {
            return null;
        }

    };

    @Override
    public Command getCommand() {
        return COMMAND;
    }

    @Override
    public void run(CommandLine line, ToolRunningContext context) throws Exception {
        Path caseFile = Paths.get(line.getOptionValue(CASE_FILE));
        Path outputFolder = Paths.get(line.getOptionValue(OUTPUT_FOLDER));
        if (!Files.exists(outputFolder)) {
            Files.createDirectories(outputFolder);
        }
        ValidationConfig config = ValidationConfig.load();
        if (line.hasOption(VERBOSE)) {
            config.setVerbose(true);
        }
        if (line.hasOption(OUTPUT_FORMAT)) {
            config.setValidationOutputWriter(ValidationOutputWriter.valueOf(line.getOptionValue(OUTPUT_FORMAT)));
        }
        ComparisonType comparisonType = null;
        if (line.hasOption(COMPARE_RESULTS)) {
            config.setCompareResults(true);
            comparisonType = ComparisonType.valueOf(line.getOptionValue(COMPARE_RESULTS));
        }
        Set validationTypes = Sets.newHashSet(ValidationType.values());
        if (line.hasOption(TYPES)) {
            validationTypes = Arrays.stream(line.getOptionValue(TYPES).split(","))
                                    .map(ValidationType::valueOf)
                                    .collect(Collectors.toSet());
        }
        Network network = loadNetwork(caseFile, line, context);
        try (ValidationWriters validationWriters = new ValidationWriters(network.getId(), validationTypes, outputFolder, config)) {
            if (config.isCompareResults() && ComparisonType.COMPUTATION.equals(comparisonType)) {
                Preconditions.checkArgument(line.hasOption(LOAD_FLOW) || line.hasOption(RUN_COMPUTATION),
                        "Computation results comparison requires to run a computation (options --" + LOAD_FLOW + " or --" + RUN_COMPUTATION + ").");

                context.getOutputStream().println("Running pre-loadflow validation on network " + network.getId());
                runValidation(network, config, validationTypes, validationWriters, context);
            }

            if (line.hasOption(LOAD_FLOW)) {
                runLoadflow(network, config, context);
                context.getOutputStream().println("Running post-loadflow validation on network " + network.getId());
            } else if (line.hasOption(RUN_COMPUTATION)) {
                runComputation(line.getOptionValue(RUN_COMPUTATION), network, context);
                context.getOutputStream().println("Running post-computation validation on network " + network.getId());
            }

            runValidation(network, config, validationTypes, validationWriters, context);

            if (config.isCompareResults() && ComparisonType.BASECASE.equals(comparisonType)) {
                Preconditions.checkArgument(line.hasOption(COMPARE_CASE_FILE),
                        "Basecases comparison requires to provide a second basecase (option --" + COMPARE_CASE_FILE + ").");
                Path compareCaseFile = Paths.get(line.getOptionValue(COMPARE_CASE_FILE));
                Network compareNetwork = loadNetwork(compareCaseFile, line, context);
                context.getOutputStream().println("Running validation on network " + compareNetwork.getId() + " to compare");
                runValidation(compareNetwork, config, validationTypes, validationWriters, context);
            }
        }
    }

    private Network loadNetwork(Path caseFile, CommandLine line, ToolRunningContext context) throws IOException {
        context.getOutputStream().println("Loading case " + caseFile);
        Properties inputParams = readProperties(line, ConversionToolUtils.OptionType.IMPORT, context);
        Network network = Network.read(caseFile, context.getShortTimeExecutionComputationManager(), ImportConfig.load(), inputParams);
        if (network == null) {
            throw new PowsyblException("Case " + caseFile + " not found");
        }
        return network;
    }

    private void runValidation(Network network, ValidationConfig config, Set validationTypes, ValidationWriters validationWriter, ToolRunningContext context) {
        validationTypes.forEach(validationType -> {
            context.getOutputStream().println("Validate load-flow results of network " + network.getId()
                                              + " - validation type: " + validationType
                                              + " - result: " + (validationType.check(network, config, validationWriter.getWriter(validationType)) ? "success" : "fail"));
            validationWriter.getWriter(validationType).setValidationCompleted();
        });
    }

    private void runLoadflow(Network network, ValidationConfig config, ToolRunningContext context) {
        context.getOutputStream().println("Running loadflow on network " + network.getId());
        LoadFlowParameters parameters = LoadFlowParameters.load();
        String loadFlowName = config.getLoadFlowName().orElse(null);
        LoadFlow.find(loadFlowName)
                .runAsync(network, VariantManagerConstants.INITIAL_VARIANT_ID, context.getShortTimeExecutionComputationManager(), parameters)
                .thenAccept(loadFlowResult -> {
                    if (!loadFlowResult.isOk()) {
                        throw new PowsyblException("Loadflow on network " + network.getId() + " does not converge");
                    }
                })
                .join();
    }

    private void runComputation(String computationName, Network network, ToolRunningContext context) {
        CandidateComputation computation = CandidateComputations.getComputation(computationName)
                .orElseThrow(() -> new IllegalArgumentException("Unknown computation type : " + computationName));
        context.getOutputStream().format("Running computation '%s' on network '%s'", computationName, network.getId()).println();
        computation.run(network, context.getShortTimeExecutionComputationManager());
    }

    enum ComparisonType {
        COMPUTATION("compare the validation of a basecase before and after the computation"),
        BASECASE("compare the validation of two basecases");

        private final String description;

        ComparisonType(String description) {
            this.description = Objects.requireNonNull(description);
        }

        @Override
        public String toString() {
            return this.name() + " (" + this.description + ")";
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy