diff --git a/README.md b/README.md
index d40b4d7..37710f3 100644
--- a/README.md
+++ b/README.md
@@ -109,7 +109,7 @@ LOAD_COL = ["LD_ID", "LD_BUS", "LD_STATUS", "LD_PD", "LD_QD"]
mpc = m.loadcase('case9', verbose=False)
cf = CaseFrames(mpc)
-cf.setattr_as_df('load', mpc.load, columns_template=LOAD_COL)
+cf.set_attribute_as_df('load', mpc.load, columns_template=LOAD_COL)
```
If data already in `DataFrame`, we can use `setattr` directly as follows,
@@ -122,7 +122,7 @@ m = start_instance()
mpc = m.loadcase('case9', verbose=False)
cf = CaseFrames(mpc)
-cf.setattr('load', df_load)
+cf.set_attribute('load', df_load)
```
### Export as `xlsx`
diff --git a/data/ex_case3a.m b/data/ex_case3a.m
new file mode 100644
index 0000000..8bdbf11
--- /dev/null
+++ b/data/ex_case3a.m
@@ -0,0 +1,75 @@
+function mpc = ex_case3a
+% ex_case3a - Three bus example system.
+%
+% Please see caseformat for details on the case file format.
+
+% MOST
+% Copyright (c) 2015-2024, Power Systems Engineering Research Center (PSERC)
+% by Ray Zimmerman, PSERC Cornell
+%
+% This file is part of MOST.
+% Covered by the 3-clause BSD License (see LICENSE file for details).
+% See https://github.com/MATPOWER/most for more info.
+
+%% 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 135 1 1.05 0.95;
+ 2 2 0 0 0 0 1 1 0 135 1 1.05 0.95;
+ 3 2 0 0 0 0 1 1 0 135 1 1.05 0.95;
+];
+
+%% 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 125 0 25 -25 1 100 1 200 0 0 0 0 0 0 0 0 250 250 0 0;
+ 1 125 0 25 -25 1 100 1 200 0 0 0 0 0 0 0 0 250 250 0 0;
+ 2 200 0 50 -50 1 100 1 500 0 0 0 0 0 0 0 0 600 600 0 0;
+ 3 -450 0 0 0 1 100 1 0 -450 0 0 0 0 0 0 0 500 500 0 0;
+];
+
+%% branch data
+% fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax
+mpc.branch = [
+ 1 2 0.005 0.01 0 300 300 300 0 0 1 -360 360;
+ 1 3 0.005 0.01 0 240 240 240 0 0 1 -360 360;
+ 2 3 0.005 0.01 0 300 300 300 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 0 0 3 0.1 0 0;
+ 2 0 0 3 0.1 0 0;
+ 2 0 0 3 0.1 0 0;
+ 2 0 0 3 0 1000 0;
+];
+
+%%----- Reserve Data -----%%
+%% reserve zones, element i, j is 1 if gen j is in zone i, 0 otherwise
+mpc.reserves.zones = [
+ 1 1 1 0;
+];
+
+%% reserve requirements for each zone in MW
+mpc.reserves.req = 150;
+
+%% reserve costs in $/MW for each gen that belongs to at least 1 zone
+%% (same order as gens, but skipping any gen that does not belong to any zone)
+% mpc.reserves.cost = [ 5; 5; 21; ];
+% mpc.reserves.cost = [ 5; 5; 16.25; ];
+% mpc.reserves.cost = [ 0; 0; 11.25; ];
+mpc.reserves.cost = [ 1; 3; 5; ];
+
+%% OPTIONAL max reserve quantities for each gen that belongs to at least 1 zone
+%% (same order as gens, but skipping any gen that does not belong to any zone)
+mpc.reserves.qty = [ 100; 100; 200; ];
diff --git a/matpowercaseframes/__init__.py b/matpowercaseframes/__init__.py
index bccb7e7..0ea2eca 100644
--- a/matpowercaseframes/__init__.py
+++ b/matpowercaseframes/__init__.py
@@ -1,2 +1,15 @@
-from .core import CaseFrames # noqa: F401
-from .version import __version__ # noqa: F401
+from .core import (
+ CaseFrames,
+ DataFramesStruct,
+ ReservesFrames,
+ xGenDataTableFrames,
+)
+from .version import __version__
+
+__all__ = [
+ "CaseFrames",
+ "DataFramesStruct",
+ "ReservesFrames",
+ "xGenDataTableFrames",
+ "__version__",
+]
diff --git a/matpowercaseframes/constants.py b/matpowercaseframes/constants.py
index 9ebf361..026b433 100644
--- a/matpowercaseframes/constants.py
+++ b/matpowercaseframes/constants.py
@@ -16,8 +16,16 @@
"dclinecost",
"case",
"reserves",
+ "xgd_table",
+ "xgd",
+ "f",
+ "et",
+ "success",
)
+ATTRIBUTES_NAME = ("bus_name", "branch_name", "gen_name")
+ATTRIBUTES_INFO = ("version", "baseMVA", "f", "et", "success")
+
COLUMNS = {
"bus": [
"BUS_I",
@@ -114,10 +122,47 @@
"MU_QMAXT",
],
"reserves": {
+ "zones": [],
"req": ["PREQ"],
"cost": ["C1"],
"qty": ["PQTY"],
},
+ "xgd_table": [
+ "CommitKey",
+ "CommitSched",
+ "MinUp",
+ "MinDown",
+ "PositiveActiveReservePrice",
+ "PositiveActiveReserveQuantity",
+ "NegativeActiveReservePrice",
+ "NegativeActiveReserveQuantity",
+ "PositiveActiveDeltaPrice",
+ "NegativeActiveDeltaPrice",
+ "PositiveLoadFollowReservePrice",
+ "PositiveLoadFollowReserveQuantity",
+ "NegativeLoadFollowReservePrice",
+ "NegativeLoadFollowReserveQuantity",
+ ],
+ "xgd": { # xGenData
+ "CommitSched": [],
+ "InitialPg": [],
+ "RampWearCostCoeff": [],
+ "PositiveActiveReservePrice": [],
+ "PositiveActiveReserveQuantity": [],
+ "NegativeActiveReservePrice": [],
+ "NegativeActiveReserveQuantity": [],
+ "PositiveActiveDeltaPrice": [],
+ "NegativeActiveDeltaPrice": [],
+ "PositiveLoadFollowReservePrice": [],
+ "PositiveLoadFollowReserveQuantity": [],
+ "NegativeLoadFollowReservePrice": [],
+ "NegativeLoadFollowReserveQuantity": [],
+ "TerminalPg": [],
+ "CommitKey": [],
+ "InitialState": [],
+ "MinUp": [],
+ "MinDow": [],
+ },
"if": {
# negative 'BRANCHIDX' defines opposite direction
"map": ["IFNUM", "BRANCHIDX"],
diff --git a/matpowercaseframes/core.py b/matpowercaseframes/core.py
index 11f5517..977c54b 100644
--- a/matpowercaseframes/core.py
+++ b/matpowercaseframes/core.py
@@ -9,8 +9,9 @@
import numpy as np
import pandas as pd
-from .constants import ATTRIBUTES, COLUMNS
+from .constants import ATTRIBUTES, ATTRIBUTES_INFO, ATTRIBUTES_NAME, COLUMNS
from .reader import find_attributes, find_name, parse_file
+from .utils import get_attr, has_attr
try:
import matpower
@@ -20,39 +21,426 @@
MATPOWER_EXIST = False
-class ReservesFrames:
- """A struct-like container for reserves data, similar to CaseFrames."""
+def display(*args, **kwargs):
+ try:
+ from IPython.display import display as ipython_display
- def __init__(self, data=None):
+ return ipython_display(*args, **kwargs)
+ except ImportError:
+ print(*args, **kwargs)
+
+
+class BaseStruct:
+ """
+ Base class for struct-like containers.
+ """
+
+ def __init__(self):
"""
- Initialize ReservesFrames with optional data.
+ Initialize the base struct with an empty attributes list.
+ """
+ object.__setattr__(self, "_attributes", [])
- Args:
- data (dict, optional): Dictionary containing reserves DataFrames.
- Expected keys: 'zones', 'req', 'cost', 'qty'
+ def set_attribute(self, name, value):
"""
- self._attributes = []
+ Set attribute and track it in _attributes list.
- if data is not None:
- if isinstance(data, dict):
- for key, value in data.items():
- self.setattr(key, value)
- else:
- raise TypeError(f"ReservesFrames data must be a dict, got {type(data)}")
- def setattr(self, name, value):
- """Set attribute and track it in _attributes list."""
+ Args:
+ name (str): Attribute name.
+ value: Attribute value.
+ """
if name not in self._attributes:
self._attributes.append(name)
- self.__setattr__(name, value)
+ super().__setattr__(name, value)
@property
def attributes(self):
- """List of attributes in this ReservesFrames object."""
+ """
+ List of attributes in this struct object.
+
+
+ Returns:
+ list: List of attribute names.
+ """
return self._attributes
+ @staticmethod
+ def _infer_numpy(df):
+ """
+ Infer and convert the data types of a DataFrame to NumPy-compatible types.
+
+
+ Args:
+ df (pd.DataFrame):
+ DataFrame to be processed.
+
+
+ Returns:
+ pd.DataFrame:
+ DataFrame with updated data types.
+ """
+ df = df.convert_dtypes()
+
+ columns = df.select_dtypes(include=["integer"]).columns
+ df[columns] = df[columns].astype(int, errors="ignore")
+
+ columns = df.select_dtypes(include=["float"]).columns
+ df[columns] = df[columns].astype(float, errors="ignore")
+
+ columns = df.select_dtypes(include=["string"]).columns
+ df[columns] = df[columns].astype(str)
+
+ columns = df.select_dtypes(include=["boolean"]).columns
+ df[columns] = df[columns].astype(bool)
+ return df
+
+ def __repr__(self):
+ """
+ String representation of the struct.
+
+
+ Returns:
+ str: String representation showing class name and attributes.
+ """
+ attrs = ", ".join(self._attributes)
+ return f"{self.__class__.__name__}(attributes=[{attrs}])"
+
+
+class DataFramesStruct(BaseStruct):
+ """
+ Base class for struct-like containers with DataFrames.
+ """
+
+ def to_dict(self):
+ """
+ Convert the DataFramesStruct data into a dictionary.
+
+
+ Returns:
+ dict:
+ Dictionary with attribute names as keys and their data as values.
+ """
+ # TODO: support mpc = cf.to_dict() with reserves data
+ data = {}
+ for attribute in self._attributes:
+ value = getattr(self, attribute)
+ if isinstance(value, pd.DataFrame):
+ data[attribute] = value.values.tolist()
+ elif isinstance(value, DataFramesStruct):
+ data[attribute] = value.to_dict()
+ else:
+ data[attribute] = value
+
+ return data
+
+ def infer_numpy(self):
+ """
+ Infer and convert data types in all DataFrames to appropriate NumPy-compatible
+ types.
+ """
+ for attribute in self._attributes:
+ df = getattr(self, attribute)
+ if isinstance(df, pd.DataFrame):
+ df = self._infer_numpy(df)
+ self.set_attribute(attribute, df)
+ elif isinstance(df, DataFramesStruct):
+ df.infer_numpy()
+
+ def display(self, prefix=""):
+ data = {"INFO": {}}
+ for attribute in ATTRIBUTES_INFO:
+ if attribute in self._attributes:
+ data["INFO"][attribute] = getattr(self, attribute, None)
+ if data["INFO"]:
+ print(prefix + "info")
+ display(pd.DataFrame(data=data))
+
+ for attribute in self._attributes:
+ df = getattr(self, attribute)
+ if isinstance(df, pd.DataFrame):
+ print(prefix + f"{attribute}")
+ display(df)
+
+ if isinstance(getattr(self, attribute), DataFramesStruct):
+ df.display(prefix=prefix + f"{attribute}.")
+
+
+class DataFrameStruct(BaseStruct):
+ """
+ A struct-like and DataFrame-like container with MATPOWER compatibility.
+ """
+
+ def __init__(self, data, index, colnames):
+ """
+ Initialize DataFrameStruct with data, index, and column names.
+
+
+ Args:
+ data (dict | np.ndarray | list | str | None):
+ Data for the table.
+ index (pd.Index | None):
+ Row index for the DataFrame.
+ colnames (list | None):
+ Column names for the DataFrame.
+
+
+ Raises:
+ NotImplementedError:
+ If data is a string (file reading not yet supported).
+ TypeError:
+ If data type is not supported.
+ """
+ super().__init__()
+
+ if data is not None:
+ if isinstance(data, dict):
+ self.set_attribute(
+ "table",
+ pd.DataFrame(
+ {k: np.asarray(v).flatten() for k, v in data.items()},
+ index=index,
+ ),
+ )
+ elif isinstance(data, (np.ndarray, list)):
+ self.set_attribute(
+ "table", pd.DataFrame(data, columns=colnames, index=index)
+ )
+ elif isinstance(data, str):
+ # TODO:
+ # 1. Support read from file.
+ # 2. Differentiate between xdg_table and xdg
+ # xgdt = m.loadgenericdata(
+ # data, 'struct', {'colnames', 'data'}, 'xgd_table', cf.to_mpc()
+ # )
+ raise NotImplementedError(
+ "Reading DataTableFrames from file not yet implemented."
+ )
+ else:
+ raise TypeError(f"Unsupported data type: {type(data)}")
+
+ if index is None:
+ self.table.index = pd.RangeIndex(
+ start=1, stop=len(self.table) + 1, name="gen"
+ )
+ else:
+ self.set_attribute("table", pd.DataFrame(columns=colnames, index=index))
+
+ @property
+ def colnames(self):
+ """
+ Get column names as 2D array (MATPOWER xgdt format).
+
+
+ Returns:
+ np.ndarray:
+ 2D array of column names.
+ """
+ return np.atleast_2d(self.table.columns)
+
+ @property
+ def data(self):
+ """
+ Get data as NumPy array.
+
+
+ Returns:
+ np.ndarray:
+ Data array.
+ """
+ return self.table.to_numpy()
+
+ @property
+ def df(self):
+ """
+ Get the underlying DataFrame.
+
+
+ Returns:
+ pd.DataFrame:
+ The underlying DataFrame.
+ """
+ return self.table
+
+ def to_df(self):
+ """
+ Convert to DataFrame.
+
+
+ Returns:
+ pd.DataFrame:
+ The underlying DataFrame.
+ """
+ return self.table
+
+ def to_dict(self):
+ """
+ Convert to dictionary with column names as keys and 2D arrays as values.
+
+
+ Returns:
+ dict:
+ Dictionary with column data as 2D arrays.
+ """
+ return {
+ col: self.table[col].values.reshape(-1, 1) for col in self.table.columns
+ }
+
+ def infer_numpy(self):
+ """
+ Infer and convert data types to NumPy-compatible types.
+ """
+ df = self._infer_numpy(self.table)
+ self.table = df
+
+ def __getattr__(self, name):
+ """
+ Delegate attribute access to the underlying DataFrame.
+
+
+ Args:
+ name (str):
+ Attribute name to access.
+
+
+ Returns:
+ np.ndarray | Any:
+ Column data as 2D array or DataFrame attribute.
+ """
+ # if attribute not found in self,
+ if name in self.table.columns:
+ # try to get it from self.table[name]
+ return np.atleast_2d(self.table[name].values)
+ else:
+ # try to get it from self.table
+ return getattr(self.table, name)
+
+ def _repr_html_(self):
+ """
+ HTML representation for Jupyter notebooks.
+
+
+ Returns:
+ str:
+ HTML representation of the DataFrame.
+ """
+ return self.table._repr_html_()
+
+ def __repr__(self):
+ """
+ String representation.
+
+
+ Returns:
+ str:
+ String representation of the DataFrame.
+ """
+ return repr(self.table)
+
+ def __str__(self):
+ """
+ String representation for print().
+
+
+ Returns:
+ str:
+ String representation of the DataFrame.
+ """
+ return str(self.table)
+
+ def __getitem__(self, key):
+ """
+ Allow indexing like a DataFrame.
+
+
+ Args:
+ key:
+ Index or column key.
+
+
+ Returns:
+ pd.Series | pd.DataFrame:
+ Indexed data.
+ """
+ return self.table[key]
+
+ def __setitem__(self, key, value):
+ """
+ Allow setting values like a DataFrame.
+
+
+ Args:
+ key:
+ Column key.
+ value:
+ Values to set.
+ """
+ self.table[key] = value
+
+ def __len__(self):
+ """
+ Return number of rows.
+
+
+ Returns:
+ int:
+ Number of rows in the DataFrame.
+ """
+ return len(self.table)
+
+ def __iter__(self):
+ """
+ Iterate over column names like a DataFrame.
+
+
+ Returns:
+ Iterator:
+ Iterator over column names.
+ """
+ return iter(self.table)
+
+
+class ReservesFrames(DataFramesStruct):
+ """
+ A struct-like container for reserves data, similar to CaseFrames.
+ """
+
+ def __init__(self, data=None, allow_any_keys=False):
+ """
+ Initialize ReservesFrames with optional data.
+
+
+ Args:
+ data (dict | None):
+ Dictionary containing reserves DataFrames.
+ Expected keys: 'zones', 'req', 'cost', 'qty'.
+ allow_any_keys (bool):
+ Whether to allow any keys beyond expected keys.
+
+
+ Raises:
+ TypeError:
+ If data is not a dictionary.
+ """
+ super().__init__()
+ if data is not None:
+ if isinstance(data, dict):
+ if not allow_any_keys:
+ for key, value in data.items():
+ if key in COLUMNS["reserves"]:
+ self.set_attribute(key, value)
+ else:
+ for key, value in data.items():
+ self.set_attribute(key, value)
+ else:
+ raise TypeError(f"ReservesFrames data must be a dict, got {type(data)}")
+
+
+class CaseFrames(DataFramesStruct):
+ """
+ Main class for MATPOWER case data representation using DataFrames.
+ """
-class CaseFrames:
def __init__(
self,
data=None,
@@ -67,14 +455,13 @@ def __init__(
"""
Load data and initialize the CaseFrames class.
+
Args:
- data (str | dict | oct2py.io.Struct | np.ndarray):
+ data (str | dict | oct2py.io.Struct | np.ndarray | None, optional):
- str: File path to MATPOWER case name, .m file, or .xlsx file.
- dict: Data from a structured dictionary.
- oct2py.io.Struct: Octave's oct2py struct.
- np.ndarray: Structured NumPy array with named fields.
- update_index (bool, optional):
- Whether to update the index numbering. Defaults to True.
load_case_engine (object, optional):
External engine used to call MATPOWER `loadcase` (e.g. Octave). Defaults
to None. If None, parse data using matpowercaseframes.reader.parse_file.
@@ -87,17 +474,21 @@ def __init__(
allow_any_keys (bool, optional):
Whether to allow any keys beyond the predefined ATTRIBUTES. Defaults to
False.
+ update_index (bool, optional):
+ Whether to update the index numbering. Defaults to True.
columns_templates (dict, optional):
Custom column templates for DataFrames. Defaults to None.
reset_index (bool, optional):
Whether to reset indices to 0-based numbering. Defaults to False.
Raises:
- TypeError: If the input data format is unsupported.
- FileNotFoundError: If the specified file cannot be found.
+ TypeError:
+ If the input data format is unsupported.
+ FileNotFoundError:
+ If the specified file cannot be found.
"""
- # TODO: support read directory containing csv
# TODO: support Path object
+ super().__init__()
if columns_templates is None:
self.columns_templates = copy.deepcopy(COLUMNS)
else:
@@ -123,6 +514,29 @@ def _read_data(
suffix="",
allow_any_keys=False,
):
+ """
+ Read data from various sources and populate the CaseFrames object.
+
+
+ Args:
+ data (str | dict | np.ndarray | None, optional):
+ Data source.
+ load_case_engine (object | None, optional):
+ External engine for loading MATPOWER cases.
+ prefix (str, optional):
+ Prefix for attribute names.
+ suffix (str, optional):
+ Suffix for attribute names.
+ allow_any_keys (bool, optional):
+ Whether to allow any keys beyond ATTRIBUTES.
+
+
+ Raises:
+ FileNotFoundError:
+ If file path is invalid.
+ TypeError:
+ If data type is not supported.
+ """
if isinstance(data, str):
# TODO: support Path
# TYPE: str of path
@@ -186,7 +600,6 @@ def _read_data(
)
elif data is None:
self.name = ""
- self._attributes = []
else:
message = (
f"Not supported source type {type(data)}. Data must be a str path to"
@@ -194,24 +607,30 @@ def _read_data(
)
raise TypeError(message)
- def setattr_as_df(self, name, value, columns_template=None):
+ def set_attribute_as_df(self, name, value, columns_template=None):
"""
- Convert value to df and assign to attributes.
+ Convert value to DataFrame and assign to attributes.
+
Args:
- name (str): Attribute name.
+ name (str):
+ Attribute name.
value: Data that can be converted into DataFrame.
- columns_template: List of column names used for DataFrame column header.
+ columns_template (list | None):
+ List of column names used for DataFrame column header.
"""
df = self._get_dataframe(name, value, columns_template=columns_template)
- self.setattr(name, df)
-
- def setattr(self, name, value):
- if name not in self._attributes:
- self._attributes.append(name)
- self.__setattr__(name, value)
+ self.set_attribute(name, df)
def update_columns_templates(self, columns_templates):
+ """
+ Update the column templates dictionary.
+
+
+ Args:
+ columns_templates (dict):
+ Dictionary of column templates to update.
+ """
self.columns_templates.update(columns_templates)
@staticmethod
@@ -219,12 +638,15 @@ def _get_path(path):
"""
Determine the correct file path for the given input.
+
Args:
path (str): File path, directory path, or MATPOWER case name.
+
Returns:
str: Resolved file path or directory path.
+
Raises:
FileNotFoundError: If the file or MATPOWER case cannot be found.
"""
@@ -265,17 +687,20 @@ def _read_matpower(self, filepath, allow_any_keys=False):
"""
Read and parse a MATPOWER file.
- Old attribute is not guaranted to be replaced in re-read. This method is
+ Old attribute is not guaranteed to be replaced in re-read. This method is
intended to be used only during initialization.
Args:
- filepath (str): Path to the MATPOWER file.
+ filepath (str):
+ Path to the MATPOWER file.
+ allow_any_keys (bool):
+ Whether to allow any keys beyond ATTRIBUTES.
"""
+ # TODO: support reserves
with open(filepath) as f:
string = f.read()
self.name = find_name(string)
- self._attributes = []
for attribute in find_attributes(string):
if attribute not in ATTRIBUTES and not allow_any_keys:
@@ -284,34 +709,36 @@ def _read_matpower(self, filepath, allow_any_keys=False):
# TODO: compare with GridCal approach
list_ = parse_file(attribute, string) # list_ in nested list array
if list_ is not None:
- if attribute == "version" or attribute == "baseMVA":
+ if attribute in ATTRIBUTES_INFO:
value = list_[0][0]
- elif attribute in ["bus_name", "branch_name", "gen_name"]:
+ elif attribute in ATTRIBUTES_NAME:
value = pd.Index([name[0] for name in list_], name=attribute)
else: # bus, branch, gen, gencost, dcline, dclinecost
n_cols = max([len(l) for l in list_])
value = self._get_dataframe(attribute, list_, n_cols)
- self.setattr(attribute, value)
+ self.set_attribute(attribute, value)
def _read_oct2py_struct(self, struct, allow_any_keys=False):
"""
Read data from an Octave struct or dictionary.
+
Args:
struct (dict):
Data in structured dictionary or Octave's oct2py struct format.
+ allow_any_keys (bool):
+ Whether to allow any keys beyond ATTRIBUTES.
"""
self.name = ""
- self._attributes = []
for attribute, list_ in struct.items():
if attribute not in ATTRIBUTES and not allow_any_keys:
continue
- if attribute == "version" or attribute == "baseMVA":
+ if attribute in ATTRIBUTES_INFO:
value = list_
- elif attribute in ["bus_name", "branch_name", "gen_name"]:
+ elif attribute in ATTRIBUTES_NAME:
value = pd.Index([name[0] for name in list_], name=attribute)
elif attribute in ["reserves"]:
dfs = reserves_data_to_dataframes(list_)
@@ -321,7 +748,7 @@ def _read_oct2py_struct(self, struct, allow_any_keys=False):
n_cols = list_.shape[1]
value = self._get_dataframe(attribute, list_, n_cols)
- self.setattr(attribute, value)
+ self.set_attribute(attribute, value)
return None
@@ -329,49 +756,58 @@ def _read_numpy_struct(self, array, allow_any_keys=False):
"""
Read data from a structured NumPy array.
+
Args:
- array (np.ndarray): Structured NumPy array with named fields.
+ array (np.ndarray):
+ Structured NumPy array with named fields.
+ allow_any_keys (bool):
+ Whether to allow any keys beyond ATTRIBUTES.
"""
+ # TODO: support reserves
self.name = ""
- self._attributes = []
for attribute in array.dtype.names:
if attribute not in ATTRIBUTES and not allow_any_keys:
continue
- if attribute == "version" or attribute == "baseMVA":
+ if attribute in ATTRIBUTES_INFO:
value = array[attribute].item().item()
- elif attribute in ["bus_name", "branch_name", "gen_name"]:
+ elif attribute in ATTRIBUTES_NAME:
value = pd.Index(array[attribute].item(), name=attribute)
else: # bus, branch, gen, gencost, dcline, dclinecost
data = array[attribute].item()
n_cols = data.shape[1]
value = self._get_dataframe(attribute, data, n_cols)
- self.setattr(attribute, value)
+ self.set_attribute(attribute, value)
def _read_excel(self, filepath, prefix="", suffix="", allow_any_keys=False):
"""
Read data from an Excel file.
+
Args:
- filepath (str): File path for the Excel file.
- prefix (str): Sheet prefix for each attribute in the Excel file.
- suffix (str): Sheet suffix for each attribute in the Excel file.
+ filepath (str):
+ File path for the Excel file.
+ prefix (str):
+ Sheet prefix for each attribute in the Excel file.
+ suffix (str):
+ Sheet suffix for each attribute in the Excel file.
+ allow_any_keys (bool):
+ Whether to allow any keys beyond ATTRIBUTES.
"""
+ # TODO: support reserves
sheets = pd.read_excel(filepath, index_col=0, sheet_name=None)
- self._attributes = []
-
# info sheet to extract general metadata
info_sheet_name = f"{prefix}info{suffix}"
if info_sheet_name in sheets:
info_data = sheets[info_sheet_name]
-
+ # TODO: support other info fields, skip if not exist
value = info_data.loc["version", "INFO"].item()
- self.setattr("version", str(value))
+ self.set_attribute("version", str(value))
value = info_data.loc["baseMVA", "INFO"].item()
- self.setattr("baseMVA", value)
+ self.set_attribute("baseMVA", value)
# iterate through the remaining sheets
for attribute, sheet_data in sheets.items():
@@ -389,24 +825,30 @@ def _read_excel(self, filepath, prefix="", suffix="", allow_any_keys=False):
if attribute not in ATTRIBUTES and not allow_any_keys:
continue
- if attribute in ["bus_name", "branch_name", "gen_name"]:
+ if attribute in ATTRIBUTES_NAME:
# convert back to an index
value = pd.Index(sheet_data[attribute].values.tolist(), name=attribute)
else:
value = sheet_data
- self.setattr(attribute, value)
+ self.set_attribute(attribute, value)
def _read_csv_dir(self, dirpath, prefix="", suffix="", allow_any_keys=False):
"""
Read data from a directory of CSV files.
+
Args:
- dirpath (str): Directory path containing the CSV files.
- prefix (str): File prefix for each attribute CSV file.
- suffix (str): File suffix for each attribute CSV file.
- allow_any_keys (bool): Whether to allow any keys beyond ATTRIBUTES.
+ dirpath (str):
+ Directory path containing the CSV files.
+ prefix (str):
+ File prefix for each attribute CSV file.
+ suffix (str):
+ File suffix for each attribute CSV file.
+ allow_any_keys (bool):
+ Whether to allow any keys beyond ATTRIBUTES.
"""
+ # TODO: support reserves
# create a dictionary mapping attribute names to file paths
csv_data = {}
for csv_file in os.listdir(dirpath):
@@ -421,18 +863,16 @@ def _read_csv_dir(self, dirpath, prefix="", suffix="", allow_any_keys=False):
csv_data[attribute] = os.path.join(dirpath, csv_file)
- self._attributes = []
-
# info CSV to extract general metadata
info_name = "info"
if info_name in csv_data:
info_data = pd.read_csv(csv_data[info_name], index_col=0)
value = info_data.loc["version", "INFO"].item()
- self.setattr("version", str(value))
+ self.set_attribute("version", str(value))
value = info_data.loc["baseMVA", "INFO"].item()
- self.setattr("baseMVA", value)
+ self.set_attribute("baseMVA", value)
# iterate through the remaining CSV files
for attribute, filepath in csv_data.items():
@@ -447,26 +887,34 @@ def _read_csv_dir(self, dirpath, prefix="", suffix="", allow_any_keys=False):
# read CSV file
sheet_data = pd.read_csv(filepath, index_col=0)
- if attribute in ["bus_name", "branch_name", "gen_name"]:
+ if attribute in ATTRIBUTES_NAME:
# convert back to an index
value = pd.Index(sheet_data[attribute].values.tolist(), name=attribute)
else:
value = sheet_data
- self.setattr(attribute, value)
+ self.set_attribute(attribute, value)
def _get_dataframe(self, attribute, data, n_cols=None, columns_template=None):
"""
Create a DataFrame with proper columns from raw data.
+
Args:
- attribute (str): Name of the attribute.
- data (list | np.ndarray): Data for the attribute.
- n_cols (int): Number of columns in the data.
+ attribute (str):
+ Name of the attribute.
+ data (list | np.ndarray):
+ Data for the attribute.
+ n_cols (int | None):
+ Number of columns in the data.
+ columns_template (list | None):
+ Custom column template.
+
Returns:
pd.DataFrame: DataFrame with appropriate columns.
+
Raises:
IndexError:
If the number of columns in the data exceeds the expected number.
@@ -521,24 +969,16 @@ def _get_dataframe(self, attribute, data, n_cols=None, columns_template=None):
return pd.DataFrame(data, columns=columns)
- @property
- def attributes(self):
+ def _update_index(self, allow_any_keys=False):
"""
- List of attributes that have been parsed from the input data.
+ Update the index of the bus, branch, and generator tables based on naming.
- Returns:
- list: List of attribute names.
- """
- return self._attributes
- def _update_index(self, allow_any_keys=False):
- """
- Update the index of the bus, branch, and generator tables based on naming. If
- naming is not available, index start from 1 to N.
+ Args:
+ allow_any_keys (bool):
+ Whether to update index for any keys beyond standard attributes.
"""
- for attribute, attribute_name in zip(
- ["bus", "branch", "gen"], ["bus_name", "branch_name", "gen_name"]
- ):
+ for attribute, attribute_name in zip(["bus", "branch", "gen"], ATTRIBUTES_NAME):
attribute_data = getattr(self, attribute)
try:
attribute_name_data = getattr(self, attribute_name)
@@ -576,6 +1016,9 @@ def _update_index(self, allow_any_keys=False):
self._update_index_any()
def _update_index_any(self):
+ """
+ Update index for any additional attributes that are DataFrames or Series.
+ """
for attribute in self._attributes:
if attribute in ["bus", "branch", "gen", "gencost", "reserves"]:
continue
@@ -592,65 +1035,26 @@ def _update_index_any(self):
inplace=True,
)
- def infer_numpy(self):
- """
- Infer and convert data types in all DataFrames to appropriate NumPy-compatible
- types.
- """
- for attribute in self._attributes:
- df = getattr(self, attribute)
- if isinstance(df, pd.DataFrame):
- df = self._infer_numpy(df)
- setattr(self, attribute, df)
-
- @staticmethod
- def _infer_numpy(df):
- """
- Infer and convert the data types of a DataFrame to NumPy-compatible types.
-
- Args:
- df (pd.DataFrame): DataFrame to be processed.
-
- Returns:
- pd.DataFrame: DataFrame with updated data types.
- """
- df = df.convert_dtypes()
-
- columns = df.select_dtypes(include=["integer"]).columns
- df[columns] = df[columns].astype(int, errors="ignore")
-
- columns = df.select_dtypes(include=["float"]).columns
- df[columns] = df[columns].astype(float, errors="ignore")
-
- columns = df.select_dtypes(include=["string"]).columns
- df[columns] = df[columns].astype(str)
-
- columns = df.select_dtypes(include=["boolean"]).columns
- df[columns] = df[columns].astype(bool)
- return df
-
def reset_index(self):
"""
Reset indices and remap bus-related indices to 0-based values.
- This method ensures that:
- 1. All DataFrames in the case have their row indices reset.
- 2. Bus numbers (BUS_I) are renumbered from 0 to n-1.
- 3. References to buses in branch (F_BUS, T_BUS) and gen (GEN_BUS) tables
- are updated consistently with the new numbering.
Notes:
- - This method requires `infer_numpy` to be called beforehand,
- as mapping does not support float-backed integers.
+ - This method requires `infer_numpy` to be called beforehand, as mapping
+ does not support float-backed integers.
- Support for additional tables (e.g., dcline) is not yet implemented.
-
- Returns:
- None: The CaseFrames object is modified in place.
"""
+ # store original gen index mapping before resetting, used in reserves
+ gen_map = {v: k for k, v in enumerate(self.gen.index)}
+
for attribute in self._attributes:
df = getattr(self, attribute)
if isinstance(df, pd.DataFrame):
+ idx_name = df.index.name
df.reset_index(drop=True, inplace=True)
+ df.index.name = idx_name
+
bus_map = {v: k for k, v in enumerate(self.bus["BUS_I"])}
self.bus["BUS_I"] = self.bus.index
self.branch[["F_BUS", "T_BUS"]] = self.branch[["F_BUS", "T_BUS"]].replace(
@@ -658,11 +1062,36 @@ def reset_index(self):
)
self.gen["GEN_BUS"] = self.gen["GEN_BUS"].replace(bus_map)
- # TODO:
- # Since mpc.reserves.zones columns use cf.gen.index, don't forget to update
- # the columns of mpc.reserves.zones if exists.
+ if hasattr(self, "reserves"):
+ reserves = self.reserves
+ for attribute in ["zones", "req"]:
+ df = getattr(reserves, attribute)
+ if isinstance(df, pd.DataFrame):
+ idx_name = df.index.name
+ df.reset_index(drop=True, inplace=True)
+ df.index.name = idx_name
+ self.reserves.zones.columns = self.gen.index
+
+ if hasattr(self.reserves, "cost"):
+ self.reserves.cost = self.reserves.cost.rename(index=gen_map)
+ self.reserves.cost.index.name = "gen"
+ if hasattr(self.reserves, "qty"):
+ self.reserves.qty = self.reserves.qty.rename(index=gen_map)
+ self.reserves.qty.index.name = "gen"
def add_schema_case(self, F=None):
+ """
+ Add case attribute to follow caseformat/schema.
+
+
+ Args:
+ F (float | None):
+ Optional frequency value.
+
+
+ Warnings:
+ This might be deprecated in the future if MATPOWER defines this later.
+ """
# add case to follow casefromat/schema
# !WARNING:
# this might be deprecated in the future if matpower define this later
@@ -670,14 +1099,15 @@ def add_schema_case(self, F=None):
version = getattr(self, "version", None)
baseMVA = getattr(self, "baseMVA", None)
if F:
- self.setattr_as_df("case", [[case_name, version, baseMVA, F]])
+ self.set_attribute_as_df("case", [[case_name, version, baseMVA, F]])
else:
- self.setattr_as_df("case", [[case_name, version, baseMVA]])
+ self.set_attribute_as_df("case", [[case_name, version, baseMVA]])
def to_pu(self):
"""
Create a new CaseFrame object with data in p.u. and rad.
+
Returns:
CaseFrames: CaseFrames object with data in p.u. and rad.
"""
@@ -742,6 +1172,7 @@ def to_excel(self, path, prefix="", suffix=""):
"""
Save the CaseFrames data into a single Excel file.
+
Args:
path (str): File path for the Excel file.
prefix (str): Sheet prefix for each attribute for the Excel file.
@@ -758,18 +1189,15 @@ def to_excel(self, path, prefix="", suffix=""):
# convert to xlsx
with pd.ExcelWriter(path) as writer:
- pd.DataFrame(
- data={
- "INFO": {
- "version": getattr(self, "version", None),
- "baseMVA": getattr(self, "baseMVA", None),
- }
- }
- ).to_excel(writer, sheet_name=f"{prefix}info{suffix}")
+ data = {"INFO": {}}
+ for attribute in ATTRIBUTES_INFO:
+ if attribute in self._attributes:
+ data["INFO"][attribute] = getattr(self, attribute, None)
+ pd.DataFrame(data=data).to_excel(writer, sheet_name=f"{prefix}info{suffix}")
for attribute in self._attributes:
- if attribute == "version" or attribute == "baseMVA":
+ if attribute in ATTRIBUTES_INFO:
continue
- elif attribute in ["bus_name", "branch_name", "gen_name"]:
+ elif attribute in ATTRIBUTES_NAME:
pd.DataFrame(data={attribute: getattr(self, attribute)}).to_excel(
writer, sheet_name=f"{prefix}{attribute}{suffix}"
)
@@ -782,27 +1210,30 @@ def to_csv(self, path, prefix="", suffix="", attributes=None):
"""
Save the CaseFrames data into multiple CSV files.
+
Args:
- path (str): Directory path where the CSV files will be saved.
- prefix (str): Sheet prefix for each attribute for the CSV files.
- suffix (str): Sheet suffix for each attribute for the CSV files.
+ path (str):
+ Directory path where the CSV files will be saved.
+ prefix (str):
+ File prefix for each attribute CSV file.
+ suffix (str):
+ File suffix for each attribute CSV file.
+ attributes (list | None):
+ Specific attributes to save (unused parameter).
"""
# make dir
os.makedirs(path, exist_ok=True)
- pd.DataFrame(
- data={
- "INFO": {
- "version": getattr(self, "version", None),
- "baseMVA": getattr(self, "baseMVA", None),
- }
- }
- ).to_csv(os.path.join(path, f"{prefix}info{suffix}.csv"))
+ data = {"INFO": {}}
+ for attribute in ATTRIBUTES_INFO:
+ if attribute in self._attributes:
+ data["INFO"][attribute] = getattr(self, attribute, None)
+ pd.DataFrame(data=data).to_csv(os.path.join(path, f"{prefix}info{suffix}.csv"))
for attribute in self._attributes:
- if attribute == "version" or attribute == "baseMVA":
+ if attribute in ATTRIBUTES_INFO:
continue
- elif attribute in ["bus_name", "branch_name", "gen_name"]:
+ elif attribute in ATTRIBUTES_NAME:
pd.DataFrame(data={attribute: getattr(self, attribute)}).to_csv(
os.path.join(path, f"{prefix}{attribute}{suffix}.csv")
)
@@ -815,31 +1246,32 @@ def to_dict(self):
"""
Convert the CaseFrames data into a dictionary.
- The value of the data will be in str, numeric, and list.
Returns:
dict: Dictionary with attribute names as keys and their data as values.
"""
+ # default version and baseMVA to None
data = {
- "version": getattr(self, "version", None),
- "baseMVA": getattr(self, "baseMVA", None),
+ "version": None,
+ "baseMVA": None,
}
for attribute in self._attributes:
- if attribute == "version" or attribute == "baseMVA":
- data[attribute] = getattr(self, attribute)
- elif attribute in ["bus_name", "branch_name", "gen_name"]:
+ value = getattr(self, attribute)
+ if attribute in ATTRIBUTES_NAME:
# NOTE: must be in 2D Cell or 2D np.array
- data[attribute] = np.atleast_2d(getattr(self, attribute).values).T
+ data[attribute] = np.atleast_2d(value.values).T
+ elif isinstance(value, pd.DataFrame):
+ data[attribute] = value.values.tolist()
+ elif isinstance(value, DataFramesStruct):
+ data[attribute] = value.to_dict()
else:
- data[attribute] = getattr(self, attribute).values.tolist()
+ data[attribute] = value
return data
def to_mpc(self):
"""
- Convert the CaseFrames data into a format compatible with MATPOWER (as a
- dictionary).
+ Convert the CaseFrames data into a format compatible with MATPOWER.
- The value of the data will be in str, numeric, and list.
Returns:
dict: MATPOWER-compatible dictionary with data.
@@ -850,10 +1282,22 @@ def to_schema(self, path, prefix="", suffix=""):
"""
Convert to format compatible with caseformat/schema.
- This method also mutate the CaseFormat by adding "case" as a new
- attribute if not exists.
- See more:
+ Args:
+ path (str):
+ Directory path where the schema files will be saved.
+ prefix (str):
+ File prefix for each attribute.
+ suffix (str):
+ File suffix for each attribute.
+
+
+ Notes:
+ This method also mutates the CaseFrames by adding "case" as a new attribute
+ if not exists.
+
+
+ See Also:
https://github.com/caseformat/schema
"""
@@ -866,36 +1310,127 @@ def reserves_data_to_dataframes(reserves):
"""
Convert all mpc.reserves struct data to DataFrames.
+
Args:
- reserves: Octave struct or dictionary of mpc.reserves object from MATPOWER
+ reserves (dict | oct2py.io.Struct):
+ mpc.reserves object from MATPOWER.
+
Returns:
- Dictionary containing:
+ dict: Dictionary containing:
- 'zones': Reserve zones DataFrame
- 'req': Reserve requirements DataFrame
- 'cost': Reserve costs DataFrame (if exists)
- 'qty': Reserve quantities DataFrame (if exists)
"""
dfs = {}
- n_zones, n_gens = reserves.zones.shape
+
+ zones_data = get_attr(reserves, "zones")
+ n_zones, n_gens = np.array(zones_data).shape
dfs["zones"] = pd.DataFrame(
- reserves.zones,
+ zones_data,
index=pd.RangeIndex(start=1, stop=n_zones + 1, name="zone"),
columns=pd.RangeIndex(start=1, stop=n_gens + 1, name="gen"),
)
+
zone_sum = dfs["zones"].sum(axis=0)
idx_gen_with_reserves = zone_sum[zone_sum > 0].index
+
dfs["req"] = pd.DataFrame(
- reserves.req,
+ get_attr(reserves, "req"),
index=pd.RangeIndex(start=1, stop=n_zones + 1, name="zone"),
columns=["PREQ"],
)
- if hasattr(reserves, "cost"):
+
+ if has_attr(reserves, "cost"):
dfs["cost"] = pd.DataFrame(
- reserves.cost, index=idx_gen_with_reserves, columns=["C1"]
+ get_attr(reserves, "cost"), index=idx_gen_with_reserves, columns=["C1"]
)
- if hasattr(reserves, "qty"):
+
+ if has_attr(reserves, "qty"):
dfs["qty"] = pd.DataFrame(
- reserves.qty, index=idx_gen_with_reserves, columns=["PQTY"]
+ get_attr(reserves, "qty"), index=idx_gen_with_reserves, columns=["PQTY"]
)
+
return dfs
+
+
+class xGenDataTableFrames(DataFrameStruct):
+ """
+ A struct-like and DataFrame-like container for xGenData with MATPOWER compatibility.
+
+
+ Supports standard DataFrame operations.
+ """
+
+ def __init__(self, data=None, colnames=None, index=None):
+ """
+ Initialize xGenDataTableFrames with optional data.
+
+
+ Args:
+ data (np.ndarray | list | dict | None):
+ Data for xGenDataTableFrames.
+ colnames (list | None):
+ Column names.
+ index (pd.Index | None):
+ Row index.
+ """
+ if data is not None and colnames is None:
+ if isinstance(data, dict):
+ colnames = list(data.keys())
+ else:
+ n_col = np.atleast_2d(data).shape[1]
+ colnames = COLUMNS["xgd_table"][:n_col]
+ elif colnames is not None:
+ colnames = np.asarray(colnames).flatten()
+ else:
+ colnames = []
+ # if not allow_any_keys:
+ # # TODO: remove columns in data that are not in COLUMNS
+ # colnames = [
+ # col
+ # for col in colnames
+ # if col in COLUMNS["xgd_table"]
+ # ]
+
+ super().__init__(data=data, index=index, colnames=colnames)
+
+ def to_dict(self):
+ """
+ Convert to combined dict with both xgd and xgd_table formats.
+
+
+ Returns:
+ dict: Combined dictionary with:
+ - Column names as keys with 2D array values (xgd format)
+ - 'colnames' and 'data' keys (xgd_table format)
+ """
+ xgd_dict = self.to_xgd()
+ xgdt_dict = self.to_xgdt()
+ return {**xgd_dict, **xgdt_dict}
+
+ def to_xgdt(self):
+ """
+ Convert to xgd_table format (for loadgenericdata).
+
+
+ Returns:
+ dict:
+ Dictionary with 'colnames' (2D array) and 'data' (2D array).
+ """
+ return {
+ "colnames": self.colnames,
+ "data": self.data,
+ }
+
+ def to_xgd(self):
+ """
+ Convert to xgd struct format (for loadxgendata/MOST functions).
+
+
+ Returns:
+ dict:
+ Dictionary where keys are column names and values are 2D arrays (n, 1).
+ """
+ return super().to_dict()
diff --git a/matpowercaseframes/idx/ct.py b/matpowercaseframes/idx/ct.py
new file mode 100644
index 0000000..5df65a1
--- /dev/null
+++ b/matpowercaseframes/idx/ct.py
@@ -0,0 +1,111 @@
+# idx_ct - Defines constants for named column indices to changes table
+# ::
+#
+# [CT_LABEL, CT_PROB, CT_TABLE, CT_TBUS, CT_TGEN, CT_TBRCH, CT_TAREABUS, ...
+# CT_TAREAGEN, CT_TAREABRCH, CT_ROW, CT_COL, CT_CHGTYPE, CT_REP, ...
+# CT_REL, CT_ADD, CT_NEWVAL, CT_TLOAD, CT_TAREALOAD, CT_LOAD_ALL_PQ, ...
+# CT_LOAD_FIX_PQ, CT_LOAD_DIS_PQ, CT_LOAD_ALL_P, CT_LOAD_FIX_P, ...
+# CT_LOAD_DIS_P, CT_TGENCOST, CT_TAREAGENCOST, CT_MODCOST_F, ...
+# CT_MODCOST_X] = idx_ct;
+#
+# CT_LABEL: column of changes table where the change set label is stored
+#
+# CT_PROB: column of changes table where the probability of the
+# change set is stored
+#
+# CT_TABLE: column of the changes table where the type of system data
+# table to be modified is stored;
+# type CT_TBUS indicates bus table
+# type CT_TGEN indicates gen table
+# type CT_TBRCH indicates branch table
+# type CT_TLOAD indicates a load modification (bus and/or gen tables)
+# type CT_TAREABUS indicates area-wide change in bus table
+# type CT_TAREAGEN indicates area-wide change in generator table
+# type CT_TAREABRCH indicates area-wide change in branch table
+# type CT_TAREALOAD indicates area-wide change in load
+# (bus and/or gen tables)
+#
+# CT_ROW: column of changes table where the row number in the data
+# table to be modified is stored. A value of "0" in this column
+# has the special meaning "apply to all rows". For an area-wide
+# type of change, the area number is stored here instead.
+#
+# CT_COL: column of changes table where the number of the column in
+# the data table to be modified is stored
+# For CT_TLOAD and CT_TAREALOAD, the value entered in this column
+# is one of the following codes (or its negative), rather than
+# a column index:
+# type CT_LOAD_ALL_PQ modify all loads, real & reactive
+# type CT_LOAD_FIX_PQ modify only fixed loads, real & reactive
+# type CT_LOAD_DIS_PQ modify only dispatchable loads, real & reactive
+# type CT_LOAD_ALL_P modify all loads, real only
+# type CT_LOAD_FIX_P modify only fixed loads, real only
+# type CT_LOAD_DIS_P modify only dispatchable loads, real only
+# If the negative of one of these codes is used, then any affected
+# dispatchable loads will have their costs scaled as well.
+# For CT_TGENCOST and CT_TAREAGENCOST, in addition to an actual
+# column index, this value can also take one of the following
+# codes to indicate a scaling (CT_REL change type) or shifting
+# (CT_ADD change type) of the specified cost functions:
+# type CT_MODCOST_F scales or shifts the cost function vertically
+# type CT_MODCOST_X scales or shifts the cost function horizontally
+# See MODCOST.
+#
+# CT_CHGTYPE: column of changes table where the type of change to
+# be made is stored:
+# type CT_REP replaces old value by value in CT_NEWVAL column
+# type CT_REL multiplies old value by factor in CT_NEWVAL column
+# type CT_ADD adds value in CT_NEWVAL column to old value
+#
+# See also apply_changes, modcost.
+
+# MATPOWER
+# Copyright (c) 2000-2024, Power Systems Engineering Research Center (PSERC)
+# by Carlos E. Murillo-Sanchez, PSERC Cornell & Universidad Nacional de Colombia
+# and Ray Zimmerman, PSERC Cornell
+#
+# This file is part of MATPOWER.
+# Covered by the 3-clause BSD License (see LICENSE file for details).
+# See https://matpower.org for more info.
+
+# column labels for changes table
+CT_LABEL = 0 # change set label
+CT_PROB = 1 # change set probability
+CT_TABLE = 2 # type of table to be modified (see possible values below)
+CT_ROW = 3 # number of the row to be modified (0 means all rows)
+CT_COL = 4 # number of the column to be modified
+# (for some values in CT_TABLE column, this can be a
+# special code instead of an actual column index)
+CT_CHGTYPE = 5 # type of parameter modification to be made
+# (see possible values below)
+CT_NEWVAL = 6 # quantity to use for replacement value, scale factor
+# or shift amount
+
+# named values for CT_TABLE entry
+CT_TBUS = 1 # bus table
+CT_TGEN = 2 # gen table
+CT_TBRCH = 3 # branch table
+CT_TAREABUS = 4 # area-wide change in bus table
+CT_TAREAGEN = 5 # area-wide change in gen table
+CT_TAREABRCH = 6 # area-wide change in branch table
+CT_TLOAD = 7 # single bus load change
+CT_TAREALOAD = 8 # area-wide bus load change
+CT_TGENCOST = 9 # gencost table
+CT_TAREAGENCOST = 10 # area-wide change in gencost table
+
+# named values for CT_CHGTYPE entry
+CT_REP = 1 # replace old value with new one in column CT_NEWVAL
+CT_REL = 2 # multiply old value by factor in column CT_NEWVAL
+CT_ADD = 3 # add value in column CT_NEWVAL to old value
+
+# codes for CT_COL entry when CT_TABLE entry is CT_TLOAD or CT_TAREALOAD
+CT_LOAD_ALL_PQ = 1 # all loads, real and reactive
+CT_LOAD_FIX_PQ = 2 # only fixed loads, real and reactive
+CT_LOAD_DIS_PQ = 3 # only dispatchable loads, real and reactive
+CT_LOAD_ALL_P = 4 # all loads, real only
+CT_LOAD_FIX_P = 5 # only fixed loads, real only
+CT_LOAD_DIS_P = 6 # only dispatchable loads, real only
+
+# codes for CT_COL entry when CT_TABLE entry is CT_TGENCOST or CT_TAREAGENCOST
+CT_MODCOST_F = -1 # scale or shift cost function vertically
+CT_MODCOST_X = -2 # scale or shift cost function horizontally
diff --git a/matpowercaseframes/idx/dcline.py b/matpowercaseframes/idx/dcline.py
new file mode 100644
index 0000000..20d0df9
--- /dev/null
+++ b/matpowercaseframes/idx/dcline.py
@@ -0,0 +1,67 @@
+# The index, name and meaning of each column of the dcline matrix is given
+# below:
+#
+# columns 0-16 must be included in input matrix (in case file)
+# 0 F_BUS f, "from" bus number
+# 1 T_BUS t, "to" bus number
+# 2 BR_STATUS initial dcline status, 1 - in service, 0 - out of service
+# 3 PF MW flow at "from" bus ("from" -> "to")
+# 4 PT MW flow at "to" bus ("from" -> "to")
+# 5 QF MVAr injection at "from" bus ("from" -> "to")
+# 6 QT MVAr injection at "to" bus ("from" -> "to")
+# 7 VF voltage setpoint at "from" bus (p.u.)
+# 8 VT voltage setpoint at "to" bus (p.u.)
+# 9 PMIN lower limit on PF (MW flow at "from" end)
+# 10 PMAX upper limit on PF (MW flow at "from" end)
+# 11 QMINF lower limit on MVAr injection at "from" bus
+# 12 QMAXF upper limit on MVAr injection at "from" bus
+# 13 QMINT lower limit on MVAr injection at "to" bus
+# 14 QMAXT upper limit on MVAr injection at "to" bus
+# 15 LOSS0 constant term of linear loss function (MW)
+# 16 LOSS1 linear term of linear loss function (MW/MW)
+# (loss = LOSS0 + LOSS1 * PF)
+#
+# columns 17-22 are added to matrix after OPF solution
+# they are typically not present in the input matrix
+# (assume OPF objective function has units, u)
+# 17 MU_PMIN Kuhn-Tucker multiplier on lower flow lim at "from" bus (u/MW)
+# 18 MU_PMAX Kuhn-Tucker multiplier on upper flow lim at "from" bus (u/MW)
+# 19 MU_QMINF Kuhn-Tucker multiplier on lower VAr lim at "from" bus (u/MVAr)
+# 20 MU_QMAXF Kuhn-Tucker multiplier on upper VAr lim at "from" bus (u/MVAr)
+# 21 MU_QMINT Kuhn-Tucker multiplier on lower VAr lim at "to" bus (u/MVAr)
+# 22 MU_QMAXT Kuhn-Tucker multiplier on upper VAr lim at "to" bus (u/MVAr)
+#
+# See also toggle_dcline.
+
+# MATPOWER
+# Copyright (c) 2011-2024, Power Systems Engineering Research Center (PSERC)
+# by Ray Zimmerman, PSERC Cornell
+#
+# This file is part of MATPOWER.
+# Covered by the 3-clause BSD License (see LICENSE file for details).
+# See https://matpower.org for more info.
+
+# define the indices
+F_BUS = 0 # f, "from" bus number
+T_BUS = 1 # t, "to" bus number
+BR_STATUS = 2 # initial dcline status, 1 - in service, 0 - out of service
+PF = 3 # MW flow at "from" bus ("from" -> "to")
+PT = 4 # MW flow at "to" bus ("from" -> "to")
+QF = 5 # MVAr injection at "from" bus ("from" -> "to")
+QT = 6 # MVAr injection at "to" bus ("from" -> "to")
+VF = 7 # voltage setpoint at "from" bus (p.u.)
+VT = 8 # voltage setpoint at "to" bus (p.u.)
+PMIN = 9 # lower limit on PF (MW flow at "from" end)
+PMAX = 10 # upper limit on PF (MW flow at "from" end)
+QMINF = 11 # lower limit on MVAr injection at "from" bus
+QMAXF = 12 # upper limit on MVAr injection at "from" bus
+QMINT = 13 # lower limit on MVAr injection at "to" bus
+QMAXT = 14 # upper limit on MVAr injection at "to" bus
+LOSS0 = 15 # constant term of linear loss function (MW)
+LOSS1 = 16 # linear term of linear loss function (MW)
+MU_PMIN = 17 # Kuhn-Tucker multiplier on lower flow lim at "from" bus (u/MW)
+MU_PMAX = 18 # Kuhn-Tucker multiplier on upper flow lim at "from" bus (u/MW)
+MU_QMINF = 19 # Kuhn-Tucker multiplier on lower VAr lim at "from" bus (u/MVAr)
+MU_QMAXF = 20 # Kuhn-Tucker multiplier on upper VAr lim at "from" bus (u/MVAr)
+MU_QMINT = 21 # Kuhn-Tucker multiplier on lower VAr lim at "to" bus (u/MVAr)
+MU_QMAXT = 22 # Kuhn-Tucker multiplier on upper VAr lim at "to" bus (u/MVAr)
diff --git a/matpowercaseframes/idx/profile.py b/matpowercaseframes/idx/profile.py
new file mode 100644
index 0000000..6cbe197
--- /dev/null
+++ b/matpowercaseframes/idx/profile.py
@@ -0,0 +1,187 @@
+# idx_profile - Defines constants used by Profiles.
+# ::
+#
+# [PR_REP, PR_REL, PR_ADD, PR_TCONT, PR_TYPES, PR_TMPCD,...
+# PR_TXGD, PR_TCTD, PR_TSTGD, PR_CHGTYPES] = idx_profile;
+#
+# Indicates and defines numeric and string id's for the types of profiles
+# that can be created to modify input data for MOST across time and
+# scenarios. Some types may require that a table is further specified,
+# i.e., a specific table of data whose content is to be modified by the
+# profile. Rows (rows) and columns (col) fields of each profile further
+# specify which entries of the data in question is to be modified by the
+# profile. The change type field (chgtype) indicates how the change is
+# applied, and finally, the values field (values) contains the new values
+# to be used.
+#
+# type: string field of each profile struct where the id of the type of
+# the profile is stored. Possible values are:
+# string 'mpcData' indicates changes on fields of mpc
+# (e.g. bus or gen tables)
+# string 'xGenData' indicates changes on xgd fields
+# (e.g. offers or commitment vars)
+# string 'ContingencyData' indicates changes on the set of
+# contingencies to be applied
+# string 'StorageData' indicates changes on the storage struct
+#
+# table: scalar or string field of each profile struct where the id/name
+# of the table/field that needs to be modified is stored.
+# Possible values are:
+# - if type is 'mpcData' you may modify the following tables
+# (specified with a scalar):
+# scalar CT_TBUS change in bus table
+# scalar CT_TGEN change in gen table
+# scalar CT_TBRCH change in branch table
+# scalar CT_TAREABUS area-wide change in bus table
+# scalar CT_TAREAGEN area-wide change in gen table
+# scalar CT_TAREABRCH area-wide change in branch table
+# scalar CT_TLOAD load modification (bus and/or gen tables)
+# scalar CT_TAREALOAD area-wide change in load (bus/gen tables)
+# scalar CT_TGENCOST change in gencost table
+# scalar CT_TAREAGENCOST area-wide change in gencost table
+# - if type is 'xGenData' you may modify the following fields:
+# string 'CommitSched'
+# string 'InitialPg'
+# string 'RampWearCostCoeff'
+# string 'PositiveActiveReservePrice'
+# string 'PositiveActiveReserveQuantity'
+# string 'NegativeActiveReservePrice'
+# string 'NegativeActiveReserveQuantity'
+# string 'PositiveActiveDeltaPrice'
+# string 'NegativeActiveDeltaPrice'
+# string 'PositiveLoadFollowReservePrice'
+# string 'PositiveLoadFollowReserveQuantity'
+# string 'NegativeLoadFollowReservePrice'
+# string 'NegativeLoadFollowReserveQuantity'
+# - if type is 'ContingencyData' you may modify the following
+# tables:
+# scalar PR_TCONT indicates a modification of the subset of
+# contingencies (of the master table of contingencies) to be
+# applied at each time period and/or scenario.
+# - if type is 'StorageData' you may modify the following fields:
+# string 'MinStorageLevel'
+# string 'MaxStorageLevel'
+# string 'OutEff'
+# string 'InEff'
+# string 'LossFactor'
+# string 'rho'
+#
+# rows: numeric vector field of each profile struct where the row
+# numbers (1st-dim) of the array to be modified are stored. Row
+# numbers usually represent specifically which gens, branches,
+# buses, contingencies (by the labels), or area will be affected
+# by the modification across time or across scenarios. A value of
+# "0" in this field has the special meaning of "apply to all
+# rows". For an area-wide type of change, the area number is
+# stored here instead.
+#
+# col: scalar field of each profile struct where the id of the
+# parameter to be modified is stored. This id may indicate a
+# column number or some other parameter depending
+# on the type and the table/field. Possible values are:
+# - if type is 'mpcData' the parameters you may modify depend on
+# the table chosen, see IDX_CT on CT_COL for details.
+# - if type is 'xGenData', 'ContingencyData', or 'StorageData',
+# then col is completely ignored.
+#
+# chgtype: scalar field of each profile struct where the id of the type of
+# change to be made is stored. Possible values are:
+# scalar PR_REP replaces old values by new 'values'
+# scalar PR_REL multiplies old value by factors in 'vales'
+# scalar PR_ADD adds entries in 'values' field to old value
+#
+# values: numeric array field of each profile struct where the new
+# values/factors, i.e., the profile itself, is stored. This array
+# must have 3 dimensions in a pre-defined order: [nt nj_max n]
+# (i) dimension corresponding to time periods
+# (ii) dimension corresponding to scenarios
+# (iii) dimension corresponding to elements indicated by 'rows'
+# A singleton dimension in 'values' not matching with nt==1,
+# nj_max==1 or length(profile.rows)==1 is interpreted as "apply
+# to all" whenever the parameter being modified allows such an
+# expansion. These "expansions" occur within loadmd().
+
+# MOST
+# Copyright (c) 2013-2024, Power Systems Engineering Research Center (PSERC)
+# by Daniel Munoz-Alvarez and Ray Zimmerman, PSERC Cornell
+#
+# This file is part of MOST.
+# Covered by the 3-clause BSD License (see LICENSE file for details).
+# See https://github.com/MATPOWER/most for more info.
+
+from .ct import (
+ CT_TAREABRCH,
+ CT_TAREABUS,
+ CT_TAREAGEN,
+ CT_TAREAGENCOST,
+ CT_TAREALOAD,
+ CT_TBRCH,
+ CT_TBUS,
+ CT_TGEN,
+ CT_TGENCOST,
+ CT_TLOAD,
+)
+
+# types of profiles
+PR_TYPES = [
+ "mpcData", # changes to mpc
+ "xGenData", # changes to xgd
+ "ContingencyData", # changes to ct_subset
+ "StorageData", # changes to Storage
+]
+
+# labels for modifiable tables
+# - tables for type 'mpcData'
+PR_TMPCD = [
+ CT_TBUS,
+ CT_TGEN,
+ CT_TBRCH,
+ CT_TAREABUS,
+ CT_TAREAGEN,
+ CT_TAREABRCH,
+ CT_TLOAD,
+ CT_TAREALOAD,
+ CT_TGENCOST,
+ CT_TAREAGENCOST,
+]
+
+# - tables for type xGenData
+PR_TXGD = [
+ "CommitSched",
+ "InitialPg",
+ "RampWearCostCoeff",
+ "PositiveActiveReservePrice",
+ "PositiveActiveReserveQuantity",
+ "NegativeActiveReservePrice",
+ "NegativeActiveReserveQuantity",
+ "PositiveActiveDeltaPrice",
+ "NegativeActiveDeltaPrice",
+ "PositiveLoadFollowReservePrice",
+ "PositiveLoadFollowReserveQuantity",
+ "NegativeLoadFollowReservePrice",
+ "NegativeLoadFollowReserveQuantity",
+ "CommitKey",
+ "InitialState",
+ "MinUp",
+ "MinDown",
+]
+
+# - tables for type 'ContingencyData'
+PR_TCONT = 1 # ct_subset of contingencies
+PR_TCTD = [PR_TCONT]
+
+# - tables for type storage
+PR_TSTGD = [
+ "MinStorageLevel",
+ "MaxStorageLevel",
+ "OutEff",
+ "InEff",
+ "LossFactor",
+ "rho",
+]
+
+# named values for chgtype entry
+PR_REP = 1 # replace old values with new ones in field 'values'
+PR_REL = 2 # multiply old values by factors in field 'values'
+PR_ADD = 3 # add value in field 'values' to old values
+PR_CHGTYPES = [PR_REP, PR_REL, PR_ADD]
diff --git a/matpowercaseframes/testing.py b/matpowercaseframes/testing.py
new file mode 100644
index 0000000..7e9612b
--- /dev/null
+++ b/matpowercaseframes/testing.py
@@ -0,0 +1,88 @@
+import pandas as pd
+from pandas.testing import assert_frame_equal, assert_index_equal
+
+from .core import DataFramesStruct
+
+
+def assert_attributes_equal(struct1, struct2):
+ if set(struct1.attributes) != set(struct2.attributes):
+ missing_in_struct2 = set(struct1.attributes) - set(struct2.attributes)
+ missing_in_struct1 = set(struct2.attributes) - set(struct1.attributes)
+ msg = "CaseFrames have different attributes:\n"
+ if missing_in_struct2:
+ msg += f" Missing in struct2: {missing_in_struct2}\n"
+ if missing_in_struct1:
+ msg += f" Missing in struct1: {missing_in_struct1}\n"
+ raise AssertionError(msg)
+
+
+def assert_frames_struct_equal(struct1, struct2):
+ """
+ Assert that two DataFramesStruct objects are equal.
+
+ Recursively compares nested DataFramesStruct objects.
+
+ Args:
+ struct1: First DataFramesStruct object
+ struct2: Second DataFramesStruct object
+
+ Raises:
+ AssertionError: If the DataFramesStruct objects are not equal
+ """
+ assert_attributes_equal(struct1, struct2)
+
+ for attribute in struct1.attributes:
+ try:
+ value1 = getattr(struct1, attribute)
+ value2 = getattr(struct2, attribute)
+
+ if isinstance(value1, pd.DataFrame):
+ try:
+ assert_frame_equal(value1, value2)
+ except AssertionError as e:
+ print(f" ✗ DataFrame '{attribute}' does not match:")
+ print(f" struct1.{attribute} shape: {value1.shape}")
+ print(f" struct2.{attribute} shape: {value2.shape}")
+ print(f" struct1.{attribute} dtypes:\n{value1.dtypes}")
+ print(f" struct2.{attribute} dtypes:\n{value2.dtypes}")
+ print(f" struct1.{attribute}:\n{value1}")
+ print(f" struct2.{attribute}:\n{value2}")
+ raise AssertionError(
+ f"DataFrame '{attribute}' mismatch:\n{str(e)}"
+ ) from e
+
+ elif isinstance(value1, pd.Index):
+ try:
+ assert_index_equal(value1, value2)
+ except AssertionError as e:
+ print(f" ✗ Index '{attribute}' does not match:")
+ print(f" struct1.{attribute}: {value1.tolist()}")
+ print(f" struct2.{attribute}: {value2.tolist()}")
+ raise AssertionError(
+ f"Index '{attribute}' mismatch:\n{str(e)}"
+ ) from e
+
+ elif isinstance(value1, DataFramesStruct):
+ # recursive for DataFramesStruct objects, for example ReservesFrames
+ try:
+ assert_frames_struct_equal(value1, value2)
+ except AssertionError as e:
+ raise AssertionError(
+ f"DataFramesStruct '{attribute}' mismatch:\n{str(e)}"
+ ) from e
+
+ else:
+ # for scalar values (version, baseMVA, etc.)
+ try:
+ assert value1 == value2
+ except (AssertionError, ValueError) as e:
+ print(f" ✗ Value '{attribute}' does not match:")
+ print(f" struct1.{attribute}: {value1} (type: {type(value1)})")
+ print(f" struct2.{attribute}: {value2} (type: {type(value2)})")
+ raise AssertionError(
+ f"Value '{attribute}' mismatch: {value1} != {value2}"
+ ) from e
+
+ except Exception as e:
+ print(f" ✗ Error comparing attribute '{attribute}': {e}")
+ raise
diff --git a/matpowercaseframes/utils.py b/matpowercaseframes/utils.py
index ddff57b..7a09228 100644
--- a/matpowercaseframes/utils.py
+++ b/matpowercaseframes/utils.py
@@ -9,3 +9,19 @@ def int_else_float_except_string(s):
return f
except ValueError:
return s
+
+
+def get_attr(obj, key):
+ """Get attribute from dict or struct."""
+ if isinstance(obj, dict):
+ return obj.get(key)
+ else:
+ return getattr(obj, key, None)
+
+
+def has_attr(obj, key):
+ """Check if attribute exists in dict or struct."""
+ if isinstance(obj, dict):
+ return key in obj
+ else:
+ return hasattr(obj, key)
diff --git a/notebooks/load_most_ex_case3a.ipynb b/notebooks/load_most_ex_case3a.ipynb
index 126ee8e..f04c7c5 100644
--- a/notebooks/load_most_ex_case3a.ipynb
+++ b/notebooks/load_most_ex_case3a.ipynb
@@ -3,6 +3,17 @@
{
"cell_type": "code",
"execution_count": 1,
+ "id": "aa3d7f0f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%load_ext autoreload\n",
+ "%autoreload 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
"id": "eae79aa2",
"metadata": {},
"outputs": [],
@@ -30,7 +41,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": 3,
"id": "5bc86ee4",
"metadata": {},
"outputs": [
@@ -53,22 +64,21 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 4,
"id": "3116b86b",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
- "import pandas as pd\n",
"from matpower import path_matpower, start_instance\n",
"\n",
- "from matpowercaseframes import CaseFrames"
+ "from matpowercaseframes import CaseFrames, xGenDataTableFrames"
]
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 5,
"id": "10fd2d91",
"metadata": {},
"outputs": [],
@@ -78,7 +88,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 6,
"id": "7e230eb0",
"metadata": {},
"outputs": [],
@@ -86,79 +96,91 @@
"m = start_instance()"
]
},
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "6d93dd06",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "{'version': '2',\n",
- " 'baseMVA': 100.0,\n",
- " 'bus': array([[ 1. , 3. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95],\n",
- " [ 2. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95],\n",
- " [ 3. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95]]),\n",
- " 'gen': array([[ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
- " 250., 0., 0.],\n",
- " [ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
- " 250., 0., 0.],\n",
- " [ 2., 200., 0., 50., -50., 1., 100., 1., 500.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 600.,\n",
- " 600., 0., 0.],\n",
- " [ 3., -450., 0., 0., 0., 1., 100., 1., 0.,\n",
- " -450., 0., 0., 0., 0., 0., 0., 0., 500.,\n",
- " 500., 0., 0.]]),\n",
- " 'branch': array([[ 1.0e+00, 2.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
- " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02],\n",
- " [ 1.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 2.4e+02,\n",
- " 2.4e+02, 2.4e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02],\n",
- " [ 2.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
- " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02]]),\n",
- " 'gencost': array([[2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 0.e+00, 1.e+03, 0.e+00]]),\n",
- " 'reserves': {'zones': array([[1., 1., 1., 0.]]),\n",
- " 'req': 150.0,\n",
- " 'cost': array([[1.],\n",
- " [3.],\n",
- " [5.]]),\n",
- " 'qty': array([[100.],\n",
- " [100.],\n",
- " [200.]])}}"
- ]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# load single most example case\n",
- "mpc = m.loadcase(os.path.join(path_most_ex_cases, \"ex_case3a.m\"))\n",
- "mpc"
- ]
- },
{
"cell_type": "code",
"execution_count": 7,
- "id": "b6bbbce4",
+ "id": "33e60f85",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "dict_keys(['version', 'baseMVA', 'bus', 'gen', 'branch', 'gencost', 'reserves'])"
+ "{'v': 25.0,\n",
+ " 'model': 'DC',\n",
+ " 'pf': {'alg': 'NR',\n",
+ " 'current_balance': 0.0,\n",
+ " 'v_cartesian': 0.0,\n",
+ " 'tol': 1e-08,\n",
+ " 'nr': {'max_it': 10.0, 'lin_solver': ''},\n",
+ " 'fd': {'max_it': 30.0},\n",
+ " 'gs': {'max_it': 1000.0},\n",
+ " 'zg': {'max_it': 1000.0},\n",
+ " 'radial': {'max_it': 20.0, 'vcorr': 0.0},\n",
+ " 'enforce_q_lims': 0.0},\n",
+ " 'cpf': {'parameterization': 3.0,\n",
+ " 'stop_at': 'NOSE',\n",
+ " 'enforce_p_lims': 0.0,\n",
+ " 'enforce_q_lims': 0.0,\n",
+ " 'enforce_v_lims': 0.0,\n",
+ " 'enforce_flow_lims': 0.0,\n",
+ " 'step': 0.05,\n",
+ " 'step_min': 0.0001,\n",
+ " 'step_max': 0.2,\n",
+ " 'adapt_step': 0.0,\n",
+ " 'adapt_step_damping': 0.7,\n",
+ " 'adapt_step_tol': 0.001,\n",
+ " 'target_lam_tol': 1e-05,\n",
+ " 'nose_tol': 1e-05,\n",
+ " 'p_lims_tol': 0.01,\n",
+ " 'q_lims_tol': 0.01,\n",
+ " 'v_lims_tol': 0.0001,\n",
+ " 'flow_lims_tol': 0.01,\n",
+ " 'plot': {'level': 0.0, 'bus': []},\n",
+ " 'user_callback': ''},\n",
+ " 'opf': {'ac': {'solver': 'DEFAULT'},\n",
+ " 'dc': {'solver': 'MIPS'},\n",
+ " 'current_balance': 0.0,\n",
+ " 'v_cartesian': 0.0,\n",
+ " 'violation': 5e-06,\n",
+ " 'use_vg': 0.0,\n",
+ " 'flow_lim': 'S',\n",
+ " 'ignore_angle_lim': 0.0,\n",
+ " 'softlims': {'default': 1.0},\n",
+ " 'start': 0.0,\n",
+ " 'return_raw_der': 0.0},\n",
+ " 'verbose': 1.0,\n",
+ " 'out': {'all': -1.0,\n",
+ " 'sys_sum': 1.0,\n",
+ " 'area_sum': 0.0,\n",
+ " 'bus': 1.0,\n",
+ " 'branch': 1.0,\n",
+ " 'gen': 1.0,\n",
+ " 'lim': {'all': -1.0, 'v': 1.0, 'line': 1.0, 'pg': 1.0, 'qg': 1.0},\n",
+ " 'force': 0.0,\n",
+ " 'suppress_detail': -1.0},\n",
+ " 'mips': {'step_control': 0.0,\n",
+ " 'linsolver': '',\n",
+ " 'feastol': 0.0,\n",
+ " 'gradtol': 1e-06,\n",
+ " 'comptol': 1e-06,\n",
+ " 'costtol': 1e-06,\n",
+ " 'max_it': 150.0,\n",
+ " 'sc': {'red_it': 20.0}},\n",
+ " 'exp': {'use_legacy_core': 0.0, 'sys_wide_zip_loads': {'pw': [], 'qw': []}},\n",
+ " 'glpk': {'opts': [], 'opt_fname': ''},\n",
+ " 'most': {'build_model': 1.0,\n",
+ " 'solve_model': 1.0,\n",
+ " 'resolve_new_cost': 0.0,\n",
+ " 'dc_model': 0.0,\n",
+ " 'fixed_res': -1.0,\n",
+ " 'q_coordination': 0.0,\n",
+ " 'security_constraints': -1.0,\n",
+ " 'storage': {'terminal_target': -1.0, 'cyclic': 0.0},\n",
+ " 'uc': {'run': -1.0, 'cyclic': 0.0},\n",
+ " 'alpha': 0.0,\n",
+ " 'solver': 'MIPS',\n",
+ " 'skip_prices': 0.0,\n",
+ " 'price_stage_warn_tol': 1e-07}}"
]
},
"execution_count": 7,
@@ -167,128 +189,90 @@
}
],
"source": [
- "mpc.keys()"
+ "verbose = 1\n",
+ "mpopt = m.mpoption(\"verbose\", verbose)\n",
+ "mpopt = m.mpoption(mpopt, \"out.gen\", 1)\n",
+ "mpopt = m.mpoption(mpopt, \"model\", \"DC\")\n",
+ "mpopt = m.mpoption(mpopt, \"opf.dc.solver\", \"MIPS\")\n",
+ "mpopt = m.mpoption(mpopt, \"most.solver\", mpopt.opf.dc.solver)\n",
+ "mpopt = m.mpoption(mpopt, \"most.dc_model\", 0) # use model with no network\n",
+ "mpopt"
]
},
{
- "cell_type": "code",
- "execution_count": 8,
- "id": "ea66211a",
+ "cell_type": "markdown",
+ "id": "e25c8a6f",
"metadata": {},
- "outputs": [],
"source": [
- "from matpowercaseframes.constants import COLUMNS"
+ "## Example 1 - Economic Dispatch"
]
},
{
"cell_type": "code",
- "execution_count": 9,
- "id": "a911808f",
+ "execution_count": 8,
+ "id": "87bc9631",
"metadata": {},
"outputs": [
{
- "data": {
- "text/plain": [
- "dict_keys(['bus', 'gen', 'branch', 'dcline', 'if', 'gencost', 'dclinecost', 'bus_name', 'branch_name', 'gen_name', 'case'])"
- ]
- },
- "execution_count": 9,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "COLUMNS.keys()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "id": "f63470e2",
- "metadata": {},
- "outputs": [
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "info\n"
+ ]
+ },
{
"data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " INFO | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | version | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ " | baseMVA | \n",
+ " 100.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
"text/plain": [
- "{'baseMVA', 'reserves', 'version'}"
+ " INFO\n",
+ "version 2\n",
+ "baseMVA 100.0"
]
},
- "execution_count": 10,
"metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "mpc_keys = set(mpc.keys())\n",
- "columns_keys = set(COLUMNS.keys())\n",
- "missing_keys = mpc_keys - columns_keys\n",
- "missing_keys"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "id": "8577fe89",
- "metadata": {},
- "outputs": [
+ "output_type": "display_data"
+ },
{
- "data": {
- "text/plain": [
- "{'zones': array([[1., 1., 1., 0.]]),\n",
- " 'req': 150.0,\n",
- " 'cost': array([[1.],\n",
- " [3.],\n",
- " [5.]]),\n",
- " 'qty': array([[100.],\n",
- " [100.],\n",
- " [200.]])}"
- ]
- },
- "execution_count": 11,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "mpc.reserves"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f46abaea",
- "metadata": {},
- "source": [
- "Reserve Data\n",
- "\n",
- "reserve zones, element i, j is 1 if gen j is in zone i, 0 otherwise.\n",
- "dimension (zone, gen)\n",
- "\n",
- "```octave\n",
- "mpc.reserves.zones = [\n",
- "\t1\t1\t1\t0;\n",
- "];\n",
- "```\n",
- "\n",
- "Mapped to `DataFrame` with index follows zone index (from 1), and columns follows\n",
- "generator index (cf.gen.index)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "id": "aaf701d3",
- "metadata": {},
- "outputs": [],
- "source": [
- "n_zones, n_gens = mpc.reserves.zones.shape"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "id": "370d5e7c",
- "metadata": {},
- "outputs": [
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "bus\n"
+ ]
+ },
{
"data": {
"text/html": [
@@ -309,14 +293,32 @@
"\n",
" \n",
" \n",
- " | gen | \n",
- " 1 | \n",
- " 2 | \n",
- " 3 | \n",
- " 4 | \n",
+ " | \n",
+ " BUS_I | \n",
+ " BUS_TYPE | \n",
+ " PD | \n",
+ " QD | \n",
+ " GS | \n",
+ " BS | \n",
+ " BUS_AREA | \n",
+ " VM | \n",
+ " VA | \n",
+ " BASE_KV | \n",
+ " ZONE | \n",
+ " VMAX | \n",
+ " VMIN | \n",
"
\n",
" \n",
- " | zone | \n",
+ " bus | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
" | \n",
" | \n",
" | \n",
@@ -327,60 +329,79 @@
"
\n",
" | 1 | \n",
" 1.0 | \n",
+ " 3.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 135.0 | \n",
+ " 1.0 | \n",
+ " 1.05 | \n",
+ " 0.95 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 2.0 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 135.0 | \n",
+ " 1.0 | \n",
+ " 1.05 | \n",
+ " 0.95 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 3.0 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
" 1.0 | \n",
" 1.0 | \n",
" 0.0 | \n",
+ " 135.0 | \n",
+ " 1.0 | \n",
+ " 1.05 | \n",
+ " 0.95 | \n",
"
\n",
" \n",
"
\n",
""
],
"text/plain": [
- "gen 1 2 3 4\n",
- "zone \n",
- "1 1.0 1.0 1.0 0.0"
+ " BUS_I BUS_TYPE PD QD GS BS BUS_AREA VM VA BASE_KV ZONE \\\n",
+ "bus \n",
+ "1 1.0 3.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 135.0 1.0 \n",
+ "2 2.0 2.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 135.0 1.0 \n",
+ "3 3.0 2.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 135.0 1.0 \n",
+ "\n",
+ " VMAX VMIN \n",
+ "bus \n",
+ "1 1.05 0.95 \n",
+ "2 1.05 0.95 \n",
+ "3 1.05 0.95 "
]
},
- "execution_count": 13,
"metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_reserves_zone = pd.DataFrame(\n",
- " mpc.reserves.zones,\n",
- " index=pd.RangeIndex(start=1, stop=n_zones + 1, name=\"zone\"),\n",
- " columns=pd.RangeIndex(start=1, stop=n_gens + 1, name=\"gen\"),\n",
- ")\n",
- "df_reserves_zone"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ded7b892",
- "metadata": {},
- "source": [
- "reserve requirements for each zone in MW\n",
- "\n",
- "```octave\n",
- "mpc.reserves.req = [60; 20];\n",
- "```\n",
- "\n",
- "or\n",
- "\n",
- "```octave\n",
- "mpc.reserves.req = 150;\n",
- "```\n",
- "\n",
- "Mapped to `DataFrame` with index follows zone index (from 1), and single column 'PREQ'."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "id": "b817c54f",
- "metadata": {},
- "outputs": [
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "gen\n"
+ ]
+ },
{
"data": {
"text/html": [
@@ -402,84 +423,190 @@
" \n",
" \n",
" | \n",
- " PREQ | \n",
+ " GEN_BUS | \n",
+ " PG | \n",
+ " QG | \n",
+ " QMAX | \n",
+ " QMIN | \n",
+ " VG | \n",
+ " MBASE | \n",
+ " GEN_STATUS | \n",
+ " PMAX | \n",
+ " PMIN | \n",
+ " ... | \n",
+ " PC2 | \n",
+ " QC1MIN | \n",
+ " QC1MAX | \n",
+ " QC2MIN | \n",
+ " QC2MAX | \n",
+ " RAMP_AGC | \n",
+ " RAMP_10 | \n",
+ " RAMP_30 | \n",
+ " RAMP_Q | \n",
+ " APF | \n",
"
\n",
" \n",
- " | zone | \n",
+ " gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
" | 1 | \n",
- " 150.0 | \n",
- "
\n",
- " \n",
- "\n",
- ""
- ],
- "text/plain": [
- " PREQ\n",
- "zone \n",
- "1 150.0"
- ]
- },
- "execution_count": 14,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_reserves_req = pd.DataFrame(\n",
- " mpc.reserves.req,\n",
- " index=pd.RangeIndex(start=1, stop=n_zones + 1, name=\"zone\"),\n",
- " columns=[\"PREQ\"],\n",
- ")\n",
- "df_reserves_req"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "82107817",
- "metadata": {},
- "source": [
- "reserve costs in $/MW for each gen that belongs to at least 1 zone. (same order as gens,\n",
- "but skipping any gen that does not belong to any zone)\n",
- "\n",
- "```octave\n",
- "mpc.reserves.cost = [\t1.9;\t2;\t3;\t4;\t5;\t5.5\t];\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "id": "fb21239e",
- "metadata": {},
- "outputs": [
- {
- "data": {
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " \n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 2.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 50.0 | \n",
+ " -50.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 600.0 | \n",
+ " 600.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 3.0 | \n",
+ " -450.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " -450.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 500.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ "\n",
+ "4 rows × 21 columns
\n",
+ ""
+ ],
"text/plain": [
- "RangeIndex(start=1, stop=4, step=1, name='gen')"
+ " GEN_BUS PG QG QMAX QMIN VG MBASE GEN_STATUS PMAX PMIN \\\n",
+ "gen \n",
+ "1 1.0 125.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "2 1.0 125.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "3 2.0 200.0 0.0 50.0 -50.0 1.0 100.0 1.0 500.0 0.0 \n",
+ "4 3.0 -450.0 0.0 0.0 0.0 1.0 100.0 1.0 0.0 -450.0 \n",
+ "\n",
+ " ... PC2 QC1MIN QC1MAX QC2MIN QC2MAX RAMP_AGC RAMP_10 RAMP_30 \\\n",
+ "gen ... \n",
+ "1 ... 0.0 0.0 0.0 0.0 0.0 0.0 250.0 250.0 \n",
+ "2 ... 0.0 0.0 0.0 0.0 0.0 0.0 250.0 250.0 \n",
+ "3 ... 0.0 0.0 0.0 0.0 0.0 0.0 600.0 600.0 \n",
+ "4 ... 0.0 0.0 0.0 0.0 0.0 0.0 500.0 500.0 \n",
+ "\n",
+ " RAMP_Q APF \n",
+ "gen \n",
+ "1 0.0 0.0 \n",
+ "2 0.0 0.0 \n",
+ "3 0.0 0.0 \n",
+ "4 0.0 0.0 \n",
+ "\n",
+ "[4 rows x 21 columns]"
]
},
- "execution_count": 15,
"metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_reserves_zone_sum = df_reserves_zone.sum(axis=0)\n",
- "idx_gen_with_reserves = df_reserves_zone_sum[df_reserves_zone_sum > 0].index\n",
- "idx_gen_with_reserves"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "id": "ebe8a6de",
- "metadata": {},
- "outputs": [
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "branch\n"
+ ]
+ },
{
"data": {
"text/html": [
@@ -501,10 +628,34 @@
" \n",
" \n",
" | \n",
- " C1 | \n",
+ " F_BUS | \n",
+ " T_BUS | \n",
+ " BR_R | \n",
+ " BR_X | \n",
+ " BR_B | \n",
+ " RATE_A | \n",
+ " RATE_B | \n",
+ " RATE_C | \n",
+ " TAP | \n",
+ " SHIFT | \n",
+ " BR_STATUS | \n",
+ " ANGMIN | \n",
+ " ANGMAX | \n",
"
\n",
" \n",
- " | gen | \n",
+ " branch | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
" | \n",
"
\n",
" \n",
@@ -512,58 +663,79 @@
" \n",
" | 1 | \n",
" 1.0 | \n",
+ " 2.0 | \n",
+ " 0.005 | \n",
+ " 0.01 | \n",
+ " 0.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " -360.0 | \n",
+ " 360.0 | \n",
"
\n",
" \n",
" | 2 | \n",
+ " 1.0 | \n",
" 3.0 | \n",
+ " 0.005 | \n",
+ " 0.01 | \n",
+ " 0.0 | \n",
+ " 240.0 | \n",
+ " 240.0 | \n",
+ " 240.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " -360.0 | \n",
+ " 360.0 | \n",
"
\n",
" \n",
" | 3 | \n",
- " 5.0 | \n",
+ " 2.0 | \n",
+ " 3.0 | \n",
+ " 0.005 | \n",
+ " 0.01 | \n",
+ " 0.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " -360.0 | \n",
+ " 360.0 | \n",
"
\n",
" \n",
"\n",
""
],
"text/plain": [
- " C1\n",
- "gen \n",
- "1 1.0\n",
- "2 3.0\n",
- "3 5.0"
+ " F_BUS T_BUS BR_R BR_X BR_B RATE_A RATE_B RATE_C TAP SHIFT \\\n",
+ "branch \n",
+ "1 1.0 2.0 0.005 0.01 0.0 300.0 300.0 300.0 0.0 0.0 \n",
+ "2 1.0 3.0 0.005 0.01 0.0 240.0 240.0 240.0 0.0 0.0 \n",
+ "3 2.0 3.0 0.005 0.01 0.0 300.0 300.0 300.0 0.0 0.0 \n",
+ "\n",
+ " BR_STATUS ANGMIN ANGMAX \n",
+ "branch \n",
+ "1 1.0 -360.0 360.0 \n",
+ "2 1.0 -360.0 360.0 \n",
+ "3 1.0 -360.0 360.0 "
]
},
- "execution_count": 16,
"metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_reserves_cost = pd.DataFrame(\n",
- " mpc.reserves.cost, index=idx_gen_with_reserves, columns=[\"C1\"]\n",
- ")\n",
- "df_reserves_cost"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a2b1e889",
- "metadata": {},
- "source": [
- "max reserve quantities for each gen that belongs to at least 1 zone. (same order as\n",
- "gens, but skipping any gen that does not belong to any zone)\n",
- "\n",
- "```octave\n",
- "mpc.reserves.qty = [\t25;\t25;\t25;\t25;\t25;\t25\t];\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "id": "4e486ee1",
- "metadata": {},
- "outputs": [
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "gencost\n"
+ ]
+ },
{
"data": {
"text/html": [
@@ -585,108 +757,87 @@
" \n",
" \n",
" | \n",
- " PQTY | \n",
+ " MODEL | \n",
+ " STARTUP | \n",
+ " SHUTDOWN | \n",
+ " NCOST | \n",
+ " C2 | \n",
+ " C1 | \n",
+ " C0 | \n",
"
\n",
" \n",
" | gen | \n",
" | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
"
\n",
" \n",
" \n",
" \n",
" | 1 | \n",
- " 100.0 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
" | 2 | \n",
- " 100.0 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
" | 3 | \n",
- " 200.0 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.0 | \n",
+ " 1000.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
"\n",
""
],
"text/plain": [
- " PQTY\n",
- "gen \n",
- "1 100.0\n",
- "2 100.0\n",
- "3 200.0"
+ " MODEL STARTUP SHUTDOWN NCOST C2 C1 C0\n",
+ "gen \n",
+ "1 2.0 0.0 0.0 3.0 0.1 0.0 0.0\n",
+ "2 2.0 0.0 0.0 3.0 0.1 0.0 0.0\n",
+ "3 2.0 0.0 0.0 3.0 0.1 0.0 0.0\n",
+ "4 2.0 0.0 0.0 3.0 0.0 1000.0 0.0"
]
},
- "execution_count": 17,
"metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_reserves_qty = pd.DataFrame(\n",
- " mpc.reserves.qty, index=idx_gen_with_reserves, columns=[\"PQTY\"]\n",
- ")\n",
- "df_reserves_qty"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5389d132",
- "metadata": {},
- "outputs": [],
- "source": [
- "def reserves_data_to_dataframes(reserves):\n",
- " \"\"\"\n",
- " Convert all mpc.reserves data to DataFrames.\n",
- "\n",
- " Args:\n",
- " reserves: mpc.reserves object from MATPOWER\n",
- "\n",
- " Returns:\n",
- " Dictionary containing:\n",
- " - 'zones': Reserve zones DataFrame\n",
- " - 'req': Reserve requirements DataFrame\n",
- " - 'cost': Reserve costs DataFrame (if exists)\n",
- " - 'qty': Reserve quantities DataFrame (if exists)\n",
- " \"\"\"\n",
- " dfs = {}\n",
- " n_zones, n_gens = reserves.zones.shape\n",
- " dfs[\"zones\"] = pd.DataFrame(\n",
- " reserves.zones,\n",
- " index=pd.RangeIndex(start=1, stop=n_zones + 1, name=\"zone\"),\n",
- " columns=pd.RangeIndex(start=1, stop=n_gens + 1, name=\"gen\"),\n",
- " )\n",
- " zone_sum = dfs[\"zones\"].sum(axis=0)\n",
- " idx_gen_with_reserves = zone_sum[zone_sum > 0].index\n",
- " dfs[\"req\"] = pd.DataFrame(\n",
- " reserves.req,\n",
- " index=pd.RangeIndex(start=1, stop=n_zones + 1, name=\"zone\"),\n",
- " columns=[\"PREQ\"],\n",
- " )\n",
- " if hasattr(reserves, \"cost\"):\n",
- " dfs[\"cost\"] = pd.DataFrame(\n",
- " reserves.cost, index=idx_gen_with_reserves, columns=[\"C1\"]\n",
- " )\n",
- " if hasattr(reserves, \"qty\"):\n",
- " dfs[\"qty\"] = pd.DataFrame(\n",
- " reserves.qty, index=idx_gen_with_reserves, columns=[\"PQTY\"]\n",
- " )\n",
- "\n",
- " return dfs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a686e0ab",
- "metadata": {},
- "outputs": [
+ "output_type": "display_data"
+ },
{
"name": "stdout",
"output_type": "stream",
"text": [
- "zones\n"
+ "reserves.zones\n"
]
},
{
@@ -714,8 +865,6 @@
" 2 | \n",
" 3 | \n",
" 4 | \n",
- " 5 | \n",
- " 6 | \n",
" \n",
" \n",
" | zone | \n",
@@ -723,8 +872,6 @@
" | \n",
" | \n",
" | \n",
- " | \n",
- " | \n",
"
\n",
" \n",
" \n",
@@ -734,17 +881,15 @@
" 1.0 | \n",
" 1.0 | \n",
" 0.0 | \n",
- " 0.0 | \n",
- " 0.0 | \n",
" \n",
" \n",
"\n",
""
],
"text/plain": [
- "gen 1 2 3 4 5 6\n",
- "zone \n",
- "1 1.0 1.0 1.0 0.0 0.0 0.0"
+ "gen 1 2 3 4\n",
+ "zone \n",
+ "1 1.0 1.0 1.0 0.0"
]
},
"metadata": {},
@@ -754,7 +899,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "req\n"
+ "reserves.req\n"
]
},
{
@@ -807,7 +952,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "cost\n"
+ "reserves.cost\n"
]
},
{
@@ -870,7 +1015,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "qty\n"
+ "reserves.qty\n"
]
},
{
@@ -931,23 +1076,808 @@
}
],
"source": [
- "dfs = reserves_data_to_dataframes(mpc.reserves)\n",
- "for key, value in dfs.items():\n",
- " print(key)\n",
- " display(value)"
+ "cf = CaseFrames(os.path.join(path_most_ex_cases, \"ex_case3a.m\"), load_case_engine=m)\n",
+ "cf.display()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "d2baa348",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'Delta_T': 1.0,\n",
+ " 'Storage': {'UnitIdx': [],\n",
+ " 'ExpectedTerminalStorageAim': [],\n",
+ " 'ExpectedTerminalStorageMin': [],\n",
+ " 'ExpectedTerminalStorageMax': [],\n",
+ " 'rho': [],\n",
+ " 'TerminalChargingPrice0': [],\n",
+ " 'TerminalDischargingPrice0': [],\n",
+ " 'TerminalChargingPriceK': [],\n",
+ " 'TerminalDischargingPriceK': []},\n",
+ " 'UC': {'CommitKey': [],\n",
+ " 'CommitSched': array([[1.],\n",
+ " [1.],\n",
+ " [1.],\n",
+ " [1.]])},\n",
+ " 'idx': {'nt': 1.0},\n",
+ " 'tstep': {'TransMat': 1.0, 'OpCondSched': {'tab': []}},\n",
+ " 'mpc': {'version': '2',\n",
+ " 'baseMVA': 100.0,\n",
+ " 'bus': array([[ 1. , 3. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95],\n",
+ " [ 2. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95],\n",
+ " [ 3. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95]]),\n",
+ " 'gen': array([[ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
+ " 250., 0., 0.],\n",
+ " [ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
+ " 250., 0., 0.],\n",
+ " [ 2., 200., 0., 50., -50., 1., 100., 1., 500.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 600.,\n",
+ " 600., 0., 0.],\n",
+ " [ 3., -450., 0., 0., 0., 1., 100., 1., 0.,\n",
+ " -450., 0., 0., 0., 0., 0., 0., 0., 500.,\n",
+ " 500., 0., 0.]]),\n",
+ " 'branch': array([[ 1.0e+00, 2.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
+ " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02],\n",
+ " [ 1.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 2.4e+02,\n",
+ " 2.4e+02, 2.4e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02],\n",
+ " [ 2.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
+ " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02]]),\n",
+ " 'gencost': array([[2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 0.e+00, 1.e+03, 0.e+00]]),\n",
+ " 'reserves': {'zones': array([[1., 1., 1., 0.]]),\n",
+ " 'req': 150.0,\n",
+ " 'cost': array([[1.],\n",
+ " [3.],\n",
+ " [5.]]),\n",
+ " 'qty': array([[100.],\n",
+ " [100.],\n",
+ " [200.]])}},\n",
+ " 'InitialPg': array([[ 125.],\n",
+ " [ 125.],\n",
+ " [ 200.],\n",
+ " [-450.]]),\n",
+ " 'RampWearCostCoeff': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'offer': {'PositiveActiveReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'PositiveActiveReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]]),\n",
+ " 'NegativeActiveReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'NegativeActiveReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]]),\n",
+ " 'PositiveActiveDeltaPrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'NegativeActiveDeltaPrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'PositiveLoadFollowReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'PositiveLoadFollowReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]]),\n",
+ " 'NegativeLoadFollowReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'NegativeLoadFollowReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]])},\n",
+ " 'cont': {'contab': []}}"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# TODO: convert mdi into DataFrame\n",
+ "# mdi: MOST data structure input\n",
+ "mdi = m.loadmd(cf.to_mpc())\n",
+ "mdi"
]
},
{
"cell_type": "code",
"execution_count": 10,
- "id": "87bc9631",
+ "id": "f10d8d18",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "=============================================================================\n",
+ " MATPOWER Optimal Scheduling Tool -- MOST Version 1.3.1\n",
+ " A multiperiod stochastic secure OPF with unit commitment\n",
+ " ----- Built on MATPOWER -----\n",
+ " by Carlos E. Murillo-Sanchez, Universidad Nacional de Colombia--Manizales\n",
+ " and Ray D. Zimmerman, Cornell University\n",
+ " (c) 2010-2025 Power Systems Engineering Research Center (PSERC)\n",
+ "=============================================================================\n",
+ "- Building indexing structures.\n",
+ "- Building expected storage-tracking mechanism.\n",
+ "- Building constraint submatrices.\n",
+ " - Building load balance constraints.\n",
+ " - Building CCV constraints for piecewise-linear costs.\n",
+ " - Building contingency reserve constraints.\n",
+ " - Building ramping transitions and reserve constraints.\n",
+ "- Building cost structures.\n",
+ "- Assembling full set of constraints.\n",
+ "- Assembling full set of variable bounds.\n",
+ "- Assembling full set of costs.\n",
+ "- Calling QP solver.\n",
+ "\n",
+ "============================================================================\n",
+ "\n",
+ "MATPOWER Interior Point Solver -- MIPS, Version 1.5.2, 12-Jul-2025\n",
+ " (using built-in linear solver)\n",
+ "Converged!\n",
+ "\n",
+ "============================================================================\n",
+ "- MOST: QP solved successfully.\n",
+ "- Post-processing results.\n",
+ "- MOST: Done.\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n",
+ "warning: load: classdef element has been converted to a struct\n",
+ "warning: called from\n",
+ " _pyeval at line 28 column 9\n",
+ "\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Delta_T': 1.0,\n",
+ " 'Storage': {'UnitIdx': [],\n",
+ " 'ExpectedTerminalStorageAim': [],\n",
+ " 'ExpectedTerminalStorageMin': [],\n",
+ " 'ExpectedTerminalStorageMax': [],\n",
+ " 'rho': [],\n",
+ " 'TerminalChargingPrice0': [],\n",
+ " 'TerminalDischargingPrice0': [],\n",
+ " 'TerminalChargingPriceK': [],\n",
+ " 'TerminalDischargingPriceK': [],\n",
+ " 'ForceCyclicStorage': 0.0,\n",
+ " 'ForceExpectedTerminalStorage': 0.0,\n",
+ " 'ExpectedStorageState': array([], shape=(0, 1), dtype=float64)},\n",
+ " 'UC': {'CommitKey': [],\n",
+ " 'CommitSched': array([[1.],\n",
+ " [1.],\n",
+ " [1.],\n",
+ " [1.]]),\n",
+ " 'run': 0.0,\n",
+ " 'CyclicCommitment': 0.0},\n",
+ " 'idx': {'nt': 1.0,\n",
+ " 'ng': 4.0,\n",
+ " 'ns': 0.0,\n",
+ " 'nj': 1.0,\n",
+ " 'ntds': 0.0,\n",
+ " 'nzds': 0.0,\n",
+ " 'nyds': 0.0,\n",
+ " 'nb': 3.0,\n",
+ " 'ny': 0.0,\n",
+ " 'nc': 0.0,\n",
+ " 'nf_total': 1.0,\n",
+ " 'nb_total': 3.0,\n",
+ " 'ns_total': 0.0,\n",
+ " 'ntramp': 0.0,\n",
+ " 'nvars': 24.0},\n",
+ " 'tstep': {'TransMat': 1.0, 'OpCondSched': {'tab': []}, 'TransMask': 1.0},\n",
+ " 'mpc': {'version': '2',\n",
+ " 'baseMVA': 100.0,\n",
+ " 'bus': array([[ 1. , 3. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95, 0. , 0. , 0. ,\n",
+ " 0. ],\n",
+ " [ 2. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95, 0. , 0. , 0. ,\n",
+ " 0. ],\n",
+ " [ 3. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95, 0. , 0. , 0. ,\n",
+ " 0. ]]),\n",
+ " 'gen': array([[ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
+ " 250., 0., 0., 0., 0., 0., 0.],\n",
+ " [ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
+ " 250., 0., 0., 0., 0., 0., 0.],\n",
+ " [ 2., 200., 0., 50., -50., 1., 100., 1., 500.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 600.,\n",
+ " 600., 0., 0., 0., 0., 0., 0.],\n",
+ " [ 3., -450., 0., 0., 0., 1., 100., 1., 0.,\n",
+ " -450., 0., 0., 0., 0., 0., 0., 0., 500.,\n",
+ " 500., 0., 0., 0., 0., 0., 0.]]),\n",
+ " 'branch': array([[ 1.0e+00, 2.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
+ " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00,\n",
+ " 0.0e+00, 0.0e+00, 0.0e+00],\n",
+ " [ 1.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 2.4e+02,\n",
+ " 2.4e+02, 2.4e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00,\n",
+ " 0.0e+00, 0.0e+00, 0.0e+00],\n",
+ " [ 2.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
+ " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00,\n",
+ " 0.0e+00, 0.0e+00, 0.0e+00]]),\n",
+ " 'gencost': array([[2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 0.e+00, 1.e+03, 0.e+00]]),\n",
+ " 'reserves': {'zones': array([[1., 1., 1., 0.]]),\n",
+ " 'req': 150.0,\n",
+ " 'cost': array([[1.],\n",
+ " [3.],\n",
+ " [5.]]),\n",
+ " 'qty': array([[100.],\n",
+ " [100.],\n",
+ " [200.]])},\n",
+ " 'f': 0.0,\n",
+ " 'et': 0.0,\n",
+ " 'success': 1.0},\n",
+ " 'InitialPg': array([[ 125.],\n",
+ " [ 125.],\n",
+ " [ 200.],\n",
+ " [-450.]]),\n",
+ " 'RampWearCostCoeff': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'offer': {'PositiveActiveReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'PositiveActiveReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]]),\n",
+ " 'NegativeActiveReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'NegativeActiveReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]]),\n",
+ " 'PositiveActiveDeltaPrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'NegativeActiveDeltaPrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'PositiveLoadFollowReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'PositiveLoadFollowReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]]),\n",
+ " 'NegativeLoadFollowReservePrice': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'NegativeLoadFollowReserveQuantity': array([[ 400.],\n",
+ " [ 400.],\n",
+ " [1000.],\n",
+ " [ 900.]])},\n",
+ " 'cont': {'contab': []},\n",
+ " 'DCMODEL': 0.0,\n",
+ " 'IncludeFixedReserves': 0.0,\n",
+ " 'SecurityConstrained': 0.0,\n",
+ " 'QCoordination': 0.0,\n",
+ " 'alpha': 0.0,\n",
+ " 'OpenEnded': 1.0,\n",
+ " 'flow': {'mpc': {'version': '2',\n",
+ " 'baseMVA': 100.0,\n",
+ " 'bus': array([[ 1. , 3. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95, 30. , 0. , 0. ,\n",
+ " 0. ],\n",
+ " [ 2. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95, 30. , 0. , 0. ,\n",
+ " 0. ],\n",
+ " [ 3. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
+ " 0. , 135. , 1. , 1.05, 0.95, 30. , 0. , 0. ,\n",
+ " 0. ]]),\n",
+ " 'gen': array([[ 1., 150., 0., 25., -25., 1., 100., 1., 200.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
+ " 250., 0., 0., 0., 0., 0., 0.],\n",
+ " [ 1., 150., 0., 25., -25., 1., 100., 1., 200.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
+ " 250., 0., 0., 0., 0., 0., 0.],\n",
+ " [ 2., 150., 0., 50., -50., 1., 100., 1., 500.,\n",
+ " 0., 0., 0., 0., 0., 0., 0., 0., 600.,\n",
+ " 600., 0., 0., 0., 0., 0., 0.],\n",
+ " [ 3., -450., 0., 0., 0., 1., 100., 1., 0.,\n",
+ " -450., 0., 0., 0., 0., 0., 0., 0., 500.,\n",
+ " 500., 0., 0., 0., 970., 0., 0.]]),\n",
+ " 'branch': array([[ 1.0e+00, 2.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
+ " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00,\n",
+ " 0.0e+00, 0.0e+00, 0.0e+00],\n",
+ " [ 1.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 2.4e+02,\n",
+ " 2.4e+02, 2.4e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00,\n",
+ " 0.0e+00, 0.0e+00, 0.0e+00],\n",
+ " [ 2.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
+ " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
+ " 3.6e+02, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00, 0.0e+00,\n",
+ " 0.0e+00, 0.0e+00, 0.0e+00]]),\n",
+ " 'gencost': array([[2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
+ " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 0.e+00, 1.e+03, 0.e+00]]),\n",
+ " 'reserves': {'zones': array([[1., 1., 1., 0.]]),\n",
+ " 'req': 150.0,\n",
+ " 'cost': array([[1.],\n",
+ " [3.],\n",
+ " [5.]]),\n",
+ " 'qty': array([[100.],\n",
+ " [100.],\n",
+ " [200.]])},\n",
+ " 'f': 0.0,\n",
+ " 'et': 0.0,\n",
+ " 'success': 1.0}},\n",
+ " 'StepProb': 1.0,\n",
+ " 'CostWeights': 1.0,\n",
+ " 'CostWeightsAdj': 1.0,\n",
+ " 'QP': {'A': ,\n",
+ " 'l': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'u': array([[ 0.],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.]]),\n",
+ " 'x0': array([[ 1.25],\n",
+ " [ 1.25],\n",
+ " [ 2. ],\n",
+ " [-4.5 ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ]]),\n",
+ " 'xmin': array([[ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [-4.5],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf],\n",
+ " [-inf]]),\n",
+ " 'xmax': array([[ 2.],\n",
+ " [ 2.],\n",
+ " [ 5.],\n",
+ " [ 0.],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [inf],\n",
+ " [ 4.],\n",
+ " [ 4.],\n",
+ " [10.],\n",
+ " [ 9.],\n",
+ " [ 4.],\n",
+ " [ 4.],\n",
+ " [10.],\n",
+ " [ 9.]]),\n",
+ " 'vtype': 'CCCCCCCCCCCCCCCCCCCCCCCC',\n",
+ " 'H1': ,\n",
+ " 'C1': array([[ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [100000.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.]]),\n",
+ " 'c1': 0.0,\n",
+ " 'Cfstor': ,\n",
+ " 'H': ,\n",
+ " 'C': array([[ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [100000.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.],\n",
+ " [ 0.]]),\n",
+ " 'c': 0.0,\n",
+ " 'opt': {'alg': 'MIPS',\n",
+ " 'verbose': 1.0,\n",
+ " 'mips_opt': {'step_control': 0.0,\n",
+ " 'linsolver': '',\n",
+ " 'feastol': 5e-06,\n",
+ " 'gradtol': 1e-06,\n",
+ " 'comptol': 1e-06,\n",
+ " 'costtol': 1e-06,\n",
+ " 'max_it': 150.0,\n",
+ " 'sc': {'red_it': 20.0}},\n",
+ " 'x0': []},\n",
+ " 'x': array([[ 1.5 ],\n",
+ " [ 1.5 ],\n",
+ " [ 1.5 ],\n",
+ " [-4.5 ],\n",
+ " [ 1.07383412],\n",
+ " [ 1.07383412],\n",
+ " [ 2.2194983 ],\n",
+ " [ 2.03796577],\n",
+ " [ 1.07383412],\n",
+ " [ 1.07383412],\n",
+ " [ 2.2194983 ],\n",
+ " [ 2.03796577],\n",
+ " [ 1.5 ],\n",
+ " [ 1.5 ],\n",
+ " [ 1.5 ],\n",
+ " [-4.5 ],\n",
+ " [ 2.14766824],\n",
+ " [ 2.14766824],\n",
+ " [ 4.43899661],\n",
+ " [ 4.07593154],\n",
+ " [ 2.14766824],\n",
+ " [ 2.14766824],\n",
+ " [ 4.43899661],\n",
+ " [ 4.07593154]]),\n",
+ " 'f': -443249.9999999571,\n",
+ " 'exitflag': 1.0,\n",
+ " 'output': {'iterations': 10.0,\n",
+ " 'hist': 1x11 StructArray containing the fields:\n",
+ " feascond\n",
+ " gradcond\n",
+ " compcond\n",
+ " costcond\n",
+ " gamma\n",
+ " stepsize\n",
+ " obj\n",
+ " alphap\n",
+ " alphad,\n",
+ " 'message': 'Converged',\n",
+ " 'alg': 'MIPS'},\n",
+ " 'lambda': {'mu_l': array([[3000.00000003],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ]]),\n",
+ " 'mu_u': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'lower': array([[ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [96999.99999998],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ],\n",
+ " [ 0. ]]),\n",
+ " 'upper': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]])}},\n",
+ " 'om': ,\n",
+ " 'results': {'Pc': array([[ 150.],\n",
+ " [ 150.],\n",
+ " [ 150.],\n",
+ " [-450.]]),\n",
+ " 'Rpp': array([[214.76682448],\n",
+ " [214.76682448],\n",
+ " [443.89966099],\n",
+ " [407.59315363]]),\n",
+ " 'Rpm': array([[214.76682448],\n",
+ " [214.76682448],\n",
+ " [443.89966099],\n",
+ " [407.59315363]]),\n",
+ " 'GenPrices': array([[30.],\n",
+ " [30.],\n",
+ " [30.],\n",
+ " [30.]]),\n",
+ " 'CondGenPrices': array([[30.],\n",
+ " [30.],\n",
+ " [30.],\n",
+ " [30.]]),\n",
+ " 'RppPrices': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'RpmPrices': array([[0.],\n",
+ " [0.],\n",
+ " [0.],\n",
+ " [0.]]),\n",
+ " 'RrpPrices': array([], shape=(4, 0), dtype=float64),\n",
+ " 'RrmPrices': array([], shape=(4, 0), dtype=float64),\n",
+ " 'ExpectedRampCost': array([], shape=(4, 0), dtype=float64),\n",
+ " 'ExpectedDispatch': array([[ 150.],\n",
+ " [ 150.],\n",
+ " [ 150.],\n",
+ " [-450.]]),\n",
+ " 'f': -443249.9999999571,\n",
+ " 'success': 1.0,\n",
+ " 'SolveTime': 0.01789093017578125,\n",
+ " 'SetupTime': 0.03305697441101074}}"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# TODO:\n",
+ "# 1. Convert mdo into DataFrame\n",
+ "# 2. Clean up classdef element has been converted to a struct\n",
+ "# mdo: MOST data structure output\n",
+ "mdo = m.most(mdi, mpopt)\n",
+ "mdo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "5faf9637",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# TODO:\n",
+ "# 1. Fix error\n",
+ "# Oct2PyError: Octave evaluation error:\n",
+ "# error: get: H must be a graphics handle\n",
+ "# error: called from:\n",
+ "# 2. ms is most summary\n",
+ "\n",
+ "# ms = m.most_summary(mdo)\n",
+ "# ms"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "f798f194",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "zones\n"
+ "info\n"
]
},
{
@@ -970,36 +1900,42 @@
"\n",
" \n",
" \n",
- " | gen | \n",
- " 1 | \n",
- " 2 | \n",
- " 3 | \n",
- " 4 | \n",
- "
\n",
- " \n",
- " | zone | \n",
- " | \n",
- " | \n",
- " | \n",
" | \n",
+ " INFO | \n",
"
\n",
" \n",
" \n",
" \n",
- " | 1 | \n",
- " 1.0 | \n",
- " 1.0 | \n",
- " 1.0 | \n",
+ " version | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ " | baseMVA | \n",
+ " 100.0 | \n",
+ "
\n",
+ " \n",
+ " | f | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | et | \n",
" 0.0 | \n",
"
\n",
+ " \n",
+ " | success | \n",
+ " 1.0 | \n",
+ "
\n",
" \n",
"
\n",
""
],
"text/plain": [
- "gen 1 2 3 4\n",
- "zone \n",
- "1 1.0 1.0 1.0 0.0"
+ " INFO\n",
+ "version 2\n",
+ "baseMVA 100.0\n",
+ "f 0.0\n",
+ "et 0.0\n",
+ "success 1.0"
]
},
"metadata": {},
@@ -1009,7 +1945,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "req\n"
+ "bus\n"
]
},
{
@@ -1033,26 +1969,122 @@
" \n",
" \n",
" | \n",
- " PREQ | \n",
+ " BUS_I | \n",
+ " BUS_TYPE | \n",
+ " PD | \n",
+ " QD | \n",
+ " GS | \n",
+ " BS | \n",
+ " BUS_AREA | \n",
+ " VM | \n",
+ " VA | \n",
+ " BASE_KV | \n",
+ " ZONE | \n",
+ " VMAX | \n",
+ " VMIN | \n",
+ " LAM_P | \n",
+ " LAM_Q | \n",
+ " MU_VMAX | \n",
+ " MU_VMIN | \n",
"
\n",
" \n",
- " | zone | \n",
+ " bus | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
" | 1 | \n",
- " 150.0 | \n",
+ " 1.0 | \n",
+ " 3.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 135.0 | \n",
+ " 1.0 | \n",
+ " 1.05 | \n",
+ " 0.95 | \n",
+ " 30.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 2.0 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 135.0 | \n",
+ " 1.0 | \n",
+ " 1.05 | \n",
+ " 0.95 | \n",
+ " 30.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 3.0 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 135.0 | \n",
+ " 1.0 | \n",
+ " 1.05 | \n",
+ " 0.95 | \n",
+ " 30.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
"\n",
""
],
"text/plain": [
- " PREQ\n",
- "zone \n",
- "1 150.0"
+ " BUS_I BUS_TYPE PD QD GS BS BUS_AREA VM VA BASE_KV ZONE \\\n",
+ "bus \n",
+ "1 1.0 3.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 135.0 1.0 \n",
+ "2 2.0 2.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 135.0 1.0 \n",
+ "3 3.0 2.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 135.0 1.0 \n",
+ "\n",
+ " VMAX VMIN LAM_P LAM_Q MU_VMAX MU_VMIN \n",
+ "bus \n",
+ "1 1.05 0.95 30.0 0.0 0.0 0.0 \n",
+ "2 1.05 0.95 30.0 0.0 0.0 0.0 \n",
+ "3 1.05 0.95 30.0 0.0 0.0 0.0 "
]
},
"metadata": {},
@@ -1062,7 +2094,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "cost\n"
+ "gen\n"
]
},
{
@@ -1086,36 +2118,178 @@
" \n",
" \n",
" | \n",
- " C1 | \n",
+ " GEN_BUS | \n",
+ " PG | \n",
+ " QG | \n",
+ " QMAX | \n",
+ " QMIN | \n",
+ " VG | \n",
+ " MBASE | \n",
+ " GEN_STATUS | \n",
+ " PMAX | \n",
+ " PMIN | \n",
+ " ... | \n",
+ " QC2MAX | \n",
+ " RAMP_AGC | \n",
+ " RAMP_10 | \n",
+ " RAMP_30 | \n",
+ " RAMP_Q | \n",
+ " APF | \n",
+ " MU_PMAX | \n",
+ " MU_PMIN | \n",
+ " MU_QMAX | \n",
+ " MU_QMIN | \n",
"
\n",
" \n",
" | gen | \n",
" | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
"
\n",
" \n",
" \n",
" \n",
" | 1 | \n",
" 1.0 | \n",
+ " 150.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
" | 2 | \n",
- " 3.0 | \n",
+ " 1.0 | \n",
+ " 150.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
" | 3 | \n",
- " 5.0 | \n",
+ " 2.0 | \n",
+ " 150.0 | \n",
+ " 0.0 | \n",
+ " 50.0 | \n",
+ " -50.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 600.0 | \n",
+ " 600.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 3.0 | \n",
+ " -450.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " -450.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 500.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 970.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
"\n",
+ "4 rows × 25 columns
\n",
""
],
"text/plain": [
- " C1\n",
- "gen \n",
- "1 1.0\n",
- "2 3.0\n",
- "3 5.0"
+ " GEN_BUS PG QG QMAX QMIN VG MBASE GEN_STATUS PMAX PMIN \\\n",
+ "gen \n",
+ "1 1.0 150.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "2 1.0 150.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "3 2.0 150.0 0.0 50.0 -50.0 1.0 100.0 1.0 500.0 0.0 \n",
+ "4 3.0 -450.0 0.0 0.0 0.0 1.0 100.0 1.0 0.0 -450.0 \n",
+ "\n",
+ " ... QC2MAX RAMP_AGC RAMP_10 RAMP_30 RAMP_Q APF MU_PMAX MU_PMIN \\\n",
+ "gen ... \n",
+ "1 ... 0.0 0.0 250.0 250.0 0.0 0.0 0.0 0.0 \n",
+ "2 ... 0.0 0.0 250.0 250.0 0.0 0.0 0.0 0.0 \n",
+ "3 ... 0.0 0.0 600.0 600.0 0.0 0.0 0.0 0.0 \n",
+ "4 ... 0.0 0.0 500.0 500.0 0.0 0.0 0.0 970.0 \n",
+ "\n",
+ " MU_QMAX MU_QMIN \n",
+ "gen \n",
+ "1 0.0 0.0 \n",
+ "2 0.0 0.0 \n",
+ "3 0.0 0.0 \n",
+ "4 0.0 0.0 \n",
+ "\n",
+ "[4 rows x 25 columns]"
]
},
"metadata": {},
@@ -1125,7 +2299,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "qty\n"
+ "branch\n"
]
},
{
@@ -1149,55 +2323,163 @@
" \n",
" \n",
" | \n",
- " PQTY | \n",
+ " F_BUS | \n",
+ " T_BUS | \n",
+ " BR_R | \n",
+ " BR_X | \n",
+ " BR_B | \n",
+ " RATE_A | \n",
+ " RATE_B | \n",
+ " RATE_C | \n",
+ " TAP | \n",
+ " SHIFT | \n",
+ " ... | \n",
+ " ANGMIN | \n",
+ " ANGMAX | \n",
+ " PF | \n",
+ " QF | \n",
+ " PT | \n",
+ " QT | \n",
+ " MU_SF | \n",
+ " MU_ST | \n",
+ " MU_ANGMIN | \n",
+ " MU_ANGMAX | \n",
"
\n",
" \n",
- " | gen | \n",
+ " branch | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
" | 1 | \n",
- " 100.0 | \n",
+ " 1.0 | \n",
+ " 2.0 | \n",
+ " 0.005 | \n",
+ " 0.01 | \n",
+ " 0.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " -360.0 | \n",
+ " 360.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
" | 2 | \n",
- " 100.0 | \n",
+ " 1.0 | \n",
+ " 3.0 | \n",
+ " 0.005 | \n",
+ " 0.01 | \n",
+ " 0.0 | \n",
+ " 240.0 | \n",
+ " 240.0 | \n",
+ " 240.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " -360.0 | \n",
+ " 360.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
" | 3 | \n",
- " 200.0 | \n",
+ " 2.0 | \n",
+ " 3.0 | \n",
+ " 0.005 | \n",
+ " 0.01 | \n",
+ " 0.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 300.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " -360.0 | \n",
+ " 360.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
"\n",
+ "3 rows × 21 columns
\n",
""
],
"text/plain": [
- " PQTY\n",
- "gen \n",
- "1 100.0\n",
- "2 100.0\n",
- "3 200.0"
+ " F_BUS T_BUS BR_R BR_X BR_B RATE_A RATE_B RATE_C TAP SHIFT \\\n",
+ "branch \n",
+ "1 1.0 2.0 0.005 0.01 0.0 300.0 300.0 300.0 0.0 0.0 \n",
+ "2 1.0 3.0 0.005 0.01 0.0 240.0 240.0 240.0 0.0 0.0 \n",
+ "3 2.0 3.0 0.005 0.01 0.0 300.0 300.0 300.0 0.0 0.0 \n",
+ "\n",
+ " ... ANGMIN ANGMAX PF QF PT QT MU_SF MU_ST MU_ANGMIN \\\n",
+ "branch ... \n",
+ "1 ... -360.0 360.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
+ "2 ... -360.0 360.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
+ "3 ... -360.0 360.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
+ "\n",
+ " MU_ANGMAX \n",
+ "branch \n",
+ "1 0.0 \n",
+ "2 0.0 \n",
+ "3 0.0 \n",
+ "\n",
+ "[3 rows x 21 columns]"
]
},
"metadata": {},
"output_type": "display_data"
- }
- ],
- "source": [
- "cf = CaseFrames(os.path.join(path_most_ex_cases, \"ex_case3a.m\"), load_case_engine=m)\n",
- "for attribute in cf.reserves._attributes:\n",
- " print(attribute)\n",
- " display(getattr(cf.reserves, attribute))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "id": "71fd2c3b",
- "metadata": {},
- "outputs": [
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "gencost\n"
+ ]
+ },
{
"data": {
"text/html": [
@@ -1218,11 +2500,115 @@
"\n",
" \n",
" \n",
- " | gen | \n",
- " 1 | \n",
- " 2 | \n",
- " 3 | \n",
- " 4 | \n",
+ " | \n",
+ " MODEL | \n",
+ " STARTUP | \n",
+ " SHUTDOWN | \n",
+ " NCOST | \n",
+ " C2 | \n",
+ " C1 | \n",
+ " C0 | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0.0 | \n",
+ " 1000.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ ""
+ ],
+ "text/plain": [
+ " MODEL STARTUP SHUTDOWN NCOST C2 C1 C0\n",
+ "gen \n",
+ "1 2.0 0.0 0.0 3.0 0.1 0.0 0.0\n",
+ "2 2.0 0.0 0.0 3.0 0.1 0.0 0.0\n",
+ "3 2.0 0.0 0.0 3.0 0.1 0.0 0.0\n",
+ "4 2.0 0.0 0.0 3.0 0.0 1000.0 0.0"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "reserves.zones\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | gen | \n",
+ " 1 | \n",
+ " 2 | \n",
+ " 3 | \n",
+ " 4 | \n",
"
\n",
" \n",
" | zone | \n",
@@ -1250,556 +2636,1659 @@
"1 1.0 1.0 1.0 0.0"
]
},
- "execution_count": 11,
"metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "cf.reserves.zones"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "8789a89c",
- "metadata": {},
- "source": [
- "> [!NOTE] \n",
- "> Currently, `loadgenericdata` didn't support absolute path. This impacts various `most`\n",
- "> functions. See: ."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "id": "65bf6c72",
- "metadata": {},
- "outputs": [
+ "output_type": "display_data"
+ },
{
- "data": {
- "text/plain": [
- "{'CommitSched': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]]),\n",
- " 'InitialPg': array([[ 125.],\n",
- " [ 125.],\n",
- " [ 200.],\n",
- " [-450.]]),\n",
- " 'RampWearCostCoeff': array([[0.],\n",
- " [0.],\n",
- " [0.],\n",
- " [0.]]),\n",
- " 'PositiveActiveReservePrice': array([[5.0e+00],\n",
- " [1.0e-08],\n",
- " [1.5e+00],\n",
- " [1.0e-08]]),\n",
- " 'PositiveActiveReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [600.],\n",
- " [800.]]),\n",
- " 'NegativeActiveReservePrice': array([[1.e+01],\n",
- " [2.e-08],\n",
- " [3.e+00],\n",
- " [2.e-08]]),\n",
- " 'NegativeActiveReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [600.],\n",
- " [800.]]),\n",
- " 'PositiveActiveDeltaPrice': array([[1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09]]),\n",
- " 'NegativeActiveDeltaPrice': array([[1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09]]),\n",
- " 'PositiveLoadFollowReservePrice': array([[1.e-06],\n",
- " [1.e-06],\n",
- " [1.e+01],\n",
- " [1.e-06]]),\n",
- " 'PositiveLoadFollowReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [100.],\n",
- " [800.]]),\n",
- " 'NegativeLoadFollowReservePrice': array([[1.e-06],\n",
- " [1.e-06],\n",
- " [1.e+01],\n",
- " [1.e-06]]),\n",
- " 'NegativeLoadFollowReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [250.],\n",
- " [800.]]),\n",
- " 'CommitKey': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [2.]]),\n",
- " 'InitialState': array([[inf],\n",
- " [inf],\n",
- " [inf],\n",
- " [inf]]),\n",
- " 'MinUp': array([[1.],\n",
- " [3.],\n",
- " [1.],\n",
- " [1.]]),\n",
- " 'MinDown': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]])}"
- ]
- },
- "execution_count": 18,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "xgd = m.loadxgendata(\"ex_xgd_uc.m\", mpc)\n",
- "xgd"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "id": "9688f94d",
- "metadata": {},
- "outputs": [
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "reserves.req\n"
+ ]
+ },
{
"data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " PREQ | \n",
+ "
\n",
+ " \n",
+ " | zone | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 150.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
"text/plain": [
- "[5.0,\n",
- " {'version': '2',\n",
- " 'baseMVA': 100.0,\n",
- " 'bus': array([[ 1. , 3. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95],\n",
- " [ 2. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95],\n",
- " [ 3. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95]]),\n",
- " 'gen': array([[ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
- " 250., 0., 0.],\n",
- " [ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
- " 250., 0., 0.],\n",
- " [ 2., 200., 0., 50., -50., 1., 100., 1., 500.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 600.,\n",
- " 600., 0., 0.],\n",
- " [ 3., -450., 0., 0., 0., 1., 100., 1., 0.,\n",
- " -450., 0., 0., 0., 0., 0., 0., 0., 500.,\n",
- " 500., 0., 0.],\n",
- " [ 2., 0., 0., 50., -50., 1., 100., 1., 100.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 200.,\n",
- " 200., 0., 0.]]),\n",
- " 'branch': array([[ 1.0e+00, 2.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
- " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02],\n",
- " [ 1.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 2.4e+02,\n",
- " 2.4e+02, 2.4e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02],\n",
- " [ 2.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
- " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02]]),\n",
- " 'gencost': array([[2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 0.e+00, 1.e+03, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 2.e+00, 0.e+00, 0.e+00, 0.e+00]]),\n",
- " 'reserves': {'zones': array([[1., 1., 1., 0., 0.]]),\n",
- " 'req': 150.0,\n",
- " 'cost': array([[1.],\n",
- " [3.],\n",
- " [5.]]),\n",
- " 'qty': array([[100.],\n",
- " [100.],\n",
- " [200.]])},\n",
- " 'genfuel': Cell([['unknown'],\n",
- " ['unknown'],\n",
- " ['unknown'],\n",
- " ['unknown'],\n",
- " ['wind']]),\n",
- " 'iwind': 5.0},\n",
- " {'CommitSched': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]]),\n",
- " 'InitialPg': array([[ 125.],\n",
- " [ 125.],\n",
- " [ 200.],\n",
- " [-450.],\n",
- " [ 0.]]),\n",
- " 'RampWearCostCoeff': array([[0.],\n",
- " [0.],\n",
- " [0.],\n",
- " [0.],\n",
- " [0.]]),\n",
- " 'PositiveActiveReservePrice': array([[5.0e+00],\n",
- " [1.0e-08],\n",
- " [1.5e+00],\n",
- " [1.0e-08],\n",
- " [1.0e-08]]),\n",
- " 'PositiveActiveReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [600.],\n",
- " [800.],\n",
- " [200.]]),\n",
- " 'NegativeActiveReservePrice': array([[1.e+01],\n",
- " [2.e-08],\n",
- " [3.e+00],\n",
- " [2.e-08],\n",
- " [2.e-08]]),\n",
- " 'NegativeActiveReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [600.],\n",
- " [800.],\n",
- " [200.]]),\n",
- " 'PositiveActiveDeltaPrice': array([[1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09]]),\n",
- " 'NegativeActiveDeltaPrice': array([[1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09]]),\n",
- " 'PositiveLoadFollowReservePrice': array([[1.e-06],\n",
- " [1.e-06],\n",
- " [1.e+01],\n",
- " [1.e-06],\n",
- " [1.e-06]]),\n",
- " 'PositiveLoadFollowReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [100.],\n",
- " [800.],\n",
- " [200.]]),\n",
- " 'NegativeLoadFollowReservePrice': array([[1.e-06],\n",
- " [1.e-06],\n",
- " [1.e+01],\n",
- " [1.e-06],\n",
- " [1.e-06]]),\n",
- " 'NegativeLoadFollowReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [250.],\n",
- " [800.],\n",
- " [200.]]),\n",
- " 'CommitKey': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [2.],\n",
- " [2.]]),\n",
- " 'InitialState': array([[inf],\n",
- " [inf],\n",
- " [inf],\n",
- " [inf],\n",
- " [inf]]),\n",
- " 'MinUp': array([[1.],\n",
- " [3.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]]),\n",
- " 'MinDown': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]])}]"
+ " PREQ\n",
+ "zone \n",
+ "1 150.0"
]
},
- "execution_count": 19,
"metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "[iwind, mpc, xgd] = m.addwind(\"ex_wind_uc\", mpc, xgd, nout=3)\n",
- "[iwind, mpc, xgd]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "id": "676057c0",
- "metadata": {},
- "outputs": [
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "reserves.cost\n"
+ ]
+ },
{
"data": {
- "text/plain": [
- "{'type': 'mpcData',\n",
- " 'table': 2.0,\n",
- " 'rows': 5.0,\n",
- " 'col': 9.0,\n",
- " 'chgtype': 2.0,\n",
- " 'values': array([[0.8 ],\n",
- " [0.65],\n",
- " [0.6 ],\n",
- " [0.82],\n",
- " [1. ],\n",
- " [0.7 ],\n",
- " [0.5 ],\n",
- " [0.85],\n",
- " [1. ],\n",
- " [1.1 ],\n",
- " [1.06],\n",
- " [0.95]])}"
- ]
- },
- "execution_count": 20,
- "metadata": {},
- "output_type": "execute_result"
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " C1 | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 3.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 5.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " C1\n",
+ "gen \n",
+ "1 1.0\n",
+ "2 3.0\n",
+ "3 5.0"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "reserves.qty\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " PQTY | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 100.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 100.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 200.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " PQTY\n",
+ "gen \n",
+ "1 100.0\n",
+ "2 100.0\n",
+ "3 200.0"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
}
],
"source": [
- "profiles = m.getprofiles(\"ex_wind_profile_d\", iwind)\n",
- "profiles"
+ "cfr2 = CaseFrames(mdo.flow.mpc)\n",
+ "cfr2.display()"
]
},
{
"cell_type": "code",
- "execution_count": 21,
- "id": "2bcf5d64",
+ "execution_count": 13,
+ "id": "8c9c81c2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "2x1 StructArray containing the fields:\n",
- " type\n",
- " table\n",
- " rows\n",
- " col\n",
- " chgtype\n",
- " values"
+ "(gen\n",
+ " 1 150.0\n",
+ " 2 150.0\n",
+ " 3 150.0\n",
+ " 4 -450.0\n",
+ " Name: PG, dtype: float64,\n",
+ " bus\n",
+ " 1 30.0\n",
+ " 2 30.0\n",
+ " 3 30.0\n",
+ " Name: LAM_P, dtype: float64)"
]
},
- "execution_count": 21,
+ "execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "profiles = m.getprofiles(\"ex_load_profile\", profiles)\n",
- "profiles"
+ "Pg2 = cfr2.gen.loc[:, \"PG\"] # active generation\n",
+ "lam2 = cfr2.bus.loc[:, \"LAM_P\"] # nodal energy price\n",
+ "Pg2, lam2"
]
},
{
- "cell_type": "code",
- "execution_count": 22,
- "id": "ee0cec81",
+ "cell_type": "markdown",
+ "id": "8789a89c",
"metadata": {},
- "outputs": [],
"source": [
- "# TODO: example on `mdi = loadmd(mpc, transmat, xgd, [], 'ex_contab', profiles);`"
+ "> [!NOTE] \n",
+ "> Currently, `loadgenericdata` didn't support absolute path. This impacts various `most`\n",
+ "> functions. See: ."
]
},
{
"cell_type": "code",
- "execution_count": 23,
- "id": "c9726cc1",
+ "execution_count": 14,
+ "id": "575be760",
"metadata": {},
"outputs": [
{
"data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " CommitKey | \n",
+ " CommitSched | \n",
+ " MinUp | \n",
+ " MinDown | \n",
+ " PositiveActiveReservePrice | \n",
+ " PositiveActiveReserveQuantity | \n",
+ " NegativeActiveReservePrice | \n",
+ " NegativeActiveReserveQuantity | \n",
+ " PositiveActiveDeltaPrice | \n",
+ " NegativeActiveDeltaPrice | \n",
+ " PositiveLoadFollowReservePrice | \n",
+ " PositiveLoadFollowReserveQuantity | \n",
+ " NegativeLoadFollowReservePrice | \n",
+ " NegativeLoadFollowReserveQuantity | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 5.000000e+00 | \n",
+ " 250.0 | \n",
+ " 1.000000e+01 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 3.0 | \n",
+ " 1.0 | \n",
+ " 1.000000e-08 | \n",
+ " 250.0 | \n",
+ " 2.000000e-08 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 1.500000e+00 | \n",
+ " 600.0 | \n",
+ " 3.000000e+00 | \n",
+ " 600.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 10.000000 | \n",
+ " 100.0 | \n",
+ " 10.000000 | \n",
+ " 250.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 2.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ " 1.000000e-08 | \n",
+ " 800.0 | \n",
+ " 2.000000e-08 | \n",
+ " 800.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
"text/plain": [
- "[6.0,\n",
- " {'version': '2',\n",
- " 'baseMVA': 100.0,\n",
- " 'bus': array([[ 1. , 3. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95],\n",
- " [ 2. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95],\n",
- " [ 3. , 2. , 0. , 0. , 0. , 0. , 1. , 1. ,\n",
- " 0. , 135. , 1. , 1.05, 0.95]]),\n",
- " 'gen': array([[ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
- " 250., 0., 0.],\n",
- " [ 1., 125., 0., 25., -25., 1., 100., 1., 200.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 250.,\n",
- " 250., 0., 0.],\n",
- " [ 2., 200., 0., 50., -50., 1., 100., 1., 500.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 600.,\n",
- " 600., 0., 0.],\n",
- " [ 3., -450., 0., 0., 0., 1., 100., 1., 0.,\n",
- " -450., 0., 0., 0., 0., 0., 0., 0., 500.,\n",
- " 500., 0., 0.],\n",
- " [ 2., 0., 0., 50., -50., 1., 100., 1., 100.,\n",
- " 0., 0., 0., 0., 0., 0., 0., 0., 200.,\n",
- " 200., 0., 0.],\n",
- " [ 3., 0., 0., 0., 0., 1., 100., 1., 80.,\n",
- " -80., 0., 0., 0., 0., 0., 0., 0., 20.,\n",
- " 20., 0., 0.]]),\n",
- " 'branch': array([[ 1.0e+00, 2.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
- " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02],\n",
- " [ 1.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 2.4e+02,\n",
- " 2.4e+02, 2.4e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02],\n",
- " [ 2.0e+00, 3.0e+00, 5.0e-03, 1.0e-02, 0.0e+00, 3.0e+02,\n",
- " 3.0e+02, 3.0e+02, 0.0e+00, 0.0e+00, 1.0e+00, -3.6e+02,\n",
- " 3.6e+02]]),\n",
- " 'gencost': array([[2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 1.e-01, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 3.e+00, 0.e+00, 1.e+03, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 2.e+00, 0.e+00, 0.e+00, 0.e+00],\n",
- " [2.e+00, 0.e+00, 0.e+00, 2.e+00, 0.e+00, 0.e+00, 0.e+00]]),\n",
- " 'reserves': {'zones': array([[1., 1., 1., 0., 0., 0.]]),\n",
- " 'req': 150.0,\n",
- " 'cost': array([[1.],\n",
- " [3.],\n",
- " [5.]]),\n",
- " 'qty': array([[100.],\n",
- " [100.],\n",
- " [200.]])},\n",
- " 'genfuel': Cell([['unknown'],\n",
- " ['unknown'],\n",
- " ['unknown'],\n",
- " ['unknown'],\n",
- " ['wind'],\n",
- " ['ess']]),\n",
- " 'iwind': 5.0,\n",
- " 'iess': 6.0},\n",
- " {'CommitSched': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]]),\n",
- " 'InitialPg': array([[ 125.],\n",
- " [ 125.],\n",
- " [ 200.],\n",
- " [-450.],\n",
- " [ 0.],\n",
- " [ 0.]]),\n",
- " 'RampWearCostCoeff': array([[0.],\n",
- " [0.],\n",
- " [0.],\n",
- " [0.],\n",
- " [0.],\n",
- " [0.]]),\n",
- " 'PositiveActiveReservePrice': array([[5.0e+00],\n",
- " [1.0e-08],\n",
- " [1.5e+00],\n",
- " [1.0e-08],\n",
- " [1.0e-08],\n",
- " [1.0e-08]]),\n",
- " 'PositiveActiveReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [600.],\n",
- " [800.],\n",
- " [200.],\n",
- " [160.]]),\n",
- " 'NegativeActiveReservePrice': array([[1.e+01],\n",
- " [2.e-08],\n",
- " [3.e+00],\n",
- " [2.e-08],\n",
- " [2.e-08],\n",
- " [2.e-08]]),\n",
- " 'NegativeActiveReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [600.],\n",
- " [800.],\n",
- " [200.],\n",
- " [160.]]),\n",
- " 'PositiveActiveDeltaPrice': array([[1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09]]),\n",
- " 'NegativeActiveDeltaPrice': array([[1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09],\n",
- " [1.e-09]]),\n",
- " 'PositiveLoadFollowReservePrice': array([[1.e-06],\n",
- " [1.e-06],\n",
- " [1.e+01],\n",
- " [1.e-06],\n",
- " [1.e-06],\n",
- " [1.e-06]]),\n",
- " 'PositiveLoadFollowReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [100.],\n",
- " [800.],\n",
- " [200.],\n",
- " [160.]]),\n",
- " 'NegativeLoadFollowReservePrice': array([[1.e-06],\n",
- " [1.e-06],\n",
- " [1.e+01],\n",
- " [1.e-06],\n",
- " [1.e-06],\n",
- " [1.e-06]]),\n",
- " 'NegativeLoadFollowReserveQuantity': array([[250.],\n",
- " [250.],\n",
- " [250.],\n",
- " [800.],\n",
- " [200.],\n",
- " [160.]]),\n",
- " 'CommitKey': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [2.],\n",
- " [2.],\n",
- " [2.]]),\n",
- " 'InitialState': array([[inf],\n",
- " [inf],\n",
- " [inf],\n",
- " [inf],\n",
- " [inf],\n",
- " [inf]]),\n",
- " 'MinUp': array([[1.],\n",
- " [3.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]]),\n",
- " 'MinDown': array([[1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.],\n",
- " [1.]])},\n",
- " {'UnitIdx': 6.0,\n",
- " 'ExpectedTerminalStorageAim': [],\n",
- " 'ExpectedTerminalStorageMin': [],\n",
- " 'ExpectedTerminalStorageMax': [],\n",
- " 'rho': 0.0,\n",
- " 'TerminalChargingPrice0': [],\n",
- " 'TerminalDischargingPrice0': [],\n",
- " 'TerminalChargingPriceK': [],\n",
- " 'TerminalDischargingPriceK': [],\n",
- " 'OutEff': 1.0,\n",
- " 'InEff': 1.0,\n",
- " 'LossFactor': 1e-05,\n",
- " 'InitialStorage': 0.0,\n",
- " 'InitialStorageLowerBound': 0.0,\n",
- " 'InitialStorageUpperBound': 200.0,\n",
- " 'InitialStorageCost': 45.166667,\n",
- " 'TerminalStoragePrice': 45.166667,\n",
- " 'MinStorageLevel': 0.0,\n",
- " 'MaxStorageLevel': 200.0}]"
+ " CommitKey CommitSched MinUp MinDown PositiveActiveReservePrice \\\n",
+ "gen \n",
+ "1 1.0 1.0 1.0 1.0 5.000000e+00 \n",
+ "2 1.0 1.0 3.0 1.0 1.000000e-08 \n",
+ "3 1.0 1.0 1.0 1.0 1.500000e+00 \n",
+ "4 2.0 1.0 1.0 1.0 1.000000e-08 \n",
+ "\n",
+ " PositiveActiveReserveQuantity NegativeActiveReservePrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e+01 \n",
+ "2 250.0 2.000000e-08 \n",
+ "3 600.0 3.000000e+00 \n",
+ "4 800.0 2.000000e-08 \n",
+ "\n",
+ " NegativeActiveReserveQuantity PositiveActiveDeltaPrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e-09 \n",
+ "2 250.0 1.000000e-09 \n",
+ "3 600.0 1.000000e-09 \n",
+ "4 800.0 1.000000e-09 \n",
+ "\n",
+ " NegativeActiveDeltaPrice PositiveLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 1.000000e-09 0.000001 \n",
+ "2 1.000000e-09 0.000001 \n",
+ "3 1.000000e-09 10.000000 \n",
+ "4 1.000000e-09 0.000001 \n",
+ "\n",
+ " PositiveLoadFollowReserveQuantity NegativeLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 250.0 0.000001 \n",
+ "2 250.0 0.000001 \n",
+ "3 100.0 10.000000 \n",
+ "4 800.0 0.000001 \n",
+ "\n",
+ " NegativeLoadFollowReserveQuantity \n",
+ "gen \n",
+ "1 250.0 \n",
+ "2 250.0 \n",
+ "3 250.0 \n",
+ "4 800.0 "
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# NOTE:\n",
+ "# This code below and `m.loadxgendata(xgdtf.to_dict(), mpc)` can be skipped using,\n",
+ "# `xgd = m.loadxgendata(\"ex_xgd_uc.m\", mpc)`\n",
+ "xgdt = m.loadgenericdata(\n",
+ " \"ex_xgd_uc.m\", \"struct\", {\"colnames\", \"data\"}, \"xgd_table\", cf.to_mpc()\n",
+ ")\n",
+ "xgdtf = xGenDataTableFrames(data=xgdt.data, colnames=xgdt.colnames, index=cf.gen.index)\n",
+ "xgdtf"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "63a198e4",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " CommitSched | \n",
+ " InitialPg | \n",
+ " RampWearCostCoeff | \n",
+ " PositiveActiveReservePrice | \n",
+ " PositiveActiveReserveQuantity | \n",
+ " NegativeActiveReservePrice | \n",
+ " NegativeActiveReserveQuantity | \n",
+ " PositiveActiveDeltaPrice | \n",
+ " NegativeActiveDeltaPrice | \n",
+ " PositiveLoadFollowReservePrice | \n",
+ " PositiveLoadFollowReserveQuantity | \n",
+ " NegativeLoadFollowReservePrice | \n",
+ " NegativeLoadFollowReserveQuantity | \n",
+ " CommitKey | \n",
+ " InitialState | \n",
+ " MinUp | \n",
+ " MinDown | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 5.000000e+00 | \n",
+ " 250.0 | \n",
+ " 1.000000e+01 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 250.0 | \n",
+ " 2.000000e-08 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 3.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 1.500000e+00 | \n",
+ " 600.0 | \n",
+ " 3.000000e+00 | \n",
+ " 600.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 10.000000 | \n",
+ " 100.0 | \n",
+ " 10.000000 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 1.0 | \n",
+ " -450.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 800.0 | \n",
+ " 2.000000e-08 | \n",
+ " 800.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ " 2.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " CommitSched InitialPg RampWearCostCoeff PositiveActiveReservePrice \\\n",
+ "gen \n",
+ "1 1.0 125.0 0.0 5.000000e+00 \n",
+ "2 1.0 125.0 0.0 1.000000e-08 \n",
+ "3 1.0 200.0 0.0 1.500000e+00 \n",
+ "4 1.0 -450.0 0.0 1.000000e-08 \n",
+ "\n",
+ " PositiveActiveReserveQuantity NegativeActiveReservePrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e+01 \n",
+ "2 250.0 2.000000e-08 \n",
+ "3 600.0 3.000000e+00 \n",
+ "4 800.0 2.000000e-08 \n",
+ "\n",
+ " NegativeActiveReserveQuantity PositiveActiveDeltaPrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e-09 \n",
+ "2 250.0 1.000000e-09 \n",
+ "3 600.0 1.000000e-09 \n",
+ "4 800.0 1.000000e-09 \n",
+ "\n",
+ " NegativeActiveDeltaPrice PositiveLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 1.000000e-09 0.000001 \n",
+ "2 1.000000e-09 0.000001 \n",
+ "3 1.000000e-09 10.000000 \n",
+ "4 1.000000e-09 0.000001 \n",
+ "\n",
+ " PositiveLoadFollowReserveQuantity NegativeLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 250.0 0.000001 \n",
+ "2 250.0 0.000001 \n",
+ "3 100.0 10.000000 \n",
+ "4 800.0 0.000001 \n",
+ "\n",
+ " NegativeLoadFollowReserveQuantity CommitKey InitialState MinUp \\\n",
+ "gen \n",
+ "1 250.0 1.0 inf 1.0 \n",
+ "2 250.0 1.0 inf 3.0 \n",
+ "3 250.0 1.0 inf 1.0 \n",
+ "4 800.0 2.0 inf 1.0 \n",
+ "\n",
+ " MinDown \n",
+ "gen \n",
+ "1 1.0 \n",
+ "2 1.0 \n",
+ "3 1.0 \n",
+ "4 1.0 "
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "xgd = m.loadxgendata(xgdtf.to_xgdt(), cf.to_mpc())\n",
+ "xgdf = xGenDataTableFrames(data=xgd)\n",
+ "xgdf # support both `xgdf[\"CommitSched\"]` and `xgdf.CommitSched`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "13caf9af",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " CommitSched | \n",
+ " InitialPg | \n",
+ " RampWearCostCoeff | \n",
+ " PositiveActiveReservePrice | \n",
+ " PositiveActiveReserveQuantity | \n",
+ " NegativeActiveReservePrice | \n",
+ " NegativeActiveReserveQuantity | \n",
+ " PositiveActiveDeltaPrice | \n",
+ " NegativeActiveDeltaPrice | \n",
+ " PositiveLoadFollowReservePrice | \n",
+ " PositiveLoadFollowReserveQuantity | \n",
+ " NegativeLoadFollowReservePrice | \n",
+ " NegativeLoadFollowReserveQuantity | \n",
+ " CommitKey | \n",
+ " InitialState | \n",
+ " MinUp | \n",
+ " MinDown | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 5.000000e+00 | \n",
+ " 250.0 | \n",
+ " 1.000000e+01 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 250.0 | \n",
+ " 2.000000e-08 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 3.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 1.500000e+00 | \n",
+ " 600.0 | \n",
+ " 3.000000e+00 | \n",
+ " 600.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 10.000000 | \n",
+ " 100.0 | \n",
+ " 10.000000 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 1.0 | \n",
+ " -450.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 800.0 | \n",
+ " 2.000000e-08 | \n",
+ " 800.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ " 2.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 200.0 | \n",
+ " 2.000000e-08 | \n",
+ " 200.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 200.0 | \n",
+ " 0.000001 | \n",
+ " 200.0 | \n",
+ " 2.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " CommitSched InitialPg RampWearCostCoeff PositiveActiveReservePrice \\\n",
+ "gen \n",
+ "1 1.0 125.0 0.0 5.000000e+00 \n",
+ "2 1.0 125.0 0.0 1.000000e-08 \n",
+ "3 1.0 200.0 0.0 1.500000e+00 \n",
+ "4 1.0 -450.0 0.0 1.000000e-08 \n",
+ "5 1.0 0.0 0.0 1.000000e-08 \n",
+ "\n",
+ " PositiveActiveReserveQuantity NegativeActiveReservePrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e+01 \n",
+ "2 250.0 2.000000e-08 \n",
+ "3 600.0 3.000000e+00 \n",
+ "4 800.0 2.000000e-08 \n",
+ "5 200.0 2.000000e-08 \n",
+ "\n",
+ " NegativeActiveReserveQuantity PositiveActiveDeltaPrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e-09 \n",
+ "2 250.0 1.000000e-09 \n",
+ "3 600.0 1.000000e-09 \n",
+ "4 800.0 1.000000e-09 \n",
+ "5 200.0 1.000000e-09 \n",
+ "\n",
+ " NegativeActiveDeltaPrice PositiveLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 1.000000e-09 0.000001 \n",
+ "2 1.000000e-09 0.000001 \n",
+ "3 1.000000e-09 10.000000 \n",
+ "4 1.000000e-09 0.000001 \n",
+ "5 1.000000e-09 0.000001 \n",
+ "\n",
+ " PositiveLoadFollowReserveQuantity NegativeLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 250.0 0.000001 \n",
+ "2 250.0 0.000001 \n",
+ "3 100.0 10.000000 \n",
+ "4 800.0 0.000001 \n",
+ "5 200.0 0.000001 \n",
+ "\n",
+ " NegativeLoadFollowReserveQuantity CommitKey InitialState MinUp \\\n",
+ "gen \n",
+ "1 250.0 1.0 inf 1.0 \n",
+ "2 250.0 1.0 inf 3.0 \n",
+ "3 250.0 1.0 inf 1.0 \n",
+ "4 800.0 2.0 inf 1.0 \n",
+ "5 200.0 2.0 inf 1.0 \n",
+ "\n",
+ " MinDown \n",
+ "gen \n",
+ "1 1.0 \n",
+ "2 1.0 \n",
+ "3 1.0 \n",
+ "4 1.0 \n",
+ "5 1.0 "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " GEN_BUS | \n",
+ " PG | \n",
+ " QG | \n",
+ " QMAX | \n",
+ " QMIN | \n",
+ " VG | \n",
+ " MBASE | \n",
+ " GEN_STATUS | \n",
+ " PMAX | \n",
+ " PMIN | \n",
+ " ... | \n",
+ " PC2 | \n",
+ " QC1MIN | \n",
+ " QC1MAX | \n",
+ " QC2MIN | \n",
+ " QC2MAX | \n",
+ " RAMP_AGC | \n",
+ " RAMP_10 | \n",
+ " RAMP_30 | \n",
+ " RAMP_Q | \n",
+ " APF | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 2.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 50.0 | \n",
+ " -50.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 600.0 | \n",
+ " 600.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 3.0 | \n",
+ " -450.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " -450.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 500.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 50.0 | \n",
+ " -50.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 200.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
5 rows × 21 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " GEN_BUS PG QG QMAX QMIN VG MBASE GEN_STATUS PMAX PMIN \\\n",
+ "gen \n",
+ "1 1.0 125.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "2 1.0 125.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "3 2.0 200.0 0.0 50.0 -50.0 1.0 100.0 1.0 500.0 0.0 \n",
+ "4 3.0 -450.0 0.0 0.0 0.0 1.0 100.0 1.0 0.0 -450.0 \n",
+ "5 2.0 0.0 0.0 50.0 -50.0 1.0 100.0 1.0 100.0 0.0 \n",
+ "\n",
+ " ... PC2 QC1MIN QC1MAX QC2MIN QC2MAX RAMP_AGC RAMP_10 RAMP_30 \\\n",
+ "gen ... \n",
+ "1 ... 0.0 0.0 0.0 0.0 0.0 0.0 250.0 250.0 \n",
+ "2 ... 0.0 0.0 0.0 0.0 0.0 0.0 250.0 250.0 \n",
+ "3 ... 0.0 0.0 0.0 0.0 0.0 0.0 600.0 600.0 \n",
+ "4 ... 0.0 0.0 0.0 0.0 0.0 0.0 500.0 500.0 \n",
+ "5 ... 0.0 0.0 0.0 0.0 0.0 0.0 200.0 200.0 \n",
+ "\n",
+ " RAMP_Q APF \n",
+ "gen \n",
+ "1 0.0 0.0 \n",
+ "2 0.0 0.0 \n",
+ "3 0.0 0.0 \n",
+ "4 0.0 0.0 \n",
+ "5 0.0 0.0 \n",
+ "\n",
+ "[5 rows x 21 columns]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# NOTE:\n",
+ "# 1. iwind, the wind index in cf.gen, can be singleton or 2D array of (n_wind, 1)\n",
+ "# 2. mpc and xgd are updated with wind generators added\n",
+ "[iwind, mpc, xgd] = m.addwind(\"ex_wind_uc\", cf.to_mpc(), xgdf.to_xgd(), nout=3)\n",
+ "xgdf = xGenDataTableFrames(data=xgd)\n",
+ "cf = CaseFrames(mpc)\n",
+ "\n",
+ "display(xgdf)\n",
+ "display(cf.gen)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "126c9a34",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " CommitSched | \n",
+ " InitialPg | \n",
+ " RampWearCostCoeff | \n",
+ " PositiveActiveReservePrice | \n",
+ " PositiveActiveReserveQuantity | \n",
+ " NegativeActiveReservePrice | \n",
+ " NegativeActiveReserveQuantity | \n",
+ " PositiveActiveDeltaPrice | \n",
+ " NegativeActiveDeltaPrice | \n",
+ " PositiveLoadFollowReservePrice | \n",
+ " PositiveLoadFollowReserveQuantity | \n",
+ " NegativeLoadFollowReservePrice | \n",
+ " NegativeLoadFollowReserveQuantity | \n",
+ " CommitKey | \n",
+ " InitialState | \n",
+ " MinUp | \n",
+ " MinDown | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 5.000000e+00 | \n",
+ " 250.0 | \n",
+ " 1.000000e+01 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 250.0 | \n",
+ " 2.000000e-08 | \n",
+ " 250.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 0.000001 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 3.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 1.500000e+00 | \n",
+ " 600.0 | \n",
+ " 3.000000e+00 | \n",
+ " 600.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 10.000000 | \n",
+ " 100.0 | \n",
+ " 10.000000 | \n",
+ " 250.0 | \n",
+ " 1.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 1.0 | \n",
+ " -450.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 800.0 | \n",
+ " 2.000000e-08 | \n",
+ " 800.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ " 0.000001 | \n",
+ " 800.0 | \n",
+ " 2.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 200.0 | \n",
+ " 2.000000e-08 | \n",
+ " 200.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 200.0 | \n",
+ " 0.000001 | \n",
+ " 200.0 | \n",
+ " 2.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.000000e-08 | \n",
+ " 160.0 | \n",
+ " 2.000000e-08 | \n",
+ " 160.0 | \n",
+ " 1.000000e-09 | \n",
+ " 1.000000e-09 | \n",
+ " 0.000001 | \n",
+ " 160.0 | \n",
+ " 0.000001 | \n",
+ " 160.0 | \n",
+ " 2.0 | \n",
+ " inf | \n",
+ " 1.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " CommitSched InitialPg RampWearCostCoeff PositiveActiveReservePrice \\\n",
+ "gen \n",
+ "1 1.0 125.0 0.0 5.000000e+00 \n",
+ "2 1.0 125.0 0.0 1.000000e-08 \n",
+ "3 1.0 200.0 0.0 1.500000e+00 \n",
+ "4 1.0 -450.0 0.0 1.000000e-08 \n",
+ "5 1.0 0.0 0.0 1.000000e-08 \n",
+ "6 1.0 0.0 0.0 1.000000e-08 \n",
+ "\n",
+ " PositiveActiveReserveQuantity NegativeActiveReservePrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e+01 \n",
+ "2 250.0 2.000000e-08 \n",
+ "3 600.0 3.000000e+00 \n",
+ "4 800.0 2.000000e-08 \n",
+ "5 200.0 2.000000e-08 \n",
+ "6 160.0 2.000000e-08 \n",
+ "\n",
+ " NegativeActiveReserveQuantity PositiveActiveDeltaPrice \\\n",
+ "gen \n",
+ "1 250.0 1.000000e-09 \n",
+ "2 250.0 1.000000e-09 \n",
+ "3 600.0 1.000000e-09 \n",
+ "4 800.0 1.000000e-09 \n",
+ "5 200.0 1.000000e-09 \n",
+ "6 160.0 1.000000e-09 \n",
+ "\n",
+ " NegativeActiveDeltaPrice PositiveLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 1.000000e-09 0.000001 \n",
+ "2 1.000000e-09 0.000001 \n",
+ "3 1.000000e-09 10.000000 \n",
+ "4 1.000000e-09 0.000001 \n",
+ "5 1.000000e-09 0.000001 \n",
+ "6 1.000000e-09 0.000001 \n",
+ "\n",
+ " PositiveLoadFollowReserveQuantity NegativeLoadFollowReservePrice \\\n",
+ "gen \n",
+ "1 250.0 0.000001 \n",
+ "2 250.0 0.000001 \n",
+ "3 100.0 10.000000 \n",
+ "4 800.0 0.000001 \n",
+ "5 200.0 0.000001 \n",
+ "6 160.0 0.000001 \n",
+ "\n",
+ " NegativeLoadFollowReserveQuantity CommitKey InitialState MinUp \\\n",
+ "gen \n",
+ "1 250.0 1.0 inf 1.0 \n",
+ "2 250.0 1.0 inf 3.0 \n",
+ "3 250.0 1.0 inf 1.0 \n",
+ "4 800.0 2.0 inf 1.0 \n",
+ "5 200.0 2.0 inf 1.0 \n",
+ "6 160.0 2.0 inf 1.0 \n",
+ "\n",
+ " MinDown \n",
+ "gen \n",
+ "1 1.0 \n",
+ "2 1.0 \n",
+ "3 1.0 \n",
+ "4 1.0 \n",
+ "5 1.0 \n",
+ "6 1.0 "
]
},
- "execution_count": 23,
"metadata": {},
- "output_type": "execute_result"
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " GEN_BUS | \n",
+ " PG | \n",
+ " QG | \n",
+ " QMAX | \n",
+ " QMIN | \n",
+ " VG | \n",
+ " MBASE | \n",
+ " GEN_STATUS | \n",
+ " PMAX | \n",
+ " PMIN | \n",
+ " ... | \n",
+ " PC2 | \n",
+ " QC1MIN | \n",
+ " QC1MAX | \n",
+ " QC2MIN | \n",
+ " QC2MAX | \n",
+ " RAMP_AGC | \n",
+ " RAMP_10 | \n",
+ " RAMP_30 | \n",
+ " RAMP_Q | \n",
+ " APF | \n",
+ "
\n",
+ " \n",
+ " | gen | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 1 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " 125.0 | \n",
+ " 0.0 | \n",
+ " 25.0 | \n",
+ " -25.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 250.0 | \n",
+ " 250.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 2.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 50.0 | \n",
+ " -50.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 600.0 | \n",
+ " 600.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 3.0 | \n",
+ " -450.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " -450.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 500.0 | \n",
+ " 500.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 2.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 50.0 | \n",
+ " -50.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 0.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 200.0 | \n",
+ " 200.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " 3.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.0 | \n",
+ " 100.0 | \n",
+ " 1.0 | \n",
+ " 80.0 | \n",
+ " -80.0 | \n",
+ " ... | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 20.0 | \n",
+ " 20.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
6 rows × 21 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " GEN_BUS PG QG QMAX QMIN VG MBASE GEN_STATUS PMAX PMIN \\\n",
+ "gen \n",
+ "1 1.0 125.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "2 1.0 125.0 0.0 25.0 -25.0 1.0 100.0 1.0 200.0 0.0 \n",
+ "3 2.0 200.0 0.0 50.0 -50.0 1.0 100.0 1.0 500.0 0.0 \n",
+ "4 3.0 -450.0 0.0 0.0 0.0 1.0 100.0 1.0 0.0 -450.0 \n",
+ "5 2.0 0.0 0.0 50.0 -50.0 1.0 100.0 1.0 100.0 0.0 \n",
+ "6 3.0 0.0 0.0 0.0 0.0 1.0 100.0 1.0 80.0 -80.0 \n",
+ "\n",
+ " ... PC2 QC1MIN QC1MAX QC2MIN QC2MAX RAMP_AGC RAMP_10 RAMP_30 \\\n",
+ "gen ... \n",
+ "1 ... 0.0 0.0 0.0 0.0 0.0 0.0 250.0 250.0 \n",
+ "2 ... 0.0 0.0 0.0 0.0 0.0 0.0 250.0 250.0 \n",
+ "3 ... 0.0 0.0 0.0 0.0 0.0 0.0 600.0 600.0 \n",
+ "4 ... 0.0 0.0 0.0 0.0 0.0 0.0 500.0 500.0 \n",
+ "5 ... 0.0 0.0 0.0 0.0 0.0 0.0 200.0 200.0 \n",
+ "6 ... 0.0 0.0 0.0 0.0 0.0 0.0 20.0 20.0 \n",
+ "\n",
+ " RAMP_Q APF \n",
+ "gen \n",
+ "1 0.0 0.0 \n",
+ "2 0.0 0.0 \n",
+ "3 0.0 0.0 \n",
+ "4 0.0 0.0 \n",
+ "5 0.0 0.0 \n",
+ "6 0.0 0.0 \n",
+ "\n",
+ "[6 rows x 21 columns]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
}
],
"source": [
+ "# NOTE:\n",
+ "# 1. iess, the ess index in cf.gen, can be singleton or 2D array of (n_ess, 1)\n",
+ "# 2. mpc and xgd are updated with ess added\n",
"[iess, mpc, xgd, sd] = m.addstorage(\"ex_storage\", mpc, xgd, nout=4)\n",
- "[iess, mpc, xgd, sd]"
+ "xgdf = xGenDataTableFrames(data=xgd)\n",
+ "cf = CaseFrames(mpc)\n",
+ "# TODO: convert sd into DataFrame\n",
+ "\n",
+ "display(xgdf)\n",
+ "display(cf.gen)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "676057c0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# TODO: convert profiles into DataFrame\n",
+ "profiles = m.getprofiles(\"ex_wind_profile_d\", iwind) # add wind profiles\n",
+ "profiles = m.getprofiles(\"ex_load_profile\", profiles) # update profiles with load\n",
+ "wind_profile, load_profile = profiles[0], profiles[1]"
]
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 19,
"id": "159b9137",
"metadata": {},
"outputs": [],
"source": [
- "m.exit()"
+ "# m.exit()"
]
},
{
diff --git a/tests/__init__.py b/tests/__init__.py
index 8d53ad0..e69de29 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,18 +0,0 @@
-import pandas as pd
-from pandas.testing import assert_frame_equal, assert_index_equal
-
-
-def assert_cf_equal(cf1, cf2):
- for attribute in cf1.attributes:
- df1 = getattr(cf1, attribute)
- df2 = getattr(cf2, attribute)
- if isinstance(df1, pd.DataFrame):
- assert_frame_equal(df1, df2)
- elif isinstance(df1, pd.Index):
- assert_index_equal(df1, df2)
- else:
- try:
- assert df1 == df2
- except ValueError as e:
- print(df1, df2)
- raise ValueError(e)
diff --git a/tests/results/case118/branch.csv b/tests/results/case118/branch.csv
index 7eb9ef8..e9fa9fd 100644
--- a/tests/results/case118/branch.csv
+++ b/tests/results/case118/branch.csv
@@ -1,4 +1,4 @@
-,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
+branch,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
1,1.0,2.0,0.0303,0.0999,0.0254,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
2,1.0,3.0,0.0129,0.0424,0.01082,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
3,4.0,5.0,0.00176,0.00798,0.0021,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
diff --git a/tests/results/case118/gen.csv b/tests/results/case118/gen.csv
index 9479aa1..05e45df 100644
--- a/tests/results/case118/gen.csv
+++ b/tests/results/case118/gen.csv
@@ -1,4 +1,4 @@
-,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
+gen,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
1,1.0,0.0,0.0,15.0,-5.0,0.955,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
2,4.0,0.0,0.0,300.0,-300.0,0.998,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
3,6.0,0.0,0.0,50.0,-13.0,0.99,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
diff --git a/tests/results/case118/gencost.csv b/tests/results/case118/gencost.csv
index 6e4f978..d9c09e9 100644
--- a/tests/results/case118/gencost.csv
+++ b/tests/results/case118/gencost.csv
@@ -1,4 +1,4 @@
-,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
+gen,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
1,2.0,0.0,0.0,3.0,0.01,40.0,0.0
2,2.0,0.0,0.0,3.0,0.01,40.0,0.0
3,2.0,0.0,0.0,3.0,0.01,40.0,0.0
diff --git a/tests/results/case118/schema/branch.csv b/tests/results/case118/schema/branch.csv
index 7eb9ef8..e9fa9fd 100644
--- a/tests/results/case118/schema/branch.csv
+++ b/tests/results/case118/schema/branch.csv
@@ -1,4 +1,4 @@
-,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
+branch,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
1,1.0,2.0,0.0303,0.0999,0.0254,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
2,1.0,3.0,0.0129,0.0424,0.01082,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
3,4.0,5.0,0.00176,0.00798,0.0021,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
diff --git a/tests/results/case118/schema/gen.csv b/tests/results/case118/schema/gen.csv
index 9479aa1..05e45df 100644
--- a/tests/results/case118/schema/gen.csv
+++ b/tests/results/case118/schema/gen.csv
@@ -1,4 +1,4 @@
-,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
+gen,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
1,1.0,0.0,0.0,15.0,-5.0,0.955,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
2,4.0,0.0,0.0,300.0,-300.0,0.998,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
3,6.0,0.0,0.0,50.0,-13.0,0.99,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
diff --git a/tests/results/case118/schema/gencost.csv b/tests/results/case118/schema/gencost.csv
index 6e4f978..d9c09e9 100644
--- a/tests/results/case118/schema/gencost.csv
+++ b/tests/results/case118/schema/gencost.csv
@@ -1,4 +1,4 @@
-,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
+gen,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
1,2.0,0.0,0.0,3.0,0.01,40.0,0.0
2,2.0,0.0,0.0,3.0,0.01,40.0,0.0
3,2.0,0.0,0.0,3.0,0.01,40.0,0.0
diff --git a/tests/results/case118_prefix_suffix/mpc.branch_test.csv b/tests/results/case118_prefix_suffix/mpc.branch_test.csv
index 7eb9ef8..e9fa9fd 100644
--- a/tests/results/case118_prefix_suffix/mpc.branch_test.csv
+++ b/tests/results/case118_prefix_suffix/mpc.branch_test.csv
@@ -1,4 +1,4 @@
-,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
+branch,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
1,1.0,2.0,0.0303,0.0999,0.0254,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
2,1.0,3.0,0.0129,0.0424,0.01082,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
3,4.0,5.0,0.00176,0.00798,0.0021,0.0,0.0,0.0,0.0,0.0,1.0,-360.0,360.0
diff --git a/tests/results/case118_prefix_suffix/mpc.gen_test.csv b/tests/results/case118_prefix_suffix/mpc.gen_test.csv
index 9479aa1..05e45df 100644
--- a/tests/results/case118_prefix_suffix/mpc.gen_test.csv
+++ b/tests/results/case118_prefix_suffix/mpc.gen_test.csv
@@ -1,4 +1,4 @@
-,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
+gen,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
1,1.0,0.0,0.0,15.0,-5.0,0.955,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
2,4.0,0.0,0.0,300.0,-300.0,0.998,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
3,6.0,0.0,0.0,50.0,-13.0,0.99,100.0,1.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
diff --git a/tests/results/case118_prefix_suffix/mpc.gencost_test.csv b/tests/results/case118_prefix_suffix/mpc.gencost_test.csv
index 6e4f978..d9c09e9 100644
--- a/tests/results/case118_prefix_suffix/mpc.gencost_test.csv
+++ b/tests/results/case118_prefix_suffix/mpc.gencost_test.csv
@@ -1,4 +1,4 @@
-,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
+gen,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
1,2.0,0.0,0.0,3.0,0.01,40.0,0.0
2,2.0,0.0,0.0,3.0,0.01,40.0,0.0
3,2.0,0.0,0.0,3.0,0.01,40.0,0.0
diff --git a/tests/results/case9/branch.csv b/tests/results/case9/branch.csv
index de5dab1..91f24da 100644
--- a/tests/results/case9/branch.csv
+++ b/tests/results/case9/branch.csv
@@ -1,4 +1,4 @@
-,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
+branch,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
1,1.0,4.0,0.0,0.0576,0.0,250.0,250.0,250.0,0.0,0.0,1.0,-360.0,360.0
2,4.0,5.0,0.017,0.092,0.158,250.0,250.0,250.0,0.0,0.0,1.0,-360.0,360.0
3,5.0,6.0,0.039,0.17,0.358,150.0,150.0,150.0,0.0,0.0,1.0,-360.0,360.0
diff --git a/tests/results/case9/bus.csv b/tests/results/case9/bus.csv
index 28052e6..f7a71a2 100644
--- a/tests/results/case9/bus.csv
+++ b/tests/results/case9/bus.csv
@@ -1,4 +1,4 @@
-,BUS_I,BUS_TYPE,PD,QD,GS,BS,BUS_AREA,VM,VA,BASE_KV,ZONE,VMAX,VMIN
+bus,BUS_I,BUS_TYPE,PD,QD,GS,BS,BUS_AREA,VM,VA,BASE_KV,ZONE,VMAX,VMIN
1,1.0,3.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
2,2.0,2.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
3,3.0,2.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
diff --git a/tests/results/case9/gen.csv b/tests/results/case9/gen.csv
index cfd26c4..311596a 100644
--- a/tests/results/case9/gen.csv
+++ b/tests/results/case9/gen.csv
@@ -1,4 +1,4 @@
-,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
+gen,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
1,1.0,72.3,27.03,300.0,-300.0,1.04,100.0,1.0,250.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
2,2.0,163.0,6.54,300.0,-300.0,1.025,100.0,1.0,300.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
3,3.0,85.0,-10.95,300.0,-300.0,1.025,100.0,1.0,270.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
diff --git a/tests/results/case9/gencost.csv b/tests/results/case9/gencost.csv
index b618f70..9458641 100644
--- a/tests/results/case9/gencost.csv
+++ b/tests/results/case9/gencost.csv
@@ -1,4 +1,4 @@
-,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
+gen,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
1,2.0,1500.0,0.0,3.0,0.11,5.0,150.0
2,2.0,2000.0,0.0,3.0,0.085,1.2,600.0
3,2.0,3000.0,0.0,3.0,0.1225,1.0,335.0
diff --git a/tests/results/case9/schema/branch.csv b/tests/results/case9/schema/branch.csv
index de5dab1..91f24da 100644
--- a/tests/results/case9/schema/branch.csv
+++ b/tests/results/case9/schema/branch.csv
@@ -1,4 +1,4 @@
-,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
+branch,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
1,1.0,4.0,0.0,0.0576,0.0,250.0,250.0,250.0,0.0,0.0,1.0,-360.0,360.0
2,4.0,5.0,0.017,0.092,0.158,250.0,250.0,250.0,0.0,0.0,1.0,-360.0,360.0
3,5.0,6.0,0.039,0.17,0.358,150.0,150.0,150.0,0.0,0.0,1.0,-360.0,360.0
diff --git a/tests/results/case9/schema/bus.csv b/tests/results/case9/schema/bus.csv
index 28052e6..f7a71a2 100644
--- a/tests/results/case9/schema/bus.csv
+++ b/tests/results/case9/schema/bus.csv
@@ -1,4 +1,4 @@
-,BUS_I,BUS_TYPE,PD,QD,GS,BS,BUS_AREA,VM,VA,BASE_KV,ZONE,VMAX,VMIN
+bus,BUS_I,BUS_TYPE,PD,QD,GS,BS,BUS_AREA,VM,VA,BASE_KV,ZONE,VMAX,VMIN
1,1.0,3.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
2,2.0,2.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
3,3.0,2.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
diff --git a/tests/results/case9/schema/gen.csv b/tests/results/case9/schema/gen.csv
index cfd26c4..311596a 100644
--- a/tests/results/case9/schema/gen.csv
+++ b/tests/results/case9/schema/gen.csv
@@ -1,4 +1,4 @@
-,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
+gen,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
1,1.0,72.3,27.03,300.0,-300.0,1.04,100.0,1.0,250.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
2,2.0,163.0,6.54,300.0,-300.0,1.025,100.0,1.0,300.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
3,3.0,85.0,-10.95,300.0,-300.0,1.025,100.0,1.0,270.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
diff --git a/tests/results/case9/schema/gencost.csv b/tests/results/case9/schema/gencost.csv
index b618f70..9458641 100644
--- a/tests/results/case9/schema/gencost.csv
+++ b/tests/results/case9/schema/gencost.csv
@@ -1,4 +1,4 @@
-,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
+gen,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
1,2.0,1500.0,0.0,3.0,0.11,5.0,150.0
2,2.0,2000.0,0.0,3.0,0.085,1.2,600.0
3,2.0,3000.0,0.0,3.0,0.1225,1.0,335.0
diff --git a/tests/results/case9_prefix_suffix/mpc.branch_test.csv b/tests/results/case9_prefix_suffix/mpc.branch_test.csv
index de5dab1..91f24da 100644
--- a/tests/results/case9_prefix_suffix/mpc.branch_test.csv
+++ b/tests/results/case9_prefix_suffix/mpc.branch_test.csv
@@ -1,4 +1,4 @@
-,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
+branch,F_BUS,T_BUS,BR_R,BR_X,BR_B,RATE_A,RATE_B,RATE_C,TAP,SHIFT,BR_STATUS,ANGMIN,ANGMAX
1,1.0,4.0,0.0,0.0576,0.0,250.0,250.0,250.0,0.0,0.0,1.0,-360.0,360.0
2,4.0,5.0,0.017,0.092,0.158,250.0,250.0,250.0,0.0,0.0,1.0,-360.0,360.0
3,5.0,6.0,0.039,0.17,0.358,150.0,150.0,150.0,0.0,0.0,1.0,-360.0,360.0
diff --git a/tests/results/case9_prefix_suffix/mpc.bus_test.csv b/tests/results/case9_prefix_suffix/mpc.bus_test.csv
index 28052e6..f7a71a2 100644
--- a/tests/results/case9_prefix_suffix/mpc.bus_test.csv
+++ b/tests/results/case9_prefix_suffix/mpc.bus_test.csv
@@ -1,4 +1,4 @@
-,BUS_I,BUS_TYPE,PD,QD,GS,BS,BUS_AREA,VM,VA,BASE_KV,ZONE,VMAX,VMIN
+bus,BUS_I,BUS_TYPE,PD,QD,GS,BS,BUS_AREA,VM,VA,BASE_KV,ZONE,VMAX,VMIN
1,1.0,3.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
2,2.0,2.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
3,3.0,2.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,345.0,1.0,1.1,0.9
diff --git a/tests/results/case9_prefix_suffix/mpc.gen_test.csv b/tests/results/case9_prefix_suffix/mpc.gen_test.csv
index cfd26c4..311596a 100644
--- a/tests/results/case9_prefix_suffix/mpc.gen_test.csv
+++ b/tests/results/case9_prefix_suffix/mpc.gen_test.csv
@@ -1,4 +1,4 @@
-,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
+gen,GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN,PC1,PC2,QC1MIN,QC1MAX,QC2MIN,QC2MAX,RAMP_AGC,RAMP_10,RAMP_30,RAMP_Q,APF
1,1.0,72.3,27.03,300.0,-300.0,1.04,100.0,1.0,250.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
2,2.0,163.0,6.54,300.0,-300.0,1.025,100.0,1.0,300.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
3,3.0,85.0,-10.95,300.0,-300.0,1.025,100.0,1.0,270.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
diff --git a/tests/results/case9_prefix_suffix/mpc.gencost_test.csv b/tests/results/case9_prefix_suffix/mpc.gencost_test.csv
index b618f70..9458641 100644
--- a/tests/results/case9_prefix_suffix/mpc.gencost_test.csv
+++ b/tests/results/case9_prefix_suffix/mpc.gencost_test.csv
@@ -1,4 +1,4 @@
-,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
+gen,MODEL,STARTUP,SHUTDOWN,NCOST,C2,C1,C0
1,2.0,1500.0,0.0,3.0,0.11,5.0,150.0
2,2.0,2000.0,0.0,3.0,0.085,1.2,600.0
3,2.0,3000.0,0.0,3.0,0.1225,1.0,335.0
diff --git a/tests/test_core.py b/tests/test_core.py
index caa51f1..8b78a4d 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -6,8 +6,7 @@
from matpowercaseframes import CaseFrames
from matpowercaseframes.idx import BUS_I, BUS_TYPE
-
-from .__init__ import assert_cf_equal
+from matpowercaseframes.testing import assert_frames_struct_equal
"""
pytest -n auto -rA --lf -c pyproject.toml --cov-report term-missing --cov=matpowercaseframes tests/
@@ -292,9 +291,9 @@ def test_reset_index_and_infer_numpy_case9():
assert cf_reset.branch["F_BUS"].between(0, len(cf_reset.bus) - 1).all()
assert cf_reset.branch["T_BUS"].between(0, len(cf_reset.bus) - 1).all()
assert cf_reset.gen["GEN_BUS"].between(0, len(cf_reset.bus) - 1).all()
- assert_cf_equal(cf, cf_reset)
+ assert_frames_struct_equal(cf, cf_reset)
# reset multiple times should not change anything
cf_reset.reset_index()
cf_reset.reset_index()
- assert_cf_equal(cf, cf_reset)
+ assert_frames_struct_equal(cf, cf_reset)
diff --git a/tests/test_read_matpower_cases.py b/tests/test_read_matpower_cases.py
index 8f11a60..037db46 100644
--- a/tests/test_read_matpower_cases.py
+++ b/tests/test_read_matpower_cases.py
@@ -1,11 +1,11 @@
import warnings
+import numpy as np
import pandas as pd
from matpower import path_matpower, start_instance
-from matpowercaseframes import CaseFrames
-
-from .__init__ import assert_cf_equal
+from matpowercaseframes import CaseFrames, ReservesFrames, xGenDataTableFrames
+from matpowercaseframes.testing import assert_frames_struct_equal
"""
pytest -n auto -rA --cov-report term --cov=matpowercaseframes tests/
@@ -42,8 +42,8 @@ def test_case118():
m.exit()
- assert_cf_equal(cf, cf_lc)
- assert_cf_equal(cf, cf_mpc)
+ assert_frames_struct_equal(cf, cf_lc)
+ assert_frames_struct_equal(cf, cf_mpc)
def test_case_RTS_GMLC():
@@ -81,7 +81,7 @@ def test_case_RTS_GMLC():
assert cf.gencost.columns.equals(cols)
assert cf_lc.gencost.columns.equals(cols)
- assert_cf_equal(cf, cf_lc)
+ assert_frames_struct_equal(cf, cf_lc)
m.exit()
@@ -106,7 +106,7 @@ def test_read_without_ext():
CASE_NAME = "case9"
cf_no_ext = CaseFrames(CASE_NAME)
- assert_cf_equal(cf, cf_no_ext)
+ assert_frames_struct_equal(cf, cf_no_ext)
def test_read_allow_any_keys():
@@ -116,3 +116,103 @@ def test_read_allow_any_keys():
cf = CaseFrames(CASE_NAME, allow_any_keys=True)
assert "load" in cf.attributes
+
+
+def test_read_case_reserve():
+ m = start_instance()
+ CASE_NAME = "data/ex_case3a.m"
+ cf = CaseFrames(CASE_NAME, load_case_engine=m)
+
+ assert "reserves" in cf.attributes
+ assert hasattr(cf, "reserves")
+
+ assert isinstance(cf.reserves, ReservesFrames)
+
+ assert "zones" in cf.reserves.attributes
+ assert isinstance(cf.reserves.zones, pd.DataFrame)
+ assert cf.reserves.zones.index.name == "zone"
+ assert cf.reserves.zones.columns.name == "gen"
+ assert cf.reserves.zones.shape == (cf.reserves.req.shape[0], cf.gen.shape[0])
+
+ assert "req" in cf.reserves.attributes
+ assert isinstance(cf.reserves.req, pd.DataFrame)
+ assert cf.reserves.req.index.name == "zone"
+ assert "PREQ" in cf.reserves.req.columns
+
+ assert isinstance(cf.reserves.cost, pd.DataFrame)
+ assert "C1" in cf.reserves.cost.columns
+ assert cf.reserves.cost.index.name == "gen"
+
+ assert isinstance(cf.reserves.qty, pd.DataFrame)
+ assert "PQTY" in cf.reserves.qty.columns
+ assert cf.reserves.qty.index.name == "gen"
+
+ for attr in ["zones", "req", "cost", "qty"]:
+ assert attr in cf.reserves.attributes
+
+ cf2 = CaseFrames(cf.to_mpc())
+ assert_frames_struct_equal(cf, cf2)
+
+ cf.reset_index()
+ assert cf.reserves.zones.index.name == "zone"
+ assert cf.reserves.zones.columns.name == "gen"
+ assert cf.reserves.zones.shape == (cf.reserves.req.shape[0], cf.gen.shape[0])
+ assert "C1" in cf.reserves.cost.columns
+ assert cf.reserves.cost.index.name == "gen"
+ for idx in cf.reserves.cost.index:
+ assert idx in cf.gen.index
+ assert "PQTY" in cf.reserves.qty.columns
+ assert cf.reserves.qty.index.equals(cf.reserves.cost.index)
+
+ m.exit()
+
+
+def test_read_xgd_table():
+ """Test reading and using xGenDataTableFrames."""
+ m = start_instance()
+
+ CASE_NAME = "data/ex_case3a.m"
+ cf = CaseFrames(CASE_NAME, load_case_engine=m)
+
+ # NOTE: loadgenericdata not yet support absolute path
+ xgd_table_file = "ex_xgd_uc.m"
+ xgdt = m.loadgenericdata(
+ xgd_table_file, "struct", {"colnames", "data"}, "xgd_table", cf.to_mpc()
+ )
+
+ xgdtf = xGenDataTableFrames(
+ data=xgdt.data, colnames=xgdt.colnames, index=cf.gen.index
+ )
+
+ assert hasattr(xgdtf, "table")
+ assert isinstance(xgdtf.table, pd.DataFrame)
+ assert xgdtf.table.index.equals(cf.gen.index)
+
+ assert xgdtf.colnames.shape[0] == 1 # Should be 2D with one row
+ assert xgdtf.data.shape == xgdtf.table.shape
+ assert len(xgdtf) == len(cf.gen)
+
+ first_col = xgdtf.table.columns[0]
+ assert xgdtf[first_col].equals(xgdtf.table[first_col])
+
+ cols_from_iter = list(xgdtf)
+ assert cols_from_iter == list(xgdtf.table.columns)
+
+ xdgt = xgdtf.to_xgdt()
+ assert "colnames" in xdgt
+ assert "data" in xdgt
+ np.testing.assert_array_equal(xdgt["colnames"], xgdtf.colnames)
+ np.testing.assert_array_equal(xdgt["data"], xgdtf.data)
+
+ xgd = m.loadxgendata(xgdtf.to_xgdt(), cf.to_mpc())
+ xgdf = xGenDataTableFrames(data=xgd)
+ xgdf_df = xgdf.to_df()
+ assert isinstance(xgdf_df, pd.DataFrame)
+
+ xdg = xgdf.to_xgd()
+ for k, v in xdg.items():
+ assert k in xgdf.colnames.flatten().tolist()
+ assert k in xgdf_df.columns
+ np.testing.assert_array_equal(v, xgdf[k].to_numpy().reshape(-1, 1))
+
+ m.exit()