Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fix to Matpower importer to import some pglib files #3256

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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 @@ -43,7 +43,6 @@ public class MatpowerExporter implements Exporter {
private static final Logger LOGGER = LoggerFactory.getLogger(MatpowerExporter.class);

private static final double BASE_MVA = 100;
private static final String FORMAT_VERSION = "2";
private static final int AREA_NUMBER = 1;
private static final int LOSS_ZONE = 1;
private static final int CONNECTED_STATUS = 1;
Expand Down Expand Up @@ -1092,7 +1091,7 @@ public void export(Network network, Properties parameters, DataSource dataSource

MatpowerModel model = new MatpowerModel(network.getId());
model.setBaseMva(BASE_MVA);
model.setVersion(FORMAT_VERSION);
model.setVersion(MatpowerFormatVersion.V2);

Context context = new Context(maxGeneratorActivePower, maxGeneratorReactivePower);
context.findSynchronousComponentsToBeExported(network);
Expand Down
11 changes: 5 additions & 6 deletions matpower/matpower-model/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
<groupId>us.hebi.matlab.mat</groupId>
<artifactId>mfl-core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-commons</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand All @@ -75,11 +80,5 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-commons</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* 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.matpower.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
public enum MatpowerFormatVersion {
V1(10),
V2(21);

private final int generatorColumns;

MatpowerFormatVersion(int generatorColumns) {
this.generatorColumns = generatorColumns;
}

public int getGeneratorColumns() {
return generatorColumns;
}

@JsonCreator
public static MatpowerFormatVersion fromString(String version) {
return switch (version) {
case "1" -> V1;
case "2" -> V2;
default -> throw new IllegalArgumentException("Unsupported Matpower format version: " + version);
};
}

@JsonValue
@Override
public String toString() {
return switch (this) {
case V1 -> "1";
case V2 -> "2";
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class MatpowerModel {
@JsonProperty("caseName")
private String caseName;
private double baseMva;
private String version;
private MatpowerFormatVersion version;

private final List<MBus> buses = new ArrayList<>();

Expand All @@ -39,12 +39,12 @@ public MatpowerModel(@JsonProperty("caseName") String caseName) {
this.caseName = Objects.requireNonNull(caseName);
}

public String getVersion() {
public MatpowerFormatVersion getVersion() {
return version;
}

public void setVersion(String version) {
this.version = version;
public void setVersion(MatpowerFormatVersion version) {
this.version = Objects.requireNonNull(version);
}

public String getCaseName() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
package com.powsybl.matpower.model;

import com.google.common.collect.Sets;
import com.powsybl.commons.PowsyblException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.hebi.matlab.mat.format.Mat5;
import us.hebi.matlab.mat.types.*;

Expand All @@ -24,10 +27,11 @@
*/
public final class MatpowerReader {

private static final Logger LOGGER = LoggerFactory.getLogger(MatpowerReader.class);

public static final String MATPOWER_STRUCT_NAME = "mpc";
public static final String MATPOWER_SUPPORTED_VERSION = "2";
public static final MatpowerFormatVersion MATPOWER_SUPPORTED_VERSION = MatpowerFormatVersion.V2;
public static final int MATPOWER_BUSES_COLUMNS = 13;
public static final int MATPOWER_GENERATORS_COLUMNS = 21;
public static final int MATPOWER_BRANCHES_COLUMNS = 13;
public static final int MATPOWER_DCLINES_COLUMNS = 17;

Expand All @@ -54,8 +58,8 @@ public static MatpowerModel read(InputStream iStream, String caseName) throws IO
if (!fieldNames.containsAll(mpcNames)) {
throw new IllegalStateException("expected MATPOWER variables not found: " + mpcNames);
}
String version = mpcStruct.get("version").toString().replace("'", "");
if (!version.equals(MATPOWER_SUPPORTED_VERSION)) {
MatpowerFormatVersion version = MatpowerFormatVersion.fromString(mpcStruct.get("version").toString().replace("'", ""));
if (version != MATPOWER_SUPPORTED_VERSION) {
throw new IllegalStateException("unsupported MATPOWER version: " + version);
}

Expand All @@ -72,39 +76,50 @@ public static MatpowerModel read(InputStream iStream, String caseName) throws IO
dcLines = mpcStruct.getMatrix("dcline");
}

checkNumberOfColumns(buses, generators, branches, dcLines);
int busColumns = buses.getDimensions()[1];
int generatorColumns = generators.getDimensions()[1];
int branchColumns = branches.getDimensions()[1];
Integer dcLineColumns = dcLines != null ? dcLines.getDimensions()[1] : null;
VersionToRead versionToRead = checkNumberOfColumns(busColumns, generatorColumns, branchColumns, dcLineColumns);

model = new MatpowerModel(caseName);
model.setVersion(version);
model.setBaseMva(baseMVA);

readBuses(buses, busesNames, model);
readGenerators(generators, model);
readGenerators(generators, versionToRead.generatorVersion, model);
readBranches(branches, model);
readDcLines(dcLines, model);
}

return model;
}

private static void checkNumberOfColumns(Matrix buses, Matrix generators, Matrix branches, Matrix dcLines) {
if (buses.getDimensions()[1] < MATPOWER_BUSES_COLUMNS
|| generators.getDimensions()[1] < MATPOWER_GENERATORS_COLUMNS
|| branches.getDimensions()[1] < MATPOWER_BRANCHES_COLUMNS
|| dcLines != null && dcLines.getDimensions()[1] < MATPOWER_DCLINES_COLUMNS) {

String exceptionMessage;
if (dcLines == null) {
exceptionMessage = String.format("Unexpected number of columns. Expected: Buses %d Generators %d Branches %d Received: Buses %d Generators %d Branches %d",
MATPOWER_BUSES_COLUMNS, MATPOWER_GENERATORS_COLUMNS, MATPOWER_BRANCHES_COLUMNS,
buses.getDimensions()[1], generators.getDimensions()[1], branches.getDimensions()[1]);
} else {
exceptionMessage = String.format("Unexpected number of columns. Expected: Buses %d Generators %d Branches %d DcLines %d Received: Buses %d Generators %d Branches %d DcLines %d",
MATPOWER_BUSES_COLUMNS, MATPOWER_GENERATORS_COLUMNS, MATPOWER_BRANCHES_COLUMNS, MATPOWER_DCLINES_COLUMNS,
buses.getDimensions()[1], generators.getDimensions()[1], branches.getDimensions()[1], dcLines.getDimensions()[1]);
}
throw new IllegalStateException(exceptionMessage);
record VersionToRead(MatpowerFormatVersion generatorVersion) {
}

static VersionToRead checkNumberOfColumns(int busColumns, int generatorColumns, int branchColumns, Integer dcLineColumns) {
if (busColumns < MATPOWER_BUSES_COLUMNS) {
throw new PowsyblException("Unexpected number of columns for buses, expected at least " + MATPOWER_BUSES_COLUMNS + " columns, but got " + busColumns);
}
if (generatorColumns < MatpowerFormatVersion.V1.getGeneratorColumns()) {
throw new PowsyblException("Unexpected number of columns for generators, expected at least " + MatpowerFormatVersion.V1.getGeneratorColumns() + " columns, but got " + generatorColumns);
}
MatpowerFormatVersion generatorVersionToRead;
if (generatorColumns < MatpowerFormatVersion.V2.getGeneratorColumns()) {
LOGGER.warn("It is not expected in Matpower v2 format to have less than {} columns for generators, reading {} columns instead as for v1 format",
MatpowerFormatVersion.V2.getGeneratorColumns(), MatpowerFormatVersion.V1.getGeneratorColumns());
generatorVersionToRead = MatpowerFormatVersion.V1;
} else {
generatorVersionToRead = MatpowerFormatVersion.V2;
}
if (branchColumns < MATPOWER_BRANCHES_COLUMNS) {
throw new PowsyblException("Unexpected number of columns for branches, expected at least " + MATPOWER_BRANCHES_COLUMNS + " columns, but got " + branchColumns);
}
if (dcLineColumns != null && dcLineColumns < MATPOWER_DCLINES_COLUMNS) {
throw new PowsyblException("Unexpected number of columns for DC lines, expected at least " + MATPOWER_DCLINES_COLUMNS + " columns, but got " + dcLineColumns);
}
return new VersionToRead(generatorVersionToRead);
}

private static void readBuses(Matrix buses, Cell busesNames, MatpowerModel model) {
Expand Down Expand Up @@ -132,7 +147,7 @@ private static void readBuses(Matrix buses, Cell busesNames, MatpowerModel model
}
}

private static void readGenerators(Matrix generators, MatpowerModel model) {
private static void readGenerators(Matrix generators, MatpowerFormatVersion generatorVersionToRead, MatpowerModel model) {
for (int row = 0; row < generators.getDimensions()[0]; row++) {
MGen gen = new MGen();
gen.setNumber(generators.getInt(row, 0));
Expand All @@ -145,17 +160,19 @@ private static void readGenerators(Matrix generators, MatpowerModel model) {
gen.setStatus(generators.getInt(row, 7));
gen.setMaximumRealPowerOutput(generators.getDouble(row, 8));
gen.setMinimumRealPowerOutput(generators.getDouble(row, 9));
gen.setPc1(generators.getDouble(row, 10));
gen.setPc2(generators.getDouble(row, 11));
gen.setQc1Min(generators.getDouble(row, 12));
gen.setQc1Max(generators.getDouble(row, 13));
gen.setQc2Min(generators.getDouble(row, 14));
gen.setQc2Max(generators.getDouble(row, 15));
gen.setRampAgc(generators.getDouble(row, 16));
gen.setRampTenMinutes(generators.getDouble(row, 17));
gen.setRampThirtyMinutes(generators.getDouble(row, 18));
gen.setRampQ(generators.getDouble(row, 19));
gen.setApf(generators.getDouble(row, 20));
if (generatorVersionToRead == MatpowerFormatVersion.V2) {
gen.setPc1(generators.getDouble(row, 10));
gen.setPc2(generators.getDouble(row, 11));
gen.setQc1Min(generators.getDouble(row, 12));
gen.setQc1Max(generators.getDouble(row, 13));
gen.setQc2Min(generators.getDouble(row, 14));
gen.setQc2Max(generators.getDouble(row, 15));
gen.setRampAgc(generators.getDouble(row, 16));
gen.setRampTenMinutes(generators.getDouble(row, 17));
gen.setRampThirtyMinutes(generators.getDouble(row, 18));
gen.setRampQ(generators.getDouble(row, 19));
gen.setApf(generators.getDouble(row, 20));
}

model.addGenerator(gen);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ private static Struct fillMatStruct(Struct struct, MatpowerModel model, boolean
Matrix busesM = fillBusesMatrix(model.getBuses());
Cell busesNames = fillBusesNames(model.getBuses(), withBusNames);

Matrix gensM = fillGeneratorsMatrix(model.getGenerators());
Matrix gensM = fillGeneratorsMatrix(model.getGenerators(), model.getVersion());
Matrix branchesM = fillBranchesMatrix(model.getBranches());
Matrix dcLinesM = fillDcLinesMatrix(model.getDcLines());

struct.set("version", Mat5.newString(model.getVersion()))
struct.set("version", Mat5.newString(model.getVersion().toString()))
.set("baseMVA", Mat5.newScalar(model.getBaseMva()))
.set("bus", busesM)
.set("gen", gensM)
Expand Down Expand Up @@ -95,8 +95,8 @@ private static Cell fillBusesNames(List<MBus> buses, boolean withBusNames) {
return busesNames;
}

private static Matrix fillGeneratorsMatrix(List<MGen> gens) {
Matrix gensM = Mat5.newMatrix(gens.size(), MatpowerReader.MATPOWER_GENERATORS_COLUMNS);
private static Matrix fillGeneratorsMatrix(List<MGen> gens, MatpowerFormatVersion version) {
Matrix gensM = Mat5.newMatrix(gens.size(), version.getGeneratorColumns());

for (int row = 0; row < gens.size(); row++) {
gensM.setInt(row, 0, gens.get(row).getNumber());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.powsybl.commons.PowsyblException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -19,6 +20,7 @@
import java.nio.file.FileSystem;
import java.nio.file.Path;

import static com.powsybl.matpower.model.MatpowerReader.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

Expand Down Expand Up @@ -92,9 +94,9 @@ void testCase9DcLine() throws IOException {
}

@Test
void testInvalidModel() throws IOException {
void testInvalidModel() {
MatpowerModel model = new MatpowerModel("not-valid");
model.setVersion("1");
model.setVersion(MatpowerFormatVersion.V1);
assertThrows(IllegalStateException.class, () -> testMatpowerFile(model));
}

Expand All @@ -110,4 +112,24 @@ void testNonValidBus2() {
assertThrows(IllegalArgumentException.class, () -> bus.setType(MBus.Type.fromInt(0)));
}

@Test
void testRequiredColumns() {
int generatorV2Columns = MatpowerFormatVersion.V2.getGeneratorColumns();
assertEquals(MatpowerFormatVersion.V2,
MatpowerReader.checkNumberOfColumns(MATPOWER_BUSES_COLUMNS, generatorV2Columns, MATPOWER_BRANCHES_COLUMNS, MATPOWER_DCLINES_COLUMNS).generatorVersion());
assertEquals(MatpowerFormatVersion.V1,
MatpowerReader.checkNumberOfColumns(MATPOWER_BUSES_COLUMNS, MatpowerFormatVersion.V1.getGeneratorColumns(), MATPOWER_BRANCHES_COLUMNS, MATPOWER_DCLINES_COLUMNS).generatorVersion());
assertEquals(MatpowerFormatVersion.V1,
MatpowerReader.checkNumberOfColumns(MATPOWER_BUSES_COLUMNS, 12, MATPOWER_BRANCHES_COLUMNS, MATPOWER_DCLINES_COLUMNS).generatorVersion());
assertEquals(MatpowerFormatVersion.V2,
MatpowerReader.checkNumberOfColumns(MATPOWER_BUSES_COLUMNS, 23, MATPOWER_BRANCHES_COLUMNS, MATPOWER_DCLINES_COLUMNS).generatorVersion());
var e = assertThrows(PowsyblException.class, () -> MatpowerReader.checkNumberOfColumns(MATPOWER_BUSES_COLUMNS, 5, MATPOWER_BRANCHES_COLUMNS, MATPOWER_DCLINES_COLUMNS));
assertEquals("Unexpected number of columns for generators, expected at least 10 columns, but got 5", e.getMessage());
e = assertThrows(PowsyblException.class, () -> MatpowerReader.checkNumberOfColumns(4, generatorV2Columns, MATPOWER_BRANCHES_COLUMNS, MATPOWER_DCLINES_COLUMNS));
assertEquals("Unexpected number of columns for buses, expected at least 13 columns, but got 4", e.getMessage());
e = assertThrows(PowsyblException.class, () -> MatpowerReader.checkNumberOfColumns(MATPOWER_BUSES_COLUMNS, generatorV2Columns, 7, MATPOWER_DCLINES_COLUMNS));
assertEquals("Unexpected number of columns for branches, expected at least 13 columns, but got 7", e.getMessage());
e = assertThrows(PowsyblException.class, () -> MatpowerReader.checkNumberOfColumns(MATPOWER_BUSES_COLUMNS, generatorV2Columns, MATPOWER_BRANCHES_COLUMNS, 13));
assertEquals("Unexpected number of columns for DC lines, expected at least 17 columns, but got 13", e.getMessage());
}
}
Loading