Skip to content

Commit

Permalink
Fix related to solar charging, change to try to reduce register write… (
Browse files Browse the repository at this point in the history
#1882)

* Fix related to solar charging, change to try to reduce register writes in low power mode charge

* [pre-commit.ci lite] apply automatic fixes

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
  • Loading branch information
springfall2008 and pre-commit-ci-lite[bot] authored Jan 19, 2025
1 parent 0aed1c1 commit 1e24d6c
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 31 deletions.
3 changes: 2 additions & 1 deletion apps/predbat/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def execute_plan(self):
target_soc = self.charge_limit_percent_best[0] if self.charge_limit_best != self.reserve else self.soc_kw
inv_target_soc = self.adjust_battery_target_multi(inverter, target_soc, True, False, check=True)

current_charge_rate = inverter.get_current_charge_rate()
new_charge_rate, new_charge_rate_real = find_charge_rate(
self.minutes_now,
inverter.soc_kw,
Expand All @@ -120,9 +121,9 @@ def execute_plan(self):
self.log,
inverter.battery_temperature,
self.battery_temperature_charge_curve,
current_charge_rate=current_charge_rate / MINUTE_WATT,
)
new_charge_rate = int(new_charge_rate * MINUTE_WATT)
current_charge_rate = inverter.get_current_charge_rate()

self.log(
"Inverter {} Target SOC {} (this inverter {}) Battery temperature {} Select charge rate {}w (real {}w) current charge rate {}".format(
Expand Down
32 changes: 20 additions & 12 deletions apps/predbat/plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -1482,7 +1482,7 @@ def sort_window_by_time(self, windows):
window_sorted.sort(key=self.window_sort_func_start)
return window_sorted

def sort_window_by_price_combined(self, charge_windows, export_windows, stand_alone=False, calculate_import_low_export=False, calculate_export_high_import=False):
def sort_window_by_price_combined(self, charge_windows, export_windows, calculate_import_low_export=False, calculate_export_high_import=False):
"""
Sort windows into price sets
"""
Expand All @@ -1492,18 +1492,15 @@ def sort_window_by_price_combined(self, charge_windows, export_windows, stand_al
price_links = {}

# Add charge windows
if self.calculate_best_charge or stand_alone:
if self.calculate_best_charge:
id = 0
for window in charge_windows:
# Account for losses in average rate as it makes import higher
if stand_alone:
average = dp2(window["average"])
else:
average = dp2(window["average"] / self.inverter_loss / self.battery_loss + self.metric_battery_cycle)
if self.carbon_enable:
carbon_intensity = self.carbon_intensity.get(window["start"] - self.minutes_now, 0)
average += carbon_intensity * self.carbon_metric / 1000.0
average += self.metric_self_sufficiency
average = dp2(window["average"] / self.inverter_loss / self.battery_loss + self.metric_battery_cycle)
if self.carbon_enable:
carbon_intensity = self.carbon_intensity.get(window["start"] - self.minutes_now, 0)
average += carbon_intensity * self.carbon_metric / 1000.0
average += self.metric_self_sufficiency
if calculate_import_low_export:
average_export = dp2((self.rate_export.get(window["start"], 0) + self.rate_export.get(window["end"] - PREDICT_STEP, 0)) / 2)
else:
Expand All @@ -1519,7 +1516,7 @@ def sort_window_by_price_combined(self, charge_windows, export_windows, stand_al
id += 1

# Add export windows
if self.calculate_best_export and not stand_alone:
if self.calculate_best_export:
id = 0
for window in export_windows:
# Account for losses in average rate as it makes export value lower
Expand Down Expand Up @@ -1918,6 +1915,17 @@ def optimise_all_windows(self, best_metric, metric_keep, debug_mode=False):
record_charge_windows = max(self.max_charge_windows(self.end_record + self.minutes_now, self.charge_window_best), 1)
record_export_windows = max(self.max_charge_windows(self.end_record + self.minutes_now, self.export_window_best), 1)
window_sorted, window_index, price_set, price_links = self.sort_window_by_price_combined(self.charge_window_best[:record_charge_windows], self.export_window_best[:record_export_windows])
if debug_mode:
price_order = []
for price_key in price_set:
links = price_links[price_key]
for key in links:
typ = window_index[key]["type"]
window_n = window_index[key]["id"]
price = window_index[key]["average"]
if typ == "c":
real_price = self.charge_window_best[window_n]["average"]
price_order.append(real_price)

best_soc = self.soc_max
best_cost = best_metric
Expand Down Expand Up @@ -2165,7 +2173,7 @@ def optimise_all_windows(self, best_metric, metric_keep, debug_mode=False):
self.update_target_values()
self.publish_html_plan(self.pv_forecast_minute_step, self.pv_forecast_minute10_step, self.load_minutes_step, self.load_minutes_step10, self.end_record)
open("plan_main_charge_{}.html".format(window_n), "w").write(self.html_plan)
print("Wrote plan to plan_main_charge_{}.html".format(window_n))
print("Wrote plan to plan_main_charge_{}.html - metric {} cost {} keep {} cycle {} import {}".format(window_n, best_metric, best_cost, best_keep, best_cycle, best_import))

if self.debug_enable:
self.log(
Expand Down
2 changes: 1 addition & 1 deletion apps/predbat/predbat.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import asyncio
import json

THIS_VERSION = "v8.11.1"
THIS_VERSION = "v8.11.2"

# fmt: off
PREDBAT_FILES = ["predbat.py", "config.py", "prediction.py", "gecloud.py","utils.py", "inverter.py", "ha.py", "download.py", "unit_test.py", "web.py", "predheat.py", "futurerate.py", "octopus.py", "solcast.py","execute.py", "plan.py", "fetch.py", "output.py", "userinterface.py"]
Expand Down
4 changes: 1 addition & 3 deletions apps/predbat/prediction.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,14 +697,12 @@ def run_prediction(self, charge_limit, charge_window, export_window, export_limi
# The battery will hit the charge limit in this period, so if the charge was spread over the period
# it could be done from solar, but in reality it will be full rate and then stop meaning the solar
# won't cover it and it will likely create an import.
# The import could be balanced with potential export for the rest of the period (or battery charging)
pv_compare = pv_dc + pv_ac
if pv_dc >= (charge_limit_n - soc) and (pv_compare < (charge_rate_now_curve * step)):
charge_time_remains = (charge_limit_n - soc) / charge_rate_now_curve # Time in minute periods left
pv_in_period = pv_compare / step * charge_time_remains
potential_import = min((charge_rate_now_curve * charge_time_remains) - pv_in_period, (charge_limit_n - soc))
potential_export = pv_compare - pv_in_period
metric_keep += max(potential_import * rate_import.get(minute_absolute, 0) - potential_export * rate_export.get(minute_absolute, 0), 0)
metric_keep += max(potential_import * rate_import.get(minute_absolute, 0), 0)
else:
# ECO Mode
pv_ac = pv_now * inverter_loss_ac
Expand Down
15 changes: 9 additions & 6 deletions apps/predbat/unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def run_nordpool_test(my_predbat):
if min_import < -15 or max_import > 100:
print("ERROR: Rate import data out of range got min {} max {}".format(min_import, max_import))
failed = True
if min_export < 0 or max_export > 50:
if min_export < 0 or max_export > 60:
print("ERROR: Rate export data out of range got min {} max {}".format(min_export, max_export))
failed = True

Expand Down Expand Up @@ -2459,13 +2459,16 @@ def run_single_debug(test_name, my_predbat, debug_file, expected_file=None):
if not expected_file:
my_predbat.args["plan_debug"] = True
# my_predbat.set_discharge_during_charge = True
# my_predbat.metric_self_sufficiency = 0

# my_predbat.metric_self_sufficiency = 5
# my_predbat.calculate_second_pass = False
# my_predbat.best_soc_keep = 0
pass
# my_predbat.best_soc_keep = 2
# my_predbat.set_charge_freeze = True
# my_predbat.combine_export_slots = False
# my_predbat.best_soc_keep = 1.0
# my_predbat.metric_min_improvement_export = 5
my_predbat.inverter_loss = 0.97
my_predbat.calculate_tweak_plan = False
pass

if re_do_rates:
# Set rate thresholds
Expand Down Expand Up @@ -2989,7 +2992,7 @@ def run_execute_tests(my_predbat):
assert_status="Charging",
assert_charge_start_time_minutes=-1,
assert_charge_end_time_minutes=my_predbat.minutes_now + 15,
assert_charge_rate=2000,
assert_charge_rate=1300, # Keep current rate as it is over the max rate we will achieve anyhow
battery_max_rate=2000,
)
if failed:
Expand Down
37 changes: 29 additions & 8 deletions apps/predbat/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,33 @@ def find_battery_temperature_cap(battery_temperature, battery_temperature_curve,


def find_charge_rate(
minutes_now, soc, window, target_soc, max_rate, soc_max, battery_charge_power_curve, set_charge_low_power, charge_low_power_margin, battery_rate_min, battery_rate_max_scaling, battery_loss, log_to, battery_temperature=20, battery_temperature_curve={}
minutes_now,
soc,
window,
target_soc,
max_rate,
soc_max,
battery_charge_power_curve,
set_charge_low_power,
charge_low_power_margin,
battery_rate_min,
battery_rate_max_scaling,
battery_loss,
log_to,
battery_temperature=20,
battery_temperature_curve={},
current_charge_rate=None,
):
"""
Find the lowest charge rate that fits the charge slow
"""
margin = charge_low_power_margin
target_soc = round(target_soc, 2)

# Current charge rate
if current_charge_rate is None:
current_charge_rate = max_rate

# Real achieved max rate
max_rate_real = get_charge_rate_curve(soc, max_rate, soc_max, max_rate, battery_charge_power_curve, battery_rate_min, battery_temperature, battery_temperature_curve) * battery_rate_max_scaling

Expand Down Expand Up @@ -268,16 +287,18 @@ def find_charge_rate(
if rate_w >= min_rate_w:
charge_now = soc
minute = 0
rate_scale_max = 0
# Compute over the time period, include the completion time
for minute in range(0, minutes_left, PREDICT_STEP):
rate_scale = get_charge_rate_curve(charge_now, rate, soc_max, max_rate, battery_charge_power_curve, battery_rate_min, battery_temperature, battery_temperature_curve)
highest_achievable_rate = max(highest_achievable_rate, rate_scale)
rate_scale *= battery_rate_max_scaling
rate_scale_max = max(rate_scale_max, rate_scale)
charge_amount = rate_scale * PREDICT_STEP * battery_loss
charge_now += charge_amount
if round(charge_now, 2) >= target_soc:
if (round(charge_now, 2) >= target_soc) and (rate_scale_max < best_rate_real):
best_rate = rate
best_rate_real = rate_scale
best_rate_real = rate_scale_max
break
# if log_to:
# log_to("Low Power mode: rate: {} minutes: {} SOC: {} Target SOC: {} Charge left: {} Charge now: {} Rate scale: {} Charge amount: {} Charge now: {} best rate: {} highest achievable_rate {}".format(
Expand All @@ -286,13 +307,13 @@ def find_charge_rate(
break
rate_w -= 100.0

# If we tried to select a rate which is actually faster than the highest achievable (due to being close to 100% SOC) then default to max rate
if best_rate < max_rate and best_rate >= highest_achievable_rate:
# Stick with current rate if it doesn't matter
if best_rate >= highest_achievable_rate and current_charge_rate >= highest_achievable_rate:
best_rate = current_charge_rate
if log_to:
log_to("Low Power mode: best rate {} >= highest achievable rate {}, default to max rate".format(best_rate * MINUTE_WATT, highest_achievable_rate * MINUTE_WATT))
best_rate = max_rate
best_rate_real = max_rate_real
log_to("Low Power mode: best rate {} is less than highest achievable rate {} and current rate {} so sticking with current rate".format(best_rate * MINUTE_WATT, highest_achievable_rate * MINUTE_WATT, current_charge_rate * MINUTE_WATT))

best_rate_real = get_charge_rate_curve(soc, best_rate, soc_max, max_rate, battery_charge_power_curve, battery_rate_min, battery_temperature, battery_temperature_curve) * battery_rate_max_scaling
if log_to:
log_to(
"Low Power mode: minutes left: {} absolute: {} SOC: {} Target SOC: {} Charge left: {} Max rate: {} Min rate: {} Best rate: {} Best rate real: {} Battery temp {}".format(
Expand Down

0 comments on commit 1e24d6c

Please sign in to comment.