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
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ core.iidm.serde.importedExtensions = Imported extensions
core.iidm.serde.importedNetwork = Network ${networkId} is imported from ${format} format.
core.iidm.serde.validationWarnings = Validation warnings
core.iidm.serde.xiidmImportDone = XIIDM import done
core.loadflow.loadflowTool = Loadflow tool
core.timeseries.invalidVersionNumber = The version number for a versioned TimeSeries should not be equals to the default version number (${versionNumber}) at line ${line}
core.timeseries.timeseriesLoadingTime = ${tsNumber} time series loaded from CSV in ${timing} ms
core.ucte.activePowerUndefined = Node ${node}: active power is undefined, set value to 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ core.iidm.serde.importedExtensions = Extensions importées.
core.iidm.serde.importedNetwork = Le réseau ${networkId} est importé à partir du format ${format}.
core.iidm.serde.validationWarnings = Avertissements sur la validation.
core.iidm.serde.xiidmImportDone = Import XIIDM terminé.
core.loadflow.loadflowTool = Outils de calcul de répartition.
core.timeseries.invalidVersionNumber = Le numéro de version pour une série temporelle versionnée ne doit pas être égal au numéro de version par défaut (${versionNumber}) ligne ${line}.
core.timeseries.timeseriesLoadingTime = ${tsNumber} séries temporelles chargées du fichier CSV en ${timing} ms.
core.ucte.activePowerUndefined = Noeud ${node}: la puissance active est indéfinie, valeur mise à 0.
Expand Down
7 changes: 6 additions & 1 deletion docs/user/itools/loadflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ usage: itools [OPTIONS] loadflow --case-file <FILE> [-E <property=value>]
[--export-parameters <EXPORT_PARAMETERS>] [--help] [-I <property=value>]
[--import-parameters <IMPORT_PARAMETERS>] [--output-case-file <FILE>]
[--output-case-format <CASEFORMAT>] [--output-file <FILE>]
[--output-format <FORMAT>] [--parameters-file <FILE>]
[--output-format <FORMAT>] [--output-log-file <FILE>]
[--parameters-file <FILE>]

Available options are:
--config-name <CONFIG_NAME> Override configuration file name
Expand All @@ -29,6 +30,7 @@ Available arguments are:
--output-file <FILE> loadflow results output path
--output-format <FORMAT> loadflow results output format
[CSV, JSON]
--output-log-file <FILE> loadflow logs output path
--parameters-file <FILE> loadflow parameters as JSON file
```

Expand Down Expand Up @@ -57,6 +59,9 @@ This option defines the path where to export the [results](../../simulation/load
`--output-format`<br>
This option defines the format of the output file. The supported format are `CSV` and `JSON`. This option is required if the `--output-file` is used.

`--output-log-file`<br>
This option defines the path where to export the logs of the load flow.

`--parameters-file`<br>
This option defines the path of the [parameters](#parameters) file of the simulation. If this option is not used, the simulation is run with the default parameters.

Expand Down
20 changes: 20 additions & 0 deletions loadflow/loadflow-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,26 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-tools-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-iidm-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-iidm-serde</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-iidm-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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;

import com.powsybl.commons.report.ReportNode;

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

private LoadFlowReports() {
}

public static ReportNode buildRootLoadFlowTool() {
return ReportNode.newRootReportNode()
.withAllResourceBundlesFromClasspath()
.withMessageTemplate("core.loadflow.loadflowTool")
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
import com.google.auto.service.AutoService;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.io.table.*;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.Exporter;
import com.powsybl.iidm.network.ImportConfig;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.tools.ConversionToolUtils;
import com.powsybl.loadflow.LoadFlow;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.loadflow.*;
import com.powsybl.loadflow.json.JsonLoadFlowParameters;
import com.powsybl.loadflow.json.LoadFlowResultSerializer;
import com.powsybl.tools.Command;
Expand Down Expand Up @@ -52,6 +51,7 @@ public class RunLoadFlowTool implements Tool {
private static final String OUTPUT_FORMAT = "output-format";
private static final String OUTPUT_CASE_FORMAT = "output-case-format";
private static final String OUTPUT_CASE_FILE = "output-case-file";
private static final String OUTPUT_LOG_FILE = "output-log-file";

private enum Format {
CSV,
Expand Down Expand Up @@ -110,6 +110,11 @@ public Options getOptions() {
.hasArg()
.argName("FILE")
.build());
options.addOption(Option.builder().longOpt(OUTPUT_LOG_FILE)
.desc("loadflow logs output path")
.hasArg()
.argName("FILE")
.build());
options.addOption(createImportParametersFileOption());
options.addOption(createImportParameterOption());
options.addOption(createExportParametersFileOption());
Expand Down Expand Up @@ -160,19 +165,42 @@ public void run(CommandLine line, ToolRunningContext context) throws Exception {
JsonLoadFlowParameters.update(params, parametersFile);
}

LoadFlowResult result = LoadFlow.run(network, context.getShortTimeExecutionComputationManager(), params);
ReportNode reportNode = LoadFlowReports.buildRootLoadFlowTool();

LoadFlowRunParameters runParameters = new LoadFlowRunParameters()
.setParameters(params)
.setComputationManager(context.getShortTimeExecutionComputationManager())
.setReportNode(reportNode);

if (outputFile != null) {
exportResult(result, context, outputFile, format);
LoadFlow.Runner runner = LoadFlow.find();
if (runner.checkParameters(runParameters)) {
LoadFlowResult result = runner.run(network, runParameters);

writeLog(line, context, reportNode);
if (outputFile != null) {
exportResult(result, context, outputFile, format);
} else {
printResult(result, context);
}

// exports the modified network to the filesystem, if requested
if (outputCaseFile != null) {
String outputCaseFormat = line.getOptionValue(OUTPUT_CASE_FORMAT);
Properties outputParams = readProperties(line, ConversionToolUtils.OptionType.EXPORT, context);
network.write(outputCaseFormat, outputParams, outputCaseFile);
}
} else {
printResult(result, context);
writeLog(line, context, reportNode);
context.getOutputStream().printf("Unsupported parameters found, %s cannot launch the loadflow%n", runner.getName());
}
}

// exports the modified network to the filesystem, if requested
if (outputCaseFile != null) {
String outputCaseFormat = line.getOptionValue(OUTPUT_CASE_FORMAT);
Properties outputParams = readProperties(line, ConversionToolUtils.OptionType.EXPORT, context);
network.write(outputCaseFormat, outputParams, outputCaseFile);
private void writeLog(CommandLine line, ToolRunningContext context, ReportNode reportNode) throws IOException {
Path outputLogFile = line.hasOption(OUTPUT_LOG_FILE) ? context.getFileSystem().getPath(line.getOptionValue(OUTPUT_LOG_FILE)) : null;
if (outputLogFile != null) {
exportLog(reportNode, context, outputLogFile);
} else {
printLog(reportNode, context);
}
}

Expand Down Expand Up @@ -241,6 +269,12 @@ private void printResult(LoadFlowResult result, ToolRunningContext context) {
printLoadFlowResult(result, writer, asciiTableFormatterFactory, TableFormatterConfig.load());
}

private void printLog(ReportNode reportNode, ToolRunningContext context) throws IOException {
Writer writer = new OutputStreamWriter(context.getOutputStream());
reportNode.print(writer);
writer.flush();
}

private void exportResult(LoadFlowResult result, ToolRunningContext context, Path outputFile, Format format) {
context.getOutputStream().println("Writing results to '" + outputFile + "'");
switch (format) {
Expand All @@ -254,4 +288,9 @@ private void exportResult(LoadFlowResult result, ToolRunningContext context, Pat
break;
}
}

private void exportLog(ReportNode reportNode, ToolRunningContext context, Path outputLogFile) throws IOException {
context.getOutputStream().println("Writing logs to '" + outputLogFile + "'");
reportNode.print(outputLogFile);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public void run() {
ReportNode reportNode = runParameters.getReportNode();
if (reportNode != null) {
reportNode.newReportNode()
.withStrictMode(false)
.withMessageTemplate("testLoadflow")
.withUntypedValue("variantId", workingStateId)
.add();
Expand Down Expand Up @@ -173,15 +174,17 @@ public boolean checkParameters(LoadFlowRunParameters runParameters) {
LoadFlowParameters parameters = runParameters.getLoadFlowParameters();
if (!parameters.isUseReactiveLimits()) {
runParameters.getReportNode().newReportNode()
.withMessageTemplate("testParameters")
.withStrictMode(false)
.withMessageTemplate("testParametersNotSupported")
.withUntypedValue("parameterName", "UseReactiveLimits")
.withUntypedValue("parameterValue", "False")
.add();
return false;
}
if (parameters.getBalanceType() == LoadFlowParameters.BalanceType.PROPORTIONAL_TO_GENERATION_P_MAX) {
runParameters.getReportNode().newReportNode()
.withMessageTemplate("testParameters")
.withStrictMode(false)
.withMessageTemplate("testParametersNotSupported")
.withUntypedValue("parameterName", "BalanceType")
.withUntypedValue("parameterValue", parameters.getBalanceType().toString())
.add();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,41 @@

import com.powsybl.commons.io.table.AsciiTableFormatterFactory;
import com.powsybl.commons.io.table.TableFormatterConfig;
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.commons.test.ComparisonUtils;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
import com.powsybl.iidm.serde.NetworkSerDe;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.loadflow.LoadFlowResultImpl;
import com.powsybl.tools.Command;
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.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

import static com.powsybl.commons.test.ComparisonUtils.assertTxtEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

/**
*
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
class RunLoadFlowToolTest extends AbstractSerDeTest {
class RunLoadFlowToolTest extends AbstractToolTest {

private final RunLoadFlowTool tool = new RunLoadFlowTool();

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

@Test
void printLoadFlowResultTest() throws IOException {
Expand All @@ -52,4 +69,104 @@ void printLoadFlowResultTest() throws IOException {
assertTxtEquals(getClass().getResourceAsStream("/LoadFlowResultResult.txt"), writer.toString());
}
}

@Override
@Test
public void assertCommand() {
Command command = tool.getCommand();

assertCommand(command, "loadflow", 11, 1);
assertEquals("Computation", command.getTheme());
assertEquals("Run loadflow", command.getDescription());
assertNull(command.getUsageFooter());
assertOption(command.getOptions(), "case-file", true, true);
assertOption(command.getOptions(), "parameters-file", false, true);
assertOption(command.getOptions(), "output-file", false, true);
assertOption(command.getOptions(), "output-format", false, true);
assertOption(command.getOptions(), "output-case-format", false, true);
assertOption(command.getOptions(), "output-case-file", false, true);
assertOption(command.getOptions(), "output-log-file", false, true);
}

@BeforeEach
@Override
public void setUp() throws Exception {
super.setUp();
// create network
Network network = EurostagTutorialExample1Factory.create();
NetworkSerDe.write(network, fileSystem.getPath("network.xiidm"));
Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/SupportedLoadFlowParameters.json")),
fileSystem.getPath("supportedParameters.json"));
Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/UnsupportedLoadFlowParameters.json")),
fileSystem.getPath("unsupportedParameters.json"));
}

@Test
void testLoadflow() {
String expectedOut = String.join(System.lineSeparator(),
"Loading network 'network.xiidm'",
"+ Loadflow tool",
" testLoadflow",
"Loadflow results:",
"+------+--------+---------+",
"| Ok | Status | Metrics |",
"+------+--------+---------+",
"| true | FAILED | {} |",
"+------+--------+---------+" + System.lineSeparator());
assertCommandSuccessful(new String[]{"loadflow", "--case-file", "network.xiidm", "--parameters-file", "supportedParameters.json"}, expectedOut);
}

@Test
void testLoadflowWithOutputFile() throws IOException {
String expectedOut = String.join(System.lineSeparator(),
"Loading network 'network.xiidm'",
"+ Loadflow tool",
" testLoadflow",
"Writing results to 'outputTest.json'" + System.lineSeparator());
String expectedOutputFile = """
{
"version" : "1.4",
"isOK" : true,
"metrics" : { }
}""";
assertCommandSuccessful(new String[]{"loadflow", "--case-file", "network.xiidm", "--parameters-file", "supportedParameters.json", "--output-file", "outputTest.json", "--output-format", "JSON"}, expectedOut);
ComparisonUtils.assertTxtEquals(expectedOutputFile, Files.newInputStream(fileSystem.getPath("outputTest.json")));
}

@Test
void testLoadflowWithOutputLogFile() throws IOException {
String expectedOut = String.join(System.lineSeparator(),
"Loading network 'network.xiidm'",
"Writing logs to 'outputTest.log'",
"Loadflow results:",
"+------+--------+---------+",
"| Ok | Status | Metrics |",
"+------+--------+---------+",
"| true | FAILED | {} |",
"+------+--------+---------+" + System.lineSeparator());
String expectedOutputFile = "+ Loadflow tool\n testLoadflow\n";
assertCommandSuccessful(new String[]{"loadflow", "--case-file", "network.xiidm", "--parameters-file", "supportedParameters.json", "--output-log-file", "outputTest.log"}, expectedOut);
ComparisonUtils.assertTxtEquals(expectedOutputFile, Files.newInputStream(fileSystem.getPath("outputTest.log")));
}

@Test
void testUnsupportedParametersLoadflow() {
String expectedOut = String.join(System.lineSeparator(),
"Loading network 'network.xiidm'",
"+ Loadflow tool",
" testParametersNotSupported",
"Unsupported parameters found, LoadFlowMock cannot launch the loadflow" + System.lineSeparator());
assertCommandSuccessful(new String[]{"loadflow", "--case-file", "network.xiidm", "--parameters-file", "unsupportedParameters.json"}, expectedOut);
}

@Test
void testUnsupportedParametersLoadflowWithOutputLogFile() throws IOException {
String expectedOut = String.join(System.lineSeparator(),
"Loading network 'network.xiidm'",
"Writing logs to 'outputTest.log'",
"Unsupported parameters found, LoadFlowMock cannot launch the loadflow" + System.lineSeparator());
String expectedOutputFile = "+ Loadflow tool\n testParametersNotSupported\n";
assertCommandSuccessful(new String[]{"loadflow", "--case-file", "network.xiidm", "--parameters-file", "unsupportedParameters.json", "--output-log-file", "outputTest.log"}, expectedOut);
ComparisonUtils.assertTxtEquals(expectedOutputFile, Files.newInputStream(fileSystem.getPath("outputTest.log")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"version" : "1.10",
"useReactiveLimits" : true,
"balanceType" : "PROPORTIONAL_TO_GENERATION_P"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"version" : "1.10",
"useReactiveLimits" : false,
"balanceType" : "PROPORTIONAL_TO_GENERATION_P_MAX"
}
Loading
Loading