From c9e75968b80dca52bbd42c40b5c5de212e446194 Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Tue, 26 Dec 2023 11:17:37 +0000 Subject: [PATCH 1/2] Discharge optimisation --- apps/predbat/predbat.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index c4ae902ec..58c1dac5f 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -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" @@ -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? @@ -6655,8 +6655,8 @@ 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 @@ -6857,9 +6857,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: @@ -7054,9 +7054,9 @@ 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: @@ -7684,11 +7684,23 @@ 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 = [] @@ -7810,7 +7822,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( From eadd8f16f1451e3acb926a41a6470e510382d3c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 11:19:13 +0000 Subject: [PATCH 2/2] [pre-commit.ci lite] apply automatic fixes --- apps/predbat/predbat.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index 58c1dac5f..9aefe7294 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -6656,7 +6656,9 @@ 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.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 + 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 @@ -7056,7 +7058,9 @@ def optimise_discharge( # 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.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 + 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: @@ -7700,7 +7704,11 @@ def optimise_all_windows(self, load_minutes_step, load_minutes_step10, pv_foreca # 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 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)) + 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 = []