Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 33 additions & 13 deletions apps/predbat/predbat.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import os
import yaml

THIS_VERSION = "v7.14.25"
THIS_VERSION = "v7.14.26"
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 @@ -3731,8 +3731,8 @@ def run_prediction(self, charge_limit, charge_window, discharge_window, discharg

discharge_window_n = self.in_charge_window(discharge_window, minute_absolute)

# Add in standing charge
if (minute_absolute % (24 * 60)) < step:
# Add in standing charge, only for the final plan when we save the results
if (minute_absolute % (24 * 60)) < step and save:
metric += self.metric_standing_charge

# Outside the recording window?
Expand Down Expand Up @@ -6655,8 +6655,10 @@ def optimise_charge_limit_price(

# Balancing payment to account for battery left over
# ie. how much extra battery is worth to us in future, assume it's the same as low rate
rate_min = self.rate_min_forward.get(end_record, self.rate_min) * self.metric_battery_value_scaling / self.inverter_loss / self.battery_loss
metric -= (soc + final_iboost) * max(rate_min, 1.0)
rate_min = self.rate_min_forward.get(end_record, self.rate_min) / self.inverter_loss / self.battery_loss
metric -= (
(soc + final_iboost) * max(rate_min, 1.0, self.rate_export_min * self.inverter_loss * self.battery_loss_discharge) * self.metric_battery_value_scaling
)

# Adjustment for battery cycles metric
metric += battery_cycle * self.metric_battery_cycle + metric_keep
Expand Down Expand Up @@ -6857,9 +6859,9 @@ def optimise_charge_limit(

# Balancing payment to account for battery left over
# ie. how much extra battery is worth to us in future, assume it's the same as low rate
rate_min = self.rate_min_forward.get(end_record, self.rate_min) * self.metric_battery_value_scaling / self.inverter_loss / self.battery_loss
metric -= (soc + final_iboost) * max(rate_min, 1.0)
metric10 -= (soc10 + final_iboost10) * max(rate_min, 1.0)
rate_min = self.rate_min_forward.get(end_record, self.rate_min) / self.inverter_loss / self.battery_loss
metric -= (soc + final_iboost) * max(rate_min, 1.0, self.rate_export_min * self.inverter_loss * self.battery_loss_discharge) * self.metric_battery_value_scaling
metric10 -= (soc10 + final_iboost10) * max(rate_min, 1.0, self.rate_export_min * self.inverter_loss * self.battery_loss_discharge) * self.metric_battery_value_scaling

# Metric adjustment based on 10% outcome weighting
if metric10 > metric:
Expand Down Expand Up @@ -7054,9 +7056,11 @@ def optimise_discharge(

# Balancing payment to account for battery left over
# ie. how much extra battery is worth to us in future, assume it's the same as low rate
rate_min = self.rate_min_forward.get(end_record, self.rate_min) * self.metric_battery_value_scaling / self.inverter_loss / self.battery_loss
metric -= (soc + final_iboost) * max(rate_min, 1.0)
metric10 -= (soc10 + final_iboost10) * max(rate_min, 1.0)
rate_min = self.rate_min_forward.get(end_record, self.rate_min) / self.inverter_loss / self.battery_loss
metric -= (soc + final_iboost) * max(rate_min, 1.0, self.rate_export_min * self.inverter_loss * self.battery_loss_discharge) * self.metric_battery_value_scaling
metric10 -= (
(soc10 + final_iboost10) * max(rate_min, 1.0, self.rate_export_min * self.inverter_loss * self.battery_loss_discharge) * self.metric_battery_value_scaling
)

# Metric adjustment based on 10% outcome weighting
if metric10 > metric:
Expand Down Expand Up @@ -7684,11 +7688,27 @@ def optimise_all_windows(self, load_minutes_step, load_minutes_step10, pv_foreca
self.rate_best_cost_threshold_charge = best_price
self.rate_best_cost_threshold_discharge = best_price_discharge

# Work out the lowest rate we charge at from the first pass
lowest_price_charge = best_price
for price in price_set:
links = price_links[price]
for key in links:
typ = window_index[key]["type"]
window_n = window_index[key]["id"]
if typ == "c":
if self.charge_limit_best[window_n] > self.reserve:
if price < lowest_price_charge:
lowest_price_charge = price

# Optimise individual windows in the price band for charge/discharge
# First optimise those at or below threshold highest to lowest (to turn down values)
# then optimise those above the threshold lowest to highest (to turn up values)
# Do the opposite for discharge.
self.log("Starting second optimisation with charge limits {} based on".format(self.charge_limit_best))
self.log(
"Starting second optimisation best_price {} best_price_discharge {} lowest_price_charge {} with charge limits {} based on".format(
best_price, best_price_discharge, lowest_price_charge, self.charge_limit_best
)
)
charge_windows = []
discharge_windows = []
charge_socs = []
Expand Down Expand Up @@ -7810,7 +7830,7 @@ def optimise_all_windows(self, load_minutes_step, load_minutes_step10, pv_foreca
continue

average = self.discharge_window_best[window_n]["average"]
if price < best_price:
if price < lowest_price_charge:
if self.debug_enable and 0:
self.log(
"Skipping discharge optimisation on rate {} as it is unlikely to be profitable (threshold {} real rate {})".format(
Expand Down