diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc90b28..274fbd3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: build: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12"] platform: [octave] os: [ubuntu-latest] @@ -47,13 +47,9 @@ jobs: if: matrix.platform == 'octave' uses: MATPOWER/action-install-octave-linux@v1 - - name: Install requirements - run: | - pip install -r requirements-dev.txt - - name: Install package run: | - pip install -e . + pip install -e ."[dev]" - name: Generate coverage report run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c70ce68..27c23df 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,7 +7,7 @@ jobs: build: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] platform: [octave] os: [ubuntu-latest] @@ -28,13 +28,9 @@ jobs: if: matrix.platform == 'octave' uses: MATPOWER/action-install-octave-linux@v1 - - name: Install requirements - run: | - pip install -r requirements-dev.txt - - name: Install package run: | - pip install -e . + pip install -e ."[dev]" - name: Generate coverage report run: | @@ -49,10 +45,10 @@ jobs: - name: Clone this repository uses: actions/checkout@v3 - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.12 cache: 'pip' cache-dependency-path: 'requirements-dev.txt' diff --git a/matpowercaseframes/core.py b/matpowercaseframes/core.py index 8ec5fe7..6998723 100644 --- a/matpowercaseframes/core.py +++ b/matpowercaseframes/core.py @@ -52,6 +52,7 @@ def __init__(self, data, update_index=True, load_case_engine=None): elif isinstance(data, np.ndarray): # TYPE: structured NumPy array # TODO: also support from.mat file via scipy.io + # TODO: when is the input from numpy array? if data.dtype.names is None: message = f"Source is {type(data)} but not structured NumPy array." raise TypeError(message) @@ -165,20 +166,20 @@ def _read_numpy_struct(self, array): @staticmethod def _get_dataframe(attribute, data, n_cols): # NOTE: .get('key') instead of ['key'] to default range - columns = COLUMNS.get(attribute, list(range(0, n_cols))) + columns = COLUMNS.get(attribute, list(range(n_cols))) columns = columns[:n_cols] if n_cols > len(columns): - if attribute != "gencost" and attribute != "dclinecost": + if attribute not in ("gencost", "dclinecost"): msg = ( - f"Number of columns in {attribute} ({n_cols}) are" - f" greater than the expected number." + f"Number of columns in {attribute} ({n_cols}) is greater" + f" than the expected number." ) raise IndexError(msg) columns = columns[:-1] + [ "{}_{}".format(columns[-1], i) for i in range(n_cols - len(columns), -1, -1) ] - return pd.DataFrame(data, columns=columns) + return pd.DataFrame(data, columns=columns).convert_dtypes() @property def attributes(self): diff --git a/notebooks/compare_load.ipynb b/notebooks/compare_load.ipynb new file mode 100644 index 0000000..30ee787 --- /dev/null +++ b/notebooks/compare_load.ipynb @@ -0,0 +1,97 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from matpower import start_instance\n", + "\n", + "from matpowercaseframes import CaseFrames\n", + "\n", + "m = start_instance()\n", + "\n", + "CASE_NAME = \"case9\"\n", + "\n", + "mpc = m.loadcase(CASE_NAME, verbose=False)\n", + "cf_mpc = CaseFrames(mpc) # _read_oct2py_struct\n", + "cf_parse = CaseFrames(CASE_NAME) # _read_matpower" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "version: True\n", + "baseMVA: True\n", + "True\n", + "bus: False\n", + "True\n", + "gen: False\n", + "True\n", + "branch: False\n", + "True\n", + "gencost: False\n" + ] + } + ], + "source": [ + "for attribute in cf_mpc.attributes:\n", + " df_mpc = getattr(cf_mpc, attribute)\n", + " df_parse = getattr(cf_parse, attribute)\n", + "\n", + " if isinstance(df_mpc, pd.DataFrame):\n", + " print(df_mpc.columns.equals(df_parse.columns))\n", + " print(f\"{attribute}: {df_mpc.equals(df_parse)}\")\n", + " else:\n", + " print(f\"{attribute}: {df_mpc == df_parse}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/pyproject.toml b/pyproject.toml index 54c8408..092a7c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Mathematics", "License :: OSI Approved :: MIT License", @@ -38,13 +39,16 @@ matpower = [ "matpower>=7.1.0.2.1.4", ] dev = [ + "matpower>=7.1.0.2.1.4", + "numpy>=1.21.5", "oct2py>=5.5.1", # latest support for 3.7 "openpyxl>=3.1.2", - "matpower>=7.1.0.2.1.4", + "pandas>=1.2.0", + "pre-commit>=2.21.0", "pytest>=7.2.0", "pytest-cov>=4.0.0", "pytest-xdist>=3.1.0", - "pre-commit>=2.21.0", + "ruff>=0.6.4", ] [project.urls] diff --git a/requirements-dev.txt b/requirements-dev.txt index 1ba0f0c..8df6db0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,15 +1,15 @@ -pandas>=1.2.0 -numpy>=1.21.5 +pandas==2.2.2 +numpy==2.1.1 -openpyxl>=3.1.2 +openpyxl==3.1.5 -oct2py>=5.7.0 -matpower>=7.1.0.2.1.8 +oct2py==5.7.2 +matpower==8.0.0.2.1.8 -pre-commit>=2.21.0 -ruff>=0.5.0 -setuptools>=68.0.0 +pre-commit==3.8.0 +ruff==0.6.4 +setuptools==74.1.2 -pytest -pytest-cov -pytest-xdist +pytest==8.3.2 +pytest-cov==5.0.0 +pytest-xdist==3.6.1 diff --git a/tests/test_core.py b/tests/test_core.py index 9081293..82c603b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,6 +1,7 @@ import os import numpy as np +import pandas as pd import pytest from matpowercaseframes import CaseFrames @@ -26,12 +27,23 @@ def test_input_oct2py_io_Struct(): m = start_instance() # before run - mpc = m.loadcase("case9", verbose=False) - CaseFrames(mpc) + mpc = m.loadcase(CASE_NAME, verbose=False) + cf_mpc = CaseFrames(mpc) # _read_oct2py_struct + cf_parse = CaseFrames(CASE_NAME) # _read_matpower + + for attribute in cf_mpc.attributes: + df_mpc = getattr(cf_mpc, attribute) + df_parse = getattr(cf_parse, attribute) + + if isinstance(df_mpc, pd.DataFrame): + assert df_mpc.columns.equals(df_parse.columns) + assert df_mpc.equals(df_parse) + else: + assert df_mpc == df_parse # after run mpc = m.runpf(mpc, verbose=False) - CaseFrames(mpc) + _ = CaseFrames(mpc) m.exit()