From 552db37f1bc8dc91fa8f26436bed598a4b003f87 Mon Sep 17 00:00:00 2001 From: Muhammad Yasirroni Date: Sun, 30 Jun 2024 17:52:05 +0700 Subject: [PATCH] update workflow --- .github/workflows/build.yml | 13 +-- .pre-commit-config.yaml | 46 +++----- CONTRIBUTING.md | 20 ++++ matpowercaseframes/constants.py | 168 ++++++++++++++++++++--------- matpowercaseframes/core.py | 86 ++++++++------- matpowercaseframes/idx/gen.py | 50 ++++----- matpowercaseframes/reader.py | 33 +++--- matpowercaseframes/utils.py | 2 +- notebooks/load_case16am.ipynb | 5 +- notebooks/load_from_data.ipynb | 1 + notebooks/load_mfile_dclines.ipynb | 1 + pyproject.toml | 15 +-- requirements-dev.txt | 4 + setup.py | 4 +- tests/results/info.csv | 2 +- tests/test_core.py | 68 +++++++----- tests/test_read_matpower_cases.py | 9 +- tests/test_run_matpower_cases.py | 3 +- 18 files changed, 306 insertions(+), 224 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e6eecc8..fd969dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,22 +10,23 @@ jobs: linters: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: "3.11" + python-version: "3.12" cache: 'pip' cache-dependency-path: 'requirements-dev.txt' - - name: ruff + - name: Install Ruff run: | python -m pip install --upgrade pip - pip install ruff - ruff --format=github . + pip install ruff --uprade + - name: Run Ruff + run: ruff check . build: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] platform: [octave] os: [ubuntu-latest] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 622354a..c33521c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,44 +3,22 @@ default_stages: [commit] repos: # check yaml and end of file fixer - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-yaml - id: end-of-file-fixer exclude: LICENSE - # remove unused import and variable using autoflake - - repo: https://github.com/PyCQA/autoflake - rev: v1.4 - hooks: - - id: autoflake - args: [ - '--in-place', - '--remove-all-unused-imports', - '--ignore-init-module-imports', - '--remove-unused-variables', - '--' - ] - - # sort import using isort - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - args: [ - '--treat-comment-as-code', - '"# %%"', - '--' - ] - - # autofix using autopep8 - - repo: https://github.com/pre-commit/mirrors-autopep8 - rev: v2.0.2 - hooks: - - id: autopep8 - - # checkstyle using ruff - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.254' + # autofix using ruff + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.5.0 hooks: + # Run the linter. - id: ruff + types_or: [ python, pyi, jupyter ] + args: [ --fix ] + # Run the formatter. + - id: ruff-format + types_or: [ python, pyi, jupyter ] + # args: [ --verbose ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bb88acd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,20 @@ +# Contirbuting + +## Install in development mode + +```shell +pip install -e ."[dev]" +``` + +## Pytest + +```shell +pytest -n auto -rA --lf -c pyproject.toml --cov-report term-missing --cov=matpowercaseframes tests/ +``` + +## Pre-Commit + +```shell +pre-commit install +pre-commit run --all-files +``` diff --git a/matpowercaseframes/constants.py b/matpowercaseframes/constants.py index c829c4b..3d71a72 100644 --- a/matpowercaseframes/constants.py +++ b/matpowercaseframes/constants.py @@ -1,64 +1,128 @@ +BUS_TYPES = {"PQ": 1, "PV": 2, "REF": 3, "NONE": 4} + +COST_MODELS = {"PW_LINEAR": 1, "POLYNOMIAL": 2} + +ATTRIBUTES = ( + "version", + "baseMVA", + "bus", + "branch", + "gen", + "gencost", + "bus_name", + "branch_name", + "gen_name", + "dcline", + "dclinecost", +) + COLUMNS = { - 'bus': [ - 'BUS_I', 'BUS_TYPE', 'PD', 'QD', 'GS', 'BS', 'BUS_AREA', 'VM', 'VA', - 'BASE_KV', 'ZONE', 'VMAX', 'VMIN', 'LAM_P', 'LAM_Q', 'MU_VMAX', - 'MU_VMIN' + "bus": [ + "BUS_I", + "BUS_TYPE", + "PD", + "QD", + "GS", + "BS", + "BUS_AREA", + "VM", + "VA", + "BASE_KV", + "ZONE", + "VMAX", + "VMIN", + "LAM_P", + "LAM_Q", + "MU_VMAX", + "MU_VMIN", ], - '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', 'MU_PMAX', - 'MU_PMIN', 'MU_QMAX', 'MU_QMIN' + "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", + "MU_PMAX", + "MU_PMIN", + "MU_QMAX", + "MU_QMIN", ], - 'branch': [ - 'F_BUS', 'T_BUS', 'BR_R', 'BR_X', 'BR_B', 'RATE_A', 'RATE_B', - 'RATE_C', 'TAP', 'SHIFT', 'BR_STATUS', 'ANGMIN', 'ANGMAX', 'PF', 'QF', - 'PT', 'QT', 'MU_SF', 'MU_ST', 'MU_ANGMIN', 'MU_ANGMAX' + "branch": [ + "F_BUS", + "T_BUS", + "BR_R", + "BR_X", + "BR_B", + "RATE_A", + "RATE_B", + "RATE_C", + "TAP", + "SHIFT", + "BR_STATUS", + "ANGMIN", + "ANGMAX", + "PF", + "QF", + "PT", + "QT", + "MU_SF", + "MU_ST", + "MU_ANGMIN", + "MU_ANGMAX", ], - 'dcline': [ - 'F_BUS', 'T_BUS', 'BR_STATUS', 'PF', 'PT', 'QF', 'QT', 'VF', - 'VT', 'PMIN', 'PMAX', 'QMINF', 'QMAXF', 'QMINT', 'QMAXT', 'LOSS0', - 'LOSS1', 'MU_PMIN', 'MU_PMAX', 'MU_QMINF', 'MU_QMAXF', 'MU_QMINT', - 'MU_QMAXT', + "dcline": [ + "F_BUS", + "T_BUS", + "BR_STATUS", + "PF", + "PT", + "QF", + "QT", + "VF", + "VT", + "PMIN", + "PMAX", + "QMINF", + "QMAXF", + "QMINT", + "QMAXT", + "LOSS0", + "LOSS1", + "MU_PMIN", + "MU_PMAX", + "MU_QMINF", + "MU_QMAXF", + "MU_QMINT", + "MU_QMAXT", ], - 'if': { + "if": { # negative 'BRANCHIDX' defines opposite direction - 'map': ['IFNUM', 'BRANCHIDX'], - 'lims': ['IFNUM', 'LOWER', 'UPPER'] + "map": ["IFNUM", "BRANCHIDX"], + "lims": ["IFNUM", "LOWER", "UPPER"], }, - 'gencost': ['MODEL', 'STARTUP', 'SHUTDOWN', 'NCOST', 'COST'], - 'dclinecost': ['MODEL', 'STARTUP', 'SHUTDOWN', 'NCOST', 'COST'], - 'bus_name': ['BUS_NAME'], - 'branch_name': ['BRANCH_NAME'], - 'gen_name': ['GEN_NAME'] -} - -BUS_TYPES = { - 'PQ': 1, - 'PV': 2, - 'REF': 3, - 'NONE': 4 -} - -COST_MODELS = { - 'PW_LINEAR': 1, - 'POLYNOMIAL': 2 + "gencost": ["MODEL", "STARTUP", "SHUTDOWN", "NCOST", "COST"], + "dclinecost": ["MODEL", "STARTUP", "SHUTDOWN", "NCOST", "COST"], + "bus_name": ["BUS_NAME"], + "branch_name": ["BRANCH_NAME"], + "gen_name": ["GEN_NAME"], } -ATTRIBUTES = ( - 'version', - 'baseMVA', - 'bus', - 'branch', - 'gen', - 'gencost', - 'bus_name', - 'branch_name', - 'gen_name', - 'dcline', - 'dclinecost', -) - # TODO: # Support following attributes: # 'ct' diff --git a/matpowercaseframes/core.py b/matpowercaseframes/core.py index a5a3dc3..8ec5fe7 100644 --- a/matpowercaseframes/core.py +++ b/matpowercaseframes/core.py @@ -12,6 +12,7 @@ try: import matpower + MATPOWER_EXIST = True except ImportError: MATPOWER_EXIST = False @@ -71,7 +72,7 @@ def _get_path(path): if os.path.isfile(path): return path - path_added_m = path + '.m' + path_added_m = path + ".m" if os.path.isfile(path_added_m): return path_added_m @@ -107,7 +108,7 @@ def _read_matpower(self, filepath): if list_ is not None: if attribute == "version" or attribute == "baseMVA": setattr(self, attribute, list_[0][0]) - elif attribute in ['bus_name', 'branch_name', 'gen_name']: + elif attribute in ["bus_name", "branch_name", "gen_name"]: idx = pd.Index([name[0] for name in list_], name=attribute) setattr(self, attribute, idx) else: # bus, branch, gen, gencost, dcline, dclinecost @@ -118,7 +119,7 @@ def _read_matpower(self, filepath): self._attributes.append(attribute) def _read_oct2py_struct(self, struct): - self.name = '' + self.name = "" self._attributes = [] for attribute, list_ in struct.items(): @@ -128,7 +129,7 @@ def _read_oct2py_struct(self, struct): if attribute == "version" or attribute == "baseMVA": setattr(self, attribute, list_) - elif attribute in ['bus_name', 'branch_name', 'gen_name']: + elif attribute in ["bus_name", "branch_name", "gen_name"]: idx = pd.Index(list_, name=attribute) setattr(self, attribute, idx) else: # bus, branch, gen, gencost, dcline, dclinecost @@ -141,7 +142,7 @@ def _read_oct2py_struct(self, struct): return None def _read_numpy_struct(self, array): - self.name = '' + self.name = "" self._attributes = [] for attribute in array.dtype.names: if attribute not in ATTRIBUTES: @@ -150,7 +151,7 @@ def _read_numpy_struct(self, array): if attribute == "version" or attribute == "baseMVA": setattr(self, attribute, array[attribute].item().item()) - elif attribute in ['bus_name', 'branch_name', 'gen_name']: + elif attribute in ["bus_name", "branch_name", "gen_name"]: idx = pd.Index(array[attribute].item(), name=attribute) setattr(self, attribute, idx) else: # bus, branch, gen, gencost, dcline, dclinecost @@ -168,12 +169,15 @@ def _get_dataframe(attribute, data, n_cols): columns = columns[:n_cols] if n_cols > len(columns): if attribute != "gencost" and attribute != "dclinecost": - msg = (f"Number of columns in {attribute} ({n_cols}) are" - f" greater than the expected number.") + msg = ( + f"Number of columns in {attribute} ({n_cols}) are" + f" greater than the expected number." + ) raise IndexError(msg) - columns = (columns[:-1] - + ["{}_{}".format(columns[-1], i) - for i in range(n_cols - len(columns), -1, -1)]) + columns = columns[:-1] + [ + "{}_{}".format(columns[-1], i) + for i in range(n_cols - len(columns), -1, -1) + ] return pd.DataFrame(data, columns=columns) @property @@ -181,30 +185,34 @@ def attributes(self): return self._attributes def _update_index(self): - if 'bus_name' in self._attributes: + if "bus_name" in self._attributes: self.bus.set_index(self.bus_name, drop=False, inplace=True) else: - self.bus.set_index(pd.RangeIndex(1, len(self.bus.index) + 1), - drop=False, inplace=True) + self.bus.set_index( + pd.RangeIndex(1, len(self.bus.index) + 1), drop=False, inplace=True + ) - if 'branch_name' in self._attributes: + if "branch_name" in self._attributes: self.branch.set_index(self.branch_name, drop=False, inplace=True) else: - self.branch.set_index(pd.RangeIndex(1, len(self.branch.index) + 1), - drop=False, inplace=True) + self.branch.set_index( + pd.RangeIndex(1, len(self.branch.index) + 1), drop=False, inplace=True + ) - if 'gen_name' in self._attributes: + if "gen_name" in self._attributes: self.gen.set_index(self.gen_name, drop=False, inplace=True) try: self.gencost.set_index(self.gen_name, drop=False, inplace=True) except AttributeError: pass else: - self.gen.set_index(pd.RangeIndex(1, len(self.gen.index) + 1), - drop=False, inplace=True) + self.gen.set_index( + pd.RangeIndex(1, len(self.gen.index) + 1), drop=False, inplace=True + ) try: - self.gencost.set_index(pd.RangeIndex(1, len(self.gen.index) + 1), - drop=False, inplace=True) + self.gencost.set_index( + pd.RangeIndex(1, len(self.gen.index) + 1), drop=False, inplace=True + ) except AttributeError: pass @@ -221,20 +229,20 @@ def to_excel(self, path): with pd.ExcelWriter(path) as writer: pd.DataFrame( data={ - 'INFO': { - 'version': getattr(self, 'version', None), - 'baseMVA': getattr(self, 'baseMVA', None), + "INFO": { + "version": getattr(self, "version", None), + "baseMVA": getattr(self, "baseMVA", None), } } - ).to_excel(writer, sheet_name='info') + ).to_excel(writer, sheet_name="info") for attribute in self._attributes: if attribute == "version" or attribute == "baseMVA": # TODO: make self._attributes_non_pandas? continue - elif attribute in ['bus_name', 'branch_name', 'gen_name']: - pd.DataFrame(data={ - attribute: getattr(self, attribute) - }).to_excel(writer, sheet_name=attribute) + elif attribute in ["bus_name", "branch_name", "gen_name"]: + pd.DataFrame(data={attribute: getattr(self, attribute)}).to_excel( + writer, sheet_name=attribute + ) else: getattr(self, attribute).to_excel(writer, sheet_name=attribute) @@ -250,9 +258,9 @@ def to_csv(self, path): """ pd.DataFrame( data={ - 'INFO': { - 'version': getattr(self, 'version', None), - 'baseMVA': getattr(self, 'baseMVA', None), + "INFO": { + "version": getattr(self, "version", None), + "baseMVA": getattr(self, "baseMVA", None), } } ).to_csv(os.path.join(path, "info.csv")) @@ -261,10 +269,10 @@ def to_csv(self, path): if attribute == "version" or attribute == "baseMVA": # TODO: make self._attributes_non_pandas? continue - elif attribute in ['bus_name', 'branch_name', 'gen_name']: - pd.DataFrame(data={ - attribute: getattr(self, attribute) - }).to_csv(os.path.join(path, f"{attribute}.csv")) + elif attribute in ["bus_name", "branch_name", "gen_name"]: + pd.DataFrame(data={attribute: getattr(self, attribute)}).to_csv( + os.path.join(path, f"{attribute}.csv") + ) else: getattr(self, attribute).to_csv(os.path.join(path, f"{attribute}.csv")) @@ -275,8 +283,8 @@ def to_dict(self): The value of the data will be in str, numeric, and list. """ data = { - 'version': getattr(self, 'version', None), - 'baseMVA': getattr(self, 'baseMVA', None), + "version": getattr(self, "version", None), + "baseMVA": getattr(self, "baseMVA", None), } for attribute in self._attributes: if attribute == "version" or attribute == "baseMVA": diff --git a/matpowercaseframes/idx/gen.py b/matpowercaseframes/idx/gen.py index 8ac5436..48fd32d 100644 --- a/matpowercaseframes/idx/gen.py +++ b/matpowercaseframes/idx/gen.py @@ -41,34 +41,34 @@ # See https://matpower.org for more info. # define the indices -GEN_BUS = 0 # bus number -PG = 1 # Pg, real power output (MW) -QG = 2 # Qg, reactive power output (MVAr) -QMAX = 3 # Qmax, maximum reactive power output at Pmin (MVAr) -QMIN = 4 # Qmin, minimum reactive power output at Pmin (MVAr) -VG = 5 # Vg, voltage magnitude setpoint (p.u.) -MBASE = 6 # mBase, total MVA base of this machine, defaults to baseMVA -GEN_STATUS = 7 # status, 1 - machine in service, 0 - machine out of service -PMAX = 8 # Pmax, maximum real power output (MW) -PMIN = 9 # Pmin, minimum real power output (MW) -PC1 = 10 # Pc1, lower real power output of PQ capability curve (MW) -PC2 = 11 # Pc2, upper real power output of PQ capability curve (MW) -QC1MIN = 12 # Qc1min, minimum reactive power output at Pc1 (MVAr) -QC1MAX = 13 # Qc1max, maximum reactive power output at Pc1 (MVAr) -QC2MIN = 14 # Qc2min, minimum reactive power output at Pc2 (MVAr) -QC2MAX = 15 # Qc2max, maximum reactive power output at Pc2 (MVAr) -RAMP_AGC = 16 # ramp rate for load following/AGC (MW/min) -RAMP_10 = 17 # ramp rate for 10 minute reserves (MW) -RAMP_30 = 18 # ramp rate for 30 minute reserves (MW) -RAMP_Q = 19 # ramp rate for reactive power (2 sec timescale) (MVAr/min) -APF = 20 # area participation factor +GEN_BUS = 0 # bus number +PG = 1 # Pg, real power output (MW) +QG = 2 # Qg, reactive power output (MVAr) +QMAX = 3 # Qmax, maximum reactive power output at Pmin (MVAr) +QMIN = 4 # Qmin, minimum reactive power output at Pmin (MVAr) +VG = 5 # Vg, voltage magnitude setpoint (p.u.) +MBASE = 6 # mBase, total MVA base of this machine, defaults to baseMVA +GEN_STATUS = 7 # status, 1 - machine in service, 0 - machine out of service +PMAX = 8 # Pmax, maximum real power output (MW) +PMIN = 9 # Pmin, minimum real power output (MW) +PC1 = 10 # Pc1, lower real power output of PQ capability curve (MW) +PC2 = 11 # Pc2, upper real power output of PQ capability curve (MW) +QC1MIN = 12 # Qc1min, minimum reactive power output at Pc1 (MVAr) +QC1MAX = 13 # Qc1max, maximum reactive power output at Pc1 (MVAr) +QC2MIN = 14 # Qc2min, minimum reactive power output at Pc2 (MVAr) +QC2MAX = 15 # Qc2max, maximum reactive power output at Pc2 (MVAr) +RAMP_AGC = 16 # ramp rate for load following/AGC (MW/min) +RAMP_10 = 17 # ramp rate for 10 minute reserves (MW) +RAMP_30 = 18 # ramp rate for 30 minute reserves (MW) +RAMP_Q = 19 # ramp rate for reactive power (2 sec timescale) (MVAr/min) +APF = 20 # area participation factor # included in opf solution, not necessarily in input # assume objective function has units, u -MU_PMAX = 21 # Kuhn-Tucker multiplier on upper Pg limit (u/MW) -MU_PMIN = 22 # Kuhn-Tucker multiplier on lower Pg limit (u/MW) -MU_QMAX = 23 # Kuhn-Tucker multiplier on upper Qg limit (u/MVAr) -MU_QMIN = 24 # Kuhn-Tucker multiplier on lower Qg limit (u/MVAr) +MU_PMAX = 21 # Kuhn-Tucker multiplier on upper Pg limit (u/MW) +MU_PMIN = 22 # Kuhn-Tucker multiplier on lower Pg limit (u/MW) +MU_QMAX = 23 # Kuhn-Tucker multiplier on upper Qg limit (u/MVAr) +MU_QMIN = 24 # Kuhn-Tucker multiplier on lower Qg limit (u/MVAr) # Note: When a generator's PQ capability curve is not simply a box and the # upper Qg limit is binding, the multiplier on this constraint is split into diff --git a/matpowercaseframes/reader.py b/matpowercaseframes/reader.py index d636bff..cb764bb 100644 --- a/matpowercaseframes/reader.py +++ b/matpowercaseframes/reader.py @@ -10,13 +10,13 @@ def find_name(string): - return re.search( - 'function\\s*mpc\\s*=\\s*(?P.*?)\n', - string).groupdict()['data'] + return re.search("function\\s*mpc\\s*=\\s*(?P.*?)\n", string).groupdict()[ + "data" + ] def find_attributes(string): - pattern = 'mpc\\.(?P.*?)\\s*=\\s*' + pattern = "mpc\\.(?P.*?)\\s*=\\s*" return re.findall(pattern, string, re.DOTALL) @@ -28,24 +28,25 @@ def parse_file(attribute, string): else: _list = [] for line in match.splitlines(): - line = line.split('%')[0] - line = line.replace(';', '') + line = line.split("%")[0] + line = line.replace(";", "") if line.strip(): - if attribute in ['version', 'bus_name', 'branch_name', 'gen_name']: + if attribute in ["version", "bus_name", "branch_name", "gen_name"]: _list.append([line.strip().strip("'")]) else: - _list.append([int_else_float_except_string(s) - for s in line.strip().split()]) + _list.append( + [int_else_float_except_string(s) for s in line.strip().split()] + ) return _list def search_file(attribute, string): - if attribute in ['gen', 'gencost', 'bus', 'branch', 'dcline', 'dclinecost']: - pattern = r'mpc\.{}\s*=\s*\[[\n]?(?P.*?)[\n]?\];'.format(attribute) - elif attribute in ['version', 'baseMVA']: - pattern = r'mpc\.{}\s*=\s*(?P.*?);'.format(attribute) - elif attribute in ['bus_name', 'branch_name', 'gen_name']: - pattern = r'mpc\.{}\s*=\s*\{{[\n]?(?P.*?)[\n]?\}};'.format(attribute) + if attribute in ["gen", "gencost", "bus", "branch", "dcline", "dclinecost"]: + pattern = r"mpc\.{}\s*=\s*\[[\n]?(?P.*?)[\n]?\];".format(attribute) + elif attribute in ["version", "baseMVA"]: + pattern = r"mpc\.{}\s*=\s*(?P.*?);".format(attribute) + elif attribute in ["bus_name", "branch_name", "gen_name"]: + pattern = r"mpc\.{}\s*=\s*\{{[\n]?(?P.*?)[\n]?\}};".format(attribute) else: msg = f"Unknown mpc attribute name of {attribute}" raise NameError(msg) @@ -55,5 +56,5 @@ def search_file(attribute, string): if match is None: return None else: - match = match.groupdict().get('data', None) + match = match.groupdict().get("data", None) return match.strip("'").strip('"') diff --git a/matpowercaseframes/utils.py b/matpowercaseframes/utils.py index d561af4..e7761fd 100644 --- a/matpowercaseframes/utils.py +++ b/matpowercaseframes/utils.py @@ -1,6 +1,6 @@ def int_else_float_except_string(s): try: - f = float(s.replace(',', '.')) + f = float(s.replace(",", ".")) i = int(f) return i if i == f else f except ValueError: diff --git a/notebooks/load_case16am.ipynb b/notebooks/load_case16am.ipynb index bedb56b..ae7c83a 100644 --- a/notebooks/load_case16am.ipynb +++ b/notebooks/load_case16am.ipynb @@ -17,6 +17,7 @@ "outputs": [], "source": [ "from matpower import start_instance\n", + "\n", "from matpowercaseframes import CaseFrames" ] }, @@ -339,7 +340,7 @@ } ], "source": [ - "CASE_NAME = f\"case16am.m\"\n", + "CASE_NAME = \"case16am.m\"\n", "cf_16am = CaseFrames(CASE_NAME)\n", "cf_16am.branch" ] @@ -668,7 +669,7 @@ ], "source": [ "# NOTE: using loadcase cause data in float\n", - "CASE_NAME = f\"case16am.m\"\n", + "CASE_NAME = \"case16am.m\"\n", "cf_16am_lc = CaseFrames(CASE_NAME, load_case_engine=m)\n", "cf_16am_lc.branch" ] diff --git a/notebooks/load_from_data.ipynb b/notebooks/load_from_data.ipynb index 1411780..c24e43f 100644 --- a/notebooks/load_from_data.ipynb +++ b/notebooks/load_from_data.ipynb @@ -7,6 +7,7 @@ "outputs": [], "source": [ "from matpower import start_instance\n", + "\n", "from matpowercaseframes import CaseFrames" ] }, diff --git a/notebooks/load_mfile_dclines.ipynb b/notebooks/load_mfile_dclines.ipynb index 1a8f679..ff29dd5 100644 --- a/notebooks/load_mfile_dclines.ipynb +++ b/notebooks/load_mfile_dclines.ipynb @@ -7,6 +7,7 @@ "outputs": [], "source": [ "from matpower import path_matpower\n", + "\n", "from matpowercaseframes import CaseFrames" ] }, diff --git a/pyproject.toml b/pyproject.toml index 8229815..54c8408 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,18 +55,10 @@ Source = "https://github.com/UGM-EPSLab/matpowercaseframes" [tool.setuptools] packages = ["matpowercaseframes"] -[tool.autopep8] -in-place = true -recursive = true -verbose = true -max_line_length = 88 -ignore = [] -aggressive = 10 - [tool.ruff] fix = true line-length = 88 -select = [ +lint.select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes @@ -74,11 +66,12 @@ select = [ "C", # flake8-comprehensions "B", # flake8-bugbear ] -ignore = [ +lint.ignore = [ "B90", # support <3.10 + "E402", # temporarily due to nbqa did not support [tool.ruff.per-file-ignores] "E741", # ambiguous variable name "F403", # 'from module import *' used; unable to detect undefined names ] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "tests/test_core.py" = ["E501"] diff --git a/requirements-dev.txt b/requirements-dev.txt index c57f1e5..1ba0f0c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,6 +6,10 @@ openpyxl>=3.1.2 oct2py>=5.7.0 matpower>=7.1.0.2.1.8 +pre-commit>=2.21.0 +ruff>=0.5.0 +setuptools>=68.0.0 + pytest pytest-cov pytest-xdist diff --git a/setup.py b/setup.py index 635c72d..2668c7e 100644 --- a/setup.py +++ b/setup.py @@ -3,9 +3,9 @@ from setuptools import setup -PACKAGE_NAME = 'matpowercaseframes' +PACKAGE_NAME = "matpowercaseframes" current_path = os.path.abspath(os.path.dirname(__file__)) -version_line = open(os.path.join(current_path, PACKAGE_NAME, 'version.py'), "rt").read() +version_line = open(os.path.join(current_path, PACKAGE_NAME, "version.py"), "rt").read() m = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_line, re.M) __version__ = m.group(1) diff --git a/tests/results/info.csv b/tests/results/info.csv index cc9bdc2..ab71732 100644 --- a/tests/results/info.csv +++ b/tests/results/info.csv @@ -1,3 +1,3 @@ ,INFO -baseMVA,100 version,2 +baseMVA,100 diff --git a/tests/test_core.py b/tests/test_core.py index a0b3ee8..9081293 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,4 +1,3 @@ - import os import numpy as np @@ -11,9 +10,9 @@ pytest -n auto -rA --lf -c pyproject.toml --cov-report term-missing --cov=matpowercaseframes tests/ """ -CASE_NAME = 'case9.m' +CASE_NAME = "case9.m" CURDIR = os.path.realpath(os.path.dirname(__file__)) -CASE_DIR = os.path.join(os.path.dirname(CURDIR), 'data') +CASE_DIR = os.path.join(os.path.dirname(CURDIR), "data") CASE_PATH = os.path.join(CASE_DIR, CASE_NAME) @@ -23,10 +22,11 @@ def test_input_str_path(): def test_input_oct2py_io_Struct(): from matpower import start_instance + m = start_instance() # before run - mpc = m.loadcase('case9', verbose=False) + mpc = m.loadcase("case9", verbose=False) CaseFrames(mpc) # after run @@ -44,30 +44,34 @@ def test_input_type_error(): def test_read_value(): cf = CaseFrames(CASE_PATH) - assert cf.version == '2' + assert cf.version == "2" assert cf.baseMVA == 100 - narr_gencost = np.array([ - [2.000e+00, 1.500e+03, 0.000e+00, 3.000e+00, 1.100e-01, 5.000e+00, 1.500e+02], - [2.000e+00, 2.000e+03, 0.000e+00, 3.000e+00, 8.500e-02, 1.200e+00, 6.000e+02], - [2.000e+00, 3.000e+03, 0.000e+00, 3.000e+00, 1.225e-01, 1.000e+00, 3.350e+02], - ]) + narr_gencost = np.array( + [ + [2.000e00, 1.500e03, 0.000e00, 3.000e00, 1.100e-01, 5.000e00, 1.500e02], + [2.000e00, 2.000e03, 0.000e00, 3.000e00, 8.500e-02, 1.200e00, 6.000e02], + [2.000e00, 3.000e03, 0.000e00, 3.000e00, 1.225e-01, 1.000e00, 3.350e02], + ] + ) assert np.allclose(cf.gencost, narr_gencost) - narr_bus = np.array([ - [1, 3, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [2, 2, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [3, 2, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [4, 1, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [5, 1, 90, 30, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [6, 1, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [7, 1, 100, 35, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [8, 1, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], - [9, 1, 125, 50, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9] - ]) + narr_bus = np.array( + [ + [1, 3, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [2, 2, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [3, 2, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [4, 1, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [5, 1, 90, 30, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [6, 1, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [7, 1, 100, 35, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [8, 1, 0, 0, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + [9, 1, 125, 50, 0, 0, 1, 1, 0, 345, 1, 1.1, 0.9], + ] + ) assert np.allclose(cf.bus, narr_bus) - assert np.allclose(cf.bus['BUS_I'], narr_bus[:, BUS_I]) - assert np.allclose(cf.bus['BUS_TYPE'], narr_bus[:, BUS_TYPE]) + assert np.allclose(cf.bus["BUS_I"], narr_bus[:, BUS_I]) + assert np.allclose(cf.bus["BUS_TYPE"], narr_bus[:, BUS_TYPE]) # TODO: # Check all data @@ -75,15 +79,15 @@ def test_read_value(): def test_read_case_name(): cf = CaseFrames(CASE_PATH) - assert cf.name == 'case9' + assert cf.name == "case9" def test_get_attributes(): cf = CaseFrames(CASE_PATH) - assert cf.attributes == ['version', 'baseMVA', 'bus', 'gen', 'branch', 'gencost'] + assert cf.attributes == ["version", "baseMVA", "bus", "gen", "branch", "gencost"] with pytest.raises(AttributeError): - cf.attributes = ['try', 'replacing', 'attributes'] + cf.attributes = ["try", "replacing", "attributes"] # TODO: protect from attributes changed by user # cf.attributes[0] = 'try' @@ -93,13 +97,19 @@ def test_get_attributes(): def test_to_xlsx(): cf = CaseFrames(CASE_PATH) - cf.to_excel('tests/results/test_to_xlsx.xlsx') + cf.to_excel("tests/results/test_to_xlsx.xlsx") def test_to_csv(): cf = CaseFrames(CASE_PATH) - cf.to_csv('tests/results') + cf.to_csv("tests/results") + def test_to_dict(): cf = CaseFrames(CASE_PATH) - dict_object = cf.to_dict() + cf.to_dict() + + +def test_to_mpc(): + cf = CaseFrames(CASE_PATH) + cf.to_mpc() diff --git a/tests/test_read_matpower_cases.py b/tests/test_read_matpower_cases.py index 6de24ca..f86c905 100644 --- a/tests/test_read_matpower_cases.py +++ b/tests/test_read_matpower_cases.py @@ -8,17 +8,17 @@ def test_case9(): - CASE_NAME = 'case9.m' + CASE_NAME = "case9.m" CaseFrames(CASE_NAME) def test_case4_dist(): - CASE_NAME = 'case4_dist.m' + CASE_NAME = "case4_dist.m" CaseFrames(CASE_NAME) def test_case118(): - CASE_NAME = 'case118.m' + CASE_NAME = "case118.m" CaseFrames(CASE_NAME) @@ -33,6 +33,7 @@ def test_loadcase_case16am(): CaseFrames(CASE_NAME, load_case_engine=m) m.exit() + def test_read_without_ext(): - CASE_NAME = 'case9' + CASE_NAME = "case9" CaseFrames(CASE_NAME) diff --git a/tests/test_run_matpower_cases.py b/tests/test_run_matpower_cases.py index 9a0ff4f..6bf0b8c 100644 --- a/tests/test_run_matpower_cases.py +++ b/tests/test_run_matpower_cases.py @@ -1,4 +1,3 @@ -from matpower import path_matpower from matpower import start_instance from matpowercaseframes import CaseFrames @@ -9,7 +8,7 @@ def test_case9(): - CASE_NAME = 'case9.m' + CASE_NAME = "case9.m" cf = CaseFrames(CASE_NAME) mpc = cf.to_dict()