diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9dbd87d..fa7b288 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,15 +7,17 @@ on: branches: [ "master" ] jobs: - test: - + checkup: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: install dependencies - run: pip install .[tests] + run: pip install .[dev] - name: Run Tests with pytest - run: pytest ./tests \ No newline at end of file + run: pytest ./tests + + - name: Type checking with mypy + run: mypy building_energy_storage_simulation \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7393ab2..f578f13 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ __pycache__ **.egg-info .ipynb_checkpoints -docs/build \ No newline at end of file +docs/build +build/ +dist/ \ No newline at end of file diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 0000000..81d8e59 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,2 @@ +[mypy] +disallow_untyped_defs = True \ No newline at end of file diff --git a/README.md b/README.md index 89c0853..88a265d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ or if you want to continue developing the package: ``` git clone https://github.com/tobirohrer/building-energy-storage-simulation.git && cd building-energy-storage-simulation -pip install -e .[docs,tests] +pip install -e .[dev] ``` ## Usage diff --git a/building_energy_storage_simulation/battery.py b/building_energy_storage_simulation/battery.py index f4181f3..453a050 100644 --- a/building_energy_storage_simulation/battery.py +++ b/building_energy_storage_simulation/battery.py @@ -20,9 +20,8 @@ def __init__(self, self.capacity = capacity self.initial_state_of_charge = initial_state_of_charge self.state_of_charge = initial_state_of_charge - pass - def use(self, amount: float): + def use(self, amount: float) -> float: """ Using means charging or discharging the battery. @@ -52,7 +51,7 @@ def use(self, amount: float): self.state_of_charge += amount return electricity_used - def reset(self): + def reset(self) -> None: """ Resetting the `state_of_charge` of the battery to the `initial_state_of_charge`. """ diff --git a/building_energy_storage_simulation/building_simulation.py b/building_energy_storage_simulation/building_simulation.py index 9cfc942..fdf98f1 100644 --- a/building_energy_storage_simulation/building_simulation.py +++ b/building_energy_storage_simulation/building_simulation.py @@ -46,9 +46,8 @@ def __init__(self, self.step_count = 0 self.start_index = 0 - pass - def reset(self): + def reset(self) -> None: """ 1. Resetting the state of the building by calling `reset()` method from the battery class. @@ -59,7 +58,6 @@ def reset(self): self.battery.reset() self.step_count = 0 - pass def simulate_one_step(self, action: float) -> Tuple[float, float]: """ @@ -97,7 +95,7 @@ def simulate_one_step(self, action: float) -> Tuple[float, float]: self.step_count += 1 return electricity_consumption_this_timestep, electricity_price_this_timestep - def _check_data_profiles_same_length(self): + def _check_data_profiles_same_length(self) -> None: if len(self.solar_generation_profile) == len(self.electricity_load_profile) == len(self.electricity_price): pass else: diff --git a/building_energy_storage_simulation/environment.py b/building_energy_storage_simulation/environment.py index bd3b9e2..df22ea4 100644 --- a/building_energy_storage_simulation/environment.py +++ b/building_energy_storage_simulation/environment.py @@ -1,4 +1,4 @@ -from typing import Tuple, Optional, Union, List +from typing import Tuple, Optional, Union, List, Iterable, Any from collections.abc import MutableSequence import gymnasium as gym @@ -50,7 +50,6 @@ def __init__(self, low=-np.inf, high=np.inf, dtype=np.float32) - pass def render(self) -> Optional[Union[RenderFrame, List[RenderFrame]]]: """ @@ -59,7 +58,7 @@ def render(self) -> Optional[Union[RenderFrame, List[RenderFrame]]]: pass - def reset(self, seed=None, options=None) -> Tuple[ObsType, dict]: + def reset(self, seed: Union[int, None] = None, options: Any = None) -> Tuple[np.ndarray, dict]: """ Resetting the state of the simulation by calling `reset()` method from the simulation class. @@ -77,7 +76,7 @@ def reset(self, seed=None, options=None) -> Tuple[ObsType, dict]: self.building_simulation.start_index = int(np.random.uniform(0, latest_possible_start_time_step)) return self.get_observation(), {} - def step(self, action: ActType) -> Tuple[ObsType, float, bool, bool, dict]: + def step(self, action: Union[float, Iterable[float]]) -> Tuple[np.ndarray, float, bool, bool, dict]: """ Perform one step, which is done by: @@ -89,7 +88,7 @@ def step(self, action: ActType) -> Tuple[ObsType, float, bool, bool, dict]: action represents the fraction of `max_battery_charge_per_timestep` which should be used to charge or discharge the battery. 1 represents the maximum possible amount of energy which can be used to charge the battery per time step. - :type action: float + :type action: float or [float] :returns: Tuple of: 1. observation @@ -103,19 +102,19 @@ def step(self, action: ActType) -> Tuple[ObsType, float, bool, bool, dict]: """ if hasattr(action, "__len__"): - action = action[0] - electricity_consumption, electricity_price = self.building_simulation.simulate_one_step(action) + action = action[0] # type: ignore + electricity_consumption, electricity_price = self.building_simulation.simulate_one_step(action) # type: ignore reward = Environment.calc_reward(electricity_consumption, electricity_price) observation = self.get_observation() return observation, reward, self._get_terminated(), False, {'electricity_consumption': electricity_consumption, 'electricity_price': electricity_price} - def _get_terminated(self): + def _get_terminated(self) -> bool: if self.building_simulation.step_count >= self.max_timesteps: return True return False - def get_observation(self): + def get_observation(self) -> np.ndarray: current_index = self.building_simulation.start_index + self.building_simulation.step_count sim = self.building_simulation electric_load_forecast = sim.electricity_load_profile[current_index: current_index + self.num_forecasting_steps] @@ -127,23 +126,23 @@ def get_observation(self): solar_gen_forecast = self._randomize_forecast(solar_gen_forecast) energy_price_forecast = self._randomize_forecast(energy_price_forecast) - return np.concatenate(([self.building_simulation.battery.state_of_charge], + return np.concatenate(([self.building_simulation.battery.state_of_charge], # type: ignore electric_load_forecast, solar_gen_forecast, energy_price_forecast), axis=0) @staticmethod - def _randomize_forecast(forecast: MutableSequence, + def _randomize_forecast(forecast: np.ndarray, standard_deviation_start: float = 0.2, - standard_deviation_end: float = 1.0) -> MutableSequence: + standard_deviation_end: float = 1.0) -> np.ndarray: # gamma can be interpreted as the quantification of the increase of uncertainty per time step. gamma = standard_deviation_end - standard_deviation_start for i in range(len(forecast)): std = standard_deviation_end - gamma ** i forecast[i] = forecast[i] + np.random.normal(0, std) - return forecast + return np.array(forecast) @staticmethod - def calc_reward(electricity_consumption, electricity_price): + def calc_reward(electricity_consumption: float, electricity_price: float) -> float: return -1 * electricity_consumption * electricity_price diff --git a/setup.py b/setup.py index a456358..bd5a94a 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ long_description = (this_directory / "README.md").read_text() setup(name='building_energy_storage_simulation', - version='0.9.0', + version='0.9.2', description='A simulation of a building to optimize energy storage utilization.', long_description=long_description, long_description_content_type='text/markdown', @@ -26,11 +26,12 @@ "numpy" ], extras_require={ - "docs": [ - "sphinx" - ], - "tests": [ - "pytest" + "dev": [ + "sphinx", + "pytest", + "mypy", + "pandas-stubs", + "types-setuptools" ] } )