diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82a3aac..64ca191 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,14 +18,14 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install ditto-reader run: pip install .[dev] - name: Run pre-commit to confirm linting and formatting # https://github.com/pre-commit/action - uses: pre-commit/action@v3.0.0 + uses: pre-commit/action@v3.0.1 with: extra_args: --all-files - name: Run unit tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0ddb9b0..a813ee4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-ast - id: check-added-large-files @@ -20,20 +20,14 @@ repos: - id: debug-statements # - id: detect-aws-credentials - id: detect-private-key - # - repo: meta - # hooks: - # - id: check-useless-excludes # Ensure the exclude syntax is correct - # - id: check-hooks-apply # Fails if a hook doesn't apply to any file - # Run the Ruff linter + # https://docs.astral.sh/ruff/integrations/#pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.4.2 hooks: + # Run the linter - id: ruff - args: [--fix, --exit-non-zero-on-fix] + args: [--fix, --exit-non-zero-on-fix, --output-format=full] types_or: [python, pyi, jupyter] - # Run the Ruff formatter - # https://docs.astral.sh/ruff/integrations/#pre-commit - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 - hooks: + # Run the formatter - id: ruff-format + types_or: [python, pyi, jupyter] diff --git a/CHANGELOG.md b/CHANGELOG.md index fcefb44..f1f810a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # URBANopt DiTTo Reader CHANGELOG +# Version 0.6.4 + +### Exciting New Features 🎉 +* Improve error handling for REopt by @vtnate in https://github.com/urbanopt/urbanopt-ditto-reader/pull/63 + + +**Full Changelog**: https://github.com/urbanopt/urbanopt-ditto-reader/compare/v0.6.3...v0.6.4 + # Version 0.6.0 Date Range 12/15/2022 - 12/05/2023 ### Future changes will be published using Github automated formatting at the release itself. Those changes are copied here. diff --git a/pyproject.toml b/pyproject.toml index 7014cd9..8b1883b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ name = "urbanopt-ditto-reader" dynamic = [ "readme", ] -version = "0.6.3" +version = "0.6.4" authors = [ { name="Tarek Elgindy", email="tarek.elgindy@nrel.gov" }, { name="Nathan Moore", email="nathan.moore@nrel.gov" }, @@ -34,14 +34,14 @@ classifiers = [ ] dependencies = [ "ditto.py[opendss] ~= 0.2.4", - "opendssdirect.py ~= 0.8", + "opendssdirect.py ~= 0.8.0", ] [project.optional-dependencies] dev = [ "pytest ~= 7.4", "pre-commit ~= 3.3", - "ruff ~= 0.1.6", + "ruff ~= 0.4.0", ] # https://setuptools.pypa.io/en/latest/userguide/package_discovery.html @@ -61,19 +61,22 @@ readme = {file = "README.md", content-type = "text/markdown"} minversion = "6.0" testpaths = "tests" -# https://github.com/charliermarsh/ruff +# https://docs.astral.sh/ruff/tutorial/#configuration [tool.ruff] fix = true # automatically fix problems if possible -select = ["RUF", "E", "F", "I", "UP", "N", "S", "BLE", "A", "C4", "T10", "ISC", "ICN", "PT", +line-length = 120 + +# https://docs.astral.sh/ruff/linter/#rule-selection +[tool.ruff.lint] +extend-select = ["RUF", "E", "F", "I", "UP", "N", "S", "BLE", "A", "C4", "T10", "ISC", "ICN", "PT", "Q", "SIM", "TID", "ARG", "DTZ", "PD", "PGH", "PLC", "PLE", "PLR", "PLW", "PIE", "COM"] # Enable these rules ignore = ["PLR0913", "PLR2004", "PLR0402", "COM812", "COM819", "SIM108", "ARG002", "ISC001"] # except for these specific errors -line-length = 120 # https://docs.astral.sh/ruff/settings/#format [tool.ruff.format] # quote-style = "double" -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "tests/*" = ["S101", "S607", "S603"] # assert statements are allowed in tests, and paths are safe "update_licenses.py" = ["SIM115", "A002", "ARG001"] "urbanopt_ditto_reader/reader/read.py" = ["PLR0912", "PLR0915"] # ignore complexity warnings in this file diff --git a/tests/urbanopt_ditto_reader/test_ditto_reader_cli.py b/tests/urbanopt_ditto_reader/test_ditto_reader_cli.py index 888d93b..d171861 100644 --- a/tests/urbanopt_ditto_reader/test_ditto_reader_cli.py +++ b/tests/urbanopt_ditto_reader/test_ditto_reader_cli.py @@ -48,6 +48,34 @@ def test_upgrade_transformers(capfd): # This text is printed by ditto.consistency.fix_undersized_transformers +def test_graceful_error_if_no_reopt(capfd): + reopt_optimization_file = ( + Path(__file__).parent.parent.parent + / "example" + / "run" + / "baseline_scenario" + / "1" + / "feature_reports" + / "feature_optimization.csv" + ) + assert reopt_optimization_file.exists() is False + subprocess.run( + [ + "ditto_reader_cli", + "run-opendss", + "--config", + "example_config.json", + "--upgrade", + "--reopt", + ], + cwd=examples_dir, + check=True, + ) + captured = capfd.readouterr() + assert "not found" in captured.out + # This text is printed by ditto.consistency.fix_undersized_transformers + + # REopt data for testing not present in this repo as of 2023-04-05 # def test_use_reopt(capfd): # subprocess.run( diff --git a/urbanopt_ditto_reader/reader/read.py b/urbanopt_ditto_reader/reader/read.py index 20347f0..a7fa375 100644 --- a/urbanopt_ditto_reader/reader/read.py +++ b/urbanopt_ditto_reader/reader/read.py @@ -39,9 +39,11 @@ OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************************** """ + import json import os from datetime import datetime +from pathlib import Path from ditto.models.base import Unicode from ditto.models.feeder_metadata import Feeder_metadata @@ -510,11 +512,15 @@ def parse_loads(self, model, **kwargs): load.nominal_voltage = model["urbanopt-feeder"].nominal_voltage # load the power draw of the buildings from the energy sim results - load_path = os.path.join(self.load_folder, id_value, "feature_reports") + load_path = Path(self.load_folder) / id_value / "feature_reports" load_multiplier = 1000 - if os.path.exists(load_path): # We've found the load data + if load_path.is_dir(): # We've found the load data if self.use_reopt: - rep_csv = os.path.join(load_path, "feature_optimization.csv") + rep_csv = load_path / "feature_optimization.csv" + if not rep_csv.is_file(): + print("feature_optimization.csv not found. Please run REopt post-processing first.") + # TODO: Get the test to read this text appropriately from inside the SystemExit() + raise SystemExit() report_mtx = self._read_csv(rep_csv) header_row = report_mtx.pop(0) load_col_i = header_row.index("REopt:Electricity:Load:Total(kw)") diff --git a/urbanopt_ditto_reader/urbanopt_ditto_reader.py b/urbanopt_ditto_reader/urbanopt_ditto_reader.py index 7b4438e..ba743e1 100644 --- a/urbanopt_ditto_reader/urbanopt_ditto_reader.py +++ b/urbanopt_ditto_reader/urbanopt_ditto_reader.py @@ -466,13 +466,13 @@ def run(self, master_file): # write the collected results into CSV files for element, result_values in voltage_df_dic.items(): - res_path = os.path.join(features_path, "%s.csv" % element.replace("_", "-")) + res_path = Path(features_path) / f"{element.replace('_', '-')}.csv" self._write_csv(result_values, res_path) for element, result_values in line_df_dic.items(): - res_path = os.path.join(lines_path, "%s.csv" % element.replace(":", "")) + res_path = Path(lines_path) / f"{element.replace(':', '')}.csv" self._write_csv(result_values, res_path) for element, result_values in transformer_df_dic.items(): - res_path = os.path.join(trans_path, "%s.csv" % element.replace(":", "")) + res_path = Path(trans_path) / f"{element.replace(':', '')}.csv" self._write_csv(result_values, res_path) @staticmethod