diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 7b6f250..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/Environment/helper/configuration/configuration.py b/Environment/helper/configuration/configuration.py index e7b27b3..05808ec 100644 --- a/Environment/helper/configuration/configuration.py +++ b/Environment/helper/configuration/configuration.py @@ -87,7 +87,7 @@ def __init__(self): energy_prices = np.append(energy_prices, np.array([power[i], prices[i]])) self.energy_prices = energy_prices.reshape(len(prices), 2) self._vehicle_configs = {} - self.dynamic_pricing = True + self.dynamic_pricing = False if mode == "tra": self.capacity_pricing = False diff --git a/Learning_agents/Learning_Pricing.py b/Learning_agents/Learning_Pricing.py index d80a8c9..7744068 100644 --- a/Learning_agents/Learning_Pricing.py +++ b/Learning_agents/Learning_Pricing.py @@ -19,6 +19,8 @@ SUMMER_END = Configuration.instance().SUMMER_END POST_FIX = Configuration.instance().POST_FIX +Configuration.instance().dynamic_pricing = True + evaluate_after_training = Configuration.instance().evaluation_after_training number_of_chargers = 200 PV_CAPA = Configuration.instance().PV diff --git a/Operations/Operator_utils.py b/Operations/Operator_utils.py new file mode 100644 index 0000000..5d7c7cd --- /dev/null +++ b/Operations/Operator_utils.py @@ -0,0 +1,118 @@ +# utilities.py + +def get_exp_free_grid_capacity_utility( + current_time, + sim_time, + planning_interval, + num_lookahead_planning_periods, + num_lookback_periods, + baseload, + non_dispatchable_generator, + electric_storage, + charging_strategy, + charging_hub, + grid_capa +): + final_time = min( + sim_time, + round(current_time + planning_interval * num_lookahead_planning_periods), + ) + + periods = [] + t = current_time + while t < final_time: + periods.append(t) + t += planning_interval + + free_capa_list_actual = [] + base_load_list = [] + generation_list = [] + free_capa_list_predicted = [] + + for t in periods: + # ACTUAL + baseload_max = max( + baseload.loc[t : t + planning_interval - 1]["load_kw_rescaled"] + ) + generation_min = min( + non_dispatchable_generator.generation_profile_actual.loc[ + t : t + planning_interval - 1 + ]["pv_generation"] + ) + battery_max = min( + electric_storage.kW_discharge_peak, + ( + ( + electric_storage.SoC + - electric_storage.min_energy_stored_kWh + ) + * (60 / planning_interval) + ), + ) + if charging_strategy in [ + "dynamic", + "integrated_storage", + "online_multi_period", + ]: + battery_max = 0 + + battery_usage = 0 + if charging_hub.dynamic_pricing: + battery_usage = ( + electric_storage.discharging_power + - electric_storage.charging_power + ) + + free_capa_list_actual.append( + grid_capa + - baseload_max + + generation_min + + battery_max + + battery_usage + ) + + # PREDICTED + offset_period = num_lookback_periods + baseload_max_pred = max( + baseload.loc[ + (t - offset_period):(t - offset_period) + (planning_interval - 1) + ]["load_kw_rescaled"] + ) + generation_min_pred = min( + non_dispatchable_generator.generation_profile_forecast.loc[ + t : t + planning_interval - 1 + ]["pv_generation"] + ) + battery_max = min( + electric_storage.kW_discharge_peak, + ( + ( + electric_storage.SoC + - electric_storage.min_energy_stored_kWh + ) + * (60 / planning_interval) + ), + ) + + free_capa_list_predicted.append( + grid_capa + - baseload_max_pred + + generation_min_pred + + battery_max + + battery_usage + ) + + base_load_list.append(baseload_max_pred) + generation_list.append(generation_min_pred) + + free_grid_capa_without_storage = ( + free_capa_list_actual[0] - battery_max - battery_usage + ) + + return { + "free_grid_capa_actual": free_capa_list_actual, + "base_load_list": base_load_list, + "generation_list": generation_list, + "free_grid_capa_without_storage": free_grid_capa_without_storage, + "free_grid_capa_predicted": free_capa_list_predicted, + } \ No newline at end of file diff --git a/Operations/operator.py b/Operations/operator.py index e8b53fc..6261c86 100644 --- a/Operations/operator.py +++ b/Operations/operator.py @@ -9,6 +9,7 @@ import pandas as pd from Operations.NonLinearAlgorithms import nonlinear_pricing +from Operations.Operator_utils import get_exp_free_grid_capacity_utility from Utilities.RL_environments.rl_pricing_env import convert_to_vector @@ -126,125 +127,25 @@ def get_exp_free_grid_capacity(self): :param num_lookahead_periods: number of planning periods for which plan is created :return: """ - # while True: - current_time = self.env.now - sim_time = self.sim_time - final_time = min( - sim_time, - round( - current_time - + self.planning_interval * self.num_lookahead_planning_periods - ), + results = get_exp_free_grid_capacity_utility( + current_time=self.env.now, + sim_time=self.sim_time, + planning_interval=self.planning_interval, + num_lookahead_planning_periods=self.num_lookahead_planning_periods, + num_lookback_periods=self.num_lookback_periods, + baseload=self.baseload, + non_dispatchable_generator=self.non_dispatchable_generator, + electric_storage=self.electric_storage, + charging_strategy=self.charging_strategy, + charging_hub=self.charging_hub, + grid_capa=self.grid_capa, ) - # get periods in lookahead_window - periods = [] - t = current_time - while t < final_time: - periods.append(t) - t += self.planning_interval - - # get list of free grid capa - free_capa_list_actual = [] - base_load_list = [] - generation_list = [] - free_capa_list_predicted = [] - - for t in periods: - # ACTUAL - baseload_max = max( - self.baseload.loc[t : t + self.planning_interval - 1][ - "load_kw_rescaled" - ] - ) - self.baseload_min = min( - self.baseload.loc[t : t + self.planning_interval - 1][ - "load_kw_rescaled" - ] - ) - self.baseload_max = baseload_max - generation_min = min( - self.non_dispatchable_generator.generation_profile_actual.loc[ - t : t + self.planning_interval - 1 - ]["pv_generation"] - ) - self.generation_min = generation_min - battery_max = min( - self.electric_storage.kW_discharge_peak, - ( - ( - self.electric_storage.SoC - - self.electric_storage.min_energy_stored_kWh - ) - * (60 / self.planning_interval) - ), - ) - - if self.charging_strategy in [ - "dynamic", - "integrated_storage", - "online_multi_period", - ]: - battery_max = 0 - battery_usage = 0 - if self.charging_hub.dynamic_pricing: - - battery_usage = ( - self.electric_storage.discharging_power - - self.electric_storage.charging_power - ) - free_capa_list_actual.append( - self.grid_capa - - baseload_max - + generation_min - + battery_max - + battery_usage - ) - # PREDICTED - offset_period = ( - self.num_lookback_periods - ) # how many periods to go back for prediction, here we fix to 1 day - baseload_max_pred = max( - self.baseload.loc[ - (t - offset_period) : (t - offset_period) - + (self.planning_interval - 1) - ]["load_kw_rescaled"] - ) - generation_min_pred = min( - self.non_dispatchable_generator.generation_profile_forecast.loc[ - t : t + self.planning_interval - 1 - ]["pv_generation"] - ) - battery_max = min( - self.electric_storage.kW_discharge_peak, - ( - ( - self.electric_storage.SoC - - self.electric_storage.min_energy_stored_kWh - ) - * (60 / self.planning_interval) - ), - ) - free_capa_list_predicted.append( - self.grid_capa - - baseload_max_pred - + generation_min_pred - + battery_max - + battery_usage - ) - base_load_list.append(baseload_max_pred) - generation_list.append(generation_min_pred) - - # update free_grid_capa - self.free_grid_capa_actual = free_capa_list_actual - self.base_load_list = base_load_list - self.generation_list = generation_list - self.free_grid_capa_without_storage = ( - free_capa_list_actual[0] - battery_max - battery_usage - ) - self.free_grid_capa_predicted = free_capa_list_predicted - # return free_capa_list - # yield self.env.timeout(self.planning_period_length) + self.free_grid_capa_actual = results["free_grid_capa_actual"] + self.base_load_list = results["base_load_list"] + self.generation_list = results["generation_list"] + self.free_grid_capa_without_storage = results["free_grid_capa_without_storage"] + self.free_grid_capa_predicted = results["free_grid_capa_predicted"] def get_available_battery_load(self): """ diff --git a/README.md b/README.md index a92752f..0c730d8 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,29 @@ The following modules are included: - **`Preferences` Module:** Initializes vehicle objects with respective charging and parking preferences (i.e., requests) based on empirical data - **`Infrastructure` Module:** Initializes infrastructure objects (EV supply equipment (EVSE), connectors per each EVSE, grid connection capacity, on-site storage and on-site generation (PV)) - **`Operations` Module:** Conatain algorithms for assigning physical space (vehicle routing) and electrical capacity (vehicle charging) to individual vehicle objects based on a pre-defined charging policy -- **`Results` Module:** Monitors EVCC activity in pre-defined intervals and accounts costs. Includes plotting routines. \ No newline at end of file +- **`Results` Module:** Monitors EVCC activity in pre-defined intervals and accounts costs. Includes plotting routines. + +## 📦 Installation + +This project uses [`uv`](https://github.com/astral-sh/uv), a modern and ultra-fast Python package manager compatible with pip. + +### 1. Install `uv` + +If you don’t have `uv` installed, run: + +### Installation Steps + +```bash +# Step 1: Install uv +pip install uv + +# Step 2: Create a virtual environment +python -m venv .venv + +# Step 3: Activate the environment +source .venv/bin/activate # On macOS/Linux +# or +.venv\Scripts\activate # On Windows + +# Step 4: Install dependencies +uv pip install -r requirements.uv.txt \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1cb0d56..0000000 --- a/requirements.txt +++ /dev/null @@ -1,57 +0,0 @@ -autocommand==2.2.2 -backports.tarfile==1.2.0 -black==25.1.0 -click==8.1.8 -cloudpickle==3.1.1 -contourpy==1.3.1 -cycler==0.12.1 -Cython==3.0.11 -docplex==2.29.241 -filelock==3.17.0 -fonttools==4.56.0 -fsspec==2025.2.0 -gym==0.26.2 -gym-notices==0.0.8 -importlib_metadata==8.0.0 -inflect==7.3.1 -jaraco.collections==5.1.0 -jaraco.context==5.3.0 -jaraco.functools==4.0.1 -jaraco.text==3.12.1 -Jinja2==3.1.5 -joblib==1.4.2 -kiwisolver==1.4.8 -MarkupSafe==3.0.2 -matplotlib==3.10.0 -mdolab-baseclasses==1.8.2 -more-itertools==10.3.0 -mpmath==1.3.0 -mypy-extensions==1.0.0 -networkx==3.4.2 -nn-builder==0.0.12 -numpy==1.26.4 -packaging==24.2 -pandas==2.2.3 -pathspec==0.12.1 -pillow==11.1.0 -platformdirs==4.3.6 -pyoptsparse==2.12.0 -pyparsing==3.2.1 -python-dateutil==2.9.0.post0 -pytz==2025.1 -scikit-learn==1.6.1 -scipy==1.15.1 -seaborn==0.13.2 -setuptools==75.8.0 -simpy==4.1.1 -six==1.17.0 -sqlitedict==2.1.0 -sympy==1.13.1 -threadpoolctl==3.5.0 -tomli==2.0.1 -torch==2.6.0 -typeguard==4.3.0 -typing_extensions==4.12.2 -tzdata==2025.1 -wheel==0.43.0 -zipp==3.19.2