diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa7b288..0b4f481 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,4 +20,7 @@ jobs: run: pytest ./tests - name: Type checking with mypy - run: mypy building_energy_storage_simulation \ No newline at end of file + run: mypy building_energy_storage_simulation + + - name: Code style checking with ruff + run: ruff check . \ No newline at end of file diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..62134c2 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,12 @@ +# Same as Black. +line-length = 127 +# Assume Python 3.8 +target-version = "py38" +# See https://beta.ruff.rs/docs/rules/ +select = ["E", "F", "B", "UP", "C90", "RUF", "I", "N", "B", "SIM"] +# B028: Ignore explicit stacklevel` +# RUF013: Too many false positives (implicit optional) +ignore = ["B028", "RUF013"] + +[extend-per-file-ignores] +"__init__.py" = ["F401", "F403"] diff --git a/building_energy_storage_simulation/building_simulation.py b/building_energy_storage_simulation/building_simulation.py index fdf98f1..2b48a46 100644 --- a/building_energy_storage_simulation/building_simulation.py +++ b/building_energy_storage_simulation/building_simulation.py @@ -1,4 +1,4 @@ -from typing import Tuple, Iterable, Union +from typing import Iterable, Tuple, Union import numpy as np @@ -26,7 +26,8 @@ class BuildingSimulation: def __init__(self, electricity_load_profile: Iterable = load_profile('electricity_load_profile.csv', 'Load [kWh]'), solar_generation_profile: Iterable = load_profile('solar_generation_profile.csv', 'Generation [kWh]'), - electricity_price: Union[Iterable, float] = load_profile('electricity_price_profile.csv', 'Day Ahead Auction'), + electricity_price: Union[Iterable, float] = load_profile('electricity_price_profile.csv', + 'Day Ahead Auction'), battery_capacity: float = 100, max_battery_charge_per_timestep: float = 20 ): diff --git a/building_energy_storage_simulation/environment.py b/building_energy_storage_simulation/environment.py index df22ea4..d203796 100644 --- a/building_energy_storage_simulation/environment.py +++ b/building_energy_storage_simulation/environment.py @@ -1,9 +1,9 @@ -from typing import Tuple, Optional, Union, List, Iterable, Any -from collections.abc import MutableSequence +from typing import Any, Iterable, List, Optional, Tuple, Union import gymnasium as gym import numpy as np -from gymnasium.core import ActType, ObsType, RenderFrame +from gymnasium.core import RenderFrame + from building_energy_storage_simulation.building_simulation import BuildingSimulation diff --git a/building_energy_storage_simulation/utils.py b/building_energy_storage_simulation/utils.py index 9b641fd..f81b080 100644 --- a/building_energy_storage_simulation/utils.py +++ b/building_energy_storage_simulation/utils.py @@ -1,5 +1,5 @@ -import pkg_resources import pandas as pd +import pkg_resources def load_profile(filename: str, column_name: str) -> pd.Series: diff --git a/example_solutions/deep_reinforcement_learning/evaluate.py b/example_solutions/deep_reinforcement_learning/evaluate.py index 696fb74..c5ef480 100644 --- a/example_solutions/deep_reinforcement_learning/evaluate.py +++ b/example_solutions/deep_reinforcement_learning/evaluate.py @@ -5,9 +5,15 @@ from stable_baselines3.common.vec_env import DummyVecEnv, VecNormalize from building_energy_storage_simulation import BuildingSimulation, Environment -from example_solutions.deep_reinforcement_learning.train import RESULT_PATH, NUM_FORECAST_STEPS -from example_solutions.helper import read_data, TEST_INDEX_START, TEST_INDEX_END, BATTERY_POWER, BATTERY_CAPACITY, \ - plot_control_trajectory +from example_solutions.deep_reinforcement_learning.train import NUM_FORECAST_STEPS, RESULT_PATH +from example_solutions.helper import ( + BATTERY_CAPACITY, + BATTERY_POWER, + TEST_INDEX_END, + TEST_INDEX_START, + plot_control_trajectory, + read_data, +) from example_solutions.observation_wrapper import ObservationWrapper @@ -17,7 +23,7 @@ def evaluate(env, agent=None): done = False obs = env.reset() while not done: - if agent is None: + if agent is None: # noqa action = [[0]] else: action = [agent.predict(obs, deterministic=True)[0][0]] @@ -44,7 +50,7 @@ def evaluate(env, agent=None): # Plot evolution of reward during training try: plot_results(RESULT_PATH, x_axis='timesteps', task_name='title', num_timesteps=None) - except: + except: # noqa print('Training Reward Plot could not be created') load, price, generation = read_data() diff --git a/example_solutions/deep_reinforcement_learning/train.py b/example_solutions/deep_reinforcement_learning/train.py index 6591cd7..5746c2c 100644 --- a/example_solutions/deep_reinforcement_learning/train.py +++ b/example_solutions/deep_reinforcement_learning/train.py @@ -5,7 +5,7 @@ from stable_baselines3.common.vec_env import DummyVecEnv, VecNormalize from building_energy_storage_simulation import BuildingSimulation, Environment -from example_solutions.helper import read_data, TEST_INDEX_START, BATTERY_CAPACITY, BATTERY_POWER +from example_solutions.helper import BATTERY_CAPACITY, BATTERY_POWER, TEST_INDEX_START, read_data from example_solutions.observation_wrapper import ObservationWrapper NUM_FORECAST_STEPS = 8 @@ -41,6 +41,7 @@ # Train :-) model = SAC("MlpPolicy", env, verbose=1, gamma=0.95) model.learn(total_timesteps=200_000) - # Store the trained Model and environment stats (which are needed as we are standardizing the observations and reward using VecNormalize()) + # Store the trained Model and environment stats (which are needed as we are standardizing the observations and + # reward using VecNormalize()) model.save(RESULT_PATH + 'model') env.save(RESULT_PATH + 'env.pkl') \ No newline at end of file diff --git a/example_solutions/helper.py b/example_solutions/helper.py index 45929b6..b4e7b0f 100644 --- a/example_solutions/helper.py +++ b/example_solutions/helper.py @@ -1,9 +1,8 @@ from pathlib import Path - -import pandas as pd -import numpy as np from typing import Tuple +import numpy as np +import pandas as pd from matplotlib import pyplot as plt # Start and end Index of data used for testing diff --git a/example_solutions/model_predictive_control.py b/example_solutions/model_predictive_control.py index 6408fc0..7b18a81 100644 --- a/example_solutions/model_predictive_control.py +++ b/example_solutions/model_predictive_control.py @@ -1,9 +1,9 @@ -import pyomo.environ as pyo import numpy as np +import pyomo.environ as pyo +from helper import BATTERY_CAPACITY, BATTERY_POWER, TEST_INDEX_END, TEST_INDEX_START, plot_control_trajectory, read_data +from optimal_control_problem import build_optimization_problem from building_energy_storage_simulation import BuildingSimulation, Environment -from optimal_control_problem import build_optimization_problem -from helper import read_data, TEST_INDEX_END, TEST_INDEX_START, BATTERY_POWER, BATTERY_CAPACITY, plot_control_trajectory FORECAST_LENGTH = 24 @@ -43,7 +43,8 @@ def normalize_to_minus_one_to_one(x, min_value, max_value): optimization_problem = build_optimization_problem(residual_fixed_load=residual_load_forecast, price=price_forecast, - soc=soc / BATTERY_CAPACITY * 100, # Convert SOC due to different SOC definitions + # Convert SOC due to different SOC definitions + soc=soc / BATTERY_CAPACITY * 100, battery_capacity=BATTERY_CAPACITY, battery_power=BATTERY_POWER) solver.solve(optimization_problem, tee=True) diff --git a/example_solutions/optimal_control_problem.py b/example_solutions/optimal_control_problem.py index cdcb243..26730c3 100644 --- a/example_solutions/optimal_control_problem.py +++ b/example_solutions/optimal_control_problem.py @@ -1,7 +1,6 @@ -import pyomo.environ as pyo import numpy as np - -from helper import read_data, TEST_INDEX_END, TEST_INDEX_START, BATTERY_CAPACITY, BATTERY_POWER, plot_control_trajectory +import pyomo.environ as pyo +from helper import BATTERY_CAPACITY, BATTERY_POWER, TEST_INDEX_END, TEST_INDEX_START, plot_control_trajectory, read_data def build_optimization_problem(residual_fixed_load, price, soc, battery_power, battery_capacity, delta_time_hours=1): @@ -20,7 +19,7 @@ def build_optimization_problem(residual_fixed_load, price, soc, battery_power, b def obj_expression(m): # pyo.log to make the objective expression smooth and therefore solvable - return sum([price[i] * pyo.log(1 + pyo.exp((m.power[i] + residual_fixed_load[i]))) for i in time]) + return sum([price[i] * pyo.log(1 + pyo.exp(m.power[i] + residual_fixed_load[i])) for i in time]) m.OBJ = pyo.Objective(rule=obj_expression, sense=pyo.minimize) diff --git a/setup.py b/setup.py index bd5a94a..b27c90f 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,11 @@ from distutils.core import setup from pathlib import Path + this_directory = Path(__file__).parent long_description = (this_directory / "README.md").read_text() setup(name='building_energy_storage_simulation', - version='0.9.2', + version='0.9.3', description='A simulation of a building to optimize energy storage utilization.', long_description=long_description, long_description_content_type='text/markdown', @@ -31,7 +32,8 @@ "pytest", "mypy", "pandas-stubs", - "types-setuptools" + "types-setuptools", + "ruff" ] } ) diff --git a/tests/test_building_simulation.py b/tests/test_building_simulation.py index dd8fd15..367f13c 100644 --- a/tests/test_building_simulation.py +++ b/tests/test_building_simulation.py @@ -5,7 +5,7 @@ def test_energy_consumption_is_trimmed_to_0(): sim = BuildingSimulation(electricity_load_profile=[0], solar_generation_profile=[100], electricity_price=[1]) - # Don´t charge the battery, meaning don´t do anything with the 100kWh energy we gained from the solar system. + # Don't charge the battery, meaning don't do anything with the 100kWh energy we gained from the solar system. electricity_consumption, excess_energy = sim.simulate_one_step(0) # Still the consumption is 0, as we loose excess electricity which we do not use to charge the battery. assert electricity_consumption == 0 @@ -29,6 +29,6 @@ def test_simulation_scalar_electricity_price_converted_into_profile(electricity_ def test_simulation_throws_when_data_profiles_have_unequal_length(): with pytest.raises(Exception) as exception_info: - sim = BuildingSimulation(electricity_load_profile=[0, 0, 0], + BuildingSimulation(electricity_load_profile=[0, 0, 0], solar_generation_profile=[0, 0]) assert exception_info.errisinstance(ValueError) diff --git a/tests/test_environment.py b/tests/test_environment.py index bcb878d..86b9e83 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -1,8 +1,8 @@ -from building_energy_storage_simulation.building_simulation import BuildingSimulation +import numpy as np +import pytest from building_energy_storage_simulation import Environment -import pytest -import numpy as np +from building_energy_storage_simulation.building_simulation import BuildingSimulation @pytest.fixture(scope='module') @@ -37,7 +37,7 @@ def test_terminated_at_timelimit_reached(data_profile_length, num_forecasting_st max_timesteps=data_profile_length - num_forecasting_steps) env.reset() print(range(data_profile_length - num_forecasting_steps)) - for i in range(data_profile_length - num_forecasting_steps): + for _ in range(data_profile_length - num_forecasting_steps): obs, reward, terminated, trunc, info = env.step(0) assert terminated is True