Skip to content

Commit

Permalink
Change method of metric keep (#1786)
Browse files Browse the repository at this point in the history
* Change method of metric keep

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

* Put back debug stuff

* again

* remove debug

* Update customisation.md

* [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 Dec 24, 2024
1 parent d9ed507 commit ab31044
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 17 deletions.
12 changes: 12 additions & 0 deletions apps/predbat/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,18 @@
"icon": "mdi:battery-50",
"default": 0.5,
},
{
"name": "best_soc_keep_weight",
"friendly_name": "Best SOC Keep Weighting",
"type": "input_number",
"min": 0.1,
"max": 5,
"step": 0.10,
"unit": "*",
"icon": "mdi:multiplication",
"default": 0.5,
"enable": "expert_mode",
},
{
"name": "metric_min_improvement",
"friendly_name": "Metric Min Improvement",
Expand Down
1 change: 1 addition & 0 deletions apps/predbat/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -1759,6 +1759,7 @@ def fetch_config_options(self):
self.best_soc_min = self.get_arg("best_soc_min")
self.best_soc_max = self.get_arg("best_soc_max")
self.best_soc_keep = self.get_arg("best_soc_keep")
self.best_soc_keep_weight = self.get_arg("best_soc_keep_weight")
self.set_soc_minutes = 30
self.set_window_minutes = 30
self.inverter_set_charge_before = self.get_arg("inverter_set_charge_before")
Expand Down
1 change: 1 addition & 0 deletions apps/predbat/predbat.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ def reset(self):
self.best_soc_max = 0
self.best_soc_margin = 0
self.best_soc_keep = 0
self.best_soc_keep_weight = 0.5
self.rate_min = 0
self.rate_min_minute = 0
self.rate_min_forward = {}
Expand Down
13 changes: 5 additions & 8 deletions apps/predbat/prediction.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ def __init__(self, base=None, pv_forecast_minute_step=None, pv_forecast_minute10
self.battery_loss = base.battery_loss
self.battery_loss_discharge = base.battery_loss_discharge
self.best_soc_keep = base.best_soc_keep
self.best_soc_keep_weight = base.best_soc_keep_weight
self.best_soc_min = base.best_soc_min
self.car_charging_battery_size = base.car_charging_battery_size
self.rate_import = base.rate_import
Expand Down Expand Up @@ -411,9 +412,9 @@ def run_prediction(self, charge_limit, charge_window, export_window, export_limi
if minute < 4 * 60:
keep_minute_scaling = 0
else:
keep_minute_scaling = min(((minute - 4 * 60) / (2 * 60)), 1.0) * 0.5
keep_minute_scaling = min(((minute - 4 * 60) / (2 * 60)), 1.0) * self.best_soc_keep_weight
else:
keep_minute_scaling = 0.5
keep_minute_scaling = self.best_soc_keep_weight

# Find charge & discharge windows
charge_window_n = charge_window_optimised.get(minute_absolute, -1)
Expand Down Expand Up @@ -808,12 +809,8 @@ def run_prediction(self, charge_limit, charge_window, export_window, export_limi
diff = get_diff(battery_draw, pv_dc, pv_ac, load_yesterday, inverter_loss)

# Metric keep - pretend the battery is empty and you have to import instead of using the battery
if soc < self.best_soc_keep:
# Apply keep as a percentage of the time in the future so it gets stronger over an 4 hour period
# Weight to 50% chance of the scenario
keep_diff = max(get_diff(0, 0, pv_now, load_yesterday, inverter_loss), battery_draw)
if keep_diff > 0:
metric_keep += rate_import[minute_absolute] * keep_diff * keep_minute_scaling
if self.best_soc_keep > 0 and soc <= self.best_soc_keep:
metric_keep += (self.best_soc_keep - soc) * rate_import[minute_absolute] * keep_minute_scaling * step / 60.0
if diff > 0:
# Import
# All imports must go to home (no inverter loss) or to the battery (inverter loss accounted before above)
Expand Down
44 changes: 36 additions & 8 deletions apps/predbat/unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@ def simple_scenario(
carbon=0,
assert_final_carbon=0.0,
keep=0.0,
keep_weight=0.5,
assert_keep=0.0,
save="best",
quiet=False,
Expand Down Expand Up @@ -1250,6 +1251,7 @@ def simple_scenario(
my_predbat.iboost_gas_scale = gas_scale
my_predbat.iboost_charging = iboost_charging
my_predbat.best_soc_keep = keep
my_predbat.best_soc_keep_weight = keep_weight
my_predbat.car_charging_soc[0] = 0
my_predbat.car_charging_limit[0] = 100.0

Expand Down Expand Up @@ -1345,7 +1347,7 @@ def simple_scenario(
if abs(final_carbon_g - assert_final_carbon) >= 0.1:
print("ERROR: Final Carbon {} should be {}".format(final_carbon_g, assert_final_carbon))
failed = True
if abs(metric_keep - assert_keep) >= 0.1:
if abs(metric_keep - assert_keep) >= 0.5:
print("ERROR: Metric keep {} should be {}".format(metric_keep, assert_keep))
failed = True
if assert_iboost_running != prediction.iboost_running:
Expand Down Expand Up @@ -3168,6 +3170,7 @@ def run_optimise_all_windows(
hybrid=False,
inverter_loss=1.0,
best_soc_keep=0.0,
best_soc_keep_weight=0.5,
):
print("Starting optimise all windows test {}".format(name))
end_record = my_predbat.forecast_minutes
Expand All @@ -3179,6 +3182,7 @@ def run_optimise_all_windows(
my_predbat.inverter_hybrid = hybrid
my_predbat.inverter_loss = inverter_loss
my_predbat.best_soc_keep = best_soc_keep
my_predbat.best_soc_keep_weight = best_soc_keep_weight

reset_rates(my_predbat, rate_import, rate_export)
update_rates_import(my_predbat, charge_window_best)
Expand Down Expand Up @@ -3276,7 +3280,7 @@ def run_optimise_all_windows_tests(my_predbat):
for n in range(0, 48):
price = 16 - n % 16
charge_window_best.append({"start": my_predbat.minutes_now + 30 * n, "end": my_predbat.minutes_now + 30 * (n + 1), "average": price})
expect_charge_limit.append(100 if price <= 5.0 else 0)
expect_charge_limit.append(10 if price <= 5.0 else 0)
failed |= run_optimise_all_windows(
"created2",
my_predbat,
Expand All @@ -3286,7 +3290,26 @@ def run_optimise_all_windows_tests(my_predbat):
pv_amount=0,
expect_best_price=5 / 0.9,
inverter_loss=0.9,
best_soc_keep=0.5,
best_soc_keep=0.0,
battery_size=10,
)
if failed:
return failed

# One extra charge as we will fall below keep otherwise
expect_charge_limit[10] = 10.0
failed |= run_optimise_all_windows(
"created3",
my_predbat,
charge_window_best=charge_window_best,
expect_charge_limit=expect_charge_limit,
load_amount=0.2,
pv_amount=0,
expect_best_price=5 / 0.9,
inverter_loss=0.9,
best_soc_keep=1,
battery_soc=2,
battery_size=10,
)
if failed:
return failed
Expand Down Expand Up @@ -4061,8 +4084,9 @@ def run_model_tests(my_predbat):
with_battery=True,
discharge=0,
battery_soc=10,
assert_keep=1 * import_rate * KEEP_SCALE,
assert_keep=14 * import_rate * 0.5 + ((1 + (1 / 12)) * import_rate * 0.5 * 0.5),
keep=1,
keep_weight=0.5,
)
failed |= simple_scenario(
"battery_discharge_loss",
Expand Down Expand Up @@ -4110,8 +4134,9 @@ def run_model_tests(my_predbat):
with_battery=True,
discharge=0,
battery_soc=10,
assert_keep=14 * import_rate * 0.5 * KEEP_SCALE + 1 * import_rate * KEEP_SCALE,
assert_keep=14 * import_rate + 1 * import_rate * 0.5,
keep=1.0,
keep_weight=1.0,
)
failed |= simple_scenario(
"battery_discharge_load_keep_mode_test1",
Expand All @@ -4123,8 +4148,9 @@ def run_model_tests(my_predbat):
with_battery=True,
discharge=0,
battery_soc=10,
assert_keep=14 * import_rate * 0.5 * KEEP_SCALE + 1 * import_rate * KEEP_SCALE,
assert_keep=14 * import_rate * 0.8 + 1 * import_rate * 0.8 * 0.5,
keep=1.0,
keep_weight=0.8,
save="test",
)
failed |= simple_scenario(
Expand All @@ -4137,8 +4163,9 @@ def run_model_tests(my_predbat):
with_battery=True,
discharge=0,
battery_soc=10,
assert_keep=14 * import_rate * 0.5 * KEEP_SCALE + 1 * import_rate * KEEP_SCALE,
assert_keep=14 * import_rate * 0.8 + 1 * import_rate * 0.8 * 0.5,
keep=1.0,
keep_weight=0.8,
save="none",
)
failed |= simple_scenario(
Expand Down Expand Up @@ -4781,8 +4808,9 @@ def run_model_tests(my_predbat):
discharge=0,
battery_size=10,
keep=1.0,
keep_weight=1.0,
assert_final_iboost=0,
assert_keep=import_rate * 14 * 0.5 * KEEP_SCALE + import_rate * 1 * KEEP_SCALE,
assert_keep=import_rate * 14 + import_rate * 1 * 0.5,
)

# Alternating high/low rates
Expand Down
8 changes: 7 additions & 1 deletion docs/customisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,15 @@ you want to export as late in the day as you can.
**input_number.predbat_best_soc_keep** is the minimum battery level in kWh that Predbat will to try to keep the battery above for the Predbat plan.
This is a soft constraint only that's used for longer term planning and is ignored for the forthcoming first 4 hours of the plan.
As this is not used for short-term planning it's possible for your SoC to drop below this - use **input_number.predbat_best_soc_min**
if you need a hard SoC constraint that will always be maintained.
if you want to force all charges to be above a set level.
It's usually good to have best_soc_keep set to a value above 0 to allow some margin in case you use more energy than planned between charge slots.

**input_number.predbat_best_soc_keep_weight** (_expert_mode_) Is used to tune how strongly you want the keep metric to apply.
A value of 0 would essentially ignore keep while higher values will make it more important to always stay above your keep threshold even if it costs
more money to do so.

The default is 0.5 - this is the recommended setting.

**input_number.predbat_best_soc_min** (_expert mode_) sets the minimum charge level (in kWh) for charging during each slot and the
minimum force export level also (set to 0 if you want to skip some slots).
If you set this to a non-zero value you will need to use the low rate threshold to control which slots you charge from or you may charge all the time.
Expand Down

0 comments on commit ab31044

Please sign in to comment.