Skip to content

Commit

Permalink
Tweak optimisation of next 8 windows
Browse files Browse the repository at this point in the history
  • Loading branch information
springfall2008 authored Dec 2, 2023
1 parent e9a1a15 commit 9c54425
Showing 1 changed file with 72 additions and 12 deletions.
84 changes: 72 additions & 12 deletions apps/predbat/predbat.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import appdaemon.plugins.hass.hassapi as hass
import adbase as ad

THIS_VERSION = "v7.13.20"
THIS_VERSION = "v7.13.21"
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
TIME_FORMAT_SECONDS = "%Y-%m-%dT%H:%M:%S.%f%z"
TIME_FORMAT_OCTOPUS = "%Y-%m-%d %H:%M:%S%z"
Expand Down Expand Up @@ -6313,7 +6313,7 @@ def optimise_charge_limit(
self,
window_n,
record_charge_windows,
try_charge_limit,
charge_limit,
charge_window,
discharge_window,
discharge_limits,
Expand All @@ -6338,6 +6338,7 @@ def optimise_charge_limit(
best_keep = 0
max_soc = self.soc_max
min_soc = 0
try_charge_limit = copy.deepcopy(charge_limit)

# For single windows, if the size is 30 minutes or less then use a larger step
if not all_n:
Expand Down Expand Up @@ -6447,15 +6448,14 @@ def optimise_charge_limit(
metric_diff = metric10 - metric
metric_diff *= self.pv_metric10_weight
metric += metric_diff
metric = self.dp2(metric)

# Adjustment for battery cycles metric
metric += battery_cycle * self.metric_battery_cycle + metric_keep
metric10 += battery_cycle * self.metric_battery_cycle + metric_keep10
metric10 += battery_cycle10 * self.metric_battery_cycle + metric_keep10

# Metric adjustment based on current charge limit, try to avoid
# constant changes by weighting the base setting a little
if not all_n and window_n == 0:
# Metric adjustment based on current charge limit when inside the window
# to try to avoid constant small changes to SOC target
if not all_n and (window_n == self.in_charge_window(charge_window, self.minutes_now)):
try_percent = self.calc_percent_limit(try_soc)
compare_with = max(self.current_charge_limit, self.reserve_current_percent)

Expand Down Expand Up @@ -6509,7 +6509,7 @@ def optimise_charge_limit(
best_soc = min(best_soc + self.best_soc_margin, self.soc_max)

self.log(
"Try optimising charge window(s) {} price {} results {} selected {}".format(all_n if all_n else window_n, charge_window[window_n]["average"], window_results, best_soc)
"Try optimising charge window(s) {} price {} results {} selected {} was {}".format(all_n if all_n else window_n, charge_window[window_n]["average"], window_results, best_soc, charge_limit[window_n])
)
return best_soc, best_metric, best_cost, best_soc_min, best_soc_min_minute, best_keep

Expand Down Expand Up @@ -7067,6 +7067,60 @@ def discard_unused_discharge_slots(self, discharge_limits_best, discharge_window

return new_enable, new_best

def tweak_plan(self, end_record, load_minutes_step, pv_forecast_minute_step, pv_forecast_minute10_step, best_metric, metric_keep):
"""
Tweak existing plan only
"""
record_charge_windows = max(self.max_charge_windows(end_record + self.minutes_now, self.charge_window_best), 1)
record_discharge_windows = max(self.max_charge_windows(end_record + self.minutes_now, self.discharge_window_best), 1)
self.log("Tweak optimisation started")
count = 0
window_sorted, window_index = self.sort_window_by_time_combined(self.charge_window_best[:record_charge_windows], self.discharge_window_best[:record_discharge_windows])
for key in window_sorted:
typ = window_index[key]["type"]
window_n = window_index[key]["id"]
if typ == "c":
if self.calculate_best_charge:
best_soc, best_metric, best_cost, soc_min, soc_min_minute, best_keep = self.optimise_charge_limit(
window_n,
record_charge_windows,
self.charge_limit_best,
self.charge_window_best,
self.discharge_window_best,
self.discharge_limits_best,
load_minutes_step,
pv_forecast_minute_step,
pv_forecast_minute10_step,
end_record=end_record,
)
self.charge_limit_best[window_n] = best_soc
else:
if self.calculate_best_discharge:
if not self.calculate_discharge_oncharge:
hit_charge = self.hit_charge_window(self.charge_window_best, self.discharge_window_best[window_n]["start"], self.discharge_window_best[window_n]["end"])
if hit_charge >= 0 and self.charge_limit_best[hit_charge] > 0.0:
continue
average = self.discharge_window_best[window_n]["average"]
best_soc, best_start, best_metric, best_cost, soc_min, soc_min_minute, best_keep = self.optimise_discharge(
window_n,
record_discharge_windows,
self.charge_limit_best,
self.charge_window_best,
self.discharge_window_best,
self.discharge_limits_best,
load_minutes_step,
pv_forecast_minute_step,
pv_forecast_minute10_step,
end_record=end_record,
)
self.discharge_limits_best[window_n] = best_soc
self.discharge_window_best[window_n]["start"] = best_start
count += 1
if count >= 8:
break

self.log("Tweak optimisation finished metric {} cost {} metric_keep {}".format(self.dp2(best_metric), self.dp2(best_cost), self.dp2(best_keep)))

def optimise_all_windows(self, end_record, load_minutes_step, pv_forecast_minute_step, pv_forecast_minute10_step, best_metric, metric_keep):
"""
Optimise all windows, both charge and discharge in rate order
Expand Down Expand Up @@ -7809,10 +7863,16 @@ def calculate_plan(self, recompute=True):
if recompute:
self.rate_best_cost_threshold_charge = None
self.rate_best_cost_threshold_discharge = None

if recompute and self.calculate_best:
if self.calculate_best:
self.log_option_best()
self.optimise_all_windows(self.end_record, load_minutes_step, pv_forecast_minute_step, pv_forecast_minute10_step, metric, metric_keep)

# Full plan
if recompute:
self.optimise_all_windows(self.end_record, load_minutes_step, pv_forecast_minute_step, pv_forecast_minute10_step, metric, metric_keep)

# Tweak plan
self.tweak_plan(self.end_record, load_minutes_step, pv_forecast_minute_step, pv_forecast_minute10_step, metric, metric_keep)

# Remove charge windows that overlap with discharge windows
self.charge_limit_best, self.charge_window_best = self.remove_intersecting_windows(
Expand Down Expand Up @@ -7885,7 +7945,7 @@ def calculate_plan(self, recompute=True):
self.plan_valid = True
self.plan_last_updated = self.now_utc
self.plan_last_updated_minutes = self.minutes_now

if self.calculate_best:
# Final simulation of best, do 10% and normal scenario
best_metric10, import_kwh_battery10, import_kwh_house10, export_kwh10, soc_min10, soc10, soc_min_minute10, battery_cycle10, metric_keep10 = self.run_prediction(
Expand Down

0 comments on commit 9c54425

Please sign in to comment.