From d5b8d9131ff152c6661eb8fdfb2cb356e339589c Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Sat, 14 Dec 2024 20:43:45 +0000 Subject: [PATCH 1/3] Fixes for Predheat, max power is now output power not input, interpolate tables --- apps/predbat/predbat.py | 2 +- apps/predbat/predheat.py | 75 ++++++++++++++++++++++++++-------------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index 1f7608e3..72273ee1 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -38,7 +38,7 @@ import asyncio import json -THIS_VERSION = "v8.8.8" +THIS_VERSION = "v8.8.9" # 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"] diff --git a/apps/predbat/predheat.py b/apps/predbat/predheat.py index 577ed6fe..159300c1 100644 --- a/apps/predbat/predheat.py +++ b/apps/predbat/predheat.py @@ -75,6 +75,31 @@ class PredHeat: The heating prediction class itself """ + def fill_table_gaps(self, table): + """ + Fill gaps correction tables + """ + sorted_keys = sorted(table.keys()) + max_key = max(sorted_keys) + min_key = min(sorted_keys) + last_key = min_key + last_value = table[min_key] + new_table = {} + for key in range(min_key, max_key + 1): + if key not in table: + next_value = last_value + for next_key in range (key + 1, max_key + 1): + if next_key in table: + next_value = table[next_key] + break + interpolated = (next_value - last_value) / (next_key - last_key) * (key - last_key) + last_value + new_table[key] = round(interpolated, 4) + else: + last_key = key + last_value = table[key] + new_table[key] = last_value + return new_table + def __init__(self, base): self.base = base self.log = base.log @@ -104,10 +129,16 @@ def __init__(self, base): for key, value in delta_correction.items(): self.delta_correction[key] = value + # Fill gaps in tables + self.delta_correction = self.fill_table_gaps(self.delta_correction) + self.gas_efficiency = self.fill_table_gaps(self.gas_efficiency) + self.heat_pump_efficiency = self.fill_table_gaps(self.heat_pump_efficiency) + self.heat_pump_efficiency_max = max(self.heat_pump_efficiency.values()) - print("Predheat: Gas boiler efficiency {}".format(self.gas_efficiency)) - print("Predheat: Heat pump efficiency {}".format(self.heat_pump_efficiency)) - print("Predheat: Heat pump efficiency max {}".format(self.heat_pump_efficiency_max)) + self.log("Predheat: Delta correction {}".format(self.delta_correction)) + self.log("Predheat: Gas boiler efficiency {}".format(self.gas_efficiency)) + self.log("Predheat: Heat pump efficiency {}".format(self.heat_pump_efficiency)) + self.log("Predheat: Heat pump efficiency max {}".format(self.heat_pump_efficiency_max)) def minutes_to_time(self, updated, now): """ @@ -326,44 +357,38 @@ def run_simulation(self, volume_temp, heating_active, save="best", last_predict_ if volume_temp < flow_temp: flow_temp_diff = min(flow_temp - volume_temp, self.flow_difference_target) power_percent = flow_temp_diff / self.flow_difference_target - heat_power_in = self.heat_max_power * power_percent - heat_power_in = max(self.heat_min_power, heat_power_in) - heat_power_in = min(self.heat_max_power, heat_power_in) + heat_power_out = self.heat_max_power * power_percent + heat_power_out = max(self.heat_min_power, heat_power_out) + heat_power_out = min(self.heat_max_power, heat_power_out) # self.log("Minute {} flow {} volume {} diff {} power {} kw".format(minute, flow_temp, volume_temp, flow_temp_diff, heat_power_in / 1000.0)) - energy_now = heat_power_in * PREDICT_STEP / 60.0 / 1000.0 - cost += energy_now * self.rate_import.get(minute_absolute, 0) - - heat_energy += energy_now - heat_power_out = heat_power_in * self.heat_cop if self.mode == "gas": # Gas boiler flow temperature adjustment in efficiency based on flow temp - inlet_temp = int(volume_temp / 10 + 0.5) * 10 + inlet_temp = int(volume_temp + 0.5) + inlet_temp = min(max(inlet_temp, 0), 100) condensing = self.gas_efficiency.get(inlet_temp, 0.80) - heat_power_out *= condensing + heat_power_in /= condensing else: # Heat pump efficiency based on outdoor temp - out_temp = int(external_temp / 2 + 0.5) * 2 + out_temp = int(external_temp + 0.5) + out_temp = min(max(out_temp, -20), 20) + cop_adjust = self.heat_pump_efficiency.get(out_temp, self.heat_pump_efficiency_max) / self.heat_pump_efficiency_max + heat_power_in = heat_power_out / (self.heat_cop * cop_adjust) - # Filling gaps in COP table - if out_temp < 0: - out_temp_use = -20 - else: - out_temp_use = 20 + energy_now_in = heat_power_in * PREDICT_STEP / 60.0 / 1000.0 + energy_now_out = heat_power_out * PREDICT_STEP / 60.0 / 1000.0 - cop_adjust = self.heat_pump_efficiency.get(out_temp, self.heat_pump_efficiency.get(out_temp_use)) / self.heat_pump_efficiency_max - heat_power_out *= cop_adjust + cost += energy_now_in * self.rate_import.get(minute_absolute, 0) + heat_energy += energy_now_out # 1.16 watts required to raise water by 1 degree in 1 hour volume_temp += (heat_power_out / WATTS_TO_DEGREES / self.heat_volume) * PREDICT_STEP / 60.0 flow_delta = volume_temp - internal_temp - flow_delta_rounded = int(flow_delta / 5 + 0.5) * 5 - flow_delta_rounded = max(flow_delta_rounded, 0) - flow_delta_rounded = min(flow_delta_rounded, 75) - correction = self.delta_correction.get(flow_delta_rounded, 0) + flow_delta_rounded = min(max(int(flow_delta), 0), 75) + correction = self.delta_correction.get(flow_delta_rounded, 1.0) heat_output = self.heat_output * correction # Cooling of the radiators From c72fd7ae7602b09bcc8714cdc54a84cdd4645d35 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sat, 14 Dec 2024 20:45:14 +0000 Subject: [PATCH 2/3] [pre-commit.ci lite] apply automatic fixes --- apps/predbat/predheat.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/predbat/predheat.py b/apps/predbat/predheat.py index 159300c1..0f5b6003 100644 --- a/apps/predbat/predheat.py +++ b/apps/predbat/predheat.py @@ -88,7 +88,7 @@ def fill_table_gaps(self, table): for key in range(min_key, max_key + 1): if key not in table: next_value = last_value - for next_key in range (key + 1, max_key + 1): + for next_key in range(key + 1, max_key + 1): if next_key in table: next_value = table[next_key] break @@ -363,7 +363,6 @@ def run_simulation(self, volume_temp, heating_active, save="best", last_predict_ # self.log("Minute {} flow {} volume {} diff {} power {} kw".format(minute, flow_temp, volume_temp, flow_temp_diff, heat_power_in / 1000.0)) - if self.mode == "gas": # Gas boiler flow temperature adjustment in efficiency based on flow temp inlet_temp = int(volume_temp + 0.5) From 7db191c31dc8504ee398b90c15b534759d91f3de Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Sat, 14 Dec 2024 20:45:19 +0000 Subject: [PATCH 3/3] Update predheat.md --- docs/predheat.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/predheat.md b/docs/predheat.md index 414a3070..ead85311 100644 --- a/docs/predheat.md +++ b/docs/predheat.md @@ -84,7 +84,8 @@ Now you need to make a list of all your radiators in the house, measure them and Add up all the BTUs and divide by 3.41 to gain the heat output in Watts and set that in **heat_output** configuration option. Add up all the litres of water, add in some extra for the piping and an expansion vessel if present (e.g. 5-10 litres) and set **heat_volume** accordingly. -Set the **heat_max_power** and **heat_min_power** to the minimum and maximum power output of your boiler/heat-pump in watts. +Set the **heat_max_power** and **heat_min_power** to the minimum and maximum power output of your boiler/heat-pump in watts. This should be specified as the maximum output power +and not the maximum input energy. E.g. a heat pump with a COP of 4 might output 7kW but could only consume 1.7kW. Set **heating_cop** to the nominal COP of your system. For a gas boiler use 1.0 (as the efficiency will be based on flow temperature) or for a heat pump set it to the best value which is likely around 4.0 (it will be scaled down for cold weather).