Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/user/itools/loadflow-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ usage: itools [OPTIONS] loadflow-validation --case-file <FILE>
[--load-flow] --output-folder <FOLDER> [--output-format
<VALIDATION_WRITER>] [--run-computation <COMPUTATION>] [--types
<VALIDATION_TYPE,VALIDATION_TYPE,...>] [--verbose]
[--with-extensions-validation]

Available options are:
--config-name <CONFIG_NAME> Override configuration file name
Expand Down Expand Up @@ -86,6 +87,9 @@ Use the `--load-flow` parameter to run a load-flow before the validation. This o
`--output-format`<br>
Use the `--output-format` parameter to specify the format of the output files. The available output formats are `CSV` or `CSV_MULTILINE`.

`--with-extensions-validation`<br>
Use the `--with-extensions-validation` parameter to perform extensions validation.

Comment on lines +90 to +92
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong place, you put it between an argument and the examples linked to that argument

If this parameter is set to `CSV`, in the output files a line contains all values of validated equipment. If the parameter
is set to `CSV_MULTILINE`, in the output files the values of a piece of equipment are split in multiple lines, one value for each
line, see examples below:
Expand Down
12 changes: 10 additions & 2 deletions loadflow/loadflow-validation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,15 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-tools-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-iidm-serde</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>

Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@

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.extension.ExtensionsValidation;
import com.powsybl.loadflow.validation.io.ValidationWriters;
import com.powsybl.tools.Command;
import com.powsybl.tools.Tool;
Expand All @@ -28,11 +28,8 @@
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.*;
import java.util.stream.Collectors;

import static com.powsybl.iidm.network.tools.ConversionToolUtils.*;
Expand All @@ -53,6 +50,7 @@ public class ValidationTool implements Tool {
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";
public static final String WITH_EXTENSIONS_OPTION = "with-extensions-validation";

private static final Command COMMAND = new Command() {

Expand Down Expand Up @@ -120,6 +118,11 @@ public Options getOptions() {
.build());
options.addOption(createImportParametersFileOption());
options.addOption(createImportParameterOption());
options.addOption(Option.builder().longOpt(WITH_EXTENSIONS_OPTION)
.desc("enable extension validation")
.hasArg(false)
.argName("EXTENSIONS")
.build());
return options;
}

Expand All @@ -137,8 +140,8 @@ public Command getCommand() {

@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));
Path caseFile = context.getFileSystem().getPath(line.getOptionValue(CASE_FILE));
Path outputFolder = context.getFileSystem().getPath(line.getOptionValue(OUTPUT_FOLDER));
if (!Files.exists(outputFolder)) {
Files.createDirectories(outputFolder);
}
Expand All @@ -154,11 +157,11 @@ public void run(CommandLine line, ToolRunningContext context) throws Exception {
config.setCompareResults(true);
comparisonType = ComparisonType.valueOf(line.getOptionValue(COMPARE_RESULTS));
}
Set<ValidationType> validationTypes = Sets.newHashSet(ValidationType.values());
Set<ValidationType> validationTypes = new TreeSet<>(Arrays.asList(ValidationType.values()));
if (line.hasOption(TYPES)) {
validationTypes = Arrays.stream(line.getOptionValue(TYPES).split(","))
.map(ValidationType::valueOf)
.collect(Collectors.toSet());
.map(ValidationType::valueOf)
.collect(Collectors.toCollection(TreeSet::new));
}
Network network = loadNetwork(caseFile, line, context);
try (ValidationWriters validationWriters = new ValidationWriters(network.getId(), validationTypes, outputFolder, config)) {
Expand All @@ -182,12 +185,16 @@ public void run(CommandLine line, ToolRunningContext context) throws Exception {

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));
"Base cases comparison requires to provide a second basecase (option --" + COMPARE_CASE_FILE + ").");
Path compareCaseFile = context.getFileSystem().getPath(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);
}
if (line.hasOption(WITH_EXTENSIONS_OPTION)) {
ExtensionsValidation extensionsValidation = new ExtensionsValidation();
extensionsValidation.runExtensionValidations(network, config, context);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2026, 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.extension;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.validation.ValidationConfig;

/**
*
* @author Samir Romdhani {@literal <samir.romdhani at rte-france.com>}
*/
public interface ExtensionValidation {

String getName();

boolean check(Network network, ValidationConfig config);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2026, 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.extension;

import com.google.common.base.Suppliers;
import com.google.common.collect.Lists;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.validation.ValidationConfig;
import com.powsybl.tools.ToolRunningContext;

import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Supplier;

/**
* @author Samir Romdhani {@literal <samir.romdhani at rte-france.com>}
*/
public class ExtensionsValidation {

private static final Supplier<List<ExtensionValidation>> EXTENSIONS = Suppliers.memoize(() -> Lists.newArrayList(
ServiceLoader.load(ExtensionValidation.class, ExtensionsValidation.class.getClassLoader())));

public static List<ExtensionValidation> getExtensions() {
return EXTENSIONS.get();
}

public static List<String> getExtensionsNames() {
return getExtensions().stream().map(ExtensionValidation::getName).toList();
}

public static Optional<ExtensionValidation> getExtension(String name) {
return getExtensions().stream().filter(v -> v.getName().equals(name)).findFirst();
}

public void runExtensionValidations(Network network, ValidationConfig config, ToolRunningContext context) {
getExtensions().forEach(extensionValidation -> {
boolean success = extensionValidation.check(network, config);
context.getOutputStream().println("Validate load-flow results of network " + network.getId()
+ " - validation type: Extension/" + extensionValidation.getName()
+ " - result: " + (success ? "success" : "fail"));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void checkCompleteConfig() throws Exception {
}

@Test
void checkSetters() throws Exception {
void checkSetters() {
ValidationConfig config = ValidationConfig.load(platformConfig);
config.setThreshold(threshold);
config.setVerbose(verbose);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Copyright (c) 2026, 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.powsybl.tools.Tool;
import com.powsybl.tools.test.AbstractToolTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.nio.file.Files;
import java.util.Collections;
import java.util.Objects;

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

/**
*
* @author Samir Romdhani {@literal <samir.romdhani at rte-france.com>}
*/
class ValidationToolTest extends AbstractToolTest {

private static final String COMMAND_NAME = "loadflow-validation";
private final ValidationTool tool = new ValidationTool();

@Override
@BeforeEach
public void setUp() throws Exception {
super.setUp();
Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/network.xiidm")), fileSystem.getPath("network.xiidm"));
}

@Override
protected Iterable<Tool> getTools() {
return Collections.singleton(tool);
}

@Override
public void assertCommand() {
assertEquals(COMMAND_NAME, tool.getCommand().getName());
assertEquals("Computation", tool.getCommand().getTheme());
assertEquals("Validate load-flow results of a network", tool.getCommand().getDescription());

assertCommand(tool.getCommand(), COMMAND_NAME, 12, 2);
assertOption(tool.getCommand().getOptions(), "case-file", true, true);
assertOption(tool.getCommand().getOptions(), "output-folder", true, true);
assertOption(tool.getCommand().getOptions(), "load-flow", false, false);
assertOption(tool.getCommand().getOptions(), "types", false, true);
assertOption(tool.getCommand().getOptions(), "with-extensions-validation", false, false);
}

@Test
void testCommand() {
assertCommand();
}

@Test
void loadFlowValidationShouldSucceedWithRequiredOption() {
String expectedOut = String.join(System.lineSeparator(),
"Loading case network.xiidm",
"Validate load-flow results of network network - validation type: FLOWS - result: success",
"Validate load-flow results of network network - validation type: GENERATORS - result: success",
"Validate load-flow results of network network - validation type: BUSES - result: success",
"Validate load-flow results of network network - validation type: SVCS - result: success",
"Validate load-flow results of network network - validation type: SHUNTS - result: success",
"Validate load-flow results of network network - validation type: TWTS - result: success",
"Validate load-flow results of network network - validation type: TWTS3W - result: success" + System.lineSeparator());
assertCommandSuccessfulMatch(new String[]{"loadflow-validation", "--case-file", "network.xiidm", "--output-folder", "test"}, expectedOut);
}

@Test
void loadFlowValidationShouldComputePrivateValidation() {
String expectedOut = String.join(System.lineSeparator(),
"Loading case network.xiidm",
"Validate load-flow results of network network - validation type: FLOWS - result: success",
"Validate load-flow results of network network - validation type: GENERATORS - result: success",
"Validate load-flow results of network network - validation type: BUSES - result: success",
"Validate load-flow results of network network - validation type: SVCS - result: success",
"Validate load-flow results of network network - validation type: SHUNTS - result: success",
"Validate load-flow results of network network - validation type: TWTS - result: success",
"Validate load-flow results of network network - validation type: TWTS3W - result: success",
"Validate load-flow results of network network - validation type: Extension/extensionValidationMock1 - result: success" + System.lineSeparator());
assertCommandSuccessfulMatch(new String[]{"loadflow-validation", "--case-file", "network.xiidm", "--output-folder", "test", "--with-extensions-validation"}, expectedOut);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2026, 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.extension;
import com.google.auto.service.AutoService;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.validation.ValidationConfig;

/**
*
* @author Samir Romdhani {@literal <samir.romdhani at rte-france.com>}
*/
@AutoService(ExtensionValidation.class)
public class ExtensionValidationMock implements ExtensionValidation {

@Override
public String getName() {
return "extensionValidationMock1";
}

@Override
public boolean check(Network network, ValidationConfig config) {
return true;
}

}
Loading
Loading