Skip to content

Commit

Permalink
Adapt OPF case parser for pglib files (#133)
Browse files Browse the repository at this point in the history
* Adapt OPF case parser for pglib files

Those files have only the 10 first columns for the gen array.
The others are actually not used in the formulations.

* Add a test and a pglib like matpower file
  • Loading branch information
pobonomo authored Mar 4, 2024
1 parent 3a28b20 commit 2cc5fe0
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 8 deletions.
Binary file added src/gurobi_optimods/data/opf/pglib_case14.mat
Binary file not shown.
9 changes: 9 additions & 0 deletions src/gurobi_optimods/opf/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,18 @@ def fix_shape(arr, ncols=None):
type=lambda df: df["type"].astype(int),
)
gen_array = fix_shape(mpc["gen"].item(), ncols=len(gen_field_names))

if gen_array.shape[1] < len(gen_field_names):
# If gen_array doesn't have all columns fill the remainder with Nan
# We must have at least 10 columns
assert gen_array.shape[1] >= 10
missing = len(gen_field_names) - gen_array.shape[1]
nan_array = np.full((gen_array.shape[0], missing), np.nan)
gen_array = np.append(gen_array, nan_array, axis=1)
gen = pd.DataFrame(data=gen_array, columns=gen_field_names).assign(
bus=lambda df: df["bus"].astype(int)
)

branch_array = fix_shape(mpc["branch"].item(), ncols=len(branch_field_names))
branch = pd.DataFrame(data=branch_array, columns=branch_field_names).assign(
fbus=lambda df: df["fbus"].astype(int), tbus=lambda df: df["tbus"].astype(int)
Expand Down
61 changes: 53 additions & 8 deletions tests/opf/test_io.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Tests of reads/writes to data files

import json
import math
import pathlib
import tempfile
import unittest
Expand Down Expand Up @@ -113,6 +114,18 @@ def test_matfile_roundtrip(self):
self.assertIsInstance(branch["fbus"], int)
self.assertIsInstance(branch["tbus"], int)

def read_write_and_read_case(self, file_path):
# Should read without errors
original = read_case_matpower(file_path)

# Test write and read back
with tempfile.TemporaryDirectory() as tmpdir:
tmpfile = pathlib.Path(tmpdir) / "testcase.mat"
write_case_matpower(original, tmpfile)
reread = read_case_matpower(tmpfile)

return (original, reread)

def test_read_case_matpower(self):
# Check that all example cases are read without errors

Expand All @@ -123,14 +136,7 @@ def test_read_case_matpower(self):

for file_path in case_mat_files:
with self.subTest(file_path=file_path):
# Should read without errors
original = read_case_matpower(file_path)

# Test write and read back
with tempfile.TemporaryDirectory() as tmpdir:
tmpfile = pathlib.Path(tmpdir) / "testcase.mat"
write_case_matpower(original, tmpfile)
reread = read_case_matpower(tmpfile)
original, reread = self.read_write_and_read_case(file_path)

# The first read and the round-trip should match exactly
self.assertEqual(set(reread.keys()), set(original.keys()))
Expand All @@ -146,3 +152,42 @@ def test_read_case_matpower(self):
for branch in reread["branch"]:
self.assertIsInstance(branch["fbus"], int)
self.assertIsInstance(branch["tbus"], int)

def test_read_case_pglib(self):
# Check that all example cases are read without errors

case_mat_files = [
self.dataset_dir.joinpath(f"pglib_case{case}.mat") for case in ["14"]
]

for file_path in case_mat_files:
with self.subTest(file_path=file_path):
original, reread = self.read_write_and_read_case(file_path)

# The first read and the round-trip should match exactly
for (field1, data1), (field2, data2) in zip(
reread.items(), original.items()
):
self.assertEqual(field1, field2)
if field1 == "gen":
# This is a list of dictionaries
# The first 10 entries of each dictionary should match the remainder should be nan's
for entry1, entry2 in zip(data1, data2):
entry1 = list(entry1.items())
entry2 = list(entry2.items())
self.assertEqual(entry1[:10], entry2[:10])
self.assertTrue(all(math.isnan(e) for _, e in entry1[10:]))
self.assertTrue(all(math.isnan(e) for _, e in entry2[10:]))
continue

self.assertEqual(data1, data2)

# Check types for some special cases
for bus in reread["bus"]:
self.assertIsInstance(bus["bus_i"], int)
self.assertIsInstance(bus["type"], int)
for gen in reread["gen"]:
self.assertIsInstance(gen["bus"], int)
for branch in reread["branch"]:
self.assertIsInstance(branch["fbus"], int)
self.assertIsInstance(branch["tbus"], int)

0 comments on commit 2cc5fe0

Please sign in to comment.