PsseFullExportTest.java
/**
* Copyright (c) 2025, 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.psse.converter;
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.commons.datasource.DataSource;
import com.powsybl.commons.datasource.DirectoryDataSource;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.SlackTerminalAdder;
import org.junit.jupiter.api.Test;
import static com.powsybl.commons.test.ComparisonUtils.assertTxtEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.Properties;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
* @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
*/
class PsseFullExportTest extends AbstractSerDeTest {
private void exportTest(Network network, String baseName, String extensionName) throws IOException {
String pathName = "/work/";
String fileName = baseName + "." + extensionName;
Path path = fileSystem.getPath(pathName);
Path file = fileSystem.getPath(pathName + fileName);
Properties properties = null;
if (extensionName.equals("rawx")) {
properties = new Properties();
properties.put("psse.export.raw-format", false);
}
DataSource dataSource = new DirectoryDataSource(path, baseName);
new PsseExporter().export(network, properties, dataSource);
try (InputStream is = Files.newInputStream(file)) {
assertTxtEquals(getClass().getResourceAsStream("/" + fileName), is);
}
}
@Test
void fullExportRawTest() throws IOException {
Network network = createNetwork();
exportTest(network, "full_export", "raw");
}
@Test
void fullExportRawxTest() throws IOException {
Network network = createNetwork();
exportTest(network, "full_export", "rawx");
}
private Network createNetwork() {
Network network = Network.create("Psse.fullExport", "no-format");
network.setCaseDate(ZonedDateTime.parse("2016-01-01T10:00:00.000+02:00"));
Substation sub1 = createSubstation(network, "Sub1");
VoltageLevel vl1S1 = createVoltageLevel(sub1, "Vl1-Sub1", 400.0, TopologyKind.NODE_BREAKER);
createSwitch(vl1S1, "Sw1-Vl1-Sub1", 1, 2, false);
createSwitch(vl1S1, "Sw-Line-Vl1-Sub1-Sub2", 1, 3, false);
createSwitch(vl1S1, "Sw-Gen-Vl1-Sub1", 2, 4, false);
createSwitch(vl1S1, "Sw-Line-Vl1-Sub1-Sub7", 2, 5, false);
createSwitch(vl1S1, "Sw-Line-Vl1-Sub1-Sub4", 2, 6, false);
createSwitch(vl1S1, "Sw-Line-Vl1-Sub1-Sub5", 2, 7, false);
Generator gen = createGenerator(vl1S1, "Gen-Vl1-Sub1", 4, 50.0, 10.0, 405.0, true);
// define the slack bus
vl1S1.newExtension(SlackTerminalAdder.class).withTerminal(gen.getTerminal()).add();
Substation sub2 = createSubstation(network, "Sub2");
VoltageLevel vl1S2 = createVoltageLevel(sub2, "Vl1-Sub2", 400.0, TopologyKind.NODE_BREAKER);
createSwitch(vl1S2, "Sw-Line-Vl1-Sub2-Sub1", 1, 2, false);
createSwitch(vl1S2, "Sw-Line-Vl1-Sub2-Sub3", 1, 3, false);
createSwitch(vl1S2, "Sw-Vsc-Vl1-Sub2-Sub4", 1, 4, false);
createSwitch(vl1S2, "Sw-Lcc-Vl1-Sub2-Sub5", 1, 5, false);
createSwitch(vl1S2, "Sw-DanglingLine-Vl1-Sub2", 1, 6, false);
DanglingLine dlVl1S2 = createDanglingLine(vl1S2, "DanglingLine-Vl1-Sub2", 6, 5.0, 2.0, "TieLine");
Substation sub3 = createSubstation(network, "Sub3");
VoltageLevel vl1S3 = createVoltageLevel(sub3, "Vl1-Sub3", 400.0, TopologyKind.NODE_BREAKER);
createSwitch(vl1S3, "Sw-Line-Vl1-Sub3-Sub2", 1, 2, false);
createSwitch(vl1S3, "Sw-Load-Vl1-Sub3", 1, 3, false);
createSwitch(vl1S3, "Sw-T2w-Vl1-Sub3", 1, 4, false);
createSwitch(vl1S3, "Sw-DanglingLine-Vl1-Sub3", 1, 5, false);
createLoad(vl1S3, "Load-Vl1-Sub3", 3, 25.0, 5.0);
DanglingLine dlVl1S3 = vl1S3.newDanglingLine()
.setId("DanglingLine-Vl1-S3")
.setName("DanglingLine-Vl1-S3")
.setNode(5)
.setR(5.0)
.setX(85.0)
.setG(0.0)
.setB(0.0)
.setP0(0.0)
.setQ0(0.0)
.setPairingKey("-")
.newGeneration()
.setTargetV(405.0)
.setTargetP(10.5)
.setTargetQ(2.5)
.setVoltageRegulationOn(true)
.setMaxP(100.0)
.setMinP(0.0)
.add()
.add();
dlVl1S3.getGeneration().newMinMaxReactiveLimits().setMinQ(-25.0).setMaxQ(35.0).add();
VoltageLevel vl2S3 = createVoltageLevel(sub3, "Vl2-Sub3", 25.0, TopologyKind.BUS_BREAKER);
Bus busVl2S3 = createBus(vl2S3, "Bus-Vl2-Sub3");
createLoad(vl2S3, "Load-Vl2-Sub3", busVl2S3.getId(), 10.0, 2.0);
TwoWindingsTransformer t2w = sub3.newTwoWindingsTransformer()
.setId("T2w-Vl1-Vl2-Sub3")
.setName("T2w-Vl1-Vl2-Sub3")
.setVoltageLevel1(vl1S3.getId())
.setVoltageLevel2(vl2S3.getId())
.setNode1(4)
.setBus2(busVl2S3.getId())
.setConnectableBus2(busVl2S3.getId())
.setR(0.001)
.setX(0.01)
.setG(0.0001)
.setB(-0.0002)
.setRatedU1(vl1S3.getNominalV())
.setRatedU2(vl2S3.getNominalV() * 1.01)
.setRatedS(100.0)
.add();
t2w.newRatioTapChanger()
.setLowTapPosition(0)
.setTapPosition(2)
.beginStep().setRho(1.05).endStep()
.beginStep().setRho(1.0).endStep()
.beginStep().setRho(0.95).endStep()
.setRegulationMode(RatioTapChanger.RegulationMode.VOLTAGE)
.setTargetV(vl2S3.getNominalV() * 1.02)
.setRegulationTerminal(t2w.getTerminal2());
t2w.newOperationalLimitsGroup1("ApparentPowerLimits")
.newApparentPowerLimits()
.setPermanentLimit(95.0)
.beginTemporaryLimit()
.setName("TemporaryApparentPowerLimit")
.setAcceptableDuration(60)
.setValue(115.0)
.endTemporaryLimit()
.add();
Substation sub4 = createSubstation(network, "Sub4");
VoltageLevel vl1S4 = createVoltageLevel(sub4, "Vl1-Sub4", 400.0, TopologyKind.NODE_BREAKER);
createSwitch(vl1S4, "Sw-Vsc-Vl1-Sub4-Sub2", 1, 2, false);
createSwitch(vl1S4, "Sw-FixedShunt-Vl1-Sub4", 1, 3, false);
createSwitch(vl1S4, "Sw-T3w-Vl1-Sub4", 1, 4, false);
createSwitch(vl1S4, "Sw-Line-Vl1-Sub4-sub1", 1, 5, false);
ShuntCompensator shunt = vl1S4.newShuntCompensator()
.setId("FixedShunt-Vl1-Sub4")
.setName("FixedShunt-Vl1-Sub4")
.setNode(3)
.setSectionCount(1)
.newLinearModel()
.setMaximumSectionCount(1)
.setGPerSection(0.001)
.setBPerSection(0.1)
.add()
.setVoltageRegulatorOn(false)
.add();
VoltageLevel vl2S4 = createVoltageLevel(sub4, "Vl2-Sub4", 110.0, TopologyKind.NODE_BREAKER);
createSwitch(vl2S4, "Sw-T3w-Vl2-Sub4", 1, 2, false);
createSwitch(vl2S4, "Sw-SwitchedShunt-Vl2-Sub4", 1, 3, false);
createSwitch(vl2S4, "Sw-Load-Vl2-Sub4", 1, 4, false);
vl2S4.newShuntCompensator()
.setId("SwitchedShunt-Vl2-Sub4")
.setName("SwitchedShunt-Vl2-Sub4")
.setNode(3)
.setSectionCount(1)
.newLinearModel()
.setMaximumSectionCount(2)
.setGPerSection(0.001)
.setBPerSection(0.1)
.add()
.setTargetV(vl2S4.getNominalV() * 1.01)
.setTargetDeadband(0.5)
.setVoltageRegulatorOn(true)
.add();
createLoad(vl2S4, "Load-Vl2-Sub4", 4, 12.0, 4.0);
VoltageLevel vl3S4 = createVoltageLevel(sub4, "Vl3-Sub4", 25.0, TopologyKind.NODE_BREAKER);
createSwitch(vl3S4, "Sw-T3w-Vl3-Sub4", 1, 2, false);
createSwitch(vl3S4, "Sw-Gen-Vl3-Sub4", 1, 3, false);
createSwitch(vl3S4, "Sw-StaticVar-Vl3-Sub4", 1, 4, false);
createGenerator(vl3S4, "Gen-Vl3-Sub4", 3, 0.5, 5.5, 25.0, false);
vl3S4.newStaticVarCompensator()
.setId("StaticVar-Vl3-Sub4")
.setName("StaticVar-Vl3-Sub4")
.setNode(4)
.setBmin(0.0)
.setBmax(10.0)
.setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE)
.setRegulatingTerminal(shunt.getTerminal())
.setVoltageSetpoint(vl1S4.getNominalV() * 1.02)
.setReactivePowerSetpoint(0.0)
.setRegulating(true)
.add();
ThreeWindingsTransformer t3w = sub4.newThreeWindingsTransformer()
.setId("T3w-Vl1-Vl2-Vl3-Sub4")
.setName("T3w-Vl1-Vl2-Vl3-Sub4")
.setRatedU0(vl1S4.getNominalV())
.newLeg1()
.setVoltageLevel(vl1S4.getId())
.setNode(4)
.setR(0.0)
.setX(0.1)
.setG(0.0001)
.setB(-0.002)
.setRatedU(vl1S4.getNominalV() * 1.03)
.setRatedS(200.0)
.add()
.newLeg2()
.setVoltageLevel(vl2S4.getId())
.setNode(2)
.setR(0.001)
.setX(0.2)
.setG(0.0)
.setB(0.0)
.setRatedU(vl2S4.getNominalV())
.setRatedS(150.0)
.add()
.newLeg3()
.setVoltageLevel(vl3S4.getId())
.setNode(2)
.setR(0.002)
.setX(0.25)
.setG(0.0)
.setB(-0.0025)
.setRatedU(vl3S4.getNominalV())
.setRatedS(50)
.add()
.add();
t3w.getLeg1().newRatioTapChanger()
.setTapPosition(0)
.setLowTapPosition(0)
.beginStep()
.setRho(1.02)
.endStep()
.beginStep()
.setRho(1.0)
.endStep()
.beginStep()
.setRho(0.98)
.endStep()
.setRegulationTerminal(t3w.getLeg2().getTerminal())
.setTargetV(vl2S4.getNominalV() * 0.99)
.setTargetDeadband(0.5)
.setRegulationMode(RatioTapChanger.RegulationMode.VOLTAGE);
t3w.getLeg1().newOperationalLimitsGroup("ActivePowerLimits")
.newActivePowerLimits()
.setPermanentLimit(210.0)
.beginTemporaryLimit()
.setName("TemporaryActivePowerLimit")
.setAcceptableDuration(120)
.setValue(225.0)
.endTemporaryLimit()
.add();
Substation sub5 = createSubstation(network, "Sub5");
VoltageLevel vl1S5 = createVoltageLevel(sub5, "Vl1-Sub5", 400.0, TopologyKind.NODE_BREAKER);
createSwitch(vl1S5, "Sw-Lcc-Vl1-Sub5-Sub2", 1, 2, false);
createSwitch(vl1S5, "Sw-DanglingLine-Vl1-Sub5", 1, 3, false);
createSwitch(vl1S5, "Sw-Gen-Vl1-Sub5", 1, 4, true);
createSwitch(vl1S5, "Sw-Load-Vl1-Sub5", 1, 5, false);
createSwitch(vl1S5, "Sw-Battery-Vl1-Sub5", 1, 6, false);
createSwitch(vl1S5, "Sw-Line-Vl1-Sub5-Sub1", 1, 7, false);
createDanglingLine(vl1S5, "DanglingLine-Vl1-Sub5", 3, 7.0, 1.25, "");
createGenerator(vl1S5, "Gen-Vl1-Sub5", 4, 2.0, 2.0, vl1S5.getNominalV(), false);
createLoad(vl1S5, "Load-Vl1-Sub5", 5, 2.0, 0.25);
vl1S5.newBattery()
.setId("Battery-Vl1-Sub5")
.setName("Battery-Vl1-Sub5")
.setNode(6)
.setMinP(0.0)
.setMaxP(50.0)
.setTargetP(14.0)
.setTargetQ(3.5)
.add();
Substation sub6 = createSubstation(network, "Sub6");
VoltageLevel vl1S6 = createVoltageLevel(sub6, "Vl1-Sub6", 400.0, TopologyKind.NODE_BREAKER);
createSwitch(vl1S6, "Sw-DanglingLine-Vl1-Sub6", 1, 2, false);
createSwitch(vl1S6, "Sw-Load-Vl1-Sub6", 1, 3, false);
DanglingLine dlVl1S6 = createDanglingLine(vl1S6, "DanglingLine-Vl1-Sub6", 2, -5.0, -2.0, "TieLine");
createLoad(vl1S6, "Load-Vl1-Sub6", 3, 5.0, 2.0);
Substation sub7 = createSubstation(network, "Sub7");
VoltageLevel vl1S7 = createVoltageLevel(sub7, "Vl1-Sub7", 400.0, TopologyKind.BUS_BREAKER);
Bus busVl1S7 = createBus(vl1S7, "Bus-Vl1-Sub7");
createLoad(vl1S7, "Load-Vl1-Sub7", busVl1S7.getId(), 5.0, 0.5);
// Lines
createLine(network, "Line-Vl1-Sub1-Vl1-Sub2", vl1S1, vl1S2, 3, 2);
createLine(network, "Line-Vl1-Sub2-Vl1-Sub3", vl1S2, vl1S3, 3, 2);
createLine(network, "Line-Vl1-Sub1-Vl1-Sub7", vl1S1, vl1S7, 5, busVl1S7);
createLine(network, "Line-Vl1-Sub1-Vl1-Sub4", vl1S1, vl1S4, 6, 5);
createLine(network, "Line-Vl1-Sub1-Vl1-Sub5", vl1S1, vl1S5, 7, 7);
// HvdcLines
VscConverterStation vsc1 = vl1S2.newVscConverterStation()
.setId("Vsc-Vl1-Sub2")
.setName("Vsc-Vl1-Sub2")
.setNode(4)
.setLossFactor(0.001f)
.setReactivePowerSetpoint(0.0)
.setVoltageSetpoint(vl1S2.getNominalV())
.setVoltageRegulatorOn(true)
.add();
vsc1.newMinMaxReactiveLimits().setMinQ(-250.0).setMaxQ(300.0).add();
VscConverterStation vsc2 = vl1S4.newVscConverterStation()
.setId("Vsc-Vl1-Sub4")
.setName("Vsc-Vl1-Sub4")
.setNode(2)
.setLossFactor(0.002f)
.setReactivePowerSetpoint(0.1)
.setVoltageSetpoint(vl1S4.getNominalV())
.setVoltageRegulatorOn(false)
.add();
vsc2.newMinMaxReactiveLimits().setMinQ(-260.0).setMaxQ(310.0).add();
network.newHvdcLine()
.setId("Vsc-Vl1-Sub2-Vl1-Sub4")
.setName("Vsc-Vl1-Sub2-Vl1-Sub4")
.setNominalV(vl1S2.getNominalV())
.setConverterStationId1(vsc1.getId())
.setConverterStationId2(vsc2.getId())
.setR(0.001)
.setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER)
.setMaxP(310.0)
.setActivePowerSetpoint(25.0)
.add();
LccConverterStation lcc1 = vl1S2.newLccConverterStation()
.setId("Lcc-Vl1-Sub2")
.setName("Lcc-Vl1-Sub2")
.setNode(5)
.setLossFactor(0.002f)
.setPowerFactor(0.95f)
.add();
LccConverterStation lcc2 = vl1S5.newLccConverterStation()
.setId("Lcc-Vl1-Sub5")
.setName("Lcc-Vl1-Sub5")
.setNode(2)
.setLossFactor(0.0021f)
.setPowerFactor(0.98f)
.add();
network.newHvdcLine()
.setId("Lcc-Vl1-Sub2-Vl1-Sub5")
.setName("Lcc-Vl1-Sub2-Vl1-Sub5")
.setNominalV(vl1S2.getNominalV())
.setConverterStationId1(lcc1.getId())
.setConverterStationId2(lcc2.getId())
.setR(0.0015)
.setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER)
.setMaxP(210.0)
.setActivePowerSetpoint(35.0)
.add();
// TieLine
network.newTieLine()
.setId("TieLine-Vl1-Sub2-Vl1-Sub6")
.setName("TieLine-Vl1-Sub2-Vl1-Sub6")
.setDanglingLine1(dlVl1S2.getId())
.setDanglingLine2(dlVl1S6.getId())
.add();
return network;
}
private static Substation createSubstation(Network network, String substationId) {
return network.newSubstation()
.setId(substationId)
.setName(substationId)
.add();
}
private static VoltageLevel createVoltageLevel(Substation substation, String voltageLevelId, double nominalV, TopologyKind topologyKind) {
return substation.newVoltageLevel()
.setId(voltageLevelId)
.setName(voltageLevelId)
.setNominalV(nominalV)
.setTopologyKind(topologyKind)
.add();
}
private static void createSwitch(VoltageLevel voltageLevel, String switchId, int node1, int node2, boolean isOpen) {
assertSame(TopologyKind.NODE_BREAKER, voltageLevel.getTopologyKind());
voltageLevel.getNodeBreakerView().newSwitch()
.setId(switchId)
.setName(switchId)
.setKind(SwitchKind.BREAKER)
.setNode1(node1)
.setNode2(node2)
.setOpen(isOpen)
.add();
}
private static Generator createGenerator(VoltageLevel voltageLevel, String generatorId, int node, double targetP, double targetQ, double targetV, boolean isRegulating) {
assertSame(TopologyKind.NODE_BREAKER, voltageLevel.getTopologyKind());
Generator gen = voltageLevel.newGenerator()
.setId(generatorId)
.setName(generatorId)
.setNode(node)
.setMinP(1.0)
.setMaxP(100.0)
.setRatedS(125.0)
.setTargetP(targetP)
.setTargetQ(targetQ)
.setTargetV(targetV)
.setVoltageRegulatorOn(false)
.add();
gen.setRegulatingTerminal(gen.getTerminal())
.setVoltageRegulatorOn(isRegulating);
gen.newMinMaxReactiveLimits().setMinQ(-225.0).setMaxQ(230.0).add();
return gen;
}
private static void createLoad(VoltageLevel voltageLevel, String loadId, int node, double p, double q) {
assertSame(TopologyKind.NODE_BREAKER, voltageLevel.getTopologyKind());
voltageLevel.newLoad()
.setId(loadId)
.setName(loadId)
.setNode(node)
.setP0(p)
.setQ0(q)
.setLoadType(LoadType.UNDEFINED)
.add();
}
private static Bus createBus(VoltageLevel voltageLevel, String busId) {
assertSame(TopologyKind.BUS_BREAKER, voltageLevel.getTopologyKind());
return voltageLevel.getBusBreakerView().newBus()
.setId(busId)
.setName(busId)
.add();
}
private static void createLoad(VoltageLevel voltageLevel, String loadId, String busId, double p, double q) {
assertSame(TopologyKind.BUS_BREAKER, voltageLevel.getTopologyKind());
voltageLevel.newLoad()
.setId(loadId)
.setName(loadId)
.setBus(busId)
.setConnectableBus(busId)
.setP0(p)
.setQ0(q)
.setLoadType(LoadType.UNDEFINED)
.add();
}
private static DanglingLine createDanglingLine(VoltageLevel voltageLevel, String danglingLineId, int node, double p0, double q0, String pairingKey) {
return voltageLevel.newDanglingLine()
.setId(danglingLineId)
.setName(danglingLineId)
.setNode(node)
.setR(10.0)
.setX(90.0)
.setG(0.02)
.setB(1.2)
.setP0(p0)
.setQ0(q0)
.setPairingKey(pairingKey)
.add();
}
private static void createLine(Network network, String lineId, VoltageLevel vl1, VoltageLevel vl2, int node1, int node2) {
createLine(network, lineId, vl1, vl2, node1, node2, null);
}
private static void createLine(Network network, String lineId, VoltageLevel vl1, VoltageLevel vl2, int node1, Bus bus2) {
createLine(network, lineId, vl1, vl2, node1, 0, bus2);
}
private static void createLine(Network network, String lineId, VoltageLevel vl1, VoltageLevel vl2, int node1, int node2, Bus bus2) {
Line line;
if (bus2 != null) {
line = network.newLine()
.setId(lineId)
.setName(lineId)
.setVoltageLevel1(vl1.getId())
.setVoltageLevel2(vl2.getId())
.setNode1(node1)
.setBus2(bus2.getId())
.setConnectableBus2(bus2.getId())
.setR(15.0)
.setX(100.0)
.add();
} else {
line = network.newLine()
.setId(lineId)
.setName(lineId)
.setVoltageLevel1(vl1.getId())
.setVoltageLevel2(vl2.getId())
.setNode1(node1)
.setNode2(node2)
.setR(10.0)
.setX(90.0)
.add();
}
line.setG1(0.0)
.setB1(0.0)
.setG2(0.0)
.setB2(0.0);
line.newCurrentLimits1()
.setPermanentLimit(500.0)
.beginTemporaryLimit()
.setName("TemporaryCurrentLimits")
.setAcceptableDuration(180)
.setValue(550.0)
.endTemporaryLimit()
.add();
}
}