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..0f5b6003 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,37 @@ 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 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).