soc predictor wip

This commit is contained in:
Lucas Tan 2025-07-18 17:11:50 +01:00
parent 62cf35c727
commit 1b29cfdea8
3 changed files with 62 additions and 8 deletions

View File

@ -26,5 +26,19 @@ def discharge_bess(bess, site_name, dt, discharge_power):
if unit["site"] == site_name: if unit["site"] == site_name:
new_soc = unit["SoC"] - (dt * discharge_energy) / unit["capacity_kWh"] new_soc = unit["SoC"] - (dt * discharge_energy) / unit["capacity_kWh"]
new_soc = 0 if new_soc < 0 else new_soc new_soc = 0 if new_soc < 0 else new_soc
bess["units"][index]["SoC"] = new_soc else:
new_soc = unit["SoC"]
# update SoC
bess["units"][index]["SoC"] = new_soc
return bess return bess
def predict_swap_time(bess_soc_for_cycle):
"""Predict the swap time for each BESS unit based on its SoC history."""
swap_times = {}
for unit_name, df in bess_soc_for_cycle.items():
# Find the timestamp when SoC reaches 0
swap_time = df[df["SoC"] == 0]["Timestamp"].min()
swap_times[unit_name] = swap_time
return swap_times

View File

@ -14,3 +14,9 @@ units:
- name: MBESS 5 - name: MBESS 5
capacity_kWh: 2096 capacity_kWh: 2096
c-rate: 0.5 c-rate: 0.5
- name: MBESS 6
capacity_kWh: 2096
c-rate: 0.5
buffer:
unit: percentage of buffer
min: 0.1

48
main.py
View File

@ -2,11 +2,17 @@ import numpy as np
import yaml import yaml
from Utilities.Time import get_start_time from Utilities.Time import get_start_time
from Utilities.LoadProfile import get_load_profiles from Utilities.LoadProfile import get_load_profiles
from Utilities.BESS import initialise_SoC, initial_site_assignment, discharge_bess from Utilities.BESS import (
initialise_SoC,
initial_site_assignment,
discharge_bess,
predict_swap_time,
)
import matplotlib.pyplot as pl import matplotlib.pyplot as pl
import pandas as pd import pandas as pd
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
# read config file # read config file
c = yaml.safe_load(open("YAMLs/config.yml")) c = yaml.safe_load(open("YAMLs/config.yml"))
@ -41,7 +47,13 @@ def generate_and_cache_profiles(c, dt):
bess_data = initialise_SoC(bess_data) bess_data = initialise_SoC(bess_data)
bess_data = initial_site_assignment(c, bess_data) bess_data = initial_site_assignment(c, bess_data)
# bess SoC dataframe # bess SoC dataframe
bess_soc = pd.DataFrame(columns=[unit["name"] for unit in bess_data["units"]]) bess_soc_since_start = pd.DataFrame(
columns=[unit["name"] for unit in bess_data["units"]]
)
# bess SoC dictionary, meant to track SoC progress over each cycle.
# resets after each charging cycle. This is for predicting swap times.
init_df = pd.DataFrame(columns=["Timestamp", "SoC"])
bess_soc_for_cycle = {unit["name"]: init_df for unit in bess_data["units"]}
# get initial load profiles # get initial load profiles
cumulative_load_profiles = get_load_profiles( cumulative_load_profiles = get_load_profiles(
@ -71,16 +83,38 @@ with ThreadPoolExecutor() as executor:
temp_soc = [unit["SoC"] for unit in bess_data["units"]] temp_soc = [unit["SoC"] for unit in bess_data["units"]]
# append SoC to dataframe # append SoC to dataframe
bess_soc = pd.concat( bess_soc_since_start = pd.concat(
[ [
bess_soc, bess_soc_since_start,
pd.DataFrame( pd.DataFrame(
[temp_soc], columns=bess_soc.columns, index=[timestamps[i]] [temp_soc],
columns=bess_soc_since_start.columns,
index=[timestamps[i]],
), ),
], ],
axis=0, axis=0,
) )
# assign SoC for cycle
for unit in bess_data["units"]:
unit_name = unit["name"]
# reset df if SoC is 0. Start a new cycle
if unit["SoC"] == 0:
bess_soc_for_cycle[unit_name] = init_df
bess_soc_for_cycle[unit_name] = pd.concat(
[
bess_soc_for_cycle[unit_name],
pd.DataFrame(
[[timestamps[i], unit["SoC"]]],
columns=["Timestamp", "SoC"],
),
],
axis=0,
)
# predict swap times
swap_times = predict_swap_time(bess_soc_for_cycle)
# add to cumulative load profiles # add to cumulative load profiles
# check if future exists and is done # check if future exists and is done
if is_running_in_async: if is_running_in_async:
@ -96,6 +130,6 @@ with ThreadPoolExecutor() as executor:
print(len(cumulative_load_profiles), "load profiles generated") print(len(cumulative_load_profiles), "load profiles generated")
is_running_in_async = False is_running_in_async = False
pl.plot(bess_soc.index, bess_soc.values, label="BESS SoC", alpha=0.5) pl.plot(cumulative_load_profiles)
pl.show() pl.show()
pl.xlabel("Time (s since epoch)") pl.plot(bess_soc_since_start)