DynaFlowProviderTest.java
/**
* Copyright (c) 2020, 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/.
*/
package com.powsybl.dynaflow;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.config.InMemoryPlatformConfig;
import com.powsybl.commons.config.MapModuleConfig;
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.computation.ComputationManager;
import com.powsybl.computation.local.LocalCommandExecutor;
import com.powsybl.computation.local.LocalComputationConfig;
import com.powsybl.computation.local.LocalComputationManager;
import com.powsybl.dynawo.commons.DynawoConstants;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.serde.NetworkSerDe;
import com.powsybl.loadflow.LoadFlow;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.loadflow.LoadFlowResult;
import org.apache.commons.lang3.SystemUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ForkJoinPool;
import static com.powsybl.commons.test.ComparisonUtils.assertXmlEquals;
import static com.powsybl.dynaflow.DynaFlowConstants.*;
import static com.powsybl.dynaflow.DynaFlowParameters.MODULE_SPECIFIC_PARAMETERS;
import static com.powsybl.dynawo.commons.DynawoConstants.*;
import static com.powsybl.loadflow.LoadFlowResult.Status.FAILED;
import static com.powsybl.loadflow.LoadFlowResult.Status.FULLY_CONVERGED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Guillaume Pernin {@literal <guillaume.pernin at rte-france.com>}
*/
class DynaFlowProviderTest extends AbstractSerDeTest {
private Path homeDir;
private DynaFlowConfig config;
private DynaFlowProvider provider;
@BeforeEach
@Override
public void setUp() throws IOException {
super.setUp();
homeDir = fileSystem.getPath("/home/dynaflow");
config = DynaFlowConfig.load();
provider = new DynaFlowProvider();
}
@Test
void checkVersionCommand() {
String versionCommand = DynaFlowProvider.getVersionCommand(config).toString(0);
String expectedVersionCommand = "[" + getProgram(homeDir) + ", --version]";
assertEquals(expectedVersionCommand, versionCommand);
}
@Test
void checkExecutionCommand() {
String executionCommand = DynaFlowProvider.getCommand(config).toString(0);
String expectedExecutionCommand = "[" + getProgram(homeDir) + ", --network, " + NETWORK_FILENAME + ", --config, " + CONFIG_FILENAME + "]";
assertEquals(expectedExecutionCommand, executionCommand);
}
private static String getProgram(Path homeDir) {
return homeDir.resolve(SystemUtils.IS_OS_WINDOWS ? "dynaflow-launcher.cmd" : "dynaflow-launcher.sh").toString();
}
private static class LocalCommandExecutorMock extends AbstractLocalCommandExecutor {
private final String stdOutFileRef;
private final String outputIidm;
private final String outputResults;
public LocalCommandExecutorMock(String stdoutFileRef, String outputIidm, String outputResults) {
this.stdOutFileRef = Objects.requireNonNull(stdoutFileRef);
this.outputIidm = Objects.requireNonNull(outputIidm);
this.outputResults = Objects.requireNonNull(outputResults);
}
@Override
public int execute(String program, List<String> args, Path outFile, Path errFile, Path workingDir, Map<String, String> env) {
try {
copyFile(stdOutFileRef, errFile);
copyFile(outputResults, workingDir.resolve(OUTPUT_RESULTS_FILENAME));
Path finalState = Files.createDirectories(workingDir.resolve(FINAL_STATE_FOLDER_PATH));
copyFile(outputIidm, finalState.resolve(OUTPUT_IIDM_FILENAME));
return 0;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
private static class EmptyLocalCommandExecutorMock extends AbstractLocalCommandExecutor {
private final String stdOutFileRef;
public EmptyLocalCommandExecutorMock(String stdoutFileRef) {
this.stdOutFileRef = Objects.requireNonNull(stdoutFileRef);
}
@Override
public int execute(String program, List<String> args, Path outFile, Path errFile, Path workingDir, Map<String, String> env) {
try {
copyFile(stdOutFileRef, errFile);
Files.createDirectories(workingDir.resolve(FINAL_STATE_FOLDER_PATH));
return 0;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
@Test
void testWithoutMergeLoads() throws Exception {
Network network = createTestNetwork();
LoadFlow.Runner dynaFlowSimulation = LoadFlow.find();
LoadFlowParameters params = LoadFlowParameters.load();
DynaFlowParameters dynaFlowParameters = params.getExtension(DynaFlowParameters.class);
dynaFlowParameters.setMergeLoads(false);
assertEquals(DYNAFLOW_NAME, dynaFlowSimulation.getName());
LocalCommandExecutor commandExecutor = new LocalCommandExecutorMock("/dynawo_version.out",
"/output.xiidm", "/results.json");
ComputationManager computationManager = new LocalComputationManager(new LocalComputationConfig(fileSystem.getPath("/working-dir"), 1), commandExecutor, ForkJoinPool.commonPool());
LoadFlowResult result = dynaFlowSimulation.run(network, computationManager, params);
assertNotNull(result);
assertEquals(FULLY_CONVERGED, result.getStatus());
InputStream pReferenceOutput = getClass().getResourceAsStream("/output.xiidm");
Network expectedNetwork = NetworkSerDe.read(pReferenceOutput);
compare(expectedNetwork, network);
}
@Test
void testWithMergeLoads() throws Exception {
Network network = createTestNetwork();
LoadFlow.Runner dynaFlowSimulation = LoadFlow.find();
LoadFlowParameters params = LoadFlowParameters.load();
assertEquals(DYNAFLOW_NAME, dynaFlowSimulation.getName());
LocalCommandExecutor commandExecutor = new LocalCommandExecutorMock("/dynawo_version.out",
"/outputMergedLoads.xiidm", "/results.json");
ComputationManager computationManager = new LocalComputationManager(new LocalComputationConfig(fileSystem.getPath("/working-dir"), 1), commandExecutor, ForkJoinPool.commonPool());
LoadFlowResult result = dynaFlowSimulation.run(network, computationManager, params);
assertNotNull(result);
assertEquals(FULLY_CONVERGED, result.getStatus());
InputStream pReferenceOutput = getClass().getResourceAsStream("/output.xiidm");
Network expectedNetwork = NetworkSerDe.read(pReferenceOutput);
compare(expectedNetwork, network);
}
@Test
void testFail() throws Exception {
Network network = Network.create("empty", "test");
LoadFlow.Runner dynaFlowSimulation = LoadFlow.find();
LoadFlowParameters params = LoadFlowParameters.load();
assertEquals(DYNAFLOW_NAME, dynaFlowSimulation.getName());
LocalCommandExecutor commandExecutor = new EmptyLocalCommandExecutorMock("/dynawo_version.out");
ComputationManager computationManager = new LocalComputationManager(new LocalComputationConfig(fileSystem.getPath("/working-dir"), 1), commandExecutor, ForkJoinPool.commonPool());
LoadFlowResult result = dynaFlowSimulation.run(network, computationManager, params);
assertNotNull(result);
assertEquals(FAILED, result.getStatus());
}
@Test
void testCallingBadVersionDynaFlow() throws Exception {
Network network = Network.create("empty", "test");
LoadFlow.Runner dynaFlowSimulation = LoadFlow.find();
LoadFlowParameters params = LoadFlowParameters.load();
LocalCommandExecutor commandExecutor = new EmptyLocalCommandExecutorMock("/dynawo_bad_version.out");
ComputationManager computationManager = new LocalComputationManager(new LocalComputationConfig(fileSystem.getPath("/working-dir"), 1), commandExecutor, ForkJoinPool.commonPool());
PowsyblException e = assertThrows(PowsyblException.class, () -> dynaFlowSimulation.run(network, computationManager, params));
assertEquals("dynaflow-launcher version not supported. Must be >= " + DynawoConstants.VERSION_MIN, e.getMessage());
}
@Test
void testUpdateSpecificParametersFromPlatform() {
InMemoryPlatformConfig platformConfig = new InMemoryPlatformConfig(fileSystem);
double dsoVoltageLevel = 2.0;
double timeStep = 0;
List<String> chosenOutputs = List.of(OutputTypes.STEADYSTATE.name(), OutputTypes.CONSTRAINTS.name());
MapModuleConfig moduleConfig = platformConfig.createModuleConfig(MODULE_SPECIFIC_PARAMETERS);
moduleConfig.setStringProperty("svcRegulationOn", Boolean.TRUE.toString());
moduleConfig.setStringProperty("shuntRegulationOn", Boolean.TRUE.toString());
moduleConfig.setStringProperty("automaticSlackBusOn", Boolean.FALSE.toString());
moduleConfig.setStringProperty("dsoVoltageLevel", Double.toString(dsoVoltageLevel));
moduleConfig.setStringListProperty("chosenOutputs", chosenOutputs);
moduleConfig.setStringProperty("timeStep", Double.toString(timeStep));
LoadFlowParameters params = LoadFlowParameters.load();
DynaFlowParameters dynaParams = params.getExtension(DynaFlowParameters.class);
provider.updateSpecificParameters(dynaParams, platformConfig);
assertTrue(dynaParams.getSvcRegulationOn());
assertTrue(dynaParams.getShuntRegulationOn());
assertFalse(dynaParams.getAutomaticSlackBusOn());
assertEquals(dsoVoltageLevel, dynaParams.getDsoVoltageLevel(), 0.1d);
assertThat(dynaParams.getChosenOutputs()).containsExactlyInAnyOrder(OutputTypes.STEADYSTATE, OutputTypes.CONSTRAINTS);
assertEquals(timeStep, dynaParams.getTimeStep(), 0.1d);
}
@Test
void testUpdateSpecificParametersFromProperties() {
Map<String, String> properties = Map.of(
"svcRegulationOn", "true",
"shuntRegulationOn", "true",
"automaticSlackBusOn", "false",
"dsoVoltageLevel", "2.0",
"chosenOutputs", "STEADYSTATE, CONSTRAINTS",
"timeStep", "0");
LoadFlowParameters params = LoadFlowParameters.load();
DynaFlowParameters dynaParams = params.getExtension(DynaFlowParameters.class);
provider.updateSpecificParameters(dynaParams, properties);
assertTrue(dynaParams.getSvcRegulationOn());
assertTrue(dynaParams.getShuntRegulationOn());
assertFalse(dynaParams.getAutomaticSlackBusOn());
assertEquals(2, dynaParams.getDsoVoltageLevel(), 0.1d);
assertThat(dynaParams.getChosenOutputs()).containsExactlyInAnyOrder(OutputTypes.STEADYSTATE, OutputTypes.CONSTRAINTS);
assertEquals(0, dynaParams.getTimeStep(), 0.1d);
}
@Test
void testGetSpecificParameters() {
Map<String, String> expectedProperties = Map.ofEntries(
Map.entry("svcRegulationOn", "true"),
Map.entry("dsoVoltageLevel", "45.0"),
Map.entry("shuntRegulationOn", "true"),
Map.entry("automaticSlackBusOn", "true"),
Map.entry("timeStep", "10.0"),
Map.entry("startingPointMode", "WARM"),
Map.entry("startTime", "0.0"),
Map.entry("stopTime", "100.0"),
Map.entry("activePowerCompensation", "PMAX"),
Map.entry("chosenOutputs", "TIMELINE"),
Map.entry("mergeLoads", "true"));
LoadFlowParameters params = LoadFlowParameters.load();
DynaFlowParameters dynaParams = params.getExtension(DynaFlowParameters.class);
Map<String, String> properties = provider.createMapFromSpecificParameters(dynaParams);
assertThat(properties).containsExactlyInAnyOrderEntriesOf(expectedProperties);
}
private void compare(Network expected, Network actual) throws IOException {
Path pexpected = tmpDir.resolve("expected.xiidm");
assertNotNull(pexpected);
Path pactual = tmpDir.resolve("actual.xiidm");
assertNotNull(pactual);
NetworkSerDe.write(expected, pexpected);
actual.setCaseDate(expected.getCaseDate());
NetworkSerDe.write(actual, pactual);
assertXmlEquals(Files.newInputStream(pexpected), Files.newInputStream(pactual));
}
private static Network createTestNetwork() {
Network network = Network.create("test", "test");
Substation s = network.newSubstation().setId("substation").add();
VoltageLevel vl1 = s.newVoltageLevel().setId("vl1").setNominalV(400).setTopologyKind(TopologyKind.NODE_BREAKER).add();
vl1.getNodeBreakerView().newBusbarSection().setId("Busbar").setNode(0).add();
vl1.getNodeBreakerView().newDisconnector().setNode1(0).setNode2(1).setId("d1").add();
vl1.getNodeBreakerView().newDisconnector().setNode1(0).setNode2(2).setId("d2").add();
vl1.getNodeBreakerView().newDisconnector().setNode1(0).setNode2(3).setId("d3").add();
vl1.newLoad().setId("load1").setP0(10.0).setQ0(5.0).setNode(1).add();
vl1.newLoad().setId("load2").setP0(12.0).setQ0(1.0).setNode(2).add();
VoltageLevel vl2 = s.newVoltageLevel().setId("vl2").setNominalV(400).setTopologyKind(TopologyKind.BUS_BREAKER).add();
Bus b1 = vl2.getBusBreakerView().newBus().setId("b1").add();
vl2.getBusBreakerView().newBus().setId("b2").add();
vl2.getBusBreakerView().newSwitch().setId("c").setBus1("b1").setBus2("b2").add();
vl2.newGenerator().setId("g1").setBus("b1").setTargetP(101).setTargetV(390).setMinP(0).setMaxP(150).setVoltageRegulatorOn(true).add();
vl2.newLoad().setId("load3").setP0(77.0).setQ0(1.0).setBus("b2").add();
network.newLine().setId("l1").setVoltageLevel1(vl1.getId()).setNode1(3).setVoltageLevel2(vl2.getId()).setBus2(b1.getId())
.setR(1).setX(3).setG1(0).setG2(0).setB1(0).setB2(0).add();
return network;
}
}