Skip to content

Commit

Permalink
Merge pull request #18 from yasirroni/master
Browse files Browse the repository at this point in the history
support any keys
  • Loading branch information
yasirroni authored Nov 12, 2024
2 parents 5571e9c + 7cd2bf4 commit e81fe89
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 23 deletions.
77 changes: 77 additions & 0 deletions data/case9_load.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
function mpc = case9
%CASE9 Power flow data for 9 bus, 3 generator case.
% Please see CASEFORMAT for details on the case file format.
%
% Based on data from p. 70 of:
%
% Chow, J. H., editor. Time-Scale Modeling of Dynamic Networks with
% Applications to Power Systems. Springer-Verlag, 1982.
% Part of the Lecture Notes in Control and Information Sciences book
% series (LNCIS, volume 46)
%
% which in turn appears to come from:
%
% R.P. Schulz, A.E. Turner and D.N. Ewart, "Long Term Power System
% Dynamics," EPRI Report 90-7-0, Palo Alto, California, 1974.

% MATPOWER

%% MATPOWER Case Format : Version 2
mpc.version = '2';

%%----- Power Flow Data -----%%
%% system MVA base
mpc.baseMVA = 100;

%% bus data
% bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin
mpc.bus = [
1 3 0 0 0 0 1 1 0 345 1 1.1 0.9;
2 2 0 0 0 0 1 1 0 345 1 1.1 0.9;
3 2 0 0 0 0 1 1 0 345 1 1.1 0.9;
4 1 0 0 0 0 1 1 0 345 1 1.1 0.9;
5 1 90 30 0 0 1 1 0 345 1 1.1 0.9;
6 1 0 0 0 0 1 1 0 345 1 1.1 0.9;
7 1 100 35 0 0 1 1 0 345 1 1.1 0.9;
8 1 0 0 0 0 1 1 0 345 1 1.1 0.9;
9 1 125 50 0 0 1 1 0 345 1 1.1 0.9;
];

%% generator data
% bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin Pc1 Pc2 Qc1min Qc1max Qc2min Qc2max ramp_agc ramp_10 ramp_30 ramp_q apf
mpc.gen = [
1 72.3 27.03 300 -300 1.04 100 1 250 10 0 0 0 0 0 0 0 0 0 0 0;
2 163 6.54 300 -300 1.025 100 1 300 10 0 0 0 0 0 0 0 0 0 0 0;
3 85 -10.95 300 -300 1.025 100 1 270 10 0 0 0 0 0 0 0 0 0 0 0;
];

%% branch data
% fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax
mpc.branch = [
1 4 0 0.0576 0 250 250 250 0 0 1 -360 360;
4 5 0.017 0.092 0.158 250 250 250 0 0 1 -360 360;
5 6 0.039 0.17 0.358 150 150 150 0 0 1 -360 360;
3 6 0 0.0586 0 300 300 300 0 0 1 -360 360;
6 7 0.0119 0.1008 0.209 150 150 150 0 0 1 -360 360;
7 8 0.0085 0.072 0.149 250 250 250 0 0 1 -360 360;
8 2 0 0.0625 0 250 250 250 0 0 1 -360 360;
8 9 0.032 0.161 0.306 250 250 250 0 0 1 -360 360;
9 4 0.01 0.085 0.176 250 250 250 0 0 1 -360 360;
];

%%----- OPF Data -----%%
%% generator cost data
% 1 startup shutdown n x1 y1 ... xn yn
% 2 startup shutdown n c(n-1) ... c0
mpc.gencost = [
2 1500 0 3 0.11 5 150;
2 2000 0 3 0.085 1.2 600;
2 3000 0 3 0.1225 1 335;
];

% ldid ldbus status Pd Qd lfact
mpc.load = [
1 5 1 90 30 1;
2 7 1 100 35 1;
3 9 1 125 50 1;
];
59 changes: 42 additions & 17 deletions matpowercaseframes/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@


class CaseFrames:
def __init__(self, data=None, update_index=True, load_case_engine=None):
def __init__(
self,
data=None,
update_index=True,
load_case_engine=None,
allow_any_keys=False,
columns_templates=None,
):
"""
Load data and initialize the CaseFrames class.
Expand All @@ -42,29 +49,46 @@ def __init__(self, data=None, update_index=True, load_case_engine=None):
"""
# TODO: support read excel
# TODO: support Path object
if columns_templates is None:
self.columns_templates = copy.deepcopy(COLUMNS)
else:
self.columns_templates = {**COLUMNS, **columns_templates}

if isinstance(data, str):
# TYPE: str of path
path = self._get_path(data)

if load_case_engine is None:
# read with matpower parser
self._read_matpower(filepath=path)
self._read_matpower(
filepath=path,
allow_any_keys=allow_any_keys,
)
else:
# read using loadcase
mpc = load_case_engine.loadcase(path)
self._read_oct2py_struct(struct=mpc)
self._read_oct2py_struct(
struct=mpc,
allow_any_keys=allow_any_keys,
)

elif isinstance(data, dict):
# TYPE: dict | oct2py.io.Struct
self._read_oct2py_struct(struct=data)
self._read_oct2py_struct(
struct=data,
allow_any_keys=allow_any_keys,
)
elif isinstance(data, np.ndarray):
# TYPE: structured NumPy array
# TODO: also support from.mat file via scipy.io
# TODO: when is the input from numpy array?
if data.dtype.names is None:
message = f"Source is {type(data)} but not a structured NumPy array."
raise TypeError(message)
self._read_numpy_struct(array=data)
self._read_numpy_struct(
array=data,
allow_any_keys=allow_any_keys,
)
elif data is None:
self.name = ""
self._attributes = []
Expand Down Expand Up @@ -96,6 +120,9 @@ def setattr(self, name, value):
self._attributes.append(name)
self.__setattr__(name, value)

def update_columns_templates(self, columns_templates):
self.columns_templates.update(columns_templates)

@staticmethod
def _get_path(path):
"""
Expand Down Expand Up @@ -130,7 +157,7 @@ def _get_path(path):

raise FileNotFoundError

def _read_matpower(self, filepath):
def _read_matpower(self, filepath, allow_any_keys=False):
"""
Read and parse a MATPOWER file.
Expand All @@ -147,8 +174,7 @@ def _read_matpower(self, filepath):
self._attributes = []

for attribute in find_attributes(string):
if attribute not in ATTRIBUTES:
# ? Should we support custom attributes?
if attribute not in ATTRIBUTES and not allow_any_keys:
continue

# TODO: compare with GridCal approach
Expand All @@ -164,7 +190,7 @@ def _read_matpower(self, filepath):

self.setattr(attribute, value)

def _read_oct2py_struct(self, struct):
def _read_oct2py_struct(self, struct, allow_any_keys=False):
"""
Read data from an Octave struct or dictionary.
Expand All @@ -176,8 +202,7 @@ def _read_oct2py_struct(self, struct):
self._attributes = []

for attribute, list_ in struct.items():
if attribute not in ATTRIBUTES:
# ? Should we support custom attributes?
if attribute not in ATTRIBUTES and not allow_any_keys:
continue

if attribute == "version" or attribute == "baseMVA":
Expand All @@ -192,7 +217,7 @@ def _read_oct2py_struct(self, struct):

return None

def _read_numpy_struct(self, array):
def _read_numpy_struct(self, array, allow_any_keys=False):
"""
Read data from a structured NumPy array.
Expand All @@ -202,8 +227,7 @@ def _read_numpy_struct(self, array):
self.name = ""
self._attributes = []
for attribute in array.dtype.names:
if attribute not in ATTRIBUTES:
# ? Should we support custom attributes?
if attribute not in ATTRIBUTES and not allow_any_keys:
continue

if attribute == "version" or attribute == "baseMVA":
Expand All @@ -217,8 +241,7 @@ def _read_numpy_struct(self, array):

self.setattr(attribute, value)

@staticmethod
def _get_dataframe(attribute, data, n_cols=None, columns_template=None):
def _get_dataframe(self, attribute, data, n_cols=None, columns_template=None):
"""
Create a DataFrame with proper columns from raw data.
Expand All @@ -242,7 +265,9 @@ def _get_dataframe(attribute, data, n_cols=None, columns_template=None):
# TODO: support custom COLUMNS
if columns_template is None:
# get columns_template, default to range
columns_template = COLUMNS.get(attribute, list(range(n_cols)))
columns_template = self.columns_templates.get(
attribute, list(range(n_cols))
)

columns = columns_template[:n_cols]
if n_cols > len(columns):
Expand Down
10 changes: 4 additions & 6 deletions matpowercaseframes/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,13 @@ def parse_file(attribute, string):


def search_file(attribute, string):
if attribute in ["gen", "gencost", "bus", "branch", "dcline", "dclinecost"]:
pattern = r"mpc\.{}\s*=\s*\[[\n]?(?P<data>.*?)[\n]?\];".format(attribute)
if attribute in ["bus_name", "branch_name", "gen_name"]:
pattern = r"mpc\.{}\s*=\s*\{{[\n]?(?P<data>.*?)[\n]?\}};".format(attribute)
elif attribute in ["version", "baseMVA"]:
pattern = r"mpc\.{}\s*=\s*(?P<data>.*?);".format(attribute)
elif attribute in ["bus_name", "branch_name", "gen_name"]:
pattern = r"mpc\.{}\s*=\s*\{{[\n]?(?P<data>.*?)[\n]?\}};".format(attribute)
else:
msg = f"Unknown mpc attribute name of {attribute}"
raise NameError(msg)
# ["gen", "gencost", "bus", "branch", "dcline", "dclinecost"] or any keys
pattern = r"mpc\.{}\s*=\s*\[[\n]?(?P<data>.*?)[\n]?\];".format(attribute)

match = re.search(pattern, string, re.DOTALL)

Expand Down
Loading

0 comments on commit e81fe89

Please sign in to comment.